BridgitDecember 2023
Northeastern University
Skills Learned: Java, Object-Oriented Programming, Graphs, Dynamic Dispatch, Imperative World Libraries, Graphics Libraries, ArrayLists
Code Available Upon RequestBridgit is a two player game where the goal of each player is to create a full path from one side of the board to the other. To do so, players alternate turns and claim spaces one at a time until a full path is reached (similar to tic tac toe). Players can choose how big they want their board to be (and thus increase the complexity of the game), however this must be an odd number greater than 3. Examples of a 7x7 game and 5x5 game are shown below.
Class Diagram
The following diagram shows the relationships and fields of all classes used in the creation of Bridgit. Methods within classes are not shown for academic honesty purposes.
Design Strategies
When creating the game, I realized that there were a few major concepts that needed to be handled in order to create a working game. I needed to figure out how to:
- Initialize a starting board based on the specified dimensions.
- Create a graphical representation of the board.
- Give each tile a reference to it's neighboring tile (ensure each tile knows its neighbors).
- Detect the occurrance and location of a mouse click. Mutate elements of the game if necessary.
- Determine when a player has won and display and end state.
I first created the IBoard interface to represent an element of the game board. Two classes implement this interface: Border and Tile class. The Border class
represents the end of the board and has no fields. The tile class has fields for the owner of the tile, it's pixel size, it's hitbox in pixels, and the four tiles which
neighbor it.
I was thus able to represent a square board of tiles by using a two dimensional ArrayList. The row and column index of each tile indicates its location on the board.
By calling methods on the ArrayList in the constructor of the overarching Bridgit class (extended from Northeastern's imperative world class),
I was able to initialize a board by creating tile objects for each position, followed by appropriately setting the owner and neighbor of each tile based on its location.
I then utilized Northeastern's image library to create images for tiles and color them according to their owner. Applying these methods to each
tile on the board (within the 2D ArrayList), I was able to draw the board in its entirety. Using specific methods from the imperative world library, I was able to retrieve the pixel coordinates
of a mouse click. I then created a Hitbox class to represent the pixel "hitbox" of a tile based on it's location in the 2D ArrayList. From here, I created methods to
determine if a given mouse click was within the bounds of a tile's hitbox, and thus determine which specific tile (if any) had been clicked. I then wrote logic to ensure that
when a tile is clicked, the correct actions/mutations in the game will occur based on who's turn it is, the current owner of the tile, and other factors.
Finally, my last hurdle was to figure out how to determine if either player had a complete path from one side of the board to the other. My solution was
to perform a Depth First Search from each player's starting tiles (left-most tiles for Player 1, top-most tiles for Player 2). The search would attempt to find tiles
that were owned by the same player as far to the right or as far down as possible, making sure to keep track of tiles that were already visited in order to avoid
cycles. If the search came across a Border tile when looking in the desired direction, then the path had reached the end of the board and was thus complete. Upon this
event, the game would terminate and display an end scene proclaiming which player had won.
Difficulties and Learning Outcomes
- Implementing Depth First Search proved to be difficult as detecting when a Border was reached was easier said then done. Initially, the method would return true if any tile's neighbors were a Border tile instead of the neighbor in the correct direction. Changes were thus made to ensure that the correct direction was checked first, and that the method would keep searching if other neighbors were Borders.
- This game helped me learn more about graphs and referential fields. Since typechecking was not allowed in this program, I was forced to create infrastructure utilizing dynamic dispatch to control functionality when certain states were reached.
- Testing the methods and classes of this program proved challenging as there seemed to be numerous edge-cases to consider. I spent a lot of time thinking of possible test and edge cases for each method and thus wrote numerous unit tests to ensure all methods worked as expected.
Overall, I thoroughly enjoyed creating Bridgit. It proved to be a culmination of the concepts and design processes I had learned throughout the semester in my various Northeastern classes, and helped me push the boundaries of my knowledge of object-oriented programming.