Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
JonBergland committed Jan 22, 2024
2 parents 48a9988 + 8c73a98 commit ee39fbe
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 0 deletions.
4 changes: 4 additions & 0 deletions backend/src/main/java/board/master/model/games/Move.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ public class Move extends Action {
private final String x; // e.g., "e2"
private final String y; // e.g., "e4"

public Move(int x) {
this.x = Integer.toString(x);
this.y = null;
}

public Move(String x, String y) {
this.x = x;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package board.master.model.games.connect_four;

import java.util.List;

import board.master.model.Action;
import board.master.model.StateHandler;
import board.master.model.games.Board;

public class ConnectFour implements StateHandler {

@Override
public int toMove() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'toMove'");
}

@Override
public List<Action> getActions() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getActions'");
}

@Override
public StateHandler result(Action action) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'result'");
}

@Override
public boolean isTerminal() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isTerminal'");
}

@Override
public int utility(int player) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'utility'");
}

@Override
public Board getBoard() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getBoard'");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package board.master.model.games.connect_four;

import java.util.ArrayList;
import java.util.List;

import board.master.model.Action;
import board.master.model.StateHandler;
import board.master.model.games.Board;
import board.master.model.games.Move;

public class ConnectFour implements StateHandler {

private int playerToMove;
private Board board;
private final int rowLength = 6;
private final int columnHeight = 7;

public ConnectFour() {
playerToMove = 1;
board = new Board(rowLength, columnHeight);
}

private ConnectFour(int playerToMove, Board board) {
this.playerToMove = playerToMove;
this.board = new Board(board.getRows(), board.getColumns());
for (int x = 0; x < board.getRows(); x++) {
for (int y = 0; y < board.getColumns(); y++) {
this.board.setPosition(x, y, board.getPosition(x, y));
}
}
}

@Override
public int toMove() {
return playerToMove;
}

@Override
public List<Action> getActions() {
List<Action> actions = new ArrayList<Action>();
for (int x = 0; x < rowLength; x++) {
if (board.getPosition(x, columnHeight-1) == "") {
actions.add(new Move(x));
}
}
return actions;
}

@Override
public StateHandler result(Action action) {
// Create a StateHandler with the same board and the opposite player to move
ConnectFour result = new ConnectFour(-playerToMove, board);

Move move = (Move) action;
int x = Integer.valueOf(move.getX());
for (int y = 0; y < columnHeight; y++) {
if (result.getBoard().getPosition(x, y) == "") {
String symbol = getPlayerSymbol(this.toMove());
result.getBoard().setPosition(x, y, symbol);
break;
}
}
return result;
}

@Override
public boolean isTerminal() {
// Check for a win
if (checkForWin(1) || checkForWin(-1)) {
return true;
}
// Check for a draw
return getActions().isEmpty();
}

private boolean checkForWin(int player) {
// Check horizontal, vertical, and diagonal lines
return checkHorizontalWin(player) || checkVerticalWin(player) || checkDiagonalWin(player);
}

private boolean checkHorizontalWin(int player) {
String playerSymbol = getPlayerSymbol(player);
for (int row = 0; row < rowLength; row++) {
for (int col = 0; col < columnHeight - 3; col++) {
if (board.getPosition(row, col).equals(playerSymbol)
&& board.getPosition(row, col + 1).equals(playerSymbol)
&& board.getPosition(row, col + 2).equals(playerSymbol)
&& board.getPosition(row, col + 3).equals(playerSymbol)) {
return true;
}
}
}
return false;
}

private boolean checkVerticalWin(int player) {
String playerSymbol = Integer.toString(player);
for (int col = 0; col < columnHeight; col++) {
for (int row = 0; row < rowLength - 3; row++) {
if (board.getPosition(row, col).equals(playerSymbol)
&& board.getPosition(row + 1, col).equals(playerSymbol)
&& board.getPosition(row + 2, col).equals(playerSymbol)
&& board.getPosition(row + 3, col).equals(playerSymbol)) {
return true;
}
}
}
return false;
}

private boolean checkDiagonalWin(int player) {
String playerSymbol = Integer.toString(player);
// Check diagonal (top-left to bottom-right)
for (int row = 0; row < rowLength - 3; row++) {
for (int col = 0; col < columnHeight - 3; col++) {
if (board.getPosition(row, col).equals(playerSymbol)
&& board.getPosition(row + 1, col + 1).equals(playerSymbol)
&& board.getPosition(row + 2, col + 2).equals(playerSymbol)
&& board.getPosition(row + 3, col + 3).equals(playerSymbol)) {
return true;
}
}
}

// Check diagonal (bottom-left to top-right)
for (int row = 3; row < rowLength; row++) {
for (int col = 0; col < columnHeight - 3; col++) {
if (board.getPosition(row, col).equals(playerSymbol)
&& board.getPosition(row - 1, col + 1).equals(playerSymbol)
&& board.getPosition(row - 2, col + 2).equals(playerSymbol)
&& board.getPosition(row - 3, col + 3).equals(playerSymbol)) {
return true;
}
}
}

return false;
}

@Override
public int utility(int player) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'utility'");
}

