diff --git a/.gitignore b/.gitignore index a743f21..9079bab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea/ +out/ + # Eclipse .metadata .settings diff --git a/Dawid.jar b/Dawid.jar new file mode 100644 index 0000000..dea6c78 Binary files /dev/null and b/Dawid.jar differ diff --git a/alg/A_Pawlikowska/APawlikowska.jar b/alg/A_Pawlikowska/APawlikowska.jar new file mode 100644 index 0000000..0364bf0 Binary files /dev/null and b/alg/A_Pawlikowska/APawlikowska.jar differ diff --git a/alg/A_Pawlikowska/info.txt b/alg/A_Pawlikowska/info.txt new file mode 100644 index 0000000..7285e65 --- /dev/null +++ b/alg/A_Pawlikowska/info.txt @@ -0,0 +1 @@ +java - jar .\Programs\A_Pawlikowska\APawlikowska.jar \ No newline at end of file diff --git "a/alg/Adam_Kami\305\204ski/BrickFighter.jar" "b/alg/Adam_Kami\305\204ski/BrickFighter.jar" new file mode 100644 index 0000000..92a916b Binary files /dev/null and "b/alg/Adam_Kami\305\204ski/BrickFighter.jar" differ diff --git "a/alg/Adam_Kami\305\204ski/info.txt" "b/alg/Adam_Kami\305\204ski/info.txt" new file mode 100644 index 0000000..9291982 --- /dev/null +++ "b/alg/Adam_Kami\305\204ski/info.txt" @@ -0,0 +1,2 @@ +java -jar BrickFighter.jar +Adam Kaminski \ No newline at end of file diff --git a/alg/Artur_Braksator/info.txt b/alg/Artur_Braksator/info.txt new file mode 100644 index 0000000..58b3029 --- /dev/null +++ b/alg/Artur_Braksator/info.txt @@ -0,0 +1,2 @@ +java -jar ./Programs/Artur_Braksator/programWalczacy.jar +programWalczacy.jar (Artur Braksator) \ No newline at end of file diff --git a/alg/Artur_Braksator/programWalczacy.jar b/alg/Artur_Braksator/programWalczacy.jar new file mode 100644 index 0000000..2677658 Binary files /dev/null and b/alg/Artur_Braksator/programWalczacy.jar differ diff --git a/alg/Borecki_Beniamin/AlgorytmWalczacy.jar b/alg/Borecki_Beniamin/AlgorytmWalczacy.jar new file mode 100644 index 0000000..8a6aef9 Binary files /dev/null and b/alg/Borecki_Beniamin/AlgorytmWalczacy.jar differ diff --git a/alg/Borecki_Beniamin/info.txt b/alg/Borecki_Beniamin/info.txt new file mode 100644 index 0000000..cac979d --- /dev/null +++ b/alg/Borecki_Beniamin/info.txt @@ -0,0 +1,2 @@ +java -jar ./Programs/Borecki_Beniamin/AlgorytmWalczacy.jar +Borecki Beniamin \ No newline at end of file diff --git a/alg/Dawid_Sowa/Dawid.jar b/alg/Dawid_Sowa/Dawid.jar new file mode 100644 index 0000000..dea6c78 Binary files /dev/null and b/alg/Dawid_Sowa/Dawid.jar differ diff --git a/alg/Dawid_Sowa/info.txt b/alg/Dawid_Sowa/info.txt new file mode 100644 index 0000000..3909d3b --- /dev/null +++ b/alg/Dawid_Sowa/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Dawid_Sowa/Dawid.jar \ No newline at end of file diff --git a/alg/Jakub_Bielawski/bricks-1.0-SNAPSHOT.jar b/alg/Jakub_Bielawski/bricks-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..6cac08f Binary files /dev/null and b/alg/Jakub_Bielawski/bricks-1.0-SNAPSHOT.jar differ diff --git a/alg/Jakub_Bielawski/info.txt b/alg/Jakub_Bielawski/info.txt new file mode 100644 index 0000000..f54c8e5 --- /dev/null +++ b/alg/Jakub_Bielawski/info.txt @@ -0,0 +1,2 @@ +java -cp bricks-1.0-SNAPSHOT.jar com.algorytmy.bricks.App +Jakub Bielawski \ No newline at end of file diff --git a/alg/Karol_Szymanski/Player.jar b/alg/Karol_Szymanski/Player.jar new file mode 100644 index 0000000..b214387 Binary files /dev/null and b/alg/Karol_Szymanski/Player.jar differ diff --git a/alg/Karol_Szymanski/info.txt b/alg/Karol_Szymanski/info.txt new file mode 100644 index 0000000..4f0bfdd --- /dev/null +++ b/alg/Karol_Szymanski/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Karol_Szymanski/Player.jar \ No newline at end of file diff --git a/alg/Maciej_Step/PlayerA.jar b/alg/Maciej_Step/PlayerA.jar new file mode 100644 index 0000000..25602cf Binary files /dev/null and b/alg/Maciej_Step/PlayerA.jar differ diff --git a/alg/Maciej_Step/info.txt b/alg/Maciej_Step/info.txt new file mode 100644 index 0000000..9b943a6 --- /dev/null +++ b/alg/Maciej_Step/info.txt @@ -0,0 +1 @@ +java -jar .\Programs\Maciej_Step\PlayerA.jar \ No newline at end of file diff --git a/alg/Maciej_Tarnowski/Maciej_Tarnowski.jar b/alg/Maciej_Tarnowski/Maciej_Tarnowski.jar new file mode 100644 index 0000000..1d0d6b1 Binary files /dev/null and b/alg/Maciej_Tarnowski/Maciej_Tarnowski.jar differ diff --git a/alg/Maciej_Tarnowski/info.txt b/alg/Maciej_Tarnowski/info.txt new file mode 100644 index 0000000..62bd696 --- /dev/null +++ b/alg/Maciej_Tarnowski/info.txt @@ -0,0 +1,2 @@ +java -jar -XX:+UseConcMarkSweepGC ./Programs/Maciej_Tarnowski/Maciej_Tarnowski.jar +Maciej "Anal destroyer" Tarnowski \ No newline at end of file diff --git a/alg/Marcin_Krasuski/Marcin_Krasuski.jar b/alg/Marcin_Krasuski/Marcin_Krasuski.jar new file mode 100644 index 0000000..b2bc868 Binary files /dev/null and b/alg/Marcin_Krasuski/Marcin_Krasuski.jar differ diff --git a/alg/Marcin_Krasuski/info.txt b/alg/Marcin_Krasuski/info.txt new file mode 100644 index 0000000..7000b06 --- /dev/null +++ b/alg/Marcin_Krasuski/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Marcin_Krasuski/Marcin_Krasuski.jar \ No newline at end of file diff --git a/alg/Mateusz_Blaszczak/Bloczki.jar b/alg/Mateusz_Blaszczak/Bloczki.jar new file mode 100644 index 0000000..11efe3d Binary files /dev/null and b/alg/Mateusz_Blaszczak/Bloczki.jar differ diff --git a/alg/Mateusz_Blaszczak/info.txt b/alg/Mateusz_Blaszczak/info.txt new file mode 100644 index 0000000..f731b9c --- /dev/null +++ b/alg/Mateusz_Blaszczak/info.txt @@ -0,0 +1,2 @@ +java -jar ./Programs/Mateusz_Blaszczak/Bloczki.jar +Bloczki.jar (Mateusz Błaszczak) \ No newline at end of file diff --git a/alg/Mateusz_Koczan/Player.exe b/alg/Mateusz_Koczan/Player.exe new file mode 100644 index 0000000..10b867b Binary files /dev/null and b/alg/Mateusz_Koczan/Player.exe differ diff --git a/alg/Mateusz_Koczan/info.txt b/alg/Mateusz_Koczan/info.txt new file mode 100644 index 0000000..2ad0f74 --- /dev/null +++ b/alg/Mateusz_Koczan/info.txt @@ -0,0 +1 @@ +./Programs/Mateusz_Koczan/Player.exe \ No newline at end of file diff --git a/alg/Mateusz_Ostrowski/Game03.jar b/alg/Mateusz_Ostrowski/Game03.jar new file mode 100644 index 0000000..f48f072 Binary files /dev/null and b/alg/Mateusz_Ostrowski/Game03.jar differ diff --git a/alg/Mateusz_Ostrowski/info.txt b/alg/Mateusz_Ostrowski/info.txt new file mode 100644 index 0000000..5d7f69b --- /dev/null +++ b/alg/Mateusz_Ostrowski/info.txt @@ -0,0 +1,2 @@ +java -jar ./Programs/Mateusz_Ostrowski/Game03.jar +MATEUSZ OSTROWSKI \ No newline at end of file diff --git a/alg/Michal_Weiss/284950.jar b/alg/Michal_Weiss/284950.jar new file mode 100644 index 0000000..593b2e8 Binary files /dev/null and b/alg/Michal_Weiss/284950.jar differ diff --git a/alg/Michal_Weiss/Mateusz_Ostrowski/Game03.jar b/alg/Michal_Weiss/Mateusz_Ostrowski/Game03.jar new file mode 100644 index 0000000..f48f072 Binary files /dev/null and b/alg/Michal_Weiss/Mateusz_Ostrowski/Game03.jar differ diff --git a/alg/Michal_Weiss/Mateusz_Ostrowski/info.txt b/alg/Michal_Weiss/Mateusz_Ostrowski/info.txt new file mode 100644 index 0000000..5d7f69b --- /dev/null +++ b/alg/Michal_Weiss/Mateusz_Ostrowski/info.txt @@ -0,0 +1,2 @@ +java -jar ./Programs/Mateusz_Ostrowski/Game03.jar +MATEUSZ OSTROWSKI \ No newline at end of file diff --git a/alg/Michal_Weiss/info.txt b/alg/Michal_Weiss/info.txt new file mode 100644 index 0000000..e601edd --- /dev/null +++ b/alg/Michal_Weiss/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Michal_Weiss/284950.jar diff --git a/alg/Patryk_Milewski/PatrykMilewski.jar b/alg/Patryk_Milewski/PatrykMilewski.jar new file mode 100644 index 0000000..794b72e Binary files /dev/null and b/alg/Patryk_Milewski/PatrykMilewski.jar differ diff --git a/alg/Patryk_Milewski/info.txt b/alg/Patryk_Milewski/info.txt new file mode 100644 index 0000000..c0c4a25 --- /dev/null +++ b/alg/Patryk_Milewski/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Patryk_Milewski/PatrykMilewski.jar \ No newline at end of file diff --git a/alg/Pawel_Zarczuk/Pawel_Zarczuk.jar b/alg/Pawel_Zarczuk/Pawel_Zarczuk.jar new file mode 100644 index 0000000..1d82afb Binary files /dev/null and b/alg/Pawel_Zarczuk/Pawel_Zarczuk.jar differ diff --git a/alg/Pawel_Zarczuk/info.txt b/alg/Pawel_Zarczuk/info.txt new file mode 100644 index 0000000..c02d231 --- /dev/null +++ b/alg/Pawel_Zarczuk/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Pawel_Zarczuk/Pawel_Zarczuk \ No newline at end of file diff --git "a/alg/Pawe\305\202_Makowski/Pawe\305\202_Makowski.exe" "b/alg/Pawe\305\202_Makowski/Pawe\305\202_Makowski.exe" new file mode 100644 index 0000000..2fd8955 Binary files /dev/null and "b/alg/Pawe\305\202_Makowski/Pawe\305\202_Makowski.exe" differ diff --git "a/alg/Pawe\305\202_Makowski/info.txt" "b/alg/Pawe\305\202_Makowski/info.txt" new file mode 100644 index 0000000..818bee4 --- /dev/null +++ "b/alg/Pawe\305\202_Makowski/info.txt" @@ -0,0 +1,2 @@ +./Programs/Paweł_Makowski/Paweł_Makowski.exe +Paweł Makowski diff --git a/alg/Rafal_Aspras/Algorytm.jar b/alg/Rafal_Aspras/Algorytm.jar new file mode 100644 index 0000000..c837d63 Binary files /dev/null and b/alg/Rafal_Aspras/Algorytm.jar differ diff --git a/alg/Rafal_Aspras/info.txt b/alg/Rafal_Aspras/info.txt new file mode 100644 index 0000000..226f5c8 --- /dev/null +++ b/alg/Rafal_Aspras/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Rafal_Aspras/Algorytm.jar \ No newline at end of file diff --git a/alg/Wiktor_Grochal/info.txt.txt b/alg/Wiktor_Grochal/info.txt.txt new file mode 100644 index 0000000..aa0c5ed --- /dev/null +++ b/alg/Wiktor_Grochal/info.txt.txt @@ -0,0 +1,2 @@ +main.exe +Wiktor Grochal \ No newline at end of file diff --git a/alg/Wiktor_Grochal/main.exe b/alg/Wiktor_Grochal/main.exe new file mode 100644 index 0000000..df98ec2 Binary files /dev/null and b/alg/Wiktor_Grochal/main.exe differ diff --git a/alg/Wojtek_Musiatowicz/Wojtek_Musiatowicz.jar b/alg/Wojtek_Musiatowicz/Wojtek_Musiatowicz.jar new file mode 100644 index 0000000..ad251f8 Binary files /dev/null and b/alg/Wojtek_Musiatowicz/Wojtek_Musiatowicz.jar differ diff --git a/alg/Wojtek_Musiatowicz/info.txt b/alg/Wojtek_Musiatowicz/info.txt new file mode 100644 index 0000000..4f51eef --- /dev/null +++ b/alg/Wojtek_Musiatowicz/info.txt @@ -0,0 +1 @@ +java -jar ./Programs/Wojtek_Musiatowicz/Wojtek_Musiatowicz.jar \ No newline at end of file diff --git a/alg/anna_grenda/bricksbot-1.0-SNAPSHOT.jar b/alg/anna_grenda/bricksbot-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..eece85e Binary files /dev/null and b/alg/anna_grenda/bricksbot-1.0-SNAPSHOT.jar differ diff --git a/alg/anna_grenda/info.txt b/alg/anna_grenda/info.txt new file mode 100644 index 0000000..9ae04ee --- /dev/null +++ b/alg/anna_grenda/info.txt @@ -0,0 +1,2 @@ +java -jar .Programs/Anna_Grenda/bricksbot-1.0-SNAPSHOT.jar 100 16 +Anna Grenda \ No newline at end of file diff --git a/alg/michal_regula/info.txt b/alg/michal_regula/info.txt new file mode 100644 index 0000000..c74ce5d --- /dev/null +++ b/alg/michal_regula/info.txt @@ -0,0 +1,2 @@ +michalregula +michal regula \ No newline at end of file diff --git a/alg/michal_regula/michalregula.exe b/alg/michal_regula/michalregula.exe new file mode 100644 index 0000000..ce07fa7 Binary files /dev/null and b/alg/michal_regula/michalregula.exe differ diff --git a/src/main/java/ttsu/game/Block.java b/src/main/java/ttsu/game/Block.java new file mode 100644 index 0000000..5f1564f --- /dev/null +++ b/src/main/java/ttsu/game/Block.java @@ -0,0 +1,50 @@ +package ttsu.game; + +import java.awt.*; + +public final class Block { + public final Point a; + public final Point b; + + public Block(Point a, Point b) { + this.a = a; + this.b = b; + } + + public boolean hasCommonPoint(Block block) { + return (block.a == a && block.b == b) || (block.a == b && block.b == a); + } + + public boolean isConsistent() { + return a.distance(b) == 1.0; + } + +// public boolean isInsideBoard() { +// return a.x >= 0 && a.x < Main.X && b.x >= 0 && b.y < Main.Y; +// } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Block)) { + return false; + } + Block other = (Block) obj; + return a.equals(other.a) && b.equals(other.b); + } + + @Override + public int hashCode() { + return a.hashCode() + b.hashCode(); + } + + @Override + public String toString() { + return (this.a.x + (Main.COUNT_FROM_ONE ? 1 : 0)) + "x" + + (this.a.y + (Main.COUNT_FROM_ONE ? 1 : 0)) + "_" + + (this.b.x + (Main.COUNT_FROM_ONE ? 1 : 0)) + "x" + + (this.b.y + (Main.COUNT_FROM_ONE ? 1 : 0)); + } +} diff --git a/src/main/java/ttsu/game/Communication.java b/src/main/java/ttsu/game/Communication.java new file mode 100644 index 0000000..44c7eae --- /dev/null +++ b/src/main/java/ttsu/game/Communication.java @@ -0,0 +1,74 @@ +package ttsu.game; + +import ttsu.game.ai.GameIntelligenceAgent; +import ttsu.game.ai.MinimaxAgent; +import ttsu.game.ai.PropabilityAgent; +import ttsu.game.ai.RandomAgent; +import ttsu.game.ai.heuristic.tictactoe.TicTacToeEvaluator; +import ttsu.game.tictactoe.TicTacToeBoardPrinter; +import ttsu.game.tictactoe.TicTacToeGameState; +import ttsu.mrozu.Algorithm; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.awt.*; +import java.util.ArrayList; + +public class Communication { + public static void Communication() throws IOException, InterruptedException { + TicTacToeGameState gameState = null; + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + TicTacToeBoardPrinter boardPrinter = new TicTacToeBoardPrinter(System.out); + GameIntelligenceAgent propabilityAgent = new PropabilityAgent(); + + String input; + int boardSize; + ArrayList excludedPoints; + + input = br.readLine(); + + long startTime = System.nanoTime(); + + boardSize = Parser.parseBoardSize(input); + excludedPoints = Parser.parseExcludedPoints(input); + gameState = new TicTacToeGameState(boardSize, excludedPoints); + + if(Main.DEBUG) { + System.out.println(Watcher.timePassedMs(startTime)); + } + + System.out.println("OK"); + + while (true) { + input = br.readLine(); + + startTime = System.nanoTime(); + + if (input.equals("stop") || input.equals("STOP")) + break; + else { + if (input.equals("start") || input.equals("START")) { + + gameState = propabilityAgent.evaluateNextState(gameState); + System.out.println(gameState.getLastMove().toString()); + + } else { + Block opponentBlock = Parser.parseOpponentMove(input); + + gameState.play(opponentBlock); + gameState.switchPlayer(); + + gameState = propabilityAgent.evaluateNextState(gameState); + System.out.println(gameState.getLastMove().toString()); + } + } + + if (Main.DEBUG) { + boardPrinter.printGameBoard(gameState.getGameBoard()); + System.out.println("Time: " + Watcher.timePassedMs(startTime)); + } + } + } + +} diff --git a/src/main/java/ttsu/game/DiscreteGameState.java b/src/main/java/ttsu/game/DiscreteGameState.java index bf9dfb9..83ea513 100644 --- a/src/main/java/ttsu/game/DiscreteGameState.java +++ b/src/main/java/ttsu/game/DiscreteGameState.java @@ -1,24 +1,14 @@ package ttsu.game; +import ttsu.game.tictactoe.GameBoard; +import ttsu.game.tictactoe.TicTacToeGameState; + import java.util.List; public interface DiscreteGameState { - - /** - * Gets a list of available next states from the current game state. - * - * @return a {@link List} of available {@link DiscreteGameState}s; an empty list when there are no - * available states. This will never be null. - */ List availableStates(); - - /** - * Gets whether this game state represents a terminal state. - * - * @return true if the game is over; false otherwise. - */ + TicTacToeGameState.Player getCurrentPlayer(); + public GameBoard getGameBoard(); + boolean hasWin(TicTacToeGameState.Player p); boolean isOver(); - - // TODO: consider getCurrentPlayer so it doesn't have to be passed into the - // minimax evaluation. } diff --git a/src/main/java/ttsu/game/Main.java b/src/main/java/ttsu/game/Main.java new file mode 100644 index 0000000..b08af2f --- /dev/null +++ b/src/main/java/ttsu/game/Main.java @@ -0,0 +1,19 @@ +package ttsu.game; + +public class Main { + public static int DEFAULT_SIZE = 4; + public static int PROP_DEPTH = 100; + public static boolean COUNT_FROM_ONE = false; + public static boolean DEBUG = false; + public static long TIME_LIMIT = 200; + + public static void main(String[] args) { + + try { + Communication.Communication(); + } catch (Exception e) { + System.err.println("IO Exception"); + } + + } +} diff --git a/src/main/java/ttsu/game/Parser.java b/src/main/java/ttsu/game/Parser.java new file mode 100644 index 0000000..b95d034 --- /dev/null +++ b/src/main/java/ttsu/game/Parser.java @@ -0,0 +1,45 @@ +package ttsu.game; + +import java.awt.*; +import java.util.ArrayList; + +public class Parser { + static int parseBoardSize(String input) { + return Integer.parseInt(input.split("_")[0]); + } + + static Block parseOpponentMove(String input) { + String[] splitted = input.split("_"); + + Point p1,p2; + Block block; + + p1 = new Point( + Integer.parseInt(splitted[0].split("x")[0]) - (Main.COUNT_FROM_ONE ? 1 : 0), + Integer.parseInt(splitted[0].split("x")[1]) - (Main.COUNT_FROM_ONE ? 1 : 0) + ); + p2 = new Point( + Integer.parseInt(splitted[1].split("x")[0]) - (Main.COUNT_FROM_ONE ? 1 : 0), + Integer.parseInt(splitted[1].split("x")[1]) - (Main.COUNT_FROM_ONE ? 1 : 0) + ); + + return new Block(p1,p2); + } + + static ArrayList parseExcludedPoints(String input) { + String[] values = input.split("_"); + ArrayList output = new ArrayList(); + + for (int i = 1; i < values.length; i++) { + String[] numbers = values[i].split("x"); + output.add( + new Point( + Integer.parseInt(numbers[0]) - (Main.COUNT_FROM_ONE ? 1 : 0), + Integer.parseInt(numbers[1]) - (Main.COUNT_FROM_ONE ? 1 : 0) + ) + ); + } + + return output; + } +} diff --git a/src/main/java/ttsu/game/Position.java b/src/main/java/ttsu/game/Position.java deleted file mode 100644 index f1dd8de..0000000 --- a/src/main/java/ttsu/game/Position.java +++ /dev/null @@ -1,55 +0,0 @@ -package ttsu.game; - -/** - * A position on a 2D game board. Positions are represented as a row and column beginning at the top - * left corner. - * - * @author Tim Tsu - * - */ -public final class Position { - private final int row; - private final int col; - - /** - * Creates a position at the given row and column. - * - * @param row - * @param col - */ - public Position(int row, int col) { - this.row = row; - this.col = col; - } - - public int getRow() { - return row; - } - - public int getCol() { - return col; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Position)) { - return false; - } - Position other = (Position) obj; - return row == other.row && col == other.col; - } - - @Override - public int hashCode() { - return row * 3 + col; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("Position: "); - return builder.append('(').append(row).append(',').append(col).append(')').toString(); - } -} diff --git a/src/main/java/ttsu/game/Watcher.java b/src/main/java/ttsu/game/Watcher.java new file mode 100644 index 0000000..ca70a01 --- /dev/null +++ b/src/main/java/ttsu/game/Watcher.java @@ -0,0 +1,7 @@ +package ttsu.game; + +public class Watcher { + public static long timePassedMs(long startTime) { + return (System.nanoTime() - startTime) / 1000000; + } +} diff --git a/src/main/java/ttsu/game/ai/GameIntelligenceAgent.java b/src/main/java/ttsu/game/ai/GameIntelligenceAgent.java index c06a5d3..6c1323c 100644 --- a/src/main/java/ttsu/game/ai/GameIntelligenceAgent.java +++ b/src/main/java/ttsu/game/ai/GameIntelligenceAgent.java @@ -2,32 +2,10 @@ import ttsu.game.DiscreteGameState; -/** - * A game agent for discrete game states that evaluates the next game state from the current game - * state. - * - * @author Tim Tsu - * - * @param the type of {@link DiscreteGameState} for the game that this agent plays - */ public interface GameIntelligenceAgent { - /** - * Returns the game state representing the game after this agent makes its move. - * - * @param currentState the current state of the game - * @return the next game state; or null if there are no more states available - */ - T evaluateNextState(T currentState); + T evaluateNextState(T currentState); - /** - * Returns the game state representing the game after this agent makes its move. Limits the - * evaluation to a specific search depth. - * - * @param currentState the current state of the game - * @param depth the limit to the number of future game states used to evaluate the next move - * @return the next game state; or null if there are no more states available - */ - T evaluateNextState(T currentState, int depth); + T evaluateNextState(T currentState, int depth); } diff --git a/src/main/java/ttsu/game/ai/MinimaxAgent.java b/src/main/java/ttsu/game/ai/MinimaxAgent.java index f197d2f..1dac1eb 100644 --- a/src/main/java/ttsu/game/ai/MinimaxAgent.java +++ b/src/main/java/ttsu/game/ai/MinimaxAgent.java @@ -48,12 +48,10 @@ public MinimaxAgent(StateEvaluator evaluator) { this.evaluator = evaluator; } - @Override public T evaluateNextState(T currentState) { return evaluateNextState(currentState, Integer.MAX_VALUE); } - @Override public T evaluateNextState(T currentState, int depth) { if (currentState == null) { throw new IllegalArgumentException("initialState cannot be null"); diff --git a/src/main/java/ttsu/game/ai/PropabilityAgent.java b/src/main/java/ttsu/game/ai/PropabilityAgent.java new file mode 100644 index 0000000..26a97d3 --- /dev/null +++ b/src/main/java/ttsu/game/ai/PropabilityAgent.java @@ -0,0 +1,111 @@ +package ttsu.game.ai; + +import ttsu.game.DiscreteGameState; +import ttsu.game.Main; +import ttsu.game.Watcher; +import ttsu.game.tictactoe.TicTacToeGameState; + +import java.util.ArrayList; +import java.util.List; + +public class PropabilityAgent implements GameIntelligenceAgent { + private TicTacToeGameState.Player initialPlayer; + private long startTime; + + private static class Node { + private S state; + private int winningStates = 0; + private int allStates = 0; + private List> children; + private PropabilityAgent.Node bestChild; + + public Node(S state) { + this.state = state; + this.bestChild = null; + } + + public double getPropabilityOfWin() throws Exception { + if (allStates == 0) { + throw new Exception("Not defined propabilityOfWin"); + } + return winningStates / allStates; + } + } + + public T evaluateNextState(T currentState) { + return evaluateNextState(currentState, Main.PROP_DEPTH); + } + + public T evaluateNextState(T currentState, int depth) { + initialPlayer = currentState.getCurrentPlayer(); + PropabilityAgent.Node root = null; + + this.startTime = System.nanoTime(); + + try { + root = buildTree(currentState, depth); + } catch (Exception e) { + RandomAgent ra = new RandomAgent(); + return ra.evaluateNextState(currentState); + } + + if (root.bestChild == null) { + RandomAgent ra = new RandomAgent(); + return ra.evaluateNextState(currentState); + } + + return root.bestChild.state; + } + + private PropabilityAgent.Node buildTree(T state, int depth) throws Exception { + PropabilityAgent.Node current = new PropabilityAgent.Node(state); + List availableStates = state.availableStates(); + + if (availableStates.isEmpty()) { + this.setPropabilitiesForLastGame(current); + } else { + ArrayList> children = new ArrayList>(); + + for (DiscreteGameState nextState : availableStates) { + if (depth < 0 || (Watcher.timePassedMs(this.startTime) > Main.TIME_LIMIT && !Main.DEBUG)) { + throw new Exception("too long"); + } +// todo: coƛ dziwnego chyba + + PropabilityAgent.Node child = buildTree((T) nextState, depth - 1); + children.add(child); + + PropabilityAgent.Node bestChild = null; + for (PropabilityAgent.Node c : children) { + current.allStates += c.allStates; + current.winningStates += c.winningStates; + try { + if (bestChild == null || (bestChild.winningStates / bestChild.allStates) < (c.winningStates / c.allStates)) { + bestChild = c; + } + } catch (ArithmeticException e) { + return current; + } + } + current.bestChild = bestChild; + } + + current.children = children; + } + + return current; + } + + private void setPropabilitiesForLastGame(PropabilityAgent.Node current) { + if (current.state.hasWin(this.initialPlayer)) { + current.winningStates = 1; + current.allStates = 1; + current.bestChild = current; + } else { + current.winningStates = 0; + current.allStates = 1; + current.bestChild = null; + } + current.children = null; + } +} diff --git a/src/main/java/ttsu/game/ai/RandomAgent.java b/src/main/java/ttsu/game/ai/RandomAgent.java new file mode 100644 index 0000000..defa96b --- /dev/null +++ b/src/main/java/ttsu/game/ai/RandomAgent.java @@ -0,0 +1,36 @@ +package ttsu.game.ai; + +import ttsu.game.DiscreteGameState; + +import java.util.List; +import java.util.Random; + +public class RandomAgent implements GameIntelligenceAgent { + + public T evaluateNextState(T currentState) { + List availableStates = currentState.availableStates(); + Random random = new Random(); + + int size = availableStates.size(); + if (size == 0) { + return null; + } + int i = size == 1 ? 0 : random.nextInt(size - 1); + + return (T) availableStates.get(i); + } + + public T evaluateNextState(T currentState, int depth) { + return evaluateNextState(currentState); + } + + + private void validate(T state, int depth) { + if (state == null) { + throw new IllegalArgumentException("initialState cannot be null"); + } + if (depth < 0) { + throw new IllegalArgumentException("depth cannot be less than zero. depth=" + depth); + } + } +} diff --git a/src/main/java/ttsu/game/ai/heuristic/StateEvaluator.java b/src/main/java/ttsu/game/ai/heuristic/StateEvaluator.java index ecc4e47..2aba084 100644 --- a/src/main/java/ttsu/game/ai/heuristic/StateEvaluator.java +++ b/src/main/java/ttsu/game/ai/heuristic/StateEvaluator.java @@ -2,20 +2,6 @@ import ttsu.game.DiscreteGameState; -/** - * An evaluator that examines a {@link DiscreteGameState} and calculates a simple heuristic score. - * - * @author Tim Tsu - * - * @param the type of game state that this evaluator examines - */ public interface StateEvaluator { - - /** - * Computes the heuristic score for a given game state. - * - * @param state the {@link DiscreteGameState} to evaluate - * @return an integer score - */ - int evaluate(T state); + int evaluate(T state); } diff --git a/src/main/java/ttsu/game/ai/heuristic/tictactoe/TicTacToeEvaluator.java b/src/main/java/ttsu/game/ai/heuristic/tictactoe/TicTacToeEvaluator.java index c7b0aec..14710d2 100644 --- a/src/main/java/ttsu/game/ai/heuristic/tictactoe/TicTacToeEvaluator.java +++ b/src/main/java/ttsu/game/ai/heuristic/tictactoe/TicTacToeEvaluator.java @@ -1,50 +1,41 @@ package ttsu.game.ai.heuristic.tictactoe; import static ttsu.game.tictactoe.TicTacToeGameState.Player.opponentOf; + import ttsu.game.ai.heuristic.StateEvaluator; import ttsu.game.tictactoe.TicTacToeGameState; import ttsu.game.tictactoe.TicTacToeGameState.Player; -/** - * A {@link StateEvaluator} for a {@link TicTacToeGameState} game state, relative to a particular - * player. - *

