From 9087c4a8d7b34822f22c2f851f13832345f54c54 Mon Sep 17 00:00:00 2001 From: Gregory Martin Date: Wed, 14 Mar 2018 14:37:14 +0100 Subject: [PATCH] AI: Working MCTS --- .gitignore | 53 ++++ BreakMCTS.iml | 26 ++ pom.xml | 33 +++ .../java/controller/BreakMCTSApplication.java | 16 ++ src/main/java/controller/MainView.java | 139 ++++++++++ src/main/java/model/ai/AI.java | 9 + src/main/java/model/ai/mcts/MCTS.java | 28 ++ src/main/java/model/ai/mcts/MCTSTree.java | 188 +++++++++++++ src/main/java/model/ai/random/Random.java | 27 ++ src/main/java/model/board/Board.java | 261 ++++++++++++++++++ src/main/java/model/board/Move.java | 26 ++ src/main/java/model/board/Position.java | 31 +++ .../java/model/board/utils/PawnColor.java | 16 ++ src/main/java/model/board/utils/Tile.java | 21 ++ src/main/resources/fxml/mainView.fxml | 62 +++++ src/test/java/MainTest.java | 40 +++ 16 files changed, 976 insertions(+) create mode 100644 .gitignore create mode 100644 BreakMCTS.iml create mode 100644 pom.xml create mode 100644 src/main/java/controller/BreakMCTSApplication.java create mode 100644 src/main/java/controller/MainView.java create mode 100644 src/main/java/model/ai/AI.java create mode 100644 src/main/java/model/ai/mcts/MCTS.java create mode 100644 src/main/java/model/ai/mcts/MCTSTree.java create mode 100644 src/main/java/model/ai/random/Random.java create mode 100644 src/main/java/model/board/Board.java create mode 100644 src/main/java/model/board/Move.java create mode 100644 src/main/java/model/board/Position.java create mode 100644 src/main/java/model/board/utils/PawnColor.java create mode 100644 src/main/java/model/board/utils/Tile.java create mode 100644 src/main/resources/fxml/mainView.fxml create mode 100644 src/test/java/MainTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5a5194 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# To cover other IntelliJ folders&files +.idea/ +target/ + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/BreakMCTS.iml b/BreakMCTS.iml new file mode 100644 index 0000000..b3fb53b --- /dev/null +++ b/BreakMCTS.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ae8de68 --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + groupId + BreakMCTS + 1.0-SNAPSHOT + + jar + + + + org.jetbrains + annotations + RELEASE + + + + + + + maven-compiler-plugin + + 1.9 + 1.9 + + + + + + \ No newline at end of file diff --git a/src/main/java/controller/BreakMCTSApplication.java b/src/main/java/controller/BreakMCTSApplication.java new file mode 100644 index 0000000..27c3e7a --- /dev/null +++ b/src/main/java/controller/BreakMCTSApplication.java @@ -0,0 +1,16 @@ +package controller; + +import javafx.application.Application; +import javafx.stage.Stage; + +public class BreakMCTSApplication extends Application { + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) throws Exception { + new MainView(primaryStage); + } +} diff --git a/src/main/java/controller/MainView.java b/src/main/java/controller/MainView.java new file mode 100644 index 0000000..ff43842 --- /dev/null +++ b/src/main/java/controller/MainView.java @@ -0,0 +1,139 @@ +package controller; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.stage.Stage; +import model.ai.AI; +import model.ai.mcts.MCTS; +import model.ai.random.Random; +import model.board.Board; +import model.board.Move; +import model.board.Position; +import model.board.utils.PawnColor; + +import java.io.IOException; +import java.util.List; + +public class MainView { + + @FXML private VBox vbox; + @FXML private GridPane gridpane; + + private Pane[][] tiles = new Pane[Board.SIZEX][Board.SIZEY]; + + private Board board = new Board(); + private PawnColor playerColor = PawnColor.BLACK; + private PawnColor aiColor = PawnColor.WHITE; + + private Stage mainStage; + + public MainView(Stage mainStage){ + this.mainStage = mainStage; + Border borderPane = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, new CornerRadii(0), new BorderWidths(1))); + + for (int i = 0; i < tiles.length; i++) { + for (int j = 0; j < tiles[i].length; j++) { + Pane pane = new Pane(); + pane.setBorder(borderPane); + pane.setStyle("-fx-background-color: grey"); + + tiles[i][j] = pane; + } + } + + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/mainView.fxml")); + fxmlLoader.setController(this); + + try { + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @FXML + public void initialize(){ + + for (int i = 0; i < tiles.length; i++) { + for (int j = 0; j < tiles[i].length; j++) { + Pane tile = tiles[i][j]; + + gridpane.add(tile, j, i); + tile.prefHeightProperty().bind(gridpane.getRowConstraints().get(0).prefHeightProperty()); + tile.prefWidthProperty().bind(gridpane.getColumnConstraints().get(0).prefWidthProperty()); + } + } + + fillPawns(); + + mainStage.setScene(new Scene(vbox)); + mainStage.setTitle("BreakMCTS"); + mainStage.show(); + + Thread thread = new Thread(this::play); + thread.setDaemon(true); + thread.start(); + } + + private void fillPawns(){ + List blackPawns = board.getBlacks(); + List whitePawns = board.getWhites(); + + for (Pane[] rowTile : tiles) { + for (Pane tile : rowTile) { + Platform.runLater(() -> tile.getChildren().clear()); + } + } + + blackPawns.forEach(position -> { + Circle pawn = new Circle(10, Color.BLACK); + Pane tile = tiles[position.x][position.y]; + + pawn.setCenterX(tile.getPrefWidth()/2); + pawn.setCenterY(tile.getPrefHeight()/2); + Platform.runLater(() -> tile.getChildren().add(pawn)); + }); + + whitePawns.forEach(position -> { + Circle pawn = new Circle(10, Color.WHITE); + Pane tile = tiles[position.x][position.y]; + + pawn.setCenterX(tile.getPrefWidth()/2); + pawn.setCenterY(tile.getPrefHeight()/2); + Platform.runLater(() -> tile.getChildren().add(pawn)); + }); + + } + + public void play(){ + AI aiWhite = new MCTS(PawnColor.WHITE); + AI aiBlack = new MCTS(PawnColor.BLACK); + + + while (!board.isFinished()){ + Move aiMove = aiWhite.getAIMove(board); + board.movePawn(aiMove); + fillPawns(); + + if(board.isFinished()){ + break; + } + + Move aiRandomMove = aiBlack.getAIMove(board); + board.movePawn(aiRandomMove); + fillPawns(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + System.err.println("Finished"); + } +} diff --git a/src/main/java/model/ai/AI.java b/src/main/java/model/ai/AI.java new file mode 100644 index 0000000..3b128e0 --- /dev/null +++ b/src/main/java/model/ai/AI.java @@ -0,0 +1,9 @@ +package model.ai; + +import model.board.Board; +import model.board.Move; + +public interface AI { + + Move getAIMove(Board board); +} diff --git a/src/main/java/model/ai/mcts/MCTS.java b/src/main/java/model/ai/mcts/MCTS.java new file mode 100644 index 0000000..0957ebc --- /dev/null +++ b/src/main/java/model/ai/mcts/MCTS.java @@ -0,0 +1,28 @@ +package model.ai.mcts; + +import model.ai.AI; +import model.board.Board; +import model.board.Move; +import model.board.utils.PawnColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class MCTS implements AI { + + private PawnColor color; + + public MCTS(PawnColor color){ + this.color = color; + } + + @Nullable + public Move getAIMove(Board board){ + + MCTSTree mctsTree = new MCTSTree(board, color); + + return mctsTree.getBestMove(5000); + } +} diff --git a/src/main/java/model/ai/mcts/MCTSTree.java b/src/main/java/model/ai/mcts/MCTSTree.java new file mode 100644 index 0000000..546599e --- /dev/null +++ b/src/main/java/model/ai/mcts/MCTSTree.java @@ -0,0 +1,188 @@ +package model.ai.mcts; + +import model.board.Board; +import model.board.Move; +import model.board.utils.PawnColor; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +class MCTSTree{ + + private class Node{ + private Board boardState; + private PawnColor playerTurn; + private Move precedentMove = null; + + private Node parent = null; + private List sons = new ArrayList<>(); + + private int nbSuccess = 0; + private int nbTries = 0; + private int depth = 0; + + /** + * For root node only. + * @param boardState + */ + public Node(@NotNull Board boardState){ + this.boardState = boardState; + this.playerTurn = myColor; + } + + /** + * For non-root nodes only. + * @param boardState + * @param playerTurn + * @param precedentMove + * @param parent + */ + public Node(@NotNull Board boardState, @NotNull PawnColor playerTurn, @NotNull Move precedentMove, @NotNull Node parent, int depth){ + this.boardState = boardState; + this.playerTurn = playerTurn; + this.precedentMove = precedentMove; + + this.parent = parent; + this.depth = depth; + } + + //***** Getters/Setters *****// + + public List getSons(){ + return sons; + } + + public Move getPrecedentMove() { + return precedentMove; + } + + public double getScore(){ + return ((double) nbSuccess) / nbTries; + } + + //***** *****// + + public int playOneTurnRandom(){ + nbTries++; + + if(boardState.isFinished()){ + if(boardState.colorHasWon(myColor)){ + nbSuccess++; + return 1; + } else { + return 0; + } + } else { + Node nextNode = null; + + if(depth > maxDepth) { // Case where we need to play randomly + List allPossibleMoves = boardState.getAllPossibleMoves(playerTurn); + Move moveToDo = allPossibleMoves.get((int) Math.floor(Math.random() * allPossibleMoves.size())); + + nextNode = new Node(new Board(boardState, moveToDo), playerTurn.getOpposite(), moveToDo, this, depth + 1); + + } else { // Case where we are in the beginning of the tree + List allPossibleMoves = boardState.getAllPossibleMoves(playerTurn); + Move moveToDo; + + // Remove the moves already known + for(Node son : sons){ + allPossibleMoves.remove(son.precedentMove); + } + + if((Math.random() < 0.30 && sons.size() > 0) || allPossibleMoves.size() == 0){ // Exploit + int intervalEnlargement = 10; + + // Choose a number between [0; nbTries] + int choice = (int) Math.floor(Math.random() * nbTries * intervalEnlargement); + int sumPreviousSuccess = 0; + nextNode = sons.get(0); + + // Detect if the choice in within the son range. + for(Node son : sons){ + if(choice < sumPreviousSuccess + son.nbSuccess * intervalEnlargement){ + nextNode = son; + break; + } else { + sumPreviousSuccess += son.nbSuccess * intervalEnlargement; + } + } + } else { // Explore + moveToDo = allPossibleMoves.get((int) Math.floor(Math.random() * allPossibleMoves.size())); + + nextNode = new Node(new Board(boardState, moveToDo), playerTurn.getOpposite(), moveToDo, this, depth + 1); + + sons.add(nextNode); + } + } + + int success = nextNode.playOneTurnRandom(); + nbSuccess += success; + + return success; + } + } + + @Override + public String toString(){ + StringBuilder strBld = new StringBuilder(""); + + strBld.append(getScore()); + strBld.append('\n'); + + for(Node son : sons){ + strBld.append("\t-"); + strBld.append(son.toString()); + } + + return strBld.toString(); + } + } + + private static final int maxDepth = 3; + + private PawnColor myColor; + private Node root; + + public MCTSTree(@NotNull Board board, @NotNull PawnColor turnColor){ + myColor = turnColor; + root = new Node(board); + } + + public Move getBestMove(long timeBudget){ + + // Explore the MCTS Tree + + long start = System.currentTimeMillis(); + + while(System.currentTimeMillis() - start < timeBudget){ + root.playOneTurnRandom(); + } + + // Get the results + + List sons = root.getSons(); + + Node bestSon = sons.get(0); + double bestScore = bestSon.getScore(); + + for(Node son : sons) { + if (bestScore < son.getScore()) { + bestSon = son; + bestScore = son.getScore(); + } + } + + System.out.println("Tries: "+ root.nbTries); + System.out.println("WinRate: "+ root.getScore() * 100); + return bestSon.getPrecedentMove(); + } + + @Override + public String toString(){ + return root.toString(); + } + +} + diff --git a/src/main/java/model/ai/random/Random.java b/src/main/java/model/ai/random/Random.java new file mode 100644 index 0000000..c0027b9 --- /dev/null +++ b/src/main/java/model/ai/random/Random.java @@ -0,0 +1,27 @@ +package model.ai.random; + +import model.ai.AI; +import model.board.Board; +import model.board.Move; +import model.board.utils.PawnColor; + +import java.util.List; + +public class Random implements AI { + + private PawnColor color; + + public Random(PawnColor color){ + this.color = color; + } + + @Override + public Move getAIMove(Board board) { + Move bestMove; + + List listPossibleMoves = board.getAllPossibleMoves(color); + bestMove = listPossibleMoves.get((int) (Math.random() * listPossibleMoves.size())); + + return bestMove; + } +} diff --git a/src/main/java/model/board/Board.java b/src/main/java/model/board/Board.java new file mode 100644 index 0000000..3f1670d --- /dev/null +++ b/src/main/java/model/board/Board.java @@ -0,0 +1,261 @@ +package model.board; + + +import model.board.utils.PawnColor; +import model.board.utils.Tile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class Board { + + public static final int SIZEX = 8; + public static final int SIZEY = 8; + + private Tile[][] tiles; + private List whites = new ArrayList<>(); + private List blacks = new ArrayList<>(); + + private boolean whiteHasWon = false; + private boolean blackHasWon = false; + + public Board(){ + tiles = new Tile[SIZEX][SIZEY]; + + initializeBoard(); + } + + public Board(Board board){ + this.tiles = new Tile[board.tiles.length][board.tiles[0].length]; + + for(int i = 0; i < this.tiles.length; i++){ + System.arraycopy(board.tiles[i], 0, this.tiles[i], 0, this.tiles[i].length); + } + + whites = new ArrayList<>(board.whites); + blacks = new ArrayList<>(board.blacks); + + whiteHasWon = board.whiteHasWon; + blackHasWon = board.blackHasWon; + +// initializeBoard(); + } + + public Board(@NotNull Board board, @NotNull Move move){ + this.tiles = new Tile[board.tiles.length][board.tiles[0].length]; + + for(int i = 0; i < this.tiles.length; i++){ + System.arraycopy(board.tiles[i], 0, this.tiles[i], 0, this.tiles[i].length); + } + + whites = new ArrayList<>(board.whites); + blacks = new ArrayList<>(board.blacks); + + whiteHasWon = board.whiteHasWon; + blackHasWon = board.blackHasWon; + + this.movePawn(move); + } + + //********** Initialize Methods **********// + + private void initializeBoard(){ + for(int i = 0; i < tiles.length; i++){ + for(int j = 0; j < tiles[0].length; j++){ + if(i < 2){ + tiles[i][j] = Tile.BLACK; + blacks.add(new Position(i, j)); + } else if(i > SIZEY-3) { + tiles[i][j] = Tile.WHITE; + whites.add(new Position(i, j)); + } else { + tiles[i][j] = Tile.EMPTY; + } + } + } + } + + //********** Public Methods **********// + + public List getPossibleMoves(Position position){ + Tile tile = tiles[position.x][position.y]; + List possiblePositions = new ArrayList<>(); + + // Get the possible moves for the white pawns. + if(tile.equals(Tile.WHITE)){ + Tile left = null; + Tile right = null; + Tile up = null; + + if (position.x != 0) { + if(position.y != 0) { + left = tiles[position.x - 1][position.y - 1]; + } + if(position.y != SIZEY-1) { + right = tiles[position.x-1][position.y+1]; + } + up = tiles[position.x-1][position.y]; + } + + if(left != null && (left.equals(Tile.EMPTY) || left.equals(Tile.BLACK))) { + possiblePositions.add( + new Move(position, new Position(position.x - 1, position.y - 1))); + } + if(right != null && (right.equals(Tile.EMPTY) || right.equals(Tile.BLACK))) { + possiblePositions.add( + new Move(position, new Position(position.x - 1, position.y + 1))); + } + if(up != null && up.equals(Tile.EMPTY)){ + possiblePositions.add( + new Move(position, new Position(position.x - 1, position.y))); + } + } else if(tile.equals(Tile.BLACK)) { + Tile left = null; + Tile right = null; + Tile down = null; + + if (position.x != SIZEX-1) { + if(position.y != 0) { + left = tiles[position.x + 1][position.y - 1]; + } + if(position.y != SIZEY-1) { + right = tiles[position.x + 1][position.y + 1]; + } + down = tiles[position.x + 1][position.y]; + } + + if(left != null && (left.equals(Tile.EMPTY) || left.equals(Tile.WHITE))) { + possiblePositions.add( + new Move(position, new Position(position.x + 1, position.y - 1))); + } + if(right != null && (right.equals(Tile.EMPTY) || right.equals(Tile.WHITE))) { + possiblePositions.add( + new Move(position, new Position(position.x + 1, position.y + 1))); + } + if(down != null && down.equals(Tile.EMPTY)){ + possiblePositions.add( + new Move(position, new Position(position.x + 1, position.y))); + } + } + + return possiblePositions; + } + + public Tile getTile(Position position){ + Tile tile = Tile.EMPTY; + + if(position.isValid()){ + tile = tiles[position.x][position.y]; + } + + return tile; + } + + public void movePawn(Move move){ + if(!whiteHasWon && !blackHasWon + && move.start.isValid() && move.end.isValid() + && !getTile(move.start).equals(Tile.EMPTY) && getPossibleMoves(move.start).contains(move)){ + + switch (getTile(move.start)){ + case WHITE: + whites.remove(move.start); + whites.add(move.end); + blacks.remove(move.end); + + tiles[move.start.x][move.start.y] = Tile.EMPTY; + tiles[move.end.x][move.end.y] = Tile.WHITE; + whiteHasWon = move.end.x == 0; + break; + case BLACK: + blacks.remove(move.start); + blacks.add(move.end); + whites.remove(move.end); + + tiles[move.start.x][move.start.y] = Tile.EMPTY; + tiles[move.end.x][move.end.y] = Tile.BLACK; + blackHasWon = move.end.x == SIZEX-1; + break; + } + } else { +// for(Move moveP : getPossibleMoves(move.start)){ +// System.err.println(moveP); +// } +// System.err.println("--"); +// System.err.println(tiles[move.end.x][move.end.y]); +// System.err.println("--"); +// System.err.println(move); +// throw new RuntimeException(); + } + } + + public List getAllPossibleMoves(PawnColor color){ + List possibleMoves = new ArrayList<>(); + List pawnPositions; + + if(color.equals(PawnColor.WHITE)){ + pawnPositions = whites; + } else { + pawnPositions = blacks; + } + + for(Position pawnPosition : pawnPositions){ + possibleMoves.addAll(getPossibleMoves(pawnPosition)); + } + return possibleMoves; + } + + //********** Getters/Setters Methods **********// + + // Getters // + public List getWhites() { + return whites; + } + + public List getBlacks() { + return blacks; + } + + public boolean whiteHasWon(){ + return whiteHasWon; + } + + public boolean blackHasWon(){ + return blackHasWon; + } + + public boolean colorHasWon(@Nullable PawnColor playerColor){ + return (PawnColor.WHITE.equals(playerColor) && whiteHasWon) + || (PawnColor.BLACK.equals(playerColor) && blackHasWon); + } + + public boolean isFinished(){ + return whiteHasWon || blackHasWon; + } + + //********** Standard Methods **********// + + @Override + public String toString() { + StringBuilder sbBoard = new StringBuilder(""); + + for (int i = -1; i < tiles.length; i++) { + + if(i == -1){ + sbBoard.append(" "); + sbBoard.append("0 1 2 3 4 5 6 7\n"); + } else { + sbBoard.append(i); + sbBoard.append('|'); + for (int j = 0; j < tiles[0].length; j++) { + sbBoard.append(tiles[i][j]); + sbBoard.append(' '); + } + sbBoard.append('\n'); + } + } + + return sbBoard.toString(); + } +} diff --git a/src/main/java/model/board/Move.java b/src/main/java/model/board/Move.java new file mode 100644 index 0000000..a051242 --- /dev/null +++ b/src/main/java/model/board/Move.java @@ -0,0 +1,26 @@ +package model.board; + +public class Move { + + public Position start; + public Position end; + + public Move(Position start, Position end){ + this.start = start; + this.end = end; + } + + @Override + public String toString() { + return start+"->"+end; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Move){ + Move m = (Move) obj; + return start.equals(m.start) && end.equals(m.end); + } + return false; + } +} diff --git a/src/main/java/model/board/Position.java b/src/main/java/model/board/Position.java new file mode 100644 index 0000000..0ecee8f --- /dev/null +++ b/src/main/java/model/board/Position.java @@ -0,0 +1,31 @@ +package model.board; + +public class Position { + + public int x; + public int y; + + public Position(Integer x, Integer y){ + this.x = x; + this.y = y; + } + + public boolean isValid(){ + return x >= 0 && x < Board.SIZEX + && y >= 0 && y < Board.SIZEY; + } + + @Override + public String toString() { + return "("+x+";"+y+")"; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Position){ + Position position = (Position) obj; + return position.x == this.x && position.y == this.y; + } + return false; + } +} diff --git a/src/main/java/model/board/utils/PawnColor.java b/src/main/java/model/board/utils/PawnColor.java new file mode 100644 index 0000000..4150ffc --- /dev/null +++ b/src/main/java/model/board/utils/PawnColor.java @@ -0,0 +1,16 @@ +package model.board.utils; + +public enum PawnColor { + WHITE, BLACK; + + public PawnColor getOpposite(){ + switch (this){ + case WHITE: + return BLACK; + case BLACK: + return WHITE; + default: + return WHITE; + } + } +} diff --git a/src/main/java/model/board/utils/Tile.java b/src/main/java/model/board/utils/Tile.java new file mode 100644 index 0000000..cbd6927 --- /dev/null +++ b/src/main/java/model/board/utils/Tile.java @@ -0,0 +1,21 @@ +package model.board.utils; + +public enum Tile{ + WHITE, + BLACK, + EMPTY; + + @Override + public String toString() { + switch (this){ + case WHITE: + return "W"; + case BLACK: + return "B"; + case EMPTY: + return "."; + default: + return "X"; + } + } +} \ No newline at end of file diff --git a/src/main/resources/fxml/mainView.fxml b/src/main/resources/fxml/mainView.fxml new file mode 100644 index 0000000..97a63a5 --- /dev/null +++ b/src/main/resources/fxml/mainView.fxml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/MainTest.java b/src/test/java/MainTest.java new file mode 100644 index 0000000..9587884 --- /dev/null +++ b/src/test/java/MainTest.java @@ -0,0 +1,40 @@ +import model.ai.mcts.MCTS; +import model.board.Board; +import model.board.Move; +import model.board.Position; +import model.board.utils.PawnColor; + +import java.util.Scanner; + +public class MainTest { + + public static void main(String[] args) throws InterruptedException { + Board board = new Board(); + MCTS ai = new MCTS(PawnColor.WHITE); + + while (!board.isFinished()){ + + Move aiMove = ai.getAIMove(board); + board.movePawn(aiMove); + + System.err.println("-----------------"); + System.err.println("Move: "+ aiMove); + System.err.println("Node Visited: "+ MCTS.nbNodeVisited); + System.err.println("-----------------"); + System.err.println(board); + + Scanner scanner = new Scanner(System.in); + String str = scanner.nextLine(); + String[] strs = str.split(" "); + + + int sx = Integer.parseInt(strs[0]); + int sy = Integer.parseInt(strs[1]); + int ex = Integer.parseInt(strs[2]); + int ey = Integer.parseInt(strs[3]); + + Move m = new Move(new Position(sx, sy), new Position(ex, ey)); + board.movePawn(m); + } + } +}