Implemented naive Monte Carlo move generator (simulates 10 random moves for 3 turns by each player).
Consequently, it performs strictly worse than Alpha-Beta, but usually avoids setting players up for an easy capture, unlike Alpha-Beta.
This commit is contained in:
@@ -1,16 +1,21 @@
|
|||||||
package model;
|
package model;
|
||||||
|
|
||||||
public class SearchResult {
|
public class SearchResult implements Comparable<SearchResult> {
|
||||||
public final Move move;
|
public final Move move;
|
||||||
public final int score;
|
public final int score;
|
||||||
|
|
||||||
public SearchResult(Move move, int score) {
|
public SearchResult(Move move, int score) {
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return move + ", score: " + score;
|
return move + ", score: " + score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(SearchResult o) {
|
||||||
|
return Integer.compare(score, o.score);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,16 @@
|
|||||||
package model.comPlayer;
|
package model.comPlayer;
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import model.Board;
|
import model.Board;
|
||||||
import model.CellPointer;
|
|
||||||
import model.Move;
|
import model.Move;
|
||||||
import model.Board.TileColor;
|
import model.comPlayer.generator.MonteCarloMoveGenerator;
|
||||||
|
import model.comPlayer.generator.MoveGenerator;
|
||||||
|
|
||||||
public class MonteCarloComPlayer implements Player {
|
public class MonteCarloComPlayer implements Player {
|
||||||
private final Random rand = new Random();
|
private MoveGenerator moveGenerator = new MonteCarloMoveGenerator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Move getMove(Board board) {
|
public Move getMove(Board board) {
|
||||||
return getRandomMove(board,true);
|
return moveGenerator.genMove(board, false);
|
||||||
}
|
|
||||||
|
|
||||||
public Move getRandomMove(Board board, boolean isCompTurn) {
|
|
||||||
TileColor tile = TileColor.BLUE;
|
|
||||||
int r = -1;
|
|
||||||
int c = -1;
|
|
||||||
|
|
||||||
while (tile != TileColor.NONE) {
|
|
||||||
r = rand.nextInt(Board.NUM_ROWS);
|
|
||||||
c = rand.nextInt(Board.NUM_COLS);
|
|
||||||
|
|
||||||
tile = board.getTile(r, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (rand.nextInt(4)) {
|
|
||||||
case 0:
|
|
||||||
tile = TileColor.BLUE;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
tile = TileColor.GREEN;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
tile = TileColor.RED;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
tile = TileColor.YELLOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Move(new CellPointer(r, c), tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,4 +27,4 @@ public class MonteCarloComPlayer implements Player {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "Monte Carlo ComPlayer";
|
return "Monte Carlo ComPlayer";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,22 +29,6 @@ public class AlphaBetaMoveGenerator implements MoveGenerator {
|
|||||||
Integer.MAX_VALUE).move;
|
Integer.MAX_VALUE).move;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SearchResult getMax(SearchResult sr1, SearchResult sr2) {
|
|
||||||
if (sr1.score >= sr2.score) {
|
|
||||||
return sr1;
|
|
||||||
} else {
|
|
||||||
return sr2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchResult getMin(SearchResult sr1, SearchResult sr2) {
|
|
||||||
if (sr1.score <= sr2.score) {
|
|
||||||
return sr1;
|
|
||||||
} else {
|
|
||||||
return sr2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchResult getMaxValue(Board board, boolean asHuman, int recursionLevel,
|
private SearchResult getMaxValue(Board board, boolean asHuman, int recursionLevel,
|
||||||
int alpha, int beta) {
|
int alpha, int beta) {
|
||||||
@@ -72,7 +56,9 @@ public class AlphaBetaMoveGenerator implements MoveGenerator {
|
|||||||
SearchResult searchResult = new SearchResult(nextMove,getMinValue(nextBoard, !asHuman, recursionLevel - 1,
|
SearchResult searchResult = new SearchResult(nextMove,getMinValue(nextBoard, !asHuman, recursionLevel - 1,
|
||||||
alpha, beta).score);
|
alpha, beta).score);
|
||||||
|
|
||||||
bestResult = getMax(bestResult,searchResult);
|
if (searchResult.compareTo(bestResult) > 0) {
|
||||||
|
bestResult = searchResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (bestResult.score >= beta) {
|
if (bestResult.score >= beta) {
|
||||||
return bestResult;
|
return bestResult;
|
||||||
@@ -110,7 +96,9 @@ public class AlphaBetaMoveGenerator implements MoveGenerator {
|
|||||||
SearchResult searchResult = new SearchResult(nextMove,getMaxValue(nextBoard, !asHuman, recursionLevel - 1,
|
SearchResult searchResult = new SearchResult(nextMove,getMaxValue(nextBoard, !asHuman, recursionLevel - 1,
|
||||||
alpha, beta).score);
|
alpha, beta).score);
|
||||||
|
|
||||||
bestResult = getMin(bestResult,searchResult);
|
if (searchResult.compareTo(bestResult) < 0) {
|
||||||
|
bestResult = searchResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (bestResult.score <= alpha) {
|
if (bestResult.score <= alpha) {
|
||||||
return bestResult;
|
return bestResult;
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ public class ValidMoveGenerator implements MoveGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Collections.shuffle(validMoves);
|
Collections.shuffle(validMoves);
|
||||||
return validMoves;
|
|
||||||
|
if (nMoves == MoveGenerator.ALL_MOVES) {
|
||||||
|
return validMoves;
|
||||||
|
} else {
|
||||||
|
return validMoves.subList(0, Math.min(validMoves.size(),nMoves));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package view;
|
|||||||
|
|
||||||
import model.comPlayer.AlphaBetaComPlayer;
|
import model.comPlayer.AlphaBetaComPlayer;
|
||||||
import model.comPlayer.MinimaxComPlayer;
|
import model.comPlayer.MinimaxComPlayer;
|
||||||
|
import model.comPlayer.MonteCarloComPlayer;
|
||||||
import model.comPlayer.Player;
|
import model.comPlayer.Player;
|
||||||
import model.comPlayer.RandomComPlayer;
|
import model.comPlayer.RandomComPlayer;
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ public class ParsedArgs {
|
|||||||
public static final String COM_RANDOM = "RANDOM";
|
public static final String COM_RANDOM = "RANDOM";
|
||||||
public static final String COM_MINIMAX = "MINIMAX";
|
public static final String COM_MINIMAX = "MINIMAX";
|
||||||
public static final String COM_ALPHABETA = "ALPHABETA";
|
public static final String COM_ALPHABETA = "ALPHABETA";
|
||||||
|
public static final String COM_MONTECARLO = "MONTECARLO";
|
||||||
public static final String COM_DEFAULT = COM_ALPHABETA;
|
public static final String COM_DEFAULT = COM_ALPHABETA;
|
||||||
|
|
||||||
private String comPlayer = COM_DEFAULT;
|
private String comPlayer = COM_DEFAULT;
|
||||||
@@ -20,6 +22,8 @@ public class ParsedArgs {
|
|||||||
return new MinimaxComPlayer();
|
return new MinimaxComPlayer();
|
||||||
} else if (COM_ALPHABETA.equalsIgnoreCase(comPlayer)) {
|
} else if (COM_ALPHABETA.equalsIgnoreCase(comPlayer)) {
|
||||||
return new AlphaBetaComPlayer();
|
return new AlphaBetaComPlayer();
|
||||||
|
} else if (COM_MONTECARLO.equalsIgnoreCase(comPlayer)) {
|
||||||
|
return new MonteCarloComPlayer();
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Unrecognized comPlayer '" + comPlayer +"', using default: " + COM_DEFAULT);
|
System.out.println("Unrecognized comPlayer '" + comPlayer +"', using default: " + COM_DEFAULT);
|
||||||
return new AlphaBetaComPlayer();
|
return new AlphaBetaComPlayer();
|
||||||
|
|||||||
Reference in New Issue
Block a user