- * The score is calculated such that a winning state always has a higher score than a drawn state - * and a drawn state always has a higher score than a losing state. A winning state in less moves - * has a higher score than a winning state in more moves. - *

- * - * @author Tim Tsu - * - */ public class TicTacToeEvaluator implements StateEvaluator { - private final Player player; + private final Player player; - /** - * Crates a new {@link TicTacToeEvaluator} that scores winning states relative to a given player. - * - * @param player a TicTacToe {@link Player}; cannot be null - */ - public TicTacToeEvaluator(Player player) { - if (player == null) { - throw new IllegalArgumentException("player cannot be null"); + public TicTacToeEvaluator(Player player) { + validate(player); + this.player = player; } - this.player = player; - } - @Override - public int evaluate(TicTacToeGameState game) { - if (game == null) { - throw new IllegalArgumentException("cannot evaluate null game"); + public int evaluate(TicTacToeGameState game) { + validate(game); + + if (game.hasWin(player)) { + return game.availableStates().size() + 1; + } else if (game.hasWin(opponentOf(player))) { + return -1; + } else { + return 0; + } } - if (game.hasWin(player)) { - return game.availableStates().size() + 1; - } else if (game.hasWin(opponentOf(player))) { - return -1; - } else { - return 0; + + private void validate(TicTacToeGameState game) { + if (game == null) { + throw new IllegalArgumentException("cannot evaluate null game"); + } } - } + private void validate(Player player) { + if (player == null) { + throw new IllegalArgumentException("player cannot be null"); + } + } } diff --git a/src/main/java/ttsu/game/tictactoe/GameBoard.java b/src/main/java/ttsu/game/tictactoe/GameBoard.java index 71d56db..022d296 100644 --- a/src/main/java/ttsu/game/tictactoe/GameBoard.java +++ b/src/main/java/ttsu/game/tictactoe/GameBoard.java @@ -1,159 +1,179 @@ package ttsu.game.tictactoe; +import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import ttsu.game.Position; +import ttsu.game.Main; +import ttsu.game.Block; +import ttsu.game.Watcher; import ttsu.game.tictactoe.TicTacToeGameState.Player; -/** - * Represents a TicTacToe game board. - * - * @author Tim Tsu - * - */ + public class GameBoard { + private final Player[][] board; + private final int size; - private static final int COLS = 3; - private static final int ROWS = 3; - - private final Player[][] board; - - /** - * Creates a new blank TicTacToe board. - */ - public GameBoard() { - board = new Player[ROWS][COLS]; - } - - /** - * Create a game board from an array. - * - * @param board the {@link Player} array to use as a game board - */ - public GameBoard(Player[][] board) { - if (board == null) { - throw new IllegalArgumentException("board cannot be null"); - } - this.board = board; - } - - /** - * Create a deep copy of another game board. - * - * @param board the board to copy - */ - public GameBoard(GameBoard other) { - board = new Player[ROWS][COLS]; - for (int row = 0; row < ROWS; row++) { - for (int col = 0; col < COLS; col++) { - board[row][col] = other.board[row][col]; - } + public GameBoard(int size) { + board = new Player[size][size]; + this.size = size; } - } - - /** - * Marks a position on the game board for a given player. - * - * @param row the row of the position on the board to mark - * @param col the column of the position on the board to mark - * @param player the {@link Player} marking the board - * @return true if the position was open to mark; false if the position - * was already marked - * @throws IllegalArgumentException if the given position is off the board or the player is - * null - */ - public boolean mark(int row, int col, Player player) { - validatePosition(row, col); - if (player == null) { - throw new IllegalArgumentException("cannot mark null player"); + + public GameBoard(int size, ArrayList excluded) { + board = new Player[size][size]; + this.size = size; + + for (Point point : excluded) { + board[point.x][point.y] = Player.N; + } } - if (board[row][col] != null) { - return false; - } else { - board[row][col] = player; - return true; + + public int getSize() { + return this.size; } - } - - /** - * Gets the mark at the given board position. - * - * @param row the row of the position to inspect - * @param col the column of the position to inspect - * @return the {@link Player} that marked the given position, or null if position is - * open - */ - public Player getMark(int row, int col) { - validatePosition(row, col); - return board[row][col]; - } - - /** - * Gets the list of open positions on the game board. - * - * @return a {@link List} of {@link Position}s; will never be null - */ - public List getOpenPositions() { - ArrayList positions = new ArrayList(); - for (int row = 0; row < ROWS; row++) { - for (int col = 0; col < COLS; col++) { - if (board[row][col] == null) { - positions.add(new Position(row, col)); + + public GameBoard(GameBoard other) { + this.size = other.getSize(); + board = new Player[this.size][this.size]; + for (int row = 0; row < this.size; row++) { + for (int col = 0; col < this.size; col++) { + board[row] = other.board[row].clone(); + } } - } } - return positions; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (int row = 0; row < ROWS; row++) { - for (int col = 0; col < COLS; col++) { - Player p = board[row][col]; - if (p != null) { - sb.append(p); + + public boolean mark(Block b, Player player) { + validateBlock(b); + return mark(b.a, b.b, player); + } + + private boolean mark(Point a, Point b, Player player) { + validateBlock(new Block(a, b)); + + if (player == null) { + throw new IllegalArgumentException("cannot mark null player"); + } + if (board[a.x][a.y] != null && board[b.x][b.y] != null) { + return false; } else { - sb.append(' '); + board[a.x][a.y] = player; + board[b.x][b.y] = player; + + return true; } - } - sb.append('\n'); } - return sb.toString(); - } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; + public Player getMark(Point a) { + validateSinglePosition(a); + return board[a.x][a.y]; } - if (!(obj instanceof GameBoard)) { - return false; + + public List getOpenPositions() { + ArrayList blocks = new ArrayList(); + Point p1, p2; + +// if (this.openPositions != null) { +// return this.openPositions; +// } + + long startTime = System.nanoTime(); + + for (int row = 0; row < this.size; row++) { + for (int col = 0; col < this.size; col++) { + if(Watcher.timePassedMs(startTime) > Main.TIME_LIMIT && blocks.size() != 0) { + return blocks; + } + + if(this.board[row][col] != Player.O && this.board[row][col] != Player.X && this.board[row][col] != Player.N) { + p1 = new Point(row, col); + p2 = new Point(row, col + 1); + + try { + if (validateBlock(new Block(p1, p2)) && getMark(p1) == null && getMark(p2) == null) { + blocks.add(new Block(p1, p2)); + } + } catch (Exception e) { + + } + + try { + p2 = new Point(row + 1, col); + if (validateBlock(new Block(p1, p2)) && getMark(p1) == null && getMark(p2) == null) { + blocks.add(new Block(p1, p2)); + } + } catch (Exception e) { + + } + } + } + } + + if (Main.DEBUG) { +// System.out.println("getOpenPositions GameBoard.java: " + Watcher.timePassedMs(startTime)); + } + +// this.openPositions = blocks; + return blocks; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int row = 0; row < this.size; row++) { + for (int col = 0; col < this.size; col++) { + Player p = board[row][col]; + if (p != null) { + sb.append(p); + } else { + sb.append(' '); + } + } + sb.append('\n'); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GameBoard)) { + return false; + } + GameBoard other = (GameBoard) obj; + for (int row = 0; row < this.size; row++) { + if (!Arrays.equals(board[row], other.board[row])) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + for (int row = 0; row < this.size; row++) { + result = prime * result + Arrays.hashCode(board[row]); + } + return result; } - GameBoard other = (GameBoard) obj; - for (int row = 0; row < ROWS; row++) { - if (!Arrays.equals(board[row], other.board[row])) { - return false; - } + + private void validateSinglePosition(Point a) { + if (a.x < 0 || a.x >= this.size || a.y < 0 || a.y >= this.size) { + throw new IllegalArgumentException("(" + a.x + "," + a.y + ") is off the board"); + } } - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - for (int row = 0; row < ROWS; row++) { - result = prime * result + Arrays.hashCode(board[row]); + + private boolean validateBlock(Block p) { + return p.isConsistent() && p.a.x >= 0 && p.a.x < this.size && p.b.x >= 0 && p.b.y < this.size; } - return result; - } - private static void validatePosition(int row, int col) { - if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { - throw new IllegalArgumentException("(" + row + "," + col + ") is off the board"); + private void validate(Player[][] board) { + if (board == null) { + throw new IllegalArgumentException("board cannot be null"); + } } - } } diff --git a/src/main/java/ttsu/game/tictactoe/TicTacToeBoardPrinter.java b/src/main/java/ttsu/game/tictactoe/TicTacToeBoardPrinter.java index ffd2a27..ea69fab 100644 --- a/src/main/java/ttsu/game/tictactoe/TicTacToeBoardPrinter.java +++ b/src/main/java/ttsu/game/tictactoe/TicTacToeBoardPrinter.java @@ -1,43 +1,58 @@ package ttsu.game.tictactoe; +import java.awt.*; import java.io.PrintStream; +import java.util.ArrayList; import ttsu.game.tictactoe.TicTacToeGameState.Player; -/** - * Prints a TicTacToe game board to the console. - * - * @author Tim Tsu - * - */ public class TicTacToeBoardPrinter { - private PrintStream printStream; - - public TicTacToeBoardPrinter(PrintStream printStream) { - this.printStream = printStream; - } - - /** - * Prints the TicTacToe game board. - * - * @param board the {@link GameBoard} to print; cannot be null - */ - public void printGameBoard(GameBoard board) { - printRow(0, board); - printStream.println("-+-+-"); - printRow(1, board); - printStream.println("-+-+-"); - printRow(2, board); - } - - private void printRow(int row, GameBoard board) { - printStream.printf("%s|%s|%s\n", markToString(board.getMark(row, 0)), - markToString(board.getMark(row, 1)), markToString(board.getMark(row, 2))); - } - - private static String markToString(Player player) { - return player == null ? " " : player.toString(); - } + private PrintStream printStream; + + public TicTacToeBoardPrinter(PrintStream printStream) { + this.printStream = printStream; + } + + public void printGameBoard(GameBoard board) { + StringBuilder str = new StringBuilder(); + + for(int i = 0; i < board.getSize(); i++) { + str.append("-"); + if (i != board.getSize() - 1) { + str.append("+"); + } + } + + for (int i = 0; i < board.getSize(); i++) { + printRow(i, board); + printStream.println(str.toString()); + } + + printStream.println("\n"); + } + + private void printRow(int row, GameBoard board) { + StringBuilder str = new StringBuilder(); + ArrayList output = new ArrayList(); + + for (int i = 0; i < board.getSize(); i++) { + output.add( + markToString(board.getMark(new Point(row, i)))); + str.append("%s"); + + if (i == board.getSize() - 1) { + str.append("\r\n"); + } else { + str.append("|"); + } + } + + printStream.printf(str.toString(), output.toArray()); + } + + private static String markToString(Player player) { + return player == null ? " " : player.toString(); + } } diff --git a/src/main/java/ttsu/game/tictactoe/TicTacToeGameRunner.java b/src/main/java/ttsu/game/tictactoe/TicTacToeGameRunner.java index 69cddf7..5939fdd 100644 --- a/src/main/java/ttsu/game/tictactoe/TicTacToeGameRunner.java +++ b/src/main/java/ttsu/game/tictactoe/TicTacToeGameRunner.java @@ -1,136 +1,67 @@ package ttsu.game.tictactoe; import java.io.PrintStream; -import java.util.Scanner; -import ttsu.game.Position; +import ttsu.game.Block; import ttsu.game.ai.GameIntelligenceAgent; +import ttsu.game.ai.RandomAgent; import ttsu.game.tictactoe.TicTacToeGameState.Player; -/** - * A class for configuring and running a TicTacToe game. - * - * @author Tim Tsu - * - */ public class TicTacToeGameRunner { - - /** - * The instructions for valid human input. - */ - static final String INSTRUCTION_TEXT = - "Enter ',' to play a position. For example, '0,2'."; - - private TicTacToeGameState game; - - private TicTacToeBoardPrinter boardPrinter; - private GameIntelligenceAgent agent; - private Scanner scanner; - private PrintStream printStream; - - /** - * Creates a new game runner. - * - * @param agent a {@link GameIntelligenceAgent} for choosing the computer opponent's moves - * @param scanner a {@link Scanner} for collecting user input - * @param printStream the {@link PrintStream} for displaying text to the user - */ - public TicTacToeGameRunner(GameIntelligenceAgent agent, Scanner scanner, - PrintStream printStream) { - this.game = new TicTacToeGameState(); - this.boardPrinter = new TicTacToeBoardPrinter(printStream); - this.agent = agent; - this.scanner = scanner; - this.printStream = printStream; - } - - /** - * Runs the TicTacToe game, alternating between human and computer moves until the game is over. - */ - public void run() { - printInstructions(); - while (!game.isOver()) { - moveHuman(); - moveComputer(); - boardPrinter.printGameBoard(game.getGameBoard()); + private TicTacToeGameState game; + private TicTacToeBoardPrinter boardPrinter; + private GameIntelligenceAgent propabilityAgent; + private PrintStream printStream; + + public TicTacToeGameRunner(GameIntelligenceAgent propabilityAgent, + PrintStream printStream) { + this.game = new TicTacToeGameState(); + this.boardPrinter = new TicTacToeBoardPrinter(printStream); + this.printStream = printStream; + this.propabilityAgent = propabilityAgent; } - printGameOver(); - } - - /** - * Gets the current game state. - * - * @return the game - */ - TicTacToeGameState getGame() { - return game; - } - void moveComputer() { - TicTacToeGameState nextState = agent.evaluateNextState(game); - if (nextState == null) { - return; + TicTacToeGameState getGame() { + return game; } - Position nextMove = nextState.getLastMove(); - game.play(nextMove.getRow(), nextMove.getCol()); - game.switchPlayer(); - } - void moveHuman() { - Position userPosition; - while (true) { - do { - printStream.print("Player X [row,col]: "); - String input = scanner.nextLine(); - userPosition = parseUserInput(input); - } while (userPosition == null); + public void run() { + printStream.println("Game is starting - randomly by " + game.getCurrentPlayer()); - try { - if (game.play(userPosition.getRow(), userPosition.getCol())) { - game.switchPlayer(); - return; - } else { - printStream.printf("(%d,%d) has already been taken. ", userPosition.getRow(), - userPosition.getCol()); - printInstructions(); + while (!game.isOver()) { + moveRandomlyComputer(); + game.switchPlayer(); +// boardPrinter.printGameBoard(game.getGameBoard()); + + moveRandomlyComputer(); + game.switchPlayer(); +// boardPrinter.printGameBoard(game.getGameBoard()); } - } catch (IllegalArgumentException e) { - printStream.printf("(%d,%d) is not on the board. ", userPosition.getRow(), - userPosition.getCol()); - printInstructions(); - } + printGameOver(); } - } - private void printGameOver() { - if (game.hasWin(Player.X)) { - ((PrintStream) printStream).println("Player X won."); - } else if (game.hasWin(Player.O)) { - printStream.println("Player O won."); - } else { - printStream.println("Game ended in a draw."); + void movePropabilityComputer() { + moveAny(propabilityAgent.evaluateNextState(game)); } - } - - private void printInstructions() { - printStream.println(INSTRUCTION_TEXT); - } - private Position parseUserInput(String input) { - String[] posInput = input.split(","); - if (posInput.length != 2) { - printInstructions(); - return null; + void moveRandomlyComputer() { + RandomAgent randomAgent = new RandomAgent(); + moveAny(randomAgent.evaluateNextState(game)); } - int row, col; - try { - row = Integer.parseInt(posInput[0]); - col = Integer.parseInt(posInput[1]); - } catch (NumberFormatException e) { - printInstructions(); - return null; + + private void moveAny(TicTacToeGameState state) { + if (state == null) { + return; + } + Block nextMove = state.getLastMove(); + game.play(nextMove); } - return new Position(row, col); - } + private void printGameOver() { + if (game.hasWin(Player.X)) { + printStream.println("Player X won."); + } else if (game.hasWin(Player.O)) { + printStream.println("Player O won."); + } + } } diff --git a/src/main/java/ttsu/game/tictactoe/TicTacToeGameState.java b/src/main/java/ttsu/game/tictactoe/TicTacToeGameState.java index a940135..23c34ef 100644 --- a/src/main/java/ttsu/game/tictactoe/TicTacToeGameState.java +++ b/src/main/java/ttsu/game/tictactoe/TicTacToeGameState.java @@ -1,169 +1,120 @@ package ttsu.game.tictactoe; +import java.awt.*; import java.util.ArrayList; import java.util.List; +import ttsu.game.Block; import ttsu.game.DiscreteGameState; -import ttsu.game.Position; - -/** - * A {@link DiscreteGameState} representing the current state of a TicTacToe game. - * - * @author Tim Tsu - * - */ +import ttsu.game.Main; + public class TicTacToeGameState implements DiscreteGameState { - public static enum Player { - O, X; - public static Player opponentOf(Player player) { - return player == X ? O : X; + public static enum Player { + O, X, N; + + public static Player opponentOf(Player player) { + return player == X ? O : X; + } + } + + private final GameBoard board; + private Player currentPlayer; + private Block lastMove; + List availableMoves; + + public TicTacToeGameState() { + board = new GameBoard(Main.DEFAULT_SIZE); + availableMoves = board.getOpenPositions(); + currentPlayer = Player.X; + } + + public TicTacToeGameState(int size, ArrayList excluded) { + board = new GameBoard(size, excluded); + availableMoves = board.getOpenPositions(); + currentPlayer = Player.X; + } + + public TicTacToeGameState(GameBoard board, Player currentPlayer) { + validate(board, currentPlayer); + this.board = board; + availableMoves = board.getOpenPositions(); + this.currentPlayer = currentPlayer; + } + + public TicTacToeGameState(TicTacToeGameState other) { + this.board = new GameBoard(other.board); + this.currentPlayer = other.getCurrentPlayer(); + availableMoves = board.getOpenPositions(); + this.lastMove = other.lastMove; + } + + public List availableStates() { + List availableMoves = board.getOpenPositions(); + List availableStates = new ArrayList(availableMoves.size()); + + for (Block move : availableMoves) { + TicTacToeGameState newState = new TicTacToeGameState(this); + newState.play(move); + newState.switchPlayer(); + availableStates.add(newState); + } + + return availableStates; + } + + public Player getCurrentPlayer() { + return currentPlayer; + } + + public Block getLastMove() { + return lastMove; + } + + public boolean hasWin(Player player) { + List openPositions = board.getOpenPositions(); + if (openPositions.size() == 1) { + return currentPlayer.equals(player); + } else if (openPositions.size() == 2) { + return openPositions.get(0).hasCommonPoint(openPositions.get(1)); + } else if (openPositions.size() == 0) { + return getCurrentPlayer().equals(player); + } + + return false; } - } - - private final GameBoard board; - private Player currentPlayer; - private Position lastMove; - - /** - * Creates the initial state of a new TicTacToe game. - */ - public TicTacToeGameState() { - board = new GameBoard(); - currentPlayer = Player.X; - } - - /** - * Creates a new instance of a TicTacToe game state with a given board layout and current player. - * - * @param board the current board state - * @param currentPlayer the current player whose turn it is to make the next move - */ - public TicTacToeGameState(GameBoard board, Player currentPlayer) { - if (board == null) { - throw new IllegalArgumentException("board cannot be null"); + + public boolean isOver() { + return hasWin(Player.O) || hasWin(Player.X); } - if (currentPlayer == null) { - throw new IllegalArgumentException("currentPlayer cannot be null"); + + private boolean play(Point a, Point b) { + Block block = new Block(a, b); + if (board.mark(block, currentPlayer)) { + lastMove = block; + return true; + } + return false; + } - this.board = board; - this.currentPlayer = currentPlayer; - } - - /** - * Creates a deep copy of the given TicTacToe game state. - * - * @param other the TicTacToe game state to copy - */ - public TicTacToeGameState(TicTacToeGameState other) { - this.board = new GameBoard(other.board); - this.currentPlayer = other.getCurrentPlayer(); - this.lastMove = other.lastMove; - } - - @Override - public List availableStates() { - List availableMoves = board.getOpenPositions(); - List availableStates = - new ArrayList(availableMoves.size()); - for (Position move : availableMoves) { - TicTacToeGameState newState = new TicTacToeGameState(this); - newState.play(move.getRow(), move.getCol()); - newState.switchPlayer(); - availableStates.add(newState); + + private void validate(GameBoard board, Player currentPlayer) { + if (board == null) { + throw new IllegalArgumentException("board cannot be null"); + } + if (currentPlayer == null) { + throw new IllegalArgumentException("currentPlayer cannot be null"); + } } - return availableStates; - } - - /** - * Gets the current player whose turn it is to make the next move. - * - * @return the {@link Player} who gets to make the next move - */ - public Player getCurrentPlayer() { - return currentPlayer; - } - - /** - * Gets the last position that was played on the TicTacToe board. - * - * @return a {@link Position} on the TicTacToe board, or null if no moves were taken yet. - */ - public Position getLastMove() { - return lastMove; - } - - /** - * Returns whether the given player has a winning position on the TicTacToe board. - * - * @param player the player to check for a win - * @return true if player has won; false otherwise - */ - public boolean hasWin(Player player) { - for (int i = 0; i < 3; i++) { - if (completesRow(player, i) || completesColumn(player, i)) { - return true; - } + + public boolean play(Block b) { + return play(b.a, b.b); } - return completesDiagonal(player); - } - - @Override - public boolean isOver() { - return hasWin(Player.O) || hasWin(Player.X) || board.getOpenPositions().isEmpty(); - } - - /** - * Play a move in the given row and column of the TicTacToe board with the current player. - * - * @param row the row to mark - * @param col the column to mark - * @return true if this position was playable; false otherwise - */ - public boolean play(int row, int col) { - if (board.mark(row, col, currentPlayer)) { - lastMove = new Position(row, col); - return true; + + public GameBoard getGameBoard() { + return board; } - return false; - - } - - /** - * Gets the game board. - * - * @return {@link GameBoard} for the current TicTacToe game; cannot be null - */ - public GameBoard getGameBoard() { - return board; - } - - /** - * Switches the current player. - */ - public void switchPlayer() { - currentPlayer = Player.opponentOf(currentPlayer); - } - - private boolean completesColumn(Player player, int col) { - Player col0 = board.getMark(0, col); - Player col1 = board.getMark(1, col); - Player col2 = board.getMark(2, col); - return player == col0 && col0 == col1 && col1 == col2; - } - - private boolean completesDiagonal(Player player) { - Player center = board.getMark(1, 1); - if (player != center) { - return false; + + public void switchPlayer() { + currentPlayer = Player.opponentOf(currentPlayer); } - return (board.getMark(0, 0) == center && center == board.getMark(2, 2)) - || (board.getMark(0, 2) == center && center == board.getMark(2, 0)); - } - - private boolean completesRow(Player player, int row) { - Player row0 = board.getMark(row, 0); - Player row1 = board.getMark(row, 1); - Player row2 = board.getMark(row, 2); - return player == row0 && row0 == row1 && row1 == row2; - } } diff --git a/src/main/java/ttsu/game/tictactoe/TicTacToeMain.java b/src/main/java/ttsu/game/tictactoe/TicTacToeMain.java index 39a8d2f..cd5aca5 100644 --- a/src/main/java/ttsu/game/tictactoe/TicTacToeMain.java +++ b/src/main/java/ttsu/game/tictactoe/TicTacToeMain.java @@ -4,22 +4,17 @@ import ttsu.game.ai.GameIntelligenceAgent; import ttsu.game.ai.MinimaxAgent; +import ttsu.game.ai.PropabilityAgent; import ttsu.game.ai.heuristic.tictactoe.TicTacToeEvaluator; import ttsu.game.tictactoe.TicTacToeGameState.Player; public class TicTacToeMain { + public static void main(String[] args) { + GameIntelligenceAgent propabilityAgent = new PropabilityAgent(); - /** - * @param args - */ - public static void main(String[] args) { - TicTacToeEvaluator evaluator = new TicTacToeEvaluator(Player.O); - GameIntelligenceAgent agent = - new MinimaxAgent(evaluator); - Scanner scanner = new Scanner(System.in); - TicTacToeGameRunner game = new TicTacToeGameRunner(agent, scanner, System.out); + TicTacToeGameRunner game = new TicTacToeGameRunner(propabilityAgent, System.out); - game.run(); - } + game.run(); + } } diff --git a/src/main/java/ttsu/mrozu/Algorithm.java b/src/main/java/ttsu/mrozu/Algorithm.java new file mode 100644 index 0000000..d3fb085 --- /dev/null +++ b/src/main/java/ttsu/mrozu/Algorithm.java @@ -0,0 +1,156 @@ +package ttsu.mrozu; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class Algorithm { + private int gridSize; + private ArrayList gridArray; + + public Algorithm(int gS) { + gridSize = gS; + gridArray = new ArrayList(); + } + + public void add(int a, int b, int c, int d) { + gridArray.add(new Point(a, b)); + gridArray.add(new Point(c, d)); + + ArrayList validMoves = validMovesLeft(gridArray); + int min = 257542; + int x = 0; + + ArrayList temp = new ArrayList(); + + if (validMoves.size() == 1) { + gridArray.add(validMoves.get(0).a); + gridArray.add(validMoves.get(0).b); + System.out.println(validMoves.get(0).a.x + " " + validMoves.get(0).a.y + " " + validMoves.get(0).b.x + " " +validMoves.get(0).b.y); + } else { + for (int i = 0; i < validMoves.size(); i++) { + temp.addAll(gridArray); + temp.add(validMoves.get(i).a); + temp.add(validMoves.get(i).b); + + if (validMovesLeft(temp).size() % 2 == 0 /*&& validMovesLeft(temp).size() < min*/) { + x = i; + //min = validMovesLeft(temp).size(); + } + } + + /*if (x == -1) + for (int i = 0; i < validMoves.size(); i++) { + temp.addAll(gridArray); + temp.add(validMoves.get(i).a); + temp.add(validMoves.get(i).b); + + if (validMovesLeft(temp).size() % 2 == 1) { + x = i; + min = validMovesLeft(temp).size(); + } + }*/ + + gridArray.add(validMoves.get(x).a); + gridArray.add(validMoves.get(x).b); + System.out.println(validMoves.get(x).a.x + " " + validMoves.get(x).a.y + " " + validMoves.get(x).b.x + " " + validMoves.get(x).b.y); + } + } + + private ArrayList validMovesLeft(ArrayList list) { + ArrayList validMovesList = new ArrayList(); + Point middle, top, bottom, left, right; + + for (int i = 1; i < gridSize; i++) { + for (int j = 1; j < gridSize; j++) { + if (isValid(middle = new Point(i, j), list)) { + if (isValid(top = new Point(middle.x, middle.y + 1), list)) + validMovesList.add(new Block(middle, top)); + if (isValid(bottom = new Point(middle.x, middle.y - 1), list)) + validMovesList.add(new Block(middle, bottom)); + if (isValid(left = new Point(middle.x - 1, middle.y), list)) + validMovesList.add(new Block(middle, left)); + if (isValid(right = new Point(middle.x + 1, middle.y), list)) + validMovesList.add(new Block(middle, right)); + } + } + } + + Set hs = new HashSet(); + hs.addAll(validMovesList); + validMovesList.clear(); + validMovesList.addAll(hs); + + return validMovesList; + } + + private boolean isValid(Point point, ArrayList list){ + if(point.x > gridSize || point.y > gridSize || point.x < 1 || point.y < 1) + return false; + + for(Point temp : gridArray) + if (temp.equals(point)) + return false; + + return true; + } + + private class Block { + private Point a; + private Point b; + + private Block(Point a, Point b){ + this.a = a; + this.b = b; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Block block = (Block) o; + + if ((a.equals(block.a) || a.equals(block.b)) && (b.equals(block.a) || b.equals(block.b))) return true; + return false; + + } + + @Override + public String toString() { + return "Block{" + + "a=" + a + + ", b=" + b + + '}' + '\n'; + } + } + + private class Point { + private int x; + private int y; + + private Point(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public String toString() { + return "Point{" + + "x=" + x + + ", y=" + y + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Point point = (Point) o; + + if (x != point.x) return false; + return y == point.y; + } + } +} \ No newline at end of file diff --git a/src/main/java/ttsu/mrozu/Block.java b/src/main/java/ttsu/mrozu/Block.java new file mode 100644 index 0000000..3402a8d --- /dev/null +++ b/src/main/java/ttsu/mrozu/Block.java @@ -0,0 +1,27 @@ +package ttsu.mrozu; + +public class Block { + private int x; + private int y; + + private Block(int x, int y){ + this.x = x; + this.y = y; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } +} \ No newline at end of file diff --git a/src/main/java/ttsu/mrozu/Communication.java b/src/main/java/ttsu/mrozu/Communication.java new file mode 100644 index 0000000..4f986d6 --- /dev/null +++ b/src/main/java/ttsu/mrozu/Communication.java @@ -0,0 +1,33 @@ +package ttsu.mrozu; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Communication { + public static void main(String[] args) throws IOException { + Algorithm algorithm = null; + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + String input; + br.readLine(); + System.out.println("PONG"); + while(true){ + input = br.readLine(); + if(input.equals("PRZEGRAÁEÚ") || input.equals("WYGRAÁEÚ")) + break; + else { + String[] values = input.split(" "); + + if(input.equals("ZACZYNAJ")) + algorithm.add(0,0,0,0); + else if(values.length == 1) + algorithm = new Algorithm(Integer.parseInt(values[0])); + else if(values.length == 4) + algorithm.add(Integer.parseInt(values[0]), + Integer.parseInt(values[1]), + Integer.parseInt(values[2]), + Integer.parseInt(values[3])); + } + } + } +} diff --git a/src/test/java/ttsu/game/tictactoe/GameBoardTest.java b/src/test/java/ttsu/game/tictactoe/GameBoardTest.java index 945de38..cbd421e 100644 --- a/src/test/java/ttsu/game/tictactoe/GameBoardTest.java +++ b/src/test/java/ttsu/game/tictactoe/GameBoardTest.java @@ -7,102 +7,204 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import ttsu.game.Position; +import ttsu.game.Block; import ttsu.game.tictactoe.GameBoard; import ttsu.game.tictactoe.TicTacToeGameState.Player; +import java.awt.*; +import java.util.ArrayList; + public class GameBoardTest { - private GameBoard board; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void setup() { - board = new GameBoard(); - } - - // -- constructor - - @Test - public void copyConstructor() { - board.mark(0, 0, Player.X); - GameBoard newBoard = new GameBoard(board); - assertThat(newBoard.getMark(0, 0)).isEqualTo(Player.X); - - newBoard.mark(1, 1, Player.O); - assertThat(board.getMark(1, 1)).isNotEqualTo(Player.X); - } - - // -- getMark - @Test - public void getMarkUnmarked() { - assertThat(board.getMark(0, 0)).isNull(); - } - - @Test - public void getMarkOffBoard() { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("(3,0) is off the board"); - board.getMark(3, 0); - } - - @Test - public void getMarkOffBoardNegative() { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("(-1,0) is off the board"); - board.getMark(-1, 0); - } - - // -- mark - - @Test - public void markOnBoard() { - boolean success = board.mark(0, 0, Player.O); - - assertThat(success).isTrue(); - assertThat(board.getMark(0, 0)).isEqualTo(Player.O); - } - - @Test - public void markTwice() { - board.mark(0, 0, Player.O); - boolean success = board.mark(0, 0, Player.X); - - assertThat(success).isFalse(); - assertThat(board.getMark(0, 0)).isEqualTo(Player.O); - } - - @Test - public void markNull() { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("cannot mark null player"); - board.mark(0, 0, null); - } - - @Test - public void markOffBoard() { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("(3,0) is off the board"); - board.mark(3, 0, null); - } - - // -- getOpenPositions - - @Test - public void getOpenPositionsAll() { - assertThat(board.getOpenPositions()).containsOnly(new Position(0, 0), new Position(0, 1), - new Position(0, 2), new Position(1, 0), new Position(1, 1), new Position(1, 2), - new Position(2, 0), new Position(2, 1), new Position(2, 2)); - } - - @Test - public void getOpenPositions() { - board.mark(0, 0, Player.X); - assertThat(board.getOpenPositions()).containsOnly(new Position(0, 1), new Position(0, 2), - new Position(1, 0), new Position(1, 1), new Position(1, 2), new Position(2, 0), - new Position(2, 1), new Position(2, 2)); - } + private GameBoard board; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setup() { + board = new GameBoard(3); + } + + @Test + public void shouldBeGood() { + // Arrange + ArrayList except = new ArrayList(); + except.add(new Point(2,2)); + except.add(new Point(2,3)); + except.add(new Point(3,1)); + except.add(new Point(3,2)); + except.add(new Point(0,1)); + except.add(new Point(0,0)); + except.add(new Point(1,0)); + except.add(new Point(2,0)); + + board = new GameBoard(4, except); + + // Act && Assert + assertThat(board.getOpenPositions()).containsOnly( + new Block(new Point(0,2), new Point(0,2)) +// new Block(new Point(0,2), new Point(1,2)), +// new Block(new Point(1,0), new Point(2,0)), +// new Block(new Point(2,0), new Point(2,1)) + ); + } + + @Test + public void Should_Return_Without_Exception_Points() { + // Arrange + ArrayList except = new ArrayList(); + except.add(new Point(0,0)); + except.add(new Point(1,1)); + except.add(new Point(2,2)); + + board = new GameBoard(3, except); + + // Act && Assert + assertThat(board.getOpenPositions()).containsOnly( + new Block(new Point(0,1), new Point(0,2)), + new Block(new Point(0,2), new Point(1,2)), + new Block(new Point(1,0), new Point(2,0)), + new Block(new Point(2,0), new Point(2,1)) + ); + } + + @Test + public void Should_Return_Without_Exception_Points_2() { + // Arrange + ArrayList except = new ArrayList(); + except.add(new Point(0,0)); + except.add(new Point(1,1)); + + board = new GameBoard(3, except); + + // Act && Assert + assertThat(board.getOpenPositions()).containsOnly( + new Block(new Point(0,1), new Point(0,2)), + new Block(new Point(0,2), new Point(1,2)), + new Block(new Point(1,0), new Point(2,0)), + new Block(new Point(2,0), new Point(2,1)), + new Block(new Point(1,2), new Point(2,2)), + new Block(new Point(2,1), new Point(2,2)) + ); + } + + // -- constructor + + @Test + public void copyConstructor() { + Point p1, p2, p3, p4; + p1 = new Point(0, 0); + p2 = new Point(0, 1); + p3 = new Point(1, 0); + p4 = new Point(1, 1); + + board.mark(new Block(p1, p2), Player.X); + GameBoard newBoard = new GameBoard(board); + + assertThat(newBoard.getMark(p1)).isEqualTo(Player.X); + assertThat(newBoard.getMark(p2)).isEqualTo(Player.X); + + newBoard.mark(new Block(p3, p4), Player.O); + + assertThat(board.getMark(p3)).isNotEqualTo(Player.X); + assertThat(board.getMark(p4)).isNotEqualTo(Player.X); + } + + // -- getMark + @Test + public void getMarkUnmarked() { + assertThat(board.getMark(new Point(0, 0))).isNull(); + } + + @Test + public void getMarkOffBoard() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("(3,3) is off the board"); + board.getMark(new Point(3, 3)); + } + + @Test + public void getMarkOffBoardNegative() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("(-1,0) is off the board"); + board.getMark(new Point(-1, 0)); + } + + // -- mark + + @Test + public void markOnBoard() { + Point p1, p2; + p1 = new Point(0, 0); + p2 = new Point(0, 1); + boolean success = board.mark(new Block(p1, p2), Player.O); + + assertThat(success).isTrue(); + assertThat(board.getMark(p1)).isEqualTo(Player.O); + assertThat(board.getMark(p2)).isEqualTo(Player.O); + } + + @Test + public void markTwice() { + Point p1, p2; + p1 = new Point(0, 0); + p2 = new Point(0, 1); + board.mark(new Block(p1, p2), Player.O); + boolean success = board.mark(new Block(p1, p2), Player.X); + + assertThat(success).isFalse(); + assertThat(board.getMark(p1)).isEqualTo(Player.O); + assertThat(board.getMark(p2)).isEqualTo(Player.O); + } + + @Test + public void markNull() { + Point p1, p2; + p1 = new Point(0, 0); + p2 = new Point(0, 1); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("cannot mark null player"); + board.mark(new Block(p1, p2), null); + } + +// // -- getOpenPositions +// + @Test + public void getOpenPositionsAll() { + Point p1, p2, p3, p4, p5, p6, p7, p8, p9; + p1 = new Point(0, 0); + p2 = new Point(1, 0); + p3 = new Point(2, 0); + p4 = new Point(0, 1); + p5 = new Point(1, 1); + p6 = new Point(2, 1); + p7 = new Point(0, 2); + p8 = new Point(1, 2); + p9 = new Point(2, 2); + + assertThat(board.getOpenPositions()).containsOnly( + new Block(p1, p2), + new Block(p2, p3), + new Block(p4, p5), + new Block(p5, p6), + new Block(p7, p8), + new Block(p8, p9), + new Block(p1, p4), + new Block(p2, p5), + new Block(p3, p6), + new Block(p4, p7), + new Block(p5, p8), + new Block(p6, p9) + ); + } + +// @Test +// public void getOpenPositions() { +// board.mark(0, 0, Player.X); +// assertThat(board.getOpenPositions()).containsOnly(new Block(0, 1), new Block(0, 2), +// new Block(1, 0), new Block(1, 1), new Block(1, 2), new Block(2, 0), +// new Block(2, 1), new Block(2, 2)); +// } } diff --git a/src/test/java/ttsu/game/tictactoe/ParserTest.java b/src/test/java/ttsu/game/tictactoe/ParserTest.java new file mode 100644 index 0000000..b0f2296 --- /dev/null +++ b/src/test/java/ttsu/game/tictactoe/ParserTest.java @@ -0,0 +1,34 @@ +package ttsu.game.tictactoe; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import ttsu.game.Block; +import ttsu.game.tictactoe.TicTacToeGameState.Player; + +import java.awt.*; + +import static org.fest.assertions.Assertions.assertThat; + + +public class ParserTest { + + private GameBoard board; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setup() { + board = new GameBoard(); + } + +// @Test +// public void getOpenPositions() { +// board.mark(0, 0, Player.X); +// assertThat(board.getOpenPositions()).containsOnly(new Block(0, 1), new Block(0, 2), +// new Block(1, 0), new Block(1, 1), new Block(1, 2), new Block(2, 0), +// new Block(2, 1), new Block(2, 2)); +// } +} diff --git a/src/test/java/ttsu/game/tictactoe/TicTacToeBoardPrinterTest.java b/src/test/java/ttsu/game/tictactoe/TicTacToeBoardPrinterTest.java deleted file mode 100644 index 90bb78b..0000000 --- a/src/test/java/ttsu/game/tictactoe/TicTacToeBoardPrinterTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package ttsu.game.tictactoe; - -import static org.mockito.Mockito.inOrder; -import static ttsu.game.tictactoe.TicTacToeGameState.Player.O; -import static ttsu.game.tictactoe.TicTacToeGameState.Player.X; - -import java.io.PrintStream; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import ttsu.game.tictactoe.TicTacToeGameState.Player; - -@RunWith(MockitoJUnitRunner.class) -public class TicTacToeBoardPrinterTest { - private TicTacToeBoardPrinter printer; - @Mock - private PrintStream printStream; - - @Before - public void setup() { - printer = new TicTacToeBoardPrinter(printStream); - } - - @Test - public void printGameBoardEmpty() { - GameBoard board = new GameBoard(); - - printer.printGameBoard(board); - - InOrder inOrder = inOrder(printStream); - inOrder.verify(printStream).printf("%s|%s|%s\n", " ", " ", " "); - inOrder.verify(printStream).println("-+-+-"); - inOrder.verify(printStream).printf("%s|%s|%s\n", " ", " ", " "); - inOrder.verify(printStream).println("-+-+-"); - inOrder.verify(printStream).printf("%s|%s|%s\n", " ", " ", " "); - } - - @Test - public void printGameBoard() { - GameBoard board = new GameBoard(new Player[][] { {O, X, O}, {X, null, O}, {X, O, X}}); - - printer.printGameBoard(board); - - InOrder inOrder = inOrder(printStream); - inOrder.verify(printStream).printf("%s|%s|%s\n", "O", "X", "O"); - inOrder.verify(printStream).println("-+-+-"); - inOrder.verify(printStream).printf("%s|%s|%s\n", "X", " ", "O"); - inOrder.verify(printStream).println("-+-+-"); - inOrder.verify(printStream).printf("%s|%s|%s\n", "X", "O", "X"); - } -} diff --git a/src/test/java/ttsu/game/tictactoe/TicTacToeGameRunnerTest.java b/src/test/java/ttsu/game/tictactoe/TicTacToeGameRunnerTest.java deleted file mode 100644 index eda4c54..0000000 --- a/src/test/java/ttsu/game/tictactoe/TicTacToeGameRunnerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package ttsu.game.tictactoe; - -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.util.Strings.join; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.PrintStream; -import java.util.Scanner; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; - -import ttsu.game.Position; -import ttsu.game.ai.GameIntelligenceAgent; -import ttsu.game.tictactoe.TicTacToeGameState.Player; - -@RunWith(MockitoJUnitRunner.class) -public class TicTacToeGameRunnerTest { - - String line = System.getProperty("line.separator"); - - @Mock - private GameIntelligenceAgent agent; - @Mock - private PrintStream printStream; - - @Test - public void moveHumanContinuesToAcceptInputUntilValid() { - Scanner scanner = scannerWithInputs("", " 1, 1", "invalid", "-1,1", "3,1", "1,2,3", "0,0"); - TicTacToeGameRunner runner = new TicTacToeGameRunner(agent, scanner, printStream); - - runner.moveHuman(); - - verify(printStream, times(6)).println(TicTacToeGameRunner.INSTRUCTION_TEXT); - } - - @Test - public void moveHumanErrorWhenOffBoard() { - Scanner scanner = scannerWithInputs("-1,0", "3,3", "0,0"); - TicTacToeGameRunner runner = new TicTacToeGameRunner(agent, scanner, printStream); - - runner.moveHuman(); - - verify(printStream).printf("(%d,%d) is not on the board. ", -1, 0); - verify(printStream).printf("(%d,%d) is not on the board. ", 3, 3); - verify(printStream, times(2)).println(TicTacToeGameRunner.INSTRUCTION_TEXT); - } - - @Test - public void moveHumanErrorWhenRepeatMove() { - Scanner scanner = scannerWithInputs("1,1", "1,1", "0,0"); - TicTacToeGameRunner runner = new TicTacToeGameRunner(agent, scanner, printStream); - - runner.moveHuman(); - runner.moveHuman(); - - verify(printStream).printf("(%d,%d) has already been taken. ", 1, 1); - verify(printStream).println(TicTacToeGameRunner.INSTRUCTION_TEXT); - } - - @Test - public void moveHumanSwitchesPlayers() { - Scanner scanner = scannerWithInputs("1,1", "0,0"); - TicTacToeGameRunner runner = new TicTacToeGameRunner(agent, scanner, printStream); - - assertThat(runner.getGame().getCurrentPlayer()).isEqualTo(Player.X); - runner.moveHuman(); - assertThat(runner.getGame().getCurrentPlayer()).isEqualTo(Player.O); - } - - @Test - public void moveComputerSwitchesPlayers() { - TicTacToeGameRunner runner = new TicTacToeGameRunner(agent, new Scanner(""), printStream); - TicTacToeGameState nextState = mock(TicTacToeGameState.class); - when(nextState.getLastMove()).thenReturn(new Position(0, 0)); - when(agent.evaluateNextState(Mockito.any(TicTacToeGameState.class))).thenReturn(nextState); - - assertThat(runner.getGame().getCurrentPlayer()).isEqualTo(Player.X); - runner.moveComputer(); - assertThat(runner.getGame().getCurrentPlayer()).isEqualTo(Player.O); - } - - // -- helper -- - - private Scanner scannerWithInputs(String... inputs) { - String joinedInputs = join(inputs).with("\n"); - Scanner scanner = new Scanner(joinedInputs); - return scanner; - } -} diff --git a/src/test/java/ttsu/game/tictactoe/TicTacToeGameStateTest.java b/src/test/java/ttsu/game/tictactoe/TicTacToeGameStateTest.java deleted file mode 100644 index 130ac5b..0000000 --- a/src/test/java/ttsu/game/tictactoe/TicTacToeGameStateTest.java +++ /dev/null @@ -1,179 +0,0 @@ -package ttsu.game.tictactoe; - -import static org.fest.assertions.Assertions.assertThat; - -import java.util.List; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import ttsu.game.DiscreteGameState; -import ttsu.game.Position; -import ttsu.game.tictactoe.TicTacToeGameState.Player; - - -public class TicTacToeGameStateTest { - - private TicTacToeGameState game; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void setup() { - game = new TicTacToeGameState(); - } - - // -- constructor - - @Test - public void startingPlayerIsX() { - assertThat(new TicTacToeGameState().getCurrentPlayer()).isEqualTo(Player.X); - } - - @Test - public void copyConstructorDeepCopiesBoard() { - game.play(0, 0); - TicTacToeGameState copy = new TicTacToeGameState(game); - assertThat(copy.getGameBoard()).isEqualTo(game.getGameBoard()).isNotSameAs(game.getGameBoard()); - assertThat(copy.getLastMove()).isEqualTo(game.getLastMove()); - assertThat(copy.getCurrentPlayer()).isEqualTo(game.getCurrentPlayer()); - } - - // -- availableStates - - @Test - public void getAvaliableStatesEmptyBoard() { - TicTacToeGameState game = new TicTacToeGameState(); - List states = game.availableStates(); - assertThat(states).hasSize(9); - } - - @Test - public void getAvailableStatesLastOne() { - TicTacToeGameState game = new TicTacToeGameState(); - game.play(0, 0); - game.play(0, 1); - game.play(0, 2); - game.play(1, 0); - game.play(1, 1); - game.play(1, 2); - game.play(2, 0); - game.play(2, 1); - - List states = game.availableStates(); - assertThat(states).hasSize(1); - TicTacToeGameState availableState = (TicTacToeGameState) states.get(0); - assertThat(availableState.getCurrentPlayer()).isEqualTo( - Player.opponentOf(game.getCurrentPlayer())); - assertThat(availableState.getLastMove()).isEqualTo(new Position(2, 2)); - } - - @Test - public void getAvailableStatesCompleteBoard() { - TicTacToeGameState game = new TicTacToeGameState(); - game.play(0, 0); - game.play(0, 1); - game.play(0, 2); - game.play(1, 0); - game.play(1, 1); - game.play(1, 2); - game.play(2, 0); - game.play(2, 1); - game.play(2, 2); - - assertThat(game.availableStates()).isEmpty(); - } - - // -- hasWin - - @Test - public void hasWinRow() { - game.play(0, 0); - game.play(0, 1); - game.play(0, 2); - assertThat(game.hasWin(Player.X)); - } - - @Test - public void hasWinCol() { - game.play(0, 0); - game.play(1, 0); - game.play(2, 0); - assertThat(game.hasWin(Player.X)); - } - - @Test - public void hasWinDiagonal() { - game.play(0, 0); - game.play(1, 1); - game.play(2, 2); - assertThat(game.hasWin(Player.X)); - } - - // -- isOver - - @Test - public void isOverWin() { - game.play(0, 0); - game.play(0, 1); - game.play(0, 2); - assertThat(game.isOver()).isTrue(); - } - - @Test - public void isOverDraw() { - // XOX - // OXX - // OXO - game.play(0, 0); - game.play(0, 2); - game.play(1, 1); - game.play(1, 2); - game.play(2, 1); - game.switchPlayer(); - game.play(0, 1); - game.play(1, 0); - game.play(2, 0); - game.play(2, 2); - - assertThat(game.isOver()).isTrue(); - } - - // -- play - - @Test - public void playOnBoard() { - assertThat(game.play(0, 0)).isTrue(); - assertThat(game.getLastMove()).isEqualTo(new Position(0, 0)); - } - - @Test - public void playOffBoard() { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("(-1,0) is off the board"); - game.play(-1, 0); - } - - @Test - public void playSameLocation() { - assertThat(game.play(0, 0)).isTrue(); - assertThat(game.play(0, 1)).isTrue(); - // should not affect the last move - assertThat(game.play(0, 0)).isFalse(); - assertThat(game.getLastMove()).isEqualTo(new Position(0, 1)); - } - - // -- switchPlayer - - @Test - public void switchPlayer() { - assertThat(game.getCurrentPlayer()).isEqualTo(Player.X); - game.switchPlayer(); - assertThat(game.getCurrentPlayer()).isEqualTo(Player.O); - game.switchPlayer(); - assertThat(game.getCurrentPlayer()).isEqualTo(Player.X); - } -}