mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2024-11-26 21:47:23 +01:00
Moved CuckooChessEngine project to trunk/
This commit is contained in:
parent
374c481a14
commit
f2946fe6c7
8
CuckooChessEngine/.classpath
Normal file
8
CuckooChessEngine/.classpath
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <launchConfigurationWorkingSet editPageId="org.eclipse.ui.resourceWorkingSetPage" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1278056238759_232" label="working set" name="working set"> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/CuckooChessEngine/src/book.bin" type="1"/> </launchConfigurationWorkingSet>}"/>
|
||||
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <launchConfigurationWorkingSet editPageId="org.eclipse.ui.resourceWorkingSetPage" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1278056261536_239" label="workingSet" name="workingSet"> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/CuckooChessEngine/src/chess/Book.java" type="1"/> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/CuckooChessEngine/src/book.txt" type="1"/> </launchConfigurationWorkingSet>}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/usr/bin/java"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="chess/Book"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/CuckooChessEngine/bin}"/>
|
||||
</launchConfiguration>
|
27
CuckooChessEngine/.project
Normal file
27
CuckooChessEngine/.project
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>CuckooChessEngine</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>auto,full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/BinBook_Builder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
12
CuckooChessEngine/.settings/org.eclipse.jdt.core.prefs
Normal file
12
CuckooChessEngine/.settings/org.eclipse.jdt.core.prefs
Normal file
|
@ -0,0 +1,12 @@
|
|||
#Sun May 09 18:17:45 CEST 2010
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
BIN
CuckooChessEngine/src/book.bin
Normal file
BIN
CuckooChessEngine/src/book.bin
Normal file
Binary file not shown.
228
CuckooChessEngine/src/book.txt
Normal file
228
CuckooChessEngine/src/book.txt
Normal file
|
@ -0,0 +1,228 @@
|
|||
# Philidors defense
|
||||
e4 e5 Nf3 d6 d4 exd4 Nxd4 Nf6 Nc3 Be7 Bf4 O-O Qd2 d5 exd5 Nxd5 Nxd5 Qxd5 Nb5 Qe4+
|
||||
e4 e5 Nf3 d6? d4 exd4 Qxd4 Nf6 Nc3 Be7 Bg5 Nc6 Bb5 O-O
|
||||
e4 e5 Nf3 d6? d4 Nd7 Bc4 c6 Ng5 Nh6 O-O Nb6
|
||||
|
||||
# Nordic gambit
|
||||
e4 e5 d4 exd4 c3 dxc3 Bc4 cxb2 Bxb2 d5 Bxd5 Nf6 Bxf7+ Kxf7 Qxd8 Bb4+
|
||||
e4 e5 d4 exd4 c3? d5 exd5 Qxd5 cxd4 Nc6 Nf3 Bg4 Be2 Bb4 Nc3 Bxf3 Bxf3 Qc4
|
||||
e4 e5 d4 exd4 c3? d5 exd5 Qxd5 Nf3 Nc6 cxd4
|
||||
e4 e5 d4? exd4 c3? d5 exd5 Qxd5 Nf3 Nc6 Be2 Nf6
|
||||
e4 e5 d4? exd4 c3? d5 exd5 Qxd5 Nf3 Nc6 Be2 Bg4 O-O O-O-O cxd4
|
||||
e4 e5 d4? exd4 Qxd4 Nc6 Qe3 Nf6 Nc3 Bb4 Bd2 O-O O-O-O Re8 Bc4 d6
|
||||
|
||||
# Scottish game
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Nxd4 Nf6 Nc3 Bb4 Nxc6 bxc6 Bd3 d5 exd5
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Nxd4 Bc5 Be3 Qf6 c3 Nge7
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Nxd4 Bc5 Nb3 Bb6 a4 a5 Nc3 Qf6 Qe2 Nge7
|
||||
e4 e5 d4 exd4 Nf3 Nc6 Nxd4 Nf6 Nxc6 bxc6 e5 Qe7 Qe2 Nd5 c4 Ba6 b3 g6 f4 f6 Ba3 Qf7 Qd2 Nb6
|
||||
|
||||
# Italian game
|
||||
e4 e5 Nf3 Nc6 Bc4 Bc5 d3 d6 Nc3 Nf6 Bg5 h6
|
||||
e4 e5 Nf3 Nc6 Bc4 Bc5 c3 Nf6 d4 exd4 cxd4 Bb4+ Bd2 Bxd2+ Nbxd2 d5 exd5 Nxd5 Qb3 Nce7
|
||||
e4 e5 Nf3 Nc6 Bc4 Bc5 c3 Qe7 d4 Bb6 O-O d6 h3 Nf6 Re1 O-O Na3
|
||||
e4 e5 Nf3 Nc6 Bc4 Bc5 c3 Nf6 d3 d6 O-O Qe7 b4 Bb6 a4 a6
|
||||
|
||||
# Two knights defense
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 Nc3 Nxe4 Nxe4 d5 Bd3
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 Ng5 d5 exd5 Na5 Bb5+ c6 dxc6 bxc6 Be2 h6 Nf3 e4 Ne5 Qd4
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 Ng5 d5 exd5 Na5 Bb5+ c6 dxc6 bxc6 Be2 h6 Nf3 e4 Ne5 Qc7
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 d3 Bc5 Nc3 d6 Bg5 h6
|
||||
|
||||
# Max Lange attack
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 d4 exd4 O-O Bc5 e5 Ng4 Bf4 d6 exd6 Bxd6 Re1+ Kf8 Bxd6+ Qxd6 c3 Qc5
|
||||
e4 e5 Nf3 Nc6 Bc4 Bc5 O-O Nf6 d4 exd4 e5 d5 exf6 dxc4 Re1+ Be6 Ng5 Qd5 Nc3 Qf5
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Bc4 Nf6 O-O Bc5
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Bc4 Bc5 O-O Nf6
|
||||
|
||||
# Skottish gambit
|
||||
e4 e5 Nf3 Nc6 Bc4 Nf6 d4 exd4 O-O Nxe4 Re1 d5 Bxd5 Qxd5 Nc3 Qa5 Nxe4 Be6
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Bc4 Nf6 e5 d5 Bb5 Ne4 Nxd4 Bd7 Bxc6 bxc6 O-O Be7 f3 Nc5 f4 Ne4
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Bc4 Nf6 e5 d5 Bb5 Ne4 Nxd4 Bd7 Bxc6 bxc6 O-O Bc5 Be3 O-O f3 Ng5 Qd2 f6 Kh1 Ne6
|
||||
e4 e5 Nf3 Nc6 d4 exd4 Bc4 Bc5 O-O d6 c3 Bg4 Qb3 Bxf3 Bxf7 Kf8 gxf3 Ne5 cxd4 Bxd4
|
||||
e4 e5 Nf3 Nc6 d4 exd4 c3 d5 exd5 Qxd5 cxd4 Bg4 Be2 Bb4 Nc3 Bxf3 Bxf3 Qc4 Qb3
|
||||
|
||||
# Hungarian
|
||||
e4 e5 Nf3 Nc6 Bc4 Be7 d4 d6 dxe5 dxe5 Qxd8+ Bxd8
|
||||
|
||||
# Three and four knights game
|
||||
e4 e5 Nf3 Nc6 Nc3 Nf6 Bb5 Nd4 Nxe5 Qe7 Nf3 Nxb5 Nxb5 Qxe4+ Qe2 Qxe2+ Kxe2 Nd5
|
||||
e4 e5 Nf3 Nf6 Nc3? Nc6 d4 exd4 Nxd4
|
||||
|
||||
# Russian defense
|
||||
e4 e5 Nf3 Nf6 Nxe5 d6 Nf3 Nxe4 Qe2 Qe7 d3 Nf6 Bg5 Nbd7 Nc3 Qxe2+ Bxe2 h6 Bh4 g6
|
||||
e4 e5 Nf3 Nf6 Nxe5 d6 Nf3 Nxe4 d4 d5 Bd3 Be7 O-O Nc6 Re1 Bg4 c4 Nf6 cxd5 Nxd5 Nc3 O-O Be4 Be6
|
||||
e4 e5 Nf3 Nf6? d4 Nxe4 dxe5 d5 Nbd2 Nc6
|
||||
|
||||
# Kings gambit
|
||||
e4 e5 f4 exf4 Nf3 d5 exd5 Nf6 Nc3 Nxd5 Nxd5 Qxd5 d4 Be7 c4 Qe4+ Be2 Nc6
|
||||
e4 e5 f4 exf4 Nf3 Be7 Bc4 Nf6 e5 Ng4 O-O Nc6 d4 d5 exd6 Qxd6
|
||||
|
||||
# Spanish
|
||||
e4 e5 Nf3 Nc6 Bb5 d6 d4 Bd7 Nc3 Nf6 O-O Be7 Re1 exd4 Nxd4 O-O
|
||||
e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Nxe4 Re1 Nd6 Nxe5 Be7 Bd3 O-O
|
||||
e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Nxe4 d4 Nd6
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 h6 d4 Re8 Nbd2 Bf8 Nf1 Bb7 Ng3 Na5 Bc2 Nc4 a4 d5
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 d6 c3 O-O d4 Bg4 d5 Na5 Bc2 c6 h3 Bc8 dxc6 Qc7 Nbd2 Qxc6 Nf1 Nc4
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 d6 c3 Na5 Bc2 c5 d4 Nc6 d5
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 d6 c3 Na5 Bc2 c5 d4 Nc6 h3 Qc7 d5
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 d6 c3 Na5 Bc2 c5 d4 cxd4 cxd4 Qc7
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 d6 c3 Na5 Bc2 c5 d3 Nc6 Nbd2 O-O Nf1 Re8 h3 h6 Ne3 Bf8
|
||||
Nf3? Nc6 e4 e5 Bb5 a6 Ba4 Nf6 O-O Nxe4 d4 b5 Bb3 d5 dxe5 Be6 c3 Bc5
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 O-O Bd7 c3 g6 d4 Bg7 Re1 Nge7 Be3 O-O Nbd2 h6 dxe5 dxe5 Bb3 b6 a4
|
||||
e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 c3 Bd7 d4 Nge7 Bb3 h6 Nbd2 Ng6 Nc4 Be7 Ne3 O-O
|
||||
e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Bc5 c3 O-O d4 Bb6 Bg5 h6 Bh4 d6 a4 a5 Re1 exd4 Bxc6 bxc6 Nxd4
|
||||
e4 e5 Nf3 Nc6 Bb5 Nf6? O-O Bc5 Nxe5 Nxe5 d4 a6 Ba4 Nxe4 Qe2 Be7 Qxe4 Ng6
|
||||
e4 e5 Nf3 Nc6 Bb5 Nf6? O-O Bc5? Nc3? O-O d3 d6
|
||||
|
||||
# Scandinavian
|
||||
e4 d5 exd5 Qxd5 Nc3 Qa5 d4 Nf6 Nf3 Bf5 Bc4 e6 Bd2 c6 Qe2 Bb4 Ne5 Nbd7 Nxd7 Nxd7 a3
|
||||
e4 d5 exd5 Qxd5 Nc3 Qa5 d4 c6 Nf3 Nf6 Bc4 Bg4 h3 Bh5 g4 Bg6 Bd2 Qb6 Qe2
|
||||
e4 d5 exd5 Nf6 d4 Nxd5 c4 Nb6 Nf3 g6 Nc3 Bg7 Be3 O-O h3 Nc6 Qd2 e5 d5
|
||||
e4 d5? exd5 Nf6 d4 Nxd5 Nf3 g6 c4 Nb6
|
||||
e4 d5? exd5 Nf6 d4 Nxd5 Nf3 g6 Be2 Bg7 O-O O-O c4 Nb6 Nc3 Nc6 d5 Ne5
|
||||
|
||||
# Queens gambit accepted
|
||||
d4 d5 c4 dxc4 Nf3 Nf6 e3 Bg4 Bxc4 e6 h3 Bh5 Nc3
|
||||
d4 d5 c4 dxc4? e3 Nf6 Nf3 Bg4
|
||||
d4 d5 c4 dxc4? e3? Nf6 Nf3 e6 Bxc4 c5 O-O a6
|
||||
|
||||
# Queens gambit declined
|
||||
d4 d5 c4 e6 Nc3 Nf6 Bg5 Nbd7 e3 Be7 Nf3 O-O Rc1 c6
|
||||
c4 e6 d4 d5 Nf3 Be7 Nc3 Nf6 Bg5 O-O e3 h6
|
||||
c4 Nf6 Nc3 e6 Nf3 d5 d4 Be7 Bg5 O-O e3 Nbd7 Qc2 c5
|
||||
c4? Nf6 Nc3 e6 Nf3 d5 d4 Be7 e3 O-O Bd3 c5
|
||||
Nf3? d5 d4 Nf6 c4 e6 Nc3 Be7 Bf4 O-O e3 c5 dxc5 Bxc5 Qc2 Nc6
|
||||
d4 d5 c4 c6 Nf3 Nf6 Nc3 e6 e3 Nbd7 Bd3 dxc4 Bxc4 b5 Bd3 a6 O-O
|
||||
d4 d5 c4 c6? cxd5 cxd5 Nc3 Nf6 Bf4 Nc6 e3 a6
|
||||
d4 d5 c4 c6? Nc3 Nf6 e3 e6 Nf3
|
||||
|
||||
# Tarrasch defense
|
||||
d4 d5 c4 e6 Nc3 c5 cxd5 exd5 Nf3 Nc6 g3 Nf6 Bg2 Be7 O-O O-O Bg5 cxd4 Nxd4 h6
|
||||
|
||||
# Buddapest defense
|
||||
d4 Nf6 c4 e5 dxe5 Ng4 Nf3 Bc5 e3 Nc6 Be2 Ngxe5 O-O d6
|
||||
|
||||
# Sicilian
|
||||
e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e6 Nc3 Qc7
|
||||
e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 Nf6 Nc3 e5 Ndb5 d6 Bg5 a6 Na3 b5
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 f4 e5 Nf3 Qc7 Bd3
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 f4 e6 Qf3 Qb6 Nb3 Qc7
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 f4 Nbd7? Be2
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Be2 e5 Nb3 Be7 O-O O-O Be3
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Be3 e5 Nb3 Be6 Qd2 Nbd7 f3 b5
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6 f4 Be7 Qf3 Qc7 O-O-O Nbd7 g4 b5
|
||||
e4 c5 Nf3 e6 d4 cxd4 Nxd4 a6 Bd3 Nf6 O-O Qc7 Qe2 d6 c4 g6 Nc3 Bg7 Rd1 O-O
|
||||
e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nf6 Nc3 d6 Be2 a6 O-O Be7 f4 O-O
|
||||
e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3 Qc7 Be3 a6 Bd3 Nf6 O-O Ne5 h3 Bc5 Qe2 d6
|
||||
e4 c5 Nf3 e6? Nc3 Nc6 d4 cxd4 Nxd4 Qc7 Be3 a6 Qd2 Nf6 O-O-O Be7
|
||||
e4 c5 Nc3 Nc6 Nge2 g6 d4 cxd4 Nxd4 Bg7 Be3 Nf6 Bc4 O-O Bb3 d6
|
||||
e4 c5 Nc3? Nc6 g3 g6 Bg2 Bg7 d3 d6 f4 e6 Nf3 Nge7 O-O O-O
|
||||
e4 c5 Nc3? e6 Nf3 Nc6 d4 cxd4 Nxd4 Qc7 Be2 a6 O-O Nf6 Be3 Bb4
|
||||
e4 c5 Nc3? a6 Nf3 d6 d4 cxd4 Nxd4 Nf6
|
||||
Nc3? c5 Nf3 Nc6 d4 cxd4 Nxd4 Nf6 e4 d6 Bg5 e6 Qd2 a6 O-O-O Bd7 f4 b5
|
||||
e4 c5 d4 cxd4 c3 dxc3 Nxc3 Nc6 Nf3 d6 Bc4 e6 O-O Nf6 Qe2 Be7 Rd1 e5
|
||||
e4 c5 c3 d5 exd5 Qxd5 d4 Nf6 Nf3 Bg4 Be2 e6 O-O Nc6 Be3 cxd4 cxd4 Be7
|
||||
e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3 O-O Qd2 Nc6 Bc4 Bd7 O-O-O Rc8 Bb3 Ne5 h4 Nc4 Bxc4 Rxc4 g4 Qa5
|
||||
|
||||
# French defense
|
||||
d4 e6 e4 d5 exd5 exd5 Nf3 Nf6 Bd3 Bd6 O-O O-O Bg5 Bg4 Nbd2 Nbd7 c3 c6 Qc2 Qc7
|
||||
e4 e6 d4 d5 exd5 exd5 Bd3 Bd6 Nf3 Nf6
|
||||
e4 e6 d4 d5 exd5 exd5 Bd3 Bd6 Nf3 Ne7 O-O O-O Bg5 f6 Bd2 Bf5
|
||||
e4 e6 d4 d5 e5 c5 c3 Nc6 Nf3 Qb6 Be2 cxd4 cxd4 Nge7 Nc3 Nf5 Na4 Qa5+ Bd2 Bb4 Bc3
|
||||
e4 e6 d4 d5 e5 c5 c3 Nc6 Nf3 Qb6 Bd3 cxd4 cxd4 Bd7
|
||||
e4 e6 d4 d5 e5 c5 c3 Nc6 Nf3 Qb6 a3 c4 Nbd2 Na5 Be2 Bd7
|
||||
e4 e6 d4 d5 Nc3 Nf6 Bg5 Be7 e5 Nfd7 Bxe7
|
||||
e4 e6 d4 d5 Nd2 Nf6 e5 Nfd7 Bd3 c5 c3 Nc6 Ne2 cxd4 cxd4 f6 exf6 Nxf6
|
||||
e4 e6? d4 d5 Nc3 Nf6 e5 Nfd7 f4 c5 Nf3 Nc6 Be3 cxd4 Nxd4 Bc5 Qd2 O-O O-O-O a6
|
||||
e4 e6? d4 d5 Nc3 Bb4 e5 c5 a3 Bxc3 bxc3 Ne7 Qg4 Qc7 Qxg7 Rg8 Qxh7 cxd4 Ne2 Nbc6 f4 Bd7
|
||||
e4 e6? d4 d5 Nc3 Bb4 e5 c5 Qg4
|
||||
e4 e6? d4 c5? d5 exd5 exd5 d6
|
||||
e4 e6 d3 d5 Nd2 c5 Ngf3 Nc6 g3 Nf6 Bg2 Be7 O-O O-O
|
||||
|
||||
# Caro Kann defense
|
||||
e4 c6 d4 d5 Nc3 dxe4 Nxe4 Bf5 Ng3 Bg6 h4 h6 Nf3 Nd7
|
||||
e4 c6 d4 d5 Nd2 dxe4 Nxe4
|
||||
e4 c6 d4 d5 exd5 cxd5 c4 Nf6 Nc3 e6 Nf3 Be7
|
||||
e4 c6 d4 d5 e5 Bf5 Nf3 e6 Be2 c5 O-O Nc6 c3 cxd4 cxd4 Nge7 Nc3 Nc8 Be3 Nb6 Rc1 Be7
|
||||
e4 c6? d3 d5 Nd2 e5 Ngf3 Bd6 g3 Nf6 Bg2 O-O O-O
|
||||
e4 c6? d4 d5 Nc3 dxe4 Nxe4 Bf5 Ng3 Bg6 Nf3 Nd7 h4 h6 h5 Bh7 Bd3 Bxd3 Qxd3 e6
|
||||
|
||||
# Aljechins defense
|
||||
e4 Nf6 e5 Nd5 d4 d6 c4 Nb6 exd6 cxd6 Be3 g6
|
||||
e4 Nf6 e5 Nd5 c4 Nb6 d4 d6 exd6 cxd6 Nf3 g6 Be2 Bg7 O-O O-O Nc3 Nc6 Be3 Bg4 b3 d5
|
||||
e4 Nf6 e5 Nd5 d4 d6 Nf3 Bg4 Be2 e6 c4 Nb6 exd6 cxd6
|
||||
e4 Nf6? Nc3 d5 e5 Nfd7 d4 e6 f4 c5 Nf3 Nc6 Be3 a6 Qd2 b5 dxc5 Bxc5 Bxc5 Nxc5
|
||||
e4 Nf6? Nc3 d5 e5 Nfd7 d4 e6 f4 c5 Nf3 Nc6 Be3 cxd4 Nxd4 Bc5 Qd2 O-O O-O-O a6
|
||||
e4 Nf6? e5 Nd5 c4 Nb6 d4 d6 Nf3 Bg4 exd6 exd6 Be2 Be7 O-O O-O Nc3 Nc6 b3 Bf6 Be3 d5
|
||||
|
||||
# Kings indian
|
||||
d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nc6
|
||||
d4 Nf6 c4 g6 Nc3 Bg7 g3 O-O Bg2 d6 Nf3 Nbd7 O-O e5
|
||||
c4 Nf6 Nf3 g6 d4 Bg7 Nc3 O-O e4 d6 Be2
|
||||
c4 Nf6 Nc3 g6 d4 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Bc4 c5
|
||||
d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Nf3 c5
|
||||
Nf3? Nf6 c4 g6 Nc3 Bg7 d4 O-O
|
||||
|
||||
# Queen indian
|
||||
d4 Nf6 c4 e6 Nf3 b6 g3 Bb7 Bg2 Be7
|
||||
c4 Nf6 d4 e6 Nf3 b6 Nc3 Bb7 a3 d5 cxd5 Nxd5 Qc2
|
||||
d4 e6 Nf3 Nf6 c4 b6 g3 Bb7 Bg2 Bb4 Bd2 Bxd2 Qxd2
|
||||
|
||||
# Nimzo indian
|
||||
d4 e6 c4 Nf6 Nc3 Bb4 Bg5 h6 Bh4 c5 d5 d6
|
||||
c4 e6 d4 Nf6 Nc3 Bb4 a3 Bxc3+ bxc3 c5 f3 d5
|
||||
d4 Nf6 c4 e6 Nc3 Bb4 Qc2 d5 a3 Bxc3+ Qxc3 Ne4 Qc2 Nc6 e3 e5
|
||||
d4 Nf6 c4 e6 Nc3 Bb4 Nf3 O-O Bg5 c5 Rc1 cxd4
|
||||
d4 Nf6 c4 e6 Nc3 Bb4 f3 d5
|
||||
d4 Nf6 c4 e6 Nc3 Bb4 e3 c5 Bd3 d5 Nf3 O-O O-O Nc6
|
||||
d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Bd3 d5 Nf3 c5 O-O
|
||||
c4 Nf6 Nc3 e6 d4
|
||||
|
||||
# Benoni
|
||||
d4 Nf6 Nf3 e6 c4 c5 d5 exd5 cxd5 d6 Nc3 g6 e4 Bg7
|
||||
d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 f4 Bg7 Bb5 Nfd7 a4 O-O Nf3 Na6 O-O Nc7
|
||||
d4 c5 d5 Nf6 c4 e6 Nc3 exd5 cxd5 d6 Nf3 g6 Bf4 a6 a4 Bg7 e4 O-O
|
||||
c4 Nf6 d4 g6 Nc3 Bg7 g3 O-O Bg2 c5 d5 e6 Nf3 exd5 cxd5 d6 O-O
|
||||
d4 c5? d5 Nf6 c4 e6 g3 exd5 cxd5 d6 Nc3 g6 Nf3 Bg7 Bg2 O-O
|
||||
|
||||
# Reti's opening
|
||||
Nf3 d5 g3 g6 Bg2 Bg7 O-O e5 d3 Ne7 Nbd2 O-O c4 c6
|
||||
Nf3 Nf6 g3 g6 c4 Bg7 Bg2 O-O O-O c5 d4 d6 d5 Na6 Nc3 Nc7
|
||||
Nf3? Nf6 g3 d5 d4 c5 Bg2 Nc6 O-O g6
|
||||
Nf3? Nf6 g3 d5 Bg2 c6 d4 Bf5 O-O g6
|
||||
Nf3? d5 d4 Nf6 c4 e6 g3 dxc4 Bg2 Nc6 Qa4 Bb4 Bd2 Nd5 Bxb4 Nxb4 O-O Rb8
|
||||
Nf3? c5 c4 Nf6 Nc3 e6 g3 Be7 Bg2 O-O O-O a6 d4 cxd4 Nxd4 Qc7
|
||||
g3 g6 Bg2 Bg7 c4 Nf6 Nc3 O-O Nf3 d6 d4 Nbd7 O-O e5 e4
|
||||
g3? d5 Bg2 Nf6 Nf3 c6 O-O Bf5 d3 e6 Nbd2 h6 b3 Be7 Bb2 O-O
|
||||
g3? Nf6 Bg2 d5 d3 c6 Nd2 e5 e4 Bd6 Ngf3 O-O O-O
|
||||
g3? d5 Nf3 Nf6 Bg2 e6 O-O Be7 d3 O-O Nbd2 c5 e4 Nc6
|
||||
g3? e5 Bg2 d5 d3 Nf6 Nf3 Nc6 O-O Be7 c4 O-O cxd5 Nxd5 Nc3 Be6
|
||||
|
||||
# Dutch
|
||||
d4 f5 g3 Nf6 Bg2 g6 Nf3 Bg7 O-O O-O c4 d6 Nc3 Qe8 d5 Na6
|
||||
c4 f5? d4 Nf6 Nc3 g6 Nf3 Bg7 e3 O-O Be2 d6 O-O Nc6
|
||||
d4 f5? Nf3 Nf6 g3 g6 Bg2 Bg7 c4 O-O Nc3 d6 O-O Nc6 d5 Ne5
|
||||
|
||||
# Less usual openings
|
||||
Nc3? d5 e4 d4 Nce2 e5 Ng3 Be6 Nf3 Nd7 c3 c5 Bb5 Bd6 O-O a6
|
||||
Nc3? d5 d4 Nf6 Bg5 Nbd7 Nf3 h6 Bh4 c6 e3 e6 Bd3 Be7 O-O O-O
|
||||
Nc3? d5 e4 c6 Nf3 Bg4 h3 Bh5 d4
|
||||
Nc3? e5 e4 Nf6 Bc4 Nc6 d3 Bb4 Bg5 h6 Bxf6 Bxc3 bxc3 Qxf6 Ne2 d6
|
||||
c4 e5 Nc3 Nf6 Nf3 Nc6 g3 d5 cxd5 Nxd5 Bg2 Nb6 O-O Be7 d3 O-O a3 Be6 b4
|
||||
c4 e5 Nc3 Nf6 g3 d5 cxd5 Nxd5 Bg2 Nb6 Nf3 Nc6 O-O Be7 a3 O-O b4 Be6 d3
|
||||
f4 d5 Nf3 Nf6 e3 g6 Be2 Bg7 O-O O-O d3 c5 Qe1 Nc6 Nc3 Re8
|
||||
f4 d5 Nf3 g6 g3 Bg7 Bg2 Nf6 O-O O-O d3 c5 Nc3 d4 Ne4
|
||||
f4? d5 Nf3 g6 e3 Bg7 Be2 Nf6
|
||||
f4? d5 e3 Nf6 Nf3 Bg4 Be2 Nbd7 Ne5
|
||||
b3? e5 Bb2 Nc6 e3 d5 Bb5 Bd6 Nf3 Qe7 c4 Nf6
|
||||
e4 g6? d4 Bg7 Nc3 d6 f4 Nf6 Nf3 O-O Bd3 Nc6 O-O e5
|
||||
e4 d6 d4 Nf6 Nc3 g6 Nf3 Bg7
|
||||
e4 d6? d4 g6 Nc3 Bg7 Nf3 Nf6
|
||||
d4 d6? e4 Nf6 Nc3 g6 Be3 Bg7 Qd2 c6
|
||||
d4 g6 e4 Bg7 Nf3 d6 Nc3 Nf6 Be2 O-O O-O c6 h3 Qc7 Bf4 Nbd7 e5 dxe5 Nxe5 Nxe5 Bxe5 Qb6
|
||||
d4 g6 c4 Bg7 Nc3 d6 e4 Nf6 Nf3 O-O Be2 e5 O-O
|
||||
d4 g6? e4 Bg7 c4 d6 Nc3 Nc6 Be3 e5 d5 Nce7
|
||||
c4 Nf6 g3 g6 Bg2 Bg7 Nc3 O-O e4 d6 Nge2 c5 O-O Nc6 d3 a6 h3 Rb8 a4
|
||||
c4 Nf6 g3? e6 d4 d5 Nf3 dxc4 Bg2 a6 O-O Nc6
|
||||
b3? e6 Bb2 Nf6 e3 c5 Nf3 Be7 d4 O-O Bd3 d5 O-O Nc6
|
359
CuckooChessEngine/src/chess/BitBoard.java
Normal file
359
CuckooChessEngine/src/chess/BitBoard.java
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
public class BitBoard {
|
||||
|
||||
/** Squares attacked by a king on a given square. */
|
||||
public static final long[] kingAttacks;
|
||||
public static final long[] knightAttacks;
|
||||
public static final long[] wPawnAttacks, bPawnAttacks;
|
||||
|
||||
// Squares preventing a pawn from being a passed pawn, if occupied by enemy pawn
|
||||
static final long[] wPawnBlockerMask, bPawnBlockerMask;
|
||||
|
||||
public static final long maskAToGFiles = 0x7F7F7F7F7F7F7F7FL;
|
||||
public static final long maskBToHFiles = 0xFEFEFEFEFEFEFEFEL;
|
||||
public static final long maskAToFFiles = 0x3F3F3F3F3F3F3F3FL;
|
||||
public static final long maskCToHFiles = 0xFCFCFCFCFCFCFCFCL;
|
||||
|
||||
public static final long[] maskFile = {
|
||||
0x0101010101010101L,
|
||||
0x0202020202020202L,
|
||||
0x0404040404040404L,
|
||||
0x0808080808080808L,
|
||||
0x1010101010101010L,
|
||||
0x2020202020202020L,
|
||||
0x4040404040404040L,
|
||||
0x8080808080808080L
|
||||
};
|
||||
|
||||
public static final long maskRow1 = 0x00000000000000FFL;
|
||||
public static final long maskRow2 = 0x000000000000FF00L;
|
||||
public static final long maskRow3 = 0x0000000000FF0000L;
|
||||
public static final long maskRow4 = 0x00000000FF000000L;
|
||||
public static final long maskRow5 = 0x000000FF00000000L;
|
||||
public static final long maskRow6 = 0x0000FF0000000000L;
|
||||
public static final long maskRow7 = 0x00FF000000000000L;
|
||||
public static final long maskRow8 = 0xFF00000000000000L;
|
||||
public static final long maskRow1Row8 = 0xFF000000000000FFL;
|
||||
|
||||
public static final long maskDarkSq = 0xAA55AA55AA55AA55L;
|
||||
public static final long maskLightSq = 0x55AA55AA55AA55AAL;
|
||||
|
||||
public static final long maskCorners = 0x8100000000000081L;
|
||||
|
||||
static {
|
||||
// Compute king attacks
|
||||
kingAttacks = new long[64];
|
||||
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
long m = 1L << sq;
|
||||
long mask = (((m >>> 1) | (m << 7) | (m >>> 9)) & maskAToGFiles) |
|
||||
(((m << 1) | (m << 9) | (m >>> 7)) & maskBToHFiles) |
|
||||
(m << 8) | (m >>> 8);
|
||||
kingAttacks[sq] = mask;
|
||||
}
|
||||
|
||||
// Compute knight attacks
|
||||
knightAttacks = new long[64];
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
long m = 1L << sq;
|
||||
long mask = (((m << 6) | (m >>> 10)) & maskAToFFiles) |
|
||||
(((m << 15) | (m >>> 17)) & maskAToGFiles) |
|
||||
(((m << 17) | (m >>> 15)) & maskBToHFiles) |
|
||||
(((m << 10) | (m >>> 6)) & maskCToHFiles);
|
||||
knightAttacks[sq] = mask;
|
||||
}
|
||||
|
||||
// Compute pawn attacks
|
||||
wPawnAttacks = new long[64];
|
||||
bPawnAttacks = new long[64];
|
||||
wPawnBlockerMask = new long[64];
|
||||
bPawnBlockerMask = new long[64];
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
long m = 1L << sq;
|
||||
long mask = ((m << 7) & maskAToGFiles) | ((m << 9) & maskBToHFiles);
|
||||
wPawnAttacks[sq] = mask;
|
||||
mask = ((m >>> 9) & maskAToGFiles) | ((m >>> 7) & maskBToHFiles);
|
||||
bPawnAttacks[sq] = mask;
|
||||
|
||||
int x = Position.getX(sq);
|
||||
int y = Position.getY(sq);
|
||||
m = 0;
|
||||
for (int y2 = y+1; y2 < 8; y2++) {
|
||||
if (x > 0) m |= 1L << Position.getSquare(x-1, y2);
|
||||
m |= 1L << Position.getSquare(x , y2);
|
||||
if (x < 7) m |= 1L << Position.getSquare(x+1, y2);
|
||||
}
|
||||
wPawnBlockerMask[sq] = m;
|
||||
m = 0;
|
||||
for (int y2 = y-1; y2 >= 0; y2--) {
|
||||
if (x > 0) m |= 1L << Position.getSquare(x-1, y2);
|
||||
m |= 1L << Position.getSquare(x , y2);
|
||||
if (x < 7) m |= 1L << Position.getSquare(x+1, y2);
|
||||
}
|
||||
bPawnBlockerMask[sq] = m;
|
||||
}
|
||||
}
|
||||
|
||||
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, 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,
|
||||
10, 9, 9, 9, 9, 9, 10, 10,
|
||||
11, 10, 10, 10, 10, 11, 11, 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,
|
||||
};
|
||||
private final static long[][] bTables;
|
||||
private final static long[] bMasks;
|
||||
private final static int[] bBits = { 5, 4, 5, 5, 5, 5, 4, 5,
|
||||
4, 4, 5, 5, 5, 5, 4, 4,
|
||||
4, 4, 7, 7, 7, 7, 4, 4,
|
||||
5, 5, 7, 9, 9, 7, 5, 5,
|
||||
5, 5, 7, 9, 9, 7, 5, 5,
|
||||
4, 4, 7, 7, 7, 7, 4, 4,
|
||||
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,
|
||||
};
|
||||
|
||||
private static final long createPattern(int i, long mask) {
|
||||
long ret = 0L;
|
||||
for (int j = 0; ; j++) {
|
||||
long nextMask = mask & (mask - 1);
|
||||
long bit = mask ^ nextMask;
|
||||
if ((i & (1L << j)) != 0)
|
||||
ret |= bit;
|
||||
mask = nextMask;
|
||||
if (mask == 0)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final long addRookRays(int x, int y, long occupied, boolean inner) {
|
||||
long mask = 0;
|
||||
mask = addRay(mask, x, y, 1, 0, occupied, inner);
|
||||
mask = addRay(mask, x, y, -1, 0, occupied, inner);
|
||||
mask = addRay(mask, x, y, 0, 1, occupied, inner);
|
||||
mask = addRay(mask, x, y, 0, -1, occupied, inner);
|
||||
return mask;
|
||||
}
|
||||
private static final long addBishopRays(int x, int y, long occupied, boolean inner) {
|
||||
long mask = 0;
|
||||
mask = addRay(mask, x, y, 1, 1, occupied, inner);
|
||||
mask = addRay(mask, x, y, -1, -1, occupied, inner);
|
||||
mask = addRay(mask, x, y, 1, -1, occupied, inner);
|
||||
mask = addRay(mask, x, y, -1, 1, occupied, inner);
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static final long addRay(long mask, int x, int y, int dx, int dy,
|
||||
long occupied, boolean inner) {
|
||||
int lo = inner ? 1 : 0;
|
||||
int hi = inner ? 6 : 7;
|
||||
while (true) {
|
||||
if (dx != 0) {
|
||||
x += dx; if ((x < lo) || (x > hi)) break;
|
||||
}
|
||||
if (dy != 0) {
|
||||
y += dy; if ((y < lo) || (y > hi)) break;
|
||||
}
|
||||
int sq = Position.getSquare(x, y);
|
||||
mask |= 1L << sq;
|
||||
if ((occupied & (1L << sq)) != 0)
|
||||
break;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static { // Rook magics
|
||||
rTables = new long[64][];
|
||||
rMasks = new long[64];
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
int x = Position.getX(sq);
|
||||
int y = Position.getY(sq);
|
||||
rMasks[sq] = addRookRays(x, y, 0L, true);
|
||||
int tableSize = 1 << rBits[sq];
|
||||
long[] table = new long[tableSize];
|
||||
for (int i = 0; i < tableSize; i++) table[i] = -1;
|
||||
int nPatterns = 1 << Long.bitCount(rMasks[sq]);
|
||||
for (int i = 0; i < nPatterns; i++) {
|
||||
long p = createPattern(i, rMasks[sq]);
|
||||
int entry = (int)((p * rMagics[sq]) >>> (64 - rBits[sq]));
|
||||
long atks = addRookRays(x, y, p, false);
|
||||
if (table[entry] == -1) {
|
||||
table[entry] = atks;
|
||||
} else if (table[entry] != atks) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
rTables[sq] = table;
|
||||
}
|
||||
}
|
||||
|
||||
static { // Bishop magics
|
||||
bTables = new long[64][];
|
||||
bMasks = new long[64];
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
int x = Position.getX(sq);
|
||||
int y = Position.getY(sq);
|
||||
bMasks[sq] = addBishopRays(x, y, 0L, true);
|
||||
int tableSize = 1 << bBits[sq];
|
||||
long[] table = new long[tableSize];
|
||||
for (int i = 0; i < tableSize; i++) table[i] = -1;
|
||||
int nPatterns = 1 << Long.bitCount(bMasks[sq]);
|
||||
for (int i = 0; i < nPatterns; i++) {
|
||||
long p = createPattern(i, bMasks[sq]);
|
||||
int entry = (int)((p * bMagics[sq]) >>> (64 - bBits[sq]));
|
||||
long atks = addBishopRays(x, y, p, false);
|
||||
if (table[entry] == -1) {
|
||||
table[entry] = atks;
|
||||
} else if (table[entry] != atks) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
bTables[sq] = table;
|
||||
}
|
||||
}
|
||||
|
||||
public static final long bishopAttacks(int sq, long occupied) {
|
||||
return bTables[sq][(int)(((occupied & bMasks[sq]) * bMagics[sq]) >>> (64 - bBits[sq]))];
|
||||
}
|
||||
|
||||
public static final long rookAttacks(int sq, long occupied) {
|
||||
return rTables[sq][(int)(((occupied & rMasks[sq]) * rMagics[sq]) >>> (64 - rBits[sq]))];
|
||||
}
|
||||
|
||||
static public final long[][] squaresBetween;
|
||||
static {
|
||||
squaresBetween = new long[64][];
|
||||
for (int sq1 = 0; sq1 < 64; sq1++) {
|
||||
squaresBetween[sq1] = new long[64];
|
||||
for (int j = 0; j < 64; j++)
|
||||
squaresBetween[sq1][j] = 0;
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
if ((dx == 0) && (dy == 0))
|
||||
continue;
|
||||
long m = 0;
|
||||
int x = Position.getX(sq1);
|
||||
int y = Position.getY(sq1);
|
||||
while (true) {
|
||||
x += dx; y += dy;
|
||||
if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
|
||||
break;
|
||||
int sq2 = Position.getSquare(x, y);
|
||||
squaresBetween[sq1][sq2] = m;
|
||||
m |= 1L << sq2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static public final int getDirection(int from, int to) {
|
||||
int offs = to + (to|7) - from - (from|7) + 0x77;
|
||||
return dirTable[offs];
|
||||
}
|
||||
|
||||
public static final long southFill(long mask) {
|
||||
mask |= (mask >>> 8);
|
||||
mask |= (mask >>> 16);
|
||||
mask |= (mask >>> 32);
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static final long northFill(long mask) {
|
||||
mask |= (mask << 8);
|
||||
mask |= (mask << 16);
|
||||
mask |= (mask << 32);
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static final int trailingZ[] = {
|
||||
63, 0, 58, 1, 59, 47, 53, 2,
|
||||
60, 39, 48, 27, 54, 33, 42, 3,
|
||||
61, 51, 37, 40, 49, 18, 28, 20,
|
||||
55, 30, 34, 11, 43, 14, 22, 4,
|
||||
62, 57, 46, 52, 38, 26, 32, 41,
|
||||
50, 36, 17, 19, 29, 10, 13, 21,
|
||||
56, 45, 25, 31, 35, 16, 9, 12,
|
||||
44, 24, 15, 8, 23, 7, 6, 5
|
||||
};
|
||||
|
||||
static public final int numberOfTrailingZeros(long mask) {
|
||||
return trailingZ[(int)(((mask & -mask) * 0x07EDD5E59A4E28C2L) >>> 58)];
|
||||
}
|
||||
}
|
280
CuckooChessEngine/src/chess/Book.java
Normal file
280
CuckooChessEngine/src/chess/Book.java
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Implements an opening book.
|
||||
* @author petero
|
||||
*/
|
||||
public class Book {
|
||||
public static class BookEntry {
|
||||
Move move;
|
||||
int count;
|
||||
BookEntry(Move move) {
|
||||
this.move = move;
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
private static Map<Long, List<BookEntry>> bookMap;
|
||||
private static Random rndGen;
|
||||
private static int numBookMoves = -1;
|
||||
private boolean verbose;
|
||||
|
||||
public Book(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
private final void initBook() {
|
||||
if (numBookMoves >= 0)
|
||||
return;
|
||||
long t0 = System.currentTimeMillis();
|
||||
bookMap = new HashMap<Long, List<BookEntry>>();
|
||||
rndGen = new SecureRandom();
|
||||
rndGen.setSeed(System.currentTimeMillis());
|
||||
numBookMoves = 0;
|
||||
try {
|
||||
InputStream inStream = getClass().getResourceAsStream("/book.bin");
|
||||
List<Byte> buf = new ArrayList<Byte>(8192);
|
||||
byte[] tmpBuf = new byte[1024];
|
||||
while (true) {
|
||||
int len = inStream.read(tmpBuf);
|
||||
if (len <= 0) break;
|
||||
for (int i = 0; i < len; i++)
|
||||
buf.add(tmpBuf[i]);
|
||||
}
|
||||
inStream.close();
|
||||
Position startPos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Position pos = new Position(startPos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
int len = buf.size();
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
int b0 = buf.get(i); if (b0 < 0) b0 += 256;
|
||||
int b1 = buf.get(i+1); if (b1 < 0) b1 += 256;
|
||||
int move = (b0 << 8) + b1;
|
||||
if (move == 0) {
|
||||
pos = new Position(startPos);
|
||||
} else {
|
||||
boolean bad = ((move >> 15) & 1) != 0;
|
||||
int prom = (move >> 12) & 7;
|
||||
Move m = new Move(move & 63, (move >> 6) & 63,
|
||||
promToPiece(prom, pos.whiteMove));
|
||||
if (!bad)
|
||||
addToBook(pos, m);
|
||||
pos.makeMove(m, ui);
|
||||
}
|
||||
}
|
||||
} catch (ChessParseError ex) {
|
||||
throw new RuntimeException();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Can't read opening book resource");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (verbose) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
System.out.printf("Book moves:%d (parse time:%.3f)%n", numBookMoves,
|
||||
(t1 - t0) / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a move to a position in the opening book. */
|
||||
private final void addToBook(Position pos, Move moveToAdd) {
|
||||
List<BookEntry> ent = bookMap.get(pos.zobristHash());
|
||||
if (ent == null) {
|
||||
ent = new ArrayList<BookEntry>();
|
||||
bookMap.put(pos.zobristHash(), ent);
|
||||
}
|
||||
for (int i = 0; i < ent.size(); i++) {
|
||||
BookEntry be = ent.get(i);
|
||||
if (be.move.equals(moveToAdd)) {
|
||||
be.count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
BookEntry be = new BookEntry(moveToAdd);
|
||||
ent.add(be);
|
||||
numBookMoves++;
|
||||
}
|
||||
|
||||
/** Return a random book move for a position, or null if out of book. */
|
||||
public final Move getBookMove(Position pos) {
|
||||
initBook();
|
||||
List<BookEntry> bookMoves = bookMap.get(pos.zobristHash());
|
||||
if (bookMoves == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MoveGen.MoveList legalMoves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, legalMoves);
|
||||
int sum = 0;
|
||||
for (int i = 0; i < bookMoves.size(); i++) {
|
||||
BookEntry be = bookMoves.get(i);
|
||||
boolean contains = false;
|
||||
for (int mi = 0; mi < legalMoves.size; mi++)
|
||||
if (legalMoves.m[mi].equals(be.move)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
if (!contains) {
|
||||
// If an illegal move was found, it means there was a hash collision.
|
||||
return null;
|
||||
}
|
||||
sum += getWeight(bookMoves.get(i).count);
|
||||
}
|
||||
if (sum <= 0) {
|
||||
return null;
|
||||
}
|
||||
int rnd = rndGen.nextInt(sum);
|
||||
sum = 0;
|
||||
for (int i = 0; i < bookMoves.size(); i++) {
|
||||
sum += getWeight(bookMoves.get(i).count);
|
||||
if (rnd < sum) {
|
||||
return bookMoves.get(i).move;
|
||||
}
|
||||
}
|
||||
// Should never get here
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
final private int getWeight(int count) {
|
||||
double tmp = Math.sqrt(count);
|
||||
return (int)(tmp * Math.sqrt(tmp) * 100 + 1);
|
||||
}
|
||||
|
||||
/** Return a string describing all book moves. */
|
||||
public final String getAllBookMoves(Position pos) {
|
||||
initBook();
|
||||
StringBuilder ret = new StringBuilder();
|
||||
List<BookEntry> bookMoves = bookMap.get(pos.zobristHash());
|
||||
if (bookMoves != null) {
|
||||
for (BookEntry be : bookMoves) {
|
||||
String moveStr = TextIO.moveToString(pos, be.move, false);
|
||||
ret.append(moveStr);
|
||||
ret.append("(");
|
||||
ret.append(be.count);
|
||||
ret.append(") ");
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/** Creates the book.bin file. */
|
||||
public static void main(String[] args) throws IOException {
|
||||
List<Byte> binBook = createBinBook();
|
||||
FileOutputStream out = new FileOutputStream("../src/book.bin");
|
||||
int bookLen = binBook.size();
|
||||
byte[] binBookA = new byte[bookLen];
|
||||
for (int i = 0; i < bookLen; i++)
|
||||
binBookA[i] = binBook.get(i);
|
||||
out.write(binBookA);
|
||||
out.close();
|
||||
}
|
||||
|
||||
public static List<Byte> createBinBook() {
|
||||
List<Byte> binBook = new ArrayList<Byte>(0);
|
||||
try {
|
||||
InputStream inStream = new Object().getClass().getResourceAsStream("/book.txt");
|
||||
InputStreamReader inFile = new InputStreamReader(inStream);
|
||||
BufferedReader inBuf = new BufferedReader(inFile);
|
||||
LineNumberReader lnr = new LineNumberReader(inBuf);
|
||||
String line;
|
||||
while ((line = lnr.readLine()) != null) {
|
||||
if (line.startsWith("#") || (line.length() == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (!addBookLine(line, binBook)) {
|
||||
System.out.printf("Book parse error, line:%d\n", lnr.getLineNumber());
|
||||
throw new RuntimeException();
|
||||
}
|
||||
// System.out.printf("no:%d line:%s%n", lnr.getLineNumber(), line);
|
||||
}
|
||||
lnr.close();
|
||||
} catch (ChessParseError ex) {
|
||||
throw new RuntimeException();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Can't read opening book resource");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return binBook;
|
||||
}
|
||||
|
||||
/** Add a sequence of moves, starting from the initial position, to the binary opening book. */
|
||||
private static boolean addBookLine(String line, List<Byte> binBook) throws ChessParseError {
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
String[] strMoves = line.split(" ");
|
||||
for (String strMove : strMoves) {
|
||||
// System.out.printf("Adding move:%s\n", strMove);
|
||||
int bad = 0;
|
||||
if (strMove.endsWith("?")) {
|
||||
strMove = strMove.substring(0, strMove.length() - 1);
|
||||
bad = 1;
|
||||
}
|
||||
Move m = TextIO.stringToMove(pos, strMove);
|
||||
if (m == null) {
|
||||
return false;
|
||||
}
|
||||
int prom = pieceToProm(m.promoteTo);
|
||||
int val = m.from + (m.to << 6) + (prom << 12) + (bad << 15);
|
||||
binBook.add((byte)(val >> 8));
|
||||
binBook.add((byte)(val & 255));
|
||||
pos.makeMove(m, ui);
|
||||
}
|
||||
binBook.add((byte)0);
|
||||
binBook.add((byte)0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int pieceToProm(int p) {
|
||||
switch (p) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN:
|
||||
return 1;
|
||||
case Piece.WROOK: case Piece.BROOK:
|
||||
return 2;
|
||||
case Piece.WBISHOP: case Piece.BBISHOP:
|
||||
return 3;
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int promToPiece(int prom, boolean whiteMove) {
|
||||
switch (prom) {
|
||||
case 1: return whiteMove ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
case 2: return whiteMove ? Piece.WROOK : Piece.BROOK;
|
||||
case 3: return whiteMove ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
case 4: return whiteMove ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
default: return Piece.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
32
CuckooChessEngine/src/chess/ChessParseError.java
Normal file
32
CuckooChessEngine/src/chess/ChessParseError.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* Exception class to represent parse errors in FEN or algebraic notation.
|
||||
* @author petero
|
||||
*/
|
||||
public class ChessParseError extends Exception {
|
||||
private static final long serialVersionUID = -6051856171275301175L;
|
||||
public ChessParseError() {
|
||||
}
|
||||
public ChessParseError(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
244
CuckooChessEngine/src/chess/ComputerPlayer.java
Normal file
244
CuckooChessEngine/src/chess/ComputerPlayer.java
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A computer algorithm player.
|
||||
* @author petero
|
||||
*/
|
||||
public class ComputerPlayer implements Player {
|
||||
public static final String engineName;
|
||||
|
||||
static {
|
||||
String name = "CuckooChess 1.13a2";
|
||||
String m = System.getProperty("sun.arch.data.model");
|
||||
if ("32".equals(m))
|
||||
name += " 32-bit";
|
||||
else if ("64".equals(m))
|
||||
name += " 64-bit";
|
||||
engineName = name;
|
||||
}
|
||||
|
||||
int minTimeMillis;
|
||||
int maxTimeMillis;
|
||||
int maxDepth;
|
||||
int maxNodes;
|
||||
public boolean verbose;
|
||||
TranspositionTable tt;
|
||||
Book book;
|
||||
boolean bookEnabled;
|
||||
boolean randomMode;
|
||||
Search currentSearch;
|
||||
|
||||
public ComputerPlayer() {
|
||||
minTimeMillis = 10000;
|
||||
maxTimeMillis = 10000;
|
||||
maxDepth = 100;
|
||||
maxNodes = -1;
|
||||
verbose = true;
|
||||
setTTLogSize(15);
|
||||
book = new Book(verbose);
|
||||
bookEnabled = true;
|
||||
randomMode = false;
|
||||
}
|
||||
|
||||
public void setTTLogSize(int logSize) {
|
||||
tt = new TranspositionTable(logSize);
|
||||
}
|
||||
|
||||
Search.Listener listener;
|
||||
public void setListener(Search.Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand(Position pos, boolean drawOffer, List<Position> history) {
|
||||
// Create a search object
|
||||
long[] posHashList = new long[200 + history.size()];
|
||||
int posHashListSize = 0;
|
||||
for (Position p : history) {
|
||||
posHashList[posHashListSize++] = p.zobristHash();
|
||||
}
|
||||
tt.nextGeneration();
|
||||
Search sc = new Search(pos, posHashList, posHashListSize, tt);
|
||||
|
||||
// Determine all legal moves
|
||||
MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
sc.scoreMoveList(moves, 0);
|
||||
|
||||
// Test for "game over"
|
||||
if (moves.size == 0) {
|
||||
// Switch sides so that the human can decide what to do next.
|
||||
return "swap";
|
||||
}
|
||||
|
||||
if (bookEnabled) {
|
||||
Move bookMove = book.getBookMove(pos);
|
||||
if (bookMove != null) {
|
||||
System.out.printf("Book moves: %s\n", book.getAllBookMoves(pos));
|
||||
return TextIO.moveToString(pos, bookMove, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Find best move using iterative deepening
|
||||
currentSearch = sc;
|
||||
sc.setListener(listener);
|
||||
Move bestM;
|
||||
if ((moves.size == 1) && (canClaimDraw(pos, posHashList, posHashListSize, moves.m[0]) == "")) {
|
||||
bestM = moves.m[0];
|
||||
bestM.score = 0;
|
||||
} else if (randomMode) {
|
||||
bestM = findSemiRandomMove(sc, moves);
|
||||
} else {
|
||||
sc.timeLimit(minTimeMillis, maxTimeMillis);
|
||||
bestM = sc.iterativeDeepening(moves, maxDepth, maxNodes, verbose);
|
||||
}
|
||||
currentSearch = null;
|
||||
// tt.printStats();
|
||||
String strMove = TextIO.moveToString(pos, bestM, false);
|
||||
|
||||
// Claim draw if appropriate
|
||||
if (bestM.score <= 0) {
|
||||
String drawClaim = canClaimDraw(pos, posHashList, posHashListSize, bestM);
|
||||
if (drawClaim != "")
|
||||
strMove = drawClaim;
|
||||
}
|
||||
return strMove;
|
||||
}
|
||||
|
||||
/** Check if a draw claim is allowed, possibly after playing "move".
|
||||
* @param move The move that may have to be made before claiming draw.
|
||||
* @return The draw string that claims the draw, or empty string if draw claim not valid.
|
||||
*/
|
||||
private String canClaimDraw(Position pos, long[] posHashList, int posHashListSize, Move move) {
|
||||
String drawStr = "";
|
||||
if (Search.canClaimDraw50(pos)) {
|
||||
drawStr = "draw 50";
|
||||
} else if (Search.canClaimDrawRep(pos, posHashList, posHashListSize, posHashListSize)) {
|
||||
drawStr = "draw rep";
|
||||
} else {
|
||||
String strMove = TextIO.moveToString(pos, move, false);
|
||||
posHashList[posHashListSize++] = pos.zobristHash();
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
if (Search.canClaimDraw50(pos)) {
|
||||
drawStr = "draw 50 " + strMove;
|
||||
} else if (Search.canClaimDrawRep(pos, posHashList, posHashListSize, posHashListSize)) {
|
||||
drawStr = "draw rep " + strMove;
|
||||
}
|
||||
pos.unMakeMove(move, ui);
|
||||
}
|
||||
return drawStr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHumanPlayer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useBook(boolean bookOn) {
|
||||
bookEnabled = bookOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeLimit(int minTimeLimit, int maxTimeLimit, boolean randomMode) {
|
||||
if (randomMode) {
|
||||
minTimeLimit = 0;
|
||||
maxTimeLimit = 0;
|
||||
}
|
||||
minTimeMillis = minTimeLimit;
|
||||
maxTimeMillis = maxTimeLimit;
|
||||
this.randomMode = randomMode;
|
||||
if (currentSearch != null) {
|
||||
currentSearch.timeLimit(minTimeLimit, maxTimeLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTT() {
|
||||
tt.clear();
|
||||
}
|
||||
|
||||
/** Search a position and return the best move and score. Used for test suite processing. */
|
||||
public TwoReturnValues<Move, String> searchPosition(Position pos, int maxTimeMillis) {
|
||||
// Create a search object
|
||||
long[] posHashList = new long[200];
|
||||
tt.nextGeneration();
|
||||
Search sc = new Search(pos, posHashList, 0, tt);
|
||||
|
||||
// Determine all legal moves
|
||||
MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
sc.scoreMoveList(moves, 0);
|
||||
|
||||
// Find best move using iterative deepening
|
||||
sc.timeLimit(maxTimeMillis, maxTimeMillis);
|
||||
Move bestM = sc.iterativeDeepening(moves, -1, -1, false);
|
||||
|
||||
// Extract PV
|
||||
String PV = TextIO.moveToString(pos, bestM, false) + " ";
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(bestM, ui);
|
||||
PV += tt.extractPV(pos);
|
||||
pos.unMakeMove(bestM, ui);
|
||||
|
||||
// tt.printStats();
|
||||
|
||||
// Return best move and PV
|
||||
return new TwoReturnValues<Move, String>(bestM, PV);
|
||||
}
|
||||
|
||||
private Move findSemiRandomMove(Search sc, MoveGen.MoveList moves) {
|
||||
sc.timeLimit(minTimeMillis, maxTimeMillis);
|
||||
Move bestM = sc.iterativeDeepening(moves, 1, maxNodes, verbose);
|
||||
int bestScore = bestM.score;
|
||||
|
||||
Random rndGen = new SecureRandom();
|
||||
rndGen.setSeed(System.currentTimeMillis());
|
||||
|
||||
int sum = 0;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
sum += moveProbWeight(moves.m[mi].score, bestScore);
|
||||
}
|
||||
int rnd = rndGen.nextInt(sum);
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
int weight = moveProbWeight(moves.m[mi].score, bestScore);
|
||||
if (rnd < weight) {
|
||||
return moves.m[mi];
|
||||
}
|
||||
rnd -= weight;
|
||||
}
|
||||
assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
private final static int moveProbWeight(int moveScore, int bestScore) {
|
||||
double d = (bestScore - moveScore) / 100.0;
|
||||
double w = 100*Math.exp(-d*d/2);
|
||||
return (int)Math.ceil(w);
|
||||
}
|
||||
|
||||
// FIXME!!! Test Botvinnik-Markoff extension
|
||||
}
|
1181
CuckooChessEngine/src/chess/Evaluate.java
Normal file
1181
CuckooChessEngine/src/chess/Evaluate.java
Normal file
File diff suppressed because it is too large
Load Diff
572
CuckooChessEngine/src/chess/Game.java
Normal file
572
CuckooChessEngine/src/chess/Game.java
Normal file
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class Game {
|
||||
protected List<Move> moveList = null;
|
||||
protected List<UndoInfo> uiInfoList = null;
|
||||
List<Boolean> drawOfferList = null;
|
||||
protected int currentMove;
|
||||
boolean pendingDrawOffer;
|
||||
GameState drawState;
|
||||
String drawStateMoveStr; // Move required to claim DRAW_REP or DRAW_50
|
||||
GameState resignState;
|
||||
public Position pos = null;
|
||||
protected Player whitePlayer;
|
||||
protected Player blackPlayer;
|
||||
|
||||
public Game(Player whitePlayer, Player blackPlayer) {
|
||||
this.whitePlayer = whitePlayer;
|
||||
this.blackPlayer = blackPlayer;
|
||||
handleCommand("new");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the game state according to move/command string from a player.
|
||||
* @param str The move or command to process.
|
||||
* @return True if str was understood, false otherwise.
|
||||
*/
|
||||
public boolean processString(String str) {
|
||||
if (handleCommand(str)) {
|
||||
return true;
|
||||
}
|
||||
if (getGameState() != GameState.ALIVE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Move m = TextIO.stringToMove(pos, str);
|
||||
if (m == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(m, ui);
|
||||
TextIO.fixupEPSquare(pos);
|
||||
while (currentMove < moveList.size()) {
|
||||
moveList.remove(currentMove);
|
||||
uiInfoList.remove(currentMove);
|
||||
drawOfferList.remove(currentMove);
|
||||
}
|
||||
moveList.add(m);
|
||||
uiInfoList.add(ui);
|
||||
drawOfferList.add(pendingDrawOffer);
|
||||
pendingDrawOffer = false;
|
||||
currentMove++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public final String getGameStateString() {
|
||||
switch (getGameState()) {
|
||||
case ALIVE:
|
||||
return "";
|
||||
case WHITE_MATE:
|
||||
return "Game over, white mates!";
|
||||
case BLACK_MATE:
|
||||
return "Game over, black mates!";
|
||||
case WHITE_STALEMATE:
|
||||
case BLACK_STALEMATE:
|
||||
return "Game over, draw by stalemate!";
|
||||
case DRAW_REP:
|
||||
{
|
||||
String ret = "Game over, draw by repetition!";
|
||||
if ((drawStateMoveStr != null) && (drawStateMoveStr.length() > 0)) {
|
||||
ret = ret + " [" + drawStateMoveStr + "]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case DRAW_50:
|
||||
{
|
||||
String ret = "Game over, draw by 50 move rule!";
|
||||
if ((drawStateMoveStr != null) && (drawStateMoveStr.length() > 0)) {
|
||||
ret = ret + " [" + drawStateMoveStr + "]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case DRAW_NO_MATE:
|
||||
return "Game over, draw by impossibility of mate!";
|
||||
case DRAW_AGREE:
|
||||
return "Game over, draw by agreement!";
|
||||
case RESIGN_WHITE:
|
||||
return "Game over, white resigns!";
|
||||
case RESIGN_BLACK:
|
||||
return "Game over, black resigns!";
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last played move, or null if no moves played yet.
|
||||
*/
|
||||
public Move getLastMove() {
|
||||
Move m = null;
|
||||
if (currentMove > 0) {
|
||||
m = moveList.get(currentMove - 1);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public enum GameState {
|
||||
ALIVE,
|
||||
WHITE_MATE, // White mates
|
||||
BLACK_MATE, // Black mates
|
||||
WHITE_STALEMATE, // White is stalemated
|
||||
BLACK_STALEMATE, // Black is stalemated
|
||||
DRAW_REP, // Draw by 3-fold repetition
|
||||
DRAW_50, // Draw by 50 move rule
|
||||
DRAW_NO_MATE, // Draw by impossibility of check mate
|
||||
DRAW_AGREE, // Draw by agreement
|
||||
RESIGN_WHITE, // White resigns
|
||||
RESIGN_BLACK // Black resigns
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of the game.
|
||||
*/
|
||||
public GameState getGameState() {
|
||||
MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
if (moves.size == 0) {
|
||||
if (MoveGen.inCheck(pos)) {
|
||||
return pos.whiteMove ? GameState.BLACK_MATE : GameState.WHITE_MATE;
|
||||
} else {
|
||||
return pos.whiteMove ? GameState.WHITE_STALEMATE : GameState.BLACK_STALEMATE;
|
||||
}
|
||||
}
|
||||
if (insufficientMaterial()) {
|
||||
return GameState.DRAW_NO_MATE;
|
||||
}
|
||||
if (resignState != GameState.ALIVE) {
|
||||
return resignState;
|
||||
}
|
||||
return drawState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a draw offer is available.
|
||||
* @return True if the current player has the option to accept a draw offer.
|
||||
*/
|
||||
public boolean haveDrawOffer() {
|
||||
if (currentMove > 0) {
|
||||
return drawOfferList.get(currentMove - 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a special command.
|
||||
* @param moveStr The command to handle
|
||||
* @return True if command handled, false otherwise.
|
||||
*/
|
||||
protected boolean handleCommand(String moveStr) {
|
||||
if (moveStr.equals("new")) {
|
||||
moveList = new ArrayList<Move>();
|
||||
uiInfoList = new ArrayList<UndoInfo>();
|
||||
drawOfferList = new ArrayList<Boolean>();
|
||||
currentMove = 0;
|
||||
pendingDrawOffer = false;
|
||||
drawState = GameState.ALIVE;
|
||||
resignState = GameState.ALIVE;
|
||||
try {
|
||||
pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
} catch (ChessParseError ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
whitePlayer.clearTT();
|
||||
blackPlayer.clearTT();
|
||||
activateHumanPlayer();
|
||||
return true;
|
||||
} else if (moveStr.equals("undo")) {
|
||||
if (currentMove > 0) {
|
||||
pos.unMakeMove(moveList.get(currentMove - 1), uiInfoList.get(currentMove - 1));
|
||||
currentMove--;
|
||||
pendingDrawOffer = false;
|
||||
drawState = GameState.ALIVE;
|
||||
resignState = GameState.ALIVE;
|
||||
return handleCommand("swap");
|
||||
} else {
|
||||
System.out.println("Nothing to undo");
|
||||
}
|
||||
return true;
|
||||
} else if (moveStr.equals("redo")) {
|
||||
if (currentMove < moveList.size()) {
|
||||
pos.makeMove(moveList.get(currentMove), uiInfoList.get(currentMove));
|
||||
currentMove++;
|
||||
pendingDrawOffer = false;
|
||||
return handleCommand("swap");
|
||||
} else {
|
||||
System.out.println("Nothing to redo");
|
||||
}
|
||||
return true;
|
||||
} else if (moveStr.equals("swap") || moveStr.equals("go")) {
|
||||
Player tmp = whitePlayer;
|
||||
whitePlayer = blackPlayer;
|
||||
blackPlayer = tmp;
|
||||
return true;
|
||||
} else if (moveStr.equals("list")) {
|
||||
listMoves();
|
||||
return true;
|
||||
} else if (moveStr.startsWith("setpos ")) {
|
||||
String fen = moveStr.substring(moveStr.indexOf(" ") + 1);
|
||||
Position newPos = null;
|
||||
try {
|
||||
newPos = TextIO.readFEN(fen);
|
||||
} catch (ChessParseError ex) {
|
||||
System.out.printf("Invalid FEN: %s (%s)%n", fen, ex.getMessage());
|
||||
}
|
||||
if (newPos != null) {
|
||||
handleCommand("new");
|
||||
pos = newPos;
|
||||
activateHumanPlayer();
|
||||
}
|
||||
return true;
|
||||
} else if (moveStr.equals("getpos")) {
|
||||
String fen = TextIO.toFEN(pos);
|
||||
System.out.println(fen);
|
||||
return true;
|
||||
} else if (moveStr.startsWith("draw ")) {
|
||||
if (getGameState() == GameState.ALIVE) {
|
||||
String drawCmd = moveStr.substring(moveStr.indexOf(" ") + 1);
|
||||
return handleDrawCmd(drawCmd);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (moveStr.equals("resign")) {
|
||||
if (getGameState()== GameState.ALIVE) {
|
||||
resignState = pos.whiteMove ? GameState.RESIGN_WHITE : GameState.RESIGN_BLACK;
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (moveStr.startsWith("book")) {
|
||||
String bookCmd = moveStr.substring(moveStr.indexOf(" ") + 1);
|
||||
return handleBookCmd(bookCmd);
|
||||
} else if (moveStr.startsWith("time")) {
|
||||
try {
|
||||
String timeStr = moveStr.substring(moveStr.indexOf(" ") + 1);
|
||||
int timeLimit = Integer.parseInt(timeStr);
|
||||
whitePlayer.timeLimit(timeLimit, timeLimit, false);
|
||||
blackPlayer.timeLimit(timeLimit, timeLimit, false);
|
||||
return true;
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
System.out.printf("Number format exception: %s\n", nfe.getMessage());
|
||||
return false;
|
||||
}
|
||||
} else if (moveStr.startsWith("perft ")) {
|
||||
try {
|
||||
String depthStr = moveStr.substring(moveStr.indexOf(" ") + 1);
|
||||
int depth = Integer.parseInt(depthStr);
|
||||
MoveGen moveGen = new MoveGen();
|
||||
long t0 = System.currentTimeMillis();
|
||||
long nodes = perfT(moveGen, pos, depth);
|
||||
long t1 = System.currentTimeMillis();
|
||||
System.out.printf("perft(%d) = %d, t=%.3fs\n", depth, nodes, (t1 - t0)*1e-3);
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
System.out.printf("Number format exception: %s\n", nfe.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Swap players around if needed to make the human player in control of the next move. */
|
||||
protected void activateHumanPlayer() {
|
||||
if (!(pos.whiteMove ? whitePlayer : blackPlayer).isHumanPlayer()) {
|
||||
Player tmp = whitePlayer;
|
||||
whitePlayer = blackPlayer;
|
||||
blackPlayer = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getPosHistory() {
|
||||
List<String> ret = new ArrayList<String>();
|
||||
|
||||
Position pos = new Position(this.pos);
|
||||
for (int i = currentMove; i > 0; i--) {
|
||||
pos.unMakeMove(moveList.get(i - 1), uiInfoList.get(i - 1));
|
||||
}
|
||||
ret.add(TextIO.toFEN(pos)); // Store initial FEN
|
||||
|
||||
StringBuilder moves = new StringBuilder();
|
||||
for (int i = 0; i < moveList.size(); i++) {
|
||||
Move move = moveList.get(i);
|
||||
String strMove = TextIO.moveToString(pos, move, false);
|
||||
moves.append(String.format(" %s", strMove));
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
}
|
||||
ret.add(moves.toString()); // Store move list string
|
||||
int numUndo = moveList.size() - currentMove;
|
||||
ret.add(((Integer)numUndo).toString());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a list of all moves.
|
||||
*/
|
||||
private void listMoves() {
|
||||
String movesStr = getMoveListString(false);
|
||||
System.out.printf("%s", movesStr);
|
||||
}
|
||||
|
||||
final public String getMoveListString(boolean compressed) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
|
||||
// Undo all moves in move history.
|
||||
Position pos = new Position(this.pos);
|
||||
for (int i = currentMove; i > 0; i--) {
|
||||
pos.unMakeMove(moveList.get(i - 1), uiInfoList.get(i - 1));
|
||||
}
|
||||
|
||||
// Print all moves
|
||||
String whiteMove = "";
|
||||
String blackMove = "";
|
||||
for (int i = 0; i < currentMove; i++) {
|
||||
Move move = moveList.get(i);
|
||||
String strMove = TextIO.moveToString(pos, move, false);
|
||||
if (drawOfferList.get(i)) {
|
||||
strMove += " (d)";
|
||||
}
|
||||
if (pos.whiteMove) {
|
||||
whiteMove = strMove;
|
||||
} else {
|
||||
blackMove = strMove;
|
||||
if (whiteMove.length() == 0) {
|
||||
whiteMove = "...";
|
||||
}
|
||||
if (compressed) {
|
||||
ret.append(String.format("%d. %s %s ",
|
||||
pos.fullMoveCounter, whiteMove, blackMove));
|
||||
} else {
|
||||
ret.append(String.format("%3d. %-10s %-10s%n",
|
||||
pos.fullMoveCounter, whiteMove, blackMove));
|
||||
}
|
||||
whiteMove = "";
|
||||
blackMove = "";
|
||||
}
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
}
|
||||
if ((whiteMove.length() > 0) || (blackMove.length() > 0)) {
|
||||
if (whiteMove.length() == 0) {
|
||||
whiteMove = "...";
|
||||
}
|
||||
if (compressed) {
|
||||
ret.append(String.format("%d. %s %s ",
|
||||
pos.fullMoveCounter, whiteMove, blackMove));
|
||||
} else {
|
||||
ret.append(String.format("%3d. %-8s %-8s%n",
|
||||
pos.fullMoveCounter, whiteMove, blackMove));
|
||||
}
|
||||
}
|
||||
String gameResult = getPGNResultString();
|
||||
if (!gameResult.equals("*")) {
|
||||
if (compressed) {
|
||||
ret.append(gameResult);
|
||||
} else {
|
||||
ret.append(String.format("%s%n", gameResult));
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public final String getPGNResultString() {
|
||||
String gameResult = "*";
|
||||
switch (getGameState()) {
|
||||
case ALIVE:
|
||||
break;
|
||||
case WHITE_MATE:
|
||||
case RESIGN_BLACK:
|
||||
gameResult = "1-0";
|
||||
break;
|
||||
case BLACK_MATE:
|
||||
case RESIGN_WHITE:
|
||||
gameResult = "0-1";
|
||||
break;
|
||||
case WHITE_STALEMATE:
|
||||
case BLACK_STALEMATE:
|
||||
case DRAW_REP:
|
||||
case DRAW_50:
|
||||
case DRAW_NO_MATE:
|
||||
case DRAW_AGREE:
|
||||
gameResult = "1/2-1/2";
|
||||
break;
|
||||
}
|
||||
return gameResult;
|
||||
}
|
||||
|
||||
/** Return a list of previous positions in this game, back to the last "zeroing" move. */
|
||||
public ArrayList<Position> getHistory() {
|
||||
ArrayList<Position> posList = new ArrayList<Position>();
|
||||
Position pos = new Position(this.pos);
|
||||
for (int i = currentMove; i > 0; i--) {
|
||||
if (pos.halfMoveClock == 0)
|
||||
break;
|
||||
pos.unMakeMove(moveList.get(i- 1), uiInfoList.get(i- 1));
|
||||
posList.add(new Position(pos));
|
||||
}
|
||||
Collections.reverse(posList);
|
||||
return posList;
|
||||
}
|
||||
|
||||
private boolean handleDrawCmd(String drawCmd) {
|
||||
if (drawCmd.startsWith("rep") || drawCmd.startsWith("50")) {
|
||||
boolean rep = drawCmd.startsWith("rep");
|
||||
Move m = null;
|
||||
String ms = drawCmd.substring(drawCmd.indexOf(" ") + 1);
|
||||
if (ms.length() > 0) {
|
||||
m = TextIO.stringToMove(pos, ms);
|
||||
}
|
||||
boolean valid;
|
||||
if (rep) {
|
||||
valid = false;
|
||||
List<Position> oldPositions = new ArrayList<Position>();
|
||||
if (m != null) {
|
||||
UndoInfo ui = new UndoInfo();
|
||||
Position tmpPos = new Position(pos);
|
||||
tmpPos.makeMove(m, ui);
|
||||
oldPositions.add(tmpPos);
|
||||
}
|
||||
oldPositions.add(pos);
|
||||
Position tmpPos = pos;
|
||||
for (int i = currentMove - 1; i >= 0; i--) {
|
||||
tmpPos = new Position(tmpPos);
|
||||
tmpPos.unMakeMove(moveList.get(i), uiInfoList.get(i));
|
||||
oldPositions.add(tmpPos);
|
||||
}
|
||||
int repetitions = 0;
|
||||
Position firstPos = oldPositions.get(0);
|
||||
for (Position p : oldPositions) {
|
||||
if (p.drawRuleEquals(firstPos))
|
||||
repetitions++;
|
||||
}
|
||||
if (repetitions >= 3) {
|
||||
valid = true;
|
||||
}
|
||||
} else {
|
||||
Position tmpPos = new Position(pos);
|
||||
if (m != null) {
|
||||
UndoInfo ui = new UndoInfo();
|
||||
tmpPos.makeMove(m, ui);
|
||||
}
|
||||
valid = tmpPos.halfMoveClock >= 100;
|
||||
}
|
||||
if (valid) {
|
||||
drawState = rep ? GameState.DRAW_REP : GameState.DRAW_50;
|
||||
drawStateMoveStr = null;
|
||||
if (m != null) {
|
||||
drawStateMoveStr = TextIO.moveToString(pos, m, false);
|
||||
}
|
||||
} else {
|
||||
pendingDrawOffer = true;
|
||||
if (m != null) {
|
||||
processString(ms);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (drawCmd.startsWith("offer ")) {
|
||||
pendingDrawOffer = true;
|
||||
String ms = drawCmd.substring(drawCmd.indexOf(" ") + 1);
|
||||
if (TextIO.stringToMove(pos, ms) != null) {
|
||||
processString(ms);
|
||||
}
|
||||
return true;
|
||||
} else if (drawCmd.equals("accept")) {
|
||||
if (haveDrawOffer()) {
|
||||
drawState = GameState.DRAW_AGREE;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleBookCmd(String bookCmd) {
|
||||
if (bookCmd.equals("off")) {
|
||||
whitePlayer.useBook(false);
|
||||
blackPlayer.useBook(false);
|
||||
return true;
|
||||
} else if (bookCmd.equals("on")) {
|
||||
whitePlayer.useBook(true);
|
||||
whitePlayer.useBook(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean insufficientMaterial() {
|
||||
if (pos.pieceTypeBB[Piece.WQUEEN] != 0) return false;
|
||||
if (pos.pieceTypeBB[Piece.WROOK] != 0) return false;
|
||||
if (pos.pieceTypeBB[Piece.WPAWN] != 0) return false;
|
||||
if (pos.pieceTypeBB[Piece.BQUEEN] != 0) return false;
|
||||
if (pos.pieceTypeBB[Piece.BROOK] != 0) return false;
|
||||
if (pos.pieceTypeBB[Piece.BPAWN] != 0) return false;
|
||||
int wb = Long.bitCount(pos.pieceTypeBB[Piece.WBISHOP]);
|
||||
int wn = Long.bitCount(pos.pieceTypeBB[Piece.WKNIGHT]);
|
||||
int bb = Long.bitCount(pos.pieceTypeBB[Piece.BBISHOP]);
|
||||
int bn = Long.bitCount(pos.pieceTypeBB[Piece.BKNIGHT]);
|
||||
if (wb + wn + bb + bn <= 1) {
|
||||
return true; // King + bishop/knight vs king is draw
|
||||
}
|
||||
if (wn + bn == 0) {
|
||||
// Only bishops. If they are all on the same color, the position is a draw.
|
||||
long bMask = pos.pieceTypeBB[Piece.WBISHOP] | pos.pieceTypeBB[Piece.BBISHOP];
|
||||
if (((bMask & BitBoard.maskDarkSq) == 0) ||
|
||||
((bMask & BitBoard.maskLightSq) == 0))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
final static long perfT(MoveGen moveGen, Position pos, int depth) {
|
||||
if (depth == 0)
|
||||
return 1;
|
||||
long nodes = 0;
|
||||
MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
if (depth == 1) {
|
||||
int ret = moves.size;
|
||||
moveGen.returnMoveList(moves);
|
||||
return ret;
|
||||
}
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
pos.makeMove(m, ui);
|
||||
nodes += perfT(moveGen, pos, depth - 1);
|
||||
pos.unMakeMove(m, ui);
|
||||
}
|
||||
moveGen.returnMoveList(moves);
|
||||
return nodes;
|
||||
}
|
||||
}
|
80
CuckooChessEngine/src/chess/History.java
Normal file
80
CuckooChessEngine/src/chess/History.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* Implements the relative history heuristic.
|
||||
* @author petero
|
||||
*/
|
||||
public final class History {
|
||||
private final int countSuccess[][];
|
||||
private final int countFail[][];
|
||||
private final int score[][];
|
||||
|
||||
public History() {
|
||||
countSuccess = new int[Piece.nPieceTypes][64];
|
||||
countFail = new int[Piece.nPieceTypes][64];
|
||||
score = new int[Piece.nPieceTypes][64];
|
||||
for (int p = 0; p < Piece.nPieceTypes; p++) {
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
countSuccess[p][sq] = 0;
|
||||
countFail[p][sq] = 0;
|
||||
score[p][sq] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Record move as a success. */
|
||||
public final void addSuccess(Position pos, Move m, int depth) {
|
||||
int p = pos.getPiece(m.from);
|
||||
int cnt = depth;
|
||||
int val = countSuccess[p][m.to] + cnt;
|
||||
if (val > 1000) {
|
||||
val /= 2;
|
||||
countFail[p][m.to] /= 2;
|
||||
}
|
||||
countSuccess[p][m.to] = val;
|
||||
score[p][m.to] = -1;
|
||||
}
|
||||
|
||||
/** Record move as a failure. */
|
||||
public final void addFail(Position pos, Move m, int depth) {
|
||||
int p = pos.getPiece(m.from);
|
||||
int cnt = depth;
|
||||
countFail[p][m.to] += cnt;
|
||||
score[p][m.to] = -1;
|
||||
}
|
||||
|
||||
/** Get a score between 0 and 49, depending of the success/fail ratio of the move. */
|
||||
public final int getHistScore(Position pos, Move m) {
|
||||
int p = pos.getPiece(m.from);
|
||||
int ret = score[p][m.to];
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
int succ = countSuccess[p][m.to];
|
||||
int fail = countFail[p][m.to];
|
||||
if (succ + fail > 0) {
|
||||
ret = succ * 49 / (succ + fail);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
score[p][m.to] = ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
73
CuckooChessEngine/src/chess/HumanPlayer.java
Normal file
73
CuckooChessEngine/src/chess/HumanPlayer.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A player that reads input from the keyboard.
|
||||
* @author petero
|
||||
*/
|
||||
public class HumanPlayer implements Player {
|
||||
private String lastCmd = "";
|
||||
private BufferedReader in;
|
||||
|
||||
public HumanPlayer() {
|
||||
in = new BufferedReader(new InputStreamReader(System.in));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand(Position pos, boolean drawOffer, List<Position> history) {
|
||||
try {
|
||||
String color = pos.whiteMove ? "white" : "black";
|
||||
System.out.print(String.format("Enter move (%s):", color));
|
||||
String moveStr = in.readLine();
|
||||
if (moveStr == null)
|
||||
return "quit";
|
||||
if (moveStr.length() == 0) {
|
||||
return lastCmd;
|
||||
} else {
|
||||
lastCmd = moveStr;
|
||||
}
|
||||
return moveStr;
|
||||
} catch (IOException ex) {
|
||||
return "quit";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHumanPlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useBook(boolean bookOn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeLimit(int minTimeLimit, int maxTimeLimit, boolean randomMode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTT() {
|
||||
}
|
||||
}
|
83
CuckooChessEngine/src/chess/KillerTable.java
Normal file
83
CuckooChessEngine/src/chess/KillerTable.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* Implement a table of killer moves for the killer heuristic.
|
||||
* @author petero
|
||||
*/
|
||||
public class KillerTable {
|
||||
/** There is one KTEntry for each ply in the search tree. */
|
||||
static final class KTEntry {
|
||||
public KTEntry() {
|
||||
move0 = move1 = 0;
|
||||
}
|
||||
int move0;
|
||||
int move1;
|
||||
}
|
||||
KTEntry[] ktList;
|
||||
|
||||
/** Create an empty killer table. */
|
||||
public KillerTable() {
|
||||
ktList = new KTEntry[200];
|
||||
for (int i = 0; i < ktList.length; i++)
|
||||
ktList[i] = new KTEntry();
|
||||
}
|
||||
|
||||
/** Add a killer move to the table. Moves are replaced on an LRU basis. */
|
||||
final public void addKiller(int ply, Move m) {
|
||||
if (ply >= ktList.length)
|
||||
return;
|
||||
int move = (short)(m.from + (m.to << 6) + (m.promoteTo << 12));
|
||||
KTEntry ent = ktList[ply];
|
||||
if (move != ent.move0) {
|
||||
ent.move1 = ent.move0;
|
||||
ent.move0 = move;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a score for move m based on hits in the killer table.
|
||||
* The score is 4 for primary hit at ply.
|
||||
* The score is 3 for secondary hit at ply.
|
||||
* The score is 2 for primary hit at ply - 2.
|
||||
* The score is 1 for secondary hit at ply - 2.
|
||||
* The score is 0 otherwise.
|
||||
*/
|
||||
final public int getKillerScore(int ply, Move m) {
|
||||
int move = (short)(m.from + (m.to << 6) + (m.promoteTo << 12));
|
||||
if (ply < ktList.length) {
|
||||
KTEntry ent = ktList[ply];
|
||||
if (move == ent.move0) {
|
||||
return 4;
|
||||
} else if (move == ent.move1) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
if ((ply - 2 >= 0) && (ply - 2 < ktList.length)) {
|
||||
KTEntry ent = ktList[ply - 2];
|
||||
if (move == ent.move0) {
|
||||
return 2;
|
||||
} else if (move == ent.move1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
97
CuckooChessEngine/src/chess/Move.java
Normal file
97
CuckooChessEngine/src/chess/Move.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class Move {
|
||||
/** From square, 0-63. */
|
||||
public int from;
|
||||
|
||||
/** To square, 0-63. */
|
||||
public int to;
|
||||
|
||||
/** Promotion piece. */
|
||||
public int promoteTo;
|
||||
|
||||
public int score;
|
||||
|
||||
/** Create a move object. */
|
||||
public Move(int from, int to, int promoteTo) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.promoteTo = promoteTo;
|
||||
this.score = 0;
|
||||
}
|
||||
|
||||
public Move(int from, int to, int promoteTo, int score) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.promoteTo = promoteTo;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
static public class SortByScore implements Comparator<Move> {
|
||||
public int compare(Move sm1, Move sm2) {
|
||||
return sm2.score - sm1.score;
|
||||
}
|
||||
}
|
||||
|
||||
public Move(Move m) {
|
||||
this.from = m.from;
|
||||
this.to = m.to;
|
||||
this.promoteTo = m.promoteTo;
|
||||
this.score = m.score;
|
||||
}
|
||||
|
||||
public void copyFrom(Move m) {
|
||||
from = m.from;
|
||||
to = m.to;
|
||||
promoteTo = m.promoteTo;
|
||||
// score = m.score;
|
||||
}
|
||||
|
||||
/** Note that score is not included in the comparison. */
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
return false;
|
||||
Move other = (Move)o;
|
||||
if (from != other.from)
|
||||
return false;
|
||||
if (to != other.to)
|
||||
return false;
|
||||
if (promoteTo != other.promoteTo)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (from * 64 + to) * 16 + promoteTo;
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.moveToUCIString(this);
|
||||
}
|
||||
}
|
1064
CuckooChessEngine/src/chess/MoveGen.java
Normal file
1064
CuckooChessEngine/src/chess/MoveGen.java
Normal file
File diff suppressed because it is too large
Load Diff
166
CuckooChessEngine/src/chess/Parameters.java
Normal file
166
CuckooChessEngine/src/chess/Parameters.java
Normal file
|
@ -0,0 +1,166 @@
|
|||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class Parameters {
|
||||
public static enum Type {
|
||||
CHECK,
|
||||
SPIN,
|
||||
COMBO,
|
||||
BUTTON,
|
||||
STRING
|
||||
}
|
||||
|
||||
public static class ParamBase {
|
||||
public String name;
|
||||
public Type type;
|
||||
public boolean visible;
|
||||
}
|
||||
|
||||
public static final class CheckParam extends ParamBase {
|
||||
public boolean value;
|
||||
public boolean defaultValue;
|
||||
CheckParam(String name, boolean visible, boolean def) {
|
||||
this.name = name;
|
||||
this.type = Type.CHECK;
|
||||
this.visible = visible;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SpinParam extends ParamBase {
|
||||
public int minValue;
|
||||
public int maxValue;
|
||||
public int value;
|
||||
public int defaultValue;
|
||||
SpinParam(String name, boolean visible, int minV, int maxV, int def) {
|
||||
this.name = name;
|
||||
this.type = Type.SPIN;
|
||||
this.visible = visible;
|
||||
this.minValue = minV;
|
||||
this.maxValue = maxV;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ComboParam extends ParamBase {
|
||||
public String[] allowedValues;
|
||||
public String value;
|
||||
public String defaultValue;
|
||||
ComboParam(String name, boolean visible, String[] allowed, String def) {
|
||||
this.name = name;
|
||||
this.type = Type.COMBO;
|
||||
this.visible = visible;
|
||||
this.allowedValues = allowed;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ButtonParam extends ParamBase {
|
||||
ButtonParam(String name, boolean visible) {
|
||||
this.name = name;
|
||||
this.type = Type.BUTTON;
|
||||
this.visible = visible;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class StringParam extends ParamBase {
|
||||
public String value;
|
||||
public String defaultValue;
|
||||
StringParam(String name, boolean visible, String def) {
|
||||
this.name = name;
|
||||
this.type = Type.STRING;
|
||||
this.visible = visible;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static Parameters instance() {
|
||||
return inst;
|
||||
}
|
||||
public final String[] getParamNames() {
|
||||
ArrayList<String> parNames = new ArrayList<String>();
|
||||
for (Map.Entry<String, ParamBase> e : params.entrySet())
|
||||
if (e.getValue().visible)
|
||||
parNames.add(e.getKey());
|
||||
return parNames.toArray(new String[parNames.size()]);
|
||||
}
|
||||
|
||||
public final ParamBase getParam(String name) {
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
private static final Parameters inst = new Parameters();
|
||||
private Map<String, ParamBase> params = new TreeMap<String, ParamBase>();
|
||||
|
||||
private Parameters() {
|
||||
addPar(new SpinParam("qV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("rV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("bV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("nV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("pV", false, -200, 200, 0));
|
||||
}
|
||||
|
||||
private final void addPar(ParamBase p) {
|
||||
params.put(p.name.toLowerCase(), p);
|
||||
}
|
||||
|
||||
final boolean getBooleanPar(String name) {
|
||||
return ((CheckParam)params.get(name.toLowerCase())).value;
|
||||
}
|
||||
final int getIntPar(String name) {
|
||||
int ret = ((SpinParam)params.get(name.toLowerCase())).value;
|
||||
return ret;
|
||||
}
|
||||
final String getStringPar(String name) {
|
||||
return ((StringParam)params.get(name.toLowerCase())).value;
|
||||
}
|
||||
|
||||
public final void set(String name, String value) {
|
||||
ParamBase p = params.get(name.toLowerCase());
|
||||
if (p == null)
|
||||
return;
|
||||
switch (p.type) {
|
||||
case CHECK: {
|
||||
CheckParam cp = (CheckParam)p;
|
||||
if (value.toLowerCase().equals("true"))
|
||||
cp.value = true;
|
||||
else if (value.toLowerCase().equals("false"))
|
||||
cp.value = false;
|
||||
break;
|
||||
}
|
||||
case SPIN: {
|
||||
SpinParam sp = (SpinParam)p;
|
||||
try {
|
||||
int val = Integer.parseInt(value);
|
||||
if ((val >= sp.minValue) && (val <= sp.maxValue))
|
||||
sp.value = val;
|
||||
} catch (NumberFormatException ex) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMBO: {
|
||||
ComboParam cp = (ComboParam)p;
|
||||
for (String allowed : cp.allowedValues)
|
||||
if (allowed.toLowerCase().equals(value.toLowerCase())) {
|
||||
cp.value = allowed;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BUTTON:
|
||||
break;
|
||||
case STRING: {
|
||||
StringParam sp = (StringParam)p;
|
||||
sp.value = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
CuckooChessEngine/src/chess/Piece.java
Normal file
57
CuckooChessEngine/src/chess/Piece.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* Constants for different piece types.
|
||||
* @author petero
|
||||
*/
|
||||
public class Piece {
|
||||
public static final int EMPTY = 0;
|
||||
|
||||
public static final int WKING = 1;
|
||||
public static final int WQUEEN = 2;
|
||||
public static final int WROOK = 3;
|
||||
public static final int WBISHOP = 4;
|
||||
public static final int WKNIGHT = 5;
|
||||
public static final int WPAWN = 6;
|
||||
|
||||
public static final int BKING = 7;
|
||||
public static final int BQUEEN = 8;
|
||||
public static final int BROOK = 9;
|
||||
public static final int BBISHOP = 10;
|
||||
public static final int BKNIGHT = 11;
|
||||
public static final int BPAWN = 12;
|
||||
|
||||
public static final int nPieceTypes = 13;
|
||||
|
||||
/**
|
||||
* Return true if p is a white piece, false otherwise.
|
||||
* Note that if p is EMPTY, an unspecified value is returned.
|
||||
*/
|
||||
public static final boolean isWhite(int pType) {
|
||||
return pType < BKING;
|
||||
}
|
||||
public static final int makeWhite(int pType) {
|
||||
return pType < BKING ? pType : pType - (BKING - WKING);
|
||||
}
|
||||
public static final int makeBlack(int pType) {
|
||||
return ((pType > EMPTY) && (pType < BKING)) ? pType + (BKING - WKING) : pType;
|
||||
}
|
||||
}
|
59
CuckooChessEngine/src/chess/Player.java
Normal file
59
CuckooChessEngine/src/chess/Player.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface for human/computer players.
|
||||
* @author petero
|
||||
*/
|
||||
public interface Player {
|
||||
/**
|
||||
* Get a command from a player.
|
||||
* The command can be a valid move string, in which case the move is played
|
||||
* and the turn goes over to the other player. The command can also be a special
|
||||
* command, such as "quit", "new", "resign", etc.
|
||||
* @param history List of earlier positions (not including the current position).
|
||||
* This makes it possible for the player to correctly handle
|
||||
* the draw by repetition rule.
|
||||
*/
|
||||
public String getCommand(Position pos, boolean drawOffer, List<Position> history);
|
||||
|
||||
/** Return true if this player is a human player. */
|
||||
public boolean isHumanPlayer();
|
||||
|
||||
/**
|
||||
* Inform player whether or not to use an opening book.
|
||||
* Of course, a human player is likely to ignore this.
|
||||
*/
|
||||
public void useBook(boolean bookOn);
|
||||
|
||||
/**
|
||||
* Inform player about min recommended/max allowed thinking time per move.
|
||||
* Of course, a human player is likely to ignore this.
|
||||
*/
|
||||
public void timeLimit(int minTimeLimit, int maxTimeLimit, boolean randomMode);
|
||||
|
||||
/**
|
||||
* Inform player that the transposition table should be cleared.
|
||||
* Of coarse, a human player has a hard time implementing this.
|
||||
*/
|
||||
public void clearTT();
|
||||
}
|
674
CuckooChessEngine/src/chess/Position.java
Normal file
674
CuckooChessEngine/src/chess/Position.java
Normal file
|
@ -0,0 +1,674 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Stores the state of a chess position.
|
||||
* All required state is stored, except for all previous positions
|
||||
* since the last capture or pawn move. That state is only needed
|
||||
* for three-fold repetition draw detection, and is better stored
|
||||
* in a separate hash table.
|
||||
* @author petero
|
||||
*/
|
||||
public class Position {
|
||||
public int[] squares;
|
||||
|
||||
// Bitboards
|
||||
public long[] pieceTypeBB;
|
||||
public long whiteBB, blackBB;
|
||||
|
||||
// Piece square table scores
|
||||
public short[] psScore1, psScore2;
|
||||
|
||||
public boolean whiteMove;
|
||||
|
||||
/** Bit definitions for the castleMask bit mask. */
|
||||
public static final int A1_CASTLE = 0; /** White long castle. */
|
||||
public static final int H1_CASTLE = 1; /** White short castle. */
|
||||
public static final int A8_CASTLE = 2; /** Black long castle. */
|
||||
public static final int H8_CASTLE = 3; /** Black short castle. */
|
||||
|
||||
private int castleMask;
|
||||
|
||||
private int epSquare;
|
||||
|
||||
/** Number of half-moves since last 50-move reset. */
|
||||
int halfMoveClock;
|
||||
|
||||
/** Game move number, starting from 1. */
|
||||
public int fullMoveCounter;
|
||||
|
||||
private long hashKey; // Cached Zobrist hash key
|
||||
private long pHashKey;
|
||||
public int wKingSq, bKingSq; // Cached king positions
|
||||
public int wMtrl; // Total value of all white pieces and pawns
|
||||
public int bMtrl; // Total value of all black pieces and pawns
|
||||
public int wMtrlPawns; // Total value of all white pawns
|
||||
public int bMtrlPawns; // Total value of all black pawns
|
||||
|
||||
/** Initialize board to empty position. */
|
||||
public Position() {
|
||||
squares = new int[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
squares[i] = Piece.EMPTY;
|
||||
pieceTypeBB = new long[Piece.nPieceTypes];
|
||||
psScore1 = new short[Piece.nPieceTypes];
|
||||
psScore2 = new short[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||
pieceTypeBB[i] = 0L;
|
||||
psScore1[i] = 0;
|
||||
psScore2[i] = 0;
|
||||
}
|
||||
whiteBB = blackBB = 0L;
|
||||
whiteMove = true;
|
||||
castleMask = 0;
|
||||
epSquare = -1;
|
||||
halfMoveClock = 0;
|
||||
fullMoveCounter = 1;
|
||||
hashKey = computeZobristHash();
|
||||
wKingSq = bKingSq = -1;
|
||||
wMtrl = bMtrl = -Evaluate.kV;
|
||||
wMtrlPawns = bMtrlPawns = 0;
|
||||
}
|
||||
|
||||
public Position(Position other) {
|
||||
squares = new int[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
squares[i] = other.squares[i];
|
||||
pieceTypeBB = new long[Piece.nPieceTypes];
|
||||
psScore1 = new short[Piece.nPieceTypes];
|
||||
psScore2 = new short[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||
pieceTypeBB[i] = other.pieceTypeBB[i];
|
||||
psScore1[i] = other.psScore1[i];
|
||||
psScore2[i] = other.psScore2[i];
|
||||
}
|
||||
whiteBB = other.whiteBB;
|
||||
blackBB = other.blackBB;
|
||||
whiteMove = other.whiteMove;
|
||||
castleMask = other.castleMask;
|
||||
epSquare = other.epSquare;
|
||||
halfMoveClock = other.halfMoveClock;
|
||||
fullMoveCounter = other.fullMoveCounter;
|
||||
hashKey = other.hashKey;
|
||||
pHashKey = other.pHashKey;
|
||||
wKingSq = other.wKingSq;
|
||||
bKingSq = other.bKingSq;
|
||||
wMtrl = other.wMtrl;
|
||||
bMtrl = other.bMtrl;
|
||||
wMtrlPawns = other.wMtrlPawns;
|
||||
bMtrlPawns = other.bMtrlPawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
return false;
|
||||
Position other = (Position)o;
|
||||
if (!drawRuleEquals(other))
|
||||
return false;
|
||||
if (halfMoveClock != other.halfMoveClock)
|
||||
return false;
|
||||
if (fullMoveCounter != other.fullMoveCounter)
|
||||
return false;
|
||||
if (hashKey != other.hashKey)
|
||||
return false;
|
||||
if (pHashKey != other.pHashKey)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)hashKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Zobrist hash value for the current position.
|
||||
* Everything except the move counters are included in the hash value.
|
||||
*/
|
||||
public final long zobristHash() {
|
||||
return hashKey;
|
||||
}
|
||||
public final long pawnZobristHash() {
|
||||
return pHashKey;
|
||||
}
|
||||
public final long kingZobristHash() {
|
||||
return psHashKeys[Piece.WKING][wKingSq] ^
|
||||
psHashKeys[Piece.BKING][bKingSq];
|
||||
}
|
||||
|
||||
public final long historyHash() {
|
||||
long ret = hashKey;
|
||||
if (halfMoveClock >= 80) {
|
||||
ret ^= moveCntKeys[Math.min(halfMoveClock, 100)];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if two positions are equal in the sense of the draw by repetition rule.
|
||||
* @return True if positions are equal, false otherwise.
|
||||
*/
|
||||
final public boolean drawRuleEquals(Position other) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (squares[i] != other.squares[i])
|
||||
return false;
|
||||
}
|
||||
if (whiteMove != other.whiteMove)
|
||||
return false;
|
||||
if (castleMask != other.castleMask)
|
||||
return false;
|
||||
if (epSquare != other.epSquare)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void setWhiteMove(boolean whiteMove) {
|
||||
if (whiteMove != this.whiteMove) {
|
||||
hashKey ^= whiteHashKey;
|
||||
this.whiteMove = whiteMove;
|
||||
}
|
||||
}
|
||||
/** Return index in squares[] vector corresponding to (x,y). */
|
||||
public final static int getSquare(int x, int y) {
|
||||
return y * 8 + x;
|
||||
}
|
||||
/** Return x position (file) corresponding to a square. */
|
||||
public final static int getX(int square) {
|
||||
return square & 7;
|
||||
}
|
||||
/** Return y position (rank) corresponding to a square. */
|
||||
public final static int getY(int square) {
|
||||
return square >> 3;
|
||||
}
|
||||
/** Return true if (x,y) is a dark square. */
|
||||
public final static boolean darkSquare(int x, int y) {
|
||||
return (x & 1) == (y & 1);
|
||||
}
|
||||
|
||||
/** Return piece occupying a square. */
|
||||
public final int getPiece(int square) {
|
||||
return squares[square];
|
||||
}
|
||||
|
||||
/** Move a non-pawn piece to an empty square. */
|
||||
private final void movePieceNotPawn(int from, int to) {
|
||||
final int piece = squares[from];
|
||||
hashKey ^= psHashKeys[piece][from];
|
||||
hashKey ^= psHashKeys[piece][to];
|
||||
hashKey ^= psHashKeys[Piece.EMPTY][from];
|
||||
hashKey ^= psHashKeys[Piece.EMPTY][to];
|
||||
|
||||
squares[from] = Piece.EMPTY;
|
||||
squares[to] = piece;
|
||||
|
||||
final long sqMaskF = 1L << from;
|
||||
final long sqMaskT = 1L << to;
|
||||
pieceTypeBB[piece] &= ~sqMaskF;
|
||||
pieceTypeBB[piece] |= sqMaskT;
|
||||
if (Piece.isWhite(piece)) {
|
||||
whiteBB &= ~sqMaskF;
|
||||
whiteBB |= sqMaskT;
|
||||
if (piece == Piece.WKING)
|
||||
wKingSq = to;
|
||||
} else {
|
||||
blackBB &= ~sqMaskF;
|
||||
blackBB |= sqMaskT;
|
||||
if (piece == Piece.BKING)
|
||||
bKingSq = to;
|
||||
}
|
||||
|
||||
psScore1[piece] += Evaluate.psTab1[piece][to] - Evaluate.psTab1[piece][from];
|
||||
psScore2[piece] += Evaluate.psTab2[piece][to] - Evaluate.psTab2[piece][from];
|
||||
}
|
||||
|
||||
/** Set a square to a piece value. */
|
||||
public final void setPiece(int square, int piece) {
|
||||
int removedPiece = squares[square];
|
||||
squares[square] = piece;
|
||||
|
||||
// Update hash key
|
||||
hashKey ^= psHashKeys[removedPiece][square];
|
||||
hashKey ^= psHashKeys[piece][square];
|
||||
|
||||
// Update bitboards
|
||||
final long sqMask = 1L << square;
|
||||
pieceTypeBB[removedPiece] &= ~sqMask;
|
||||
pieceTypeBB[piece] |= sqMask;
|
||||
|
||||
if (removedPiece != Piece.EMPTY) {
|
||||
int pVal = Evaluate.pieceValue[removedPiece];
|
||||
if (Piece.isWhite(removedPiece)) {
|
||||
wMtrl -= pVal;
|
||||
whiteBB &= ~sqMask;
|
||||
if (removedPiece == Piece.WPAWN) {
|
||||
wMtrlPawns -= pVal;
|
||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||
}
|
||||
} else {
|
||||
bMtrl -= pVal;
|
||||
blackBB &= ~sqMask;
|
||||
if (removedPiece == Piece.BPAWN) {
|
||||
bMtrlPawns -= pVal;
|
||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (piece != Piece.EMPTY) {
|
||||
int pVal = Evaluate.pieceValue[piece];
|
||||
if (Piece.isWhite(piece)) {
|
||||
wMtrl += pVal;
|
||||
whiteBB |= sqMask;
|
||||
if (piece == Piece.WPAWN) {
|
||||
wMtrlPawns += pVal;
|
||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||
}
|
||||
if (piece == Piece.WKING)
|
||||
wKingSq = square;
|
||||
} else {
|
||||
bMtrl += pVal;
|
||||
blackBB |= sqMask;
|
||||
if (piece == Piece.BPAWN) {
|
||||
bMtrlPawns += pVal;
|
||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||
}
|
||||
if (piece == Piece.BKING)
|
||||
bKingSq = square;
|
||||
}
|
||||
}
|
||||
|
||||
// Update piece/square table scores
|
||||
psScore1[removedPiece] -= Evaluate.psTab1[removedPiece][square];
|
||||
psScore2[removedPiece] -= Evaluate.psTab2[removedPiece][square];
|
||||
psScore1[piece] += Evaluate.psTab1[piece][square];
|
||||
psScore2[piece] += Evaluate.psTab2[piece][square];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a square to a piece value.
|
||||
* Special version that only updates enough of the state for the SEE function to be happy.
|
||||
*/
|
||||
public final void setSEEPiece(int square, int piece) {
|
||||
int removedPiece = squares[square];
|
||||
|
||||
// Update board
|
||||
squares[square] = piece;
|
||||
|
||||
// Update bitboards
|
||||
long sqMask = 1L << square;
|
||||
pieceTypeBB[removedPiece] &= ~sqMask;
|
||||
pieceTypeBB[piece] |= sqMask;
|
||||
if (removedPiece != Piece.EMPTY) {
|
||||
if (Piece.isWhite(removedPiece))
|
||||
whiteBB &= ~sqMask;
|
||||
else
|
||||
blackBB &= ~sqMask;
|
||||
}
|
||||
if (piece != Piece.EMPTY) {
|
||||
if (Piece.isWhite(piece))
|
||||
whiteBB |= sqMask;
|
||||
else
|
||||
blackBB |= sqMask;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if white long castling right has not been lost. */
|
||||
public final boolean a1Castle() {
|
||||
return (castleMask & (1 << A1_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if white short castling right has not been lost. */
|
||||
public final boolean h1Castle() {
|
||||
return (castleMask & (1 << H1_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if black long castling right has not been lost. */
|
||||
public final boolean a8Castle() {
|
||||
return (castleMask & (1 << A8_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if black short castling right has not been lost. */
|
||||
public final boolean h8Castle() {
|
||||
return (castleMask & (1 << H8_CASTLE)) != 0;
|
||||
}
|
||||
/** Bitmask describing castling rights. */
|
||||
public final int getCastleMask() {
|
||||
return castleMask;
|
||||
}
|
||||
public final void setCastleMask(int castleMask) {
|
||||
hashKey ^= castleHashKeys[this.castleMask];
|
||||
hashKey ^= castleHashKeys[castleMask];
|
||||
this.castleMask = castleMask;
|
||||
}
|
||||
|
||||
/** En passant square, or -1 if no ep possible. */
|
||||
public final int getEpSquare() {
|
||||
return epSquare;
|
||||
}
|
||||
public final void setEpSquare(int epSquare) {
|
||||
if (this.epSquare != epSquare) {
|
||||
hashKey ^= epHashKeys[(this.epSquare >= 0) ? getX(this.epSquare) + 1 : 0];
|
||||
hashKey ^= epHashKeys[(epSquare >= 0) ? getX(epSquare) + 1 : 0];
|
||||
this.epSquare = epSquare;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final int getKingSq(boolean whiteMove) {
|
||||
return whiteMove ? wKingSq : bKingSq;
|
||||
}
|
||||
|
||||
/** Apply a move to the current position. */
|
||||
public final void makeMove(Move move, UndoInfo ui) {
|
||||
ui.capturedPiece = squares[move.to];
|
||||
ui.castleMask = castleMask;
|
||||
ui.epSquare = epSquare;
|
||||
ui.halfMoveClock = halfMoveClock;
|
||||
boolean wtm = whiteMove;
|
||||
|
||||
final int p = squares[move.from];
|
||||
int capP = squares[move.to];
|
||||
long fromMask = 1L << move.from;
|
||||
|
||||
int prevEpSquare = epSquare;
|
||||
setEpSquare(-1);
|
||||
|
||||
if ((capP != Piece.EMPTY) || (((pieceTypeBB[Piece.WPAWN] | pieceTypeBB[Piece.BPAWN]) & fromMask) != 0)) {
|
||||
halfMoveClock = 0;
|
||||
|
||||
// Handle en passant and epSquare
|
||||
if (p == Piece.WPAWN) {
|
||||
if (move.to - move.from == 2 * 8) {
|
||||
int x = Position.getX(move.to);
|
||||
if ( ((x > 0) && (squares[move.to - 1] == Piece.BPAWN)) ||
|
||||
((x < 7) && (squares[move.to + 1] == Piece.BPAWN))) {
|
||||
setEpSquare(move.from + 8);
|
||||
}
|
||||
} else if (move.to == prevEpSquare) {
|
||||
setPiece(move.to - 8, Piece.EMPTY);
|
||||
}
|
||||
} else if (p == Piece.BPAWN) {
|
||||
if (move.to - move.from == -2 * 8) {
|
||||
int x = Position.getX(move.to);
|
||||
if ( ((x > 0) && (squares[move.to - 1] == Piece.WPAWN)) ||
|
||||
((x < 7) && (squares[move.to + 1] == Piece.WPAWN))) {
|
||||
setEpSquare(move.from - 8);
|
||||
}
|
||||
} else if (move.to == prevEpSquare) {
|
||||
setPiece(move.to + 8, Piece.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
if (((pieceTypeBB[Piece.WKING] | pieceTypeBB[Piece.BKING]) & fromMask) != 0) {
|
||||
if (wtm) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H1_CASTLE));
|
||||
} else {
|
||||
setCastleMask(castleMask & ~(1 << Position.A8_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H8_CASTLE));
|
||||
}
|
||||
}
|
||||
|
||||
// Perform move
|
||||
setPiece(move.from, Piece.EMPTY);
|
||||
// Handle promotion
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
setPiece(move.to, move.promoteTo);
|
||||
} else {
|
||||
setPiece(move.to, p);
|
||||
}
|
||||
} else {
|
||||
halfMoveClock++;
|
||||
|
||||
// Handle castling
|
||||
if (((pieceTypeBB[Piece.WKING] | pieceTypeBB[Piece.BKING]) & fromMask) != 0) {
|
||||
int k0 = move.from;
|
||||
if (move.to == k0 + 2) { // O-O
|
||||
movePieceNotPawn(k0 + 3, k0 + 1);
|
||||
} else if (move.to == k0 - 2) { // O-O-O
|
||||
movePieceNotPawn(k0 - 4, k0 - 1);
|
||||
}
|
||||
if (wtm) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H1_CASTLE));
|
||||
} else {
|
||||
setCastleMask(castleMask & ~(1 << Position.A8_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H8_CASTLE));
|
||||
}
|
||||
}
|
||||
|
||||
// Perform move
|
||||
movePieceNotPawn(move.from, move.to);
|
||||
}
|
||||
if (wtm) {
|
||||
// Update castling rights when rook moves
|
||||
if ((BitBoard.maskCorners & fromMask) != 0) {
|
||||
if (p == Piece.WROOK)
|
||||
removeCastleRights(move.from);
|
||||
}
|
||||
if ((BitBoard.maskCorners & (1L << move.to)) != 0) {
|
||||
if (capP == Piece.BROOK)
|
||||
removeCastleRights(move.to);
|
||||
}
|
||||
} else {
|
||||
fullMoveCounter++;
|
||||
// Update castling rights when rook moves
|
||||
if ((BitBoard.maskCorners & fromMask) != 0) {
|
||||
if (p == Piece.BROOK)
|
||||
removeCastleRights(move.from);
|
||||
}
|
||||
if ((BitBoard.maskCorners & (1L << move.to)) != 0) {
|
||||
if (capP == Piece.WROOK)
|
||||
removeCastleRights(move.to);
|
||||
}
|
||||
}
|
||||
|
||||
hashKey ^= whiteHashKey;
|
||||
whiteMove = !wtm;
|
||||
}
|
||||
|
||||
public final void unMakeMove(Move move, UndoInfo ui) {
|
||||
hashKey ^= whiteHashKey;
|
||||
whiteMove = !whiteMove;
|
||||
int p = squares[move.to];
|
||||
setPiece(move.from, p);
|
||||
setPiece(move.to, ui.capturedPiece);
|
||||
setCastleMask(ui.castleMask);
|
||||
setEpSquare(ui.epSquare);
|
||||
halfMoveClock = ui.halfMoveClock;
|
||||
boolean wtm = whiteMove;
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
p = wtm ? Piece.WPAWN : Piece.BPAWN;
|
||||
setPiece(move.from, p);
|
||||
}
|
||||
if (!wtm) {
|
||||
fullMoveCounter--;
|
||||
}
|
||||
|
||||
// Handle castling
|
||||
int king = wtm ? Piece.WKING : Piece.BKING;
|
||||
if (p == king) {
|
||||
int k0 = move.from;
|
||||
if (move.to == k0 + 2) { // O-O
|
||||
movePieceNotPawn(k0 + 1, k0 + 3);
|
||||
} else if (move.to == k0 - 2) { // O-O-O
|
||||
movePieceNotPawn(k0 - 1, k0 - 4);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle en passant
|
||||
if (move.to == epSquare) {
|
||||
if (p == Piece.WPAWN) {
|
||||
setPiece(move.to - 8, Piece.BPAWN);
|
||||
} else if (p == Piece.BPAWN) {
|
||||
setPiece(move.to + 8, Piece.WPAWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a move to the current position.
|
||||
* Special version that only updates enough of the state for the SEE function to be happy.
|
||||
*/
|
||||
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) {
|
||||
if (p == Piece.WPAWN) {
|
||||
setSEEPiece(move.to - 8, Piece.EMPTY);
|
||||
} else if (p == Piece.BPAWN) {
|
||||
setSEEPiece(move.to + 8, Piece.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform move
|
||||
setSEEPiece(move.from, Piece.EMPTY);
|
||||
setSEEPiece(move.to, p);
|
||||
whiteMove = !wtm;
|
||||
}
|
||||
|
||||
public final void unMakeSEEMove(Move move, UndoInfo ui) {
|
||||
whiteMove = !whiteMove;
|
||||
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) {
|
||||
if (p == Piece.WPAWN) {
|
||||
setSEEPiece(move.to - 8, Piece.BPAWN);
|
||||
} else if (p == Piece.BPAWN) {
|
||||
setSEEPiece(move.to + 8, Piece.WPAWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final void removeCastleRights(int square) {
|
||||
if (square == Position.getSquare(0, 0)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||
} else if (square == Position.getSquare(7, 0)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.H1_CASTLE));
|
||||
} else if (square == Position.getSquare(0, 7)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A8_CASTLE));
|
||||
} else if (square == Position.getSquare(7, 7)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.H8_CASTLE));
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------- Hashing code ------------------ */
|
||||
|
||||
static final long[][] psHashKeys; // [piece][square]
|
||||
private static final long whiteHashKey;
|
||||
private static final long[] castleHashKeys; // [castleMask]
|
||||
private static final long[] epHashKeys; // [epFile + 1] (epFile==-1 for no ep)
|
||||
private static final long[] moveCntKeys; // [min(halfMoveClock, 100)]
|
||||
|
||||
static {
|
||||
psHashKeys = new long[Piece.nPieceTypes][64];
|
||||
castleHashKeys = new long[16];
|
||||
epHashKeys = new long[9];
|
||||
moveCntKeys = new long[101];
|
||||
int rndNo = 0;
|
||||
for (int p = 0; p < Piece.nPieceTypes; p++) {
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
psHashKeys[p][sq] = getRandomHashVal(rndNo++);
|
||||
}
|
||||
}
|
||||
whiteHashKey = getRandomHashVal(rndNo++);
|
||||
for (int cm = 0; cm < castleHashKeys.length; cm++)
|
||||
castleHashKeys[cm] = getRandomHashVal(rndNo++);
|
||||
for (int f = 0; f < epHashKeys.length; f++)
|
||||
epHashKeys[f] = getRandomHashVal(rndNo++);
|
||||
for (int mc = 0; mc < moveCntKeys.length; mc++)
|
||||
moveCntKeys[mc] = getRandomHashVal(rndNo++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the Zobrist hash value non-incrementally. Only useful for test programs.
|
||||
*/
|
||||
final long computeZobristHash() {
|
||||
long hash = 0;
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
int p = squares[sq];
|
||||
hash ^= psHashKeys[p][sq];
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN))
|
||||
pHashKey ^= psHashKeys[p][sq];
|
||||
}
|
||||
if (whiteMove)
|
||||
hash ^= whiteHashKey;
|
||||
hash ^= castleHashKeys[castleMask];
|
||||
hash ^= epHashKeys[(epSquare >= 0) ? getX(epSquare) + 1 : 0];
|
||||
return hash;
|
||||
}
|
||||
|
||||
private final static long getRandomHashVal(int rndNo) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] input = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
input[i] = (byte)((rndNo >> (i * 8)) & 0xff);
|
||||
byte[] digest = md.digest(input);
|
||||
long ret = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ret ^= ((long)digest[i]) << (i * 8);
|
||||
}
|
||||
return ret;
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new UnsupportedOperationException("SHA-1 not available");
|
||||
}
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.asciiBoard(this) + (whiteMove ? "white\n" : "black\n") +
|
||||
Long.toHexString(zobristHash()) + "\n";
|
||||
}
|
||||
}
|
1220
CuckooChessEngine/src/chess/Search.java
Normal file
1220
CuckooChessEngine/src/chess/Search.java
Normal file
File diff suppressed because it is too large
Load Diff
612
CuckooChessEngine/src/chess/TextIO.java
Normal file
612
CuckooChessEngine/src/chess/TextIO.java
Normal file
|
@ -0,0 +1,612 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class TextIO {
|
||||
static public final String startPosFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/** Parse a FEN string and return a chess Position object. */
|
||||
public static final Position readFEN(String fen) throws ChessParseError {
|
||||
Position pos = new Position();
|
||||
String[] words = fen.split(" ");
|
||||
if (words.length < 2) {
|
||||
throw new ChessParseError("Too few spaces");
|
||||
}
|
||||
|
||||
// Piece placement
|
||||
int row = 7;
|
||||
int col = 0;
|
||||
for (int i = 0; i < words[0].length(); i++) {
|
||||
char c = words[0].charAt(i);
|
||||
switch (c) {
|
||||
case '1': col += 1; break;
|
||||
case '2': col += 2; break;
|
||||
case '3': col += 3; break;
|
||||
case '4': col += 4; break;
|
||||
case '5': col += 5; break;
|
||||
case '6': col += 6; break;
|
||||
case '7': col += 7; break;
|
||||
case '8': col += 8; break;
|
||||
case '/': row--; col = 0; break;
|
||||
case 'P': safeSetPiece(pos, col, row, Piece.WPAWN); col++; break;
|
||||
case 'N': safeSetPiece(pos, col, row, Piece.WKNIGHT); col++; break;
|
||||
case 'B': safeSetPiece(pos, col, row, Piece.WBISHOP); col++; break;
|
||||
case 'R': safeSetPiece(pos, col, row, Piece.WROOK); col++; break;
|
||||
case 'Q': safeSetPiece(pos, col, row, Piece.WQUEEN); col++; break;
|
||||
case 'K': safeSetPiece(pos, col, row, Piece.WKING); col++; break;
|
||||
case 'p': safeSetPiece(pos, col, row, Piece.BPAWN); col++; break;
|
||||
case 'n': safeSetPiece(pos, col, row, Piece.BKNIGHT); col++; break;
|
||||
case 'b': safeSetPiece(pos, col, row, Piece.BBISHOP); col++; break;
|
||||
case 'r': safeSetPiece(pos, col, row, Piece.BROOK); col++; break;
|
||||
case 'q': safeSetPiece(pos, col, row, Piece.BQUEEN); col++; break;
|
||||
case 'k': safeSetPiece(pos, col, row, Piece.BKING); col++; break;
|
||||
default: throw new ChessParseError("Invalid piece");
|
||||
}
|
||||
}
|
||||
if (words[1].length() == 0) {
|
||||
throw new ChessParseError("Invalid side");
|
||||
}
|
||||
pos.setWhiteMove(words[1].charAt(0) == 'w');
|
||||
|
||||
// Castling rights
|
||||
int castleMask = 0;
|
||||
if (words.length > 2) {
|
||||
for (int i = 0; i < words[2].length(); i++) {
|
||||
char c = words[2].charAt(i);
|
||||
switch (c) {
|
||||
case 'K':
|
||||
castleMask |= (1 << Position.H1_CASTLE);
|
||||
break;
|
||||
case 'Q':
|
||||
castleMask |= (1 << Position.A1_CASTLE);
|
||||
break;
|
||||
case 'k':
|
||||
castleMask |= (1 << Position.H8_CASTLE);
|
||||
break;
|
||||
case 'q':
|
||||
castleMask |= (1 << Position.A8_CASTLE);
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
throw new ChessParseError("Invalid castling flags");
|
||||
}
|
||||
}
|
||||
}
|
||||
pos.setCastleMask(castleMask);
|
||||
|
||||
if (words.length > 3) {
|
||||
// En passant target square
|
||||
String epString = words[3];
|
||||
if (!epString.equals("-")) {
|
||||
if (epString.length() < 2) {
|
||||
throw new ChessParseError("Invalid en passant square");
|
||||
}
|
||||
pos.setEpSquare(getSquare(epString));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (words.length > 4) {
|
||||
pos.halfMoveClock = Integer.parseInt(words[4]);
|
||||
}
|
||||
if (words.length > 5) {
|
||||
pos.fullMoveCounter = Integer.parseInt(words[5]);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// Ignore errors here, since the fields are optional
|
||||
}
|
||||
|
||||
// Each side must have exactly one king
|
||||
int wKings = 0;
|
||||
int bKings = 0;
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
if (p == Piece.WKING) {
|
||||
wKings++;
|
||||
} else if (p == Piece.BKING) {
|
||||
bKings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wKings != 1) {
|
||||
throw new ChessParseError("White must have exactly one king");
|
||||
}
|
||||
if (bKings != 1) {
|
||||
throw new ChessParseError("Black must have exactly one king");
|
||||
}
|
||||
|
||||
// Make sure king can not be captured
|
||||
Position pos2 = new Position(pos);
|
||||
pos2.setWhiteMove(!pos.whiteMove);
|
||||
if (MoveGen.inCheck(pos2)) {
|
||||
throw new ChessParseError("King capture possible");
|
||||
}
|
||||
|
||||
fixupEPSquare(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */
|
||||
public static final void fixupEPSquare(Position pos) {
|
||||
int epSquare = pos.getEpSquare();
|
||||
if (epSquare >= 0) {
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
boolean epValid = false;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
if (m.to == epSquare) {
|
||||
if (pos.getPiece(m.from) == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) {
|
||||
epValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!epValid) {
|
||||
pos.setEpSquare(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final void safeSetPiece(Position pos, int col, int row, int p) throws ChessParseError {
|
||||
if (row < 0) throw new ChessParseError("Too many rows");
|
||||
if (col > 7) throw new ChessParseError("Too many columns");
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN)) {
|
||||
if ((row == 0) || (row == 7))
|
||||
throw new ChessParseError("Pawn on first/last rank");
|
||||
}
|
||||
pos.setPiece(Position.getSquare(col, row), p);
|
||||
}
|
||||
|
||||
/** Return a FEN string corresponding to a chess Position object. */
|
||||
public static final String toFEN(Position pos) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
// Piece placement
|
||||
for (int r = 7; r >=0; r--) {
|
||||
int numEmpty = 0;
|
||||
for (int c = 0; c < 8; c++) {
|
||||
int p = pos.getPiece(Position.getSquare(c, r));
|
||||
if (p == Piece.EMPTY) {
|
||||
numEmpty++;
|
||||
} else {
|
||||
if (numEmpty > 0) {
|
||||
ret.append(numEmpty);
|
||||
numEmpty = 0;
|
||||
}
|
||||
switch (p) {
|
||||
case Piece.WKING: ret.append('K'); break;
|
||||
case Piece.WQUEEN: ret.append('Q'); break;
|
||||
case Piece.WROOK: ret.append('R'); break;
|
||||
case Piece.WBISHOP: ret.append('B'); break;
|
||||
case Piece.WKNIGHT: ret.append('N'); break;
|
||||
case Piece.WPAWN: ret.append('P'); break;
|
||||
case Piece.BKING: ret.append('k'); break;
|
||||
case Piece.BQUEEN: ret.append('q'); break;
|
||||
case Piece.BROOK: ret.append('r'); break;
|
||||
case Piece.BBISHOP: ret.append('b'); break;
|
||||
case Piece.BKNIGHT: ret.append('n'); break;
|
||||
case Piece.BPAWN: ret.append('p'); break;
|
||||
default: throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numEmpty > 0) {
|
||||
ret.append(numEmpty);
|
||||
}
|
||||
if (r > 0) {
|
||||
ret.append('/');
|
||||
}
|
||||
}
|
||||
ret.append(pos.whiteMove ? " w " : " b ");
|
||||
|
||||
// Castling rights
|
||||
boolean anyCastle = false;
|
||||
if (pos.h1Castle()) {
|
||||
ret.append('K');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.a1Castle()) {
|
||||
ret.append('Q');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.h8Castle()) {
|
||||
ret.append('k');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.a8Castle()) {
|
||||
ret.append('q');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (!anyCastle) {
|
||||
ret.append('-');
|
||||
}
|
||||
|
||||
// En passant target square
|
||||
{
|
||||
ret.append(' ');
|
||||
if (pos.getEpSquare() >= 0) {
|
||||
int x = Position.getX(pos.getEpSquare());
|
||||
int y = Position.getY(pos.getEpSquare());
|
||||
ret.append((char)(x + 'a'));
|
||||
ret.append((char)(y + '1'));
|
||||
} else {
|
||||
ret.append('-');
|
||||
}
|
||||
}
|
||||
|
||||
// Move counters
|
||||
ret.append(' ');
|
||||
ret.append(pos.halfMoveClock);
|
||||
ret.append(' ');
|
||||
ret.append(pos.fullMoveCounter);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move to human readable form.
|
||||
* @param pos The chess position.
|
||||
* @param move The executed move.
|
||||
* @param longForm If true, use long notation, eg Ng1-f3.
|
||||
* Otherwise, use short notation, eg Nf3
|
||||
*/
|
||||
public static final String moveToString(Position pos, Move move, boolean longForm) {
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
return moveToString(pos, move, longForm, moves);
|
||||
}
|
||||
private static final String moveToString(Position pos, Move move, boolean longForm, MoveGen.MoveList moves) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int wKingOrigPos = Position.getSquare(4, 0);
|
||||
int bKingOrigPos = Position.getSquare(4, 7);
|
||||
if (move.from == wKingOrigPos && pos.getPiece(wKingOrigPos) == Piece.WKING) {
|
||||
// Check white castle
|
||||
if (move.to == Position.getSquare(6, 0)) {
|
||||
ret.append("O-O");
|
||||
} else if (move.to == Position.getSquare(2, 0)) {
|
||||
ret.append("O-O-O");
|
||||
}
|
||||
} else if (move.from == bKingOrigPos && pos.getPiece(bKingOrigPos) == Piece.BKING) {
|
||||
// Check white castle
|
||||
if (move.to == Position.getSquare(6, 7)) {
|
||||
ret.append("O-O");
|
||||
} else if (move.to == Position.getSquare(2, 7)) {
|
||||
ret.append("O-O-O");
|
||||
}
|
||||
}
|
||||
if (ret.length() == 0) {
|
||||
int p = pos.getPiece(move.from);
|
||||
ret.append(pieceToChar(p));
|
||||
int x1 = Position.getX(move.from);
|
||||
int y1 = Position.getY(move.from);
|
||||
int x2 = Position.getX(move.to);
|
||||
int y2 = Position.getY(move.to);
|
||||
if (longForm) {
|
||||
ret.append((char)(x1 + 'a'));
|
||||
ret.append((char) (y1 + '1'));
|
||||
ret.append(isCapture(pos, move) ? 'x' : '-');
|
||||
} else {
|
||||
if (p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) {
|
||||
if (isCapture(pos, move)) {
|
||||
ret.append((char) (x1 + 'a'));
|
||||
}
|
||||
} else {
|
||||
int numSameTarget = 0;
|
||||
int numSameFile = 0;
|
||||
int numSameRow = 0;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
if (m == null)
|
||||
break;
|
||||
if ((pos.getPiece(m.from) == p) && (m.to == move.to)) {
|
||||
numSameTarget++;
|
||||
if (Position.getX(m.from) == x1)
|
||||
numSameFile++;
|
||||
if (Position.getY(m.from) == y1)
|
||||
numSameRow++;
|
||||
}
|
||||
}
|
||||
if (numSameTarget < 2) {
|
||||
// No file/row info needed
|
||||
} else if (numSameFile < 2) {
|
||||
ret.append((char) (x1 + 'a')); // Only file info needed
|
||||
} else if (numSameRow < 2) {
|
||||
ret.append((char) (y1 + '1')); // Only row info needed
|
||||
} else {
|
||||
ret.append((char) (x1 + 'a')); // File and row info needed
|
||||
ret.append((char) (y1 + '1'));
|
||||
}
|
||||
}
|
||||
if (isCapture(pos, move)) {
|
||||
ret.append('x');
|
||||
}
|
||||
}
|
||||
ret.append((char) (x2 + 'a'));
|
||||
ret.append((char) (y2 + '1'));
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
ret.append(pieceToChar(move.promoteTo));
|
||||
}
|
||||
}
|
||||
UndoInfo ui = new UndoInfo();
|
||||
if (MoveGen.givesCheck(pos, move)) {
|
||||
pos.makeMove(move, ui);
|
||||
MoveGen.MoveList nextMoves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, nextMoves);
|
||||
if (nextMoves.size == 0) {
|
||||
ret.append('#');
|
||||
} else {
|
||||
ret.append('+');
|
||||
}
|
||||
pos.unMakeMove(move, ui);
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/** Convert a move object to UCI string format. */
|
||||
public static final String moveToUCIString(Move m) {
|
||||
String ret = squareToString(m.from);
|
||||
ret += squareToString(m.to);
|
||||
switch (m.promoteTo) {
|
||||
case Piece.WQUEEN:
|
||||
case Piece.BQUEEN:
|
||||
ret += "q";
|
||||
break;
|
||||
case Piece.WROOK:
|
||||
case Piece.BROOK:
|
||||
ret += "r";
|
||||
break;
|
||||
case Piece.WBISHOP:
|
||||
case Piece.BBISHOP:
|
||||
ret += "b";
|
||||
break;
|
||||
case Piece.WKNIGHT:
|
||||
case Piece.BKNIGHT:
|
||||
ret += "n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to a Move object.
|
||||
* @return A move object, or null if move has invalid syntax
|
||||
*/
|
||||
public static final Move uciStringToMove(String move) {
|
||||
Move m = null;
|
||||
if ((move.length() < 4) || (move.length() > 5))
|
||||
return m;
|
||||
int fromSq = TextIO.getSquare(move.substring(0, 2));
|
||||
int toSq = TextIO.getSquare(move.substring(2, 4));
|
||||
if ((fromSq < 0) || (toSq < 0)) {
|
||||
return m;
|
||||
}
|
||||
char prom = ' ';
|
||||
boolean white = true;
|
||||
if (move.length() == 5) {
|
||||
prom = move.charAt(4);
|
||||
if (Position.getY(toSq) == 7) {
|
||||
white = true;
|
||||
} else if (Position.getY(toSq) == 0) {
|
||||
white = false;
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
int promoteTo;
|
||||
switch (prom) {
|
||||
case ' ':
|
||||
promoteTo = Piece.EMPTY;
|
||||
break;
|
||||
case 'q':
|
||||
promoteTo = white ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
break;
|
||||
case 'r':
|
||||
promoteTo = white ? Piece.WROOK : Piece.BROOK;
|
||||
break;
|
||||
case 'b':
|
||||
promoteTo = white ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
break;
|
||||
case 'n':
|
||||
promoteTo = white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
break;
|
||||
default:
|
||||
return m;
|
||||
}
|
||||
m = new Move(fromSq, toSq, promoteTo);
|
||||
return m;
|
||||
}
|
||||
|
||||
private static final boolean isCapture(Position pos, Move move) {
|
||||
if (pos.getPiece(move.to) == Piece.EMPTY) {
|
||||
int p = pos.getPiece(move.from);
|
||||
if ((p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) && (move.to == pos.getEpSquare())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move string to a Move object.
|
||||
* Any prefix of the string representation of a valid move counts as a legal move string,
|
||||
* as long as the string only matches one valid move.
|
||||
*/
|
||||
public static final Move stringToMove(Position pos, String strMove) {
|
||||
strMove = strMove.replaceAll("=", "");
|
||||
Move move = null;
|
||||
if (strMove.length() == 0)
|
||||
return move;
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
{
|
||||
char lastChar = strMove.charAt(strMove.length() - 1);
|
||||
if ((lastChar == '#') || (lastChar == '+')) {
|
||||
MoveGen.MoveList subMoves = new MoveGen.MoveList();
|
||||
int len = 0;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = TextIO.moveToString(pos, m, true, moves);
|
||||
if (str1.charAt(str1.length() - 1) == lastChar) {
|
||||
subMoves.m[len++] = m;
|
||||
}
|
||||
}
|
||||
subMoves.size = len;
|
||||
moves = subMoves;
|
||||
strMove = normalizeMoveString(strMove);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Search for full match
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = normalizeMoveString(TextIO.moveToString(pos, m, true, moves));
|
||||
String str2 = normalizeMoveString(TextIO.moveToString(pos, m, false, moves));
|
||||
if (i == 0) {
|
||||
if (strMove.equals(str1) || strMove.equals(str2)) {
|
||||
return m;
|
||||
}
|
||||
} else {
|
||||
if (strMove.toLowerCase().equals(str1.toLowerCase()) ||
|
||||
strMove.toLowerCase().equals(str2.toLowerCase())) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Search for unique substring match
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = normalizeMoveString(TextIO.moveToString(pos, m, true));
|
||||
String str2 = normalizeMoveString(TextIO.moveToString(pos, m, false));
|
||||
boolean match;
|
||||
if (i == 0) {
|
||||
match = (str1.startsWith(strMove) || str2.startsWith(strMove));
|
||||
} else {
|
||||
match = (str1.toLowerCase().startsWith(strMove.toLowerCase()) ||
|
||||
str2.toLowerCase().startsWith(strMove.toLowerCase()));
|
||||
}
|
||||
if (match) {
|
||||
if (move != null) {
|
||||
return null; // More than one match, not ok
|
||||
} else {
|
||||
move = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move != null)
|
||||
return move;
|
||||
}
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string, such as "e4" to a square number.
|
||||
* @return The square number, or -1 if not a legal square.
|
||||
*/
|
||||
public static final int getSquare(String s) {
|
||||
int x = s.charAt(0) - 'a';
|
||||
int y = s.charAt(1) - '1';
|
||||
if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
|
||||
return -1;
|
||||
return Position.getSquare(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a square number to a string, such as "e4".
|
||||
*/
|
||||
public static final String squareToString(int square) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int x = Position.getX(square);
|
||||
int y = Position.getY(square);
|
||||
ret.append((char) (x + 'a'));
|
||||
ret.append((char) (y + '1'));
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ascii representation of a position.
|
||||
*/
|
||||
public static final String asciiBoard(Position pos) {
|
||||
StringBuilder ret = new StringBuilder(400);
|
||||
String nl = String.format("%n");
|
||||
ret.append(" +----+----+----+----+----+----+----+----+"); ret.append(nl);
|
||||
for (int y = 7; y >= 0; y--) {
|
||||
ret.append(" |");
|
||||
for (int x = 0; x < 8; x++) {
|
||||
ret.append(' ');
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
if (p == Piece.EMPTY) {
|
||||
boolean dark = Position.darkSquare(x, y);
|
||||
ret.append(dark ? ".. |" : " |");
|
||||
} else {
|
||||
ret.append(Piece.isWhite(p) ? ' ' : '*');
|
||||
String pieceName = pieceToChar(p);
|
||||
if (pieceName.length() == 0)
|
||||
pieceName = "P";
|
||||
ret.append(pieceName);
|
||||
ret.append(" |");
|
||||
}
|
||||
}
|
||||
ret.append(nl);
|
||||
ret.append(" +----+----+----+----+----+----+----+----+");
|
||||
ret.append(nl);
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert move string to lower case and remove special check/mate symbols.
|
||||
*/
|
||||
private static final String normalizeMoveString(String str) {
|
||||
if (str.length() > 0) {
|
||||
char lastChar = str.charAt(str.length() - 1);
|
||||
if ((lastChar == '#') || (lastChar == '+')) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private final static String pieceToChar(int p) {
|
||||
switch (p) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN: return "Q";
|
||||
case Piece.WROOK: case Piece.BROOK: return "R";
|
||||
case Piece.WBISHOP: case Piece.BBISHOP: return "B";
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT: return "N";
|
||||
case Piece.WKING: case Piece.BKING: return "K";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
343
CuckooChessEngine/src/chess/TranspositionTable.java
Normal file
343
CuckooChessEngine/src/chess/TranspositionTable.java
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class TranspositionTable {
|
||||
static final public class TTEntry {
|
||||
long key; // Zobrist hash key
|
||||
private short move; // from + (to<<6) + (promote<<12)
|
||||
private short score; // Score from search
|
||||
private short depthSlot; // Search depth (bit 0-14) and hash slot (bit 15).
|
||||
byte generation; // Increase when OTB position changes
|
||||
public byte type; // exact score, lower bound, upper bound
|
||||
short evalScore; // Score from static evaluation
|
||||
// FIXME!!! Test storing both upper and lower bound in each hash entry.
|
||||
|
||||
static public final int T_EXACT = 0; // Exact score
|
||||
static public final int T_GE = 1; // True score >= this.score
|
||||
static public final int T_LE = 2; // True score <= this.score
|
||||
static public final int T_EMPTY = 3; // Empty hash slot
|
||||
|
||||
/** Return true if this object is more valuable than the other, false otherwise. */
|
||||
public final boolean betterThan(TTEntry other, int currGen) {
|
||||
if ((generation == currGen) != (other.generation == currGen)) {
|
||||
return generation == currGen; // Old entries are less valuable
|
||||
}
|
||||
if ((type == T_EXACT) != (other.type == T_EXACT)) {
|
||||
return type == T_EXACT; // Exact score more valuable than lower/upper bound
|
||||
}
|
||||
if (getDepth() != other.getDepth()) {
|
||||
return getDepth() > other.getDepth(); // Larger depth is more valuable
|
||||
}
|
||||
return false; // Otherwise, pretty much equally valuable
|
||||
}
|
||||
|
||||
/** Return true if entry is good enough to spend extra time trying to avoid overwriting it. */
|
||||
public final boolean valuable(int currGen) {
|
||||
if (generation != currGen)
|
||||
return false;
|
||||
return (type == T_EXACT) || (getDepth() > 3 * Search.plyScale);
|
||||
}
|
||||
|
||||
public final void getMove(Move m) {
|
||||
m.from = move & 63;
|
||||
m.to = (move >> 6) & 63;
|
||||
m.promoteTo = (move >> 12) & 15;
|
||||
}
|
||||
public final void setMove(Move move) {
|
||||
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". */
|
||||
public final int getScore(int ply) {
|
||||
int sc = score;
|
||||
if (sc > Search.MATE0 - 1000) {
|
||||
sc -= ply;
|
||||
} else if (sc < -(Search.MATE0 - 1000)) {
|
||||
sc += ply;
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
} else if (score < -(Search.MATE0 - 1000)) {
|
||||
score -= ply;
|
||||
}
|
||||
this.score = (short)score;
|
||||
}
|
||||
|
||||
/** Get depth from the hash entry. */
|
||||
public final int getDepth() {
|
||||
return depthSlot & 0x7fff;
|
||||
}
|
||||
|
||||
/** Set depth. */
|
||||
public final void setDepth(int d) {
|
||||
depthSlot &= 0x8000;
|
||||
depthSlot |= ((short)d) & 0x7fff;
|
||||
}
|
||||
|
||||
final int getHashSlot() {
|
||||
return depthSlot >>> 15;
|
||||
}
|
||||
|
||||
public final void setHashSlot(int s) {
|
||||
depthSlot &= 0x7fff;
|
||||
depthSlot |= (s << 15);
|
||||
}
|
||||
}
|
||||
TTEntry[] table;
|
||||
TTEntry emptySlot;
|
||||
byte generation;
|
||||
|
||||
/** Constructor. Creates an empty transposition table with numEntries slots. */
|
||||
public TranspositionTable(int log2Size) {
|
||||
final int numEntries = (1 << log2Size);
|
||||
table = new TTEntry[numEntries];
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
TTEntry ent = new TTEntry();
|
||||
ent.key = 0;
|
||||
ent.depthSlot = 0;
|
||||
ent.type = TTEntry.T_EMPTY;
|
||||
table[i] = ent;
|
||||
}
|
||||
emptySlot = new TTEntry();
|
||||
emptySlot.type = TTEntry.T_EMPTY;
|
||||
generation = 0;
|
||||
}
|
||||
|
||||
public final void insert(long key, Move sm, int type, int ply, int depth, int evalScore) {
|
||||
if (depth < 0) depth = 0;
|
||||
int idx0 = h0(key);
|
||||
int idx1 = h1(key);
|
||||
TTEntry ent = table[idx0];
|
||||
byte hashSlot = 0;
|
||||
if (ent.key != key) {
|
||||
ent = table[idx1];
|
||||
hashSlot = 1;
|
||||
}
|
||||
if (ent.key != key) {
|
||||
if (table[idx1].betterThan(table[idx0], generation)) {
|
||||
ent = table[idx0];
|
||||
hashSlot = 0;
|
||||
}
|
||||
if (ent.valuable(generation)) {
|
||||
int altEntIdx = (ent.getHashSlot() == 0) ? h1(ent.key) : h0(ent.key);
|
||||
if (ent.betterThan(table[altEntIdx], generation)) {
|
||||
TTEntry altEnt = table[altEntIdx];
|
||||
altEnt.key = ent.key;
|
||||
altEnt.move = ent.move;
|
||||
altEnt.score = ent.score;
|
||||
altEnt.depthSlot = ent.depthSlot;
|
||||
altEnt.generation = (byte)ent.generation;
|
||||
altEnt.type = ent.type;
|
||||
altEnt.setHashSlot(1 - ent.getHashSlot());
|
||||
altEnt.evalScore = ent.evalScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean doStore = true;
|
||||
if ((ent.key == key) && (ent.getDepth() > depth) && (ent.type == type)) {
|
||||
if (type == TTEntry.T_EXACT) {
|
||||
doStore = false;
|
||||
} else if ((type == TTEntry.T_GE) && (sm.score <= ent.score)) {
|
||||
doStore = false;
|
||||
} else if ((type == TTEntry.T_LE) && (sm.score >= ent.score)) {
|
||||
doStore = false;
|
||||
}
|
||||
}
|
||||
if (doStore) {
|
||||
if ((ent.key != key) || (sm.from != sm.to))
|
||||
ent.setMove(sm);
|
||||
ent.key = key;
|
||||
ent.setScore(sm.score, ply);
|
||||
ent.setDepth(depth);
|
||||
ent.generation = (byte)generation;
|
||||
ent.type = (byte)type;
|
||||
ent.setHashSlot(hashSlot);
|
||||
ent.evalScore = (short)evalScore;
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieve an entry from the hash table corresponding to "pos". */
|
||||
public final TTEntry probe(long key) {
|
||||
int idx0 = h0(key);
|
||||
TTEntry ent = table[idx0];
|
||||
if (ent.key == key) {
|
||||
ent.generation = (byte)generation;
|
||||
return ent;
|
||||
}
|
||||
int idx1 = h1(key);
|
||||
ent = table[idx1];
|
||||
if (ent.key == key) {
|
||||
ent.generation = (byte)generation;
|
||||
return ent;
|
||||
}
|
||||
return emptySlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase hash table generation. This means that subsequent inserts will be considered
|
||||
* more valuable than the entries currently present in the hash table.
|
||||
*/
|
||||
public final void nextGeneration() {
|
||||
generation++;
|
||||
}
|
||||
|
||||
/** Clear the transposition table. */
|
||||
public final void clear() {
|
||||
for (TTEntry ent : table) {
|
||||
ent.type = TTEntry.T_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a list of PV moves, starting from "rootPos" and first move "m".
|
||||
*/
|
||||
public final ArrayList<Move> extractPVMoves(Position rootPos, Move m) {
|
||||
Position pos = new Position(rootPos);
|
||||
m = new Move(m);
|
||||
ArrayList<Move> ret = new ArrayList<Move>();
|
||||
UndoInfo ui = new UndoInfo();
|
||||
List<Long> hashHistory = new ArrayList<Long>();
|
||||
MoveGen moveGen = new MoveGen();
|
||||
while (true) {
|
||||
ret.add(m);
|
||||
pos.makeMove(m, ui);
|
||||
if (hashHistory.contains(pos.zobristHash())) {
|
||||
break;
|
||||
}
|
||||
hashHistory.add(pos.zobristHash());
|
||||
TTEntry ent = probe(pos.historyHash());
|
||||
if (ent.type == TTEntry.T_EMPTY) {
|
||||
break;
|
||||
}
|
||||
m = new Move(0,0,0);
|
||||
ent.getMove(m);
|
||||
MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
boolean contains = false;
|
||||
for (int mi = 0; mi < moves.size; mi++)
|
||||
if (moves.m[mi].equals(m)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
if (!contains)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Extract the PV starting from pos, using hash entries, both exact scores and bounds. */
|
||||
public final String extractPV(Position pos) {
|
||||
StringBuilder ret = new StringBuilder(100);
|
||||
pos = new Position(pos); // To avoid modifying the input parameter
|
||||
boolean first = true;
|
||||
TTEntry ent = probe(pos.historyHash());
|
||||
UndoInfo ui = new UndoInfo();
|
||||
ArrayList<Long> hashHistory = new ArrayList<Long>();
|
||||
boolean repetition = false;
|
||||
MoveGen moveGen = MoveGen.instance;
|
||||
while (ent.type != TTEntry.T_EMPTY) {
|
||||
String type = "";
|
||||
if (ent.type == TTEntry.T_LE) {
|
||||
type = "<";
|
||||
} else if (ent.type == TTEntry.T_GE) {
|
||||
type = ">";
|
||||
}
|
||||
Move m = new Move(0,0,0);
|
||||
ent.getMove(m);
|
||||
MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
boolean contains = false;
|
||||
for (int mi = 0; mi < moves.size; mi++)
|
||||
if (moves.m[mi].equals(m)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
if (!contains)
|
||||
break;
|
||||
String moveStr = TextIO.moveToString(pos, m, false);
|
||||
if (repetition)
|
||||
break;
|
||||
if (!first) {
|
||||
ret.append(" ");
|
||||
}
|
||||
ret.append(type);
|
||||
ret.append(moveStr);
|
||||
pos.makeMove(m, ui);
|
||||
if (hashHistory.contains(pos.zobristHash())) {
|
||||
repetition = true;
|
||||
}
|
||||
hashHistory.add(pos.zobristHash());
|
||||
ent = probe(pos.historyHash());
|
||||
first = false;
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/** Print hash table statistics. */
|
||||
public final void printStats() {
|
||||
int unused = 0;
|
||||
int thisGen = 0;
|
||||
List<Integer> depHist = new ArrayList<Integer>();
|
||||
final int maxDepth = 20*8;
|
||||
for (int i = 0; i < maxDepth; i++) {
|
||||
depHist.add(0);
|
||||
}
|
||||
for (TTEntry ent : table) {
|
||||
if (ent.type == TTEntry.T_EMPTY) {
|
||||
unused++;
|
||||
} else {
|
||||
if (ent.generation == generation) {
|
||||
thisGen++;
|
||||
}
|
||||
if (ent.getDepth() < maxDepth) {
|
||||
depHist.set(ent.getDepth(), depHist.get(ent.getDepth()) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
double w = 100.0 / table.length;
|
||||
System.out.printf("Hash stats: size:%d unused:%d (%.2f%%) thisGen:%d (%.2f%%)\n",
|
||||
table.length, unused, unused*w, thisGen, thisGen*w);
|
||||
for (int i = 0; i < maxDepth; i++) {
|
||||
int c = depHist.get(i);
|
||||
if (c > 0)
|
||||
System.out.printf("%3d %8d (%6.2f%%)\n", i, c, c*w);
|
||||
}
|
||||
}
|
||||
|
||||
private final int h0(long key) {
|
||||
return (int)(key & (table.length - 1));
|
||||
}
|
||||
|
||||
private final int h1(long key) {
|
||||
return (int)((key >> 32) & (table.length - 1));
|
||||
}
|
||||
}
|
617
CuckooChessEngine/src/chess/TreeLogger.java
Normal file
617
CuckooChessEngine/src/chess/TreeLogger.java
Normal file
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import chess.TranspositionTable.TTEntry;
|
||||
|
||||
public final class TreeLogger {
|
||||
private byte[] entryBuffer = new byte[16];
|
||||
private ByteBuffer bb = ByteBuffer.wrap(entryBuffer);
|
||||
|
||||
// Used in write mode
|
||||
private FileOutputStream os = null;
|
||||
private BufferedOutputStream bos = null;
|
||||
private long nextIndex = 0;
|
||||
|
||||
// Used in analyze mode
|
||||
private MappedByteBuffer mapBuf = null;
|
||||
private FileChannel fc = null;
|
||||
private int numEntries = 0;
|
||||
|
||||
private TreeLogger() {
|
||||
}
|
||||
|
||||
/** Get a logger object set up for writing to a log file. */
|
||||
public static final TreeLogger getWriter(String filename, Position pos) {
|
||||
try {
|
||||
TreeLogger log = new TreeLogger();
|
||||
log.os = new FileOutputStream(filename);
|
||||
log.bos = new BufferedOutputStream(log.os, 65536);
|
||||
log.writeHeader(pos);
|
||||
log.nextIndex = 0;
|
||||
return log;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private final void writeHeader(Position pos) {
|
||||
try {
|
||||
byte[] fen = TextIO.toFEN(pos).getBytes();
|
||||
bos.write((byte)(fen.length));
|
||||
bos.write(fen);
|
||||
byte[] pad = new byte[128-1-fen.length];
|
||||
for (int i = 0; i < pad.length; i++)
|
||||
pad[i] = 0;
|
||||
bos.write(pad);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get a logger object set up for analyzing a log file. */
|
||||
public static final TreeLogger getAnalyzer(String filename) {
|
||||
try {
|
||||
TreeLogger log = new TreeLogger();
|
||||
RandomAccessFile raf;
|
||||
raf = new RandomAccessFile(filename, "rw");
|
||||
log.fc = raf.getChannel();
|
||||
long len = raf.length();
|
||||
log.numEntries = (int) ((len - 128) / 16);
|
||||
log.mapBuf = log.fc.map(MapMode.READ_WRITE, 0, len);
|
||||
log.computeForwardPointers();
|
||||
return log;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public final void close() {
|
||||
try {
|
||||
if (bos != null) bos.close();
|
||||
if (fc != null) fc.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the on-disk format. Big-endian byte-order is used.
|
||||
* First there is one header entry. Then there is a set of start/end entries.
|
||||
* A StartEntry can be identified by its first 4 bytes (endIndex/startIndex)
|
||||
* being either -1 (endIndex not computed), or > the entry index.
|
||||
*
|
||||
* private static final class Header {
|
||||
* byte fenLen; // Used length of fen array
|
||||
* byte[] fen; // 126 bytes, 0-padded
|
||||
* byte flags; // bit 0: 1 if endIndex has been computed for all StartEntries.
|
||||
* }
|
||||
*
|
||||
* private static final class StartEntry {
|
||||
* int endIndex;
|
||||
* int parentIndex; // -1 for root node
|
||||
* short move;
|
||||
* short alpha;
|
||||
* short beta;
|
||||
* byte ply;
|
||||
* byte depth;
|
||||
* }
|
||||
*
|
||||
* private static final class EndEntry {
|
||||
* int startIndex;
|
||||
* short score;
|
||||
* short scoreType;
|
||||
* short evalScore;
|
||||
* byte[] hashKey; // lower 6 bytes of position hash key
|
||||
* }
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Functions used for tree logging
|
||||
|
||||
/**
|
||||
* Log information when entering a search node.
|
||||
* @param parentId Index of parent node.
|
||||
* @param m Move made to go from parent node to this node
|
||||
* @param alpha Search parameter
|
||||
* @param beta Search parameter
|
||||
* @param ply Search parameter
|
||||
* @param depth Search parameter
|
||||
* @return node index
|
||||
*/
|
||||
final long logNodeStart(long parentIndex, Move m, int alpha, int beta, int ply, int depth) {
|
||||
bb.putInt ( 0, (int)-1);
|
||||
bb.putInt ( 4, (int)parentIndex);
|
||||
bb.putShort( 8, (short)(m.from + (m.to << 6) + (m.promoteTo << 12)));
|
||||
bb.putShort(10, (short)alpha);
|
||||
bb.putShort(12, (short)beta);
|
||||
bb.put (14, (byte)ply);
|
||||
bb.put (15, (byte)depth);
|
||||
try {
|
||||
bos.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return nextIndex++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startIndex Pointer to corresponding start node entry.
|
||||
* @param score Computed score for this node.
|
||||
* @param scoreType See TranspositionTable, T_EXACT, T_GE, T_LE.
|
||||
* @param evalScore Score returned by evaluation function at this node, if known.
|
||||
* @return node index
|
||||
*/
|
||||
final long logNodeEnd(long startIndex, int score, int scoreType, int evalScore, long hashKey) {
|
||||
bb.putInt ( 0, (int)startIndex);
|
||||
bb.putShort( 4, (short)score);
|
||||
bb.putShort( 6, (short)scoreType);
|
||||
bb.putLong( 8, hashKey);
|
||||
bb.putShort( 8, (short)evalScore); // Overwrites first two byte of hashKey
|
||||
try {
|
||||
bos.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return nextIndex++;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Functions used for tree analyzing
|
||||
|
||||
private static final int indexToFileOffs(int index) {
|
||||
return 128 + index * 16;
|
||||
}
|
||||
|
||||
/** Compute endIndex for all StartNode entries. */
|
||||
private final void computeForwardPointers() {
|
||||
if ((mapBuf.get(127) & (1<<7)) != 0)
|
||||
return;
|
||||
System.out.printf("Computing forward pointers...\n");
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
boolean isStart = readEntry(i, se, ee);
|
||||
if (!isStart) {
|
||||
int offs = indexToFileOffs(ee.startIndex);
|
||||
mapBuf.putInt(offs, i);
|
||||
}
|
||||
}
|
||||
mapBuf.put(127, (byte)(1 << 7));
|
||||
mapBuf.force();
|
||||
System.out.printf("Computing forward pointers... done\n");
|
||||
}
|
||||
|
||||
/** Get FEN string for root node position. */
|
||||
private final String getRootNodeFEN() {
|
||||
int len = mapBuf.get(0);
|
||||
byte[] fenB = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
fenB[i] = mapBuf.get(1+i);
|
||||
String ret = new String(fenB);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static final class StartEntry {
|
||||
int endIndex;
|
||||
int parentIndex; // -1 for root node
|
||||
Move move;
|
||||
short alpha;
|
||||
short beta;
|
||||
byte ply;
|
||||
byte depth;
|
||||
}
|
||||
static final class EndEntry {
|
||||
int startIndex;
|
||||
short score;
|
||||
short scoreType;
|
||||
short evalScore;
|
||||
long hashKey; // Note! Upper 2 bytes are not valid (ie 0)
|
||||
}
|
||||
|
||||
/** Read a start/end entry.
|
||||
* @return True if entry was a start entry, false if it was an end entry. */
|
||||
private final boolean readEntry(int index, StartEntry se, EndEntry ee) {
|
||||
int offs = indexToFileOffs(index);
|
||||
for (int i = 0; i < 16; i++)
|
||||
bb.put(i, mapBuf.get(offs + i));
|
||||
int otherIndex = bb.getInt(0);
|
||||
boolean isStartEntry = (otherIndex == -1) || (otherIndex > index);
|
||||
if (isStartEntry) {
|
||||
se.endIndex = otherIndex;
|
||||
se.parentIndex = bb.getInt(4);
|
||||
int m = bb.getShort(8);
|
||||
se.move = new Move(m & 63, (m >> 6) & 63, (m >> 12) & 15);
|
||||
se.alpha = bb.getShort(10);
|
||||
se.beta = bb.getShort(12);
|
||||
se.ply = bb.get(14);
|
||||
se.depth = bb.get(15);
|
||||
} else {
|
||||
ee.startIndex = otherIndex;
|
||||
ee.score = bb.getShort(4);
|
||||
ee.scoreType = bb.getShort(6);
|
||||
ee.evalScore = bb.getShort(8);
|
||||
ee.hashKey = bb.getLong(8) & 0x0000ffffffffffffL;
|
||||
}
|
||||
return isStartEntry;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Functions used for the interactive tree browser
|
||||
|
||||
public static final void main(String[] args) throws IOException {
|
||||
if (args.length != 1) {
|
||||
System.out.printf("Usage: progname filename\n");
|
||||
System.exit(1);
|
||||
}
|
||||
TreeLogger an = getAnalyzer(args[0]);
|
||||
try {
|
||||
Position rootPos = TextIO.readFEN(an.getRootNodeFEN());
|
||||
an.mainLoop(rootPos);
|
||||
} catch (ChessParseError e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
an.close();
|
||||
}
|
||||
|
||||
private final void mainLoop(Position rootPos) throws IOException {
|
||||
int currIndex = -1;
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||
String prevStr = "";
|
||||
|
||||
boolean doPrint = true;
|
||||
while (true) {
|
||||
if (doPrint) {
|
||||
ArrayList<Move> moves = getMoveSequence(currIndex);
|
||||
for (Move m : moves)
|
||||
System.out.printf(" %s", TextIO.moveToUCIString(m));
|
||||
System.out.printf("\n");
|
||||
printNodeInfo(rootPos, currIndex);
|
||||
Position pos = getPosition(rootPos, currIndex);
|
||||
System.out.print(TextIO.asciiBoard(pos));
|
||||
System.out.printf("%s\n", TextIO.toFEN(pos));
|
||||
System.out.printf("%16x\n", pos.historyHash());
|
||||
if (currIndex >= 0) {
|
||||
ArrayList<Integer> children = findChildren(currIndex);
|
||||
for (Integer c : children)
|
||||
printNodeInfo(rootPos, c);
|
||||
}
|
||||
}
|
||||
doPrint = true;
|
||||
System.out.printf("Command:");
|
||||
String cmdStr = in.readLine();
|
||||
if (cmdStr == null)
|
||||
return;
|
||||
if (cmdStr.length() == 0)
|
||||
cmdStr = prevStr;
|
||||
if (cmdStr.startsWith("q")) {
|
||||
return;
|
||||
} else if (cmdStr.startsWith("?")) {
|
||||
printHelp();
|
||||
doPrint = false;
|
||||
} else if (isMove(cmdStr)) {
|
||||
ArrayList<Integer> children = findChildren(currIndex);
|
||||
String m = cmdStr;
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
ArrayList<Integer> found = new ArrayList<Integer>();
|
||||
for (Integer c : children) {
|
||||
readEntries(c, se, ee);
|
||||
if (TextIO.moveToUCIString(se.move).equals(m))
|
||||
found.add(c);
|
||||
}
|
||||
if (found.size() == 0) {
|
||||
System.out.printf("No such move\n");
|
||||
doPrint = false;
|
||||
} else if (found.size() > 1) {
|
||||
System.out.printf("Ambiguous move\n");
|
||||
for (Integer c : found)
|
||||
printNodeInfo(rootPos, c);
|
||||
doPrint = false;
|
||||
} else {
|
||||
currIndex = found.get(0);
|
||||
}
|
||||
} else if (cmdStr.startsWith("u")) {
|
||||
int n = getArg(cmdStr, 1);
|
||||
for (int i = 0; i < n; i++)
|
||||
currIndex = findParent(currIndex);
|
||||
} else if (cmdStr.startsWith("l")) {
|
||||
ArrayList<Integer> children = findChildren(currIndex);
|
||||
String m = getArgStr(cmdStr, "");
|
||||
for (Integer c : children)
|
||||
printNodeInfo(rootPos, c, m);
|
||||
doPrint = false;
|
||||
} else if (cmdStr.startsWith("n")) {
|
||||
ArrayList<Integer> nodes = getNodeSequence(currIndex);
|
||||
for (int node : nodes)
|
||||
printNodeInfo(rootPos, node);
|
||||
doPrint = false;
|
||||
} else if (cmdStr.startsWith("d")) {
|
||||
ArrayList<Integer> nVec = getArgs(cmdStr, 0);
|
||||
for (int n : nVec) {
|
||||
ArrayList<Integer> children = findChildren(currIndex);
|
||||
if ((n >= 0) && (n < children.size())) {
|
||||
currIndex = children.get(n);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
} else if (cmdStr.startsWith("p")) {
|
||||
ArrayList<Move> moves = getMoveSequence(currIndex);
|
||||
for (Move m : moves)
|
||||
System.out.printf(" %s", TextIO.moveToUCIString(m));
|
||||
System.out.printf("\n");
|
||||
doPrint = false;
|
||||
} else if (cmdStr.startsWith("h")) {
|
||||
long hashKey = getPosition(rootPos, currIndex).historyHash();
|
||||
hashKey = getHashKey(cmdStr, hashKey);
|
||||
ArrayList<Integer> nodes = getNodeForHashKey(hashKey);
|
||||
for (int node : nodes)
|
||||
printNodeInfo(rootPos, node);
|
||||
doPrint = false;
|
||||
} else {
|
||||
try {
|
||||
int i = Integer.parseInt(cmdStr);
|
||||
if ((i >= -1) && (i < numEntries))
|
||||
currIndex = i;
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
prevStr = cmdStr;
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean isMove(String cmdStr) {
|
||||
if (cmdStr.length() != 4)
|
||||
return false;
|
||||
cmdStr = cmdStr.toLowerCase();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int c = cmdStr.charAt(i);
|
||||
if ((i == 0) || (i == 2)) {
|
||||
if ((c < 'a') || (c > 'h'))
|
||||
return false;
|
||||
} else {
|
||||
if ((c < '1') || (c > '8'))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return all nodes with a given hash key. */
|
||||
private final ArrayList<Integer> getNodeForHashKey(long hashKey) {
|
||||
hashKey &= 0x0000ffffffffffffL;
|
||||
ArrayList<Integer> ret = new ArrayList<Integer>();
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
for (int index = 0; index < numEntries; index++) {
|
||||
boolean isStart = readEntry(index, se, ee);
|
||||
if (!isStart) {
|
||||
if (ee.hashKey == hashKey) {
|
||||
int sIdx = ee.startIndex;
|
||||
ret.add(sIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get hash key from an input string. */
|
||||
private final long getHashKey(String s, long defKey) {
|
||||
long key = defKey;
|
||||
int idx = s.indexOf(' ');
|
||||
if (idx > 0) {
|
||||
s = s.substring(idx + 1);
|
||||
if (s.startsWith("0x"))
|
||||
s = s.substring(2);
|
||||
try {
|
||||
key = Long.parseLong(s, 16);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/** Get integer parameter from an input string. */
|
||||
private static final int getArg(String s, int defVal) {
|
||||
try {
|
||||
int idx = s.indexOf(' ');
|
||||
if (idx > 0) {
|
||||
return Integer.parseInt(s.substring(idx+1));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return defVal;
|
||||
}
|
||||
|
||||
/** Get a list of integer parameters from an input string. */
|
||||
final ArrayList<Integer> getArgs(String s, int defVal) {
|
||||
ArrayList<Integer> ret = new ArrayList<Integer>();
|
||||
String[] split = s.split(" ");
|
||||
try {
|
||||
for (int i = 1; i < split.length; i++)
|
||||
ret.add(Integer.parseInt(split[i]));
|
||||
} catch (NumberFormatException e) {
|
||||
ret.clear();
|
||||
}
|
||||
if (ret.size() == 0)
|
||||
ret.add(defVal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get a string parameter from an input string. */
|
||||
private static final String getArgStr(String s, String defVal) {
|
||||
int idx = s.indexOf(' ');
|
||||
if (idx > 0)
|
||||
return s.substring(idx+1);
|
||||
return defVal;
|
||||
}
|
||||
|
||||
private final void printHelp() {
|
||||
System.out.printf(" p - Print move sequence\n");
|
||||
System.out.printf(" n - Print node info corresponding to move sequence\n");
|
||||
System.out.printf(" l [move] - List child nodes, optionally only for one move\n");
|
||||
System.out.printf(" d [n1 [n2...]] - Go to child \"n\"\n");
|
||||
System.out.printf(" move - Go to child \"move\", if unique\n");
|
||||
System.out.printf(" u [levels] - Move up\n");
|
||||
System.out.printf(" h [key] - Find nodes with current (or given) hash key\n");
|
||||
System.out.printf(" num - Go to node \"num\"\n");
|
||||
System.out.printf(" q - Quit\n");
|
||||
System.out.printf(" ? - Print this help\n");
|
||||
}
|
||||
|
||||
/** Read start/end entries for a tree node. Return true if the end entry exists. */
|
||||
private final boolean readEntries(int index, StartEntry se, EndEntry ee) {
|
||||
boolean isStart = readEntry(index, se, ee);
|
||||
if (isStart) {
|
||||
int eIdx = se.endIndex;
|
||||
if (eIdx >= 0) {
|
||||
readEntry(eIdx, null, ee);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int sIdx = ee.startIndex;
|
||||
readEntry(sIdx, se, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Find the parent node to a node. */
|
||||
private final int findParent(int index) {
|
||||
if (index >= 0) {
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
readEntries(index, se, ee);
|
||||
index = se.parentIndex;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Find all children of a node. */
|
||||
private final ArrayList<Integer> findChildren(int index) {
|
||||
ArrayList<Integer> ret = new ArrayList<Integer>();
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
int child = index + 1;
|
||||
while ((child >= 0) && (child < numEntries)) {
|
||||
boolean haveEE = readEntries(child, se, ee);
|
||||
if (se.parentIndex == index)
|
||||
ret.add(child);
|
||||
if (!haveEE)
|
||||
break;
|
||||
if (child != ee.startIndex)
|
||||
break; // two end entries in a row, no more children
|
||||
// if (se.parentIndex != index)
|
||||
// break;
|
||||
child = se.endIndex + 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get node position in parents children list. */
|
||||
private final int getChildNo(int index) {
|
||||
ArrayList<Integer> childs = findChildren(findParent(index));
|
||||
for (int i = 0; i < childs.size(); i++)
|
||||
if (childs.get(i) == index)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Get list of nodes from root position to a node. */
|
||||
private final ArrayList<Integer> getNodeSequence(int index) {
|
||||
ArrayList<Integer> nodes = new ArrayList<Integer>();
|
||||
nodes.add(index);
|
||||
while (index >= 0) {
|
||||
index = findParent(index);
|
||||
nodes.add(index);
|
||||
}
|
||||
Collections.reverse(nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/** Find list of moves from root node to a node. */
|
||||
private final ArrayList<Move> getMoveSequence(int index) {
|
||||
ArrayList<Move> moves = new ArrayList<Move>();
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
while (index >= 0) {
|
||||
readEntries(index, se, ee);
|
||||
moves.add(se.move);
|
||||
index = findParent(index);
|
||||
}
|
||||
Collections.reverse(moves);
|
||||
return moves;
|
||||
}
|
||||
|
||||
/** Find the position corresponding to a node. */
|
||||
private final Position getPosition(Position rootPos, int index) {
|
||||
ArrayList<Move> moves = getMoveSequence(index);
|
||||
Position ret = new Position(rootPos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (Move m : moves)
|
||||
ret.makeMove(m, ui);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private final void printNodeInfo(Position rootPos, int index) {
|
||||
printNodeInfo(rootPos, index, "");
|
||||
}
|
||||
private final void printNodeInfo(Position rootPos, int index, String filterMove) {
|
||||
if (index < 0) { // Root node
|
||||
System.out.printf("%8d entries:%d\n", index, numEntries);
|
||||
} else {
|
||||
StartEntry se = new StartEntry();
|
||||
EndEntry ee = new EndEntry();
|
||||
boolean haveEE = readEntries(index, se, ee);
|
||||
String m = TextIO.moveToUCIString(se.move);
|
||||
if ((filterMove.length() > 0) && !m.equals(filterMove))
|
||||
return;
|
||||
System.out.printf("%3d %8d %s a:%6d b:%6d p:%2d d:%2d", getChildNo(index), index,
|
||||
m, se.alpha, se.beta, se.ply, se.depth);
|
||||
if (haveEE) {
|
||||
int subTreeNodes = (se.endIndex - ee.startIndex - 1) / 2;
|
||||
String type;
|
||||
switch (ee.scoreType) {
|
||||
case TTEntry.T_EXACT: type = "= "; break;
|
||||
case TTEntry.T_GE : type = ">="; break;
|
||||
case TTEntry.T_LE : type = "<="; break;
|
||||
default : type = " "; break;
|
||||
}
|
||||
System.out.printf(" s:%s%6d e:%6d sub:%d", type, ee.score, ee.evalScore,
|
||||
subTreeNodes);
|
||||
}
|
||||
System.out.printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
33
CuckooChessEngine/src/chess/TwoReturnValues.java
Normal file
33
CuckooChessEngine/src/chess/TwoReturnValues.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* A small helper class that makes it possible to return two values from a function.
|
||||
* @author petero
|
||||
*/
|
||||
public final class TwoReturnValues<T1, T2> {
|
||||
public final T1 first;
|
||||
public final T2 second;
|
||||
|
||||
TwoReturnValues(T1 first, T2 second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
31
CuckooChessEngine/src/chess/UndoInfo.java
Normal file
31
CuckooChessEngine/src/chess/UndoInfo.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
/**
|
||||
* Contains enough information to undo a previous move.
|
||||
* Set by makeMove(). Used by unMakeMove().
|
||||
* @author petero
|
||||
*/
|
||||
public class UndoInfo {
|
||||
int capturedPiece;
|
||||
int castleMask;
|
||||
int epSquare;
|
||||
int halfMoveClock;
|
||||
}
|
549
CuckooChessEngine/src/guibase/ChessController.java
Normal file
549
CuckooChessEngine/src/guibase/ChessController.java
Normal file
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package guibase;
|
||||
|
||||
import chess.ChessParseError;
|
||||
import chess.ComputerPlayer;
|
||||
import chess.Game;
|
||||
import chess.HumanPlayer;
|
||||
import chess.Move;
|
||||
import chess.MoveGen;
|
||||
import chess.Piece;
|
||||
import chess.Player;
|
||||
import chess.Position;
|
||||
import chess.Search;
|
||||
import chess.TextIO;
|
||||
import chess.UndoInfo;
|
||||
import chess.Game.GameState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* The glue between the chess engine and the GUI.
|
||||
* @author petero
|
||||
*/
|
||||
public class ChessController {
|
||||
Player humanPlayer;
|
||||
ComputerPlayer computerPlayer;
|
||||
Game game;
|
||||
GUIInterface gui;
|
||||
boolean humanIsWhite;
|
||||
Thread computerThread;
|
||||
int threadStack; // Thread stack size, or zero to use OS default
|
||||
|
||||
// Search statistics
|
||||
String thinkingPV;
|
||||
|
||||
class SearchListener implements Search.Listener {
|
||||
int currDepth = 0;
|
||||
int currMoveNr = 0;
|
||||
String currMove = "";
|
||||
int currNodes = 0;
|
||||
int currNps = 0;
|
||||
int currTime = 0;
|
||||
|
||||
int pvDepth = 0;
|
||||
int pvScore = 0;
|
||||
boolean pvIsMate = false;
|
||||
boolean pvUpperBound = false;
|
||||
boolean pvLowerBound = false;
|
||||
String pvStr = "";
|
||||
|
||||
private void setSearchInfo() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(String.format("%n[%d] ", pvDepth));
|
||||
if (pvUpperBound) {
|
||||
buf.append("<=");
|
||||
} else if (pvLowerBound) {
|
||||
buf.append(">=");
|
||||
}
|
||||
if (pvIsMate) {
|
||||
buf.append(String.format("m%d", pvScore));
|
||||
} else {
|
||||
buf.append(String.format("%.2f", pvScore / 100.0));
|
||||
}
|
||||
buf.append(pvStr);
|
||||
buf.append(String.format("%n"));
|
||||
buf.append(String.format("d:%d %d:%s t:%.2f n:%d nps:%d", currDepth,
|
||||
currMoveNr, currMove, currTime / 1000.0, currNodes, currNps));
|
||||
final String newPV = buf.toString();
|
||||
gui.runOnUIThread(new Runnable() {
|
||||
public void run() {
|
||||
thinkingPV = newPV;
|
||||
setThinkingPV();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyDepth(int depth) {
|
||||
currDepth = depth;
|
||||
setSearchInfo();
|
||||
}
|
||||
|
||||
public void notifyCurrMove(Move m, int moveNr) {
|
||||
currMove = TextIO.moveToString(new Position(game.pos), m, false);
|
||||
currMoveNr = moveNr;
|
||||
setSearchInfo();
|
||||
}
|
||||
|
||||
public void notifyPV(int depth, int score, int time, int nodes, int nps, boolean isMate,
|
||||
boolean upperBound, boolean lowerBound, ArrayList<Move> pv) {
|
||||
pvDepth = depth;
|
||||
pvScore = score;
|
||||
currTime = time;
|
||||
currNodes = nodes;
|
||||
currNps = nps;
|
||||
pvIsMate = isMate;
|
||||
pvUpperBound = upperBound;
|
||||
pvLowerBound = lowerBound;
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Position pos = new Position(game.pos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (Move m : pv) {
|
||||
buf.append(String.format(" %s", TextIO.moveToString(pos, m, false)));
|
||||
pos.makeMove(m, ui);
|
||||
}
|
||||
pvStr = buf.toString();
|
||||
setSearchInfo();
|
||||
}
|
||||
|
||||
public void notifyStats(int nodes, int nps, int time) {
|
||||
currNodes = nodes;
|
||||
currNps = nps;
|
||||
currTime = time;
|
||||
setSearchInfo();
|
||||
}
|
||||
}
|
||||
SearchListener listener;
|
||||
|
||||
public ChessController(GUIInterface gui) {
|
||||
this.gui = gui;
|
||||
listener = new SearchListener();
|
||||
thinkingPV = "";
|
||||
threadStack = 0;
|
||||
}
|
||||
|
||||
public void setThreadStackSize(int size) {
|
||||
threadStack = size;
|
||||
}
|
||||
|
||||
public final void newGame(boolean humanIsWhite, int ttLogSize, boolean verbose) {
|
||||
stopComputerThinking();
|
||||
this.humanIsWhite = humanIsWhite;
|
||||
humanPlayer = new HumanPlayer();
|
||||
computerPlayer = new ComputerPlayer();
|
||||
computerPlayer.verbose = verbose;
|
||||
computerPlayer.setTTLogSize(ttLogSize);
|
||||
computerPlayer.setListener(listener);
|
||||
if (humanIsWhite) {
|
||||
game = new Game(humanPlayer, computerPlayer);
|
||||
} else {
|
||||
game = new Game(computerPlayer, humanPlayer);
|
||||
}
|
||||
}
|
||||
public final void startGame() {
|
||||
gui.setSelection(-1);
|
||||
updateGUI();
|
||||
startComputerThinking();
|
||||
}
|
||||
|
||||
public final void setPosHistory(List<String> posHistStr) {
|
||||
try {
|
||||
String fen = posHistStr.get(0);
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
game.processString("new");
|
||||
game.pos = pos;
|
||||
String[] strArr = posHistStr.get(1).split(" ");
|
||||
final int arrLen = strArr.length;
|
||||
for (int i = 0; i < arrLen; i++) {
|
||||
game.processString(strArr[i]);
|
||||
}
|
||||
int numUndo = Integer.parseInt(posHistStr.get(2));
|
||||
for (int i = 0; i < numUndo; i++) {
|
||||
game.processString("undo");
|
||||
}
|
||||
} catch (ChessParseError e) {
|
||||
// Just ignore invalid positions
|
||||
}
|
||||
}
|
||||
|
||||
public final List<String> getPosHistory() {
|
||||
return game.getPosHistory();
|
||||
}
|
||||
|
||||
public String getFEN() {
|
||||
return TextIO.toFEN(game.pos);
|
||||
}
|
||||
|
||||
/** Convert current game to PGN format. */
|
||||
public String getPGN() {
|
||||
StringBuilder pgn = new StringBuilder();
|
||||
List<String> posHist = getPosHistory();
|
||||
String fen = posHist.get(0);
|
||||
String moves = game.getMoveListString(true);
|
||||
if (game.getGameState() == GameState.ALIVE)
|
||||
moves += " *";
|
||||
int year, month, day;
|
||||
{
|
||||
Calendar now = GregorianCalendar.getInstance();
|
||||
year = now.get(Calendar.YEAR);
|
||||
month = now.get(Calendar.MONTH) + 1;
|
||||
day = now.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
pgn.append(String.format("[Date \"%04d.%02d.%02d\"]%n", year, month, day));
|
||||
String white = "Player";
|
||||
String black = ComputerPlayer.engineName;
|
||||
if (!humanIsWhite) {
|
||||
String tmp = white; white = black; black = tmp;
|
||||
}
|
||||
pgn.append(String.format("[White \"%s\"]%n", white));
|
||||
pgn.append(String.format("[Black \"%s\"]%n", black));
|
||||
pgn.append(String.format("[Result \"%s\"]%n", game.getPGNResultString()));
|
||||
if (!fen.equals(TextIO.startPosFEN)) {
|
||||
pgn.append(String.format("[FEN \"%s\"]%n", fen));
|
||||
pgn.append("[SetUp \"1\"]\n");
|
||||
}
|
||||
pgn.append("\n");
|
||||
String[] strArr = moves.split(" ");
|
||||
int currLineLength = 0;
|
||||
final int arrLen = strArr.length;
|
||||
for (int i = 0; i < arrLen; i++) {
|
||||
String move = strArr[i].trim();
|
||||
int moveLen = move.length();
|
||||
if (moveLen > 0) {
|
||||
if (currLineLength + 1 + moveLen >= 80) {
|
||||
pgn.append("\n");
|
||||
pgn.append(move);
|
||||
currLineLength = moveLen;
|
||||
} else {
|
||||
if (currLineLength > 0) {
|
||||
pgn.append(" ");
|
||||
currLineLength++;
|
||||
}
|
||||
pgn.append(move);
|
||||
currLineLength += moveLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
pgn.append("\n\n");
|
||||
return pgn.toString();
|
||||
}
|
||||
|
||||
public void setPGN(String pgn) throws ChessParseError {
|
||||
// First pass, remove comments
|
||||
{
|
||||
StringBuilder out = new StringBuilder();
|
||||
Scanner sc = new Scanner(pgn);
|
||||
sc.useDelimiter("");
|
||||
while (sc.hasNext()) {
|
||||
String c = sc.next();
|
||||
if (c.equals("{")) {
|
||||
sc.skip("[^}]*}");
|
||||
} else if (c.equals(";")) {
|
||||
sc.skip("[^\n]*\n");
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
pgn = out.toString();
|
||||
}
|
||||
|
||||
// Parse tag section
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Scanner sc = new Scanner(pgn);
|
||||
sc.useDelimiter("\\s+");
|
||||
while (sc.hasNext("\\[.*")) {
|
||||
String tagName = sc.next();
|
||||
if (tagName.length() > 1) {
|
||||
tagName = tagName.substring(1);
|
||||
} else {
|
||||
tagName = sc.next();
|
||||
}
|
||||
String tagValue = sc.findWithinHorizon(".*\\]", 0);
|
||||
tagValue = tagValue.trim();
|
||||
if (tagValue.charAt(0) == '"')
|
||||
tagValue = tagValue.substring(1);
|
||||
if (tagValue.charAt(tagValue.length()-1) == ']')
|
||||
tagValue = tagValue.substring(0, tagValue.length() - 1);
|
||||
if (tagValue.charAt(tagValue.length()-1) == '"')
|
||||
tagValue = tagValue.substring(0, tagValue.length() - 1);
|
||||
if (tagName.equals("FEN")) {
|
||||
pos = TextIO.readFEN(tagValue);
|
||||
}
|
||||
}
|
||||
game.processString("new");
|
||||
game.pos = pos;
|
||||
|
||||
// Handle (ignore) recursive annotation variations
|
||||
{
|
||||
StringBuilder out = new StringBuilder();
|
||||
sc.useDelimiter("");
|
||||
int level = 0;
|
||||
while (sc.hasNext()) {
|
||||
String c = sc.next();
|
||||
if (c.equals("(")) {
|
||||
level++;
|
||||
} else if (c.equals(")")) {
|
||||
level--;
|
||||
} else if (level == 0) {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
pgn = out.toString();
|
||||
}
|
||||
|
||||
// Parse move text section
|
||||
sc = new Scanner(pgn);
|
||||
sc.useDelimiter("\\s+");
|
||||
while (sc.hasNext()) {
|
||||
String strMove = sc.next();
|
||||
strMove = strMove.replaceFirst("\\$?[0-9]*\\.*([^?!]*)[?!]*", "$1");
|
||||
if (strMove.length() == 0) continue;
|
||||
Move m = TextIO.stringToMove(game.pos, strMove);
|
||||
if (m == null)
|
||||
break;
|
||||
game.processString(strMove);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFENOrPGN(String fenPgn) throws ChessParseError {
|
||||
try {
|
||||
Position pos = TextIO.readFEN(fenPgn);
|
||||
game.processString("new");
|
||||
game.pos = pos;
|
||||
} catch (ChessParseError e) {
|
||||
// Try read as PGN instead
|
||||
setPGN(fenPgn);
|
||||
}
|
||||
gui.setSelection(-1);
|
||||
updateGUI();
|
||||
startComputerThinking();
|
||||
}
|
||||
|
||||
/** Set color for human player. Doesn't work when computer is thinking. */
|
||||
public final void setHumanWhite(final boolean humanIsWhite) {
|
||||
if (computerThread != null)
|
||||
return;
|
||||
if (this.humanIsWhite != humanIsWhite) {
|
||||
this.humanIsWhite = humanIsWhite;
|
||||
game.processString("swap");
|
||||
startComputerThinking();
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean humansTurn() {
|
||||
return game.pos.whiteMove == humanIsWhite;
|
||||
}
|
||||
public final boolean computerThinking() {
|
||||
return computerThread != null;
|
||||
}
|
||||
|
||||
public final void takeBackMove() {
|
||||
if (humansTurn()) {
|
||||
if (game.getLastMove() != null) {
|
||||
game.processString("undo");
|
||||
if (game.getLastMove() != null) {
|
||||
game.processString("undo");
|
||||
} else {
|
||||
game.processString("redo");
|
||||
}
|
||||
updateGUI();
|
||||
setSelection();
|
||||
}
|
||||
} else if (game.getGameState() != Game.GameState.ALIVE) {
|
||||
if (game.getLastMove() != null) {
|
||||
game.processString("undo");
|
||||
if (!humansTurn()) {
|
||||
if (game.getLastMove() != null) {
|
||||
game.processString("undo");
|
||||
} else {
|
||||
game.processString("redo");
|
||||
}
|
||||
}
|
||||
updateGUI();
|
||||
setSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void redoMove() {
|
||||
if (humansTurn()) {
|
||||
game.processString("redo");
|
||||
game.processString("redo");
|
||||
updateGUI();
|
||||
setSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public final void humanMove(Move m) {
|
||||
if (humansTurn()) {
|
||||
if (doMove(m)) {
|
||||
updateGUI();
|
||||
startComputerThinking();
|
||||
} else {
|
||||
gui.setSelection(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Move promoteMove;
|
||||
public final void reportPromotePiece(int choice) {
|
||||
final boolean white = game.pos.whiteMove;
|
||||
int promoteTo;
|
||||
switch (choice) {
|
||||
case 1:
|
||||
promoteTo = white ? Piece.WROOK : Piece.BROOK;
|
||||
break;
|
||||
case 2:
|
||||
promoteTo = white ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
break;
|
||||
case 3:
|
||||
promoteTo = white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
break;
|
||||
default:
|
||||
promoteTo = white ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
break;
|
||||
}
|
||||
promoteMove.promoteTo = promoteTo;
|
||||
Move m = promoteMove;
|
||||
promoteMove = null;
|
||||
humanMove(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a piece from one square to another.
|
||||
* @return True if the move was legal, false otherwise.
|
||||
*/
|
||||
final private boolean doMove(Move move) {
|
||||
Position pos = game.pos;
|
||||
MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
int promoteTo = move.promoteTo;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
if ((m.from == move.from) && (m.to == move.to)) {
|
||||
if ((m.promoteTo != Piece.EMPTY) && (promoteTo == Piece.EMPTY)) {
|
||||
promoteMove = m;
|
||||
gui.requestPromotePiece();
|
||||
return false;
|
||||
}
|
||||
if (m.promoteTo == promoteTo) {
|
||||
String strMove = TextIO.moveToString(pos, m, false);
|
||||
game.processString(strMove);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
gui.reportInvalidMove(move);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final private void updateGUI() {
|
||||
setStatusString();
|
||||
setMoveList();
|
||||
setThinkingPV();
|
||||
gui.setPosition(game.pos);
|
||||
}
|
||||
|
||||
final private void setStatusString() {
|
||||
String str = game.pos.whiteMove ? "White's move" : "Black's move";
|
||||
if (computerThread != null) str += " (thinking)";
|
||||
if (game.getGameState() != Game.GameState.ALIVE) {
|
||||
str = game.getGameStateString();
|
||||
}
|
||||
gui.setStatusString(str);
|
||||
}
|
||||
|
||||
public final void setMoveList() {
|
||||
String str = game.getMoveListString(true);
|
||||
gui.setMoveListString(str);
|
||||
}
|
||||
|
||||
public final void setThinkingPV() {
|
||||
String str = "";
|
||||
if (gui.showThinking()) {
|
||||
str = thinkingPV;
|
||||
}
|
||||
gui.setThinkingString(str);
|
||||
}
|
||||
|
||||
final private void setSelection() {
|
||||
Move m = game.getLastMove();
|
||||
int sq = (m != null) ? m.to : -1;
|
||||
gui.setSelection(sq);
|
||||
}
|
||||
|
||||
|
||||
private void startComputerThinking() {
|
||||
if (game.pos.whiteMove != humanIsWhite) {
|
||||
if (computerThread == null) {
|
||||
Runnable run = new Runnable() {
|
||||
public void run() {
|
||||
computerPlayer.timeLimit(gui.timeLimit(), gui.timeLimit(), gui.randomMode());
|
||||
final String cmd = computerPlayer.getCommand(new Position(game.pos),
|
||||
game.haveDrawOffer(), game.getHistory());
|
||||
gui.runOnUIThread(new Runnable() {
|
||||
public void run() {
|
||||
game.processString(cmd);
|
||||
thinkingPV = "";
|
||||
updateGUI();
|
||||
setSelection();
|
||||
stopComputerThinking();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
if (threadStack > 0) {
|
||||
ThreadGroup tg = new ThreadGroup("searcher");
|
||||
computerThread = new Thread(tg, run, "searcher", threadStack);
|
||||
} else {
|
||||
computerThread = new Thread(run);
|
||||
}
|
||||
thinkingPV = "";
|
||||
updateGUI();
|
||||
computerThread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void stopComputerThinking() {
|
||||
if (computerThread != null) {
|
||||
computerPlayer.timeLimit(0, 0, false);
|
||||
try {
|
||||
computerThread.join();
|
||||
} catch (InterruptedException ex) {
|
||||
System.out.printf("Could not stop thread%n");
|
||||
}
|
||||
computerThread = null;
|
||||
updateGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setTimeLimit() {
|
||||
if (computerThread != null) {
|
||||
computerPlayer.timeLimit(gui.timeLimit(), gui.timeLimit(), gui.randomMode());
|
||||
}
|
||||
}
|
||||
}
|
58
CuckooChessEngine/src/guibase/GUIInterface.java
Normal file
58
CuckooChessEngine/src/guibase/GUIInterface.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package guibase;
|
||||
|
||||
import chess.Move;
|
||||
import chess.Position;
|
||||
|
||||
public interface GUIInterface {
|
||||
|
||||
/** Update the displayed board position. */
|
||||
public void setPosition(Position pos);
|
||||
|
||||
/** Mark square i as selected. Set to -1 to clear selection. */
|
||||
public void setSelection(int sq);
|
||||
|
||||
/** Set the status text. */
|
||||
public void setStatusString(String str);
|
||||
|
||||
/** Update the list of moves. */
|
||||
public void setMoveListString(String str);
|
||||
|
||||
/** Update the computer thinking information. */
|
||||
public void setThinkingString(String str);
|
||||
|
||||
/** Get the current time limit. */
|
||||
public int timeLimit();
|
||||
|
||||
/** Get "random move" setting. */
|
||||
public boolean randomMode();
|
||||
|
||||
/** Return true if "show thinking" is enabled. */
|
||||
public boolean showThinking();
|
||||
|
||||
/** Ask what to promote a pawn to. Should call reportPromotePiece() when done. */
|
||||
public void requestPromotePiece();
|
||||
|
||||
/** Run code on the GUI thread. */
|
||||
public void runOnUIThread(Runnable runnable);
|
||||
|
||||
/** Report that user attempted to make an invalid move. */
|
||||
public void reportInvalidMove(Move m);
|
||||
}
|
BIN
CuckooChessEngine/src/kpk.bitbase
Normal file
BIN
CuckooChessEngine/src/kpk.bitbase
Normal file
Binary file not shown.
BIN
CuckooChessEngine/src/krkp.winmasks
Normal file
BIN
CuckooChessEngine/src/krkp.winmasks
Normal file
Binary file not shown.
136
CuckooChessEngine/test/chess/BitBoardTest.java
Normal file
136
CuckooChessEngine/test/chess/BitBoardTest.java
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BitBoardTest {
|
||||
public BitBoardTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/** Test of kingAttacks, of class BitBoard. */
|
||||
@Test
|
||||
public void testKingAttacks() throws ChessParseError {
|
||||
System.out.println("kingAttacks");
|
||||
assertEquals(5, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("g1")]));
|
||||
assertEquals(3, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("h1")]));
|
||||
assertEquals(3, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("a1")]));
|
||||
assertEquals(5, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("a2")]));
|
||||
assertEquals(3, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("h8")]));
|
||||
assertEquals(5, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("a6")]));
|
||||
assertEquals(8, Long.bitCount(BitBoard.kingAttacks[TextIO.getSquare("b2")]));
|
||||
}
|
||||
|
||||
/** Test of knightAttacks, of class BitBoard. */
|
||||
@Test
|
||||
public void testKnightAttacks() throws ChessParseError {
|
||||
System.out.println("knightAttacks");
|
||||
assertEquals(3, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("g1")]));
|
||||
assertEquals(2, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("a1")]));
|
||||
assertEquals(2, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("h1")]));
|
||||
assertEquals(4, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("h6")]));
|
||||
assertEquals(4, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("b7")]));
|
||||
assertEquals(8, Long.bitCount(BitBoard.knightAttacks[TextIO.getSquare("c6")]));
|
||||
assertEquals((1L<<TextIO.getSquare("e2")) |
|
||||
(1L<<TextIO.getSquare("f3")) |
|
||||
(1L<<TextIO.getSquare("h3")),
|
||||
BitBoard.knightAttacks[TextIO.getSquare("g1")]);
|
||||
}
|
||||
|
||||
/** Test of squaresBetween[][], of class BitBoard. */
|
||||
@Test
|
||||
public void testSquaresBetween() throws ChessParseError {
|
||||
System.out.println("squaresBetween");
|
||||
// Tests that the set of nonzero elements is correct
|
||||
for (int sq1 = 0; sq1 < 64; sq1++) {
|
||||
for (int sq2 = 0; sq2 < 64; sq2++) {
|
||||
int d = BitBoard.getDirection(sq1, sq2);
|
||||
if (d == 0) {
|
||||
assertEquals(0, BitBoard.squaresBetween[sq1][sq2]);
|
||||
} else {
|
||||
int dx = Position.getX(sq1) - Position.getX(sq2);
|
||||
int dy = Position.getY(sq1) - Position.getY(sq2);
|
||||
if (Math.abs(dx * dy) == 2) { // Knight direction
|
||||
assertEquals(0, BitBoard.squaresBetween[sq1][sq2]);
|
||||
} else {
|
||||
if ((Math.abs(dx) > 1) || (Math.abs(dy) > 1)) {
|
||||
assertTrue(BitBoard.squaresBetween[sq1][sq2] != 0);
|
||||
} else {
|
||||
assertEquals(0, BitBoard.squaresBetween[sq1][sq2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(0x0040201008040200L, BitBoard.squaresBetween[0][63]);
|
||||
assertEquals(0x000000001C000000L, BitBoard.squaresBetween[TextIO.getSquare("b4")][TextIO.getSquare("f4")]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a piece type that can move from "from" to "to", return the
|
||||
* corresponding direction, 8*dy+dx.
|
||||
*/
|
||||
private static final int computeDirection(int from, int to) {
|
||||
int dx = Position.getX(to) - Position.getX(from);
|
||||
int dy = Position.getY(to) - Position.getY(from);
|
||||
if (dx == 0) { // Vertical rook direction
|
||||
if (dy == 0) return 0;
|
||||
return (dy > 0) ? 8 : -8;
|
||||
}
|
||||
if (dy == 0) // Horizontal rook direction
|
||||
return (dx > 0) ? 1 : -1;
|
||||
if (Math.abs(dx) == Math.abs(dy)) // Bishop direction
|
||||
return ((dy > 0) ? 8 : -8) + (dx > 0 ? 1 : -1);
|
||||
if (Math.abs(dx * dy) == 2) // Knight direction
|
||||
return dy * 8 + dx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirection() {
|
||||
System.out.println("getDirection");
|
||||
for (int from = 0; from < 64; from++) {
|
||||
for (int to = 0; to < 64; to++) {
|
||||
assertEquals(computeDirection(from, to), BitBoard.getDirection(from, to));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrailingZeros() {
|
||||
System.out.println("trailingZeros");
|
||||
for (int i = 0; i < 64; i++) {
|
||||
long mask = 1L << i;
|
||||
assertEquals(i, BitBoard.numberOfTrailingZeros(mask));
|
||||
}
|
||||
}
|
||||
}
|
85
CuckooChessEngine/test/chess/BookTest.java
Normal file
85
CuckooChessEngine/test/chess/BookTest.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class BookTest {
|
||||
|
||||
public BookTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getBookMove method, of class Book.
|
||||
*/
|
||||
@Test
|
||||
public void testGetBookMove() throws ChessParseError {
|
||||
System.out.println("getBookMove");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Book book = new Book(true);
|
||||
Move move = book.getBookMove(pos);
|
||||
checkValid(pos, move);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getAllBookMoves method, of class Book.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAllBookMoves() throws ChessParseError {
|
||||
System.out.println("getAllBookMoves");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Book book = new Book(true);
|
||||
String moveListString = book.getAllBookMoves(pos);
|
||||
String[] strMoves = moveListString.split("\\([0-9]*\\) ");
|
||||
assertTrue(strMoves.length > 1);
|
||||
for (String strMove : strMoves) {
|
||||
Move m = TextIO.stringToMove(pos, strMove);
|
||||
checkValid(pos, m);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that move is a legal move in position pos. */
|
||||
private void checkValid(Position pos, Move move) {
|
||||
assertTrue(move != null);
|
||||
MoveGen.MoveList moveList = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moveList);
|
||||
boolean contains = false;
|
||||
for (int mi = 0; mi < moveList.size; mi++)
|
||||
if (moveList.m[mi].equals(move)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
assertTrue(contains);
|
||||
}
|
||||
}
|
114
CuckooChessEngine/test/chess/ComputerPlayerTest.java
Normal file
114
CuckooChessEngine/test/chess/ComputerPlayerTest.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class ComputerPlayerTest {
|
||||
|
||||
public ComputerPlayerTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getCommand method, of class ComputerPlayer.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCommand() throws ChessParseError {
|
||||
System.out.println("getCommand");
|
||||
ArrayList<Position> nullHist = new ArrayList<Position>();
|
||||
|
||||
Position pos = TextIO.readFEN("7k/5Q2/p5K1/8/8/8/8/8 b - - 99 80");
|
||||
ComputerPlayer cp = new ComputerPlayer();
|
||||
cp.maxDepth = 1;
|
||||
cp.maxTimeMillis = -1;
|
||||
cp.verbose = false;
|
||||
String result = cp.getCommand(pos, false, nullHist);
|
||||
assertEquals("a5", result); // Only one legal move
|
||||
|
||||
pos = TextIO.readFEN("7k/5Q2/p5K1/8/8/8/8/8 b - - 100 80");
|
||||
result = cp.getCommand(pos, false, nullHist);
|
||||
assertEquals("draw 50", result); // Should claim draw without making a move
|
||||
|
||||
pos = TextIO.readFEN("3k4/1R6/R7/8/8/8/8/1K6 w - - 100 80");
|
||||
result = cp.getCommand(pos, false, nullHist);
|
||||
assertEquals("Ra8#", result); // Can claim draw, but should not
|
||||
|
||||
pos = TextIO.readFEN("8/1R5k/R7/8/8/8/B7/1K6 b - - 99 80");
|
||||
result = cp.getCommand(pos, false, nullHist);
|
||||
assertEquals("draw 50 Kh8", result); // Should claim draw by 50-move rule
|
||||
|
||||
// Only one possible move. Should realize that draw claim is possible, but very bad
|
||||
pos = TextIO.readFEN("6Nk/8/5K1R/q7/q7/q7/8/8 b - - 100 80");
|
||||
result = cp.getCommand(pos, false, nullHist);
|
||||
assertEquals("Kxg8", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of draw by repetition, of class ComputerPlayer.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawRep() throws ChessParseError {
|
||||
System.out.println("drawRep");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
ComputerPlayer cp = new ComputerPlayer();
|
||||
cp.maxDepth = 3;
|
||||
cp.maxTimeMillis = -1;
|
||||
cp.verbose = false;
|
||||
game.processString("setpos 7k/5RR1/8/8/8/8/q3q3/2K5 w - - 0 1");
|
||||
game.processString("Rh7");
|
||||
game.processString("Kg8");
|
||||
game.processString("Rhg7");
|
||||
String result = cp.getCommand(new Position(game.pos), false, game.getHistory());
|
||||
assertEquals("Kh8", result); // Not valid to claim draw here
|
||||
game.processString("Kh8");
|
||||
game.processString("Rh7");
|
||||
game.processString("Kg8");
|
||||
game.processString("Rhg7");
|
||||
result = cp.getCommand(new Position(game.pos), false, game.getHistory());
|
||||
assertEquals("draw rep Kh8", result); // Can't win, but can claim draw.
|
||||
|
||||
game.processString("setpos 7k/R7/1R6/8/8/8/8/K7 w - - 0 1");
|
||||
game.processString("Ra8");
|
||||
game.processString("Kh7");
|
||||
result = cp.getCommand(new Position(game.pos), false, game.getHistory());
|
||||
assertEquals("Ra7+", result); // Ra7 is mate-in-two
|
||||
game.processString("Ra7");
|
||||
game.processString("Kh8");
|
||||
game.processString("Ra8");
|
||||
game.processString("Kh7");
|
||||
result = cp.getCommand(new Position(game.pos), false, game.getHistory());
|
||||
assertTrue(!result.equals("Ra7+")); // Ra7 now leads to a draw by repetition
|
||||
}
|
||||
}
|
492
CuckooChessEngine/test/chess/EvaluateTest.java
Normal file
492
CuckooChessEngine/test/chess/EvaluateTest.java
Normal file
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class EvaluateTest {
|
||||
|
||||
public EvaluateTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of evalPos method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testEvalPos() throws ChessParseError {
|
||||
System.out.println("evalPos");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "e4"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "e5"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nf3"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nc6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Bb5"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nge7"), ui);
|
||||
assertTrue(moveScore(pos, "O-O") > 0); // Castling is good
|
||||
assertTrue(moveScore(pos, "Ke2") < 0); // Losing right to castle is bad
|
||||
assertTrue(moveScore(pos, "Kf1") < 0);
|
||||
assertTrue(moveScore(pos, "Rg1") < 0);
|
||||
assertTrue(moveScore(pos, "Rf1") < 0);
|
||||
|
||||
pos = TextIO.readFEN("8/8/8/1r3k2/4pP2/4P3/8/4K2R w K - 0 1");
|
||||
assertEquals(true, pos.h1Castle());
|
||||
int cs1 = evalWhite(pos);
|
||||
pos.setCastleMask(pos.getCastleMask() & ~(1 << Position.H1_CASTLE));
|
||||
assertEquals(false, pos.h1Castle());
|
||||
int cs2 = evalWhite(pos);
|
||||
assertTrue(cs2 >= cs1); // No bonus for useless castle right
|
||||
|
||||
// Test rook open file bonus
|
||||
pos = TextIO.readFEN("r4rk1/1pp1qppp/3b1n2/4p3/2B1P1b1/1QN2N2/PP3PPP/R3R1K1 w - - 0 1");
|
||||
int ms1 = moveScore(pos, "Red1");
|
||||
int ms2 = moveScore(pos, "Rec1");
|
||||
int ms3 = moveScore(pos, "Rac1");
|
||||
int ms4 = moveScore(pos, "Rad1");
|
||||
assertTrue(ms1 > 0); // Good to have rook on open file
|
||||
assertTrue(ms2 > 0); // Good to have rook on half-open file
|
||||
assertTrue(ms1 > ms2); // Open file better than half-open file
|
||||
assertTrue(ms3 > 0);
|
||||
assertTrue(ms4 > 0);
|
||||
assertTrue(ms4 > ms1);
|
||||
assertTrue(ms3 > ms2);
|
||||
|
||||
pos = TextIO.readFEN("r3kb1r/p3pp1p/bpPq1np1/4N3/2pP4/2N1PQ2/P1PB1PPP/R3K2R b KQkq - 0 12");
|
||||
assertTrue(moveScore(pos, "O-O-O") > 0); // Black long castle is bad for black
|
||||
pos.makeMove(TextIO.stringToMove(pos, "O-O-O"), ui);
|
||||
assertTrue(moveScore(pos, "O-O") > 0); // White short castle is good for white
|
||||
|
||||
pos = TextIO.readFEN("8/3k4/2p5/1pp5/1P1P4/3K4/8/8 w - - 0 1");
|
||||
int sc1 = moveScore(pos, "bxc5");
|
||||
int sc2 = moveScore(pos, "dxc5");
|
||||
assertTrue(sc1 < sc2); // Don't give opponent a passed pawn.
|
||||
|
||||
pos = TextIO.readFEN("8/pp1bk3/8/8/8/8/PPPBK3/8 w - - 0 1");
|
||||
sc1 = evalWhite(pos);
|
||||
pos.setPiece(Position.getSquare(3, 1), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(3, 0), Piece.WBISHOP);
|
||||
sc2 = evalWhite(pos);
|
||||
assertTrue(sc2 > sc1); // Easier to win if bishops on same color
|
||||
|
||||
// Test bishop mobility
|
||||
pos = TextIO.readFEN("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
|
||||
sc1 = moveScore(pos, "Bd3");
|
||||
sc2 = moveScore(pos, "Bc4");
|
||||
assertTrue(sc2 > sc1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of pieceSquareEval method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testPieceSquareEval() throws ChessParseError {
|
||||
System.out.println("pieceSquareEval");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
int score = evalWhite(pos);
|
||||
assertEquals(0, score); // Should be zero, by symmetry
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "e4"), ui);
|
||||
score = evalWhite(pos);
|
||||
assertTrue(score > 0); // Centralizing a pawn is a good thing
|
||||
pos.makeMove(TextIO.stringToMove(pos, "e5"), ui);
|
||||
score = evalWhite(pos);
|
||||
assertEquals(0, score); // Should be zero, by symmetry
|
||||
assertTrue(moveScore(pos, "Nf3") > 0); // Developing knight is good
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nf3"), ui);
|
||||
assertTrue(moveScore(pos, "Nc6") < 0); // Developing knight is good
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nc6"), ui);
|
||||
assertTrue(moveScore(pos, "Bb5") > 0); // Developing bishop is good
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Bb5"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nge7"), ui);
|
||||
assertTrue(moveScore(pos, "Qe2") > 0); // Queen away from edge is good
|
||||
score = evalWhite(pos);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Bxc6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nxc6"), ui);
|
||||
int score2 = evalWhite(pos);
|
||||
assertTrue(score2 < score); // Bishop worth more than knight in this case
|
||||
|
||||
pos = TextIO.readFEN("5k2/4nppp/p1n5/1pp1p3/4P3/2P1BN2/PP3PPP/3R2K1 w - - 0 1");
|
||||
assertTrue(moveScore(pos, "Rd7") > 0); // Rook on 7:th rank is good
|
||||
assertTrue(moveScore(pos, "Rd8") <= 0); // Rook on 8:th rank not particularly good
|
||||
pos.setPiece(TextIO.getSquare("a1"), Piece.WROOK);
|
||||
assertTrue(moveScore(pos, "Rac1") > 0); // Rook on c-f files considered good
|
||||
|
||||
pos = TextIO.readFEN("r4rk1/pppRRppp/1q4b1/n7/8/2N3B1/PPP1QPPP/6K1 w - - 0 1");
|
||||
score = evalWhite(pos);
|
||||
assertTrue(score > 100); // Two rooks on 7:th rank is very good
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of tradeBonus method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testTradeBonus() throws ChessParseError {
|
||||
System.out.println("tradeBonus");
|
||||
String fen = "8/5k2/6r1/2p1p3/3p4/2P2N2/3PPP2/4K1R1 w - - 0 1";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
int score1 = evalWhite(pos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Rxg6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Kxg6"), ui);
|
||||
int score2 = evalWhite(pos);
|
||||
assertTrue(score2 > score1); // White ahead, trading pieces is good
|
||||
|
||||
pos = TextIO.readFEN(fen);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "cxd4"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "cxd4"), ui);
|
||||
score2 = evalWhite(pos);
|
||||
assertTrue(score2 < score1); // White ahead, trading pawns is bad
|
||||
|
||||
pos = TextIO.readFEN("8/8/1b2b3/4kp2/5N2/4NKP1/6B1/8 w - - 0 62");
|
||||
score1 = evalWhite(pos);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nxe6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Kxe6"), ui);
|
||||
score2 = evalWhite(pos);
|
||||
assertTrue(score2 > score1); // White ahead, trading pieces is good
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of material method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testMaterial() throws ChessParseError {
|
||||
System.out.println("material");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
assertEquals(0, Evaluate.material(pos));
|
||||
|
||||
final int pV = Evaluate.pV;
|
||||
final int qV = Evaluate.qV;
|
||||
assertTrue(pV != 0);
|
||||
assertTrue(qV != 0);
|
||||
assertTrue(qV > pV);
|
||||
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "e4"), ui);
|
||||
assertEquals(0, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "d5"), ui);
|
||||
assertEquals(0, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "exd5"), ui);
|
||||
assertEquals(pV, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Qxd5"), ui);
|
||||
assertEquals(0, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nc3"), ui);
|
||||
assertEquals(0, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Qxd2"), ui);
|
||||
assertEquals(-pV, Evaluate.material(pos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Qxd2"), ui);
|
||||
assertEquals(-pV+qV, Evaluate.material(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of kingSafety method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testKingSafety() throws ChessParseError {
|
||||
System.out.println("kingSafety");
|
||||
Position pos = TextIO.readFEN("r3kb1r/p1p1pppp/b2q1n2/4N3/3P4/2N1PQ2/P2B1PPP/R3R1K1 w kq - 0 1");
|
||||
int s1 = evalWhite(pos);
|
||||
pos.setPiece(TextIO.getSquare("g7"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("b7"), Piece.BPAWN);
|
||||
int s2 = evalWhite(pos);
|
||||
assertTrue(s2 < s1); // Half-open g-file is bad for white
|
||||
|
||||
// Trapping rook with own king is bad
|
||||
pos = TextIO.readFEN("rnbqk1nr/pppp1ppp/8/8/1bBpP3/8/PPP2PPP/RNBQK1NR w KQkq - 2 4");
|
||||
s1 = evalWhite(pos);
|
||||
pos.setPiece(TextIO.getSquare("e1"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("f1"), Piece.WKING);
|
||||
s2 = evalWhite(pos);
|
||||
assertTrue(s2 < s1);
|
||||
|
||||
pos = TextIO.readFEN("rnbqk1nr/pppp1ppp/8/8/1bBpPB2/8/PPP1QPPP/RN1K2NR w kq - 0 1");
|
||||
s1 = evalWhite(pos);
|
||||
pos.setPiece(TextIO.getSquare("d1"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("c1"), Piece.WKING);
|
||||
s2 = evalWhite(pos);
|
||||
assertTrue(s2 < s1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of endGameEval method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testEndGameEval() throws ChessParseError {
|
||||
System.out.println("endGameEval");
|
||||
Position pos = new Position();
|
||||
pos.setPiece(Position.getSquare(4, 1), Piece.WKING);
|
||||
pos.setPiece(Position.getSquare(4, 6), Piece.BKING);
|
||||
int score = evalWhite(pos);
|
||||
assertEquals(0, score);
|
||||
|
||||
pos.setPiece(Position.getSquare(3, 1), Piece.WBISHOP);
|
||||
score = evalWhite(pos);
|
||||
assertTrue(Math.abs(score) < 50); // Insufficient material to mate
|
||||
|
||||
pos.setPiece(Position.getSquare(3, 1), Piece.WKNIGHT);
|
||||
score = evalWhite(pos);
|
||||
assertTrue(Math.abs(score) < 50); // Insufficient material to mate
|
||||
|
||||
pos.setPiece(Position.getSquare(3, 1), Piece.WROOK);
|
||||
score = evalWhite(pos);
|
||||
final int rV = Evaluate.rV;
|
||||
assertTrue(Math.abs(score) > rV + 100); // Enough material to force mate
|
||||
|
||||
pos.setPiece(Position.getSquare(3, 6), Piece.BBISHOP);
|
||||
score = evalWhite(pos);
|
||||
final int bV = Evaluate.bV;
|
||||
assertTrue(score >= 0);
|
||||
assertTrue(score < rV - bV); // Insufficient excess material to mate
|
||||
|
||||
pos.setPiece(Position.getSquare(5, 6), Piece.BROOK);
|
||||
score = evalWhite(pos);
|
||||
assertTrue(score <= 0);
|
||||
assertTrue(-score < bV);
|
||||
|
||||
pos.setPiece(Position.getSquare(2, 6), Piece.BBISHOP);
|
||||
score = evalWhite(pos);
|
||||
assertTrue(-score > bV * 2 + 100);
|
||||
|
||||
// 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;
|
||||
assertTrue(score > rV + pV - bV - 100);
|
||||
|
||||
// KNNK is a draw
|
||||
pos = TextIO.readFEN("8/8/4k3/8/8/3NK3/3N4/8 w - - 0 1");
|
||||
score = evalWhite(pos);
|
||||
assertTrue(Math.abs(score) < 50);
|
||||
|
||||
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
|
||||
score = moveScore(pos, "Ke6");
|
||||
assertTrue(score < 0); // Black king going away from wrong corner, good for black
|
||||
|
||||
// KRN vs KR is generally drawn
|
||||
pos = TextIO.readFEN("rk/p/8/8/8/8/NKR/8 w - - 0 1");
|
||||
score = evalWhite(pos);
|
||||
assertTrue(score < nV - 2 * pV);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of endGameEval method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testPassedPawns() throws ChessParseError {
|
||||
System.out.println("passedPawns");
|
||||
Position pos = TextIO.readFEN("8/8/8/P3k/8/8/p/K w");
|
||||
int score = evalWhite(pos);
|
||||
assertTrue(score > 300); // Unstoppable passed pawn
|
||||
pos.whiteMove = false;
|
||||
score = evalWhite(pos);
|
||||
assertTrue(score <= 0); // Not unstoppable
|
||||
|
||||
pos = TextIO.readFEN("4R3/8/8/3K4/8/4pk2/8/8 w - - 0 1");
|
||||
score = evalWhite(pos);
|
||||
pos.setPiece(TextIO.getSquare("d5"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("d4"), Piece.WKING);
|
||||
int score2 = evalWhite(pos);
|
||||
assertTrue(score2 > score); // King closer to passed pawn promotion square
|
||||
|
||||
// Connected passed pawn test. Disabled because it didn't help in tests
|
||||
// pos = TextIO.readFEN("4k3/8/8/4P3/3P1K2/8/8/8 w - - 0 1");
|
||||
// score = evalWhite(pos);
|
||||
// pos.setPiece(TextIO.getSquare("d4"), Piece.EMPTY);
|
||||
// pos.setPiece(TextIO.getSquare("d5"), Piece.WPAWN);
|
||||
// score2 = evalWhite(pos);
|
||||
// assertTrue(score2 > score); // Advancing passed pawn is good
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of endGameEval method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testBishAndRookPawns() throws ChessParseError {
|
||||
System.out.println("bishAndRookPawns");
|
||||
final int pV = Evaluate.pV;
|
||||
final int bV = Evaluate.bV;
|
||||
final int winScore = pV + bV;
|
||||
final int drawish = (pV + bV) / 20;
|
||||
Position pos = TextIO.readFEN("k7/8/8/8/2B5/2K5/P7/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("k7/8/8/8/3B4/2K5/P7/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/8/8/3B4/2K5/P7/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/8/8/3B4/2K4P/8/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/8/8/4B3/2K4P/8/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/8/4B3/2K4P/8/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/8/4B3/2K4P/7P/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/8/2B1B3/2K4P/7P/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/2B5/4B3/2K4P/7P/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/8/4B3/2K4P/P7/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
pos = TextIO.readFEN("8/6k1/8/8/4B3/2K3PP/8/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrappedBishop() throws ChessParseError {
|
||||
Position pos = TextIO.readFEN("r2q1rk1/ppp2ppp/3p1n2/8/3P4/1P1Q1NP1/b1P2PBP/2KR3R w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > 0); // Black has trapped bishop
|
||||
|
||||
pos = TextIO.readFEN("r2q2k1/pp1b1p1p/2p2np1/3p4/3P4/1BNQ2P1/PPPB1P1b/2KR4 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > 0); // Black has trapped bishop
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of endGameEval method, of class Evaluate.
|
||||
*/
|
||||
@Test
|
||||
public void testKQKP() throws ChessParseError {
|
||||
System.out.println("KQKP");
|
||||
final int pV = Evaluate.pV;
|
||||
final int qV = Evaluate.qV;
|
||||
final int winScore = qV - pV - 200;
|
||||
final int drawish = (pV + qV) / 20;
|
||||
|
||||
// Pawn on a2
|
||||
Position pos = TextIO.readFEN("8/8/1K6/8/8/Q7/p7/1k6 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
pos = TextIO.readFEN("8/8/8/1K6/8/Q7/p7/1k6 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
pos = TextIO.readFEN("3Q4/8/8/8/K7/8/1kp5/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
|
||||
// Pawn on c2
|
||||
pos = TextIO.readFEN("3Q4/8/8/8/3K4/8/1kp5/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
pos = TextIO.readFEN("3Q4/8/8/8/8/4K3/1kp5/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKRKP() throws ChessParseError {
|
||||
System.out.println("KRKP");
|
||||
final int pV = Evaluate.pV;
|
||||
final int rV = Evaluate.rV;
|
||||
final int winScore = rV - pV;
|
||||
final int drawish = (pV + rV) / 20;
|
||||
Position pos = TextIO.readFEN("6R1/8/8/8/5K2/2kp4/8/8 w - - 0 1");
|
||||
assertTrue(evalWhite(pos) > winScore);
|
||||
pos.whiteMove = !pos.whiteMove;
|
||||
assertTrue(evalWhite(pos) < drawish);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantWin() throws ChessParseError {
|
||||
Position pos = TextIO.readFEN("8/8/8/3k4/3p4/3K4/4N3/8 w - - 0 1");
|
||||
int score1 = evalWhite(pos);
|
||||
assertTrue(score1 < 0);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nxd4"), ui);
|
||||
int score2 = evalWhite(pos);
|
||||
assertTrue(score2 <= 0);
|
||||
assertTrue(score2 > score1);
|
||||
}
|
||||
|
||||
/** Return static evaluation score for white, regardless of whose turn it is to move. */
|
||||
final static int evalWhite(Position pos) {
|
||||
Evaluate eval = new Evaluate();
|
||||
int ret = eval.evalPos(pos);
|
||||
Position symPos = swapColors(pos);
|
||||
int symScore = eval.evalPos(symPos);
|
||||
assertEquals(ret, symScore);
|
||||
if (!pos.whiteMove) {
|
||||
ret = -ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
final static Position swapColors(Position pos) {
|
||||
Position sym = new Position();
|
||||
sym.whiteMove = !pos.whiteMove;
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
p = Piece.isWhite(p) ? Piece.makeBlack(p) : Piece.makeWhite(p);
|
||||
sym.setPiece(Position.getSquare(x, 7-y), p);
|
||||
}
|
||||
}
|
||||
|
||||
int castleMask = 0;
|
||||
if (pos.a1Castle()) castleMask |= 1 << Position.A8_CASTLE;
|
||||
if (pos.h1Castle()) castleMask |= 1 << Position.H8_CASTLE;
|
||||
if (pos.a8Castle()) castleMask |= 1 << Position.A1_CASTLE;
|
||||
if (pos.h8Castle()) castleMask |= 1 << Position.H1_CASTLE;
|
||||
sym.setCastleMask(castleMask);
|
||||
|
||||
if (pos.getEpSquare() >= 0) {
|
||||
int x = Position.getX(pos.getEpSquare());
|
||||
int y = Position.getY(pos.getEpSquare());
|
||||
sym.setEpSquare(Position.getSquare(x, 7-y));
|
||||
}
|
||||
|
||||
sym.halfMoveClock = pos.halfMoveClock;
|
||||
sym.fullMoveCounter = pos.fullMoveCounter;
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
/** Compute change in eval score for white after making "moveStr" in position "pos". */
|
||||
private final int moveScore(Position pos, String moveStr) {
|
||||
int score1 = evalWhite(pos);
|
||||
Position tmpPos = new Position(pos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
tmpPos.makeMove(TextIO.stringToMove(tmpPos, moveStr), ui);
|
||||
int score2 = evalWhite(tmpPos);
|
||||
// System.out.printf("move:%s s1:%d s2:%d\n", moveStr, score1, score2);
|
||||
return score2 - score1;
|
||||
}
|
||||
}
|
441
CuckooChessEngine/test/chess/GameTest.java
Normal file
441
CuckooChessEngine/test/chess/GameTest.java
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class GameTest {
|
||||
|
||||
public GameTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of haveDrawOffer method, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testHaveDrawOffer() {
|
||||
System.out.println("haveDrawOffer");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
|
||||
boolean res = game.processString("e4");
|
||||
assertEquals(true, res);
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
|
||||
res = game.processString("draw offer e5");
|
||||
assertEquals(true, res);
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw
|
||||
assertEquals(Piece.BPAWN, game.pos.getPiece(Position.getSquare(4, 4))); // e5 move made
|
||||
|
||||
res = game.processString("draw offer Nf3");
|
||||
assertEquals(true, res);
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw
|
||||
assertEquals(Piece.WKNIGHT, game.pos.getPiece(Position.getSquare(5, 2))); // Nf3 move made
|
||||
|
||||
res = game.processString("Nc6");
|
||||
assertEquals(true, res);
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
assertEquals(Piece.BKNIGHT, game.pos.getPiece(Position.getSquare(2, 5))); // Nc6 move made
|
||||
|
||||
res = game.processString("draw offer Bb5");
|
||||
assertEquals(true, res);
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
assertEquals(Piece.WBISHOP, game.pos.getPiece(Position.getSquare(1, 4))); // Bb5 move made
|
||||
|
||||
res = game.processString("draw accept");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Draw by agreement
|
||||
|
||||
res = game.processString("undo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Piece.EMPTY, game.pos.getPiece(Position.getSquare(1, 4))); // Bb5 move undone
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
res = game.processString("undo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Piece.EMPTY, game.pos.getPiece(Position.getSquare(2, 5))); // Nc6 move undone
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Piece.BKNIGHT, game.pos.getPiece(Position.getSquare(2, 5))); // Nc6 move redone
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Piece.WBISHOP, game.pos.getPiece(Position.getSquare(1, 4))); // Bb5 move redone
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Can't redo draw accept
|
||||
|
||||
// Test draw offer in connection with invalid move
|
||||
res = game.processString("new");
|
||||
assertEquals(true, res);
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
|
||||
res = game.processString("draw offer e5");
|
||||
assertEquals(true, res);
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.pos)); // Move invalid, not executed
|
||||
res = game.processString("e4");
|
||||
assertEquals(true, res);
|
||||
assertEquals(true, game.haveDrawOffer()); // Previous draw offer still valid
|
||||
assertEquals(Piece.WPAWN, game.pos.getPiece(Position.getSquare(4, 3))); // e4 move made
|
||||
|
||||
// Undo/redo shall clear "pendingDrawOffer".
|
||||
game.processString("new");
|
||||
game.processString("e4");
|
||||
game.processString("draw offer e4"); // Invalid black move
|
||||
assertEquals(true, game.pendingDrawOffer);
|
||||
game.processString("undo");
|
||||
game.processString("redo");
|
||||
game.processString("e5");
|
||||
assertEquals(true,game.pos.whiteMove);
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of draw by 50 move rule, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testDraw50() {
|
||||
System.out.println("draw50");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
boolean res = game.processString("draw 50");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid
|
||||
res = game.processString("e4");
|
||||
assertEquals(true, game.haveDrawOffer()); // Invalid claim converted to draw offer
|
||||
|
||||
String cmd = "setpos 8/4k3/8/P7/8/8/8/1N2K2R w K - 99 83";
|
||||
res = game.processString(cmd);
|
||||
assertEquals(true, res);
|
||||
res = game.processString("draw 50");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid
|
||||
|
||||
game.processString(cmd);
|
||||
game.processString("draw 50 Nc3");
|
||||
assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Draw claim valid
|
||||
assertEquals("Game over, draw by 50 move rule! [Nc3]", game.getGameStateString());
|
||||
|
||||
game.processString(cmd);
|
||||
game.processString("draw 50 a6");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Pawn move resets counter
|
||||
assertEquals(Piece.WPAWN, game.pos.getPiece(Position.getSquare(0, 5))); // Move a6 made
|
||||
|
||||
game.processString(cmd);
|
||||
game.processString("draw 50 O-O");
|
||||
assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Castling doesn't reset counter
|
||||
|
||||
game.processString(cmd);
|
||||
game.processString("draw 50 Kf2");
|
||||
assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Loss of castling right doesn't reset counter
|
||||
|
||||
game.processString(cmd);
|
||||
game.processString("draw 50 Ke3");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Ke3 is invalid
|
||||
assertEquals(true,game.pos.whiteMove);
|
||||
game.processString("a6");
|
||||
assertEquals(true, game.haveDrawOffer()); // Previous invalid claim converted to offer
|
||||
game.processString("draw 50");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // 50 move counter reset.
|
||||
res = game.processString("draw accept");
|
||||
assertEquals(true, res);
|
||||
assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Can accept previous implicit offer
|
||||
|
||||
cmd = "setpos 3k4/R7/3K4/8/8/8/8/8 w - - 99 78";
|
||||
game.processString(cmd);
|
||||
game.processString("Ra8");
|
||||
assertEquals(Game.GameState.WHITE_MATE, game.getGameState());
|
||||
game.processString("draw 50");
|
||||
assertEquals(Game.GameState.WHITE_MATE, game.getGameState()); // Can't claim draw when game over
|
||||
assertEquals(Game.GameState.ALIVE, game.drawState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of draw by repetition, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawRep() {
|
||||
System.out.println("drawRep");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
game.processString("Nc3");
|
||||
game.processString("Nc6");
|
||||
game.processString("Nb1");
|
||||
game.processString("Nb8");
|
||||
game.processString("Nf3");
|
||||
game.processString("Nf6");
|
||||
game.processString("Ng1");
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
game.processString("draw rep");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, one more move needed
|
||||
game.processString("draw rep Nc6");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, wrong move claimed
|
||||
assertEquals(Piece.BKNIGHT, game.pos.getPiece(Position.getSquare(2, 5))); // Move Nc6 made
|
||||
assertEquals(true, game.haveDrawOffer());
|
||||
game.processString("undo");
|
||||
assertEquals(false, game.haveDrawOffer());
|
||||
assertEquals(Piece.EMPTY, game.pos.getPiece(Position.getSquare(2, 5)));
|
||||
game.processString("draw rep Ng8");
|
||||
assertEquals(Game.GameState.DRAW_REP, game.getGameState());
|
||||
assertEquals(Piece.EMPTY, game.pos.getPiece(Position.getSquare(6, 7))); // Ng8 not played
|
||||
|
||||
// Test draw by repetition when a "potential ep square but not real ep square" position is present.
|
||||
game.processString("new");
|
||||
game.processString("e4"); // e3 is not a real epSquare here
|
||||
game.processString("Nf6");
|
||||
game.processString("Nf3");
|
||||
game.processString("Ng8");
|
||||
game.processString("Ng1");
|
||||
game.processString("Nf6");
|
||||
game.processString("Nf3");
|
||||
game.processString("Ng8");
|
||||
game.processString("draw rep Ng1");
|
||||
assertEquals(Game.GameState.DRAW_REP, game.getGameState());
|
||||
|
||||
// Now check the case when e3 *is* an epSquare
|
||||
game.processString("new");
|
||||
game.processString("Nf3");
|
||||
game.processString("d5");
|
||||
game.processString("Ng1");
|
||||
game.processString("d4");
|
||||
game.processString("e4"); // Here e3 is a real epSquare
|
||||
game.processString("Nf6");
|
||||
game.processString("Nf3");
|
||||
game.processString("Ng8");
|
||||
game.processString("Ng1");
|
||||
game.processString("Nf6");
|
||||
game.processString("Nf3");
|
||||
game.processString("Ng8");
|
||||
game.processString("draw rep Ng1");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
|
||||
// EP capture not valid because it would leave the king in check. Therefore
|
||||
// the position has been repeated three times at the end of the move sequence.
|
||||
game.processString("setpos 4k2n/8/8/8/4p3/8/3P4/3KR2N w - - 0 1");
|
||||
game.processString("d4");
|
||||
game.processString("Ng6");
|
||||
game.processString("Ng3");
|
||||
game.processString("Nh8");
|
||||
game.processString("Nh1");
|
||||
game.processString("Ng6");
|
||||
game.processString("Ng3");
|
||||
game.processString("Nh8");
|
||||
game.processString("draw rep Nh1");
|
||||
assertEquals(Game.GameState.DRAW_REP, game.getGameState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of resign command, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testResign() {
|
||||
System.out.println("resign");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.processString("f3");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.processString("resign");
|
||||
assertEquals(Game.GameState.RESIGN_BLACK, game.getGameState());
|
||||
game.processString("undo");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.processString("f3");
|
||||
game.processString("e5");
|
||||
game.processString("resign");
|
||||
assertEquals(Game.GameState.RESIGN_WHITE, game.getGameState());
|
||||
game.processString("undo");
|
||||
game.processString("e5");
|
||||
game.processString("g4");
|
||||
game.processString("Qh4");
|
||||
assertEquals(Game.GameState.BLACK_MATE, game.getGameState());
|
||||
game.processString("resign");
|
||||
assertEquals(Game.GameState.BLACK_MATE, game.getGameState()); // Can't resign after game over
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of processString method, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testProcessString() throws ChessParseError {
|
||||
System.out.println("processString");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.pos));
|
||||
boolean res = game.processString("Nf3");
|
||||
assertEquals(true, res);
|
||||
assertEquals(1, game.pos.halfMoveClock);
|
||||
assertEquals(1, game.pos.fullMoveCounter);
|
||||
res = game.processString("d5");
|
||||
assertEquals(true, res);
|
||||
assertEquals(0, game.pos.halfMoveClock);
|
||||
assertEquals(2, game.pos.fullMoveCounter);
|
||||
|
||||
res = game.processString("undo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(1, game.pos.halfMoveClock);
|
||||
assertEquals(1, game.pos.fullMoveCounter);
|
||||
res = game.processString("undo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.pos));
|
||||
res = game.processString("undo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.pos));
|
||||
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(1, game.pos.halfMoveClock);
|
||||
assertEquals(1, game.pos.fullMoveCounter);
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(0, game.pos.halfMoveClock);
|
||||
assertEquals(2, game.pos.fullMoveCounter);
|
||||
res = game.processString("redo");
|
||||
assertEquals(true, res);
|
||||
assertEquals(0, game.pos.halfMoveClock);
|
||||
assertEquals(2, game.pos.fullMoveCounter);
|
||||
|
||||
res = game.processString("new");
|
||||
assertEquals(true, res);
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.pos));
|
||||
|
||||
String fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
res = game.processString("setpos " + fen);
|
||||
assertEquals(true, res);
|
||||
assertEquals(pos, game.pos);
|
||||
|
||||
res = game.processString("junk");
|
||||
assertEquals(false, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getGameState method, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testGetGameState() {
|
||||
System.out.println("getGameState");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.processString("f3");
|
||||
game.processString("e5");
|
||||
game.processString("g4");
|
||||
game.processString("Qh4");
|
||||
assertEquals(Game.GameState.BLACK_MATE, game.getGameState());
|
||||
|
||||
game.processString("setpos 5k2/5P2/5K2/8/8/8/8/8 b - - 0 1");
|
||||
assertEquals(Game.GameState.BLACK_STALEMATE, game.getGameState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of insufficientMaterial method, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testInsufficientMaterial() {
|
||||
System.out.println("insufficientMaterial");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.processString("setpos 4k3/8/8/8/8/8/8/4K3 w - - 0 1");
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
final int a1 = Position.getSquare(0, 0);
|
||||
game.pos.setPiece(a1, Piece.WROOK);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.pos.setPiece(a1, Piece.BQUEEN);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.pos.setPiece(a1, Piece.WPAWN);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.pos.setPiece(a1, Piece.BKNIGHT);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
game.pos.setPiece(a1, Piece.WBISHOP);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
|
||||
final int c1 = Position.getSquare(2, 0);
|
||||
game.pos.setPiece(c1, Piece.WKNIGHT);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
game.pos.setPiece(c1, Piece.BBISHOP);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
game.pos.setPiece(c1, Piece.WBISHOP);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
|
||||
final int b2 = Position.getSquare(1, 1);
|
||||
game.pos.setPiece(b2, Piece.WBISHOP);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
game.pos.setPiece(b2, Piece.BBISHOP);
|
||||
assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
|
||||
|
||||
final int b3 = Position.getSquare(1, 2);
|
||||
game.pos.setPiece(b3, Piece.WBISHOP);
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
|
||||
// Can't force mate with KNNK, but still not an automatic draw.
|
||||
game.processString("setpos 8/8/8/8/8/8/8/K3nnk1 w - - 0 1");
|
||||
assertEquals(Game.GameState.ALIVE, game.getGameState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of perfT method, of class Game.
|
||||
*/
|
||||
@Test
|
||||
public void testPerfT() {
|
||||
System.out.println("perfT");
|
||||
Game game = new Game(new HumanPlayer(), new HumanPlayer());
|
||||
game.processString("new");
|
||||
doTestPerfT(game.pos, 5, new long[]{20,400,8902,197281,4865609,119060324,3195901860L,84998978956L});
|
||||
|
||||
game.processString("setpos 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -");
|
||||
doTestPerfT(game.pos, 5, new long[]{14, 191, 2812, 43238, 674624, 11030083, 178633661});
|
||||
|
||||
game.processString("setpos r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -");
|
||||
doTestPerfT(game.pos, 4, new long[]{48,2039,97862,4085603,193690690});
|
||||
}
|
||||
|
||||
private void doTestPerfT(Position pos, int maxDepth, long[] expectedNodeCounts) {
|
||||
for (int d = 1; d <= maxDepth; d++) {
|
||||
MoveGen moveGen = new MoveGen();
|
||||
long t0 = System.nanoTime();
|
||||
long nodes = Game.perfT(moveGen, pos, d);
|
||||
long t1 = System.nanoTime();
|
||||
System.out.printf("perft(%d) = %d, t=%.6fs\n", d, nodes, (t1 - t0)*1e-9);
|
||||
assertEquals(expectedNodeCounts[d-1], nodes);
|
||||
}
|
||||
}
|
||||
}
|
85
CuckooChessEngine/test/chess/HistoryTest.java
Normal file
85
CuckooChessEngine/test/chess/HistoryTest.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class HistoryTest {
|
||||
|
||||
public HistoryTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getHistScore method, of class History.
|
||||
*/
|
||||
@Test
|
||||
public void testGetHistScore() throws ChessParseError {
|
||||
System.out.println("getHistScore");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
History hs = new History();
|
||||
Move m1 = TextIO.stringToMove(pos, "e4");
|
||||
Move m2 = TextIO.stringToMove(pos, "d4");
|
||||
assertEquals(0, hs.getHistScore(pos, m1));
|
||||
|
||||
hs.addSuccess(pos, m1, 1);
|
||||
assertEquals(1 * 49 / 1, hs.getHistScore(pos, m1));
|
||||
assertEquals(0, hs.getHistScore(pos, m2));
|
||||
|
||||
hs.addSuccess(pos, m1, 1);
|
||||
assertEquals(1 * 49 / 1, hs.getHistScore(pos, m1));
|
||||
assertEquals(0, hs.getHistScore(pos, m2));
|
||||
|
||||
hs.addFail(pos, m1, 1);
|
||||
assertEquals(2 * 49 / 3, hs.getHistScore(pos, m1));
|
||||
assertEquals(0, hs.getHistScore(pos, m2));
|
||||
|
||||
hs.addFail(pos, m1, 1);
|
||||
assertEquals(2 * 49 / 4, hs.getHistScore(pos, m1));
|
||||
assertEquals(0, hs.getHistScore(pos, m2));
|
||||
|
||||
hs.addSuccess(pos, m2, 1);
|
||||
assertEquals(2 * 49 / 4, hs.getHistScore(pos, m1));
|
||||
assertEquals(1 * 49 / 1, hs.getHistScore(pos, m2));
|
||||
}
|
||||
}
|
95
CuckooChessEngine/test/chess/KillerTableTest.java
Normal file
95
CuckooChessEngine/test/chess/KillerTableTest.java
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class KillerTableTest {
|
||||
|
||||
public KillerTableTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addKiller method, of class KillerTable.
|
||||
*/
|
||||
@Test
|
||||
public void testAddKiller() {
|
||||
System.out.println("addKiller");
|
||||
KillerTable kt = new KillerTable();
|
||||
Move m = new Move(TextIO.getSquare("b1"), TextIO.getSquare("b5"), Piece.EMPTY);
|
||||
kt.addKiller(3, m);
|
||||
kt.addKiller(7, m);
|
||||
kt.addKiller(3, m);
|
||||
kt.addKiller(3, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getKillerScore method, of class KillerTable.
|
||||
*/
|
||||
@Test
|
||||
public void testGetKillerScore() {
|
||||
System.out.println("getKillerScore");
|
||||
KillerTable kt = new KillerTable();
|
||||
Move m1 = new Move(TextIO.getSquare("b1"), TextIO.getSquare("b5"), Piece.EMPTY);
|
||||
Move m2 = new Move(TextIO.getSquare("c1"), TextIO.getSquare("d2"), Piece.EMPTY);
|
||||
Move m3 = new Move(TextIO.getSquare("e1"), TextIO.getSquare("g1"), Piece.EMPTY);
|
||||
kt.addKiller(0, m1);
|
||||
assertEquals(4, kt.getKillerScore(0, m1));
|
||||
assertEquals(0, kt.getKillerScore(0, m2));
|
||||
assertEquals(0, kt.getKillerScore(0, new Move(m2)));
|
||||
kt.addKiller(0, m1);
|
||||
assertEquals(4, kt.getKillerScore(0, m1));
|
||||
kt.addKiller(0, m2);
|
||||
assertEquals(4, kt.getKillerScore(0, m2));
|
||||
assertEquals(4, kt.getKillerScore(0, new Move(m2))); // Must compare by value
|
||||
assertEquals(3, kt.getKillerScore(0, m1));
|
||||
kt.addKiller(0, new Move(m2));
|
||||
assertEquals(4, kt.getKillerScore(0, m2));
|
||||
assertEquals(3, kt.getKillerScore(0, m1));
|
||||
assertEquals(0, kt.getKillerScore(0, m3));
|
||||
kt.addKiller(0, m3);
|
||||
assertEquals(0, kt.getKillerScore(0, m1));
|
||||
assertEquals(3, kt.getKillerScore(0, m2));
|
||||
assertEquals(4, kt.getKillerScore(0, m3));
|
||||
|
||||
assertEquals(0, kt.getKillerScore(1, m3));
|
||||
assertEquals(2, kt.getKillerScore(2, m3));
|
||||
assertEquals(0, kt.getKillerScore(3, m3));
|
||||
assertEquals(0, kt.getKillerScore(4, m3));
|
||||
|
||||
kt.addKiller(2, m2);
|
||||
assertEquals(4, kt.getKillerScore(2, m2));
|
||||
assertEquals(3, kt.getKillerScore(0, m2));
|
||||
}
|
||||
}
|
543
CuckooChessEngine/test/chess/MoveGenTest.java
Normal file
543
CuckooChessEngine/test/chess/MoveGenTest.java
Normal file
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class MoveGenTest {
|
||||
|
||||
public MoveGenTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of pseudoLegalMoves method, of class MoveGen.
|
||||
*/
|
||||
@Test
|
||||
public void testPseudoLegalMoves() throws ChessParseError {
|
||||
System.out.println("pseudoLegalMoves");
|
||||
String fen = "8/3k4/8/2n2pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 2";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
List<String> strMoves = getMoveList(pos, false);
|
||||
assertTrue(strMoves.contains("a1d1"));
|
||||
assertTrue(!strMoves.contains("a1e1"));
|
||||
assertTrue(!strMoves.contains("a1f1"));
|
||||
assertTrue(strMoves.contains("a1a7"));
|
||||
assertTrue(strMoves.contains("e1f2"));
|
||||
assertTrue(!strMoves.contains("e1g3"));
|
||||
assertTrue(strMoves.contains("c3f6"));
|
||||
assertTrue(!strMoves.contains("b3d2"));
|
||||
|
||||
// Test castling
|
||||
assertTrue(strMoves.contains("e1g1"));
|
||||
assertTrue(strMoves.contains("e1c1"));
|
||||
assertEquals(49, strMoves.size());
|
||||
|
||||
pos.setPiece(Position.getSquare(4,3), Piece.BROOK);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(!strMoves.contains("e1g1")); // In check, no castling possible
|
||||
assertTrue(!strMoves.contains("e1c1"));
|
||||
|
||||
pos.setPiece(Position.getSquare(4, 3), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(5, 3), Piece.BROOK);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(!strMoves.contains("e1g1")); // f1 attacked, short castle not possible
|
||||
assertTrue(strMoves.contains("e1c1"));
|
||||
|
||||
pos.setPiece(Position.getSquare(5, 3), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(6, 3), Piece.BBISHOP);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(strMoves.contains("e1g1")); // d1 attacked, long castle not possible
|
||||
assertTrue(!strMoves.contains("e1c1"));
|
||||
|
||||
pos.setPiece(Position.getSquare(6, 3), Piece.EMPTY);
|
||||
pos.setCastleMask(1 << Position.A1_CASTLE);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(!strMoves.contains("e1g1")); // short castle right has been lost
|
||||
assertTrue(strMoves.contains("e1c1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of pseudoLegalMoves method, of class MoveGen. Pawn moves.
|
||||
*/
|
||||
@Test
|
||||
public void testPawnMoves() throws ChessParseError {
|
||||
System.out.println("pawnMoves");
|
||||
String fen = "1r2k3/P1pppp1p/8/1pP3p1/1nPp2P1/n4p1P/1P2PP2/4KBNR w K b6 0 1";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
List<String> strMoves = getMoveList(pos, false);
|
||||
assertTrue(strMoves.contains("c5b6")); // En passant capture
|
||||
assertTrue(strMoves.contains("a7a8q")); // promotion
|
||||
assertTrue(strMoves.contains("a7a8n")); // under promotion
|
||||
assertTrue(strMoves.contains("a7b8r")); // capture promotion
|
||||
assertTrue(strMoves.contains("b2b3")); // pawn single move
|
||||
assertTrue(strMoves.contains("b2a3")); // pawn capture to the left
|
||||
assertTrue(strMoves.contains("e2e4")); // pawn double move
|
||||
assertTrue(strMoves.contains("e2f3")); // pawn capture to the right
|
||||
assertEquals(22, strMoves.size());
|
||||
|
||||
pos.setEpSquare(-1);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertEquals(21, strMoves.size()); // No ep, one less move possible
|
||||
|
||||
// Check black pawn moves
|
||||
pos.setWhiteMove(false);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(strMoves.contains("f3e2"));
|
||||
assertTrue(strMoves.contains("d4d3"));
|
||||
assertTrue(strMoves.contains("e7e6"));
|
||||
assertTrue(strMoves.contains("e7e5"));
|
||||
assertEquals(28, strMoves.size());
|
||||
|
||||
// Check black pawn promotion
|
||||
pos.setPiece(Position.getSquare(0,1), Piece.BPAWN);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertTrue(strMoves.contains("a2a1q"));
|
||||
assertTrue(strMoves.contains("a2a1r"));
|
||||
assertTrue(strMoves.contains("a2a1n"));
|
||||
assertTrue(strMoves.contains("a2a1b"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of inCheck method, of class MoveGen.
|
||||
*/
|
||||
@Test
|
||||
public void testInCheck() {
|
||||
System.out.println("inCheck");
|
||||
Position pos = new Position();
|
||||
pos.setPiece(Position.getSquare(4,2), Piece.WKING);
|
||||
pos.setPiece(Position.getSquare(4,7), Piece.BKING);
|
||||
assertEquals(false, MoveGen.inCheck(pos));
|
||||
|
||||
pos.setPiece(Position.getSquare(3,3), Piece.BQUEEN);
|
||||
assertEquals(true, MoveGen.inCheck(pos));
|
||||
pos.setPiece(Position.getSquare(3,3), Piece.BROOK);
|
||||
assertEquals(false, MoveGen.inCheck(pos));
|
||||
pos.setPiece(Position.getSquare(3,3), Piece.BPAWN);
|
||||
assertEquals(true, MoveGen.inCheck(pos));
|
||||
|
||||
pos.setPiece(Position.getSquare(3,3), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(5,3), Piece.WQUEEN);
|
||||
assertEquals(false, MoveGen.inCheck(pos));
|
||||
|
||||
pos.setPiece(Position.getSquare(4, 6), Piece.BROOK);
|
||||
assertEquals(true, MoveGen.inCheck(pos));
|
||||
pos.setPiece(Position.getSquare(4, 4), Piece.WPAWN);
|
||||
assertEquals(false, MoveGen.inCheck(pos));
|
||||
|
||||
pos.setPiece(Position.getSquare(2, 3), Piece.BKNIGHT);
|
||||
assertEquals(true, MoveGen.inCheck(pos));
|
||||
|
||||
pos.setPiece(Position.getSquare(2, 3), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(0, 4), Piece.BKNIGHT);
|
||||
assertEquals(false, MoveGen.inCheck(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of givesCheck method, of class MoveGen.
|
||||
*/
|
||||
@Test
|
||||
public void testGivesCheck() throws ChessParseError {
|
||||
System.out.println("givesCheck");
|
||||
Position pos = new Position();
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.setPiece(TextIO.getSquare("e3"), Piece.WKING);
|
||||
pos.setPiece(TextIO.getSquare("e8"), Piece.BKING);
|
||||
pos.setPiece(TextIO.getSquare("c2"), Piece.WROOK);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Rc8")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Rc6")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Rc7")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Re2")));
|
||||
|
||||
pos.setPiece(TextIO.getSquare("c2"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("e2"), Piece.WROOK);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Kd3")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Kd4")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Ke4")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Kf2")));
|
||||
|
||||
pos.setPiece(TextIO.getSquare("e4"), Piece.WBISHOP);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Bd5")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Bc6")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Kd3")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Re1")));
|
||||
|
||||
pos = TextIO.readFEN("4k3/3p4/8/8/4B3/2K5/4R3/8 w - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Bc6")));
|
||||
pos = TextIO.readFEN("4k3/8/5K2/8/6N1/8/8/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Ke6")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, new Move(TextIO.getSquare("f6"),
|
||||
TextIO.getSquare("e7"),
|
||||
Piece.EMPTY)));
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/8/4N3/8/2K3B1/8/8 w - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Kc4")));
|
||||
pos.setPiece(TextIO.getSquare("g3"), Piece.WROOK);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos.setPiece(TextIO.getSquare("g3"), Piece.WQUEEN);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos.setPiece(TextIO.getSquare("g3"), Piece.WKNIGHT);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos.setPiece(TextIO.getSquare("g3"), Piece.WPAWN);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos.setPiece(TextIO.getSquare("c3"), Piece.EMPTY);
|
||||
pos.setPiece(TextIO.getSquare("g3"), Piece.WKING);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/3p4/4N3/8/2K3B1/8/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/8/4N3/8/6q1/2K5/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos = TextIO.readFEN("8/2k5/8/4N3/8/8/2K5/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "Nf7")));
|
||||
pos = TextIO.readFEN("2nk4/3P4/8/8/3R4/8/2K5/8 w - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc8N")));
|
||||
|
||||
pos = TextIO.readFEN("8/2k5/2p5/1P1P4/8/2K5/8/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "d6")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "bxc6")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "b6")));
|
||||
|
||||
pos = TextIO.readFEN("8/8/R1PkP2R/8/8/2K5/8/8 w - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c7")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "e7")));
|
||||
|
||||
// Test pawn promotion
|
||||
pos = TextIO.readFEN("8/1P6/2kP4/8/8/2K5/8/8 w - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "d7")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "b8Q")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "b8N")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "b8R")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "b8B")));
|
||||
|
||||
pos = TextIO.readFEN("8/2P1P3/2k5/8/8/2K5/8/8 w - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "e8Q")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "e8N")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "e8R")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "e8B")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c8Q")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c8N")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c8R")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c8B")));
|
||||
|
||||
// Test castling
|
||||
pos = TextIO.readFEN("8/8/8/8/5k2/8/8/R3K2R w KQ - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
pos = TextIO.readFEN("8/8/8/8/6k1/8/8/R3K2R w KQ - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
pos = TextIO.readFEN("8/8/8/8/3k4/8/8/R3K2R w KQ - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
pos = TextIO.readFEN("8/8/8/8/5k2/8/5P2/R3K2R w KQ - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
pos = TextIO.readFEN("8/8/8/8/8/8/8/R3K2k w Q - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
pos = TextIO.readFEN("8/8/8/8/8/8/8/2k1K2R w K - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
pos.setPiece(TextIO.getSquare("d1"), Piece.WKNIGHT);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
|
||||
// Test en passant
|
||||
pos = TextIO.readFEN("8/1kp5/8/3P4/8/8/8/4K3 b - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
|
||||
pos = TextIO.readFEN("3k4/2p5/8/3P4/8/8/3R4/4K3 b - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
|
||||
pos = TextIO.readFEN("5k2/2p5/8/3P4/8/B7/8/4K3 b - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
|
||||
pos = TextIO.readFEN("5k2/2p5/8/3P4/1P6/B7/8/4K3 b - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
|
||||
pos = TextIO.readFEN("8/2p5/8/R2P1k2/8/8/8/4K3 b - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "dxc6")));
|
||||
|
||||
// Black pawn moves
|
||||
pos = TextIO.readFEN("8/2p5/8/R4k2/1K6/8/8/8 b - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c5")));
|
||||
pos = TextIO.readFEN("8/2p5/8/R4k2/2K5/8/8/8 b - - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c5")));
|
||||
pos = TextIO.readFEN("8/2p5/8/R4k2/3K4/8/8/8 b - - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "c5")));
|
||||
|
||||
// Black castling
|
||||
pos = TextIO.readFEN("r3k2r/8/8/5K2/8/8/8/8 b kq - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
pos = TextIO.readFEN("r3k2r/8/8/6K1/8/8/8/8 b kq - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O")));
|
||||
pos = TextIO.readFEN("r3k2r/8/8/2K5/8/8/8/8 b kq - 0 1");
|
||||
assertTrue(!MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
pos = TextIO.readFEN("r3k2r/8/8/3K4/8/8/8/8 b kq - 0 1");
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "O-O-O")));
|
||||
|
||||
// Black en passant
|
||||
pos = TextIO.readFEN("8/8/4k3/8/4p3/8/5PK1/8 w - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "f4"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "exf3")));
|
||||
|
||||
pos = TextIO.readFEN("8/8/4k3/8/K3p1r1/8/5P2/8 w - - 0 1");
|
||||
pos.makeMove(TextIO.stringToMove(pos, "f4"), ui);
|
||||
assertTrue(MoveGen.givesCheck(pos, TextIO.stringToMove(pos, "exf3")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of removeIllegal method, of class MoveGen.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveIllegal() throws ChessParseError {
|
||||
System.out.println("removeIllegal");
|
||||
Position pos = TextIO.readFEN("8/3k4/8/2n1rpP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 1");
|
||||
List<String> strMoves = getMoveList(pos, true);
|
||||
assertTrue(strMoves.contains("c2e4"));
|
||||
assertTrue(strMoves.contains("c3e5"));
|
||||
assertTrue(strMoves.contains("e1d1"));
|
||||
assertTrue(strMoves.contains("e1f1"));
|
||||
assertTrue(strMoves.contains("e1f2"));
|
||||
assertEquals(5, strMoves.size());
|
||||
|
||||
pos = TextIO.readFEN("4k3/8/8/2KPp1r1/8/8/8/8 w - e6 0 2");
|
||||
strMoves = getMoveList(pos, true);
|
||||
assertTrue(!strMoves.contains("d5e6"));
|
||||
assertEquals(7, strMoves.size());
|
||||
|
||||
pos = TextIO.readFEN("8/6p1/4p3/2k1Pp1B/4KP1p/6rP/8/8 w - f6 0 55");
|
||||
strMoves = getMoveList(pos, true);
|
||||
assertTrue(strMoves.contains("e5f6"));
|
||||
assertEquals(1, strMoves.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that if king capture is possible, only a king capture move is returned in the move list.
|
||||
*/
|
||||
@Test
|
||||
public void testKingCapture() throws ChessParseError {
|
||||
System.out.println("kingCapture");
|
||||
Position pos = TextIO.readFEN("8/4k3/8/8/8/8/8/4RK2 b - - 0 1");
|
||||
pos.setWhiteMove(true);
|
||||
List<String> strMoves = getMoveList(pos, false);
|
||||
assertEquals(1, strMoves.size());
|
||||
assertEquals("e1e7", strMoves.get(0));
|
||||
|
||||
pos.setPiece(Position.getSquare(0, 2), Piece.WBISHOP);
|
||||
pos.setPiece(Position.getSquare(4, 1), Piece.WPAWN);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertEquals(1, strMoves.size());
|
||||
assertEquals("a3e7", strMoves.get(0));
|
||||
|
||||
pos.setPiece(Position.getSquare(1, 3), Piece.WPAWN);
|
||||
pos.setPiece(Position.getSquare(5, 5), Piece.WPAWN);
|
||||
strMoves = getMoveList(pos, false);
|
||||
assertEquals(1, strMoves.size());
|
||||
assertEquals("f6e7", strMoves.get(0));
|
||||
}
|
||||
|
||||
/** Test that captureList and captureAndcheckList are generated correctly. */
|
||||
@Test
|
||||
public void testCaptureList() throws ChessParseError {
|
||||
System.out.println("captureList");
|
||||
Position pos = TextIO.readFEN("rnbqkbnr/ppp2ppp/3p1p2/R7/4N3/8/PPPPQPPP/2B1KB1R w Kkq - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("rnb1kbn1/ppp1qppp/5p2/4p3/3N3r/3P4/PPP2PPP/R1BQKB1R b KQq - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("rnb1k1n1/ppp1qppp/5p2/b3p3/1r1N4/3P4/PPP2PPP/R1BQKB1R b KQq - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("8/8/8/8/3k4/8/4P3/4K3 w - - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("8/8/8/3k4/8/8/4P3/4K3 w - - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("8/8/8/3k4/4p3/8/3KP3/8 b - - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("3k4/r2p2K1/8/8/8/8/8/8 b - - 0 1");
|
||||
getMoveList(pos, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckEvasions() throws ChessParseError {
|
||||
System.out.println("checkEvasions");
|
||||
Position pos = TextIO.readFEN("n7/8/8/7k/5pP1/5K2/8/8 b - g3 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
pos = TextIO.readFEN("rn1qkbnr/pppB1ppp/3p4/4p3/4P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 0 1");
|
||||
getMoveList(pos, false);
|
||||
|
||||
// King captures must be included in check evasions
|
||||
pos = TextIO.readFEN("r1bq2r1/pp3pbk/2p1p1P1/8/3P4/2PB1N2/PP3PPR/2KR4 b - - 0 1");
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.uciStringToMove("g7h6"), ui);
|
||||
getMoveList(pos, false);
|
||||
List<String> evList = getCheckEvasions(pos, false);
|
||||
assertTrue(evList.contains("g6h7"));
|
||||
|
||||
pos = TextIO.readFEN("1R6/1brk2p1/2P1p2p/p3Pp2/P7/6P1/1P4P1/2R3K1 b - - 0 1");
|
||||
getMoveList(pos, false);
|
||||
evList = getCheckEvasions(pos, false);
|
||||
assertTrue(evList.contains("b7c6"));
|
||||
}
|
||||
|
||||
private List<String> getMoveList(Position pos, boolean onlyLegal) {
|
||||
Position swap = EvaluateTest.swapColors(pos);
|
||||
List<String> swapList = getMoveList0(swap, onlyLegal);
|
||||
List<String> ret = getMoveList0(pos, onlyLegal);
|
||||
assertEquals(swapList.size(), ret.size());
|
||||
// FIXME! Test that swapList contains swapped moves compared to ret
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<String> getMoveList0(Position pos, boolean onlyLegal) {
|
||||
MoveGen moveGen = new MoveGen();
|
||||
MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
|
||||
if (onlyLegal)
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
ArrayList<String> strMoves = new ArrayList<String>();
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String mStr = TextIO.moveToUCIString(m);
|
||||
strMoves.add(mStr);
|
||||
}
|
||||
|
||||
List<String> capList1 = getCaptureList(pos, false, onlyLegal);
|
||||
assertTrue(strMoves.containsAll(capList1));
|
||||
|
||||
List<String> capList2 = getCaptureList(pos, true, onlyLegal);
|
||||
assertTrue(strMoves.containsAll(capList2));
|
||||
|
||||
List<String> evList = getCheckEvasions(pos, onlyLegal);
|
||||
if (evList != null)
|
||||
assertTrue(strMoves.containsAll(evList));
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (String sm : strMoves) {
|
||||
Move m = TextIO.uciStringToMove(sm);
|
||||
if (m != null) {
|
||||
pos.makeMove(m, ui);
|
||||
boolean invalid = MoveGen.canTakeKing(pos);
|
||||
pos.unMakeMove(m, ui);
|
||||
if (invalid) m = null;
|
||||
}
|
||||
if (m == null) // Move was illegal (but pseudo-legal)
|
||||
continue;
|
||||
boolean qProm = false; // Promotion types considered in qsearch
|
||||
switch (m.promoteTo) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN:
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT:
|
||||
case Piece.EMPTY:
|
||||
qProm = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!MoveGen.canTakeKing(pos) && MoveGen.givesCheck(pos, m)) {
|
||||
if (qProm)
|
||||
assertTrue(capList2.contains(sm));
|
||||
} else {
|
||||
switch (m.promoteTo) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN:
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT:
|
||||
assertTrue(capList1.contains(sm)); // All queen/knight promotions
|
||||
assertTrue(capList2.contains(sm)); // All queen/knight promotions
|
||||
break;
|
||||
case Piece.EMPTY:
|
||||
break;
|
||||
default:
|
||||
assertTrue(!capList1.contains(sm)); // No rook/bishop promotions
|
||||
assertTrue(!capList2.contains(sm)); // No rook/bishop promotions
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos.getPiece(m.to) != Piece.EMPTY) {
|
||||
if (qProm) {
|
||||
assertTrue(capList1.contains(sm));
|
||||
assertTrue(capList2.contains(sm));
|
||||
}
|
||||
}
|
||||
if (evList != null) {
|
||||
assertTrue(evList.contains(sm));
|
||||
}
|
||||
}
|
||||
|
||||
return strMoves;
|
||||
}
|
||||
|
||||
private List<String> getCaptureList(Position pos, boolean includeChecks, boolean onlyLegal) {
|
||||
MoveGen.MoveList moves;
|
||||
if (includeChecks) {
|
||||
moves = new MoveGen().pseudoLegalCapturesAndChecks(pos);
|
||||
} else {
|
||||
moves = new MoveGen().pseudoLegalCaptures(pos);
|
||||
}
|
||||
if (onlyLegal)
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
ArrayList<String> strMoves = new ArrayList<String>();
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String mStr = TextIO.moveToUCIString(m);
|
||||
strMoves.add(mStr);
|
||||
}
|
||||
return strMoves;
|
||||
}
|
||||
|
||||
private List<String> getCheckEvasions(Position pos, boolean onlyLegal) {
|
||||
if (!MoveGen.inCheck(pos))
|
||||
return null;
|
||||
MoveGen.MoveList moves = new MoveGen().checkEvasions(pos);
|
||||
if (onlyLegal)
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
ArrayList<String> strMoves = new ArrayList<String>();
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String mStr = TextIO.moveToUCIString(m);
|
||||
strMoves.add(mStr);
|
||||
}
|
||||
return strMoves;
|
||||
}
|
||||
}
|
84
CuckooChessEngine/test/chess/MoveTest.java
Normal file
84
CuckooChessEngine/test/chess/MoveTest.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class MoveTest {
|
||||
|
||||
public MoveTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of move constructor, of class Move.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveConstructor() {
|
||||
System.out.println("MoveTest");
|
||||
int f = Position.getSquare(4, 1);
|
||||
int t = Position.getSquare(4, 3);
|
||||
int p = Piece.WROOK;
|
||||
Move move = new Move(f, t, p);
|
||||
assertEquals(move.from, f);
|
||||
assertEquals(move.to,t);
|
||||
assertEquals(move.promoteTo, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of equals, of class Move.
|
||||
*/
|
||||
@Test
|
||||
public void testEquals() {
|
||||
System.out.println("equals");
|
||||
Move m1 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK);
|
||||
Move m2 = new Move(Position.getSquare(0, 6), Position.getSquare(0, 7), Piece.WROOK);
|
||||
Move m3 = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK);
|
||||
Move m4 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WKNIGHT);
|
||||
Move m5 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK);
|
||||
assertTrue(!m1.equals(m2));
|
||||
assertTrue(!m1.equals(m3));
|
||||
assertTrue(!m1.equals(m4));
|
||||
assertTrue(m1.equals(m5));
|
||||
}
|
||||
}
|
54
CuckooChessEngine/test/chess/PieceTest.java
Normal file
54
CuckooChessEngine/test/chess/PieceTest.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class PieceTest {
|
||||
|
||||
public PieceTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of isWhite method, of class Piece.
|
||||
*/
|
||||
@Test
|
||||
public void testIsWhite() {
|
||||
System.out.println("isWhite");
|
||||
assertEquals(false, Piece.isWhite(Piece.BBISHOP));
|
||||
assertEquals(true , Piece.isWhite(Piece.WBISHOP));
|
||||
assertEquals(true , Piece.isWhite(Piece.WKING));
|
||||
assertEquals(false, Piece.isWhite(Piece.BKING));
|
||||
}
|
||||
}
|
444
CuckooChessEngine/test/chess/PositionTest.java
Normal file
444
CuckooChessEngine/test/chess/PositionTest.java
Normal file
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class PositionTest {
|
||||
|
||||
public PositionTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getPiece method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testGetPiece() throws ChessParseError {
|
||||
System.out.println("getPiece");
|
||||
Position pos = new Position();
|
||||
int result = pos.getPiece(0);
|
||||
assertEquals(result, Piece.EMPTY);
|
||||
|
||||
pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
result = pos.getPiece(0);
|
||||
assertEquals(result, Piece.WROOK);
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
int p1 = pos.getPiece(Position.getSquare(x, y));
|
||||
int p2 = pos.getPiece(Position.getSquare(x, 7-y));
|
||||
int bwDiff = Piece.BPAWN - Piece.WPAWN;
|
||||
assertEquals(p2, p1 + bwDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getIndex method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIndex() {
|
||||
System.out.println("getIndex");
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
int sq = Position.getSquare(x, y);
|
||||
int x2 = Position.getX(sq);
|
||||
int y2 = Position.getY(sq);
|
||||
assertEquals(x, x2);
|
||||
assertEquals(y, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setPiece method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testSetPiece() {
|
||||
System.out.println("setPiece");
|
||||
Position instance = new Position();
|
||||
assertEquals(Piece.EMPTY, instance.getPiece(Position.getSquare(0, 0)));
|
||||
instance.setPiece(Position.getSquare(3, 4), Piece.WKING);
|
||||
assertEquals(Piece.WKING, instance.getPiece(Position.getSquare(3, 4)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of makeMove method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testMakeMove() throws ChessParseError {
|
||||
System.out.println("makeMove");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Position origPos = new Position(pos);
|
||||
assertTrue(pos.equals(origPos));
|
||||
Move move = new Move(Position.getSquare(4,1), Position.getSquare(4,3), Piece.EMPTY);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(pos.whiteMove, false);
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,1)));
|
||||
assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,3)));
|
||||
assertTrue(!pos.equals(origPos));
|
||||
int castleMask = (1 << Position.A1_CASTLE) |
|
||||
(1 << Position.H1_CASTLE) |
|
||||
(1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask,pos.getCastleMask());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertEquals(pos.whiteMove, true);
|
||||
assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,1)));
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,3)));
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 2";
|
||||
pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
origPos = new Position(pos);
|
||||
assertEquals(Position.getSquare(1,5), pos.getEpSquare());
|
||||
|
||||
// Test capture
|
||||
move = new Move(Position.getSquare(0, 3), Position.getSquare(1,4), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
assertEquals(Piece.WBISHOP, pos.getPiece(Position.getSquare(1,4)));
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(0,3)));
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
// Test castling
|
||||
move = new Move(Position.getSquare(4, 0), Position.getSquare(6,0), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(Piece.WROOK, pos.getPiece(Position.getSquare(5,0)));
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(7,0)));
|
||||
castleMask = (1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask,pos.getCastleMask());
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
// Test castling rights (king move)
|
||||
move = new Move(Position.getSquare(4, 0), Position.getSquare(4,1), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
castleMask = (1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask,pos.getCastleMask());
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
// Test castling rights (rook move)
|
||||
move = new Move(Position.getSquare(7, 0), Position.getSquare(6,0), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
castleMask = (1 << Position.A1_CASTLE) |
|
||||
(1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask,pos.getCastleMask());
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
// Test en passant
|
||||
move = new Move(Position.getSquare(2, 4), Position.getSquare(1,5), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(1,5)));
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(2,4)));
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,4)));
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos));
|
||||
|
||||
// Test castling rights loss when rook captured
|
||||
pos.setPiece(Position.getSquare(6,2), Piece.BKNIGHT);
|
||||
pos.setWhiteMove(false);
|
||||
Position origPos2 = new Position(pos);
|
||||
move = new Move(Position.getSquare(6,2), Position.getSquare(7,0), Piece.EMPTY);
|
||||
pos.makeMove(move, ui);
|
||||
castleMask = (1 << Position.A1_CASTLE) |
|
||||
(1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask,pos.getCastleMask());
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(pos.equals(origPos2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCastleMask() throws ChessParseError {
|
||||
System.out.println("castleMask");
|
||||
Position pos = TextIO.readFEN("rnbqk1nr/pppp1ppp/8/4p3/4P3/2N2N2/PPPP1bPP/R1BQKB1R w KQkq - 0 1");
|
||||
UndoInfo ui = new UndoInfo();
|
||||
Move m = TextIO.stringToMove(pos, "Kxf2");
|
||||
pos.makeMove(m, ui);
|
||||
int castleMask = (1 << Position.A8_CASTLE) |
|
||||
(1 << Position.H8_CASTLE);
|
||||
assertEquals(castleMask, pos.getCastleMask());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of makeMove method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testPromotion() throws ChessParseError {
|
||||
System.out.println("promotion");
|
||||
String fen = "r1bqk2r/1Pppbppp/p1n2n2/2P1p3/B3P3/5N2/Pp1P1PPP/R1BQK2R w KQkq - 0 1";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
Position origPos = new Position(pos);
|
||||
assertEquals(origPos, pos);
|
||||
|
||||
Move move = new Move(Position.getSquare(1, 6), Position.getSquare(0,7), Piece.WQUEEN);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6)));
|
||||
assertEquals(Piece.WQUEEN, pos.getPiece(Position.getSquare(0,7)));
|
||||
pos.unMakeMove(move, ui);
|
||||
assertEquals(origPos, pos);
|
||||
|
||||
move = new Move(Position.getSquare(1, 6), Position.getSquare(1,7), Piece.WKNIGHT);
|
||||
ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6)));
|
||||
assertEquals(Piece.WKNIGHT, pos.getPiece(Position.getSquare(1,7)));
|
||||
pos.unMakeMove(move, ui);
|
||||
assertEquals(origPos, pos);
|
||||
|
||||
pos.setWhiteMove(false);
|
||||
origPos = new Position(pos);
|
||||
|
||||
move = new Move(Position.getSquare(1, 1), Position.getSquare(2, 0), Piece.BROOK);
|
||||
ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,1)));
|
||||
assertEquals(Piece.BROOK, pos.getPiece(Position.getSquare(2,0)));
|
||||
pos.unMakeMove(move, ui);
|
||||
assertEquals(origPos, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test move counters, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveCounters() throws ChessParseError {
|
||||
System.out.println("moveCounters");
|
||||
String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 7";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
|
||||
Move move = TextIO.stringToMove(pos, "Nc3");
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(1, pos.halfMoveClock);
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
move = TextIO.stringToMove(pos, "O-O");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(1, pos.halfMoveClock); // Castling does not reset 50 move counter
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
move = TextIO.stringToMove(pos, "a3");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(0, pos.halfMoveClock); // Pawn move resets 50 move counter
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
move = TextIO.stringToMove(pos, "Nxe5");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(0, pos.halfMoveClock); // Capture move resets 50 move counter
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
move = TextIO.stringToMove(pos, "cxb6");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(0, pos.halfMoveClock); // EP capture move resets 50 move counter
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
move = TextIO.stringToMove(pos, "Kf1");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(1, pos.halfMoveClock); // Loss of castling rights does not reset 50 move counter
|
||||
assertEquals(7, pos.fullMoveCounter);
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
Move firstMove = TextIO.stringToMove(pos, "Nc3");
|
||||
UndoInfo firstUi = new UndoInfo();
|
||||
pos.makeMove(move, firstUi);
|
||||
move = TextIO.stringToMove(pos, "O-O");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(2, pos.halfMoveClock);
|
||||
assertEquals(8, pos.fullMoveCounter); // Black move increases fullMoveCounter
|
||||
pos.unMakeMove(move, ui);
|
||||
pos.unMakeMove(firstMove, firstUi);
|
||||
|
||||
fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68";
|
||||
pos = TextIO.readFEN(fen);
|
||||
move = TextIO.stringToMove(pos, "c1Q");
|
||||
pos.makeMove(move, ui);
|
||||
assertEquals(0, pos.halfMoveClock); // Pawn promotion resets 50 move counter
|
||||
assertEquals(69, pos.fullMoveCounter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of drawRuleEquals, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawRuleEquals() throws ChessParseError {
|
||||
System.out.println("drawRuleEquals");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Position origPos = new Position(pos);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nf3"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nf6"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Ng1"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Ng8"), ui);
|
||||
assertEquals(true, pos.drawRuleEquals(origPos));
|
||||
assertEquals(false, pos.equals(origPos)); // Move counters have changed
|
||||
|
||||
String fen = "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1";
|
||||
pos = TextIO.readFEN(fen);
|
||||
origPos = new Position(pos);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Ke2"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Be7"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Ke1"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Bf8"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, castling rights lost
|
||||
|
||||
pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c4"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "a6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "b5"), ui);
|
||||
assertEquals(Position.getSquare(1, 5), pos.getEpSquare());
|
||||
origPos = new Position(pos);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nc3"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nc6"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nb1"), ui);
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Nb8"), ui);
|
||||
assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, en passant rights lost
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hashCode method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testHashCode() throws ChessParseError {
|
||||
System.out.println("hashCode");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
long h1 = pos.zobristHash();
|
||||
assertEquals(h1, pos.computeZobristHash());
|
||||
UndoInfo ui = new UndoInfo();
|
||||
Move move = TextIO.stringToMove(pos, "e4");
|
||||
pos.makeMove(move, ui);
|
||||
assertTrue(h1 != pos.zobristHash());
|
||||
pos.unMakeMove(move, ui);
|
||||
assertTrue(h1 == pos.zobristHash());
|
||||
|
||||
pos.setWhiteMove(!pos.whiteMove);
|
||||
long h4 = pos.zobristHash();
|
||||
assertEquals(h4, pos.computeZobristHash());
|
||||
assertTrue(h1 != pos.zobristHash());
|
||||
pos.setWhiteMove(!pos.whiteMove);
|
||||
assertTrue(h1 == pos.zobristHash());
|
||||
|
||||
pos.setCastleMask(0);
|
||||
assertTrue(h1 != pos.zobristHash());
|
||||
|
||||
pos = TextIO.readFEN("rnbqkbnr/pppp1ppp/8/2P1p3/8/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1");
|
||||
h1 = pos.zobristHash();
|
||||
assertEquals(h1, pos.computeZobristHash());
|
||||
|
||||
String[] moves = {
|
||||
"b5", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "d5",
|
||||
"cxd6", "Qxd6", "h4", "Be6", "h5", "Nc6", "h6", "o-o-o", "hxg7", "Nf6", "gxh8Q", "Be7"
|
||||
};
|
||||
List<UndoInfo> uiList = new ArrayList<UndoInfo>();
|
||||
List<Long> hashList = new ArrayList<Long>();
|
||||
List<Move> moveList = new ArrayList<Move>();
|
||||
for (int i = 0; i < moves.length; i++) {
|
||||
uiList.add(new UndoInfo());
|
||||
Move m = TextIO.stringToMove(pos, moves[i]);
|
||||
moveList.add(m);
|
||||
pos.makeMove(m, uiList.get(i));
|
||||
long h = pos.zobristHash();
|
||||
assertEquals(h, pos.computeZobristHash());
|
||||
hashList.add(h);
|
||||
}
|
||||
assertTrue(!hashList.get(0).equals(hashList.get(4)));
|
||||
assertTrue(hashList.get(4).equals(hashList.get(8)));
|
||||
for (int i = moves.length - 1; i >= 0; i--) {
|
||||
pos.unMakeMove(moveList.get(i), uiList.get(i));
|
||||
long h = pos.zobristHash();
|
||||
assertEquals(h, pos.computeZobristHash());
|
||||
assertEquals(h, i > 0 ? hashList.get(i - 1) : h1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getKingSq method, of class Position.
|
||||
*/
|
||||
@Test
|
||||
public void testGetKingSq() throws ChessParseError {
|
||||
System.out.println("getKingSq");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true));
|
||||
assertEquals(TextIO.getSquare("e8"), pos.getKingSq(false));
|
||||
pos = TextIO.readFEN("r1bq1bnr/ppppkppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQ - 0 4");
|
||||
assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true));
|
||||
assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false));
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(TextIO.stringToMove(pos, "o-o"), ui);
|
||||
assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true));
|
||||
assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false));
|
||||
pos.makeMove(TextIO.stringToMove(pos, "Kd6"), ui);
|
||||
assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true));
|
||||
assertEquals(TextIO.getSquare("d6"), pos.getKingSq(false));
|
||||
}
|
||||
}
|
461
CuckooChessEngine/test/chess/SearchTest.java
Normal file
461
CuckooChessEngine/test/chess/SearchTest.java
Normal file
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import chess.Search.StopSearch;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class SearchTest {
|
||||
static final long[] nullHist = new long[200];
|
||||
static TranspositionTable tt = new TranspositionTable(19);
|
||||
|
||||
public SearchTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of negaScout method, of class Search.
|
||||
*/
|
||||
@Test
|
||||
public void testNegaScout() throws ChessParseError, StopSearch {
|
||||
System.out.println("negaScout");
|
||||
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);
|
||||
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
|
||||
int score2 = idSearch(sc, 2).score;
|
||||
assertEquals(score, score2);
|
||||
|
||||
pos = TextIO.readFEN("8/1P6/k7/2K5/8/8/8/8 w - - 0 1");
|
||||
sc = new Search(pos, nullHist, 0, tt);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
Move bestM = idSearch(sc, 2);
|
||||
assertTrue(!TextIO.moveToString(pos, bestM, false).equals("Qxb3"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of draw by 50 move rule, of class Search.
|
||||
*/
|
||||
@Test
|
||||
public void testDraw50() throws ChessParseError, StopSearch {
|
||||
System.out.println("draw50");
|
||||
final int mate0 = Search.MATE0;
|
||||
final int mateInOne = mate0 - 2;
|
||||
final int matedInOne = -mate0 + 3;
|
||||
final int mateInTwo = mate0 - 4;
|
||||
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);
|
||||
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.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.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.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.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.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.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.maxTimeMillis = -1;
|
||||
score = idSearch(sc, 6).score;
|
||||
assertEquals(mateInThree, score); // Need an extra pawn move to avoid 50-move rule
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of draw by repetition rule, of class Search.
|
||||
*/
|
||||
@Test
|
||||
public void testDrawRep() throws ChessParseError, StopSearch {
|
||||
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);
|
||||
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.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.maxTimeMillis = -1;
|
||||
score = idSearch(sc, 3).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.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.maxTimeMillis = -1;
|
||||
score = idSearch(sc, 7).score;
|
||||
assertEquals(0, score); // Draw, black can not escape from perpetual checks
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hash table, of class Search.
|
||||
*/
|
||||
@Test
|
||||
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);
|
||||
Move bestM = idSearch(sc, 28);
|
||||
assertEquals(TextIO.stringToMove(pos, "Kb1"), new Move(bestM));
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
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);
|
||||
bestM = idSearch(sc, 1);
|
||||
assertEquals(Search.MATE0 - 4, bestM.score);
|
||||
assertEquals(TextIO.stringToMove(pos, "Qxh7+"), new Move(bestM));
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
Move bestM = idSearch(sc, 3);
|
||||
assertEquals(0, bestM.score);
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
Move bestM = idSearch(sc, 9);
|
||||
assertEquals(Search.MATE0-18, bestM.score);
|
||||
}
|
||||
|
||||
private Move idSearch(Search sc, int maxDepth) {
|
||||
MoveGen.MoveList moves = new MoveGen().pseudoLegalMoves(sc.pos);
|
||||
MoveGen.removeIllegal(sc.pos, moves);
|
||||
sc.scoreMoveList(moves, 0);
|
||||
sc.timeLimit(-1, -1);
|
||||
Move bestM = sc.iterativeDeepening(moves, maxDepth, -1, false);
|
||||
return bestM;
|
||||
}
|
||||
|
||||
/** Compute SEE(m) and assure that signSEE and negSEE give matching results. */
|
||||
private int getSEE(Search sc, Move m) {
|
||||
int see = sc.SEE(m);
|
||||
boolean neg = sc.negSEE(m);
|
||||
assertEquals(neg, see < 0);
|
||||
int sign = sc.signSEE(m);
|
||||
if (sign > 0)
|
||||
assertTrue(see > 0);
|
||||
else if (sign == 0)
|
||||
assertEquals(0, see);
|
||||
else
|
||||
assertTrue(see < 0);
|
||||
return see;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of SEE method, of class Search.
|
||||
*/
|
||||
@Test
|
||||
public void testSEE() throws ChessParseError {
|
||||
System.out.println("SEE");
|
||||
final int pV = Evaluate.pV;
|
||||
final int nV = Evaluate.nV;
|
||||
final int bV = Evaluate.bV;
|
||||
final int rV = Evaluate.rV;
|
||||
|
||||
// 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);
|
||||
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")));
|
||||
assertEquals(pV - nV, getSEE(sc, TextIO.stringToMove(pos, "Nxa7")));
|
||||
assertEquals(pV - nV, getSEE(sc, TextIO.stringToMove(pos, "Nxd6")));
|
||||
assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "d5")));
|
||||
assertEquals(-bV, getSEE(sc, TextIO.stringToMove(pos, "Bf4")));
|
||||
assertEquals(-bV, getSEE(sc, TextIO.stringToMove(pos, "Bh6")));
|
||||
assertEquals(-rV, getSEE(sc, TextIO.stringToMove(pos, "Ra5")));
|
||||
assertEquals(-rV, getSEE(sc, TextIO.stringToMove(pos, "Ra6")));
|
||||
|
||||
pos.setWhiteMove(false);
|
||||
sc = new Search(pos, nullHist, 0, tt);
|
||||
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")));
|
||||
assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "exd4")));
|
||||
assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "Nxe4")));
|
||||
assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "Bxe4")));
|
||||
assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "d5")));
|
||||
assertEquals(-nV, getSEE(sc, TextIO.stringToMove(pos, "Nd5")));
|
||||
assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "a6")));
|
||||
|
||||
// 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);
|
||||
// 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")));
|
||||
// 1 3 1 1 3 1 3 3 5 5 5 9 9
|
||||
//-1 2 1 0 3 0 3 0 5 0 5 0 9
|
||||
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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
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);
|
||||
// 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);
|
||||
// 1 1 3
|
||||
// 1 0 3
|
||||
assertEquals(pV, getSEE(sc, TextIO.stringToMove(pos, "bxa6")));
|
||||
|
||||
|
||||
// Test king capture
|
||||
pos = TextIO.readFEN("8/8/8/4k3/r3P3/4K3/8/4R3 b - - 0 1");
|
||||
sc = new Search(pos, nullHist, 0, tt);
|
||||
// 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);
|
||||
// 1 5 5 99
|
||||
//-4 5 0 99
|
||||
assertEquals(pV - rV, getSEE(sc, TextIO.stringToMove(pos, "Rxe4+")));
|
||||
// 1 99
|
||||
//-98 99
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
int score1 = EvaluateTest.evalWhite(sc.pos);
|
||||
long h1 = sc.pos.zobristHash();
|
||||
assertEquals(0, getSEE(sc, TextIO.stringToMove(pos, "exf3")));
|
||||
int score2 = EvaluateTest.evalWhite(sc.pos);
|
||||
long h2 = sc.pos.zobristHash();
|
||||
assertEquals(score1, score2);
|
||||
assertEquals(h1, h2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of scoreMoveList method, of class Search.
|
||||
*/
|
||||
@Test
|
||||
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);
|
||||
MoveGen moveGen = new MoveGen();
|
||||
MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
|
||||
sc.scoreMoveList(moves, 0);
|
||||
for (int i = 0; i < moves.size; i++) {
|
||||
Search.selectBest(moves, i);
|
||||
if (i > 0) {
|
||||
int sc1 = moves.m[i - 1].score;
|
||||
int sc2 = moves.m[i].score;
|
||||
assertTrue(sc2 <= sc1);
|
||||
}
|
||||
}
|
||||
|
||||
moves = moveGen.pseudoLegalMoves(pos);
|
||||
moves.m[0].score = 17;
|
||||
moves.m[1].score = 666;
|
||||
moves.m[2].score = 4711;
|
||||
sc.scoreMoveList(moves, 0, 2);
|
||||
assertEquals(17, moves.m[0].score);
|
||||
assertEquals(666, moves.m[1].score);
|
||||
for (int i = 1; i < moves.size; i++) {
|
||||
Search.selectBest(moves, i);
|
||||
if (i > 1) {
|
||||
int sc1 = moves.m[i - 1].score;
|
||||
int sc2 = moves.m[i].score;
|
||||
assertTrue(sc2 <= sc1);
|
||||
}
|
||||
}
|
||||
|
||||
// The hashMove should be first in the list
|
||||
Move m = TextIO.stringToMove(pos, "Ra6");
|
||||
moves = moveGen.pseudoLegalMoves(pos);
|
||||
boolean res = Search.selectHashMove(moves, m);
|
||||
assertEquals(true, res);
|
||||
assertEquals(m, moves.m[0]);
|
||||
}
|
||||
}
|
398
CuckooChessEngine/test/chess/TextIOTest.java
Normal file
398
CuckooChessEngine/test/chess/TextIOTest.java
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class TextIOTest {
|
||||
|
||||
public TextIOTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of readFEN method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testReadFEN() throws ChessParseError {
|
||||
System.out.println("readFEN");
|
||||
String fen = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R b kq - 0 1";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
assertEquals(pos.getPiece(Position.getSquare(0, 3)), Piece.WQUEEN);
|
||||
assertEquals(pos.getPiece(Position.getSquare(4, 7)), Piece.BKING);
|
||||
assertEquals(pos.getPiece(Position.getSquare(4, 1)), Piece.WKING);
|
||||
assertEquals(pos.whiteMove, false);
|
||||
assertEquals(pos.a1Castle(), false);
|
||||
assertEquals(pos.h1Castle(), false);
|
||||
assertEquals(pos.a8Castle(), true);
|
||||
assertEquals(pos.h8Castle(), true);
|
||||
|
||||
fen = "8/3k4/8/5pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 1 2";
|
||||
pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
assertEquals(1, pos.halfMoveClock);
|
||||
assertEquals(2, pos.fullMoveCounter);
|
||||
|
||||
// Must have exactly one king
|
||||
boolean wasError = testFENParseError("8/8/8/8/8/8/8/kk1K4 w - - 0 1");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
// Must not be possible to capture the king
|
||||
wasError = testFENParseError("8/8/8/8/8/8/8/k1RK4 w - - 0 1");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
// Make sure bogus en passant square information is removed
|
||||
fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1";
|
||||
pos = TextIO.readFEN(fen);
|
||||
assertEquals(-1, pos.getEpSquare());
|
||||
|
||||
// Test for too many rows (slashes)
|
||||
wasError = testFENParseError("8/8/8/8/4k3/8/8/8/KBN5 w - - 0 1");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
// Test for too many columns
|
||||
wasError = testFENParseError("8K/8/8/8/4k3/8/8/8 w - - 0 1");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
// Pawns must not be on first/last rank
|
||||
wasError = testFENParseError("kp6/8/8/8/8/8/8/K7 w - - 0 1");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
wasError = testFENParseError("kr/pppp/8/8/8/8/8/KBR w");
|
||||
assertEquals(false, wasError); // OK not to specify castling flags and ep square
|
||||
|
||||
wasError = testFENParseError("k/8/8/8/8/8/8/K");
|
||||
assertEquals(true, wasError); // Error side to move not specified
|
||||
|
||||
wasError = testFENParseError("");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
wasError = testFENParseError(" |");
|
||||
assertEquals(true, wasError);
|
||||
|
||||
wasError = testFENParseError("1B1B4/6k1/7r/7P/6q1/r7/q7/7K b - - acn 6; acs 0;");
|
||||
assertEquals(false, wasError); // Extra stuff after FEN string is allowed
|
||||
}
|
||||
|
||||
/** Tests if trying to parse a FEN string causes an error. */
|
||||
private boolean testFENParseError(String fen) {
|
||||
boolean wasError;
|
||||
wasError = false;
|
||||
try {
|
||||
TextIO.readFEN(fen);
|
||||
} catch (ChessParseError err) {
|
||||
wasError = true;
|
||||
}
|
||||
return wasError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of moveToString method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveToString() throws ChessParseError {
|
||||
System.out.println("moveToString");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
assertEquals(TextIO.startPosFEN, TextIO.toFEN(pos));
|
||||
Move move = new Move(Position.getSquare(4, 1), Position.getSquare(4, 3),
|
||||
Piece.EMPTY);
|
||||
boolean longForm = true;
|
||||
String result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("e2-e4", result);
|
||||
|
||||
move = new Move(Position.getSquare(6, 0), Position.getSquare(5, 2), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Ng1-f3", result);
|
||||
|
||||
move = new Move(Position.getSquare(4, 7), Position.getSquare(2, 7),
|
||||
Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("O-O-O", result);
|
||||
|
||||
String fen = "1r3k2/2P5/8/8/8/4K3/8/8 w - - 0 1";
|
||||
pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
move = new Move(Position.getSquare(2,6), Position.getSquare(1,7), Piece.WROOK);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("c7xb8R+", result);
|
||||
|
||||
move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WKNIGHT);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("c7-c8N", result);
|
||||
|
||||
move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WQUEEN);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("c7-c8Q+", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of moveToString method, of class TextIO, mate/stalemate tests.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveToStringMate() throws ChessParseError {
|
||||
System.out.println("moveToStringMate");
|
||||
Position pos = TextIO.readFEN("3k4/1PR5/3N4/8/4K3/8/8/8 w - - 0 1");
|
||||
boolean longForm = true;
|
||||
|
||||
Move move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK);
|
||||
String result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("b7-b8R+", result); // check
|
||||
|
||||
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WQUEEN);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("b7-b8Q#", result); // check mate
|
||||
|
||||
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WKNIGHT);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("b7-b8N", result);
|
||||
|
||||
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WBISHOP);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("b7-b8B", result); // stalemate
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of moveToString method, of class TextIO, short form.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveToStringShortForm() throws ChessParseError {
|
||||
System.out.println("moveToStringShortForm");
|
||||
String fen = "r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1";
|
||||
Position pos = TextIO.readFEN(fen);
|
||||
assertEquals(fen, TextIO.toFEN(pos));
|
||||
boolean longForm = false;
|
||||
|
||||
Move move = new Move(Position.getSquare(4,5), Position.getSquare(4,3), Piece.EMPTY);
|
||||
String result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Qee4", result); // File disambiguation needed
|
||||
|
||||
move = new Move(Position.getSquare(2,5), Position.getSquare(4,3), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Qc6e4", result); // Full disambiguation needed
|
||||
|
||||
move = new Move(Position.getSquare(2,3), Position.getSquare(4,3), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Q4e4", result); // Row disambiguation needed
|
||||
|
||||
move = new Move(Position.getSquare(2,3), Position.getSquare(2,0), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Qc1+", result); // No disambiguation needed
|
||||
|
||||
move = new Move(Position.getSquare(0,1), Position.getSquare(0,0), Piece.BQUEEN);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("a1Q", result); // Normal promotion
|
||||
|
||||
move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BQUEEN);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("axb1Q#", result); // Capture promotion and check mate
|
||||
|
||||
move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BKNIGHT);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("axb1N", result); // Capture promotion
|
||||
|
||||
move = new Move(Position.getSquare(3,6), Position.getSquare(4,4), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Ne5", result); // Other knight pinned, no disambiguation needed
|
||||
|
||||
move = new Move(Position.getSquare(7,6), Position.getSquare(7,4), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("h5", result); // Regular pawn move
|
||||
|
||||
move = new Move(Position.getSquare(5,7), Position.getSquare(3,7), Piece.EMPTY);
|
||||
result = TextIO.moveToString(pos, move, longForm);
|
||||
assertEquals("Rfd8", result); // File disambiguation needed
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of stringToMove method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testStringToMove() throws ChessParseError {
|
||||
System.out.println("stringToMove");
|
||||
Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1");
|
||||
|
||||
Move mNe5 = new Move(Position.getSquare(3, 6), Position.getSquare(4, 4), Piece.EMPTY);
|
||||
Move m = TextIO.stringToMove(pos, "Ne5");
|
||||
assertEquals(mNe5, m);
|
||||
m = TextIO.stringToMove(pos, "ne");
|
||||
assertEquals(mNe5, m);
|
||||
m = TextIO.stringToMove(pos, "N");
|
||||
assertEquals(null, m);
|
||||
|
||||
Move mQc6e4 = new Move(Position.getSquare(2, 5), Position.getSquare(4, 3), Piece.EMPTY);
|
||||
m = TextIO.stringToMove(pos, "Qc6-e4");
|
||||
assertEquals(mQc6e4, m);
|
||||
m = TextIO.stringToMove(pos, "Qc6e4");
|
||||
assertEquals(mQc6e4, m);
|
||||
m = TextIO.stringToMove(pos, "Qce4");
|
||||
assertEquals(null, m);
|
||||
m = TextIO.stringToMove(pos, "Q6e4");
|
||||
assertEquals(null, m);
|
||||
|
||||
Move maxb1Q = new Move(Position.getSquare(0, 1), Position.getSquare(1, 0), Piece.BQUEEN);
|
||||
m = TextIO.stringToMove(pos, "axb1Q");
|
||||
assertEquals(maxb1Q, m);
|
||||
m = TextIO.stringToMove(pos, "axb1Q#");
|
||||
assertEquals(maxb1Q, m);
|
||||
m = TextIO.stringToMove(pos, "axb1Q+");
|
||||
assertEquals(null, m);
|
||||
|
||||
Move mh5= new Move(Position.getSquare(7, 6), Position.getSquare(7, 4), Piece.EMPTY);
|
||||
m = TextIO.stringToMove(pos, "h5");
|
||||
assertEquals(mh5, m);
|
||||
m = TextIO.stringToMove(pos, "h7-h5");
|
||||
assertEquals(mh5, m);
|
||||
m = TextIO.stringToMove(pos, "h");
|
||||
assertEquals(null, m);
|
||||
|
||||
pos = TextIO.readFEN("r1b1k2r/1pqpppbp/p5pn/3BP3/8/2pP4/PPPBQPPP/R3K2R w KQkq - 0 12");
|
||||
m = TextIO.stringToMove(pos, "bxc3");
|
||||
assertEquals(TextIO.getSquare("b2"), m.from);
|
||||
m = TextIO.stringToMove(pos, "Bxc3");
|
||||
assertEquals(TextIO.getSquare("d2"), m.from);
|
||||
m = TextIO.stringToMove(pos, "bxc");
|
||||
assertEquals(TextIO.getSquare("b2"), m.from);
|
||||
m = TextIO.stringToMove(pos, "Bxc");
|
||||
assertEquals(TextIO.getSquare("d2"), m.from);
|
||||
|
||||
// Test castling. o-o is a substring of o-o-o, which could cause problems.
|
||||
pos = TextIO.readFEN("5k2/p1pQn3/1p2Bp1r/8/4P1pN/2N5/PPP2PPP/R3K2R w KQ - 0 16");
|
||||
Move kCastle = new Move(Position.getSquare(4,0), Position.getSquare(6,0), Piece.EMPTY);
|
||||
Move qCastle = new Move(Position.getSquare(4,0), Position.getSquare(2,0), Piece.EMPTY);
|
||||
m = TextIO.stringToMove(pos, "o");
|
||||
assertEquals(null, m);
|
||||
m = TextIO.stringToMove(pos, "o-o");
|
||||
assertEquals(kCastle, m);
|
||||
m = TextIO.stringToMove(pos, "O-O");
|
||||
assertEquals(kCastle, m);
|
||||
m = TextIO.stringToMove(pos, "o-o-o");
|
||||
assertEquals(qCastle, m);
|
||||
|
||||
// Test 'o-o+'
|
||||
pos.setPiece(Position.getSquare(5,1), Piece.EMPTY);
|
||||
pos.setPiece(Position.getSquare(5,5), Piece.EMPTY);
|
||||
m = TextIO.stringToMove(pos, "o");
|
||||
assertEquals(null, m);
|
||||
m = TextIO.stringToMove(pos, "o-o");
|
||||
assertEquals(kCastle, m);
|
||||
m = TextIO.stringToMove(pos, "o-o-o");
|
||||
assertEquals(qCastle, m);
|
||||
m = TextIO.stringToMove(pos, "o-o+");
|
||||
assertEquals(kCastle, m);
|
||||
|
||||
// Test d8=Q+ syntax
|
||||
pos = TextIO.readFEN("1r3r2/2kP2Rp/p1bN1p2/2p5/5P2/2P5/P5PP/3R2K1 w - -");
|
||||
m = TextIO.stringToMove(pos, "d8=Q+");
|
||||
Move m2 = TextIO.stringToMove(pos, "d8Q");
|
||||
assertEquals(m2, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getSquare method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testGetSquare() throws ChessParseError {
|
||||
System.out.println("getSquare");
|
||||
assertEquals(Position.getSquare(0, 0), TextIO.getSquare("a1"));
|
||||
assertEquals(Position.getSquare(1, 7), TextIO.getSquare("b8"));
|
||||
assertEquals(Position.getSquare(3, 3), TextIO.getSquare("d4"));
|
||||
assertEquals(Position.getSquare(4, 3), TextIO.getSquare("e4"));
|
||||
assertEquals(Position.getSquare(3, 1), TextIO.getSquare("d2"));
|
||||
assertEquals(Position.getSquare(7, 7), TextIO.getSquare("h8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of squareToString method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testSquareToString() {
|
||||
System.out.println("squareToString");
|
||||
assertEquals("a1", TextIO.squareToString(Position.getSquare(0, 0)));
|
||||
assertEquals("h6", TextIO.squareToString(Position.getSquare(7, 5)));
|
||||
assertEquals("e4", TextIO.squareToString(Position.getSquare(4, 3)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of asciiBoard method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testAsciiBoard() throws ChessParseError {
|
||||
System.out.println("asciiBoard");
|
||||
Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1");
|
||||
String aBrd = TextIO.asciiBoard(pos);
|
||||
// System.out.print(aBrd);
|
||||
assertEquals(12, aBrd.length() - aBrd.replaceAll("\\*", "").length()); // 12 black pieces
|
||||
assertEquals(3, aBrd.length() - aBrd.replaceAll("\\*Q", " ").length()); // 3 black queens
|
||||
assertEquals(3, aBrd.length() - aBrd.replaceAll(" P", " ").length()); // 3 white pawns
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of uciStringToMove method, of class TextIO.
|
||||
*/
|
||||
@Test
|
||||
public void testUciStringToMove() throws ChessParseError {
|
||||
System.out.println("stringToMove");
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Move m = TextIO.uciStringToMove("e2e4");
|
||||
assertEquals(TextIO.stringToMove(pos, "e4"), m);
|
||||
m = TextIO.uciStringToMove("e2e5");
|
||||
assertEquals(new Move(12, 12+8*3, Piece.EMPTY), m);
|
||||
|
||||
m = TextIO.uciStringToMove("e2e5q");
|
||||
assertEquals(null, m);
|
||||
|
||||
m = TextIO.uciStringToMove("e7e8q");
|
||||
assertEquals(Piece.WQUEEN, m.promoteTo);
|
||||
m = TextIO.uciStringToMove("e7e8r");
|
||||
assertEquals(Piece.WROOK, m.promoteTo);
|
||||
m = TextIO.uciStringToMove("e7e8b");
|
||||
assertEquals(Piece.WBISHOP, m.promoteTo);
|
||||
m = TextIO.uciStringToMove("e2e1n");
|
||||
assertEquals(Piece.BKNIGHT, m.promoteTo);
|
||||
m = TextIO.uciStringToMove("e7e8x");
|
||||
assertEquals(null, m); // Invalid promotion piece
|
||||
m = TextIO.uciStringToMove("i1i3");
|
||||
assertEquals(null, m); // Outside board
|
||||
}
|
||||
}
|
162
CuckooChessEngine/test/chess/TranspositionTableTest.java
Normal file
162
CuckooChessEngine/test/chess/TranspositionTableTest.java
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package chess;
|
||||
|
||||
import chess.TranspositionTable.TTEntry;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author petero
|
||||
*/
|
||||
public class TranspositionTableTest {
|
||||
|
||||
public TranspositionTableTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of TTEntry nested class, of class TranspositionTable.
|
||||
*/
|
||||
@Test
|
||||
public void testTTEntry() throws ChessParseError {
|
||||
System.out.println("TTEntry");
|
||||
final int mate0 = Search.MATE0;
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
Move move = TextIO.stringToMove(pos, "e4");
|
||||
|
||||
// Test "normal" (non-mate) score
|
||||
int score = 17;
|
||||
int ply = 3;
|
||||
TTEntry ent1 = new TTEntry();
|
||||
ent1.key = 1;
|
||||
ent1.setMove(move);
|
||||
ent1.setScore(score, ply);
|
||||
ent1.setDepth(3);
|
||||
ent1.generation = 0;
|
||||
ent1.type = TranspositionTable.TTEntry.T_EXACT;
|
||||
ent1.setHashSlot(0);
|
||||
Move tmpMove = new Move(0,0,0);
|
||||
ent1.getMove(tmpMove);
|
||||
assertEquals(move, tmpMove);
|
||||
assertEquals(score, ent1.getScore(ply));
|
||||
assertEquals(score, ent1.getScore(ply + 3)); // Non-mate score, should be ply-independent
|
||||
|
||||
// Test positive mate score
|
||||
TTEntry ent2 = new TTEntry();
|
||||
score = mate0 - 6;
|
||||
ply = 3;
|
||||
ent2.key = 3;
|
||||
move = new Move(8, 0, Piece.BQUEEN);
|
||||
ent2.setMove(move);
|
||||
ent2.setScore(score, ply);
|
||||
ent2.setDepth(99);
|
||||
ent2.generation = 0;
|
||||
ent2.type = TranspositionTable.TTEntry.T_EXACT;
|
||||
ent2.setHashSlot(0);
|
||||
ent2.getMove(tmpMove);
|
||||
assertEquals(move, tmpMove);
|
||||
assertEquals(score, ent2.getScore(ply));
|
||||
assertEquals(score + 2, ent2.getScore(ply - 2));
|
||||
|
||||
// Compare ent1 and ent2
|
||||
assertTrue(!ent1.betterThan(ent2, 0)); // More depth is good
|
||||
assertTrue(ent2.betterThan(ent1, 0));
|
||||
|
||||
ent2.generation = 1;
|
||||
assertTrue(!ent2.betterThan(ent1, 0)); // ent2 has wrong generation
|
||||
assertTrue(ent2.betterThan(ent1, 1)); // ent1 has wrong generation
|
||||
|
||||
ent2.generation = 0;
|
||||
ent1.setDepth(7); ent2.setDepth(7);
|
||||
ent1.type = TranspositionTable.TTEntry.T_GE;
|
||||
assertTrue(ent2.betterThan(ent1, 0));
|
||||
ent2.type = TranspositionTable.TTEntry.T_LE;
|
||||
assertTrue(!ent2.betterThan(ent1, 0)); // T_GE is equally good as T_LE
|
||||
assertTrue(!ent1.betterThan(ent2, 0));
|
||||
|
||||
// Test negative mate score
|
||||
TTEntry ent3 = new TTEntry();
|
||||
score = -mate0 + 5;
|
||||
ply = 3;
|
||||
ent3.key = 3;
|
||||
move = new Move(8, 0, Piece.BQUEEN);
|
||||
ent3.setMove(move);
|
||||
ent3.setScore(score, ply);
|
||||
ent3.setDepth(99);
|
||||
ent3.generation = 0;
|
||||
ent3.type = TranspositionTable.TTEntry.T_EXACT;
|
||||
ent3.setHashSlot(0);
|
||||
ent3.getMove(tmpMove);
|
||||
assertEquals(move, tmpMove);
|
||||
assertEquals(score, ent3.getScore(ply));
|
||||
assertEquals(score - 2, ent3.getScore(ply - 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of insert method, of class TranspositionTable.
|
||||
*/
|
||||
@Test
|
||||
public void testInsert() throws ChessParseError {
|
||||
System.out.println("insert");
|
||||
TranspositionTable tt = new TranspositionTable(16);
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
String[] moves = {
|
||||
"e4", "e5", "Nf3", "Nc6", "Bb5", "a6", "Ba4", "b5", "Bb3", "Nf6", "O-O", "Be7", "Re1"
|
||||
};
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (int i = 0; i < moves.length; i++) {
|
||||
Move m = TextIO.stringToMove(pos, moves[i]);
|
||||
pos.makeMove(m, ui);
|
||||
int score = i * 17 + 3;
|
||||
m.score = score;
|
||||
int type = TranspositionTable.TTEntry.T_EXACT;
|
||||
int ply = i + 1;
|
||||
int depth = i * 2 + 5;
|
||||
tt.insert(pos.historyHash(), m, type, ply, depth, score * 2 + 3);
|
||||
}
|
||||
|
||||
pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
for (int i = 0; i < moves.length; i++) {
|
||||
Move m = TextIO.stringToMove(pos, moves[i]);
|
||||
pos.makeMove(m, ui);
|
||||
TranspositionTable.TTEntry ent = tt.probe(pos.historyHash());
|
||||
assertEquals(TranspositionTable.TTEntry.T_EXACT, ent.type);
|
||||
int score = i * 17 + 3;
|
||||
int ply = i + 1;
|
||||
int depth = i * 2 + 5;
|
||||
assertEquals(score, ent.getScore(ply));
|
||||
assertEquals(depth, ent.getDepth());
|
||||
assertEquals(score * 2 + 3, ent.evalScore);
|
||||
Move tmpMove = new Move(0,0,0);
|
||||
ent.getMove(tmpMove);
|
||||
assertEquals(m, tmpMove);
|
||||
}
|
||||
}
|
||||
}
|
47
CuckooChessEngine/test/guibase/ChessControllerTest.java
Normal file
47
CuckooChessEngine/test/guibase/ChessControllerTest.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package guibase;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import chess.ChessParseError;
|
||||
import chess.Piece;
|
||||
import chess.TextIO;
|
||||
|
||||
public class ChessControllerTest {
|
||||
|
||||
@Test
|
||||
public final void testSetPGN() throws ChessParseError {
|
||||
ChessController ctrl = new ChessController(null);
|
||||
ctrl.newGame(true, 8, false);
|
||||
ctrl.setPGN("[FEN \"k/8/8/8/8/8/KP/8 w\"]\n");
|
||||
assertEquals(TextIO.getSquare("a2"), ctrl.game.pos.getKingSq(true));
|
||||
assertEquals(TextIO.getSquare("a8"), ctrl.game.pos.getKingSq(false));
|
||||
|
||||
ctrl.setPGN("1.e4 e5 2. Nf3!!! $6 (Nc3 (a3)) Nc6?? Bb5!!? a6?! * Ba4");
|
||||
assertEquals(Piece.BPAWN, ctrl.game.pos.getPiece(TextIO.getSquare("a6")));
|
||||
assertEquals(Piece.WBISHOP, ctrl.game.pos.getPiece(TextIO.getSquare("b5")));
|
||||
assertEquals(Piece.EMPTY, ctrl.game.pos.getPiece(TextIO.getSquare("a4")));
|
||||
|
||||
ctrl.setPGN("[FEN \"r1bq1rk1/pp3ppp/2n1pn2/6B1/1bBP4/2N2N2/PPQ2PPP/R3K2R w KQ - 1 10\"]\n");
|
||||
assertEquals(10, ctrl.game.pos.fullMoveCounter);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user