From ab87bb9ae88b1b8801da44d7354e143332bd5dc8 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 15 Jul 2012 10:39:23 +0000 Subject: [PATCH] CuckooChess: Back-ported Texel improvements to CuckooChess. +24 ELO against previous version after 1894 games at 60/6 time control. --- CuckooChess/src/uci/EngineControl.java | 6 +- CuckooChessEngine/src/chess/BitBoard.java | 123 +++++++++++------- .../src/chess/ComputerPlayer.java | 8 +- CuckooChessEngine/src/chess/Evaluate.java | 108 ++++++++++----- CuckooChessEngine/src/chess/History.java | 10 +- CuckooChessEngine/src/chess/Move.java | 16 ++- CuckooChessEngine/src/chess/Position.java | 30 +---- CuckooChessEngine/src/chess/Search.java | 29 +++-- .../src/chess/TranspositionTable.java | 4 +- .../test/chess/BitBoardTest.java | 13 ++ .../test/chess/EvaluateTest.java | 35 ++++- CuckooChessEngine/test/chess/SearchTest.java | 95 +++++++------- 12 files changed, 292 insertions(+), 185 deletions(-) diff --git a/CuckooChess/src/uci/EngineControl.java b/CuckooChess/src/uci/EngineControl.java index 9d191f5..f6a395c 100644 --- a/CuckooChess/src/uci/EngineControl.java +++ b/CuckooChess/src/uci/EngineControl.java @@ -20,6 +20,7 @@ package uci; import chess.Book; import chess.ComputerPlayer; +import chess.History; import chess.Move; import chess.MoveGen; import chess.Parameters; @@ -51,6 +52,7 @@ public class EngineControl { private final Object threadMutex; Search sc; TranspositionTable tt; + History ht; MoveGen moveGen; Position pos; @@ -120,6 +122,7 @@ public class EngineControl { this.os = os; threadMutex = new Object(); setupTT(); + ht = new History(); moveGen = new MoveGen(); } @@ -163,6 +166,7 @@ public class EngineControl { final public void newGame() { randomSeed = new Random().nextLong(); tt.clear(); + ht.init(); } /** @@ -222,7 +226,7 @@ public class EngineControl { final private void startThread(final int minTimeLimit, final int maxTimeLimit, int maxDepth, final int maxNodes) { synchronized (threadMutex) {} // Must not start new search until old search is finished - sc = new Search(pos, posHashList, posHashListSize, tt); + sc = new Search(pos, posHashList, posHashListSize, tt, ht); sc.timeLimit(minTimeLimit, maxTimeLimit); sc.setListener(new SearchListener(os)); sc.setStrength(strength, randomSeed); diff --git a/CuckooChessEngine/src/chess/BitBoard.java b/CuckooChessEngine/src/chess/BitBoard.java index 15e7e26..cc2d6b8 100644 --- a/CuckooChessEngine/src/chess/BitBoard.java +++ b/CuckooChessEngine/src/chess/BitBoard.java @@ -116,30 +116,30 @@ public class BitBoard { private final static long[][] rTables; private final static long[] rMasks; private final static int[] rBits = { 12, 11, 11, 11, 11, 11, 11, 12, + 11, 10, 10, 11, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 11, 10, 10, 10, 11, 10, 9, 9, 9, 9, 9, 10, 10, - 11, 10, 10, 10, 10, 11, 11, 11 }; + 11, 10, 10, 10, 10, 11, 10, 11 }; private final static long[] rMagics = { - 0x0080011084624000L, 0x1440031000200141L, 0x2080082004801000L, 0x0100040900100020L, - 0x0200020010200408L, 0x0300010008040002L, 0x040024081000a102L, 0x0080003100054680L, - 0x1100800040008024L, 0x8440401000200040L, 0x0432001022008044L, 0x0402002200100840L, - 0x4024808008000400L, 0x100a000410820008L, 0x8042001144020028L, 0x2451000041002082L, - 0x1080004000200056L, 0xd41010c020004000L, 0x0004410020001104L, 0x0000818050000800L, - 0x0000050008010010L, 0x0230808002000400L, 0x2000440090022108L, 0x0488020000811044L, - 0x8000410100208006L, 0x2000a00240100140L, 0x2088802200401600L, 0x0a10100180080082L, - 0x0000080100110004L, 0x0021002300080400L, 0x8400880400010230L, 0x2001008200004401L, - 0x0000400022800480L, 0x00200040e2401000L, 0x4004100084802000L, 0x0218800800801002L, - 0x0420800800800400L, 0x002a000402001008L, 0x0e0b000401008200L, 0x0815908072000401L, - 0x1840008002498021L, 0x1070122002424000L, 0x1040200100410010L, 0x0600080010008080L, - 0x0215001008010004L, 0x0000020004008080L, 0x1300021051040018L, 0x0004040040820001L, - 0x48fffe99fecfaa00L, 0x48fffe99fecfaa00L, 0x497fffadff9c2e00L, 0x613fffddffce9200L, - 0xffffffe9ffe7ce00L, 0xfffffff5fff3e600L, 0x2000080281100400L, 0x510ffff5f63c96a0L, - 0xebffffb9ff9fc526L, 0x61fffeddfeedaeaeL, 0x53bfffedffdeb1a2L, 0x127fffb9ffdfb5f6L, - 0x411fffddffdbf4d6L, 0x0005000208040001L, 0x264038060100d004L, 0x7645fffecbfea79eL, + 0x19a80065ff2bffffL, 0x3fd80075ffebffffL, 0x4010000df6f6fffeL, 0x0050001faffaffffL, + 0x0050028004ffffb0L, 0x7f600280089ffff1L, 0x7f5000b0029ffffcL, 0x5b58004848a7fffaL, + 0x002a90005547ffffL, 0x000050007f13ffffL, 0x007fa0006013ffffL, 0x006a9005656fffffL, + 0x007f600f600affffL, 0x007ec007e6bfffe2L, 0x007ec003eebffffbL, 0x0071d002382fffdaL, + 0x009f803000e7fffaL, 0x00680030008bffffL, 0x00606060004f3ffcL, 0x001a00600bff9ffdL, + 0x000d006005ff9fffL, 0x0001806003005fffL, 0x00000300040bfffaL, 0x000192500065ffeaL, + 0x00fff112d0006800L, 0x007ff037d000c004L, 0x003fd062001a3ff8L, 0x00087000600e1ffcL, + 0x000fff0100100804L, 0x0007ff0100080402L, 0x0003ffe0c0060003L, 0x0001ffd53000d300L, + 0x00fffd3000600061L, 0x007fff7f95900040L, 0x003fff8c00600060L, 0x001ffe2587a01860L, + 0x000fff3fbf40180cL, 0x0007ffc73f400c06L, 0x0003ff86d2c01405L, 0x0001fffeaa700100L, + 0x00fffdfdd8005000L, 0x007fff80ebffb000L, 0x003fffdf603f6000L, 0x001fffe050405000L, + 0x000fff400700c00cL, 0x0007ff6007bf600aL, 0x0003ffeebffec005L, 0x0001fffdf3feb001L, + 0x00ffff39ff484a00L, 0x007fff3fff486300L, 0x003fff99ffac2e00L, 0x001fff31ff2a6a00L, + 0x000fff19ff15b600L, 0x0007fff5fff28600L, 0x0003fffddffbfee0L, 0x0001fff5f63c96a0L, + 0x00ffff5dff65cfb6L, 0x007fffbaffd1c5aeL, 0x003fff71ff6cbceaL, 0x001fffd9ffd4756eL, + 0x000ffff5fff338e6L, 0x0007fffdfffe24f6L, 0x0003ffef27eebe74L, 0x0001ffff23ff605eL }; private final static long[][] bTables; private final static long[] bMasks; @@ -152,22 +152,22 @@ public class BitBoard { 4, 4, 5, 5, 5, 5, 4, 4, 5, 4, 5, 5, 5, 5, 4, 5 }; private final static long[] bMagics = { - 0xffedf9fd7cfcffffL, 0xfc0962854a77f576L, 0x9010210041047000L, 0x52242420800c0000L, - 0x884404220480004aL, 0x0002080248000802L, 0xfc0a66c64a7ef576L, 0x7ffdfdfcbd79ffffL, - 0xfc0846a64a34fff6L, 0xfc087a874a3cf7f6L, 0x02000888010a2211L, 0x0040044040801808L, - 0x0880040420000000L, 0x0000084110109000L, 0xfc0864ae59b4ff76L, 0x3c0860af4b35ff76L, - 0x73c01af56cf4cffbL, 0x41a01cfad64aaffcL, 0x1010000200841104L, 0x802802142a006000L, - 0x0a02000412020020L, 0x0000800040504030L, 0x7c0c028f5b34ff76L, 0xfc0a028e5ab4df76L, - 0x0020082044905488L, 0xa572211102080220L, 0x0014020001280300L, 0x0220208058008042L, - 0x0001010000104016L, 0x0005114028080800L, 0x0202640000848800L, 0x040040900a008421L, - 0x400e094000600208L, 0x800a100400120890L, 0x0041229001480020L, 0x0000020080880082L, - 0x0040002020060080L, 0x1819100100c02400L, 0x04112a4082c40400L, 0x0001240130210500L, - 0xdcefd9b54bfcc09fL, 0xf95ffa765afd602bL, 0x008200222800a410L, 0x0100020102406400L, - 0x80a8040094000200L, 0x002002006200a041L, 0x43ff9a5cf4ca0c01L, 0x4bffcd8e7c587601L, - 0xfc0ff2865334f576L, 0xfc0bf6ce5924f576L, 0x0900420442088104L, 0x0062042084040010L, - 0x01380810220a0240L, 0x0000101002082800L, 0xc3ffb7dc36ca8c89L, 0xc3ff8a54f4ca2c89L, - 0xfffffcfcfd79edffL, 0xfc0863fccb147576L, 0x0050009040441000L, 0x00139a0000840400L, - 0x9080000412220a00L, 0x0000002020010a42L, 0xfc087e8e4bb2f736L, 0x43ff9e4ef4ca2c89L, + 0x0006eff5367ff600L, 0x00345835ba77ff2bL, 0x00145f68a3f5dab6L, 0x003a1863fb56f21dL, + 0x0012eb6bfe9d93cdL, 0x000d82827f3420d6L, 0x00074bcd9c7fec97L, 0x000034fe99f9ffffL, + 0x0000746f8d6717f6L, 0x00003acb32e1a3f7L, 0x0000185daf1ffb8aL, 0x00003a1867f17067L, + 0x0000038ee0ccf92eL, 0x000002a2b7ff926eL, 0x000006c9aa93ff14L, 0x00000399b5e5bf87L, + 0x00400f342c951ffcL, 0x0020230579ed8ff0L, 0x007b008a0077dbfdL, 0x001d00010c13fd46L, + 0x00040022031c1ffbL, 0x000fa00fd1cbff79L, 0x000400a4bc9affdfL, 0x000200085e9cffdaL, + 0x002a14560a3dbfbdL, 0x000a0a157b9eafd1L, 0x00060600fd002ffaL, 0x004006000c009010L, + 0x001a002042008040L, 0x001a00600fd1ffc0L, 0x000d0ace50bf3f8dL, 0x000183a48434efd1L, + 0x001fbd7670982a0dL, 0x000fe24301d81a0fL, 0x0007fbf82f040041L, 0x000040c800008200L, + 0x007fe17018086006L, 0x003b7ddf0ffe1effL, 0x001f92f861df4a0aL, 0x000fd713ad98a289L, + 0x000fd6aa751e400cL, 0x0007f2a63ae9600cL, 0x0003ff7dfe0e3f00L, 0x000003fd2704ce04L, + 0x00007fc421601d40L, 0x007fff5f70900120L, 0x003fa66283556403L, 0x001fe31969aec201L, + 0x0007fdfc18ac14bbL, 0x0003fb96fb568a47L, 0x000003f72ea4954dL, 0x00000003f8dc0383L, + 0x0000007f3a814490L, 0x00007dc5c9cf62a6L, 0x007f23d3342897acL, 0x003fee36eee1565cL, + 0x0003ff3e99fcccc7L, 0x000003ecfcfac5feL, 0x00000003f97f7453L, 0x0000000003f8dc03L, + 0x000000007efa8146L, 0x0000007ed3e2ef60L, 0x00007f47243adcd6L, 0x007fb65afabfb3b5L }; private static final long createPattern(int i, long mask) { @@ -306,21 +306,21 @@ public class BitBoard { } private static final byte dirTable[] = { - -9, 0, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, 0, -7, - 0, 0, -9, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, -7, 0, - 0, 0, 0, -9, 0, 0, 0, 0, -8, 0, 0, 0, 0, -7, 0, 0, - 0, 0, 0, 0, -9, 0, 0, 0, -8, 0, 0, 0, -7, 0, 0, 0, - 0, 0, 0, 0, 0, -9, 0, 0, -8, 0, 0, -7, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -9, -17, -8, -15, -7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -10, -9, -8, -7, -6, 0, 0, 0, 0, 0, - 0, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 15, 8, 17, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 7, 0, 0, 0, 0, 8, 0, 0, 0, 0, 9, 0, 0, - 0, 0, 7, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 9, 0, - 0, 7, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9 + -9, 0, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, 0, -7, + 0, 0, -9, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, -7, 0, + 0, 0, 0, -9, 0, 0, 0, 0, -8, 0, 0, 0, 0, -7, 0, 0, + 0, 0, 0, 0, -9, 0, 0, 0, -8, 0, 0, 0, -7, 0, 0, 0, + 0, 0, 0, 0, 0, -9, 0, 0, -8, 0, 0, -7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -9,-17, -8,-15, -7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,-10, -9, -8, -7, -6, 0, 0, 0, 0, 0, + 0, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 15, 8, 17, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, + 0, 0, 0, 7, 0, 0, 0, 0, 8, 0, 0, 0, 0, 9, 0, 0, + 0, 0, 7, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 9, 0, + 0, 7, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9 }; static public final int getDirection(int from, int to) { @@ -328,6 +328,29 @@ public class BitBoard { return dirTable[offs]; } + private static final byte distTable[] = { + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, + 0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, + 0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, + 0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, + 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + }; + + public static final int getDistance(int from, int to) { + int offs = to + (to|7) - from - (from|7) + 0x77; + return distTable[offs]; + } + public static final long southFill(long mask) { mask |= (mask >>> 8); mask |= (mask >>> 16); diff --git a/CuckooChessEngine/src/chess/ComputerPlayer.java b/CuckooChessEngine/src/chess/ComputerPlayer.java index f5a1b92..657c075 100644 --- a/CuckooChessEngine/src/chess/ComputerPlayer.java +++ b/CuckooChessEngine/src/chess/ComputerPlayer.java @@ -30,7 +30,7 @@ public class ComputerPlayer implements Player { public static final String engineName; static { - String name = "CuckooChess 1.13a8"; + String name = "CuckooChess 1.13a9"; String m = System.getProperty("sun.arch.data.model"); if ("32".equals(m)) name += " 32-bit"; @@ -80,7 +80,8 @@ public class ComputerPlayer implements Player { posHashList[posHashListSize++] = p.zobristHash(); } tt.nextGeneration(); - Search sc = new Search(pos, posHashList, posHashListSize, tt); + History ht = new History(); + Search sc = new Search(pos, posHashList, posHashListSize, tt, ht); // Determine all legal moves MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos); @@ -186,7 +187,8 @@ public class ComputerPlayer implements Player { // Create a search object long[] posHashList = new long[200]; tt.nextGeneration(); - Search sc = new Search(pos, posHashList, 0, tt); + History ht = new History(); + Search sc = new Search(pos, posHashList, 0, tt, ht); // Determine all legal moves MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos); diff --git a/CuckooChessEngine/src/chess/Evaluate.java b/CuckooChessEngine/src/chess/Evaluate.java index c553021..ee08b26 100644 --- a/CuckooChessEngine/src/chess/Evaluate.java +++ b/CuckooChessEngine/src/chess/Evaluate.java @@ -484,61 +484,88 @@ public class Evaluate { score -= interpolate(pos.wMtrl - pos.wMtrlPawns, 0, 2 * phd.passedBonusB, hiMtrl, phd.passedBonusB); // Passed pawns are more dangerous if enemy king is far away - int mtrlNoPawns; - final int highMtrl = qV + rV; + int bestWPawnDist = 8; + int bestWPromSq = -1; long m = phd.passedPawnsW; if (m != 0) { - mtrlNoPawns = pos.bMtrl - pos.bMtrlPawns; - if (mtrlNoPawns < highMtrl) { + int mtrlNoPawns = pos.bMtrl - pos.bMtrlPawns; + if (mtrlNoPawns < hiMtrl) { int kingPos = pos.getKingSq(false); - int kingX = Position.getX(kingPos); - int kingY = Position.getY(kingPos); while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); int x = Position.getX(sq); int y = Position.getY(sq); int pawnDist = Math.min(5, 7 - y); - int kingDistX = Math.abs(kingX - x); - int kingDistY = Math.abs(kingY - 7); - int kingDist = Math.max(kingDistX, kingDistY); + int kingDist = BitBoard.getDistance(kingPos, Position.getSquare(x, 7)); int kScore = kingDist * 4; if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); - score += interpolate(mtrlNoPawns, 0, kScore, highMtrl, 0); + score += interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); if (!pos.whiteMove) kingDist--; - if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) - score += 500; // King can't stop pawn + if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { + if ((BitBoard.northFill(1L< pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); - score -= interpolate(mtrlNoPawns, 0, kScore, highMtrl, 0); + score -= interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); if (pos.whiteMove) kingDist--; - if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) - score -= 500; // King can't stop pawn + if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { + if ((BitBoard.southFill(1L<= 0) { + if (bestBPromSq >= 0) { + int wPly = bestWPawnDist * 2; if (pos.whiteMove) wPly--; + int bPly = bestBPawnDist * 2; if (!pos.whiteMove) bPly--; + if (wPly < bPly - 1) { + score += 500; + } else if (wPly == bPly - 1) { + if (BitBoard.getDirection(bestWPromSq, pos.getKingSq(false)) != 0) + score += 500; + } else if (wPly == bPly + 1) { + if (BitBoard.getDirection(bestBPromSq, pos.getKingSq(true)) != 0) + score -= 500; + } else { + score -= 500; + } + } else + score += 500; + } else if (bestBPromSq >= 0) + score -= 500; + return score; } @@ -585,7 +612,7 @@ public class Evaluate { // Evaluate passed pawn bonus, white long passedPawnsW = wPawns & ~BitBoard.southFill(bPawns | bPawnAttacks | (wPawns >>> 8)); - final int[] ppBonus = {-1,24,26,30,36,47,64,-1}; + final int[] ppBonus = {-1,24,26,30,36,55,100,-1}; int passedBonusW = 0; if (passedPawnsW != 0) { long guardedPassedW = passedPawnsW & (((wPawns & BitBoard.maskBToHFiles) << 7) | @@ -616,11 +643,19 @@ public class Evaluate { } } - // Connected passed pawn bonus. Seems logical but doesn't help in tests -// if (passedPawnsW != 0) -// passedBonusW += 15 * Long.bitCount(passedPawnsW & ((passedPawnsW & BitBoard.maskBToHFiles) >>> 1)); -// if (passedPawnsB != 0) -// passedBonusB += 15 * Long.bitCount(passedPawnsB & ((passedPawnsB & BitBoard.maskBToHFiles) >>> 1)); + // Connected passed pawn bonus. Seems logical but scored -8 elo in tests +// if (passedPawnsW != 0) { +// long mask = passedPawnsW; +// mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | +// (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); +// passedBonusW += 13 * Long.bitCount(passedPawnsW & mask); +// } +// if (passedPawnsB != 0) { +// long mask = passedPawnsB; +// mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | +// (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); +// passedBonusB += 13 * Long.bitCount(passedPawnsB & mask); +// } ph.key = pos.pawnZobristHash(); ph.score = score; @@ -954,7 +989,7 @@ public class Evaluate { int wq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WQUEEN]); int bk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BKING]); int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); - score = evalKQKP(wk, wq, bk, bp); + score = evalKQKP(wk, wq, bk, bp, pos.whiteMove); handled = true; } if (!handled && (pos.wMtrl == rV) && (pos.pieceTypeBB[Piece.WROOK] != 0)) { @@ -981,7 +1016,7 @@ public class Evaluate { int bq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BQUEEN]); int wk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WKING]); int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); - score = -evalKQKP(63-bk, 63-bq, 63-wk, 63-wp); + score = -evalKQKP(63-bk, 63-bq, 63-wk, 63-wp, !pos.whiteMove); handled = true; } if (!handled && (pos.bMtrl == rV) && (pos.pieceTypeBB[Piece.BROOK] != 0)) { @@ -1031,7 +1066,7 @@ public class Evaluate { if (wMtrlNoPawns - bMtrlNoPawns > bV) { int wKnights = Long.bitCount(pos.pieceTypeBB[Piece.WKNIGHT]); int wBishops = Long.bitCount(pos.pieceTypeBB[Piece.WBISHOP]); - if ((wKnights == 2) && (wMtrlNoPawns == 2 * nV) && (bMtrlNoPawns == 0)) { + if ((wKnights == 2) && (pos.wMtrl == 2 * nV) && (bMtrlNoPawns == 0)) { score /= 50; // KNNK is a draw } else if ((wKnights == 1) && (wBishops == 1) && (wMtrlNoPawns == nV + bV) && (bMtrlNoPawns == 0)) { score /= 10; @@ -1084,7 +1119,7 @@ public class Evaluate { if (bMtrlNoPawns - wMtrlNoPawns > bV) { int bKnights = Long.bitCount(pos.pieceTypeBB[Piece.BKNIGHT]); int bBishops = Long.bitCount(pos.pieceTypeBB[Piece.BBISHOP]); - if ((bKnights == 2) && (bMtrlNoPawns == 2 * nV) && (wMtrlNoPawns == 0)) { + if ((bKnights == 2) && (pos.bMtrl == 2 * nV) && (wMtrlNoPawns == 0)) { score /= 50; // KNNK is a draw } else if ((bKnights == 1) && (bBishops == 1) && (bMtrlNoPawns == nV + bV) && (wMtrlNoPawns == 0)) { score /= 10; @@ -1115,7 +1150,7 @@ public class Evaluate { // FIXME! KRBKR is very hard to draw } - private static final int evalKQKP(int wKing, int wQueen, int bKing, int bPawn) { + private static final int evalKQKP(int wKing, int wQueen, int bKing, int bPawn, boolean whiteMove) { boolean canWin = false; if (((1L << bKing) & 0xFFFF) == 0) { canWin = true; // King doesn't support pawn @@ -1125,6 +1160,8 @@ public class Evaluate { switch (bPawn) { case 8: // a2 canWin = ((1L << wKing) & 0x0F1F1F1F1FL) != 0; + if (canWin && (bKing == 0) && (Position.getX(wQueen) == 1) && !whiteMove) + canWin = false; // Stale-mate break; case 10: // c2 canWin = ((1L << wKing) & 0x071F1F1FL) != 0; @@ -1134,6 +1171,8 @@ public class Evaluate { break; case 15: // h2 canWin = ((1L << wKing) & 0xF0F8F8F8F8L) != 0; + if (canWin && (bKing == 7) && (Position.getX(wQueen) == 6) && !whiteMove) + canWin = false; // Stale-mate break; default: canWin = true; @@ -1141,8 +1180,7 @@ public class Evaluate { } } - final int dist = Math.max(Math.abs(Position.getX(wKing)-Position.getX(bPawn)), - Math.abs(Position.getY(wKing)-Position.getY(bPawn))); + final int dist = BitBoard.getDistance(wKing, bPawn); int score = qV - pV - 20 * dist; if (!canWin) score /= 50; diff --git a/CuckooChessEngine/src/chess/History.java b/CuckooChessEngine/src/chess/History.java index 4935264..58597ad 100644 --- a/CuckooChessEngine/src/chess/History.java +++ b/CuckooChessEngine/src/chess/History.java @@ -23,11 +23,15 @@ package chess; * @author petero */ public final class History { - private final int countSuccess[][]; - private final int countFail[][]; - private final int score[][]; + private int countSuccess[][]; + private int countFail[][]; + private int score[][]; public History() { + init(); + } + + public void init() { countSuccess = new int[Piece.nPieceTypes][64]; countFail = new int[Piece.nPieceTypes][64]; score = new int[Piece.nPieceTypes][64]; diff --git a/CuckooChessEngine/src/chess/Move.java b/CuckooChessEngine/src/chess/Move.java index 7f7cc51..c75a679 100644 --- a/CuckooChessEngine/src/chess/Move.java +++ b/CuckooChessEngine/src/chess/Move.java @@ -64,13 +64,27 @@ public class Move { this.score = m.score; } - public void copyFrom(Move m) { + public final void copyFrom(Move m) { from = m.from; to = m.to; promoteTo = m.promoteTo; // score = m.score; } + public final void clear() { + from = 0; + to = 0; + promoteTo = 0; + score = 0; + } + + public final void setMove(int from, int to, int promoteTo, int score) { + this.from = from; + this.to = to; + this.promoteTo = promoteTo; + this.score = score; + } + /** Note that score is not included in the comparison. */ @Override public boolean equals(Object o) { diff --git a/CuckooChessEngine/src/chess/Position.java b/CuckooChessEngine/src/chess/Position.java index c5deab7..856b4e4 100644 --- a/CuckooChessEngine/src/chess/Position.java +++ b/CuckooChessEngine/src/chess/Position.java @@ -529,22 +529,8 @@ public class Position { */ public final void makeSEEMove(Move move, UndoInfo ui) { ui.capturedPiece = squares[move.to]; - boolean wtm = whiteMove; int p = squares[move.from]; - long fromMask = 1L << move.from; - - // Handle castling - if (((pieceTypeBB[Piece.WKING] | pieceTypeBB[Piece.BKING]) & fromMask) != 0) { - int k0 = move.from; - if (move.to == k0 + 2) { // O-O - setSEEPiece(k0 + 1, squares[k0 + 3]); - setSEEPiece(k0 + 3, Piece.EMPTY); - } else if (move.to == k0 - 2) { // O-O-O - setSEEPiece(k0 - 1, squares[k0 - 4]); - setSEEPiece(k0 - 4, Piece.EMPTY); - } - } // Handle en passant if (move.to == epSquare) { @@ -558,7 +544,7 @@ public class Position { // Perform move setSEEPiece(move.from, Piece.EMPTY); setSEEPiece(move.to, p); - whiteMove = !wtm; + whiteMove = !whiteMove; } public final void unMakeSEEMove(Move move, UndoInfo ui) { @@ -566,20 +552,6 @@ public class Position { int p = squares[move.to]; setSEEPiece(move.from, p); setSEEPiece(move.to, ui.capturedPiece); - boolean wtm = whiteMove; - - // Handle castling - int king = wtm ? Piece.WKING : Piece.BKING; - if (p == king) { - int k0 = move.from; - if (move.to == k0 + 2) { // O-O - setSEEPiece(k0 + 3, squares[k0 + 1]); - setSEEPiece(k0 + 1, Piece.EMPTY); - } else if (move.to == k0 - 2) { // O-O-O - setSEEPiece(k0 - 4, squares[k0 - 1]); - setSEEPiece(k0 - 1, Piece.EMPTY); - } - } // Handle en passant if (move.to == epSquare) { diff --git a/CuckooChessEngine/src/chess/Search.java b/CuckooChessEngine/src/chess/Search.java index 9770cb9..d2cc91f 100644 --- a/CuckooChessEngine/src/chess/Search.java +++ b/CuckooChessEngine/src/chess/Search.java @@ -87,15 +87,16 @@ public class Search { public final static int UNKNOWN_SCORE = -32767; // Represents unknown static eval score int q0Eval; // Static eval score at first level of quiescence search - public Search(Position pos, long[] posHashList, int posHashListSize, TranspositionTable tt) { + public Search(Position pos, long[] posHashList, int posHashListSize, TranspositionTable tt, + History ht) { this.pos = new Position(pos); this.moveGen = new MoveGen(); this.posHashList = posHashList; this.posHashListSize = posHashListSize; this.tt = tt; + this.ht = ht; eval = new Evaluate(); kt = new KillerTable(); - ht = new History(); posHashFirstNew = posHashListSize; initNodeStats(); minTimeMillis = -1; @@ -519,23 +520,23 @@ public class Search { evalScore = ent.evalScore; int plyToMate = MATE0 - Math.abs(score); int eDepth = ent.getDepth(); + hashMove = sti.hashMove; + ent.getMove(hashMove); if ((beta == alpha + 1) && ((eDepth >= depth) || (eDepth >= plyToMate*plyScale))) { if ( (ent.type == TTEntry.T_EXACT) || (ent.type == TTEntry.T_GE) && (score >= beta) || (ent.type == TTEntry.T_LE) && (score <= alpha)) { if (score >= beta) { hashMove = sti.hashMove; - ent.getMove(hashMove); if ((hashMove != null) && (hashMove.from != hashMove.to)) if (pos.getPiece(hashMove.to) == Piece.EMPTY) kt.addKiller(ply, hashMove); } + sti.bestMove = hashMove; if (log != null) log.logNodeEnd(searchTreeInfo[ply].nodeIdx, score, ent.type, evalScore, hKey); return score; } } - hashMove = sti.hashMove; - ent.getMove(hashMove); } int posExtend = inCheck ? plyScale : 0; // Check extension @@ -543,6 +544,7 @@ public class Search { // If out of depth, perform quiescence search if (depth + posExtend <= 0) { q0Eval = evalScore; + sti.bestMove.clear(); int score = quiesce(alpha, beta, ply, 0, inCheck); int type = TTEntry.T_EXACT; if (score <= alpha) { @@ -550,8 +552,8 @@ public class Search { } else if (score >= beta) { type = TTEntry.T_GE; } - emptyMove.score = score; - tt.insert(hKey, emptyMove, type, ply, depth, q0Eval); + sti.bestMove.score = score; + tt.insert(hKey, sti.bestMove, type, ply, depth, q0Eval); if (log != null) log.logNodeEnd(sti.nodeIdx, score, type, q0Eval, hKey); return score; } @@ -628,6 +630,7 @@ public class Search { int epSquare = pos.getEpSquare(); pos.setEpSquare(-1); searchTreeInfo[ply+1].allowNullMove = false; + searchTreeInfo[ply+1].bestMove.clear(); int score = -negaScout(-beta, -(beta - 1), ply + 1, depth - R, -1, false); searchTreeInfo[ply+1].allowNullMove = true; pos.setEpSquare(epSquare); @@ -955,7 +958,7 @@ public class Search { if (score > alpha) alpha = score; int bestScore = score; - final boolean tryChecks = (depth > -3); + final boolean tryChecks = (depth > -1); MoveGen.MoveList moves; if (inCheck) { moves = moveGen.checkEvasions(pos); @@ -1001,7 +1004,7 @@ public class Search { if (optimisticScore < alpha) { // Delta pruning if ((pos.wMtrlPawns > 0) && (pos.wMtrl > capt + pos.wMtrlPawns) && (pos.bMtrlPawns > 0) && (pos.bMtrl > capt + pos.bMtrlPawns)) { - if (depth -1 > -4) { + if (depth -1 > -2) { givesCheck = MoveGen.givesCheck(pos, m); givesCheckComputed = true; } @@ -1016,11 +1019,11 @@ public class Search { } if (!givesCheckComputed) { - if (depth - 1 > -4) { + if (depth - 1 > -2) { givesCheck = MoveGen.givesCheck(pos, m); } } - final boolean nextInCheck = (depth - 1) > -4 ? givesCheck : false; + final boolean nextInCheck = (depth - 1) > -2 ? givesCheck : false; pos.makeMove(m, ui); qNodes++; @@ -1030,6 +1033,10 @@ public class Search { if (score > bestScore) { bestScore = score; if (score > alpha) { + if (depth == 0) { + SearchTreeInfo sti = searchTreeInfo[ply]; + sti.bestMove.setMove(m.from, m.to, m.promoteTo, score); + } alpha = score; if (alpha >= beta) { moveGen.returnMoveList(moves); diff --git a/CuckooChessEngine/src/chess/TranspositionTable.java b/CuckooChessEngine/src/chess/TranspositionTable.java index c69bbcc..ccdb699 100644 --- a/CuckooChessEngine/src/chess/TranspositionTable.java +++ b/CuckooChessEngine/src/chess/TranspositionTable.java @@ -71,7 +71,7 @@ public class TranspositionTable { this.move = (short)(move.from + (move.to << 6) + (move.promoteTo << 12)); } - /** Get the score from the hash entry, and convert from "mate in x" to "mate at ply". */ + /** Get the score from the hash entry and convert from "mate in x" to "mate at ply". */ public final int getScore(int ply) { int sc = score; if (sc > Search.MATE0 - 1000) { @@ -82,7 +82,7 @@ public class TranspositionTable { return sc; } - /** Convert score from "mate at ply" to "mate in x", and store in hash entry. */ + /** Convert score from "mate at ply" to "mate in x" and store in hash entry. */ public final void setScore(int score, int ply) { if (score > Search.MATE0 - 1000) { score += ply; diff --git a/CuckooChessEngine/test/chess/BitBoardTest.java b/CuckooChessEngine/test/chess/BitBoardTest.java index d04b2aa..18f4dc8 100644 --- a/CuckooChessEngine/test/chess/BitBoardTest.java +++ b/CuckooChessEngine/test/chess/BitBoardTest.java @@ -125,6 +125,19 @@ public class BitBoardTest { } } + private static final int computeDistance(int from, int to) { + int dx = Position.getX(to) - Position.getX(from); + int dy = Position.getY(to) - Position.getY(from); + return Math.max(Math.abs(dx), Math.abs(dy)); + } + + @Test + public void testGetDistance() { + for (int from = 0; from < 64; from++) + for (int to = 0; to < 64; to++) + assertEquals(computeDistance(from, to), BitBoard.getDistance(from, to)); + } + @Test public void testTrailingZeros() { System.out.println("trailingZeros"); diff --git a/CuckooChessEngine/test/chess/EvaluateTest.java b/CuckooChessEngine/test/chess/EvaluateTest.java index a7405dc..dcbba7f 100644 --- a/CuckooChessEngine/test/chess/EvaluateTest.java +++ b/CuckooChessEngine/test/chess/EvaluateTest.java @@ -277,7 +277,7 @@ public class EvaluateTest { score = evalWhite(pos); assertTrue(-score > bV * 2 + 100); - // KrpKn is win for white + // KRPKN is win for white pos = TextIO.readFEN("8/3bk3/8/8/8/3P4/3RK3/8 w - - 0 1"); score = evalWhite(pos); final int pV = Evaluate.pV; @@ -287,10 +287,14 @@ public class EvaluateTest { pos = TextIO.readFEN("8/8/4k3/8/8/3NK3/3N4/8 w - - 0 1"); score = evalWhite(pos); assertTrue(Math.abs(score) < 50); - + + final int nV = Evaluate.nV; + pos = TextIO.readFEN("8/8/8/4k3/N6N/P2K4/8/8 b - - 0 66"); + score = evalWhite(pos); + assertTrue(score > nV * 2); + pos = TextIO.readFEN("8/8/3k4/8/8/3NK3/2B5/8 b - - 0 1"); score = evalWhite(pos); - final int nV = Evaluate.nV; assertTrue(score > bV + nV + 150); // KBNK is won, should have a bonus score = moveScore(pos, "Kc6"); assertTrue(score > 0); // Black king going into wrong corner, good for white @@ -412,6 +416,8 @@ public class EvaluateTest { assertTrue(evalWhite(pos) > winScore); pos = TextIO.readFEN("3Q4/8/8/8/K7/8/1kp5/8 w - - 0 1"); assertTrue(evalWhite(pos) > winScore); + pos = TextIO.readFEN("8/8/8/8/8/1Q6/p3K3/k7 b - - 0 1"); + assertTrue(evalWhite(pos) < drawish); // Pawn on c2 pos = TextIO.readFEN("3Q4/8/8/8/3K4/8/1kp5/8 w - - 0 1"); @@ -445,6 +451,29 @@ public class EvaluateTest { assertTrue(score2 > score1); } + @Test + public void testPawnRace() throws ChessParseError { + final int pV = Evaluate.pV; + final int winScore = 400; + final int drawish = 100; + Position pos = TextIO.readFEN("8/8/K7/1P3p2/8/6k1/8/8 w - - 0 1"); + assertTrue(evalWhite(pos) > winScore); + pos = TextIO.readFEN("8/8/K7/1P3p2/8/6k1/8/8 b - - 0 1"); + assertTrue(evalWhite(pos) > winScore); + + pos = TextIO.readFEN("8/8/K7/1P3p2/6k1/8/8/8 b - - 0 1"); + assertTrue(Math.abs(evalWhite(pos)) < drawish); + pos = TextIO.readFEN("8/8/K7/1P6/5pk1/8/8/8 b - - 0 1"); + assertTrue(evalWhite(pos) < -winScore); + pos = TextIO.readFEN("8/K7/8/1P6/5pk1/8/8/8 b - - 0 1"); + assertTrue(Math.abs(evalWhite(pos)) < drawish); + pos = TextIO.readFEN("8/K7/8/8/1PP2p1k/8/8/8 w - - 0 1"); + assertTrue(evalWhite(pos) < drawish + pV); + assertTrue(evalWhite(pos) > 0); + pos = TextIO.readFEN("8/K7/8/8/1PP2p1k/8/8/8 b - - 0 1"); + assertTrue(evalWhite(pos) < -winScore + pV); + } + /** Return static evaluation score for white, regardless of whose turn it is to move. */ final static int evalWhite(Position pos) { Evaluate eval = new Evaluate(); diff --git a/CuckooChessEngine/test/chess/SearchTest.java b/CuckooChessEngine/test/chess/SearchTest.java index 45bbf96..52427b5 100644 --- a/CuckooChessEngine/test/chess/SearchTest.java +++ b/CuckooChessEngine/test/chess/SearchTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.*; public class SearchTest { static final long[] nullHist = new long[200]; static TranspositionTable tt = new TranspositionTable(19); + static History ht = new History(); public SearchTest() { } @@ -53,7 +54,7 @@ public class SearchTest { final int mate0 = Search.MATE0; Position pos = TextIO.readFEN("3k4/8/3K2R1/8/8/8/8/8 w - - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); final int plyScale = Search.plyScale; int score = sc.negaScout(-mate0, mate0, 0, 2*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(mate0 - 2, score); // depth 2 is enough to find mate in 1 @@ -61,33 +62,33 @@ public class SearchTest { assertEquals(score, score2); pos = TextIO.readFEN("8/1P6/k7/2K5/8/8/8/8 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); score = sc.negaScout(-mate0, mate0, 0, 4*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(mate0 - 4, score); // depth 4 is enough to find mate in 2 score2 = idSearch(sc, 4).score; assertEquals(score, score2); pos = TextIO.readFEN("8/5P1k/5K2/8/8/8/8/8 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); score = sc.negaScout(-mate0, mate0, 0, 5*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(mate0 - 4, score); // must avoid stale-mate after f8Q score2 = idSearch(sc, 5).score; assertEquals(score, score2); pos = TextIO.readFEN("4k3/8/3K1Q2/8/8/8/8/8 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); score = sc.negaScout(-mate0, mate0, 0, 2*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(0, score); // Position is stale-mate pos = TextIO.readFEN("3kB3/8/1N1K4/8/8/8/8/8 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); score = sc.negaScout(-mate0, mate0, 0, 3*plyScale, -1, MoveGen.inCheck(pos)); assertTrue(Math.abs(score) < 50); // Stale-mate trap score2 = idSearch(sc, 5).score; assertEquals(score, score2); pos = TextIO.readFEN("8/8/2K5/3QP3/P6P/1q6/8/k7 w - - 31 51"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 2); assertTrue(!TextIO.moveToString(pos, bestM, false).equals("Qxb3")); } @@ -105,50 +106,50 @@ public class SearchTest { final int mateInThree = mate0 - 6; Position pos = TextIO.readFEN("8/1R2k3/R7/8/8/8/8/1K6 b - - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; final int plyScale = Search.plyScale; int score = sc.negaScout(-mate0, mate0, 0, 2*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(matedInOne, score); pos = TextIO.readFEN("8/1R2k3/R7/8/8/8/8/1K6 b - - 99 80"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = sc.negaScout(-mate0, mate0, 0, 2*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(0, score); // Draw by 50-move rule pos = TextIO.readFEN("8/1R2k3/R7/8/8/8/8/1K6 b - - 98 80"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = sc.negaScout(-mate0, mate0, 0, 2*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(matedInOne, score); // No draw pos = TextIO.readFEN("8/1R2k3/R7/8/8/8/8/1K6 b - - 99 80"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 3).score; assertEquals(0, score); pos = TextIO.readFEN("3k4/1R6/R7/8/8/8/8/1K6 w - - 100 80"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 2).score; assertEquals(mateInOne, score); // Black forgot to claim draw. Now it's too late. pos = TextIO.readFEN("8/7k/1R6/R7/8/7P/8/1K6 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 3).score; assertEquals(mateInTwo, score); pos = TextIO.readFEN("8/7k/1R6/R7/8/7P/8/1K6 w - - 98 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 6).score; assertEquals(mateInThree, score); // Need an extra pawn move to avoid 50-move rule pos = TextIO.readFEN("8/7k/1R6/R7/8/7P/8/1K6 w - - 125 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 6).score; assertEquals(mateInThree, score); // Need an extra pawn move to avoid 50-move rule @@ -162,32 +163,32 @@ public class SearchTest { System.out.println("drawRep"); final int mate0 = Search.MATE0; Position pos = TextIO.readFEN("7k/5RR1/8/8/8/8/q3q3/2K5 w - - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; final int plyScale = Search.plyScale; int score = sc.negaScout(-mate0, mate0, 0, 3*plyScale, -1, MoveGen.inCheck(pos)); assertEquals(0, score); pos = TextIO.readFEN("7k/5RR1/8/8/8/8/q3q3/2K5 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 3).score; assertEquals(0, score); pos = TextIO.readFEN("7k/5RR1/8/8/8/8/1q3q2/3K4 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 4).score; assertTrue(score < 0); pos = TextIO.readFEN("7k/5RR1/8/8/8/8/1q3q2/3K4 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = sc.negaScout(-mate0, mate0, 0, 3*plyScale, -1, MoveGen.inCheck(pos)); assertTrue(score < 0); pos = TextIO.readFEN("qn6/qn4k1/pp3R2/5R2/8/8/8/K7 w - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); sc.maxTimeMillis = -1; score = idSearch(sc, 7).score; assertEquals(0, score); // Draw, black can not escape from perpetual checks @@ -200,7 +201,7 @@ public class SearchTest { public void testHashing() throws ChessParseError { System.out.println("hashing"); Position pos = TextIO.readFEN("/k/3p/p2P1p/P2P1P///K/ w - -"); // Fine #70 - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 28); assertEquals(TextIO.stringToMove(pos, "Kb1"), new Move(bestM)); } @@ -211,7 +212,7 @@ public class SearchTest { @Test public void testLMP() throws ChessParseError { Position pos = TextIO.readFEN("2r2rk1/6p1/p3pq1p/1p1b1p2/3P1n2/PP3N2/3N1PPP/1Q2RR1K b"); // WAC 174 - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 2); assertTrue(bestM.score < Search.MATE0 / 2); } @@ -220,12 +221,12 @@ public class SearchTest { public void testCheckEvasion() throws ChessParseError { System.out.println("check evasion"); Position pos = TextIO.readFEN("6r1/R5PK/2p5/1k6/8/8/p7/8 b - - 0 62"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 3); assertTrue(bestM.score < 0); pos = TextIO.readFEN("r1bq2rk/pp3pbp/2p1p1pQ/7P/3P4/2PB1N2/PP3PPR/2KR4 w - -"); // WAC 004 - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); bestM = idSearch(sc, 1); assertEquals(Search.MATE0 - 4, bestM.score); assertEquals(TextIO.stringToMove(pos, "Qxh7+"), new Move(bestM)); @@ -235,7 +236,7 @@ public class SearchTest { public void testStalemateTrap() throws ChessParseError { System.out.println("stalemate trap"); Position pos = TextIO.readFEN("7k/1P3R1P/6r1/5K2/8/8/6R1/8 b - - 98 194"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 3); assertEquals(0, bestM.score); } @@ -244,7 +245,7 @@ public class SearchTest { public void testKQKRNullMove() throws ChessParseError { System.out.println("kqkrNullMove"); Position pos = TextIO.readFEN("7K/6R1/5k2/3q4/8/8/8/8 b - - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); Move bestM = idSearch(sc, 10); assertEquals(Search.MATE0-18, bestM.score); } @@ -286,7 +287,7 @@ public class SearchTest { // Basic tests Position pos = TextIO.readFEN("r2qk2r/ppp2ppp/1bnp1nb1/1N2p3/3PP3/1PP2N2/1P3PPP/R1BQRBK1 w kq - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "dxe5"))); assertEquals(pV - nV, getSEE(sc, TextIO.stringToMove(pos, "Nxe5"))); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa7"))); @@ -299,7 +300,7 @@ public class SearchTest { assertEquals(-rV, getSEE(sc, TextIO.stringToMove(pos, "Ra6"))); pos.setWhiteMove(false); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertTrue(nV <= bV); // Assumed by following test assertEquals(pV - nV, getSEE(sc, TextIO.stringToMove(pos, "Nxd4"))); assertEquals(pV - bV, getSEE(sc, TextIO.stringToMove(pos, "Bxd4"))); @@ -312,7 +313,7 @@ public class SearchTest { // Test X-ray attacks pos = TextIO.readFEN("3r2k1/pp1q1ppp/1bnr1nb1/1Np1p3/1P1PP3/2P1BN2/1Q1R1PPP/3R1BK1 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 1 1 3 3 3 3 3 5 5 9 5 5 // 0 1 0 1 0 3 0 3 0 5 0 9 0 5 assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "exd4"))); @@ -321,38 +322,38 @@ public class SearchTest { assertEquals(2 * pV - nV, getSEE(sc, TextIO.stringToMove(pos, "Nxd4"))); pos.setPiece(TextIO.getSquare("b2"), Piece.EMPTY); // Remove white queen - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 1 1 3 3 3 3 3 5 5 9 5 // 0 1 0 1 0 3 0 3 0 4 1 4 5 assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "exd4"))); assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "cxb4"))); pos.setPiece(TextIO.getSquare("b5"), Piece.EMPTY); // Remove white knight - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 1 1 3 3 3 3 5 5 5 // 1 0 1 0 3 0 3 0 5 0 5 assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "exd4"))); pos.setPiece(TextIO.getSquare("b2"), Piece.WQUEEN); // Restore white queen - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 1 1 3 3 3 3 5 5 5 9 9 // 1 0 1 0 3 0 3 0 5 0 5 0 9 assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "exd4"))); pos.setPiece(TextIO.getSquare("b6"), Piece.EMPTY); // Remove black bishop pos.setPiece(TextIO.getSquare("c6"), Piece.EMPTY); // Remove black knight - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(-pV, getSEE(sc, TextIO.stringToMove(pos, "a5"))); // Test EP capture pos = TextIO.readFEN("2b3k1/1p3ppp/8/pP6/8/2PB4/5PPP/6K1 w - a6 0 2"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 1 3 // 0 1 0 3 assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "bxa6"))); pos.setPiece(TextIO.getSquare("b7"), Piece.EMPTY); // Remove black pawn - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 1 3 // 1 0 3 assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "bxa6"))); @@ -360,14 +361,14 @@ public class SearchTest { // Test king capture pos = TextIO.readFEN("8/8/8/4k3/r3P3/4K3/8/4R3 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 5 99 // 1 0 99 assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "Rxe4+"))); pos = TextIO.readFEN("8/8/8/4k3/r3P1R1/4K3/8/8 b - - 0 1"); final int kV = Evaluate.kV; - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); // 1 5 5 99 //-4 5 0 99 assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxe4+"))); @@ -376,47 +377,47 @@ public class SearchTest { assertEquals(pV - kV, getSEE(sc, new Move(TextIO.getSquare("e5"), TextIO.getSquare("e4"), Piece.EMPTY))); pos = TextIO.readFEN("8/8/4k3/8/r3P3/4K3/8/8 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxe4+"))); // Test king too far away pos = TextIO.readFEN("8/8/4k3/8/r3P3/8/4K3/8 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "Rxe4+"))); pos = TextIO.readFEN("8/3k4/8/8/r1K5/8/8/2R5 w - - 0 1"); pos.setWhiteMove(false); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(kV, getSEE(sc, new Move(TextIO.getSquare("a4"), TextIO.getSquare("c4"), Piece.EMPTY))); // Test blocking pieces pos = TextIO.readFEN("r7/p2k4/8/r7/P7/8/4K3/R7 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); // Ra8 doesn't help pos.setPiece(TextIO.getSquare("a7"), Piece.BBISHOP); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); // Ra8 doesn't help pos.setPiece(TextIO.getSquare("a7"), Piece.BPAWN); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); // Ra8 doesn't help pos.setPiece(TextIO.getSquare("a7"), Piece.BQUEEN); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); // Ra8 does help pos = TextIO.readFEN("8/3k4/R7/r7/P7/8/4K3/8 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); pos = TextIO.readFEN("Q7/q6k/R7/r7/P7/8/4K3/8 b - - 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxa4"))); pos = TextIO.readFEN("8/3k4/5R2/8/4pP2/8/8/3K4 b - f3 0 1"); - sc = new Search(pos, nullHist, 0, tt); + sc = new Search(pos, nullHist, 0, tt, ht); int score1 = EvaluateTest.evalWhite(sc.pos); long h1 = sc.pos.zobristHash(); assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "exf3"))); @@ -433,7 +434,7 @@ public class SearchTest { public void testScoreMoveList() throws ChessParseError { System.out.println("SEEorder"); Position pos = TextIO.readFEN("r2qk2r/ppp2ppp/1bnp1nb1/1N2p3/3PP3/1PP2N2/1P3PPP/R1BQRBK1 w kq - 0 1"); - Search sc = new Search(pos, nullHist, 0, tt); + Search sc = new Search(pos, nullHist, 0, tt, ht); MoveGen moveGen = new MoveGen(); MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos); sc.scoreMoveList(moves, 0);