@Override
public Board getBoard() {
return board;
}

private String getPlayerSymbol(int player) {
return player == 1 ? "X" : "O";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package board.master.model.games.connect_four;

import org.junit.jupiter.api.Test;

import board.master.model.games.Board;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;

public class ConnectFourTest {

private ConnectFour connectFour;
private int columnHeight;

@BeforeEach
void setUp() {
connectFour = new ConnectFour();
columnHeight = 7;
}

@Nested
class TestGetActions {

@Test
void testGetAllActionsAtStart() {
int expected = 6;
int actual = connectFour.getActions().size();
assertEquals(expected, actual);
}

@Test
void testGetAllActionsAfterOneMove() {
int expected = 6;
connectFour.result(connectFour.getActions().get(0));
int actual = connectFour.getActions().size();
assertEquals(expected, actual);
}

@Test
void testGetAllActionsAfterColumnIsFull() {
int expected = 5;
for (int i = 0; i < columnHeight; i++) {
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
}
int actual = connectFour.getActions().size();
assertEquals(expected, actual);
}

}

@Nested
class testGetBoard {

@Test
void testGetBoardAtStart() {
for (int x = 0; x < connectFour.getBoard().getRows(); x++) {
for (int y = 0; y < connectFour.getBoard().getColumns(); y++) {
String expected = "";
String actual = connectFour.getBoard().getPosition(x, y);
assertEquals(expected, actual);
}
}
}

@Test
void testGetBoardAfterOneMove() {
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
String laidPiece = connectFour.getBoard().getPosition(0, 0);
assertEquals("X", laidPiece);
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
laidPiece = connectFour.getBoard().getPosition(0, 1);
assertEquals("O", laidPiece);
}

}


@Nested
class testIsTerminal {

@Test
void testIsTerminalAtStart() {
boolean expected = false;
boolean actual = connectFour.isTerminal();
assertEquals(expected, actual);
}

@Test
void testIsTerminalAfterLessThenPossibleWinningCombination() {
boolean expected = false;
int numberOfMoves = 3;
for (int i = 0; i < numberOfMoves; i++) {
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(1));
}
boolean actual = connectFour.isTerminal();
assertEquals(expected, actual);
}

@Test
void testIsTerminalAfterGameIsWon() {
boolean expected = true;
// Player 1 makes tower in column 0 and wins
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(1));

connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(1));

connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(1));
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
boolean actual = connectFour.isTerminal();
assertEquals(expected, actual);


}

}


@Nested
class TestResult {

@Test
void testResultDoesNotMutateOriginal() {
ConnectFour originalStateHandler = connectFour;
int originalToMove = connectFour.toMove();
Board originalBoard = connectFour.getBoard();
ConnectFour transformedStateHandler = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
assertEquals(originalBoard, originalStateHandler.getBoard());
assertEquals(originalToMove, originalStateHandler.toMove());
}

@Test
void testResultsTransformationHasTransformed() {
ConnectFour originalStateHandler = connectFour;
ConnectFour transformedStateHandler = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
assertNotEquals(originalStateHandler, transformedStateHandler);
assertNotEquals(originalStateHandler.getBoard(), transformedStateHandler.getBoard());

}

}
@Nested
class TestToMove {

@Test
void testToMoveAtStart() {
int expected = 1;
int actual = connectFour.toMove();
assertEquals(expected, actual);
}

@Test
void testToMoveAfterOneMove() {
int expected = -1;
connectFour = (ConnectFour) connectFour.result(connectFour.getActions().get(0));
int actual = connectFour.toMove();
assertEquals(expected, actual);
}

@Test
void testToMoveAfterTwoMoves() {
int expected = 1;
connectFour.result(connectFour.getActions().get(0));
connectFour.result(connectFour.getActions().get(0));
int actual = connectFour.toMove();
assertEquals(expected, actual);
}

}


@Test
void testUtility() {

}
}

0 comments on commit ee39fbe

Please sign in to comment.