-
Notifications
You must be signed in to change notification settings - Fork 0
/
AlphaBetaWithMoveOrdering.java
153 lines (142 loc) · 7.39 KB
/
AlphaBetaWithMoveOrdering.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package model.artificialintelligence;
import model.board.Board;
import model.board.BoardUtils;
import model.board.Move;
import model.board.MoveTransition;
import model.player.Player;
import java.util.Observable;
public class AlphaBetaWithMoveOrdering extends Observable implements MoveStrategy {
private final BoardEvaluator evaluator;
private final int searchDepth;
private long numEvaluatedBoards;
private int cutOffsProduced;
public AlphaBetaWithMoveOrdering(final int searchDepth) {
this.evaluator = StandardBoardEvaluator.get();
this.searchDepth = searchDepth;
this.numEvaluatedBoards = 0;
this.cutOffsProduced = 0;
}
@Override
public String toString() {
return "Alpha Beta Pruning + Move Ordering";
}
@Override
public long getNumEvaluatedBoards() {
return this.numEvaluatedBoards;
}
@Override
public Move execute(final Board board) {
final long startTime = System.currentTimeMillis();
final Player currentPlayer = board.getCurrentPlayer();
Move optimalMove = Move.MoveFactory.getNullMove();
int highestSeenValue = Integer.MIN_VALUE; // alpha = highestSeenValue
int lowestSeenValue = Integer.MAX_VALUE; // beta = lowestSeenValue
int currentValue;
int moveCounter = 1;
final int numMoves = board.getCurrentPlayer().getValidMoves().size();
System.out.println(board.getCurrentPlayer() + " is thinking with with a depth of " + this.searchDepth);
System.out.println("\tOrdered moves : " + MoveSorter.EXPENSIVE.sort(board.getCurrentPlayer().getValidMoves()));
for (final Move move : MoveSorter.EXPENSIVE.sort(board.getCurrentPlayer().getValidMoves())) {
if (!move.isBannedRepetitiveMove()) {
final MoveTransition moveTransition = board.getCurrentPlayer().makeMove(move);
final String s;
if (moveTransition.getMoveStatus().isDone()) {
final long potentialMoveStartTime = System.nanoTime();
currentValue = currentPlayer.getAllyColor().isBlue() ?
min(moveTransition.getToBoard(), this.searchDepth - 1, highestSeenValue, lowestSeenValue) :
max(moveTransition.getToBoard(), this.searchDepth - 1, highestSeenValue, lowestSeenValue);
if (currentPlayer.getAllyColor().isBlue() && currentValue > highestSeenValue) {
highestSeenValue = currentValue;
optimalMove = move;
if (moveTransition.getToBoard().redPlayer().isDenPenetrated()
|| moveTransition.getToBoard().redPlayer().getActivePieces().isEmpty()
|| moveTransition.getToBoard().redPlayer().getValidMoves().isEmpty()) {
break;
}
} else if (currentPlayer.getAllyColor().isRed() && currentValue < lowestSeenValue) {
lowestSeenValue = currentValue;
optimalMove = move;
if (moveTransition.getToBoard().bluePlayer().isDenPenetrated()
|| moveTransition.getToBoard().bluePlayer().getActivePieces().isEmpty()
|| moveTransition.getToBoard().bluePlayer().getValidMoves().isEmpty()) {
break;
}
}
s = "\t" + this + " (" + this.searchDepth + "), move: (" + moveCounter + "/" + numMoves + ") " + move + ", best: " + optimalMove
+ " " + score(currentPlayer, highestSeenValue, lowestSeenValue) + ", t: " + calculateTimeTaken(potentialMoveStartTime, System.nanoTime());
} else {
s = "\t" + this + " (" + this.searchDepth + ")" + ", m: (" + moveCounter + "/" + numMoves + ") " + move + " is illegal! best: " + optimalMove;
}
System.out.println(s);
setChanged();
notifyObservers(s);
moveCounter++;
}
}
final long executionTime = System.currentTimeMillis() - startTime;
final String result = String.format("%s selects %s [#total boards evaluated = %d," +
" time taken = %dms, evaluation rate = %.1fboards/second," +
" cut-off count = %d, prune percent = %.2f]\n",
board.getCurrentPlayer(), optimalMove, this.numEvaluatedBoards,
executionTime, (1000 * ((double) this.numEvaluatedBoards / executionTime)),
this.cutOffsProduced, 100 * ((double) this.cutOffsProduced / (this.numEvaluatedBoards + this.cutOffsProduced)));
System.out.print(result);
setChanged();
notifyObservers(result);
return optimalMove;
}
public int max(final Board board, final int depth, final int highest, final int lowest) {
// alpha = highest (currentHighest)
// beta = lowest
if (depth == 0 || BoardUtils.isGameOverScenarioStandardConditions(board)) {
this.numEvaluatedBoards++;
return this.evaluator.evaluate(board, depth);
}
int currentHighest = highest;
for (final Move move : MoveSorter.STANDARD.sort((board.getCurrentPlayer().getValidMoves()))) {
final MoveTransition moveTransition = board.getCurrentPlayer().makeMove(move);
if (moveTransition.getMoveStatus().isDone()) {
currentHighest = Math.max(currentHighest,
min(moveTransition.getToBoard(), depth - 1, currentHighest, lowest));
if (currentHighest >= lowest) {
this.cutOffsProduced++;
return currentHighest;
}
}
}
return currentHighest;
}
public int min(final Board board, final int depth, final int highest, final int lowest) {
// alpha = highest
// beta = lowest (currentLowest)
if (depth == 0 || BoardUtils.isGameOverScenarioStandardConditions(board)) {
this.numEvaluatedBoards++;
return this.evaluator.evaluate(board, depth);
}
int currentLowest = lowest;
for (final Move move : MoveSorter.STANDARD.sort((board.getCurrentPlayer().getValidMoves()))) {
final MoveTransition moveTransition = board.getCurrentPlayer().makeMove(move);
if (moveTransition.getMoveStatus().isDone()) {
currentLowest = Math.min(currentLowest,
max(moveTransition.getToBoard(), depth - 1, highest, currentLowest));
if (currentLowest <= highest) {
this.cutOffsProduced++;
return currentLowest;
}
}
}
return currentLowest;
}
private static String score(final Player currentPlayer, final int highestSeenValue, final int lowestSeenValue) {
if (currentPlayer.getAllyColor().isBlue()) {
return "[score: " + highestSeenValue + "]";
} else if (currentPlayer.getAllyColor().isRed()) {
return "[score: " + lowestSeenValue + "]";
}
throw new RuntimeException("Something went wrong!");
}
private static String calculateTimeTaken(final long start, final long end) {
final long timeTaken = (end - start) / 1_000_000;
return timeTaken + " ms";
}
}