Skip to content

Commit 4572475

Browse files
committed
optimized move selection
1 parent 7816cfa commit 4572475

File tree

1 file changed

+71
-36
lines changed

1 file changed

+71
-36
lines changed

src/main/java/main/java/codingame/TicTacToe/TicTacToe.java

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public static void main(String args[]) throws IOException {
2323
largeBoard.play(2, opponentMove);
2424
algorithm.root = algorithm.root.getChild(opponentMove);
2525
algorithm.root.parent = null;
26-
algorithm.construct(largeBoard, 2);
26+
algorithm.construct(largeBoard, 1);
2727
System.err.println(largeBoard);
2828
}
2929
final int validActionCount = Integer.parseInt(in.readLine());
@@ -36,12 +36,13 @@ public static void main(String args[]) throws IOException {
3636
largeBoard.play(1, bestMove);
3737
algorithm.root = algorithm.root.getChild(bestMove);
3838
algorithm.root.parent = null;
39-
algorithm.construct(largeBoard, 1);
39+
algorithm.construct(largeBoard, 2);
4040
System.err.println(largeBoard);
4141
}
4242
}
4343
}
4444

45+
//todo: write unit tests for each function
4546
class MCTS {
4647
public static final int TIME_OUT = 50;
4748
public static final double CONSTANT = 10000d;
@@ -51,7 +52,7 @@ public int suggestMove() {
5152
return root.getChildren()
5253
.stream()
5354
.max(Comparator.comparingDouble(node -> node.wins / (double) node.plays + node.plays / CONSTANT))
54-
.map(c -> c.col)
55+
.map(c -> c.move)
5556
.orElseThrow(() -> new RuntimeException("No moves to play!"));
5657
}
5758

@@ -60,7 +61,8 @@ public void construct(final LargeBoard board, int player) {
6061
while (System.currentTimeMillis() - startTime <= TIME_OUT) {
6162
TreeNode current = root;
6263
int position = current.selectChild(board);
63-
while (current.getChild(position) != null) {
64+
//todo: Why do you check for canPlay?
65+
while (board.canPlay(position) && current.getChild(position) != null) {
6466
current = current.getChild(position);
6567
board.play(player, position);
6668
position = current.selectChild(board);
@@ -80,15 +82,15 @@ public void construct(final LargeBoard board, int player) {
8082
class TreeNode {
8183
private static final Random random = new Random();
8284
public static final int SIMULATION_CONSTANT = 50;
83-
public final int col;
85+
public final int move;
8486
public int plays;
8587
public double wins;
8688
public TreeNode parent;
8789
private final int player;
8890
private Map<Integer, TreeNode> children = new HashMap<>();
8991

90-
public TreeNode(final int col, final TreeNode parent, final int player) {
91-
this.col = col;
92+
public TreeNode(final int move, final TreeNode parent, final int player) {
93+
this.move = move;
9294
this.parent = parent;
9395
this.player = player;
9496
}
@@ -98,21 +100,39 @@ public int selectChild(final LargeBoard board) {
98100
.stream()
99101
.max(Comparator.comparingDouble(TreeNode::getUtility));
100102
double maxUtility = child.map(TreeNode::getUtility).orElse(0d);
101-
int bestColumn = child.map(c -> c.col).orElse(0);
103+
int bestColumn = child.map(c -> c.move).orElse(0);
102104
final Set<Integer> expandedSet = children.keySet();
103105
final int currentBoardIndex = board.currentBoard();
104-
int max = 81;
105-
int i = 0;
106106
if (currentBoardIndex != -1) {
107-
i = (currentBoardIndex / 3) * 27 + (currentBoardIndex % 3) * 3;
108-
max = i + 21;
109-
}
110-
for (; i < max; i++) {
111-
if (board.canPlay(i) && !expandedSet.contains(i)) {
112-
final double utility = Math.sqrt(Math.log(plays + 1)) + (0.05 / Math.abs(9 / 2.0 - i));
113-
if (utility > maxUtility) {
114-
maxUtility = utility;
115-
bestColumn = i;
107+
int start = (currentBoardIndex / 3) * 27 + (currentBoardIndex % 3) * 3;
108+
for (int x = 0; x < 3; x++, start++) {
109+
for (int y = 0; y < 3; y++) {
110+
int i = start + y * 9;
111+
if (board.canPlay(i) && !expandedSet.contains(i)) {
112+
final double utility = Math.sqrt(Math.log(plays + 1)) + (0.05 / Math.abs(40 - i));
113+
if (utility > maxUtility) {
114+
maxUtility = utility;
115+
bestColumn = i;
116+
}
117+
}
118+
}
119+
}
120+
} else {
121+
for (int boardIndex = 0; boardIndex < 9; boardIndex++) {
122+
if ((board.largeOccupied & (1 << boardIndex)) == 0) {
123+
int start = (boardIndex / 3) * 27 + (boardIndex % 3) * 3;
124+
for (int x = 0; x < 3; x++, start++) {
125+
for (int y = 0; y < 3; y++) {
126+
int i = start + y * 9;
127+
if (board.canPlay(i) && !expandedSet.contains(i)) {
128+
final double utility = Math.sqrt(Math.log(plays + 1)) + (0.05 / Math.abs(40 - i));
129+
if (utility > maxUtility) {
130+
maxUtility = utility;
131+
bestColumn = i;
132+
}
133+
}
134+
}
135+
}
116136
}
117137
}
118138
}
@@ -127,19 +147,34 @@ private double simulate(final LargeBoard board, int player) {
127147
final int numberOfMovesPlayed = board.movesPlayed;
128148
final int originalPlayer = player;
129149
while (board.result() == -1) {
150+
int movesToPlay = 0;
130151
final int currentBoardIndex = board.currentBoard();
131-
int max = 81;
132-
int position = 0;
152+
final int possibilities[] = new int[currentBoardIndex != -1 ? 9 : 81];
133153
if (currentBoardIndex != -1) {
134-
position = (currentBoardIndex / 3) * 27 + (currentBoardIndex % 3) * 3;
135-
max = position + 21;
136-
}
137-
final int possibilities[] = new int[max - position];
138-
int movesToPlay = 0;
139-
for (; position < max; position++) {
140-
if (board.canPlay(position)) {
141-
possibilities[movesToPlay] = position;
142-
movesToPlay++;
154+
int start = (currentBoardIndex / 3) * 27 + (currentBoardIndex % 3) * 3;
155+
for (int x = 0; x < 3; x++, start++) {
156+
for (int y = 0; y < 3; y++) {
157+
int position = start + y * 9;
158+
if (board.canPlay(position)) {
159+
possibilities[movesToPlay] = position;
160+
movesToPlay++;
161+
}
162+
}
163+
}
164+
} else {
165+
for (int boardIndex = 0; boardIndex < 9; boardIndex++) {
166+
if ((board.largeOccupied & (1 << boardIndex)) == 0) {
167+
int start = (boardIndex / 3) * 27 + (boardIndex % 3) * 3;
168+
for (int x = 0; x < 3; x++, start++) {
169+
for (int y = 0; y < 3; y++) {
170+
int position = start + y * 9;
171+
if (board.canPlay(position)) {
172+
possibilities[movesToPlay] = position;
173+
movesToPlay++;
174+
}
175+
}
176+
}
177+
}
143178
}
144179
}
145180
if (movesToPlay == 0) {
@@ -188,14 +223,14 @@ public TreeNode getChild(final int col) {
188223
@Override
189224
public String toString() {
190225
return "TreeNode{" +
191-
"\ncol=" + col +
226+
"\nmove=" + move +
192227
", \nplays=" + plays +
193228
", \nwins=" + wins +
194-
", \nparent=" + (parent == null ? -1 : parent.col) +
229+
", \nparent=" + (parent == null ? -1 : parent.move) +
195230
", \nplayer=" + player +
196231
", \nchildren=" + children.values()
197232
.stream()
198-
.map(c -> "MOVE: " + c.col + " WINS: " + c.wins + " PLAYS: " + c.plays + "\n")
233+
.map(node -> "MOVE: " + node.move + " WINS: " + node.wins + " PLAYS: " + node.plays + "\n")
199234
.collect(Collectors.joining("\n")) +
200235
'}';
201236
}
@@ -318,9 +353,9 @@ public int currentBoard() {
318353
@Override
319354
public String toString() {
320355
return "LargeBoard{" +
321-
"\nlargeBoard=" + largeBoard +
322-
", \nlargeCaptures=" + largeCaptures +
323-
", \nlargeOccupied=" + largeOccupied +
356+
"\nlargeBoard=" + Integer.toBinaryString(largeBoard) +
357+
", \nlargeCaptures=" + Integer.toBinaryString(largeCaptures) +
358+
", \nlargeOccupied=" + Integer.toBinaryString(largeOccupied) +
324359
", \nmovesPlayed=" + movesPlayed +
325360
", \nmoves=" + Arrays.toString(moves) +
326361
", \nboards=" + Arrays.deepToString(boards) +

0 commit comments

Comments
 (0)