@@ -33,6 +33,7 @@ public static void main(String[] args) throws IOException {
3333}
3434
3535class MinMax {
36+ public static final int PIECE_VALUE = 1000 ;
3637 public static int MAX_DEPTH = 60 ;
3738 public final int TIME_OUT ;
3839 private final int movesPlayed ;
@@ -85,16 +86,12 @@ public void printStats() {
8586 }
8687
8788 private Move findBestMove (final int player , final Board board ) {
88- int toTake = startConfigs [0 ].strength - 2 , toGive = startConfigs [0 ].strength + 2 ;
89- int result = player == 1 ? MIN_VALUE : MAX_VALUE ;
89+ int toTake = startConfigs [0 ].strength - 2 * PIECE_VALUE , toGive = startConfigs [0 ].strength + 2 * PIECE_VALUE ;
90+ int result = player == 1 ? toTake : toGive ;
9091 Move bestMove = startConfigs [0 ].move ;
9192 try {
9293 for (final Configuration possibleConfig : startConfigs ) {
93- final int moveValue = evaluate (board .play (possibleConfig .move ),
94- flip (player ),
95- 0 ,
96- toTake ,
97- toGive );
94+ final int moveValue = evaluate (board .play (possibleConfig .move ), flip (player ), 0 , toTake , toGive );
9895 possibleConfig .strength = moveValue ;
9996 if (player == 1 ) {
10097 if (toTake < moveValue ) {
@@ -106,13 +103,9 @@ private Move findBestMove(final int player, final Board board) {
106103 if (player == 1 && result < moveValue ) {
107104 result = moveValue ;
108105 bestMove = possibleConfig .move ;
109- // System.out.println("RESULT: " + result + " " + possibleConfig.move + " PLAYER: " + player + " " +
110- // "depth: " + depth);
111106 } else if (player == 2 && result > moveValue ) {
112107 result = moveValue ;
113108 bestMove = possibleConfig .move ;
114- // System.out.println("RESULT: " + result + " " + possibleConfig.move + " PLAYER: " + player + " " +
115- // "depth: " + depth);
116109 }
117110 if (toTake >= toGive ) {
118111 if (possibleConfig .killer ) {
@@ -191,7 +184,121 @@ private int evaluate(final Board board,
191184 if (terminated != null ) {
192185 result = terminated ;
193186 } else if (level >= depth ) {
194- result = board .evaluatePosition (board .pieceCount [1 ], board .pieceCount [2 ]);
187+ result = quietSearch (board , player , level , a , b );
188+ } else {
189+ boolean furtherProcessingRequired = true ;
190+ if (!board .isEndGame () && depth > 4 ) {
191+ final int previousPlayer = MinMax .flip (player );
192+ final int minimumTheyWillTake = previousPlayer == 1 ? toTake : toGive - 1 ;
193+ final int nullSearchResult = nullSearch (board ,
194+ previousPlayer ,
195+ level + 3 ,
196+ minimumTheyWillTake ,
197+ minimumTheyWillTake + 1 );
198+ if (Math .abs (nullSearchResult ) == MinMax .MAX_VALUE ) {
199+ furtherProcessingRequired = true ;
200+ } else if (previousPlayer == 2 && nullSearchResult > minimumTheyWillTake ) {
201+ result = toGive ;
202+ furtherProcessingRequired = false ;
203+ } else if (previousPlayer == 1 && nullSearchResult <= minimumTheyWillTake ) {
204+ result = toTake ;
205+ furtherProcessingRequired = false ;
206+ }
207+ }
208+ if (furtherProcessingRequired ) {
209+ final Configuration [] configurations = new Configuration [board .options [player ]];
210+ for (int i = 0 ; i < configurations .length ; i ++) {
211+ configurations [i ] = new Configuration (board .moves [player ][i ],
212+ board ,
213+ level );
214+ }
215+ Arrays .sort (configurations , getConfigurationComparator (player ));
216+ for (final Configuration possibleConfig : configurations ) {
217+ computations ++;
218+ final int moveValue = evaluate (board .play (possibleConfig .move ),
219+ flip (player ),
220+ level + 1 ,
221+ toTake ,
222+ toGive );
223+ possibleConfig .strength = moveValue ;
224+ if (player == 1 ) {
225+ if (toTake < moveValue ) {
226+ toTake = moveValue ;
227+ }
228+ } else if (toGive > moveValue ) {
229+ toGive = moveValue ;
230+ }
231+ if (player == 1 && result < moveValue ) {
232+ result = moveValue ;
233+ } else if (player == 2 && result > moveValue ) {
234+ result = moveValue ;
235+ }
236+ if (toTake >= toGive ) {
237+ result = moveValue ;
238+ if (possibleConfig .killer ) {
239+ if (possibleConfig .move .equals (killerMoves [level ][0 ])) {
240+ efficiency [level ][0 ]++;
241+ } else {
242+ efficiency [level ][1 ]++;
243+ if (efficiency [level ][0 ] < efficiency [level ][1 ]) {
244+ final Move temp = killerMoves [level ][0 ];
245+ killerMoves [level ][0 ] = killerMoves [level ][1 ];
246+ killerMoves [level ][1 ] = temp ;
247+ }
248+ }
249+ } else {
250+ if (killerMoves [level ][0 ] == null ) {
251+ killerMoves [level ][0 ] = possibleConfig .move ;
252+ efficiency [level ][0 ] = 1 ;
253+ } else if (killerMoves [level ][1 ] == null || efficiency [level ][1 ] < 1 ) {
254+ killerMoves [level ][1 ] = possibleConfig .move ;
255+ efficiency [level ][1 ] = 1 ;
256+ }
257+ }
258+ break ;
259+ } else if (possibleConfig .killer ) {
260+ if (possibleConfig .move .equals (killerMoves [level ][0 ])) {
261+ efficiency [level ][0 ]--;
262+ } else if (possibleConfig .move .equals (killerMoves [level ][1 ])) {
263+ efficiency [level ][1 ]--;
264+ }
265+ if (efficiency [level ][0 ] < efficiency [level ][1 ] && killerMoves [level ][1 ] != null ) {
266+ final Move temp = killerMoves [level ][0 ];
267+ killerMoves [level ][0 ] = killerMoves [level ][1 ];
268+ killerMoves [level ][1 ] = temp ;
269+ final int t = efficiency [level ][0 ];
270+ efficiency [level ][0 ] = efficiency [level ][1 ];
271+ efficiency [level ][1 ] = t ;
272+ }
273+ if (efficiency [level ][0 ] < 0 ) {
274+ killerMoves [level ][0 ] = null ;
275+ }
276+ if (efficiency [level ][1 ] < 0 ) {
277+ killerMoves [level ][1 ] = null ;
278+ }
279+ }
280+ }
281+ }
282+ }
283+ return result ;
284+ }
285+
286+ private int nullSearch (final Board board ,
287+ final int player ,
288+ final int level ,
289+ final int a ,
290+ final int b ) throws TimeoutException {
291+ int toTake = a , toGive = b ;
292+ int result = player == 1 ? a : b ;
293+ if (!test && System .currentTimeMillis () - startTime >= TIME_OUT ) {
294+ timeOut = true ;
295+ throw new TimeoutException ();
296+ }
297+ final Integer terminated = board .isTerminated (movesPlayed + level , board .pieceCount [1 ], board .pieceCount [2 ]);
298+ if (terminated != null ) {
299+ result = terminated ;
300+ } else if (level >= depth ) {
301+ result = quietSearch (board , player , level + 1 , a , b );
195302 } else {
196303 final Configuration [] configurations = new Configuration [board .options [player ]];
197304 for (int i = 0 ; i < configurations .length ; i ++) {
@@ -202,11 +309,11 @@ private int evaluate(final Board board,
202309 Arrays .sort (configurations , getConfigurationComparator (player ));
203310 for (final Configuration possibleConfig : configurations ) {
204311 computations ++;
205- final int moveValue = evaluate (board .play (possibleConfig .move ),
206- flip (player ),
207- level + 1 ,
208- toTake ,
209- toGive );
312+ final int moveValue = nullSearch (board .play (possibleConfig .move ),
313+ flip (player ),
314+ level + 1 ,
315+ toTake ,
316+ toGive );
210317 possibleConfig .strength = moveValue ;
211318 if (player == 1 ) {
212319 if (toTake < moveValue ) {
@@ -222,52 +329,72 @@ private int evaluate(final Board board,
222329 }
223330 if (toTake >= toGive ) {
224331 result = moveValue ;
225- if (possibleConfig .killer ) {
226- if (possibleConfig .move .equals (killerMoves [level ][0 ])) {
227- efficiency [level ][0 ]++;
228- } else {
229- efficiency [level ][1 ]++;
230- if (efficiency [level ][0 ] < efficiency [level ][1 ]) {
231- final Move temp = killerMoves [level ][0 ];
232- killerMoves [level ][0 ] = killerMoves [level ][1 ];
233- killerMoves [level ][1 ] = temp ;
234- }
235- }
236- } else {
237- if (killerMoves [level ][0 ] == null ) {
238- killerMoves [level ][0 ] = possibleConfig .move ;
239- efficiency [level ][0 ] = 1 ;
240- } else if (killerMoves [level ][1 ] == null || efficiency [level ][1 ] < 1 ) {
241- killerMoves [level ][1 ] = possibleConfig .move ;
242- efficiency [level ][1 ] = 1 ;
243- }
244- }
245332 break ;
246- } else if (possibleConfig .killer ) {
247- if (possibleConfig .move .equals (killerMoves [level ][0 ])) {
248- efficiency [level ][0 ]--;
249- } else if (possibleConfig .move .equals (killerMoves [level ][1 ])) {
250- efficiency [level ][1 ]--;
251- }
252- if (efficiency [level ][0 ] < efficiency [level ][1 ] && killerMoves [level ][1 ] != null ) {
253- final Move temp = killerMoves [level ][0 ];
254- killerMoves [level ][0 ] = killerMoves [level ][1 ];
255- killerMoves [level ][1 ] = temp ;
256- final int t = efficiency [level ][0 ];
257- efficiency [level ][0 ] = efficiency [level ][1 ];
258- efficiency [level ][1 ] = t ;
333+ }
334+ }
335+ }
336+ return result ;
337+ }
338+
339+ private int quietSearch (final Board board ,
340+ final int player ,
341+ final int level ,
342+ final int a ,
343+ final int b ) throws TimeoutException {
344+ int toTake = a , toGive = b ;
345+ int result = player == 1 ? a : b ;
346+ if (!test && System .currentTimeMillis () - startTime >= TIME_OUT ) {
347+ timeOut = true ;
348+ throw new TimeoutException ();
349+ }
350+ final Integer terminated = board .isTerminated (movesPlayed + level , board .pieceCount [1 ], board .pieceCount [2 ]);
351+ if (terminated != null ) {
352+ result = terminated ;
353+ } else if (level >= depth + 5 ) {
354+ result = board .evaluatePosition (board .pieceCount [1 ], board .pieceCount [2 ]);
355+ } else {
356+ final List <Move > captureMoves = Arrays .stream (board .moves [player ])
357+ .filter (Objects ::nonNull )
358+ .filter (Move ::isACapture )
359+ .collect (Collectors .toList ());
360+ if (captureMoves .isEmpty ()) {
361+ result = board .evaluatePosition (board .pieceCount [1 ], board .pieceCount [2 ]);
362+ } else {
363+ //System.out.println("Quiet search level: " + (level - depth) + " move: " + captureMoves.size());
364+ final Configuration [] configurations = new Configuration [captureMoves .size ()];
365+ for (int i = 0 ; i < captureMoves .size (); i ++) {
366+ configurations [i ] = new Configuration (captureMoves .get (i ),
367+ board ,
368+ level );
369+ }
370+ Arrays .sort (configurations , getConfigurationComparator (player ));
371+ for (final Configuration possibleConfig : configurations ) {
372+ computations ++;
373+ final int moveValue = quietSearch (board .play (possibleConfig .move ),
374+ flip (player ),
375+ level + 1 ,
376+ toTake ,
377+ toGive );
378+ possibleConfig .strength = moveValue ;
379+ if (player == 1 ) {
380+ if (toTake < moveValue ) {
381+ toTake = moveValue ;
382+ }
383+ } else if (toGive > moveValue ) {
384+ toGive = moveValue ;
259385 }
260- if (efficiency [level ][0 ] < 0 ) {
261- killerMoves [level ][0 ] = null ;
386+ if (player == 1 && result < moveValue ) {
387+ result = moveValue ;
388+ } else if (player == 2 && result > moveValue ) {
389+ result = moveValue ;
262390 }
263- if (efficiency [level ][1 ] < 0 ) {
264- killerMoves [level ][1 ] = null ;
391+ if (toTake >= toGive ) {
392+ result = moveValue ;
393+ break ;
265394 }
266395 }
267396 }
268397 }
269- //System.out.println("LEVEL: " + level + " " + Arrays.toString(killerMoves[level]));
270- //board.undo(move);
271398 return result ;
272399 }
273400
@@ -448,7 +575,7 @@ public String toString() {
448575
449576 public boolean isACapture () {
450577 //TODO: Add quiet search
451- return capturedPieces .isEmpty ();
578+ return ! capturedPieces .isEmpty ();
452579 }
453580
454581 public String describe () {
@@ -768,20 +895,20 @@ public int heuristicValue(final int movesPlayed,
768895 final int firstPlayerPieceCount ,
769896 final int secondPlayerPieceCount ) {
770897 final Integer terminated = isTerminated (movesPlayed , firstPlayerPieceCount , secondPlayerPieceCount );
771- return terminated != null ? terminated : evaluatePosition ( firstPlayerPieceCount , secondPlayerPieceCount ) ;
898+ return terminated != null ? terminated : firstPlayerPieceCount - secondPlayerPieceCount ;
772899 }
773900
774901 public int evaluatePosition (final int firstPlayerPieceCount , final int secondPlayerPieceCount ) {
775- return firstPlayerPieceCount - secondPlayerPieceCount ;
902+ return ( firstPlayerPieceCount - secondPlayerPieceCount ) * MinMax . PIECE_VALUE ;
776903 }
777904
778905 public Integer isTerminated (final int moveNumber ,
779- final int firstPLayerPieceCount ,
780- final int secondPLayerPieceCount ) {
781- final boolean hasEnded = moveNumber >= 100 || firstPLayerPieceCount == 0 || secondPLayerPieceCount == 0 ;
906+ final int firstPlayerPieceCount ,
907+ final int secondPlayerPieceCount ) {
908+ final boolean hasEnded = moveNumber >= 100 || firstPlayerPieceCount == 0 || secondPlayerPieceCount == 0 ;
782909 if (hasEnded ) {
783- assert firstPLayerPieceCount + secondPLayerPieceCount > 0 ;
784- return firstPLayerPieceCount > secondPLayerPieceCount ? MinMax .MAX_VALUE : MinMax .MIN_VALUE ;
910+ assert firstPlayerPieceCount + secondPlayerPieceCount > 0 ;
911+ return firstPlayerPieceCount > secondPlayerPieceCount ? MinMax .MAX_VALUE : MinMax .MIN_VALUE ;
785912 } else {
786913 return null ;
787914 }
@@ -813,6 +940,10 @@ private String toReadableString() {
813940 return stringBuilder .toString ();
814941 }
815942
943+ public boolean isEndGame () {
944+ return pieceCount [1 ] + pieceCount [2 ] < 9 ;
945+ }
946+
816947 public static class Cell {
817948 final int x , y ;
818949
0 commit comments