mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 19:04:49 +00:00
small amount of refactoring. might break everything :).
This commit is contained in:
@@ -4,52 +4,31 @@ import java.util.Arrays;
|
||||
|
||||
public abstract class Game {
|
||||
public enum State {
|
||||
NORMAL, DRAW, LOSE, WIN,
|
||||
NORMAL, DRAW, WIN,
|
||||
}
|
||||
|
||||
public record Move(int position, char value) {}
|
||||
|
||||
public static final char EMPTY = (char)0;
|
||||
|
||||
protected final int rowSize;
|
||||
protected final int columnSize;
|
||||
protected final char[] board;
|
||||
public final int rowSize;
|
||||
public final int columnSize;
|
||||
public final char[] board;
|
||||
|
||||
protected final Player[] players;
|
||||
protected int currentPlayer;
|
||||
|
||||
protected Game(int rowSize, int columnSize, Player... players) {
|
||||
protected Game(int rowSize, int columnSize) {
|
||||
assert rowSize > 0 && columnSize > 0;
|
||||
assert players.length >= 1;
|
||||
|
||||
this.rowSize = rowSize;
|
||||
this.columnSize = columnSize;
|
||||
|
||||
board = new char[rowSize * columnSize];
|
||||
Arrays.fill(board, EMPTY);
|
||||
|
||||
this.players = players;
|
||||
currentPlayer = 0;
|
||||
}
|
||||
|
||||
protected Game(Game other) {
|
||||
rowSize = other.rowSize;
|
||||
columnSize = other.columnSize;
|
||||
board = Arrays.copyOf(other.board, other.board.length);
|
||||
|
||||
players = Arrays.copyOf(other.players, other.players.length);
|
||||
currentPlayer = other.currentPlayer;
|
||||
}
|
||||
|
||||
public int getRowSize() { return rowSize; }
|
||||
public int getColumnSize() { return columnSize; }
|
||||
public char[] getBoard() { return board; }
|
||||
|
||||
public Player[] getPlayers() { return players; }
|
||||
public Player getCurrentPlayer() { return players[currentPlayer]; }
|
||||
|
||||
protected void nextPlayer() {
|
||||
currentPlayer = (currentPlayer + 1) % players.length;
|
||||
}
|
||||
|
||||
public abstract Move[] getLegalMoves();
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package org.toop.game;
|
||||
|
||||
public record Player(String name, boolean isAI, char... values) {}
|
||||
26
game/src/main/java/org/toop/game/TurnBasedGame.java
Normal file
26
game/src/main/java/org/toop/game/TurnBasedGame.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.toop.game;
|
||||
|
||||
public abstract class TurnBasedGame extends Game {
|
||||
public final int turns;
|
||||
|
||||
protected int currentTurn;
|
||||
|
||||
protected TurnBasedGame(int rowSize, int columnSize, int turns) {
|
||||
assert turns >= 2;
|
||||
|
||||
super(rowSize, columnSize);
|
||||
this.turns = turns;
|
||||
}
|
||||
|
||||
protected TurnBasedGame(TurnBasedGame other) {
|
||||
super(other);
|
||||
turns = other.turns;
|
||||
currentTurn = other.currentTurn;
|
||||
}
|
||||
|
||||
protected void nextTurn() {
|
||||
currentTurn = (currentTurn + 1) % turns;
|
||||
}
|
||||
|
||||
public int getCurrentTurn() { return currentTurn; }
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
package org.toop.game.tictactoe;
|
||||
|
||||
import org.toop.game.Game;
|
||||
import org.toop.game.Player;
|
||||
import org.toop.game.TurnBasedGame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class TicTacToe extends Game {
|
||||
public final class TicTacToe extends TurnBasedGame {
|
||||
private int movesLeft;
|
||||
|
||||
public TicTacToe(String player1, boolean isPlayer1AI, String player2, boolean isPlayer2AI) {
|
||||
super(3, 3, new Player(player1, isPlayer1AI, 'X'), new Player(player2, isPlayer2AI, 'O'));
|
||||
public TicTacToe() {
|
||||
super(3, 3, 2);
|
||||
movesLeft = board.length;
|
||||
}
|
||||
|
||||
@@ -20,11 +19,12 @@ public final class TicTacToe extends Game {
|
||||
|
||||
@Override
|
||||
public Move[] getLegalMoves() {
|
||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||
final char currentValue = getCurrentValue();
|
||||
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
if (board[i] == EMPTY) {
|
||||
legalMoves.add(new Move(i, getCurrentPlayer().values()[0]));
|
||||
legalMoves.add(new Move(i, currentValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public final class TicTacToe extends Game {
|
||||
public State play(Move move) {
|
||||
assert move != null;
|
||||
assert move.position() >= 0 && move.position() < board.length;
|
||||
assert move.value() == getCurrentPlayer().values()[0];
|
||||
assert move.value() == getCurrentValue();
|
||||
|
||||
board[move.position()] = move.value();
|
||||
movesLeft--;
|
||||
@@ -44,12 +44,12 @@ public final class TicTacToe extends Game {
|
||||
return State.WIN;
|
||||
}
|
||||
|
||||
nextPlayer();
|
||||
nextTurn();
|
||||
|
||||
if (movesLeft <= 2) {
|
||||
if (checkDraw(new TicTacToe(this))) {
|
||||
return State.DRAW;
|
||||
}
|
||||
if (movesLeft <= 0 || checkForEarlyDraw(this)) {
|
||||
return State.DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
return State.NORMAL;
|
||||
@@ -60,18 +60,14 @@ public final class TicTacToe extends Game {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int index = i * 3;
|
||||
|
||||
if (board[index] != EMPTY
|
||||
&& board[index] == board[index + 1]
|
||||
&& board[index] == board[index + 2]) {
|
||||
if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (board[i] != EMPTY
|
||||
&& board[i] == board[i + 3]
|
||||
&& board[i] == board[i + 6]) {
|
||||
if (board[i] != EMPTY && board[i] == board[i + 3] && board[i] == board[i + 6]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -85,25 +81,19 @@ public final class TicTacToe extends Game {
|
||||
return board[2] != EMPTY && board[2] == board[4] && board[2] == board[6];
|
||||
}
|
||||
|
||||
public boolean checkDraw(TicTacToe game) {
|
||||
if (game.checkForWin()) {
|
||||
return false;
|
||||
}
|
||||
if (game.movesLeft == 0) {
|
||||
return true;
|
||||
}
|
||||
// try every move on a legal copy
|
||||
for (Move move : game.getLegalMoves()) {
|
||||
TicTacToe copy = new TicTacToe(game);
|
||||
State result = copy.play(move);
|
||||
private boolean checkForEarlyDraw(TicTacToe game) {
|
||||
for (final Move move : game.getLegalMoves()) {
|
||||
final TicTacToe copy = new TicTacToe(game);
|
||||
|
||||
if (result == State.WIN) {
|
||||
return false;
|
||||
}
|
||||
if (!checkDraw(copy)) {
|
||||
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private char getCurrentValue() {
|
||||
return currentTurn == 0? 'X' : 'O';
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
//package org.toop.game;
|
||||
//
|
||||
//import org.junit.jupiter.api.BeforeEach;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//
|
||||
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
//
|
||||
//class PlayerTest {
|
||||
// private Player playerA;
|
||||
// private Player playerB;
|
||||
// private Player playerC;
|
||||
//
|
||||
// @BeforeEach
|
||||
// void setup() {
|
||||
// playerA = new Player("test A", 'x', 'Z', 'i');
|
||||
// playerB = new Player("test B", 'O', (char)12, (char)-34, 's');
|
||||
// playerC = new Player("test C", (char)9, '9', (char)-9, '0', 'X', 'O');
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testNameGetter_returnsTrueForValidName() {
|
||||
// assertEquals("test A", playerA.name());
|
||||
// assertEquals("test B", playerB.name());
|
||||
// assertEquals("test C", playerC.name());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testValuesGetter_returnsTrueForValidValues() {
|
||||
// final char[] valuesA = playerA.values();
|
||||
// assertEquals('x', valuesA[0]);
|
||||
// assertEquals('Z', valuesA[1]);
|
||||
// assertEquals('i', valuesA[2]);
|
||||
//
|
||||
// final char[] valuesB = playerB.values();
|
||||
// assertEquals('O', valuesB[0]);
|
||||
// assertEquals(12, valuesB[1]);
|
||||
// assertEquals((char)-34, valuesB[2]);
|
||||
// assertEquals('s', valuesB[3]);
|
||||
//
|
||||
// final char[] valuesC = playerC.values();
|
||||
// assertEquals((char)9, valuesC[0]);
|
||||
// assertEquals('9', valuesC[1]);
|
||||
// assertEquals((char)-9, valuesC[2]);
|
||||
// assertEquals('0', valuesC[3]);
|
||||
// assertEquals('X', valuesC[4]);
|
||||
// assertEquals('O', valuesC[5]);
|
||||
// }
|
||||
//}
|
||||
@@ -1,83 +1,83 @@
|
||||
//package org.toop.game.tictactoe;
|
||||
//
|
||||
//import org.toop.game.Game;
|
||||
//
|
||||
//import java.util.Set;
|
||||
//
|
||||
//import org.junit.jupiter.api.BeforeEach;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//
|
||||
//import static org.junit.jupiter.api.Assertions.*;
|
||||
//
|
||||
//class TicTacToeAITest {
|
||||
// private TicTacToe game;
|
||||
// private TicTacToeAI ai;
|
||||
//
|
||||
// @BeforeEach
|
||||
// void setup() {
|
||||
// game = new TicTacToe("AI", "AI");
|
||||
// ai = new TicTacToeAI();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testBestMove_returnWinningMoveWithDepth1() {
|
||||
// // X X -
|
||||
// // O O -
|
||||
// // - - -
|
||||
// game.play(new Game.Move(0, 'X'));
|
||||
// game.play(new Game.Move(3, 'O'));
|
||||
// game.play(new Game.Move(1, 'X'));
|
||||
// game.play(new Game.Move(4, 'O'));
|
||||
//
|
||||
// final Game.Move move = ai.findBestMove(game, 1);
|
||||
//
|
||||
// assertNotNull(move);
|
||||
// assertEquals('X', move.value());
|
||||
// assertEquals(2, move.position());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testBestMove_blockOpponentWinDepth1() {
|
||||
// // - - -
|
||||
// // O - -
|
||||
// // X X -
|
||||
// game.play(new Game.Move(6, 'X'));
|
||||
// game.play(new Game.Move(3, 'O'));
|
||||
// game.play(new Game.Move(7, 'X'));
|
||||
//
|
||||
// final Game.Move move = ai.findBestMove(game, 1);
|
||||
//
|
||||
// assertNotNull(move);
|
||||
// assertEquals('O', move.value());
|
||||
// assertEquals(8, move.position());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testBestMove_preferCornerOnEmpty() {
|
||||
// final Game.Move move = ai.findBestMove(game, 0);
|
||||
//
|
||||
// assertNotNull(move);
|
||||
// assertEquals('X', move.value());
|
||||
// assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testBestMove_findBestMoveDraw() {
|
||||
// // O X -
|
||||
// // - O X
|
||||
// // X O X
|
||||
// game.play(new Game.Move(1, 'X'));
|
||||
// game.play(new Game.Move(0, 'O'));
|
||||
// game.play(new Game.Move(5, 'X'));
|
||||
// game.play(new Game.Move(4, 'O'));
|
||||
// game.play(new Game.Move(6, 'X'));
|
||||
// game.play(new Game.Move(7, 'O'));
|
||||
// game.play(new Game.Move(8, 'X'));
|
||||
//
|
||||
// final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
||||
//
|
||||
// assertNotNull(move);
|
||||
// assertEquals('O', move.value());
|
||||
// assertEquals(2, move.position());
|
||||
// }
|
||||
//}
|
||||
package org.toop.game.tictactoe;
|
||||
|
||||
import org.toop.game.Game;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TicTacToeAITest {
|
||||
private TicTacToe game;
|
||||
private TicTacToeAI ai;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
game = new TicTacToe();
|
||||
ai = new TicTacToeAI();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_returnWinningMoveWithDepth1() {
|
||||
// X X -
|
||||
// O O -
|
||||
// - - -
|
||||
game.play(new Game.Move(0, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_blockOpponentWinDepth1() {
|
||||
// - - -
|
||||
// O - -
|
||||
// X X -
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(7, 'X'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(8, move.position());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_preferCornerOnEmpty() {
|
||||
final Game.Move move = ai.findBestMove(game, 0);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_findBestMoveDraw() {
|
||||
// O X -
|
||||
// - O X
|
||||
// X O X
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(0, 'O'));
|
||||
game.play(new Game.Move(5, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(7, 'O'));
|
||||
game.play(new Game.Move(8, 'X'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user