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);
+ }
+ }
+}