AI: Working MCTS

master
Gregory Martin 2018-03-14 14:37:14 +01:00
parent b64f0dd755
commit 9087c4a8d7
No known key found for this signature in database
GPG Key ID: 8791DD65FA92D9F0
16 changed files with 976 additions and 0 deletions

53
.gitignore vendored Normal file
View File

@ -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

26
BreakMCTS.iml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="Maven: com.oracle:javafx:8">
<CLASSES>
<root url="jar://$USER_HOME$/bin/jdk/jre/lib/ext/jfxrt.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="Maven: org.jetbrains:annotations:15.0" level="project" />
</component>
</module>

33
pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>BreakMCTS</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.9</source>
<target>1.9</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

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

View File

@ -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<Position> blackPawns = board.getBlacks();
List<Position> 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");
}
}

View File

@ -0,0 +1,9 @@
package model.ai;
import model.board.Board;
import model.board.Move;
public interface AI {
Move getAIMove(Board board);
}

View File

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

View File

@ -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<Node> 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<Node> 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<Move> 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<Move> 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<Node> 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();
}
}

View File

@ -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<Move> listPossibleMoves = board.getAllPossibleMoves(color);
bestMove = listPossibleMoves.get((int) (Math.random() * listPossibleMoves.size()));
return bestMove;
}
}

View File

@ -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<Position> whites = new ArrayList<>();
private List<Position> 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<Move> getPossibleMoves(Position position){
Tile tile = tiles[position.x][position.y];
List<Move> 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<Move> getAllPossibleMoves(PawnColor color){
List<Move> possibleMoves = new ArrayList<>();
List<Position> 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<Position> getWhites() {
return whites;
}
public List<Position> 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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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";
}
}
}

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<VBox fx:id="vbox" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar maxHeight="-Infinity" maxWidth="1.7976931348623157E308" VBox.vgrow="NEVER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<VBox alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS">
<children>
<GridPane fx:id="gridpane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" VBox.vgrow="NEVER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</children>
</VBox>
</children>
</VBox>

View File

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