AI: Working MCTS
parent
b64f0dd755
commit
9087c4a8d7
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package model.ai;
|
||||
|
||||
import model.board.Board;
|
||||
import model.board.Move;
|
||||
|
||||
public interface AI {
|
||||
|
||||
Move getAIMove(Board board);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue