@@ -10,46 +10,59 @@ public class TicTacToe {
10
10
public static void main (String args []) throws IOException {
11
11
final BufferedReader in = new BufferedReader (new InputStreamReader (System .in ));
12
12
final LargeBoard largeBoard = new LargeBoard ();
13
- final MCTS algorithm = new MCTS (largeBoard );
13
+ // largeBoard.play(1, 0);
14
+ // largeBoard.play(1, 1);
15
+ // largeBoard.play(1, 3);
16
+ // largeBoard.play(1, 2);
17
+ // largeBoard.play(1, 7);
18
+ // largeBoard.play(1, 9);
19
+ // System.out.println(largeBoard);
20
+ final MCTS algorithm = new MCTS ();
21
+ algorithm .construct (largeBoard , MCTS .TIME_OUT );
14
22
while (true ) {
15
23
String line [] = in .readLine ().split (" " );
16
24
final int opponentRow = Integer .parseInt (line [0 ]), opponentCol = Integer .parseInt (line [1 ]);
17
25
if (opponentCol >= 0 ) {
18
- largeBoard .play (2 , opponentRow * 9 + opponentCol );
26
+ final int opponentMove = opponentRow * 9 + opponentCol ;
27
+ if (algorithm .root .getChild (opponentMove ) == null ) {
28
+ algorithm .root .expand (largeBoard , opponentMove );
29
+ }
30
+ largeBoard .play (2 , opponentMove );
31
+ algorithm .root = algorithm .root .getChild (opponentMove );
32
+ algorithm .construct (largeBoard , MCTS .TIME_OUT );
19
33
System .err .println (largeBoard );
20
34
}
21
35
final int validActionCount = Integer .parseInt (in .readLine ());
22
- int bRow = 0 , bCol = 0 ;
23
36
for (int i = 0 ; i < validActionCount ; i ++) {
24
- line = in .readLine ().split (" " );
25
- bRow = Integer .parseInt (line [0 ]);
26
- bCol = Integer .parseInt (line [1 ]);
37
+ in .readLine ();
27
38
}
28
39
final int bestMove = algorithm .suggestMove ();
29
- final int row = bestMove / 3 , col = bestMove % 3 ;
40
+ final int row = bestMove / 9 , col = bestMove % 9 ;
41
+ System .out .println (row + " " + col );
30
42
largeBoard .play (1 , bestMove );
31
- System .out .println (((bRow / 3 ) * 3 + row ) + " " + ((bCol / 3 ) * 3 + col ));
43
+ algorithm .root = algorithm .root .getChild (bestMove );
44
+ algorithm .construct (largeBoard , MCTS .TIME_OUT );
32
45
System .err .println (largeBoard );
33
46
}
34
47
}
35
48
}
36
49
37
50
class MCTS {
38
- public static final int TIME_OUT = 200 ;
51
+ public static final int TIME_OUT = 50 ;
39
52
public static final double CONSTANT = 10000d ;
40
- private final TreeNode root = new TreeNode (-1 , null , 1 );
53
+ TreeNode root = new TreeNode (-1 , null , 1 );
41
54
42
55
public int suggestMove () {
43
56
return root .getChildren ()
44
57
.stream ()
45
58
.max (Comparator .comparingDouble (node -> node .wins / (double ) node .plays + node .plays / CONSTANT ))
46
59
.map (c -> c .col )
47
- .orElse ( 0 );
60
+ .orElseThrow (() -> new RuntimeException ( "No moves to play!" ) );
48
61
}
49
62
50
- public MCTS (final LargeBoard board ) {
63
+ public void construct (final LargeBoard board , final int timeOut ) {
51
64
final long startTime = System .currentTimeMillis ();
52
- while (System .currentTimeMillis () - startTime <= TIME_OUT ) {
65
+ while (System .currentTimeMillis () - startTime <= timeOut ) {
53
66
TreeNode current = root ;
54
67
int position = current .selectChild (board );
55
68
int player = 1 ;
@@ -111,6 +124,7 @@ private double getUtility() {
111
124
112
125
private double simulate (final LargeBoard board , int player ) {
113
126
int numberOfMovesPlayed = board .movesPlayed ;
127
+ final int originalPlayer = player ;
114
128
while (board .result () == -1 ) {
115
129
final int possibilities [] = new int [81 ];
116
130
int movesToPlay = 0 ;
@@ -120,10 +134,13 @@ private double simulate(final LargeBoard board, int player) {
120
134
movesToPlay ++;
121
135
final int result = board .result ();
122
136
if (result != -1 ) {
123
- return result == player ? 1 : (result == 0 ? 0.5 : 0 );
137
+ return result == originalPlayer ? 1 : (result == 0 ? 0.5 : 0 );
124
138
}
125
139
}
126
140
}
141
+ if (movesToPlay == 0 ) {
142
+ break ;
143
+ }
127
144
board .play (player , possibilities [random .nextInt (movesToPlay )]);
128
145
player = player == 1 ? 2 : 1 ;
129
146
}
@@ -165,21 +182,21 @@ public TreeNode getChild(final int col) {
165
182
@ Override
166
183
public String toString () {
167
184
return "TreeNode{" +
168
- "col =" + col +
169
- ", plays =" + plays +
170
- ", wins =" + wins +
171
- ", parent =" + (parent == null ? -1 : parent .col ) +
172
- ", player =" + player +
173
- ", children =" + children .values ()
185
+ "\n col =" + col +
186
+ ", \n plays =" + plays +
187
+ ", \n wins =" + wins +
188
+ ", \n parent =" + (parent == null ? -1 : parent .col ) +
189
+ ", \n player =" + player +
190
+ ", \n children =" + children .values ()
174
191
.stream ()
175
- .map (c -> "COL : " + c .col + " WINS: " + c .wins + " PLAYS: " + c .plays + "\n " )
176
- .collect (Collectors .joining (", " )) +
192
+ .map (c -> "MOVE : " + c .col + " WINS: " + c .wins + " PLAYS: " + c .plays + "\n " )
193
+ .collect (Collectors .joining ("\n " )) +
177
194
'}' ;
178
195
}
179
196
}
180
197
181
198
class LargeBoard {
182
- public static final int FULL = (1 << 10 ) - 1 ;
199
+ public static final int FULL = (1 << 9 ) - 1 ;
183
200
int movesPlayed ;
184
201
int largeBoard , largeCaptures , largeOccupied ;
185
202
final int moves [] = new int [81 ];
@@ -197,14 +214,25 @@ public void play(final int player, final int p) {
197
214
final int row = (p / 9 ) % 3 , col = p % 3 ;
198
215
if (movesPlayed > 0 ) {
199
216
final int previousMove = moves [movesPlayed - 1 ];
200
- final int pRow = previousMove / 27 , pCol = (previousMove % 9 ) / 3 ;
201
- assert (largeOccupied & (1 << (pRow * 3 + pCol ))) != 0 || (bRow == pRow && bCol == pCol );
217
+ final int pMoveRow = (previousMove / 9 ) % 3 , pMoveCol = previousMove % 3 ;
218
+ if (!((largeOccupied & (1 << (pMoveRow * 3 + pMoveCol ))) != 0 || (bRow == pMoveRow && bCol == pMoveCol ))) {
219
+ throw new RuntimeException ();
220
+ }
202
221
}
203
222
final int position = bRow * 3 + bCol ;
204
- assert (largeOccupied & (1 << position )) == 0 ;
223
+ int bit = 1 << position ;
224
+ if ((largeOccupied & bit ) != 0 ) {
225
+ throw new RuntimeException ();
226
+ }
205
227
boards [position ].play (player , row * 3 + col );
206
- if (boards [position ].occupied == FULL ) {
207
- largeOccupied = largeOccupied | (1 << position );
228
+ if (boards [position ].result (player ) == player ) {
229
+ if (player == 1 ) {
230
+ largeBoard = largeBoard | bit ;
231
+ }
232
+ largeCaptures = largeCaptures | bit ;
233
+ largeOccupied = largeOccupied | bit ;
234
+ } else if (boards [position ].occupied == FULL ) {
235
+ largeOccupied = largeOccupied | bit ;
208
236
}
209
237
movesPlayed ++;
210
238
}
@@ -223,22 +251,19 @@ public void undo() {
223
251
224
252
public int result () {
225
253
int firstScore = 0 , secondScore = 0 ;
226
- for (int i = 0 ; i < 3 ; i ++) {
227
- for (int j = 0 ; j < 3 ; j ++) {
228
- final int position = i * 3 + j ;
229
- final int bit = 1 << position ;
230
- if (boards [position ].result (1 ) == 1 ) {
231
- largeBoard = largeBoard | bit ;
232
- firstScore ++;
233
- largeCaptures = largeCaptures | bit ;
234
- largeOccupied = largeOccupied | bit ;
235
- } else if (boards [position ].result (2 ) == 2 ) {
236
- secondScore ++;
237
- largeCaptures = largeCaptures | bit ;
238
- largeOccupied = largeOccupied | bit ;
239
- } else if (boards [position ].occupied == FULL ) {
240
- largeOccupied = largeOccupied | bit ;
241
- }
254
+ for (int position = 0 ; position < 9 ; position ++) {
255
+ final int bit = 1 << position ;
256
+ if (boards [position ].result (1 ) == 1 ) {
257
+ largeBoard = largeBoard | bit ;
258
+ firstScore ++;
259
+ largeCaptures = largeCaptures | bit ;
260
+ largeOccupied = largeOccupied | bit ;
261
+ } else if (boards [position ].result (2 ) == 2 ) {
262
+ secondScore ++;
263
+ largeCaptures = largeCaptures | bit ;
264
+ largeOccupied = largeOccupied | bit ;
265
+ } else if (boards [position ].occupied == FULL ) {
266
+ largeOccupied = largeOccupied | bit ;
242
267
}
243
268
}
244
269
if (firstScore > 4 ) {
@@ -261,8 +286,8 @@ public boolean canPlay(final int p) {
261
286
final int row = (p / 9 ) % 3 , col = p % 3 ;
262
287
if (movesPlayed > 0 ) {
263
288
final int previousMove = moves [movesPlayed - 1 ];
264
- final int pRow = previousMove / 27 , pCol = ( previousMove % 9 ) / 3 ;
265
- if (!((largeOccupied & (1 << (pRow * 3 + pCol ))) != 0 || (bRow == pRow && bCol == pCol ))) {
289
+ final int pMoveRow = ( previousMove / 9 ) % 3 , pMoveCol = previousMove % 3 ;
290
+ if (!((largeOccupied & (1 << (pMoveRow * 3 + pMoveCol ))) != 0 || (bRow == pMoveRow && bCol == pMoveCol ))) {
266
291
return false ;
267
292
}
268
293
}
@@ -273,9 +298,12 @@ public boolean canPlay(final int p) {
273
298
@ Override
274
299
public String toString () {
275
300
return "LargeBoard{" +
276
- "largeBoard=" + largeBoard +
277
- ", largeCaptures=" + largeCaptures +
278
- ", boards=" + Arrays .deepToString (boards ) +
301
+ "\n largeBoard=" + largeBoard +
302
+ ", \n largeCaptures=" + largeCaptures +
303
+ ", \n largeOccupied=" + largeOccupied +
304
+ ", \n movesPlayed=" + movesPlayed +
305
+ ", \n moves=" + Arrays .toString (moves ) +
306
+ ", \n boards=" + Arrays .deepToString (boards ) +
279
307
'}' ;
280
308
}
281
309
}
@@ -297,7 +325,9 @@ class Board {
297
325
298
326
public void play (final int player , final int p ) {
299
327
final int bit = 1 << p ;
300
- assert (occupied & bit ) == 0 ;
328
+ if ((occupied & bit ) != 0 ) {
329
+ throw new RuntimeException ();
330
+ }
301
331
if (player == 1 ) {
302
332
board = board | bit ;
303
333
}
@@ -306,7 +336,9 @@ public void play(final int player, final int p) {
306
336
307
337
public void undo (final int p ) {
308
338
final int bit = 1 << p ;
309
- assert (occupied & bit ) != 0 ;
339
+ if ((occupied & bit ) == 0 ) {
340
+ throw new RuntimeException ();
341
+ }
310
342
board = board & (~bit );
311
343
occupied = occupied & (~bit );
312
344
decided = 0 ;
@@ -319,9 +351,15 @@ public int result(final int player) {
319
351
public static int evaluateBoard (final int player , int board , int occupied ) {
320
352
final int boardForPlayer = player == 1 ? board : ~board ;
321
353
final int effectiveBoard = boardForPlayer & occupied ;
322
- for (final int winningState : winningStates ) {
323
- if (effectiveBoard >= winningState ) {
324
- if (winningState == (effectiveBoard & winningState )) {
354
+ int i = 0 ;
355
+ for (; i < winningStates .length ; i ++) {
356
+ if (effectiveBoard >= winningStates [i ]) {
357
+ break ;
358
+ }
359
+ }
360
+ for (; i < winningStates .length ; i ++) {
361
+ if (effectiveBoard >= winningStates [i ]) {
362
+ if (winningStates [i ] == (effectiveBoard & winningStates [i ])) {
325
363
return player ;
326
364
}
327
365
} else {
@@ -333,6 +371,6 @@ public static int evaluateBoard(final int player, int board, int occupied) {
333
371
334
372
@ Override
335
373
public String toString () {
336
- return "Occupied:" + Integer .toBinaryString (occupied ) + "\n Board :" + Integer .toBinaryString (board );
374
+ return "Occupied:" + Integer .toBinaryString (occupied ) + " Board :" + Integer .toBinaryString (board ) + " \n " ;
337
375
}
338
376
}
0 commit comments