[LIB-9] Add original chesshog source files
authorgotty <gotty@hedgecode.org>
Fri, 7 Dec 2018 19:39:58 +0000 (22:39 +0300)
committergotty <gotty@hedgecode.org>
Fri, 7 Dec 2018 19:39:58 +0000 (22:39 +0300)
129 files changed:
pom.xml [new file with mode: 0644]
src/main/java/org/hedgecode/chess/Builders.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ChessHogApp.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ChessHogConstants.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/Parsers.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ascii/ASCII.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ascii/ASCIIBoardType.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ascii/ASCIIBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/ascii/ASCIIParser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/fen/FEN.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/fen/FENBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/fen/FENParser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/AbstractMove.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/Game.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/GameMove.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/GameMoves.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/Move.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/game/Moves.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/HedgefishCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngine.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngineRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/HedgefishTransmitter.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/Types.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/hedgefish/UCI.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/img/DiagramBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/img/ImageBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/img/ImageFormat.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/pgn/PGNBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/pgn/PGNParser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Builder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Castle.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Color.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/ColorPiece.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/DiagramPosition.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/GamePosition.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/ParseException.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Parser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Piece.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Position.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/PositionType.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Positions.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/position/Square.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcd/TCD.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcd/TCDBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcd/TCDParser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcg/TCG.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcg/TCGBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/tcg/TCGParser.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/Acceptor.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/AcceptorException.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/AcceptorRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/AcceptorStub.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/AcceptorStubRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/CommandDescriptor.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/CommandEnvironment.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/CommandException.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/CommandExecutor.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/ConsoleAcceptor.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/ConsoleAcceptorRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/DebugMode.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/Engine.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/EngineException.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/EngineRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/ExternalEngine.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/ExternalEngineRunner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/Receiver.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/Runner.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/SyncCommandExecutor.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/Transmitter.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/UCICommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/UCIConstants.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/annotation/Command.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/annotation/CommandDirection.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/AbstractCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/CommandParams.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/InfoParams.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/Move.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/OptionParams.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/OptionType.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/BestMoveCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/CopyProtectionCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/IdCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/InfoCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/OkCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/OptionCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/ReadyOkCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/RegistrationCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/from/TerminateCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/DebugCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/GoCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/InitCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/IsReadyCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/NewGameCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/PonderHitCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/PositionCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/QuitCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/RegisterCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/SetOptionCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/uci/command/to/StopCommand.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/wiki/Wiki.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/wiki/WikiBuilder.java [new file with mode: 0644]
src/main/java/org/hedgecode/chess/wiki/WikiParser.java [new file with mode: 0644]
src/main/resources/org/hedgecode/chess/LocalStrings.properties [new file with mode: 0644]
src/main/resources/org/hedgecode/chess/LocalStrings_ru.properties [new file with mode: 0644]
src/main/resources/org/hedgecode/chess/uci/LocalStrings.properties [new file with mode: 0644]
src/main/resources/org/hedgecode/chess/uci/LocalStrings_ru.properties [new file with mode: 0644]
src/test/java/org/hedgecode/chess/AbstractPositionTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/AbstractXMLConfigTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/ascii/ASCIIBuilderTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/ascii/ASCIIParserTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/ascii/ASCIIPositionTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/fen/FENBuilderTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/fen/FENParserTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/fen/FENPositionTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/tcd/TCDBuilderTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/tcd/TCDParserTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/tcd/TCDPositionTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/uci/command/AbstractCommandTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/wiki/WikiBuilderTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/wiki/WikiParserTest.java [new file with mode: 0644]
src/test/java/org/hedgecode/chess/wiki/WikiPositionTest.java [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/ascii/ASCIIBuilderTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/ascii/ASCIIParserTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/fen/FENBuilderTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/fen/FENParserTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/tcd/TCDBuilderTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/tcd/TCDParserTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/wiki/WikiBuilderTest.xml [new file with mode: 0644]
src/test/resources/org/hedgecode/chess/wiki/WikiParserTest.xml [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..55e3409
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,236 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.hedgecode.maven</groupId>
+        <artifactId>lib-parent</artifactId>
+        <version>2</version>
+        <relativePath>../lib-parent/pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.hedgecode.chess</groupId>
+    <artifactId>chesshog</artifactId>
+    <version>0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>Hedgecode Chess</name>
+    <description>
+        Hedgecode Chess.
+    </description>
+    <inceptionYear>2018</inceptionYear>
+
+    <url>http://lib.hedgecode.org/${project.artifactId}/</url>
+
+    <scm>
+        <connection>scm:svn:http://svn.hedgecode.org/lib/${project.artifactId}/trunk/</connection>
+        <developerConnection>scm:svn:https://svn.hedgecode.org/lib/${project.artifactId}/trunk/</developerConnection>
+        <url>http://svn.hedgecode.org/lib/${project.artifactId}/trunk/</url>
+    </scm>
+
+    <issueManagement>
+        <system>JIRA</system>
+        <url>http://issues.hedgecode.org/browse/${issueKey}/component/${issueComponentId}</url>
+    </issueManagement>
+
+    <distributionManagement>
+        <site>
+            <id>hedgecode.website</id>
+            <name>Hedgecode Libs Website</name>
+            <url>dav:https://hedgecode.org/libs/${project.artifactId}/</url>
+        </site>
+    </distributionManagement>
+
+    <properties>
+        <junitVersion>4.12</junitVersion>
+        <commonsConfigVersion>1.10</commonsConfigVersion>
+        <commonsCollectionVersion>3.2.1</commonsCollectionVersion>
+        <mavenDependencyPluginVersion>2.8</mavenDependencyPluginVersion>
+        <mavenAssemblyPluginVersion>2.4.1</mavenAssemblyPluginVersion>
+        <issueComponentId>10030</issueComponentId>
+        <issueNumber>9</issueNumber>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junitVersion}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>${commonsConfigVersion}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>${commonsCollectionVersion}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>${mavenDependencyPluginVersion}</version>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>${mavenJarPluginVersion}</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <classpathPrefix>lib/</classpathPrefix>
+                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            <mainClass>org.hedgecode.chess.ChessHogApp</mainClass>
+                        </manifest>
+                        <manifestEntries>
+                            <Implementation-Title>${project.artifactId}</Implementation-Title>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>${mavenReleasePluginVersion}</version>
+                <configuration>
+                    <tagBase>https://svn.hedgecode.org/lib/${project.artifactId}/tags</tagBase>
+                    <branchBase>https://svn.hedgecode.org/lib/${project.artifactId}/branches</branchBase>
+                    <preparationGoals>clean install</preparationGoals>
+                    <goals>deploy</goals>
+                    <releaseProfiles>release</releaseProfiles>
+                    <scmCommentPrefix xml:space="preserve">[${issueKey}-${issueNumber}] </scmCommentPrefix>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>${mavenReportsPluginVersion}</version>
+                <configuration>
+                    <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>index</report>
+                            <report>summary</report>
+                            <report>dependency-info</report>
+                            <report>project-team</report>
+                            <report>scm</report>
+                            <report>dependency-management</report>
+                            <report>dependencies</report>
+                            <report>plugin-management</report>
+                            <report>plugins</report>
+                            <report>distribution-management</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <profiles>
+        <profile>
+            <id>jar-with-dependencies</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <version>${mavenAssemblyPluginVersion}</version>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                    <addClasspath>true</addClasspath>
+                                    <classpathPrefix>lib/</classpathPrefix>
+                                    <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                                    <mainClass>org.hedgecode.chess.ChessHogApp</mainClass>
+                                </manifest>
+                                <manifestEntries>
+                                    <Implementation-Title>${project.artifactId}</Implementation-Title>
+                                </manifestEntries>
+                            </archive>
+                            <descriptorRefs>
+                                <descriptorRef>jar-with-dependencies</descriptorRef>
+                            </descriptorRefs>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>make-assembly</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>reporting</id>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <sourceFileExcludes>
+                                <!--<exclude>org/hedgecode/chess/*.java</exclude>-->
+                            </sourceFileExcludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/src/main/java/org/hedgecode/chess/Builders.java b/src/main/java/org/hedgecode/chess/Builders.java
new file mode 100644 (file)
index 0000000..94e2a91
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+import org.hedgecode.chess.ascii.ASCIIBuilder;
+import org.hedgecode.chess.fen.FENBuilder;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.tcd.TCDBuilder;
+import org.hedgecode.chess.wiki.WikiBuilder;
+
+/**
+ * Chess Position & Game Builders.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Builders {
+
+    FEN   ( FENBuilder.getInstance() ),
+    TCD   ( TCDBuilder.getInstance() ),
+    ASCII ( ASCIIBuilder.getInstance() ),
+    WIKI  ( WikiBuilder.getInstance() );
+
+    private Builder builder;
+
+    Builders(Builder builder) {
+        this.builder = builder;
+    }
+
+    public Builder builder() {
+        return builder;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/ChessHogApp.java b/src/main/java/org/hedgecode/chess/ChessHogApp.java
new file mode 100644 (file)
index 0000000..94dfaa0
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+/**
+ * ChessHog Main Application.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessHogApp {
+
+    public static void main(String[] args) {
+
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/hedgecode/chess/ChessHogConstants.java b/src/main/java/org/hedgecode/chess/ChessHogConstants.java
new file mode 100644 (file)
index 0000000..77d1d95
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ResourceBundle;
+
+/**
+ * Store of library constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessHogConstants {
+
+    public static final Charset CHARSET = StandardCharsets.UTF_8;
+
+    public static final String LOCALE_BUNDLE_FILE = "org.hedgecode.chess.LocalStrings";
+
+
+    private ChessHogConstants() {
+        throw new AssertionError(
+                "No org.hedgecode.chess.ChessHogConstants instances!"
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/Parsers.java b/src/main/java/org/hedgecode/chess/Parsers.java
new file mode 100644 (file)
index 0000000..ac5ed91
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+import org.hedgecode.chess.ascii.ASCIIParser;
+import org.hedgecode.chess.fen.FENParser;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.tcd.TCDParser;
+import org.hedgecode.chess.wiki.WikiParser;
+
+/**
+ * Chess Position & Game Parsers.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Parsers {
+
+    FEN   ( FENParser.getInstance() ),
+    TCD   ( TCDParser.getInstance() ),
+    ASCII ( ASCIIParser.getInstance() ),
+    WIKI  ( WikiParser.getInstance() );
+
+    private Parser parser;
+
+    Parsers(Parser parser) {
+        this.parser = parser;
+    }
+
+    public Parser parser() {
+        return parser;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/ascii/ASCII.java b/src/main/java/org/hedgecode/chess/ascii/ASCII.java
new file mode 100644 (file)
index 0000000..c8467c1
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Piece;
+
+/**
+ * ASCII chess diagram constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ASCII {
+
+    public static final String CRLF = System.lineSeparator();
+
+    public static final char EMPTY = ' ';
+    public static final String INDENT = "  ";
+
+    public static final char WHITE_PAWN   = 'P';
+    public static final char WHITE_KNIGHT = 'N';
+    public static final char WHITE_BISHOP = 'B';
+    public static final char WHITE_ROOK   = 'R';
+    public static final char WHITE_QUEEN  = 'Q';
+    public static final char WHITE_KING   = 'K';
+
+    public static final char BLACK_PAWN   = 'p';
+    public static final char BLACK_KNIGHT = 'n';
+    public static final char BLACK_BISHOP = 'b';
+    public static final char BLACK_ROOK   = 'r';
+    public static final char BLACK_QUEEN  = 'q';
+    public static final char BLACK_KING   = 'k';
+
+    public static final char[] NOTATION = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+
+    public static final String HYPHEN_LINE  = "+-------------------------------+";
+    public static final String HYPHEN_SEP   = "|";
+
+    public static final String DOT_LINE     = "+------------------------+";
+    public static final String DOT_EDGE     = "|";
+    public static final char   DOT_EMPTY    = '.';
+
+    public static final String TIGHT_LINE   = " ___ ___ ___ ___ ___ ___ ___ ___ ";
+    public static final String TIGHT_SEP    = "|";
+    public static final char   TIGHT_EMPTY  = '_';
+
+    public static final String CP866_HEADER = "╔═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╗";
+    public static final String CP866_LINE   = "╟───┼───┼───┼───┼───┼───┼───┼───╢";
+    public static final String CP866_FOOTER = "╚═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝";
+    public static final String CP866_SEP    = "│";
+    public static final String CP866_EDGE   = "║";
+
+    public static final String BAR_REGEX = HYPHEN_SEP + TIGHT_SEP + CP866_SEP;
+    public static final String EDGE_REGEX = HYPHEN_SEP + DOT_EDGE + TIGHT_SEP + CP866_EDGE;
+
+    public static final String PIECE_REGEX = "PNBRQKpnbrqk";
+    public static final String DOT_PIECE_REGEX = PIECE_REGEX + DOT_EMPTY;
+
+    private static final Pattern PIECE_PATTERN = Pattern.compile(
+            String.format("([%s])", PIECE_REGEX)
+    );
+
+    private static final Map<ColorPiece, Character> PIECES = new HashMap<ColorPiece, Character>() {
+        {
+            put(ColorPiece.WHITE_PAWN, WHITE_PAWN);     put(ColorPiece.BLACK_PAWN, BLACK_PAWN);
+            put(ColorPiece.WHITE_KNIGHT, WHITE_KNIGHT); put(ColorPiece.BLACK_KNIGHT, BLACK_KNIGHT);
+            put(ColorPiece.WHITE_BISHOP, WHITE_BISHOP); put(ColorPiece.BLACK_BISHOP, BLACK_BISHOP);
+            put(ColorPiece.WHITE_ROOK, WHITE_ROOK);     put(ColorPiece.BLACK_ROOK, BLACK_ROOK);
+            put(ColorPiece.WHITE_QUEEN, WHITE_QUEEN);   put(ColorPiece.BLACK_QUEEN, BLACK_QUEEN);
+            put(ColorPiece.WHITE_KING, WHITE_KING);     put(ColorPiece.BLACK_KING, BLACK_KING);
+        }
+    };
+
+    private static ASCIIBoardType boardType = ASCIIBoardType.HYPHEN;
+    private static boolean withNotation = false;
+
+    private ASCII() {
+    }
+
+    public static void setType(ASCIIBoardType type) {
+        boardType = type;
+    }
+
+    public static ASCIIBoardType getType() {
+        return boardType;
+    }
+
+    public static void setNotation(boolean isNotation) {
+        withNotation = isNotation;
+    }
+
+    public static boolean isNotation() {
+        return withNotation;
+    }
+
+    public static ColorPiece getColorPiece(String piece) {
+        if (piece != null) {
+            Matcher matcher = PIECE_PATTERN.matcher(piece);
+            if (matcher.find()) {
+                char chPiece = matcher.group(1).charAt(0);
+                for (Map.Entry<ColorPiece, Character> entry : PIECES.entrySet()) {
+                    if (entry.getValue() == chPiece)
+                        return entry.getKey();
+                }
+            }
+        }
+        return null;
+    }
+
+    public static char getAsciiPiece(Color color, Piece piece) {
+        return getAsciiPiece(
+                ColorPiece.getColorPiece(color, piece)
+        );
+    }
+
+    public static char getAsciiPiece(ColorPiece colorPiece) {
+        return (colorPiece != null)
+                ? PIECES.get(colorPiece)
+                : EMPTY;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/hedgecode/chess/ascii/ASCIIBoardType.java b/src/main/java/org/hedgecode/chess/ascii/ASCIIBoardType.java
new file mode 100644 (file)
index 0000000..08428b8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+/**
+ * ASCII chess diagram board types.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum ASCIIBoardType {
+
+    HYPHEN (
+            ASCII.HYPHEN_LINE, ASCII.HYPHEN_LINE, ASCII.HYPHEN_LINE,
+            ASCII.HYPHEN_SEP, ASCII.HYPHEN_SEP, ASCII.EMPTY, ASCII.EMPTY
+    ),
+
+    DOT    (
+            ASCII.DOT_LINE, null, ASCII.DOT_LINE,
+            null, ASCII.DOT_EDGE, ASCII.DOT_EMPTY, ASCII.EMPTY
+    ),
+
+    TIGHT  (
+            ASCII.TIGHT_LINE, null, null,
+            ASCII.TIGHT_SEP, ASCII.TIGHT_SEP, ASCII.TIGHT_EMPTY, ASCII.TIGHT_EMPTY
+    ),
+
+    CP866  (
+            ASCII.CP866_HEADER, ASCII.CP866_LINE, ASCII.CP866_FOOTER,
+            ASCII.CP866_SEP, ASCII.CP866_EDGE, ASCII.EMPTY, ASCII.EMPTY
+    );
+
+    private String header;
+    private String line;
+    private String footer;
+    private String separator;
+    private String edge;
+    private char empty;
+    private char space;
+
+    ASCIIBoardType(
+            String header, String line, String footer,
+            String separator, String edge,
+            char empty, char space
+    ) {
+        this.header = header;
+        this.line = line;
+        this.footer = footer;
+        this.separator = separator;
+        this.edge = edge;
+        this.empty = empty;
+        this.space = space;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public String getLine() {
+        return line;
+    }
+
+    public String getFooter() {
+        return footer;
+    }
+
+    public String getSeparator() {
+        return separator;
+    }
+
+    public String getEdge() {
+        return edge;
+    }
+
+    public char getEmpty() {
+        return empty;
+    }
+
+    public char getSpace() {
+        return space;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/ascii/ASCIIBuilder.java b/src/main/java/org/hedgecode/chess/ascii/ASCIIBuilder.java
new file mode 100644 (file)
index 0000000..54599bb
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.Map;
+
+import org.hedgecode.chess.Parsers;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * ASCII chess diagram builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ASCIIBuilder implements Builder {
+
+    private static Builder _instance = new ASCIIBuilder();
+
+    private ASCIIBuilder() {
+    }
+
+    @Override
+    public String build(Position position) {
+        return _build(
+                ASCII.getType(), position
+        );
+    }
+
+    private String _build(ASCIIBoardType type, Position position) {
+        StringBuilder sb = new StringBuilder();
+        Map<Square, ColorPiece> squares = position.getSquares();
+
+        String indent = ASCII.INDENT;
+        char empty = type.getEmpty();
+        char space = type.getSpace();
+        String separator = type.getSeparator();
+
+        if (ASCII.isNotation())
+            sb.append(indent).append(
+                    _buildNotation(separator != null)
+            ).append(ASCII.CRLF);
+
+        if (type.getHeader() != null)
+            sb.append(indent).append(type.getHeader()).append(ASCII.CRLF);
+
+        for (int i = Square.getSize() - 1; i >= 0; --i) {
+            if (ASCII.isNotation())
+                sb.append(i + 1).append(ASCII.EMPTY);
+            else
+                sb.append(indent);
+
+            sb.append(type.getEdge());
+
+            for (int j = 0; j < Square.getSize(); ++j) {
+                Square square = Square.getSquare(j, i);
+                ColorPiece piece = squares.get(square);
+                char pieceChar = (piece != null) ? ASCII.getAsciiPiece(piece) : empty;
+                sb.append(space).append(pieceChar).append(space);
+                if (separator != null && j < Square.getSize() - 1)
+                    sb.append(separator);
+            }
+
+            sb.append(type.getEdge());
+            if (ASCII.isNotation())
+                sb.append(ASCII.EMPTY).append(i + 1);
+            else
+                sb.append(indent);
+
+            sb.append(ASCII.CRLF);
+
+            if (i > 0 && type.getLine() != null)
+                sb.append(indent).append(type.getLine()).append(ASCII.CRLF);
+        }
+
+        if (type.getFooter() != null)
+            sb.append(indent).append(type.getFooter()).append(ASCII.CRLF);
+
+        if (ASCII.isNotation())
+            sb.append(indent).append(
+                    _buildNotation(separator != null)
+            ).append(ASCII.CRLF);
+
+        return sb.toString();
+    }
+
+    private String _buildNotation(boolean withSeparator) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ASCII.EMPTY);
+        for (char letter : ASCII.NOTATION) {
+            sb.append(ASCII.EMPTY).append(letter).append(ASCII.EMPTY);
+            if (withSeparator)
+                sb.append(ASCII.EMPTY);
+        }
+        sb.append(ASCII.EMPTY);
+        return sb.toString();
+    }
+
+    public static Builder getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+        ASCII.setType(ASCIIBoardType.HYPHEN);
+        ASCII.setNotation(false);
+
+        Position position = Parsers.FEN.parser().parse(
+                "r7/7p/8/8/8/8/PPPPPP2/1N1Q2NR w KQkq - 20 20"
+        );
+        System.out.println(
+                getInstance().build(position)
+        );
+
+/*
+        Position position = Parsers.WIKI.parser().parse(
+                "{{Шахматная диаграмма\n" +
+                        "|\n" +
+                        "|\n" +
+                        "|rd|nd|bd|qd|kd|bd|nd|rd\n" +
+                        "|pd|pd|  |pd|pd|pd|pd|pd\n" +
+                        "|  |  |  |  |  |  |  |\n" +
+                        "|  |  |  |  |  |  |  |\n" +
+                        "|  |  |  |pd|pl|  |  |\r\n" +
+                        "|  |  |pl|  |  |  |  |\n" +
+                        "|pl|pl|  |  |  |pl|pl|pl\n" +
+                        "|rl|nl|bl|ql|kl|bl|nl|rl\n" +
+                        "| }}"
+        );
+        System.out.println(
+                getInstance().build(position)
+        );
+*/
+
+/*
+        Position position = Parsers.TCD.parser().parse("AHL30v0GIv+PqX16ap-G");
+        System.out.println(
+                getInstance().build(position)
+        );
+*/
+
+        /*
+        System.out.println(
+                getInstance().build(Positions.INITIAL.getPosition())
+        );
+*/
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/ascii/ASCIIParser.java b/src/main/java/org/hedgecode/chess/ascii/ASCIIParser.java
new file mode 100644 (file)
index 0000000..65fa3e0
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.Builders;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * ASCII chess diagram parser.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ASCIIParser implements Parser {
+
+    private static final String LINE_REGEX = "\\r?\\n";
+    private static final String BAR_SQUARE_REGEX = String.format("[%s]", ASCII.BAR_REGEX);
+    private static final String DOT_SQUARE_REGEX = String.format("[^%s]+", ASCII.DOT_PIECE_REGEX);
+
+    private static final Pattern BAR_PATTERN =
+            Pattern.compile(
+                    String.format(
+                            "^.*[%s](([^%s]+[%s]){%d}[^%s]+)[%s].*$",
+                            ASCII.EDGE_REGEX, ASCII.BAR_REGEX, ASCII.BAR_REGEX,
+                            Square.getSize() - 1, ASCII.EDGE_REGEX, ASCII.EDGE_REGEX
+                    )
+            );
+
+    private static final Pattern DOT_PATTERN =
+            Pattern.compile(
+                    String.format(
+                            "^.*[%s](([^%s]+[%s]){%d}[^%s]+)[%s].*$",
+                            ASCII.DOT_EDGE, ASCII.DOT_PIECE_REGEX, ASCII.DOT_PIECE_REGEX,
+                            Square.getSize(), ASCII.DOT_PIECE_REGEX, ASCII.DOT_EDGE
+                    )
+            );
+
+    private static Parser _instance = new ASCIIParser();
+
+    private ASCIIParser() {
+    }
+
+    @Override
+    public Position parse(String string) throws ParseException {
+        return _parse(
+                string.split(LINE_REGEX)
+        );
+    }
+
+    private Position _parse(String[] strings) throws ParseException {
+        String[] lines;
+        String squareRegex = BAR_SQUARE_REGEX;
+        lines = _parseLines(strings, BAR_PATTERN);
+        if (lines.length != Square.getSize()) {
+            squareRegex = DOT_SQUARE_REGEX;
+            lines = _parseLines(strings, DOT_PATTERN);
+            if (lines.length != Square.getSize())
+                throw new ParseException("parse.ascii.incorrect.board");
+        }
+        Position position = Positions.EMPTY.getPosition();
+        _parsePieces(
+                lines,
+                position,
+                squareRegex
+        );
+        return position;
+    }
+
+    private String[] _parseLines(String[] strings, Pattern linePattern) {
+        List<String> lines = new ArrayList<>();
+        for (String line : strings) {
+            Matcher matcher = linePattern.matcher(line);
+            if (matcher.find()) {
+                lines.add(
+                        matcher.group(1).trim()
+                );
+            }
+        }
+        return lines.toArray(
+                new String[lines.size()]
+        );
+    }
+
+    private void _parsePieces(String[] lines, Position position, String squareRegex) throws ParseException {
+        for (int i = 0; i < lines.length; ++i) {
+            int line = lines.length - i;
+            String[] pieces = lines[i].split(squareRegex, -1);
+            if (pieces.length != Square.getSize())
+                throw new ParseException("parse.ascii.incorrect.board");
+            for (int j = 0; j < pieces.length; ++j) {
+                position.setPiece(
+                        ASCII.getColorPiece(pieces[j]),
+                        Square.getSquare(j, line - 1)
+                );
+            }
+        }
+    }
+
+    public static Parser getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+        ASCII.setType(ASCIIBoardType.DOT);
+        ASCII.setNotation(false);
+
+        // Position position = Positions.INITIAL.getPosition();
+        Position position = Positions.EMPTY.getPosition();
+        position.setKing(Color.WHITE, Square.A1);
+        position.setPawn(Color.WHITE, Square.A2);
+        position.setPawn(Color.WHITE, Square.E4);
+        position.setQueen(Color.WHITE, Square.A7);
+        position.setKing(Color.BLACK, Square.H8);
+        position.setPawn(Color.BLACK, Square.G7);
+        position.setPawn(Color.BLACK, Square.H7);
+        position.setKnight(Color.BLACK, Square.G2);
+        position.setBishop(Color.BLACK, Square.D4);
+        position.setRook(Color.BLACK, Square.E2);
+
+        String ascii = Builders.ASCII.builder().build(position);
+        System.out.println(
+                ascii
+        );
+
+        position = getInstance().parse(ascii);
+
+        ASCII.setType(ASCIIBoardType.HYPHEN);
+        ascii = Builders.ASCII.builder().build(position);
+        System.out.println(
+                ascii
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/fen/FEN.java b/src/main/java/org/hedgecode/chess/fen/FEN.java
new file mode 100644 (file)
index 0000000..5145ae4
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hedgecode.chess.position.Castle;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Forsyth–Edwards Notation (FEN) constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class FEN {
+
+    enum Fields {
+        PIECES     (0, "[pPnNbBrRqQkK1-8]+/?"),
+        MOVE       (1, "[wb]"),
+        CASTLE     (2, "[qQkK]+|-"),
+        EN_PASSANT (3, "[a-h][36]|-"),
+        HALFMOVE   (4, "[0-9]+"),
+        FULLMOVE   (5, "[1-9][0-9]*");
+
+        private int index;
+        private String regex;
+
+        Fields(int index, String regex) {
+            this.index = index;
+            this.regex = regex;
+        }
+
+        public int index() {
+            return index;
+        }
+
+        public String regex() {
+            return regex;
+        }
+    }
+
+    public static final char WHITE = 'w';
+    public static final char BLACK = 'b';
+    public static final char HYPHEN = '-';
+
+    public static final String FIELDS_SEP = " ";
+    public static final String LINE_SEP = "/";
+
+    public static final char WHITE_PAWN   = 'P';
+    public static final char WHITE_KNIGHT = 'N';
+    public static final char WHITE_BISHOP = 'B';
+    public static final char WHITE_ROOK   = 'R';
+    public static final char WHITE_QUEEN  = 'Q';
+    public static final char WHITE_KING   = 'K';
+
+    public static final char BLACK_PAWN   = 'p';
+    public static final char BLACK_KNIGHT = 'n';
+    public static final char BLACK_BISHOP = 'b';
+    public static final char BLACK_ROOK   = 'r';
+    public static final char BLACK_QUEEN  = 'q';
+    public static final char BLACK_KING   = 'k';
+
+    private static final Map<Color, Character> COLORS = new HashMap<Color, Character>() {
+        {
+            put(Color.WHITE, WHITE);
+            put(Color.BLACK, BLACK);
+        }
+    };
+
+    private static final Map<ColorPiece, Character> PIECES = new HashMap<ColorPiece, Character>() {
+        {
+            put(ColorPiece.WHITE_PAWN, WHITE_PAWN);     put(ColorPiece.BLACK_PAWN, BLACK_PAWN);
+            put(ColorPiece.WHITE_KNIGHT, WHITE_KNIGHT); put(ColorPiece.BLACK_KNIGHT, BLACK_KNIGHT);
+            put(ColorPiece.WHITE_BISHOP, WHITE_BISHOP); put(ColorPiece.BLACK_BISHOP, BLACK_BISHOP);
+            put(ColorPiece.WHITE_ROOK, WHITE_ROOK);     put(ColorPiece.BLACK_ROOK, BLACK_ROOK);
+            put(ColorPiece.WHITE_QUEEN, WHITE_QUEEN);   put(ColorPiece.BLACK_QUEEN, BLACK_QUEEN);
+            put(ColorPiece.WHITE_KING, WHITE_KING);     put(ColorPiece.BLACK_KING, BLACK_KING);
+        }
+    };
+
+    private FEN() {
+    }
+
+    public static Color getColor(char color) {
+        for (Map.Entry<Color, Character> entry : COLORS.entrySet()) {
+            if (entry.getValue() == color)
+                return entry.getKey();
+        }
+        return null;
+    }
+
+    public static String getColor(Color color) {
+        return String.valueOf(
+                color != null
+                        ? COLORS.get(color)
+                        : COLORS.get(Color.WHITE)
+        );
+    }
+
+    public static ColorPiece getColorPiece(char piece) {
+        for (Map.Entry<ColorPiece, Character> entry : PIECES.entrySet()) {
+            if (entry.getValue() == piece)
+                return entry.getKey();
+        }
+        return null;
+    }
+
+    public static char getFenPiece(Color color, Piece piece) {
+        return getFenPiece(
+                ColorPiece.getColorPiece(color, piece)
+        );
+    }
+
+    public static char getFenPiece(ColorPiece colorPiece) {
+        return PIECES.get(colorPiece);
+    }
+
+    public static Castle getCastle(Color color, String castles) {
+        Castle castle;
+        if (castles.indexOf(HYPHEN) >= 0) {
+            castle = Castle.NONE;
+        } else {
+            boolean isKing = false, isQueen = false;
+            switch (color) {
+                case WHITE:
+                    isKing = (castles.indexOf(WHITE_KING) >= 0);
+                    isQueen = (castles.indexOf(WHITE_QUEEN) >= 0);
+                    break;
+                case BLACK:
+                    isKing = (castles.indexOf(BLACK_KING) >= 0);
+                    isQueen = (castles.indexOf(BLACK_QUEEN) >= 0);
+                    break;
+            }
+            castle = isKing && isQueen ? Castle.BOTH
+                    : isKing ? Castle.KING
+                    : isQueen ? Castle.QUEEN
+                    : Castle.NONE;
+        }
+        return castle;
+    }
+
+    public static String getCastle(Castle whiteCastle, Castle blackCastle) {
+        StringBuilder sb = new StringBuilder();
+        switch (whiteCastle) {
+            case KING:
+                sb.append(WHITE_KING);
+                break;
+            case QUEEN:
+                sb.append(WHITE_QUEEN);
+                break;
+            case BOTH:
+                sb.append(WHITE_KING).append(WHITE_QUEEN);
+                break;
+        }
+        switch (blackCastle) {
+            case KING:
+                sb.append(BLACK_KING);
+                break;
+            case QUEEN:
+                sb.append(BLACK_QUEEN);
+                break;
+            case BOTH:
+                sb.append(BLACK_KING).append(BLACK_QUEEN);
+                break;
+        }
+        String castles = sb.toString();
+        return castles.isEmpty()
+                ? String.valueOf(HYPHEN)
+                : castles;
+    }
+
+    public static Square getEnPassant(String enPassant) {
+        if (enPassant.indexOf(HYPHEN) < 0) {
+            return Square.getSquare(enPassant);
+        }
+        return null;
+    }
+
+    public static String getEnPassant(Square square){
+        return (square != null)
+                ? square.name().toLowerCase()
+                : String.valueOf(HYPHEN);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/fen/FENBuilder.java b/src/main/java/org/hedgecode/chess/fen/FENBuilder.java
new file mode 100644 (file)
index 0000000..d63bb93
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.Map;
+
+import org.hedgecode.chess.Parsers;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Forsyth–Edwards Notation (FEN) builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class FENBuilder implements Builder {
+
+    private static Builder _instance = new FENBuilder();
+
+    private FENBuilder() {
+    }
+
+    @Override
+    public String build(Position position) {
+        return _build(
+                position
+        );
+    }
+
+    private String _build(Position position) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(
+                _buildPosition(position)
+        ).append(
+                FEN.FIELDS_SEP
+        ).append(
+                FEN.getColor(
+                        position.getMove()
+                )
+        ).append(
+                FEN.FIELDS_SEP
+        ).append(
+                FEN.getCastle(
+                        position.getCastle(Color.WHITE),
+                        position.getCastle(Color.BLACK)
+                )
+        ).append(
+                FEN.FIELDS_SEP
+        ).append(
+                FEN.getEnPassant(
+                        position.getEnPassant()
+                )
+        ).append(
+                FEN.FIELDS_SEP
+        ).append(
+                position.getHalfMove()
+        ).append(
+                FEN.FIELDS_SEP
+        ).append(
+                position.getFullMove()
+        );
+        return sb.toString();
+    }
+
+    private String _buildPosition(Position position) {
+        StringBuilder sb = new StringBuilder();
+        Map<Square, ColorPiece> squares = position.getSquares();
+        for (int i = Square.getSize() - 1; i >= 0; --i) {
+            int empty = 0;
+            for (int j = 0; j < Square.getSize(); ++j) {
+                Square square = Square.getSquare(j, i);
+                ColorPiece piece = squares.get(square);
+                if (piece != null) {
+                    if (empty > 0)
+                        sb.append(empty);
+                    sb.append(
+                            FEN.getFenPiece(piece)
+                    );
+                    empty = 0;
+                } else {
+                    ++empty;
+                }
+            }
+            if (empty > 0)
+                sb.append(empty);
+            if (i > 0)
+                sb.append(FEN.LINE_SEP);
+        }
+        return sb.toString();
+    }
+
+    public static Builder getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+
+        Position position = Parsers.FEN.parser().parse(
+                "r7/7p/8/8/8/8/PPPPPP2/1N1Q2NR w KQkq a3 1 20"
+        );
+        System.out.println(
+                getInstance().build(position)
+        );
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/fen/FENParser.java b/src/main/java/org/hedgecode/chess/fen/FENParser.java
new file mode 100644 (file)
index 0000000..4162a55
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Forsyth–Edwards Notation (FEN) parser.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class FENParser implements Parser {
+
+    private static final String SPLIT_REGEX = "\\s+";
+    private static final String LINE_REGEX = FEN.LINE_SEP;
+
+    private static final Pattern PATTERN =
+            Pattern.compile(
+                    String.format("^(%s){%d} (%s) (%s) (%s) (%s) (%s)$",
+                            FEN.Fields.PIECES.regex(), Square.getSize(),
+                            FEN.Fields.MOVE.regex(),
+                            FEN.Fields.CASTLE.regex(), FEN.Fields.EN_PASSANT.regex(),
+                            FEN.Fields.HALFMOVE.regex(), FEN.Fields.FULLMOVE.regex()
+                    )
+            );
+
+/*
+    private static final Pattern PATTERN =
+            Pattern.compile(
+                    "^([pPnNbBrRqQkK1-8]+/?)* [wb] [qQkK]+|- ([a-h][36])|- [0-9]+ [1-9][0-9]*$"
+            );
+*/
+
+    private static Parser _instance = new FENParser();
+
+    private FENParser() {
+    }
+
+    public boolean isValidFen(String fen) {
+        return PATTERN.matcher(fen).matches();
+    }
+
+    @Override
+    public Position parse(String string) throws ParseException {
+        if (string == null)
+            throw new ParseException("parse.null.input.string");
+        if (!isValidFen(string))
+            throw new ParseException("parse.fen.invalid.string");
+
+        Position position = Positions.EMPTY.getPosition();
+
+        String[] fields = string.split(SPLIT_REGEX);
+
+        _parsePieces(
+                fields[FEN.Fields.PIECES.index()],
+                position
+        );
+        _parseMove(
+                fields[FEN.Fields.MOVE.index()],
+                position
+        );
+        _parseCastle(
+                fields[FEN.Fields.CASTLE.index()],
+                position
+        );
+        _parseEnPassant(
+                fields[FEN.Fields.EN_PASSANT.index()],
+                position
+        );
+        _parseHalfMove(
+                fields[FEN.Fields.HALFMOVE.index()],
+                position
+        );
+        _parseFullMove(
+                fields[FEN.Fields.FULLMOVE.index()],
+                position
+        );
+        return position;
+    }
+
+    private void _parsePieces(String fen, Position position) throws ParseException {
+        String[] lines = fen.split(LINE_REGEX);
+        if (lines.length != Square.getSize())
+            throw new ParseException("parse.fen.incorrect.board");
+        for (int i = 0; i < lines.length; ++i) {
+            int hLine = lines.length - (i + 1);
+            int vLine = 0;
+            char[] pieces = lines[i].toCharArray();
+            for (char piece : pieces) {
+                if (vLine >= Square.getSize())
+                    throw new ParseException("parse.fen.incorrect.board");
+                if (Character.isDigit(piece)) {
+                    int num = Character.getNumericValue(piece);
+                    if (vLine + num > Square.getSize())
+                        throw new ParseException("parse.fen.incorrect.board");
+                    for (int j = 0; j < num; ++j) {
+                        position.setPiece(
+                                null,
+                                Square.getSquare(vLine, hLine)
+                        );
+                        vLine++;
+                    }
+                } else {
+                    position.setPiece(
+                            FEN.getColorPiece(piece),
+                            Square.getSquare(vLine, hLine)
+                    );
+                    vLine++;
+                }
+            }
+        }
+    }
+
+    private void _parseMove(String move, Position position) {
+        position.setMove(
+                FEN.getColor(move.charAt(0))
+        );
+    }
+
+    private void _parseCastle(String castle, Position position) {
+        position.setCastle(
+                Color.WHITE,
+                FEN.getCastle(Color.WHITE, castle)
+        );
+        position.setCastle(
+                Color.BLACK,
+                FEN.getCastle(Color.BLACK, castle)
+        );
+    }
+
+    private void _parseEnPassant(String enPassant, Position position) {
+        position.setEnPassant(
+                FEN.getEnPassant(enPassant)
+        );
+    }
+
+    private void _parseHalfMove(String halfMove, Position position) {
+        position.setHalfMove(
+                Integer.parseInt(halfMove)
+        );
+    }
+
+    private void _parseFullMove(String fullMove, Position position) {
+        position.setFullMove(
+                Integer.parseInt(fullMove)
+        );
+    }
+
+    public static Parser getInstance() {
+        return _instance;
+    }
+
+    public static void main(String[] args) throws ParseException {
+
+/*
+        Pattern pattern =
+                Pattern.compile(
+                        "^([pPnNbBrRqQkK1-8]+/?){8} [wb] ([qQkK]+|-) ([a-h][36]|-) [0-9]+ [1-9][0-9]*$"
+                );
+        System.out.println(
+                pattern.matcher("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - a6 0 1").matches()
+        );
+*/
+
+        Position position = getInstance().parse(
+                //"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
+                "r7/7p/8/8/8/8/PPPPPP2/1N1Q2NR w KQkq a3 20 20"
+        );
+        System.out.println(position);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/AbstractMove.java b/src/main/java/org/hedgecode/chess/game/AbstractMove.java
new file mode 100644 (file)
index 0000000..a417325
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.Square;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractMove implements Move {
+
+    Color color;
+    Square from, to;
+
+    protected AbstractMove(Color color, Square from, Square  to) {
+        this.color = color;
+        this.from = from;
+        this.to = to;
+    }
+
+    @Override
+    public Color color() {
+        return color;
+    }
+
+    @Override
+    public Square from() {
+        return from;
+    }
+
+    @Override
+    public Square to() {
+        return to;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/Game.java b/src/main/java/org/hedgecode/chess/game/Game.java
new file mode 100644 (file)
index 0000000..b50a71f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Game {
+
+    Moves getMoves();
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/GameMove.java b/src/main/java/org/hedgecode/chess/game/GameMove.java
new file mode 100644 (file)
index 0000000..392e58a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Square;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class GameMove extends AbstractMove {
+
+    protected GameMove(Color color, Square from, Square to) {
+        super(color, from, to);
+    }
+
+    @Override
+    public boolean isCapture() {
+        return false;
+    }
+
+    @Override
+    public boolean isCastle() {
+        return false;
+    }
+
+    @Override
+    public boolean isPromote() {
+        return false;
+    }
+
+    @Override
+    public Piece promote() {
+        return null;
+    }
+
+    @Override
+    public boolean isCheck() {
+        return false;
+    }
+
+    @Override
+    public boolean isMate() {
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/GameMoves.java b/src/main/java/org/hedgecode/chess/game/GameMoves.java
new file mode 100644 (file)
index 0000000..75c303d
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.Square;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class GameMoves implements Moves {
+
+    private int moveNumber;
+    private List<Move> moves;
+
+    public GameMoves() {
+        moveNumber = -1;
+        moves = new ArrayList<>();
+    }
+
+    @Override
+    public void move(Move move) {
+
+    }
+
+    @Override
+    public Move first() {
+        if (moves.isEmpty())
+            return null;
+        moveNumber = 0;
+        return moves.get(moveNumber);
+    }
+
+    @Override
+    public Move last() {
+        if (moves.isEmpty())
+            return null;
+        moveNumber = moves.size() - 1;
+        return moves.get(moveNumber);
+    }
+
+    @Override
+    public boolean hasNext() {
+        return moves.size() > moveNumber + 1;
+    }
+
+    @Override
+    public Move next() {
+        if (hasNext())
+            return moves.get(++moveNumber);
+        return null;
+    }
+
+    @Override
+    public void clear() {
+        moveNumber = -1;
+        moves.clear();
+    }
+
+    @Override
+    public int count() {
+        return moves.size();
+    }
+
+    @Override
+    public Move byNumber(int num, Color color) {
+        return null;
+    }
+
+    static class Builder {
+
+        Move build(Color color, Square from, Square to) {
+            return new GameMove(color, from, to);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/Move.java b/src/main/java/org/hedgecode/chess/game/Move.java
new file mode 100644 (file)
index 0000000..16d3bd8
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Square;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Move {
+
+    Color color();
+
+    Square from();
+    Square to();
+
+    boolean isCapture();
+    boolean isCastle();
+
+    boolean isPromote();
+    Piece promote();
+
+    boolean isCheck();
+    boolean isMate();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/game/Moves.java b/src/main/java/org/hedgecode/chess/game/Moves.java
new file mode 100644 (file)
index 0000000..e4d69c2
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.game;
+
+import org.hedgecode.chess.position.Color;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Moves {
+
+    void move(Move move);
+
+    Move first();
+    Move last();
+
+    boolean hasNext();
+    Move next();
+
+    void clear();
+
+    int count();
+
+    Move byNumber(int num, Color color);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/HedgefishCommand.java b/src/main/java/org/hedgecode/chess/hedgefish/HedgefishCommand.java
new file mode 100644 (file)
index 0000000..c64c7c3
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class HedgefishCommand {
+
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public String getParams() {
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngine.java b/src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngine.java
new file mode 100644 (file)
index 0000000..fc79b78
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.DebugMode;
+import org.hedgecode.chess.uci.Transmitter;
+
+/**
+ * Hedgefish UCI Engine.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class HedgefishEngine implements Engine {
+
+    private static final String HEDGEFISH_COPYIGHT = "Hedgefish 0.1 (c) 2018 Hedgecode, D.Samoshin aka gotty";
+
+    private Transmitter<HedgefishCommand> transmitter;
+
+    private DebugMode debugMode;
+
+    HedgefishEngine() {
+        transmitter = new HedgefishTransmitter();
+        debugMode = DebugMode.OFF;
+    }
+
+    public Transmitter<HedgefishCommand> transmitter() {
+        return transmitter;
+    }
+
+    public void command(HedgefishCommand command) {
+        transmitter.addCommand(command);
+    }
+
+
+    @Override
+    public void init() {
+
+    }
+
+    @Override
+    public void debug(DebugMode mode) {
+        debugMode = mode;
+    }
+
+    @Override
+    public void isReady() {
+
+    }
+
+    @Override
+    public void go() {
+
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+    @Override
+    public void ponderhit() {
+
+    }
+
+    @Override
+    public void quit() {
+        transmitter.stop();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngineRunner.java b/src/main/java/org/hedgecode/chess/hedgefish/HedgefishEngineRunner.java
new file mode 100644 (file)
index 0000000..0eaedc6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+import org.hedgecode.chess.uci.CommandExecutor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.EngineException;
+import org.hedgecode.chess.uci.EngineRunner;
+import org.hedgecode.chess.uci.Transmitter;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+
+/**
+ * Hedgefish UCI Engine Runner.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class HedgefishEngineRunner implements EngineRunner {
+
+    private HedgefishEngine engine;
+    private boolean isRunning;
+
+    HedgefishEngineRunner() {
+        engine = new HedgefishEngine();
+        isRunning = false;
+    }
+
+    @Override
+    public Engine init() throws EngineException {
+        if (isRunning)
+            throw new EngineException("uci.engine.already.run");
+        return engine;
+    }
+
+    @Override
+    public void run(final CommandExecutor commandExecutor) throws EngineException {
+        if (isRunning)
+            throw new EngineException("uci.engine.already.run");
+
+        Thread engineThread = new Thread(
+                new Runnable() {
+                    public void run() {
+                        try {
+                            Transmitter<HedgefishCommand> engineTransmitter = engine.transmitter();
+                            while (engineTransmitter.isActive()) {
+                                if (engineTransmitter.hasCommand()) {
+                                    HedgefishCommand command = engineTransmitter.transmitCommand();
+                                    commandExecutor.exec(
+                                            command.getName(),
+                                            CommandDirection.FROM_ENGINE,
+                                            command.getParams()
+                                    );
+                                }
+                            }
+                        } finally {
+                            commandExecutor.exec(
+                                    UCIConstants.TERMINATE,
+                                    CommandDirection.FROM_ENGINE,
+                                    null
+                            );
+                            isRunning = false;
+                        }
+                    }
+                }
+        );
+        engineThread.start();
+        isRunning = true;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/HedgefishTransmitter.java b/src/main/java/org/hedgecode/chess/hedgefish/HedgefishTransmitter.java
new file mode 100644 (file)
index 0000000..a945b02
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.hedgecode.chess.uci.Transmitter;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class HedgefishTransmitter implements Transmitter<HedgefishCommand> {
+
+    private Queue<HedgefishCommand> queue;
+    private boolean isActive;
+
+    HedgefishTransmitter() {
+        queue = new LinkedList<>();
+        isActive = true;
+    }
+
+    @Override
+    public boolean isActive() {
+        return isActive && queue.isEmpty();
+    }
+
+    @Override
+    public boolean hasCommand() {
+        return !queue.isEmpty();
+    }
+
+    @Override
+    public HedgefishCommand transmitCommand() {
+        return queue.poll();
+    }
+
+    @Override
+    public void addCommand(HedgefishCommand command) {
+        if (isActive)
+            queue.add(command);
+    }
+
+    @Override
+    public void stop() {
+        isActive = false;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/Types.java b/src/main/java/org/hedgecode/chess/hedgefish/Types.java
new file mode 100644 (file)
index 0000000..7221c79
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class Types {
+
+    public static final int MAX_MOVES = 256;
+    public static final int MAX_PLY   = 128;
+
+    public enum MoveType {
+        NORMAL,
+        PROMOTION,
+        ENPASSANT,
+        CASTLING
+    }
+
+    public enum Color {
+        WHITE,
+        BLACK,
+        COLOR_NB
+    }
+
+    public enum CastlingSide {
+        KING_SIDE,
+        QUEEN_SIDE,
+        CASTLING_SIDE_NB
+    }
+
+    public enum CastlingRight {
+        NO_CASTLING,
+        WHITE_OO,
+        WHITE_OOO,
+        BLACK_OO,
+        BLACK_OOO,
+        ANY_CASTLING,
+        CASTLING_RIGHT_NB
+    }
+
+    public enum Phase {
+        PHASE_ENDGAME,
+        PHASE_MIDGAME,
+        MG,
+        EG,
+        PHASE_NB
+    }
+
+    public enum ScaleFactor {
+        SCALE_FACTOR_DRAW,
+        SCALE_FACTOR_NORMAL,
+        SCALE_FACTOR_MAX,
+        SCALE_FACTOR_NONE
+    }
+
+    public enum Bound {
+        BOUND_NONE,
+        BOUND_UPPER,
+        BOUND_LOWER,
+        BOUND_EXACT
+    }
+
+//    enum Value
+
+    public enum PieceType {
+        NO_PIECE_TYPE,
+        PAWN,
+        KNIGHT,
+        BISHOP,
+        ROOK,
+        QUEEN,
+        KING,
+        ALL_PIECES,
+        PIECE_TYPE_NB
+    }
+
+    public enum Piece {
+        NO_PIECE,
+        W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
+        B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
+        PIECE_NB
+    }
+
+//  ...
+
+}
diff --git a/src/main/java/org/hedgecode/chess/hedgefish/UCI.java b/src/main/java/org/hedgecode/chess/hedgefish/UCI.java
new file mode 100644 (file)
index 0000000..69fa4a1
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.hedgefish;
+
+/**
+ * Universal Chess Interface.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class UCI {
+
+    public static class Option {
+
+        String defaultValue, currentValue, type;
+        int min, max;
+        int idx;
+//        OnChange on_change;
+
+
+        public Option() {
+
+        }
+
+        public Option(boolean v) {
+
+        }
+
+        public Option(final char[] v) {
+
+        }
+
+        public Option(double v, int minv, int maxv) {
+
+        }
+
+        public Option(final char[] v, final char[] cur) {
+
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/img/DiagramBuilder.java b/src/main/java/org/hedgecode/chess/img/DiagramBuilder.java
new file mode 100644 (file)
index 0000000..17ff21a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.img;
+
+import java.awt.Image;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.imageio.ImageIO;
+
+import org.hedgecode.chess.position.Position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class DiagramBuilder implements ImageBuilder {
+
+    private static ImageBuilder _instance = new DiagramBuilder();
+
+    private DiagramBuilder() {
+    }
+
+    @Override
+    public String build(Position position) {
+
+        return "";
+    }
+
+    @Override
+    public Image build(Position position, String type) {
+        // BufferedImage
+        // Image image = ImageIO.read(new URL(urlname));
+        // Graphics.drawImage()
+
+/*
+        Image image = ImageIO.read(new File("f://image1.jpg"));
+
+        ImageIO.write(image, "JPEG", new File("f://image2.jpg"));
+*/
+
+        return null;
+    }
+
+    public static ImageBuilder getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) {
+        String[] formatNames;
+        Set<String> set = new HashSet<>();
+
+        formatNames = ImageIO.getReaderFormatNames();
+        for (String formatName : formatNames)
+            set.add(formatName.toLowerCase());
+        System.out.println("Supported read formats: " + set);
+
+        set.clear();
+
+        formatNames = ImageIO.getWriterFormatNames();
+        for (String formatName : formatNames)
+            set.add(formatName.toLowerCase());
+        System.out.println("Supported write formats: " + set);
+
+        set.clear();
+
+        formatNames = ImageIO.getReaderMIMETypes();
+        for (String formatName : formatNames)
+            set.add(formatName.toLowerCase());
+        System.out.println("Supported read MIME types: " + set);
+
+        set.clear();
+
+        formatNames = ImageIO.getWriterMIMETypes();
+        for (String formatName : formatNames)
+            set.add(formatName.toLowerCase());
+        System.out.println("Supported write MIME types: " + set);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/img/ImageBuilder.java b/src/main/java/org/hedgecode/chess/img/ImageBuilder.java
new file mode 100644 (file)
index 0000000..45537d5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.img;
+
+import java.awt.Image;
+
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface ImageBuilder extends Builder {
+
+    String build(Position position);
+
+    Image build(Position position, String type);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/img/ImageFormat.java b/src/main/java/org/hedgecode/chess/img/ImageFormat.java
new file mode 100644 (file)
index 0000000..26954ab
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.img;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Supported image formats for reading/writing.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum ImageFormat {
+
+    PNG ( new String[]{"png"} ),
+    GIF ( new String[]{"gif"} ),
+    JPG ( new String[]{"jpg", "jpeg"} ),
+    SVG ( new String[]{"svg"} ), // todo
+    BMP ( new String[]{"bmp", "wbmp"} );
+
+    private String[] fortmatExts;
+
+    private boolean isRead;
+    private boolean isWrite;
+
+    ImageFormat(String[] exts) {
+        fortmatExts = exts;
+        isRead = isExist(
+                ImageIO.getReaderFormatNames(), fortmatExts
+        );
+        isWrite = isExist(
+                ImageIO.getWriterFormatNames(), fortmatExts
+        );
+    }
+
+    public String getExt() {
+        return fortmatExts[0];
+    }
+
+    public String[] getExts() {
+        return fortmatExts;
+    }
+
+    public boolean isRead() {
+        return isRead;
+    }
+
+    public boolean isWrite() {
+        return isWrite;
+    }
+
+    public static ImageFormat findFormat(String formatName) {
+        if (formatName != null) {
+            for (ImageFormat imageFormat : ImageFormat.values()) {
+                if (isExist(imageFormat.getExts(), formatName))
+                    return imageFormat;
+            }
+        }
+        return null;
+    }
+
+    private static boolean isExist(String[] names, String... args) {
+        for (String arg : args) {
+            for (String name : names) {
+                if (arg.equalsIgnoreCase(name))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+
+/*
+    public static void main(String[] args) {
+        ImageFormat imageFormat = JPG;
+        System.out.println("Supported format: " + imageFormat);
+        imageFormat = findFormat("jpeg");
+        imageFormat = findFormat("svg");
+        imageFormat = findFormat("jpeeg");
+    }
+*/
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/hedgecode/chess/pgn/PGNBuilder.java b/src/main/java/org/hedgecode/chess/pgn/PGNBuilder.java
new file mode 100644 (file)
index 0000000..7095a56
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class PGNBuilder {
+
+
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/pgn/PGNParser.java b/src/main/java/org/hedgecode/chess/pgn/PGNParser.java
new file mode 100644 (file)
index 0000000..274bfdf
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class PGNParser {
+
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Builder.java b/src/main/java/org/hedgecode/chess/position/Builder.java
new file mode 100644 (file)
index 0000000..ce09887
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Builder {
+
+    String build(Position position);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Castle.java b/src/main/java/org/hedgecode/chess/position/Castle.java
new file mode 100644 (file)
index 0000000..c18be1d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Castle {
+
+    NONE,
+    KING,
+    QUEEN,
+    BOTH
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Color.java b/src/main/java/org/hedgecode/chess/position/Color.java
new file mode 100644 (file)
index 0000000..742dc0d
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Color {
+
+    WHITE,
+    BLACK
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/ColorPiece.java b/src/main/java/org/hedgecode/chess/position/ColorPiece.java
new file mode 100644 (file)
index 0000000..77fa7a4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum ColorPiece {
+
+    WHITE_PAWN   (Color.WHITE, Piece.PAWN),
+    WHITE_KNIGHT (Color.WHITE, Piece.KNIGHT),
+    WHITE_BISHOP (Color.WHITE, Piece.BISHOP),
+    WHITE_ROOK   (Color.WHITE, Piece.ROOK),
+    WHITE_QUEEN  (Color.WHITE, Piece.QUEEN),
+    WHITE_KING   (Color.WHITE, Piece.KING),
+
+    BLACK_PAWN   (Color.BLACK, Piece.PAWN),
+    BLACK_KNIGHT (Color.BLACK, Piece.KNIGHT),
+    BLACK_BISHOP (Color.BLACK, Piece.BISHOP),
+    BLACK_ROOK   (Color.BLACK, Piece.ROOK),
+    BLACK_QUEEN  (Color.BLACK, Piece.QUEEN),
+    BLACK_KING   (Color.BLACK, Piece.KING);
+
+    private Color color;
+    private Piece piece;
+
+    ColorPiece(Color color, Piece piece) {
+        this.color = color;
+        this.piece = piece;
+    }
+
+    public Color color() {
+        return color;
+    }
+
+    public Piece piece() {
+        return piece;
+    }
+
+    public static ColorPiece getColorPiece(Color color, Piece piece) {
+        if (color == null || piece == null)
+            return null;
+        for (ColorPiece colorPiece : ColorPiece.values()) {
+            if (colorPiece.color.equals(color) && (colorPiece.piece.equals(piece)))
+                return colorPiece;
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/DiagramPosition.java b/src/main/java/org/hedgecode/chess/position/DiagramPosition.java
new file mode 100644 (file)
index 0000000..e12b477
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class DiagramPosition implements Position {
+
+    protected final Map<Square, ColorPiece> squares = new HashMap<Square, ColorPiece>() {
+        {
+            put(Square.A1, null); put(Square.B1, null); put(Square.C1, null); put(Square.D1, null);
+            put(Square.E1, null); put(Square.F1, null); put(Square.G1, null); put(Square.H1, null);
+            put(Square.A2, null); put(Square.B2, null); put(Square.C2, null); put(Square.D2, null);
+            put(Square.E2, null); put(Square.F2, null); put(Square.G2, null); put(Square.H2, null);
+            put(Square.A3, null); put(Square.B3, null); put(Square.C3, null); put(Square.D3, null);
+            put(Square.E3, null); put(Square.F3, null); put(Square.G3, null); put(Square.H3, null);
+            put(Square.A4, null); put(Square.B4, null); put(Square.C4, null); put(Square.D4, null);
+            put(Square.E4, null); put(Square.F4, null); put(Square.G4, null); put(Square.H4, null);
+            put(Square.A5, null); put(Square.B5, null); put(Square.C5, null); put(Square.D5, null);
+            put(Square.E5, null); put(Square.F5, null); put(Square.G5, null); put(Square.H5, null);
+            put(Square.A6, null); put(Square.B6, null); put(Square.C6, null); put(Square.D6, null);
+            put(Square.E6, null); put(Square.F6, null); put(Square.G6, null); put(Square.H6, null);
+            put(Square.A7, null); put(Square.B7, null); put(Square.C7, null); put(Square.D7, null);
+            put(Square.E7, null); put(Square.F7, null); put(Square.G7, null); put(Square.H7, null);
+            put(Square.A8, null); put(Square.B8, null); put(Square.C8, null); put(Square.D8, null);
+            put(Square.E8, null); put(Square.F8, null); put(Square.G8, null); put(Square.H8, null);
+        }
+    };
+
+    private Color move;
+
+    @Override
+    public Map<Square, ColorPiece> getSquares() {
+        return squares;
+    }
+
+    @Override
+    public Map<Square, ColorPiece> getSquarePieces(Color color) {
+        return getSquarePieces(color, null);
+    }
+
+    @Override
+    public Map<Square, ColorPiece> getSquarePieces(Color color, Piece piece) {
+        Map<Square, ColorPiece> squarePieces = new HashMap<>();
+        for (Map.Entry<Square, ColorPiece> entry : squares.entrySet()) {
+            Square square = entry.getKey();
+            ColorPiece colorPiece = entry.getValue();
+            if (colorPiece != null) {
+                if (color == null || color.equals(colorPiece.color()))
+                    if (piece == null || piece.equals(colorPiece.piece()))
+                        squarePieces.put(square, colorPiece);
+            }
+        }
+        return squarePieces;
+    }
+
+    @Override
+    public void setPiece(Color color, Piece piece, Square square) {
+        squares.put(square, ColorPiece.getColorPiece(color, piece));
+    }
+
+    @Override
+    public void setPiece(ColorPiece colorPiece, Square square) {
+        squares.put(square, colorPiece);
+    }
+
+    @Override
+    public Color getMove() {
+        return move;
+    }
+
+    @Override
+    public void setMove(Color color) {
+        move = color;
+    }
+
+    @Override
+    public boolean isDiagram() {
+        return true;
+    }
+
+    @Override
+    public Castle getCastle(Color color) {
+        return Castle.NONE;
+    }
+
+    @Override
+    public void setCastle(Color color, Castle castle) {
+        // do not supported in this class
+    }
+
+    @Override
+    public Square getEnPassant() {
+        return null;
+    }
+
+    @Override
+    public void setEnPassant(Square square) {
+        // do not supported in this class
+    }
+
+    @Override
+    public int getHalfMove() {
+        return 0;
+    }
+
+    @Override
+    public void setHalfMove(int count) {
+        // do not supported in this class
+    }
+
+    @Override
+    public int getFullMove() {
+        return 0;
+    }
+
+    @Override
+    public void setFullMove(int number) {
+        // do not supported in this class
+    }
+
+    @Override
+    public void clear() {
+        for (Square square : Square.values()) {
+            squares.put(square, null);
+        }
+    }
+
+    @Override
+    public void initial() {
+        PositionBuilder.buildInitial(
+                this
+        );
+    }
+
+    static class PositionBuilder {
+
+        public static void buildInitial(Position position) {
+            position.clear();
+
+            Map<Square, ColorPiece> squares = position.getSquares();
+
+            squares.put(Square.A1, ColorPiece.WHITE_ROOK);
+            squares.put(Square.B1, ColorPiece.WHITE_KNIGHT);
+            squares.put(Square.C1, ColorPiece.WHITE_BISHOP);
+            squares.put(Square.D1, ColorPiece.WHITE_QUEEN);
+            squares.put(Square.E1, ColorPiece.WHITE_KING);
+            squares.put(Square.F1, ColorPiece.WHITE_BISHOP);
+            squares.put(Square.G1, ColorPiece.WHITE_KNIGHT);
+            squares.put(Square.H1, ColorPiece.WHITE_ROOK);
+
+            squares.put(Square.A8, ColorPiece.BLACK_ROOK);
+            squares.put(Square.B8, ColorPiece.BLACK_KNIGHT);
+            squares.put(Square.C8, ColorPiece.BLACK_BISHOP);
+            squares.put(Square.D8, ColorPiece.BLACK_QUEEN);
+            squares.put(Square.E8, ColorPiece.BLACK_KING);
+            squares.put(Square.F8, ColorPiece.BLACK_BISHOP);
+            squares.put(Square.G8, ColorPiece.BLACK_KNIGHT);
+            squares.put(Square.H8, ColorPiece.BLACK_ROOK);
+
+            for (Square square : Square.values()) {
+                if (square.getH() == Square.A2.getH())
+                    squares.put(square, ColorPiece.WHITE_PAWN);
+                else if (square.getH() == Square.A7.getH())
+                    squares.put(square, ColorPiece.BLACK_PAWN);
+            }
+
+            position.setMove(Color.WHITE);
+            position.setCastle(Color.WHITE, Castle.BOTH);
+            position.setCastle(Color.BLACK, Castle.BOTH);
+            position.setEnPassant(null);
+            position.setHalfMove(0);
+            position.setFullMove(1);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/GamePosition.java b/src/main/java/org/hedgecode/chess/position/GamePosition.java
new file mode 100644 (file)
index 0000000..42f9ae2
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class GamePosition extends DiagramPosition {
+
+    private Map<Color, Castle> castles = new HashMap<Color, Castle>(){
+        {
+            put(Color.WHITE, Castle.NONE);
+            put(Color.BLACK, Castle.NONE);
+        }
+    };
+
+    private int halfMove;
+    private int fullMove;
+
+    private Square enPassant;
+
+    @Override
+    public boolean isDiagram() {
+        return false;
+    }
+
+    @Override
+    public Castle getCastle(Color color) {
+        return castles.get(color);
+    }
+
+    @Override
+    public void setCastle(Color color, Castle castle) {
+        castles.put(color, castle);
+    }
+
+    @Override
+    public Square getEnPassant() {
+        return enPassant;
+    }
+
+    @Override
+    public void setEnPassant(Square square) {
+        enPassant = square;
+    }
+
+    @Override
+    public int getHalfMove() {
+        return halfMove;
+    }
+
+    @Override
+    public void setHalfMove(int count) {
+        halfMove = count;
+    }
+
+    @Override
+    public int getFullMove() {
+        return fullMove;
+    }
+
+    @Override
+    public void setFullMove(int number) {
+        fullMove = number;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/ParseException.java b/src/main/java/org/hedgecode/chess/position/ParseException.java
new file mode 100644 (file)
index 0000000..ac1b911
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+import java.util.ResourceBundle;
+
+import org.hedgecode.chess.ChessHogConstants;
+
+/**
+ * Incorrect parsing chess diagram/game Exception.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ParseException extends Exception {
+
+    private static final ResourceBundle LOCALE_BUNDLE =
+            ResourceBundle.getBundle(ChessHogConstants.LOCALE_BUNDLE_FILE);
+
+    private String localeKey;
+    private String message;
+
+    public ParseException(String localeKey) {
+        this.localeKey = localeKey;
+        this.message = null;
+    }
+
+    public ParseException(String localeKey, String message) {
+        this.localeKey = localeKey;
+        this.message = message;
+    }
+
+    public String getLocaleKey() {
+        return localeKey;
+    }
+
+    public String getMessage() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(LOCALE_BUNDLE.getString(localeKey));
+        if (message != null)
+            sb.append(": [").append(message).append("]");
+        return message;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Parser.java b/src/main/java/org/hedgecode/chess/position/Parser.java
new file mode 100644 (file)
index 0000000..ae448ae
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Parser {
+
+    Position parse(String string) throws ParseException;
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Piece.java b/src/main/java/org/hedgecode/chess/position/Piece.java
new file mode 100644 (file)
index 0000000..9554606
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Piece {
+
+    PAWN    ('p'),
+    KNIGHT  ('n'),
+    BISHOP  ('b'),
+    ROOK    ('r'),
+    QUEEN   ('q'),
+    KING    ('k');
+
+    private char letter;
+
+    Piece(char letter) {
+        this.letter = letter;
+    }
+
+    public char letter() {
+        return letter;
+    }
+
+    public static Piece byLetter(char letter) {
+        for (Piece piece : Piece.values()) {
+            if (piece.letter() == letter)
+                return piece;
+        }
+        return null;
+    }
+
+    public static Piece byLetter(String letter) {
+        if (letter == null || letter.length() != 1)
+            return null;
+        for (Piece piece : Piece.values()) {
+            if (piece.letter() == letter.charAt(0))
+                return piece;
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Position.java b/src/main/java/org/hedgecode/chess/position/Position.java
new file mode 100644 (file)
index 0000000..6fd9713
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Position {
+
+    Map<Square, ColorPiece> getSquares();
+
+    Map<Square, ColorPiece> getSquarePieces(Color color);
+
+    Map<Square, ColorPiece> getSquarePieces(Color color, Piece piece);
+
+    void setPiece(Color color, Piece piece, Square square);
+
+    void setPiece(ColorPiece colorPiece, Square square);
+
+    Color getMove();
+
+    void setMove(Color color);
+
+    boolean isDiagram();
+
+    Castle getCastle(Color color);
+
+    void setCastle(Color color, Castle castle);
+
+    Square getEnPassant();
+
+    void setEnPassant(Square square);
+
+    int getHalfMove();
+
+    void setHalfMove(int count);
+
+    int getFullMove();
+
+    void setFullMove(int number);
+
+    void initial();
+
+    void clear();
+
+    default void setPawn(Color color, Square square) {
+        setPiece(color, Piece.PAWN, square);
+    }
+
+    default void setPawns(Color color, Square... squares) {
+        for(Square square : squares)
+            setPiece(color, Piece.PAWN, square);
+    }
+
+    default void setKnight(Color color, Square square) {
+        setPiece(color, Piece.KNIGHT, square);
+    }
+
+    default void setBishop(Color color, Square square) {
+        setPiece(color, Piece.BISHOP, square);
+    }
+
+    default void setRook(Color color, Square square) {
+        setPiece(color, Piece.ROOK, square);
+    }
+
+    default void setQueen(Color color, Square square) {
+        setPiece(color, Piece.QUEEN, square);
+    }
+
+    default void setKing(Color color, Square square) {
+        setPiece(color, Piece.KING, square);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/PositionType.java b/src/main/java/org/hedgecode/chess/position/PositionType.java
new file mode 100644 (file)
index 0000000..0fdd4fe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum PositionType {
+
+    DIAGRAM,
+    GAME
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Positions.java b/src/main/java/org/hedgecode/chess/position/Positions.java
new file mode 100644 (file)
index 0000000..f5071e2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Positions {
+
+    INITIAL {
+        @Override
+        public Position getPosition() {
+            Position position = FACTORY.create();
+            position.initial();
+            return position;
+        }
+    },
+
+    EMPTY {
+        @Override
+        public Position getPosition() {
+            Position position = FACTORY.create();
+            position.clear();
+            return position;
+        }
+    };
+
+    public abstract Position getPosition();
+
+    static class Factory {
+
+        private Factory() {
+        }
+
+        Position create() {
+            return create(
+                    PositionType.GAME
+            );
+        }
+
+        Position create(PositionType type) {
+            Position position;
+            switch (type) {
+                case DIAGRAM:
+                    position = new DiagramPosition();
+                    break;
+                case GAME:
+                default:
+                    position = new GamePosition();
+            }
+            return position;
+        }
+
+    }
+
+    private static final Factory FACTORY = new Factory();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/position/Square.java b/src/main/java/org/hedgecode/chess/position/Square.java
new file mode 100644 (file)
index 0000000..df752a2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.position;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum Square {
+
+    A1 (0, 0), B1 (1, 0), C1 (2, 0), D1 (3, 0), E1 (4, 0), F1 (5, 0), G1 (6, 0), H1 (7, 0),
+    A2 (0, 1), B2 (1, 1), C2 (2, 1), D2 (3, 1), E2 (4, 1), F2 (5, 1), G2 (6, 1), H2 (7, 1),
+    A3 (0, 2), B3 (1, 2), C3 (2, 2), D3 (3, 2), E3 (4, 2), F3 (5, 2), G3 (6, 2), H3 (7, 2),
+    A4 (0, 3), B4 (1, 3), C4 (2, 3), D4 (3, 3), E4 (4, 3), F4 (5, 3), G4 (6, 3), H4 (7, 3),
+    A5 (0, 4), B5 (1, 4), C5 (2, 4), D5 (3, 4), E5 (4, 4), F5 (5, 4), G5 (6, 4), H5 (7, 4),
+    A6 (0, 5), B6 (1, 5), C6 (2, 5), D6 (3, 5), E6 (4, 5), F6 (5, 5), G6 (6, 5), H6 (7, 5),
+    A7 (0, 6), B7 (1, 6), C7 (2, 6), D7 (3, 6), E7 (4, 6), F7 (5, 6), G7 (6, 6), H7 (7, 6),
+    A8 (0, 7), B8 (1, 7), C8 (2, 7), D8 (3, 7), E8 (4, 7), F8 (5, 7), G8 (6, 7), H8 (7, 7);
+
+    private static final int SIZE = 8;
+
+    private int vertical;
+    private int horizontal;
+
+    Square(int vLine, int hLine) {
+        this.vertical = vLine;
+        this.horizontal = hLine;
+    }
+
+    public int getV() {
+        return vertical;
+    }
+
+    public int getH() {
+        return horizontal;
+    }
+
+    public static Square getSquare(int vLine, int hLine) {
+        for (Square square : Square.values()) {
+            if (square.getV() == vLine && square.getH() == hLine)
+                return square;
+        }
+        return null;
+    }
+
+    public static Square getSquare(String squareName) {
+        for (Square square : Square.values()) {
+            if (square.name().equalsIgnoreCase(squareName))
+                return square;
+        }
+        return null;
+    }
+
+    public static int getSize() {
+        return SIZE;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcd/TCD.java b/src/main/java/org/hedgecode/chess/tcd/TCD.java
new file mode 100644 (file)
index 0000000..2d900c8
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tiny Chess Diagram (TCD) constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCD {
+
+    public static final byte BYTE_MASK = 0b01000000;
+    public static final byte FRAME_MASK = 0b111;
+    public static final byte FRAME_LENGTH = 3;
+
+    public static final byte WHITE = 0b000;
+    public static final byte BLACK = 0b111;
+
+    public static final byte END = 0b111;
+
+    public static final byte PAWN   = 0b001;
+    public static final byte KNIGHT = 0b010;
+    public static final byte BISHOP = 0b011;
+    public static final byte ROOK   = 0b100;
+    public static final byte QUEEN  = 0b101;
+    public static final byte KING   = 0b110;
+
+    private static final byte SQUARE_A = 0b000, SQUARE_1 = SQUARE_A;
+    private static final byte SQUARE_B = 0b001, SQUARE_2 = SQUARE_B;
+    private static final byte SQUARE_C = 0b010, SQUARE_3 = SQUARE_C;
+    private static final byte SQUARE_D = 0b011, SQUARE_4 = SQUARE_D;
+    private static final byte SQUARE_E = 0b100, SQUARE_5 = SQUARE_E;
+    private static final byte SQUARE_F = 0b101, SQUARE_6 = SQUARE_F;
+    private static final byte SQUARE_G = 0b110, SQUARE_7 = SQUARE_G;
+    private static final byte SQUARE_H = 0b111, SQUARE_8 = SQUARE_H;
+
+    public static final byte SQUARE_SIZE = 8;
+
+    public static final byte[][] SQUARE = {
+            {SQUARE_A, SQUARE_1},
+            {SQUARE_B, SQUARE_2},
+            {SQUARE_C, SQUARE_3},
+            {SQUARE_D, SQUARE_4},
+            {SQUARE_E, SQUARE_5},
+            {SQUARE_F, SQUARE_6},
+            {SQUARE_G, SQUARE_7},
+            {SQUARE_H, SQUARE_8},
+    };
+
+    public static final byte MOVE = 0b001;
+
+    public static final byte CASTLE_WHITE = 0b000;
+    public static final byte CASTLE_BLACK = 0b111;
+
+    public static final byte CASTLE_NONE = 0b000;
+    public static final byte CASTLE_KING = 0b001;
+    public static final byte CASTLE_QUEEN = 0b010;
+    public static final byte CASTLE_BOTH = 0b011;
+
+    public static final byte[] CASTLE_OPTS = {CASTLE_NONE, CASTLE_KING, CASTLE_QUEEN, CASTLE_BOTH};
+
+    public static final byte EN_PASSANT = 0b100;
+
+    public static final byte HALFMOVE = 0b010;
+    public static final byte FULLMOVE = 0b011;
+
+    public static final byte MIN_BYTE = 0x40;
+    public static final byte MAX_BYTE = 0x7F;
+
+    private static final byte[] REPLACE_TO =
+            {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2D};
+    private static final byte[] REPLACE_FROM =
+            {0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F};
+
+    public static final Charset OUTPUT_CHARSET = StandardCharsets.US_ASCII;
+
+    private static boolean onlyDiagram = true;
+
+    private TCD() {
+    }
+
+    public static void setDiagram(boolean isDiagram) {
+        onlyDiagram = isDiagram;
+    }
+
+    public static boolean isDiagram() {
+        return onlyDiagram;
+    }
+
+    public static void replaceTo(byte[] bytes) {
+        for (int i = 0; i < bytes.length; ++i) {
+            int j = frameSearch(REPLACE_FROM, bytes[i]);
+            if (j >= 0)
+                bytes[i] = REPLACE_TO[j];
+        }
+    }
+
+    public static void replaceFrom(byte[] bytes) {
+        for (int i = 0; i < bytes.length; ++i) {
+            int j = frameSearch(REPLACE_TO, bytes[i]);
+            if (j >= 0)
+                bytes[i] = REPLACE_FROM[j];
+        }
+    }
+
+    public static boolean isValid(byte[] bytes) {
+        if (bytes.length < 1)
+            return false;
+        for (byte bite : bytes) {
+            if (bite < MIN_BYTE)
+                return false;
+        }
+        return true;
+    }
+
+    public static boolean isColor(byte frame) {
+        return frame == WHITE || frame == BLACK;
+    }
+
+    public static boolean isNotColor(byte frame) {
+        return !isColor(frame);
+    }
+
+    public static boolean isWhiteBlack(byte first, byte second) {
+        return first == WHITE && second == BLACK;
+    }
+
+    public static boolean isBlackWhite(byte first, byte second) {
+        return isWhiteBlack(second, first);
+    }
+
+    public static boolean isNotCastle(byte frame) {
+        return frameSearch(CASTLE_OPTS, frame) < 0;
+    }
+
+    private static int frameSearch(byte[] bytes, byte frame) {
+        for (int i = 0; i < bytes.length; ++i)
+            if (bytes[i] == frame)
+                return i;
+        return -1;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcd/TCDBuilder.java b/src/main/java/org/hedgecode/chess/tcd/TCDBuilder.java
new file mode 100644 (file)
index 0000000..bc37d20
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import java.util.Map;
+
+import org.hedgecode.chess.Builders;
+import org.hedgecode.chess.Parsers;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Castle;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Tiny Chess Diagram (TCD) builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCDBuilder implements Builder {
+
+    private static final byte[] EMPTY = {};
+
+    private static Builder _instance = new TCDBuilder();
+
+    private TCDBuilder() {
+    }
+
+    @Override
+    public String build(Position position) {
+        return _build(
+                position
+        );
+    }
+
+    private String _build(Position position) {
+        // todo: isInitial
+        byte[] white = _buildColor(Color.WHITE, position);
+        byte[] black = _buildColor(Color.BLACK, position);
+        byte[] addition = _buildAddition(position);
+
+        byte[] bytes;
+        int length = white.length + black.length + addition.length;
+        if (length % 2 == 0) {
+            bytes = _concat(white, black, addition);
+        } else {
+            byte[] end = {TCD.END};
+            bytes = _concat(white, black, addition, end);
+        }
+
+        bytes = _compress(bytes);
+        TCD.replaceTo(bytes);
+
+        return new String(
+                bytes, TCD.OUTPUT_CHARSET
+        );
+    }
+
+    private byte[] _buildColor(Color color, Position position) {
+        byte[] bytes = {Color.WHITE.equals(color) ? TCD.WHITE : TCD.BLACK};
+        for (Piece piece : Piece.values()) {
+            Map<Square, ColorPiece> squarePieces = position.getSquarePieces(color, piece);
+            if (!squarePieces.isEmpty()) {
+                byte[] pieces = new byte[2 * (squarePieces.size() + 1)];
+                int i = 0;
+                switch (piece) {
+                    case PAWN:
+                        pieces[i++] = TCD.PAWN;
+                        break;
+                    case KNIGHT:
+                        pieces[i++] = TCD.KNIGHT;
+                        break;
+                    case BISHOP:
+                        pieces[i++] = TCD.BISHOP;
+                        break;
+                    case ROOK:
+                        pieces[i++] = TCD.ROOK;
+                        break;
+                    case QUEEN:
+                        pieces[i++] = TCD.QUEEN;
+                        break;
+                    case KING:
+                        pieces[i++] = TCD.KING;
+                        break;
+                }
+                pieces[i++] = (byte) (squarePieces.size() - 1);
+                for (Square square : squarePieces.keySet()) {
+                    pieces[i++] = (byte) square.getV();
+                    pieces[i++] = (byte) square.getH();
+                }
+                bytes = _concat(bytes, pieces);
+            }
+        }
+        return bytes;
+    }
+
+    private byte[] _buildAddition(Position position) {
+        byte[] bytes = {TCD.WHITE, TCD.BLACK};
+        if (!TCD.isDiagram() && !position.isDiagram()) {
+            bytes = _concat(
+                    bytes,
+                    _buildMove(position),
+                    _buildCastle(Color.WHITE, position),
+                    _buildCastle(Color.BLACK, position),
+                    _buildEnPassant(position),
+                    _buildHalfMove(position),
+                    _buildFullMove(position)
+            );
+        }
+        return bytes;
+    }
+
+    private byte[] _buildMove(Position position) {
+        if (position.getMove() != null) {
+            return new byte[] {
+                    TCD.MOVE,
+                    Color.WHITE.equals(position.getMove()) ? TCD.WHITE : TCD.BLACK
+            };
+        }
+        return EMPTY;
+    }
+
+    private byte[] _buildCastle(Color color, Position position) {
+        Castle castle = position.getCastle(color);
+        if (castle != null) {
+            byte castleByte;
+            switch (castle) {
+                case KING:
+                    castleByte = TCD.CASTLE_KING;
+                    break;
+                case QUEEN:
+                    castleByte = TCD.CASTLE_QUEEN;
+                    break;
+                case BOTH:
+                    castleByte = TCD.CASTLE_BOTH;
+                    break;
+                default:
+                    castleByte = TCD.CASTLE_NONE;
+            }
+            return new byte[] {
+                    Color.WHITE.equals(color) ? TCD.CASTLE_WHITE : TCD.CASTLE_BLACK,
+                    castleByte
+            };
+        }
+        return EMPTY;
+    }
+
+    private byte[] _buildEnPassant(Position position) {
+        Square square = position.getEnPassant();
+        if (square != null) {
+            return new byte[] {
+                    TCD.EN_PASSANT,
+                    (byte) square.getV(),
+                    (byte) square.getH()
+            };
+        }
+        return EMPTY;
+    }
+
+    private byte[] _buildHalfMove(Position position) {
+        int halfMove = position.getHalfMove();
+        return new byte[] {
+                TCD.HALFMOVE,
+                (byte) (halfMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+                (byte) (halfMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+                (byte) (halfMove & TCD.FRAME_MASK)
+        };
+    }
+
+    private byte[] _buildFullMove(Position position) {
+        int fullMove = position.getFullMove();
+        return new byte[] {
+                TCD.FULLMOVE,
+                (byte) (fullMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+                (byte) (fullMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+                (byte) (fullMove & TCD.FRAME_MASK),
+        };
+    }
+
+    private byte[] _concat(byte[]... byteArrays) {
+        int length = 0;
+        for (byte[] bytes : byteArrays) {
+            length += bytes.length;
+        }
+        byte[] concatBytes = new byte[length];
+        length = 0;
+        for (byte[] bytes : byteArrays) {
+            if (bytes.length == 0)
+                continue;
+            System.arraycopy(bytes, 0, concatBytes, length, bytes.length);
+            length += bytes.length;
+        }
+        return concatBytes;
+    }
+
+    private byte[] _compress(byte[] bytes) {
+        byte[] cmpBytes = new byte[bytes.length / 2];
+        byte high, low;
+        int i = 0;
+        for (int j = 0; j < cmpBytes.length; ++j) {
+            high = (byte) ((bytes[i++] & TCD.FRAME_MASK) << TCD.FRAME_LENGTH);
+            low  = (byte) (bytes[i++] & TCD.FRAME_MASK);
+            cmpBytes[j] = (byte) (TCD.BYTE_MASK | high | low);
+        }
+        return cmpBytes;
+    }
+
+    public static Builder getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+        Position position = Positions.EMPTY.getPosition();
+
+/*
+        position.setKing(Color.WHITE, Square.C1);
+        position.setQueen(Color.WHITE, Square.B3);
+        position.setRook(Color.WHITE, Square.D1);
+        position.setBishop(Color.WHITE, Square.G5);
+        position.setPawn(Color.WHITE, Square.A2);
+        position.setPawn(Color.WHITE, Square.B2);
+        position.setPawn(Color.WHITE, Square.C2);
+        position.setPawn(Color.WHITE, Square.E4);
+        position.setPawn(Color.WHITE, Square.F2);
+        position.setPawn(Color.WHITE, Square.G2);
+        position.setPawn(Color.WHITE, Square.H2);
+
+        position.setKing(Color.BLACK, Square.E8);
+        position.setQueen(Color.BLACK, Square.E6);
+        position.setRook(Color.BLACK, Square.H8);
+        position.setBishop(Color.BLACK, Square.F8);
+        position.setKnight(Color.BLACK, Square.D7);
+        position.setPawn(Color.BLACK, Square.A7);
+        position.setPawn(Color.BLACK, Square.E5);
+        position.setPawn(Color.BLACK, Square.F7);
+        position.setPawn(Color.BLACK, Square.G7);
+        position.setPawn(Color.BLACK, Square.H7);
+*/
+
+/*
+        position.setKing(Color.WHITE, Square.G1);
+        position.setQueen(Color.WHITE, Square.A4);
+        position.setRook(Color.WHITE, Square.D1);
+        position.setBishop(Color.WHITE, Square.A3);
+        position.setBishop(Color.WHITE, Square.D3);
+        position.setPawn(Color.WHITE, Square.A2);
+        position.setPawn(Color.WHITE, Square.C3);
+        position.setPawn(Color.WHITE, Square.F6);
+        position.setPawn(Color.WHITE, Square.F2);
+        position.setPawn(Color.WHITE, Square.G2);
+        position.setPawn(Color.WHITE, Square.H2);
+
+        position.setKing(Color.BLACK, Square.E8);
+        position.setQueen(Color.BLACK, Square.F3);
+        position.setRook(Color.BLACK, Square.B8);
+        position.setRook(Color.BLACK, Square.G8);
+        position.setBishop(Color.BLACK, Square.B7);
+        position.setBishop(Color.BLACK, Square.B6);
+        position.setKnight(Color.BLACK, Square.E7);
+        position.setPawn(Color.BLACK, Square.A7);
+        position.setPawn(Color.BLACK, Square.C7);
+        position.setPawn(Color.BLACK, Square.D7);
+        position.setPawn(Color.BLACK, Square.F7);
+        position.setPawn(Color.BLACK, Square.H7);
+*/
+
+        position.setKing(Color.WHITE, Square.H1);
+        position.setQueen(Color.WHITE, Square.E3);
+        position.setRook(Color.WHITE, Square.E1);
+        position.setRook(Color.WHITE, Square.G1);
+        position.setBishop(Color.WHITE, Square.D2);
+        position.setBishop(Color.WHITE, Square.G2);
+        position.setKnight(Color.WHITE, Square.B1);
+        position.setPawn(Color.WHITE, Square.A2);
+        position.setPawn(Color.WHITE, Square.B2);
+        position.setPawn(Color.WHITE, Square.D4);
+        position.setPawn(Color.WHITE, Square.G3);
+        position.setPawn(Color.WHITE, Square.H3);
+
+        position.setKing(Color.BLACK, Square.G8);
+        position.setQueen(Color.BLACK, Square.D7);
+        position.setRook(Color.BLACK, Square.F5);
+        position.setRook(Color.BLACK, Square.F2);
+        position.setBishop(Color.BLACK, Square.D6);
+        position.setBishop(Color.BLACK, Square.D3);
+        position.setPawn(Color.BLACK, Square.A6);
+        position.setPawn(Color.BLACK, Square.B4);
+        position.setPawn(Color.BLACK, Square.D5);
+        position.setPawn(Color.BLACK, Square.E6);
+        position.setPawn(Color.BLACK, Square.E4);
+        position.setPawn(Color.BLACK, Square.G7);
+        position.setPawn(Color.BLACK, Square.H6);
+
+/*
+        position.setKing(Color.WHITE, Square.H5);
+        position.setPawn(Color.WHITE, Square.C6);
+        position.setKing(Color.BLACK, Square.A6);
+        position.setPawn(Color.BLACK, Square.F6);
+        position.setPawn(Color.BLACK, Square.G7);
+        position.setPawn(Color.BLACK, Square.H6);
+*/
+
+
+        position.setMove(Color.WHITE);
+
+        position.setCastle(Color.WHITE, Castle.NONE);
+        position.setCastle(Color.BLACK, Castle.NONE);
+
+        //position.setEnPassant(Square.A3);
+
+        position.setHalfMove(0);
+        position.setFullMove(26);
+
+        TCD.setDiagram(false);
+
+        String tcd = getInstance().build(position);
+
+        System.out.println(
+                tcd
+        );
+
+        Position tcdPosition = Parsers.TCD.parser().parse(tcd);
+
+        System.out.println(
+                Builders.ASCII.builder().build(tcdPosition)
+        );
+
+        System.out.println(
+                getInstance().build(tcdPosition)
+        );
+
+        System.out.println(
+                Builders.FEN.builder().build(tcdPosition)
+        );
+
+/*
+        System.out.println(
+                getInstance().build(Positions.INITIAL.getPosition())
+        );
+*/
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcd/TCDParser.java b/src/main/java/org/hedgecode/chess/tcd/TCDParser.java
new file mode 100644 (file)
index 0000000..bcf1989
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import org.hedgecode.chess.position.Castle;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Tiny Chess Diagram (TCD) parser.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCDParser implements Parser {
+
+    private static Parser _instance = new TCDParser();
+
+    private TCDParser() {
+    }
+
+    private final class TCDIterator {
+
+        private byte[] bytes;
+        private int pos;
+        private boolean isHighPart;
+
+        TCDIterator(byte[] bytes) {
+            this.bytes = bytes;
+            this.pos = 0;
+            this.isHighPart = true;
+        }
+
+        boolean hasNext() {
+            return (bytes.length > pos + 1) || (isHighPart && bytes.length > pos);
+        }
+
+        boolean isLast() {
+            return !isHighPart && (bytes.length - 1 == pos);
+        }
+
+        byte next() throws ParseException {
+            isHighPart = !isHighPart;
+            if (isHighPart)
+                pos++;
+            return current();
+        }
+
+        byte current() throws ParseException {
+            if (bytes.length <= pos)
+                throw new ParseException("parse.tcd.index.out.of.bounds");
+            byte frame = bytes[pos];
+            if (isHighPart)
+                frame = (byte) (frame >> TCD.FRAME_LENGTH);
+            return (byte) (frame & TCD.FRAME_MASK);
+        }
+    }
+
+    @Override
+    public Position parse(String string) throws ParseException {
+        return parse(
+                string.getBytes()
+        );
+    }
+
+    public Position parse(byte[] bytes) throws ParseException {
+        TCD.replaceFrom(bytes);
+        if (!TCD.isValid(bytes))
+            throw new ParseException("parse.tcd.invalid.bytes");
+        TCDIterator iterator = new TCDIterator(bytes);
+        return _parse(iterator);
+    }
+
+    private Position _parse(TCDIterator iterator) throws ParseException {
+        Position position;
+
+        byte curr = iterator.current();
+        byte next = iterator.next();
+
+        if (TCD.isBlackWhite(curr, next)) {
+            position = Positions.INITIAL.getPosition();
+            curr = iterator.next();
+            next = iterator.next();
+        } else {
+            position = Positions.EMPTY.getPosition();
+            boolean isWhiteAssigned = false, isBlackAssigned = false;
+            while (!isWhiteAssigned || !isBlackAssigned) {
+                switch (curr) {
+                    case TCD.WHITE:
+                        _parsePieces(Color.WHITE, position, iterator);
+                        isWhiteAssigned = true;
+                        break;
+                    case TCD.BLACK:
+                        _parsePieces(Color.BLACK, position, iterator);
+                        isBlackAssigned = true;
+                        break;
+                    default:
+                        throw new ParseException("parse.tcd.incorrect.color");
+                }
+                curr = iterator.current();
+                next = iterator.next();
+            }
+        }
+
+        if (!TCD.isWhiteBlack(curr, next))
+            throw new ParseException("parse.tcd.incorrect.end");
+
+        _parseAddition(position, iterator);
+
+        return position;
+    }
+
+    private void _parsePieces(Color color, Position position, TCDIterator iterator) throws ParseException {
+        byte piece = iterator.current();
+        while (TCD.isNotColor(piece)) {
+            int pieceCount = iterator.next() + 1;
+            for (int i = 0; i < pieceCount; ++i) {
+                Square square = _parseSquare(iterator);
+                switch (piece) {
+                    case TCD.PAWN:
+                        position.setPawn(color, square);
+                        break;
+                    case TCD.KNIGHT:
+                        position.setKnight(color, square);
+                        break;
+                    case TCD.BISHOP:
+                        position.setBishop(color, square);
+                        break;
+                    case TCD.ROOK:
+                        position.setRook(color, square);
+                        break;
+                    case TCD.QUEEN:
+                        position.setQueen(color, square);
+                        break;
+                    case TCD.KING:
+                        position.setKing(color, square);
+                        break;
+                    default:
+                        throw new ParseException("parse.tcd.incorrect.piece");
+                }
+            }
+            piece = iterator.next();
+        }
+    }
+
+    private void _parseAddition(Position position, TCDIterator iterator) throws ParseException {
+        if (iterator.hasNext()) {
+            byte frame = iterator.next();
+            if (iterator.isLast() && frame != TCD.END) {
+                throw new ParseException("parse.tcd.incorrect.end");
+            } else {
+                boolean isEndPosition = false;
+                while (!isEndPosition) {
+                    switch (frame) {
+                        case TCD.MOVE:
+                            position.setMove(
+                                    _parseMove(iterator)
+                            );
+                            break;
+                        case TCD.CASTLE_WHITE:
+                            position.setCastle(
+                                    Color.WHITE, _parseCastle(iterator)
+                            );
+                            break;
+                        case TCD.CASTLE_BLACK:
+                            position.setCastle(
+                                    Color.BLACK, _parseCastle(iterator)
+                            );
+                            break;
+                        case TCD.EN_PASSANT:
+                            position.setEnPassant(
+                                    _parseSquare(iterator)
+                            );
+                            break;
+                        case TCD.HALFMOVE:
+                            position.setHalfMove(
+                                    _parseNumber(iterator)
+                            );
+                            break;
+                        case TCD.FULLMOVE:
+                            position.setFullMove(
+                                    _parseNumber(iterator)
+                            );
+                            break;
+                        default:
+                            throw new ParseException("parse.tcd.incorrect.addition");
+                    }
+                    isEndPosition = true;
+                    if (iterator.hasNext()) {
+                        frame = iterator.next();
+                        if (iterator.isLast() && frame != TCD.END) {
+                            throw new ParseException("parse.tcd.incorrect.end");
+                        }
+                        isEndPosition = iterator.isLast();
+                    }
+                }
+            }
+        }
+    }
+
+    private Square _parseSquare(TCDIterator iterator) throws ParseException {
+        byte vl = iterator.next();
+        byte hl = iterator.next();
+        return Square.getSquare(vl, hl);
+    }
+
+    private Color _parseMove(TCDIterator iterator) throws ParseException {
+        byte frame = iterator.next();
+        if (TCD.isNotColor(frame))
+            throw new ParseException("parse.tcd.incorrect.move.color");
+        return frame == TCD.WHITE ? Color.WHITE : Color.BLACK;
+    }
+
+    private Castle _parseCastle(TCDIterator iterator) throws ParseException {
+        byte frame = iterator.next();
+        if (TCD.isNotCastle(frame))
+            throw new ParseException("parse.tcd.incorrect.castle");
+        Castle castle = Castle.NONE;
+        switch (frame) {
+            case TCD.CASTLE_KING:
+                castle = Castle.KING;
+                break;
+            case TCD.CASTLE_QUEEN:
+                castle = Castle.QUEEN;
+                break;
+            case TCD.CASTLE_BOTH:
+                castle = Castle.BOTH;
+                break;
+        }
+        return castle;
+    }
+
+    private int _parseNumber(TCDIterator iterator) throws ParseException {
+        return iterator.next() << (2 * TCD.FRAME_LENGTH)
+                | iterator.next() << TCD.FRAME_LENGTH
+                | iterator.next();
+    }
+
+    public static Parser getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+        //Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFD0OvFV+4fnNQwOYoWaG-h5pgG");
+        //Position position = getInstance().parse("xG");
+        Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFDGOvFV+4fnNQwOYoWaG-h5pgG");
+        System.out.println(position);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcg/TCG.java b/src/main/java/org/hedgecode/chess/tcg/TCG.java
new file mode 100644 (file)
index 0000000..d1f7742
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcg;
+
+import org.hedgecode.chess.tcd.TCD;
+
+/**
+ * Tiny Chess Game (TCG) constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCG {
+
+    public static final byte PAWN   = TCD.PAWN;
+    public static final byte KNIGHT = TCD.KNIGHT;
+    public static final byte BISHOP = TCD.BISHOP;
+    public static final byte ROOK   = TCD.ROOK;
+    public static final byte QUEEN  = TCD.QUEEN;
+    public static final byte KING   = TCD.KING;
+
+    public static final byte[][] SQUARE = TCD.SQUARE;
+
+    private TCG() {
+    }
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcg/TCGBuilder.java b/src/main/java/org/hedgecode/chess/tcg/TCGBuilder.java
new file mode 100644 (file)
index 0000000..f1f32ba
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcg;
+
+/**
+ * Tiny Chess Game (TCG) builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCGBuilder {
+
+}
diff --git a/src/main/java/org/hedgecode/chess/tcg/TCGParser.java b/src/main/java/org/hedgecode/chess/tcg/TCGParser.java
new file mode 100644 (file)
index 0000000..0a5c819
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcg;
+
+/**
+ * Tiny Chess Game (TCG) parser.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCGParser {
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/Acceptor.java b/src/main/java/org/hedgecode/chess/uci/Acceptor.java
new file mode 100644 (file)
index 0000000..4dc2e84
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.util.List;
+
+/**
+ * Universal Chess Interface (UCI) Acceptor.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Acceptor {
+
+    void start();
+
+    void id(String params);
+
+    void uciOK(String params);
+
+    void readyOK(String params);
+
+    void setOption(String name);
+    void setOption(String name, boolean def);
+    void setOption(String name, int def, int min, int max);
+    void setOption(String name, String def);
+    void setOption(String name, String def, List<String> vars);
+
+    void terminate(String params);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/AcceptorException.java b/src/main/java/org/hedgecode/chess/uci/AcceptorException.java
new file mode 100644 (file)
index 0000000..f19da9a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.util.ResourceBundle;
+
+/**
+ * UCI Acceptor Exception.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class AcceptorException extends Exception {
+
+    private static final String ACCEPTOR = "UCI Acceptor: ";
+
+    private static final ResourceBundle LOCALE_BUNDLE =
+            ResourceBundle.getBundle(UCIConstants.LOCALE_BUNDLE_FILE);
+
+    private String localeKey;
+    private String message;
+
+    public AcceptorException(String localeKey) {
+        this.localeKey = localeKey;
+        this.message = null;
+    }
+
+    public AcceptorException(String localeKey, String message) {
+        this.localeKey = localeKey;
+        this.message = message;
+    }
+
+    public String getLocaleKey() {
+        return localeKey;
+    }
+
+    public String getMessage() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ACCEPTOR);
+        sb.append(
+                LOCALE_BUNDLE.getString(localeKey)
+        );
+        if (message != null)
+            sb.append(": [").append(message).append("]");
+        return message;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/AcceptorRunner.java b/src/main/java/org/hedgecode/chess/uci/AcceptorRunner.java
new file mode 100644 (file)
index 0000000..138f202
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * Universal Chess Interface (UCI) Acceptor Runner.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface AcceptorRunner extends Runner<Acceptor, AcceptorException> {
+
+    @Override
+    Acceptor init() throws AcceptorException;
+
+    @Override
+    void run(CommandExecutor commandExecutor) throws AcceptorException;
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/AcceptorStub.java b/src/main/java/org/hedgecode/chess/uci/AcceptorStub.java
new file mode 100644 (file)
index 0000000..c667a7d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.util.List;
+
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.OptionParams;
+import org.hedgecode.chess.uci.command.OptionType;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class AcceptorStub implements Acceptor {
+
+    private static final String LOG_TO_FORMAT = ">> %s %s";
+    private static final String LOG_FROM_FORMAT = "<< %s %s";
+
+    private CommandExecutor commandExecutor;
+
+    AcceptorStub() {
+    }
+
+    public void init(CommandExecutor executor) {
+        commandExecutor = executor;
+    }
+
+    @Override
+    public void start() {
+        commandExecutor.exec(
+                UCIConstants.UCI, CommandDirection.TO_ENGINE, null
+        );
+        log(CommandDirection.TO_ENGINE, UCIConstants.UCI, null);
+    }
+
+    @Override
+    public void id(String params) {
+        log(CommandDirection.FROM_ENGINE, UCIConstants.ID, params);
+    }
+
+    @Override
+    public void uciOK(String params) {
+        log(CommandDirection.FROM_ENGINE, UCIConstants.UCI_OK, params);
+        commandExecutor.exec(
+                UCIConstants.IS_READY, CommandDirection.TO_ENGINE, null
+        );
+        log(CommandDirection.TO_ENGINE,  UCIConstants.IS_READY, null);
+    }
+
+    @Override
+    public void readyOK(String params) {
+        log(CommandDirection.FROM_ENGINE, UCIConstants.READY_OK, params);
+        commandExecutor.exec(
+                UCIConstants.QUIT, CommandDirection.TO_ENGINE, null
+        );
+        log(CommandDirection.TO_ENGINE, UCIConstants.QUIT, null);
+    }
+
+    @Override
+    public void setOption(String name) {
+        log(
+                CommandDirection.FROM_ENGINE,
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.BUTTON.type()
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, boolean def) {
+        log(
+                CommandDirection.FROM_ENGINE,
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %b",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.CHECK.type(),
+                        OptionParams.OPTION_DEFAULT, def
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, int def, int min, int max) {
+        log(
+                CommandDirection.FROM_ENGINE,
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %d %s %d %s %d",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.SPIN.type(),
+                        OptionParams.OPTION_DEFAULT, def, OptionParams.OPTION_MIN, min, OptionParams.OPTION_MAX, max
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, String def) {
+        log(
+                CommandDirection.FROM_ENGINE,
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.STRING.type(),
+                        OptionParams.OPTION_DEFAULT, def == null ? "" : def
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, String def, List<String> vars) {
+        StringBuilder varOptions = new StringBuilder();
+        for (String var : vars)
+            varOptions.append(
+                    String.format("%s %s ", OptionParams.OPTION_VAR, var == null ? "" : var)
+            );
+        log(
+                CommandDirection.FROM_ENGINE,
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.COMBO.type(),
+                        OptionParams.OPTION_DEFAULT, def == null ? "" : def, varOptions.toString().trim()
+                )
+        );
+    }
+
+    @Override
+    public void terminate(String params) {
+        log(CommandDirection.FROM_ENGINE, UCIConstants.TERMINATE, params);
+    }
+
+    private void log(CommandDirection direction, String name, String params) {
+        System.out.println(
+                String.format(
+                        CommandDirection.TO_ENGINE.equals(direction)
+                                ? LOG_TO_FORMAT
+                                : LOG_FROM_FORMAT,
+                        name,
+                        params != null ? params : ""
+                ).trim()
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/AcceptorStubRunner.java b/src/main/java/org/hedgecode/chess/uci/AcceptorStubRunner.java
new file mode 100644 (file)
index 0000000..8f8ad14
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class AcceptorStubRunner implements AcceptorRunner {
+
+    private AcceptorStub acceptor;
+
+    AcceptorStubRunner() {
+        acceptor = new AcceptorStub();
+    }
+
+    @Override
+    public Acceptor init() throws AcceptorException {
+        return acceptor;
+    }
+
+    @Override
+    public void run(final CommandExecutor commandExecutor) throws AcceptorException {
+        acceptor.init(commandExecutor);
+        acceptor.start();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/CommandDescriptor.java b/src/main/java/org/hedgecode/chess/uci/CommandDescriptor.java
new file mode 100644 (file)
index 0000000..db88dfb
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class CommandDescriptor {
+
+    private static final char PKG_SEPARATOR = '.';
+    private static final char DIR_SEPARATOR = '/';
+    private static final String CLASS_FILE_SUFFIX = ".class";
+
+    private static final String PACKAGE_IS_NOT_EXISTS = "The package '%s' is not exists.";
+
+    private Map<String, UCICommand> commandsTo = new HashMap<>();
+    private Map<String, UCICommand> commandsFrom = new HashMap<>();
+
+    CommandDescriptor() throws Exception {
+        initCommands();
+    }
+
+    private void initCommands() throws Exception {
+        initCommands(
+                CommandDescriptor.class.getPackage().getName()
+        );
+    }
+
+    private void initCommands(String packageName) throws Exception {
+        URL packageUrl = Thread.currentThread().getContextClassLoader().getResource(
+                packageName.replace(PKG_SEPARATOR, DIR_SEPARATOR)
+        );
+        if (packageUrl == null)
+            throw new IllegalArgumentException(
+                    String.format(PACKAGE_IS_NOT_EXISTS, packageName)
+            );
+
+        List<Class> commandClasses = findCommands(
+                new File(
+                        packageUrl.getFile()
+                ),
+                packageName
+        );
+
+        commandsTo.clear();
+        commandsFrom.clear();
+        for (Class commandClass : commandClasses) {
+            if (UCICommand.class.isAssignableFrom(commandClass)) {
+                UCICommand uciCommand = (UCICommand) commandClass.newInstance();
+                Command command = (Command) commandClass.getAnnotation(Command.class);
+                switch (command.direction()) {
+                    case TO_ENGINE:
+                        commandsTo.put(
+                                command.name(), uciCommand
+                        );
+                        break;
+                    case FROM_ENGINE:
+                        commandsFrom.put(
+                                command.name(), uciCommand
+                        );
+                        break;
+                }
+            }
+        }
+    }
+
+    private List<Class> findCommands(File dir, String packageName) throws Exception {
+        List<Class> commands = new ArrayList<>();
+
+        File[] classFiles = dir.listFiles(
+                new FilenameFilter() {
+                    public boolean accept(File file, String name) {
+                        return name.endsWith(CLASS_FILE_SUFFIX);
+                    }
+                }
+        );
+        if (classFiles != null) {
+            for (File classFile : classFiles) {
+                String className = packageName + PKG_SEPARATOR + classFile.getName();
+                int endIndex = className.length() - CLASS_FILE_SUFFIX.length();
+                className = className.substring(0, endIndex);
+                Class clazz = Class.forName(className);
+                if (clazz.isAnnotationPresent(Command.class)) {
+                    commands.add(clazz);
+                }
+            }
+        }
+
+        File[] subDirs = dir.listFiles(
+                new FilenameFilter() {
+                    public boolean accept(File file, String name) {
+                        return file.isDirectory();
+                    }
+                }
+        );
+        if (subDirs != null) {
+            for (File subDir : subDirs) {
+                commands.addAll(
+                        findCommands(
+                                subDir,
+                                packageName + PKG_SEPARATOR + subDir.getName()
+                        )
+                );
+            }
+        }
+
+        return commands;
+    }
+
+    public UCICommand getCommand(String name, CommandDirection direction) {
+        switch (direction) {
+            case TO_ENGINE:
+                return commandsTo.get(name);
+            case FROM_ENGINE:
+                return commandsFrom.get(name);
+        }
+        return null;
+    }
+
+
+    public static void main(String[] arg) throws Exception {
+        CommandDescriptor cd = new CommandDescriptor();
+        //cd.initCommands();
+        UCICommand command;
+        command = cd.getCommand("uci", CommandDirection.TO_ENGINE);
+        System.out.println(command);
+        command = cd.getCommand("debug", CommandDirection.TO_ENGINE);
+        System.out.println(command);
+        command = cd.getCommand("test", CommandDirection.TO_ENGINE);
+        System.out.println(command);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/CommandEnvironment.java b/src/main/java/org/hedgecode/chess/uci/CommandEnvironment.java
new file mode 100644 (file)
index 0000000..348a902
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.File;
+import java.nio.file.Paths;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class CommandEnvironment {
+
+    private static final String ENGINE_BIN_DIR = Paths.get("bin").toAbsolutePath().toString();
+
+
+    public static void main(String... args) throws Exception {
+
+        EngineRunner engineRunner = new ExternalEngineRunner(
+                ENGINE_BIN_DIR + File.separator + "stockfish.exe" // "houdini.exe" "komodo.exe"
+        );
+        Engine engine = engineRunner.init();
+
+        AcceptorRunner acceptorRunner = new ConsoleAcceptorRunner(); // new AcceptorStubRunner();
+        Acceptor acceptor = acceptorRunner.init();
+        //AcceptorStub acceptor = new AcceptorStub();
+
+        CommandExecutor commandExecutor = SyncCommandExecutor.Factory.create(engine, acceptor);
+
+        //acceptor.init(commandExecutor);
+        engineRunner.run(commandExecutor);
+        acceptorRunner.run(commandExecutor);
+        //acceptor.start();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/CommandException.java b/src/main/java/org/hedgecode/chess/uci/CommandException.java
new file mode 100644 (file)
index 0000000..032b36c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class CommandException extends Exception {
+
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/CommandExecutor.java b/src/main/java/org/hedgecode/chess/uci/CommandExecutor.java
new file mode 100644 (file)
index 0000000..9de15df
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+
+/**
+ * UCI Command Executor.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface CommandExecutor {
+
+    void exec(String commandName, CommandDirection direction, String params);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/ConsoleAcceptor.java b/src/main/java/org/hedgecode/chess/uci/ConsoleAcceptor.java
new file mode 100644 (file)
index 0000000..028cdaf
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Scanner;
+
+import org.hedgecode.chess.uci.command.OptionParams;
+import org.hedgecode.chess.uci.command.OptionType;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ConsoleAcceptor implements Acceptor {
+
+    private class ConsoleReceiver implements Receiver<String> {
+
+        private Scanner scanner;
+        private boolean isActive;
+
+        ConsoleReceiver(InputStream input) {
+            scanner = new Scanner(input);
+            isActive = true;
+        }
+
+        @Override
+        public boolean isActive() {
+            return isActive;
+        }
+
+        @Override
+        public boolean hasCommand() {
+            return isActive && scanner.hasNextLine();
+        }
+
+        @Override
+        public String receiveCommand() {
+            return scanner.nextLine();
+        }
+
+        @Override
+        public void stop() {
+            isActive = false;
+        }
+
+        @Override
+        public void close() {
+            scanner.close();
+        }
+    }
+
+    private ConsoleReceiver receiver;
+    private PrintWriter writer;
+
+    ConsoleAcceptor(InputStream input, OutputStream output) {
+        receiver = new ConsoleReceiver(input);
+        writer = new PrintWriter(output, true);
+    }
+
+    public Receiver<String> receiver() {
+        return receiver;
+    }
+
+    @Override
+    public void start() {
+    }
+
+    @Override
+    public void id(String params) {
+        console(UCIConstants.ID, params);
+    }
+
+    @Override
+    public void uciOK(String params) {
+        console(UCIConstants.UCI_OK, params);
+    }
+
+    @Override
+    public void readyOK(String params) {
+        console(UCIConstants.READY_OK, params);
+    }
+
+    @Override
+    public void setOption(String name) {
+        console(
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.BUTTON.type()
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, boolean def) {
+        console(
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %b",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.CHECK.type(),
+                        OptionParams.OPTION_DEFAULT, def
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, int def, int min, int max) {
+        console(
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %d %s %d %s %d",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.SPIN.type(),
+                        OptionParams.OPTION_DEFAULT, def, OptionParams.OPTION_MIN, min, OptionParams.OPTION_MAX, max
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, String def) {
+        console(
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.STRING.type(),
+                        OptionParams.OPTION_DEFAULT, def == null ? "" : def
+                )
+        );
+    }
+
+    @Override
+    public void setOption(String name, String def, List<String> vars) {
+        StringBuilder varOptions = new StringBuilder();
+        for (String var : vars)
+            varOptions.append(
+                    String.format("%s %s ", OptionParams.OPTION_VAR, var == null ? "" : var)
+            );
+        console(
+                UCIConstants.OPTION,
+                String.format(
+                        "%s %s %s %s %s %s %s",
+                        OptionParams.OPTION_NAME, name, OptionParams.OPTION_TYPE, OptionType.COMBO.type(),
+                        OptionParams.OPTION_DEFAULT, def == null ? "" : def, varOptions.toString().trim()
+                )
+        );
+    }
+
+    @Override
+    public void terminate(String params) {
+        receiver.stop();
+    }
+
+    private void console(String name, String params) {
+        writer.println(
+                String.format(
+                        "%s %s",
+                        name,
+                        params != null ? params : ""
+                ).trim()
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/ConsoleAcceptorRunner.java b/src/main/java/org/hedgecode/chess/uci/ConsoleAcceptorRunner.java
new file mode 100644 (file)
index 0000000..d699eed
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.CommandParams;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ConsoleAcceptorRunner implements AcceptorRunner {
+
+    private ConsoleAcceptor acceptor;
+
+    private boolean isRunning;
+
+    ConsoleAcceptorRunner() {
+        acceptor = new ConsoleAcceptor(
+                System.in, System.out
+        );
+        isRunning = false;
+    }
+
+    ConsoleAcceptorRunner(InputStream input, OutputStream output) {
+        acceptor = new ConsoleAcceptor(
+                input, output
+        );
+        isRunning = false;
+    }
+
+    @Override
+    public Acceptor init() throws AcceptorException {
+        if (isRunning)
+            throw new AcceptorException("uci.acceptor.already.run");
+        return acceptor;
+    }
+
+    public void run(final CommandExecutor commandExecutor) throws AcceptorException {
+        if (isRunning)
+            throw new AcceptorException("uci.acceptor.already.run");
+
+        Thread engineThread = new Thread(
+                new Runnable() {
+                    public void run() {
+                        Receiver<String> acceptorReceiver = acceptor.receiver();
+                        try {
+                            while (acceptorReceiver.isActive()) {
+                                if (acceptorReceiver.hasCommand()) {
+                                    String command = acceptorReceiver.receiveCommand();
+                                    if (command.isEmpty()) continue;
+                                    CommandParams commandParams = new CommandParams(command);
+                                    commandExecutor.exec(
+                                            commandParams.getName(),
+                                            CommandDirection.TO_ENGINE,
+                                            commandParams.getParams()
+                                    );
+                                    if (UCIConstants.QUIT.equals(commandParams.getName()))
+                                        acceptorReceiver.stop();
+                                }
+                            }
+                        } finally {
+                            acceptorReceiver.close();
+                            isRunning = false;
+                        }
+                    }
+                }
+        );
+        engineThread.start();
+        isRunning = true;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/DebugMode.java b/src/main/java/org/hedgecode/chess/uci/DebugMode.java
new file mode 100644 (file)
index 0000000..820da33
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * Debug Mode for UCI Engine.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum DebugMode {
+
+    OFF,
+    ON;
+
+    public String value() {
+        return this.name().toLowerCase();
+    }
+
+    public static DebugMode get(String mode) {
+        for (DebugMode debugMode : DebugMode.values()) {
+            if (debugMode.name().equalsIgnoreCase(mode))
+                return debugMode;
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/Engine.java b/src/main/java/org/hedgecode/chess/uci/Engine.java
new file mode 100644 (file)
index 0000000..0ee971f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * Universal Chess Interface (UCI) Engine.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Engine {
+
+    void init();
+
+    void debug(DebugMode mode);
+
+    void isReady();
+
+    void go();
+
+    void stop();
+
+    void ponderhit();
+
+    void quit();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/EngineException.java b/src/main/java/org/hedgecode/chess/uci/EngineException.java
new file mode 100644 (file)
index 0000000..c48ec3c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.util.ResourceBundle;
+
+/**
+ * UCI Engine Exception.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class EngineException extends Exception {
+
+    private static final String ENGINE = "UCI Engine: ";
+
+    private static final ResourceBundle LOCALE_BUNDLE =
+            ResourceBundle.getBundle(UCIConstants.LOCALE_BUNDLE_FILE);
+
+    private String localeKey;
+    private String message;
+
+    public EngineException(String localeKey) {
+        this.localeKey = localeKey;
+        this.message = null;
+    }
+
+    public EngineException(String localeKey, String message) {
+        this.localeKey = localeKey;
+        this.message = message;
+    }
+
+    public String getLocaleKey() {
+        return localeKey;
+    }
+
+    public String getMessage() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ENGINE);
+        sb.append(
+                LOCALE_BUNDLE.getString(localeKey)
+        );
+        if (message != null)
+            sb.append(": [").append(message).append("]");
+        return message;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/EngineRunner.java b/src/main/java/org/hedgecode/chess/uci/EngineRunner.java
new file mode 100644 (file)
index 0000000..af8ce5a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * Universal Chess Interface (UCI) Engine Runner.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface EngineRunner extends Runner<Engine, EngineException> {
+
+    @Override
+    Engine init() throws EngineException;
+
+    @Override
+    void run(CommandExecutor commandExecutor) throws EngineException;
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/ExternalEngine.java b/src/main/java/org/hedgecode/chess/uci/ExternalEngine.java
new file mode 100644 (file)
index 0000000..7b09fb5
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * External UCI Engine.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ExternalEngine implements Engine {
+
+    private static final String COMMAND_FORMAT = "%s %s";
+    private static final String CRLF = System.getProperty("line.separator");
+
+    private BufferedWriter engineWriter;
+
+    ExternalEngine(BufferedWriter writer) {
+        engineWriter = writer;
+    }
+
+    private void write(String command) {
+        try {
+            engineWriter.write(command);
+            engineWriter.write(CRLF);
+            engineWriter.flush();
+        } catch (IOException ignored) {
+        }
+    }
+
+    private String format(String name, String params) {
+        return String.format(
+                COMMAND_FORMAT, name, params
+        );
+    }
+
+    @Override
+    public void init() {
+        write(
+                UCIConstants.UCI
+        );
+    }
+
+    @Override
+    public void debug(DebugMode mode) {
+        write(
+                format(
+                        UCIConstants.DEBUG, mode.value()
+                )
+        );
+    }
+
+    @Override
+    public void isReady() {
+        write(
+                UCIConstants.IS_READY
+        );
+    }
+
+    @Override
+    public void go() {
+        write(
+                UCIConstants.GO // todo: + params
+        );
+    }
+
+    @Override
+    public void stop() {
+        write(
+                UCIConstants.STOP
+        );
+    }
+
+    @Override
+    public void ponderhit() {
+        write(
+                UCIConstants.PONDER_HIT
+        );
+    }
+
+    @Override
+    public void quit() {
+        write(
+                UCIConstants.QUIT
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/ExternalEngineRunner.java b/src/main/java/org/hedgecode/chess/uci/ExternalEngineRunner.java
new file mode 100644 (file)
index 0000000..fd81352
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.List;
+import java.util.Scanner;
+
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.CommandParams;
+
+/**
+ * External UCI Engine Runner.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ExternalEngineRunner implements EngineRunner {
+
+    private Process process;
+    private ProcessBuilder processBuilder;
+
+    private BufferedReader reader;
+    private BufferedWriter writer;
+
+    private boolean isInit = false;
+
+    ExternalEngineRunner(String extEngineCommand) {
+        processBuilder = new ProcessBuilder(extEngineCommand);
+    }
+
+    ExternalEngineRunner(List<String> extEngineCommands) {
+        processBuilder = new ProcessBuilder(extEngineCommands);
+    }
+
+    @Override
+    public Engine init() throws EngineException {
+        if (isInit)
+            throw new EngineException("uci.engine.already.init");
+
+        try {
+            process = processBuilder.start();
+        } catch (IOException e) {
+            throw new EngineException("uci.engine.external.start", e.getLocalizedMessage());
+        }
+        reader = new BufferedReader(
+                new InputStreamReader(
+                        process.getInputStream()
+                )
+        );
+        writer = new BufferedWriter(
+                new OutputStreamWriter(
+                        process.getOutputStream()
+                )
+        );
+        isInit = true;
+
+        return new ExternalEngine(
+                writer
+        );
+    }
+
+    @Override
+    public void run(final CommandExecutor commandExecutor) throws EngineException {
+        if (!isInit)
+            throw new EngineException("uci.engine.not.init");
+
+        Thread engineThread = new Thread(
+                new Runnable() {
+                    public void run() {
+                        try {
+                            Scanner scanner = new Scanner(reader);
+                            while (scanner.hasNextLine()) {
+                                String command = scanner.nextLine();
+                                if (command.isEmpty()) continue;
+                                CommandParams commandParams = new CommandParams(command);
+                                commandExecutor.exec(
+                                        commandParams.getName(),
+                                        CommandDirection.FROM_ENGINE,
+                                        commandParams.getParams()
+                                );
+                            }
+                        } finally {
+                            try {
+                                writer.close();
+                                reader.close();
+                            } catch (IOException ignored) {
+                            }
+                            commandExecutor.exec(
+                                    UCIConstants.TERMINATE,
+                                    CommandDirection.FROM_ENGINE,
+                                    null
+                            );
+                        }
+                    }
+                }
+        );
+        engineThread.start();
+    }
+
+    public void run() throws IOException, InterruptedException {
+        //Map<String, String> environment = processBuilder.environment();
+        Process process = processBuilder.start();
+        InputStream is = process.getInputStream();
+        BufferedReader br = new BufferedReader(new InputStreamReader(is));
+        OutputStream os = process.getOutputStream();
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
+
+        Thread engineThread = new Thread(
+                new Runnable() {
+                    public void run() {
+                        Scanner scanner = new Scanner(br); //
+                        while (scanner.hasNextLine()) {
+                            System.out.println(scanner.nextLine());
+                        }
+/*
+                        String line;
+                        try {
+                            while ((line = br.readLine()) != null) {
+                                System.out.println(line);
+                                bw.write("isready");
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+*/
+                        System.out.println("Engine terminated.");
+                    }
+                }
+        );
+        engineThread.start();
+
+        bw.write("uci\n");
+        bw.flush();
+        bw.write("isready\n");
+        bw.flush();
+        bw.write("quit\n");
+        bw.flush();
+
+        //process.waitFor();
+
+    }
+
+
+    public static void main(String... args) throws Exception {
+/*
+        String[] strings = " uci     test one more test".trim().split("\\s+", 2);
+        for (String string : strings)
+            System.out.println(string);
+*/
+
+        new ExternalEngineRunner("stockfish.exe").run();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/Receiver.java b/src/main/java/org/hedgecode/chess/uci/Receiver.java
new file mode 100644 (file)
index 0000000..e135642
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Receiver<T> {
+
+    boolean isActive();
+
+    boolean hasCommand();
+
+    T receiveCommand();
+
+    void stop();
+
+    void close();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/Runner.java b/src/main/java/org/hedgecode/chess/uci/Runner.java
new file mode 100644 (file)
index 0000000..7e466ed
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * Common Runner Interface based on commands.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Runner<T, E extends Exception> {
+
+    T init() throws E;
+
+    void run(CommandExecutor commandExecutor) throws E;
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/SyncCommandExecutor.java b/src/main/java/org/hedgecode/chess/uci/SyncCommandExecutor.java
new file mode 100644 (file)
index 0000000..cb024de
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+
+/**
+ * Synchronized UCI Command Executor.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class SyncCommandExecutor implements CommandExecutor {
+
+    private Engine engine;
+    private Acceptor acceptor;
+    private CommandDescriptor commandDescriptor;
+
+    private SyncCommandExecutor(Engine engine, Acceptor acceptor) throws Exception {
+        this.engine = engine;
+        this.acceptor = acceptor;
+        initDescriptor();
+    }
+
+    private void initDescriptor() throws Exception {
+        commandDescriptor = new CommandDescriptor();
+    }
+
+    @Override
+    public synchronized void exec(String commandName, CommandDirection direction, String params) {
+        UCICommand uciCommand = commandDescriptor.getCommand(
+                commandName, direction
+        );
+        if (uciCommand != null) {
+            uciCommand.exec(
+                    engine, acceptor, params
+            );
+        } else {
+            System.out.println(
+                    String.format("%s %s",
+                            commandName,
+                            params != null ? params : ""
+                    ).trim()
+            ); // todo
+        }
+    }
+
+    static final class Factory {
+
+        static CommandExecutor create(Engine engine, Acceptor acceptor) throws Exception {
+            return new SyncCommandExecutor(engine, acceptor);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/Transmitter.java b/src/main/java/org/hedgecode/chess/uci/Transmitter.java
new file mode 100644 (file)
index 0000000..d030563
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Transmitter<T> {
+
+    boolean isActive();
+
+    boolean hasCommand();
+
+    T transmitCommand();
+
+    void addCommand(T command);
+
+    void stop();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/UCICommand.java b/src/main/java/org/hedgecode/chess/uci/UCICommand.java
new file mode 100644 (file)
index 0000000..b5b7d65
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface UCICommand {
+
+    void exec(final Engine engine, final Acceptor acceptor, String params);
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/UCIConstants.java b/src/main/java/org/hedgecode/chess/uci/UCIConstants.java
new file mode 100644 (file)
index 0000000..e8e1bfc
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci;
+
+/**
+ * UCI protocol constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class UCIConstants {
+
+    public static final String UCI = "uci";
+    public static final String DEBUG = "debug";
+    public static final String IS_READY = "isready";
+    public static final String SET_OPTION = "setoption";
+    public static final String REGISTER = "register";
+    public static final String UCI_NEW_GAME = "ucinewgame";
+    public static final String POSITION = "position";
+    public static final String GO = "go";
+    public static final String STOP = "stop";
+    public static final String PONDER_HIT = "ponderhit";
+    public static final String QUIT = "quit";
+
+    public static final String ID = "id";
+    public static final String UCI_OK = "uciok";
+    public static final String READY_OK = "readyok";
+    public static final String BEST_MOVE = "bestmove";
+    public static final String COPY_PROTECTION = "copyprotection";
+    public static final String REGISTRATION = "registration";
+    public static final String INFO = "info";
+    public static final String OPTION = "option";
+    public static final String TERMINATE = "terminate";
+
+    public static final String COMMAND_SEP_REGEX = "\\s+";
+
+    public static final String NULLMOVE = "0000";
+    public static final String MOVE_REGEX = "^([a-h][1-8])([a-h][1-8])([nbrq])?$";
+    public static final int MOVE_REGEX_FROM = 1;
+    public static final int MOVE_REGEX_TO = 2;
+    public static final int MOVE_REGEX_PROMOTE = 3;
+
+    public static final String LOCALE_BUNDLE_FILE = "org.hedgecode.chess.uci.LocalStrings";
+
+
+    private UCIConstants() {
+        throw new AssertionError(
+                "No org.hedgecode.chess.uci.UCIConstants instances!"
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/annotation/Command.java b/src/main/java/org/hedgecode/chess/uci/annotation/Command.java
new file mode 100644 (file)
index 0000000..7c086b4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Target(value= ElementType.TYPE)
+@Retention(value= RetentionPolicy.RUNTIME)
+public @interface Command {
+
+    String name();
+
+    CommandDirection direction();
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/annotation/CommandDirection.java b/src/main/java/org/hedgecode/chess/uci/annotation/CommandDirection.java
new file mode 100644 (file)
index 0000000..fde12b8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.annotation;
+
+/**
+ * UCI command direction.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum CommandDirection {
+    /**
+     * Describes the commands sent from GUI to Engine.
+     */
+    TO_ENGINE,
+
+    /**
+     * Describes the commands sent from Engine to GUI.
+     */
+    FROM_ENGINE
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/AbstractCommand.java b/src/main/java/org/hedgecode/chess/uci/command/AbstractCommand.java
new file mode 100644 (file)
index 0000000..0f65888
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Square;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+
+/**
+ * Abstract UCI command with parsing functionality.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractCommand implements UCICommand {
+
+    private static final String NULLMOVE = UCIConstants.NULLMOVE;
+
+    private static final Pattern MOVE_PATTERN = Pattern.compile(UCIConstants.MOVE_REGEX);
+
+    private static final int SQUARE_FROM = UCIConstants.MOVE_REGEX_FROM;
+    private static final int SQUARE_TO = UCIConstants.MOVE_REGEX_TO;
+    private static final int PIECE_PROMOTE = UCIConstants.MOVE_REGEX_PROMOTE;
+
+
+    protected Move parseMove(String move) {
+        if (NULLMOVE.equals(move))
+            return new Move(null, null);
+
+        Matcher matcher = MOVE_PATTERN.matcher(move);
+        if (matcher.find()) {
+            return new Move(
+                    Square.getSquare(matcher.group(SQUARE_FROM)),
+                    Square.getSquare(matcher.group(SQUARE_TO)),
+                    Piece.byLetter(matcher.group(PIECE_PROMOTE))
+            );
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/CommandParams.java b/src/main/java/org/hedgecode/chess/uci/command/CommandParams.java
new file mode 100644 (file)
index 0000000..4baf6b7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+import org.hedgecode.chess.uci.UCIConstants;
+
+/**
+ * Container class for UCI command with parameters.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class CommandParams {
+
+    private String name;
+    private String params;
+
+    public CommandParams(String command) {
+        String[] arrCommand = command.trim().split(
+                UCIConstants.COMMAND_SEP_REGEX, 2
+        );
+        name = arrCommand[0];
+        params = arrCommand.length > 1 ? arrCommand[1] : null;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getParams() {
+        return params;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/InfoParams.java b/src/main/java/org/hedgecode/chess/uci/command/InfoParams.java
new file mode 100644 (file)
index 0000000..c4d67d4
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+/**
+ * Container class for values of UCI command "info".
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class InfoParams {
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/Move.java b/src/main/java/org/hedgecode/chess/uci/command/Move.java
new file mode 100644 (file)
index 0000000..441e2f9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+import org.hedgecode.chess.game.AbstractMove;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Square;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class Move extends AbstractMove {
+
+    private Piece promote;
+
+    protected Move(Square from, Square to) {
+        super(null, from, to);
+        this.promote = null;
+    }
+
+    protected Move(Square from, Square to, Piece promote) {
+        super(null, from, to);
+        this.promote = promote;
+    }
+
+    @Override
+    public boolean isCapture() {
+        return false;
+    }
+
+    @Override
+    public boolean isCastle() {
+        return false;
+    }
+
+    @Override
+    public boolean isPromote() {
+        return promote != null;
+    }
+
+    @Override
+    public Piece promote() {
+        return promote;
+    }
+
+    @Override
+    public boolean isCheck() {
+        return false;
+    }
+
+    @Override
+    public boolean isMate() {
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/OptionParams.java b/src/main/java/org/hedgecode/chess/uci/command/OptionParams.java
new file mode 100644 (file)
index 0000000..cb677e3
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Container class for values of UCI command "option".
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class OptionParams {
+
+    public static final String OPTION_NAME = "name";
+    public static final String OPTION_TYPE = "type";
+    public static final String OPTION_DEFAULT = "default";
+    public static final String OPTION_MIN = "min";
+    public static final String OPTION_MAX = "max";
+    public static final String OPTION_VAR = "var";
+
+    private static final String OPTION_PIPE = "|";
+    private static final String OPTION_EMPTY = "<empty>";
+
+    private static final Pattern OPTION_PATTERN =
+            Pattern.compile(
+                    String.format("^\\s*%s(.*)%s\\s+(%s)(.*)$", OPTION_NAME, OPTION_TYPE, typeRegex())
+            );
+
+    private static final String PARAMS_REGEX = "%s\\s+([^\\s]+)\\s*";
+
+    private String name;
+    private OptionType type;
+
+    private String defValue;
+    private int minValue, maxValue;
+    private List<String> vars = new ArrayList<>();
+
+    private OptionParams(String name, String type, String params) {
+        this.name = name;
+        this.type = OptionType.get(type);
+        if (!params.isEmpty())
+            parseParams(params);
+    }
+
+    private void parseParams(String option) {
+        parseDefValue(option);
+        parseMinValue(option);
+        parseMaxValue(option);
+        parseVars(option);
+    }
+
+    private void parseDefValue(String option) {
+        Pattern pattern = Pattern.compile(
+                String.format(PARAMS_REGEX, OPTION_DEFAULT)
+        );
+        Matcher matcher = pattern.matcher(option);
+        if (matcher.find()) {
+            defValue = parseEmptyValue(matcher.group(1));
+        }
+    }
+
+    private void parseMinValue(String option) {
+        Pattern pattern = Pattern.compile(
+                String.format(PARAMS_REGEX, OPTION_MIN)
+        );
+        Matcher matcher = pattern.matcher(option);
+        if (matcher.find()) {
+            minValue = Integer.parseInt(
+                    matcher.group(1)
+            );
+        }
+    }
+
+    private void parseMaxValue(String option) {
+        Pattern pattern = Pattern.compile(
+                String.format(PARAMS_REGEX, OPTION_MAX)
+        );
+        Matcher matcher = pattern.matcher(option);
+        if (matcher.find()) {
+            maxValue = Integer.parseInt(
+                    matcher.group(1)
+            );
+        }
+    }
+
+    private void parseVars(String option) {
+        Pattern pattern = Pattern.compile(
+                String.format(PARAMS_REGEX, OPTION_VAR)
+        );
+        Matcher matcher = pattern.matcher(option);
+        vars.clear();
+        while (matcher.find()) {
+            vars.add(
+                    parseEmptyValue(matcher.group(1))
+            );
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public OptionType getType() {
+        return type;
+    }
+
+    public String getDefValue() {
+        return defValue;
+    }
+
+    public int getMinValue() {
+        return minValue;
+    }
+
+    public int getMaxValue() {
+        return maxValue;
+    }
+
+    public List<String> getVars() {
+        return vars;
+    }
+
+    private String parseEmptyValue(String value) {
+        return OPTION_EMPTY.equals(value) ? "" : value;
+    }
+
+    private static String typeRegex() {
+        StringBuilder sb = new StringBuilder();
+        for (OptionType optionType : OptionType.values()) {
+            if (optionType.ordinal() > 0)
+                sb.append(OPTION_PIPE);
+            sb.append(optionType.type());
+        }
+        return sb.toString();
+    }
+
+    public static final class Factory {
+
+        public static OptionParams parse(String option) {
+            Matcher matcher = OPTION_PATTERN.matcher(option);
+            if (matcher.find()) {
+                return new OptionParams(
+                        matcher.group(1).trim(),
+                        matcher.group(2).trim(),
+                        matcher.group(3).trim()
+                );
+            }
+            return null;
+        }
+
+    }
+
+
+    public static void main(String... args) throws Exception {
+        String[] strings = {"name Debug Log File type string default",
+                " name Contempt type spin default 21 min -100 max 100",
+                "   name Analysis Contempt type combo default Both var Off var White var Black var Both",
+                "name Threads type spin default 1 min 1 max 512",
+                "name Hash type spin default 16 min 1 max 131072",
+                "name Clear Hash type button",
+                "name Ponder type check default false",
+                "name MultiPV type spin default 1 min 1 max 500",
+                "name Skill Level type spin default 20 min 0 max 20",
+                "name Move Overhead type spin default 30 min 0 max 5000",
+                "name Minimum Thinking Time type spin default 20 min 0 max 5000",
+                "name Slow Mover type spin default 84 min 10 max 1000",
+                "name nodestime type spin default 0 min 0 max 10000",
+                "name UCI_Chess960 type check default false",
+                "name UCI_AnalyseMode type check default false",
+                "name SyzygyPath type string default <empty>",
+                "name SyzygyProbeDepth type spin default 1 min 1 max 100",
+                "name Syzygy50MoveRule type check default true",
+                "name SyzygyProbeLimit type spin default 7 min 0 max 7"};
+
+        for (String string : strings) {
+            OptionParams optionParams = OptionParams.Factory.parse(string);
+            System.out.println(optionParams);
+        }
+    }
+
+
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/OptionType.java b/src/main/java/org/hedgecode/chess/uci/command/OptionType.java
new file mode 100644 (file)
index 0000000..fc91c6a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+/**
+ * Types of options that UCI Engine can send/receive.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum OptionType {
+
+    /**
+     * Checkbox that can either be true or false.
+     */
+    CHECK ("check"),
+
+    /**
+     * Spin wheel that can be an integer in a certain range.
+     */
+    SPIN ("spin"),
+
+    /**
+     * Combo box that can have different predefined strings as a value.
+     */
+    COMBO ("combo"),
+
+    /**
+     * Button that can be pressed to send a command to the engine.
+     */
+    BUTTON ("button"),
+
+    /**
+     * Text field that has a string as a value, an empty string has the value "<empty>".
+     */
+    STRING ("string");
+
+    private String type;
+
+    OptionType(String type) {
+        this.type = type;
+    }
+
+    public String type() {
+        return type;
+    }
+
+    public static OptionType get(String type) {
+        for (OptionType optionType : OptionType.values()) {
+            if (optionType.type().equals(type))
+                return optionType;
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/BestMoveCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/BestMoveCommand.java
new file mode 100644 (file)
index 0000000..1a9b982
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.BEST_MOVE,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class BestMoveCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/CopyProtectionCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/CopyProtectionCommand.java
new file mode 100644 (file)
index 0000000..5bae74c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.COPY_PROTECTION,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class CopyProtectionCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/IdCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/IdCommand.java
new file mode 100644 (file)
index 0000000..167ba05
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.ID,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class IdCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        acceptor.id(params);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/InfoCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/InfoCommand.java
new file mode 100644 (file)
index 0000000..453463d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.INFO,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class InfoCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/OkCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/OkCommand.java
new file mode 100644 (file)
index 0000000..44d0b99
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.UCI_OK,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class OkCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        acceptor.uciOK(params);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/OptionCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/OptionCommand.java
new file mode 100644 (file)
index 0000000..080d143
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+import org.hedgecode.chess.uci.command.OptionParams;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.OPTION,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class OptionCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        OptionParams optionParams = OptionParams.Factory.parse(params);
+        if (optionParams != null) {
+            switch (optionParams.getType()) {
+                case CHECK:
+                    acceptor.setOption(
+                            optionParams.getName(),
+                            Boolean.parseBoolean(optionParams.getDefValue())
+                    );
+                    break;
+                case SPIN:
+                    acceptor.setOption(
+                            optionParams.getName(),
+                            Integer.parseInt(optionParams.getDefValue()),
+                            optionParams.getMinValue(),
+                            optionParams.getMaxValue()
+                    );
+                    break;
+                case COMBO:
+                    acceptor.setOption(
+                            optionParams.getName(),
+                            optionParams.getDefValue(),
+                            optionParams.getVars()
+                    );
+                    break;
+                case BUTTON:
+                    acceptor.setOption(
+                            optionParams.getName()
+                    );
+                    break;
+                case STRING:
+                    acceptor.setOption(
+                            optionParams.getName(),
+                            optionParams.getDefValue()
+                    );
+                    break;
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/ReadyOkCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/ReadyOkCommand.java
new file mode 100644 (file)
index 0000000..225e7e4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.READY_OK,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class ReadyOkCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        acceptor.readyOK(params);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/RegistrationCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/RegistrationCommand.java
new file mode 100644 (file)
index 0000000..1ba791d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.REGISTRATION,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class RegistrationCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/from/TerminateCommand.java b/src/main/java/org/hedgecode/chess/uci/command/from/TerminateCommand.java
new file mode 100644 (file)
index 0000000..299184e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.from;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.TERMINATE,
+        direction = CommandDirection.FROM_ENGINE
+)
+public class TerminateCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        acceptor.terminate(params);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/DebugCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/DebugCommand.java
new file mode 100644 (file)
index 0000000..558ede9
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.DEBUG,
+        direction = CommandDirection.TO_ENGINE
+)
+public class DebugCommand extends AbstractCommand implements UCICommand {
+
+    private static final String PARAM_REGEX = "[((on)|(off))]";
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        engine.debug(null); // todo
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/GoCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/GoCommand.java
new file mode 100644 (file)
index 0000000..45edc58
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.GO,
+        direction = CommandDirection.TO_ENGINE
+)
+public class GoCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/InitCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/InitCommand.java
new file mode 100644 (file)
index 0000000..870a535
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.UCI,
+        direction = CommandDirection.TO_ENGINE
+)
+public class InitCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        engine.init();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/IsReadyCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/IsReadyCommand.java
new file mode 100644 (file)
index 0000000..47b32c2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.IS_READY,
+        direction = CommandDirection.TO_ENGINE
+)
+public class IsReadyCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        engine.isReady();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/NewGameCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/NewGameCommand.java
new file mode 100644 (file)
index 0000000..ea44bbc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.UCI_NEW_GAME,
+        direction = CommandDirection.TO_ENGINE
+)
+public class NewGameCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/PonderHitCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/PonderHitCommand.java
new file mode 100644 (file)
index 0000000..b3922cc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.PONDER_HIT,
+        direction = CommandDirection.TO_ENGINE
+)
+public class PonderHitCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/PositionCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/PositionCommand.java
new file mode 100644 (file)
index 0000000..981f580
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.POSITION,
+        direction = CommandDirection.TO_ENGINE
+)
+public class PositionCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/QuitCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/QuitCommand.java
new file mode 100644 (file)
index 0000000..0d5a474
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.QUIT,
+        direction = CommandDirection.TO_ENGINE
+)
+public class QuitCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        engine.quit();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/RegisterCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/RegisterCommand.java
new file mode 100644 (file)
index 0000000..05c34ae
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.REGISTER,
+        direction = CommandDirection.TO_ENGINE
+)
+public class RegisterCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/SetOptionCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/SetOptionCommand.java
new file mode 100644 (file)
index 0000000..070c8ec
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.SET_OPTION,
+        direction = CommandDirection.TO_ENGINE
+)
+public class SetOptionCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/uci/command/to/StopCommand.java b/src/main/java/org/hedgecode/chess/uci/command/to/StopCommand.java
new file mode 100644 (file)
index 0000000..102c7b6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command.to;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+import org.hedgecode.chess.uci.UCICommand;
+import org.hedgecode.chess.uci.UCIConstants;
+import org.hedgecode.chess.uci.annotation.Command;
+import org.hedgecode.chess.uci.annotation.CommandDirection;
+import org.hedgecode.chess.uci.command.AbstractCommand;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Command(
+        name = UCIConstants.STOP,
+        direction = CommandDirection.TO_ENGINE
+)
+public class StopCommand extends AbstractCommand implements UCICommand {
+
+    @Override
+    public void exec(final Engine engine, final Acceptor acceptor, String params) {
+        engine.stop();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/wiki/Wiki.java b/src/main/java/org/hedgecode/chess/wiki/Wiki.java
new file mode 100644 (file)
index 0000000..93819d6
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Piece;
+
+/**
+ * Wikipedia chess diagram constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class Wiki {
+
+    public static final String CRLF = System.lineSeparator();
+
+    public static final String EMPTY = "  ";
+    public static final String SEPARATOR = "|";
+
+    public static final String TEMPLATE_BEGIN = "{{";
+    public static final String TEMPLATE_END = "}}";
+
+    public static final String WHITE_PAWN   = "pl";
+    public static final String WHITE_KNIGHT = "nl";
+    public static final String WHITE_BISHOP = "bl";
+    public static final String WHITE_ROOK   = "rl";
+    public static final String WHITE_QUEEN  = "ql";
+    public static final String WHITE_KING   = "kl";
+
+    public static final String BLACK_PAWN   = "pd";
+    public static final String BLACK_KNIGHT = "nd";
+    public static final String BLACK_BISHOP = "bd";
+    public static final String BLACK_ROOK   = "rd";
+    public static final String BLACK_QUEEN  = "qd";
+    public static final String BLACK_KING   = "kd";
+
+    private static final Map<ColorPiece, String> PIECES = new HashMap<ColorPiece, String>() {
+        {
+            put(ColorPiece.WHITE_PAWN, WHITE_PAWN);     put(ColorPiece.BLACK_PAWN, BLACK_PAWN);
+            put(ColorPiece.WHITE_KNIGHT, WHITE_KNIGHT); put(ColorPiece.BLACK_KNIGHT, BLACK_KNIGHT);
+            put(ColorPiece.WHITE_BISHOP, WHITE_BISHOP); put(ColorPiece.BLACK_BISHOP, BLACK_BISHOP);
+            put(ColorPiece.WHITE_ROOK, WHITE_ROOK);     put(ColorPiece.BLACK_ROOK, BLACK_ROOK);
+            put(ColorPiece.WHITE_QUEEN, WHITE_QUEEN);   put(ColorPiece.BLACK_QUEEN, BLACK_QUEEN);
+            put(ColorPiece.WHITE_KING, WHITE_KING);     put(ColorPiece.BLACK_KING, BLACK_KING);
+        }
+    };
+
+    private static boolean withTemplate = false;
+
+    private Wiki() {
+    }
+
+    public static boolean withTemplate() {
+        return withTemplate;
+    }
+
+    public static void setTemplate(boolean isTemplate) {
+        withTemplate = isTemplate;
+    }
+
+    public static ColorPiece getColorPiece(String piece) {
+        if (piece != null && !EMPTY.equals(piece)) {
+            for (Map.Entry<ColorPiece, String> entry : PIECES.entrySet()) {
+                if (entry.getValue().equals(piece))
+                    return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    public static String getWikiPiece(Color color, Piece piece) {
+        return getWikiPiece(
+                ColorPiece.getColorPiece(color, piece)
+        );
+    }
+
+    public static String getWikiPiece(ColorPiece colorPiece) {
+        return (colorPiece != null)
+                ? PIECES.get(colorPiece)
+                : EMPTY;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/wiki/WikiBuilder.java b/src/main/java/org/hedgecode/chess/wiki/WikiBuilder.java
new file mode 100644 (file)
index 0000000..cf26552
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.hedgecode.chess.ChessHogConstants;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Wikipedia chess diagram builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class WikiBuilder implements Builder {
+
+    private static Builder _instance = new WikiBuilder();
+
+    private WikiBuilder() {
+    }
+
+    @Override
+    public String build(Position position) {
+        return _build(
+                position, Wiki.withTemplate()
+        );
+    }
+
+    private String _build(Position position, boolean withTemplate) {
+        StringBuilder sb = new StringBuilder();
+        Map<Square, ColorPiece> squares = position.getSquares();
+
+        if (withTemplate) {
+            ResourceBundle resourceBundle =
+                    ResourceBundle.getBundle(ChessHogConstants.LOCALE_BUNDLE_FILE);
+            sb.append(Wiki.TEMPLATE_BEGIN).append(
+                    resourceBundle.getString("wiki.chess.diagram.name")
+            ).append(Wiki.CRLF);
+            sb.append(Wiki.SEPARATOR).append(Wiki.CRLF);
+            sb.append(Wiki.SEPARATOR).append(Wiki.CRLF);
+        }
+
+        for (int i = Square.getSize() - 1; i >= 0; --i) {
+            for (int j = 0; j < Square.getSize(); ++j) {
+                Square square = Square.getSquare(j, i);
+                ColorPiece piece = squares.get(square);
+                String pieceName = (piece != null) ? Wiki.getWikiPiece(piece) : Wiki.EMPTY;
+                sb.append(Wiki.SEPARATOR).append(pieceName);
+            }
+            sb.append(Wiki.CRLF);
+        }
+
+        if (withTemplate) {
+            sb.append(Wiki.SEPARATOR).append(Wiki.CRLF);
+            sb.append(Wiki.TEMPLATE_END).append(Wiki.CRLF);
+        }
+
+        return sb.toString();
+    }
+
+    public static Builder getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) {
+        // Position position = Positions.INITIAL.getPosition()
+        Position position = Positions.EMPTY.getPosition();
+        position.setKing(Color.WHITE, Square.A1);
+        position.setPawn(Color.WHITE, Square.A2);
+        position.setPawn(Color.WHITE, Square.E4);
+        position.setQueen(Color.WHITE, Square.A7);
+        position.setKing(Color.BLACK, Square.H8);
+        position.setPawn(Color.BLACK, Square.G7);
+        position.setPawn(Color.BLACK, Square.H7);
+        position.setKnight(Color.BLACK, Square.G2);
+        position.setBishop(Color.BLACK, Square.D4);
+        position.setRook(Color.BLACK, Square.E2);
+
+        Wiki.setTemplate(true);
+        System.out.println(
+                getInstance().build(position)
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/chess/wiki/WikiParser.java b/src/main/java/org/hedgecode/chess/wiki/WikiParser.java
new file mode 100644 (file)
index 0000000..93d7ffa
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Wikipedia chess diagram parser.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class WikiParser implements Parser {
+
+    private static final String LINE_REGEX = "\\r?\\n";
+    private static final String SQUARE_REGEX = String.format("[%s]", Wiki.SEPARATOR);
+
+    private static final Pattern PATTERN =
+            Pattern.compile(
+                    String.format("^([%s][^%s]*){%d}$", Wiki.SEPARATOR, Wiki.SEPARATOR, Square.getSize())
+            );
+
+    private static Parser _instance = new WikiParser();
+
+    private WikiParser() {
+    }
+
+    @Override
+    public Position parse(String string) throws ParseException {
+        return _parse(
+                string.split(LINE_REGEX)
+        );
+    }
+
+    private Position _parse(String[] strings) throws ParseException {
+        List<String> lines = new ArrayList<>();
+        for (String line : strings) {
+            Matcher matcher = PATTERN.matcher(line);
+            if (matcher.find()) {
+                lines.add(
+                        matcher.group(0)
+                );
+            }
+        }
+        if (lines.size() != Square.getSize())
+            throw new ParseException("parse.wiki.incorrect.board");
+        Position position = Positions.EMPTY.getPosition();
+        _parsePieces(
+                lines.toArray(
+                        new String[lines.size()]
+                ),
+                position
+        );
+        return position;
+    }
+
+    private void _parsePieces(String[] lines, Position position) throws ParseException {
+        for (int i = 0; i < lines.length; ++i) {
+            int line = lines.length - i;
+            String[] pieces = lines[i].split(SQUARE_REGEX, -1);
+            if (pieces.length != Square.getSize() + 1)
+                throw new ParseException("parse.wiki.incorrect.board");
+            for (int j = 1; j < pieces.length; ++j) {
+                position.setPiece(
+                        Wiki.getColorPiece(pieces[j]),
+                        Square.getSquare(j - 1, line - 1)
+                );
+            }
+        }
+    }
+
+    public static Parser getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) throws ParseException {
+        Position position = getInstance().parse(
+                "{{Шахматная диаграмма\n" +
+                        "|\n" +
+                        "|\n" +
+                        "|rd|nd|bd|qd|kd|bd|nd|rd\n" +
+                        "|pd|pd|  |pd|pd|pd|pd|pd\n" +
+                        "|  |  |  |  |  |  |  |\n" +
+                        "|  |  |  |  |  |  |  |\n" +
+                        "|  |  |  |pd|pl|  |  |\r\n" +
+                        "|  |  |pl|  |  |  |  |\n" +
+                        "|pl|pl|  |  |  |pl|pl|pl\n" +
+                        "|rl|nl|bl|ql|kl|bl|nl|rl\n" +
+                        "| }}"
+        );
+        System.out.println(position);
+    }
+
+}
diff --git a/src/main/resources/org/hedgecode/chess/LocalStrings.properties b/src/main/resources/org/hedgecode/chess/LocalStrings.properties
new file mode 100644 (file)
index 0000000..66a5a66
--- /dev/null
@@ -0,0 +1,31 @@
+# Copyright (c) 2018. Developed by Hedgecode.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale en_US
+
+parse.null.input.string=Null input string
+parse.ascii.incorrect.board=Incorrect board
+parse.fen.invalid.string=Invalid input
+parse.fen.incorrect.board=Incorrect board
+parse.tcd.index.out.of.bounds=Array Index Out Of Bounds
+parse.tcd.invalid.bytes=Invalid input bytes
+parse.tcd.incorrect.color=Incorrect color
+parse.tcd.incorrect.piece=Incorrect piece
+parse.tcd.incorrect.end=Incorrect end
+parse.tcd.incorrect.addition=Incorrect addition
+parse.tcd.incorrect.move.color=Incorrect move color
+parse.tcd.incorrect.castle=Incorrect castle
+parse.wiki.incorrect.board=Incorrect board
+wiki.chess.diagram.name=Chess diagram
diff --git a/src/main/resources/org/hedgecode/chess/LocalStrings_ru.properties b/src/main/resources/org/hedgecode/chess/LocalStrings_ru.properties
new file mode 100644 (file)
index 0000000..05dad64
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright (c) 2018. Developed by Hedgecode.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Localized for Locale ru_RU
+
+parse.null.input.string=
+parse.ascii.incorrect.board=
+parse.fen.invalid.string=
+parse.fen.incorrect.board=
+parse.tcd.index.out.of.bounds=
+parse.tcd.invalid.bytes=
+parse.tcd.incorrect.color=
+parse.tcd.incorrect.piece=
+parse.tcd.incorrect.end=
+parse.tcd.incorrect.addition=
+parse.tcd.incorrect.move.color=
+parse.tcd.incorrect.castle=
+parse.wiki.incorrect.board=
+wiki.chess.diagram.name=\u0428\u0430\u0445\u043C\u0430\u0442\u043D\u0430\u044F \u0434\u0438\u0430\u0433\u0440\u0430\u043C\u043C\u0430
diff --git a/src/main/resources/org/hedgecode/chess/uci/LocalStrings.properties b/src/main/resources/org/hedgecode/chess/uci/LocalStrings.properties
new file mode 100644 (file)
index 0000000..c3f9a7c
--- /dev/null
@@ -0,0 +1,22 @@
+# Copyright (c) 2018. Developed by Hedgecode.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Default localized string information
+# Localized for Locale en_US
+
+uci.acceptor.already.run=UCI Acceptor already running
+uci.engine.not.init=UCI Engine not initialized
+uci.engine.already.init=UCI Engine already initialized
+uci.engine.already.run=UCI Engine already running
+uci.engine.external.start=Error occurred while starting external UCI engine
diff --git a/src/main/resources/org/hedgecode/chess/uci/LocalStrings_ru.properties b/src/main/resources/org/hedgecode/chess/uci/LocalStrings_ru.properties
new file mode 100644 (file)
index 0000000..3a7ce0d
--- /dev/null
@@ -0,0 +1,21 @@
+# Copyright (c) 2018. Developed by Hedgecode.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Localized for Locale ru_RU
+
+uci.acceptor.already.run=UCI \u043F\u0440\u0438\u0451\u043C\u043D\u0438\u043A \u0443\u0436\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D
+uci.engine.not.init=UCI \u0434\u0432\u0438\u0436\u043E\u043A \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D
+uci.engine.already.init=UCI \u0434\u0432\u0438\u0436\u043E\u043A \u0443\u0436\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D
+uci.engine.already.run=UCI \u0434\u0432\u0438\u0436\u043E\u043A \u0443\u0436\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D
+uci.engine.external.start=\u041F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435 \u0432\u043D\u0435\u0448\u043D\u0435\u0433\u043E UCI \u0434\u0432\u0438\u0436\u043A\u0430 \u0432\u043E\u0437\u043D\u0438\u043A\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430
diff --git a/src/test/java/org/hedgecode/chess/AbstractPositionTest.java b/src/test/java/org/hedgecode/chess/AbstractPositionTest.java
new file mode 100644 (file)
index 0000000..6aa3f74
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.SubnodeConfiguration;
+
+import org.junit.Test;
+
+import org.hedgecode.chess.fen.FENBuilderTest;
+import org.hedgecode.chess.position.Castle;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Abstract Common Chess Position Test class.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractPositionTest extends AbstractXMLConfigTest {
+
+    protected static final String NAME = "name";
+    protected static final String POSITION = "position";
+
+    private static final String INITIAL = "initial";
+    private static final String EMPTY = "empty";
+    private static final String SQUARES = "squares";
+    private static final String MOVE = "move";
+    private static final String CASTLE = "castle";
+    private static final String EN_PASSANT = "enPassant";
+    private static final String HALFMOVE = "halfmove";
+    private static final String FULLMOVE = "fullmove";
+
+    private static final String MSG_POSITION = "[%s]:";
+    private static final String MSG_POSITION_SQUARE = "[%s -> Square %s]:";
+    private static final String MSG_POSITION_MOVE = "[%s -> Move]:";
+    private static final String MSG_POSITION_CASTLE = "[%s -> Castle (%s)]:";
+    private static final String MSG_POSITION_EN_PASSANT = "[%s -> En Passant]:";
+    private static final String MSG_POSITION_HALFMOVE = "[%s -> Halfmove]:";
+    private static final String MSG_POSITION_FULLMOVE = "[%s -> Fullmove]:";
+
+    private String positionName;
+
+    public abstract List<XMLConfiguration> getXMLPositions() throws Exception;
+
+    @Test
+    public void testPositions() throws Exception {
+        for (XMLConfiguration xmlConfig : getXMLPositions())
+            testPosition(xmlConfig);
+    }
+
+    public void testPosition(HierarchicalConfiguration xmlPosition) throws Exception {
+        boolean isInitial = false, isEmpty = false;
+        boolean isSquares = !xmlPosition.subset(SQUARES).isEmpty();
+        if (xmlPosition.containsKey(INITIAL))
+            isInitial = xmlPosition.getBoolean(INITIAL);
+        if (xmlPosition.containsKey(EMPTY))
+            isEmpty = xmlPosition.getBoolean(EMPTY);
+        assertTrue(
+                isInitial || isEmpty || isSquares
+        );
+        if (isSquares && !isEmpty) {
+            SubnodeConfiguration squaresNode = xmlPosition.configurationAt(SQUARES);
+            Iterator<String> keys = squaresNode.getKeys();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                String value = squaresNode.getString(key);
+                assertNotNull(
+                        Square.getSquare(key)
+                );
+                assertNotNull(
+                        ColorPiece.valueOf(value)
+                );
+            }
+        }
+        if (xmlPosition.containsKey(MOVE)) {
+            assertNotNull(
+                    Color.valueOf(
+                            xmlPosition.getString(MOVE)
+                    )
+            );
+        }
+        if (!xmlPosition.subset(CASTLE).isEmpty()) {
+            SubnodeConfiguration castleNode = xmlPosition.configurationAt(CASTLE);
+            Iterator<String> keys = castleNode.getKeys();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                String value = castleNode.getString(key);
+                assertNotNull(
+                        Color.valueOf(key.toUpperCase())
+                );
+                assertNotNull(
+                        Castle.valueOf(value)
+                );
+            }
+        }
+        if (xmlPosition.containsKey(EN_PASSANT)) {
+            assertNotNull(
+                    Square.getSquare(
+                            xmlPosition.getString(EN_PASSANT)
+                    )
+            );
+        }
+        if (xmlPosition.containsKey(HALFMOVE)) {
+            assertTrue(
+                    xmlPosition.getInt(HALFMOVE, -1) >= 0
+            );
+        }
+        if (xmlPosition.containsKey(FULLMOVE)) {
+            assertTrue(
+                    xmlPosition.getInt(FULLMOVE, 0) > 0
+            );
+        }
+    }
+
+    @Override
+    protected String getConfigName() {
+        return getClass().getSimpleName();
+    }
+
+    protected Position assignPosition(HierarchicalConfiguration xmlPosition) {
+        if (xmlPosition.containsKey(INITIAL) && xmlPosition.getBoolean(INITIAL)) {
+            return Positions.INITIAL.getPosition();
+        }
+        Position position = Positions.EMPTY.getPosition();
+        if (!xmlPosition.subset(SQUARES).isEmpty()) {
+            SubnodeConfiguration squaresNode = xmlPosition.configurationAt(SQUARES);
+            Iterator<String> keys = squaresNode.getKeys();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                String value = squaresNode.getString(key);
+                position.setPiece(
+                        ColorPiece.valueOf(value),
+                        Square.getSquare(key)
+                );
+            }
+        }
+        if (xmlPosition.containsKey(MOVE))
+            position.setMove(
+                    Color.valueOf(
+                            xmlPosition.getString(MOVE)
+                    )
+            );
+        if (!xmlPosition.subset(CASTLE).isEmpty()) {
+            SubnodeConfiguration castleNode = xmlPosition.configurationAt(CASTLE);
+            Iterator<String> keys = castleNode.getKeys();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                String value = castleNode.getString(key);
+                position.setCastle(
+                        Color.valueOf(key.toUpperCase()),
+                        Castle.valueOf(value)
+                );
+            }
+        }
+        if (xmlPosition.containsKey(EN_PASSANT))
+            position.setEnPassant(
+                    Square.getSquare(
+                            xmlPosition.getString(EN_PASSANT)
+                    )
+            );
+        if (xmlPosition.containsKey(HALFMOVE))
+            position.setHalfMove(
+                    xmlPosition.getInt(HALFMOVE)
+            );
+        if (xmlPosition.containsKey(FULLMOVE))
+            position.setFullMove(
+                    xmlPosition.getInt(FULLMOVE)
+            );
+        return position;
+    }
+
+    protected void assertPositionEquals(Position expected, Position actual) {
+        assertDiagramEquals(
+                expected,
+                actual
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_MOVE,
+                        positionName
+                ),
+                expected.getMove(),
+                actual.getMove()
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_CASTLE,
+                        positionName,
+                        Color.WHITE
+                ),
+                expected.getCastle(Color.WHITE),
+                actual.getCastle(Color.WHITE)
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_CASTLE,
+                        positionName,
+                        Color.BLACK
+                ),
+                expected.getCastle(Color.BLACK),
+                actual.getCastle(Color.BLACK)
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_EN_PASSANT,
+                        positionName
+                ),
+                expected.getEnPassant(),
+                actual.getEnPassant()
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_HALFMOVE,
+                        positionName
+                ),
+                expected.getHalfMove(),
+                actual.getHalfMove()
+        );
+        assertEquals(
+                String.format(
+                        MSG_POSITION_FULLMOVE,
+                        positionName
+                ),
+                expected.getFullMove(),
+                actual.getFullMove()
+        );
+    }
+
+    protected void assertDiagramEquals(Position expected, Position actual) {
+        assertNotNull(
+                expected
+        );
+        assertNotNull(
+                actual
+        );
+        assertSquaresEquals(
+                expected.getSquares(),
+                actual.getSquares()
+        );
+    }
+
+    private void assertSquaresEquals(Map<Square, ColorPiece> expected, Map<Square, ColorPiece> actual) {
+        for (Map.Entry<Square, ColorPiece> entry : expected.entrySet()) {
+            assertEquals(
+                    String.format(
+                            MSG_POSITION_SQUARE,
+                            positionName,
+                            entry.getKey().name()
+                    ),
+                    entry.getValue(),
+                    actual.get(entry.getKey())
+            );
+        }
+    }
+
+    protected void assertStringEquals(String expected, String actual) {
+        assertEquals(
+                String.format(
+                        MSG_POSITION,
+                        positionName
+                ),
+                expected,
+                actual
+        );
+    }
+
+    public String getPositionName() {
+        return positionName;
+    }
+
+    protected void setPositionName(String name) {
+        positionName = name;
+    }
+
+
+/*
+    public static void main(String[] args) throws Exception {
+        XMLConfiguration xmlConfig = new XMLConfiguration(
+                new File(
+                        FENBuilderTest.class.getResource("FENBuilderTest.xml").toURI()
+                )
+        );
+        for (HierarchicalConfiguration testConfig : xmlConfig.configurationsAt(TESTS)) {
+            AbstractPositionTest.testPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+        }
+    }
+*/
+
+}
diff --git a/src/test/java/org/hedgecode/chess/AbstractXMLConfigTest.java b/src/test/java/org/hedgecode/chess/AbstractXMLConfigTest.java
new file mode 100644 (file)
index 0000000..e27274a
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess;
+
+import java.io.File;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Abstract Test class with input params from XML file.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractXMLConfigTest extends Assert {
+
+    private static final String RESOURCES_ROOT = "/";
+    private static final String XML_EXT = ".xml";
+
+    protected static final String CONFIG = "config";
+    protected static final String TESTS = "tests.test";
+
+    @Test
+    public void testXML() throws Exception {
+        XMLConfiguration xmlConfig = getXMLConfig();
+        assertTrue(
+                CONFIG.equals(xmlConfig.getRoot().getName())
+        );
+        assertTrue(
+                xmlConfig.configurationsAt(TESTS).size() > 0
+        );
+    }
+
+    protected abstract String getConfigName();
+
+    protected XMLConfiguration getXMLConfig() throws Exception {
+        String xmlConfigName = getConfigName() + XML_EXT;
+        return new XMLConfiguration(
+                getResourceFile(
+                        xmlConfigName
+                )
+        );
+    }
+
+    protected XMLConfiguration getDefXMLConfig() throws Exception {
+        return new XMLConfiguration(
+                getResourceFile(
+                        getClass().getSimpleName() + XML_EXT
+                )
+        );
+    }
+
+    private File getResourceFile(String name) throws Exception {
+        if (getClass().getResource(name) != null)
+            return new File(
+                    getClass().getResource(name).toURI()
+            );
+        return new File(
+                getClass().getResource(RESOURCES_ROOT).getPath() + name
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/ascii/ASCIIBuilderTest.java b/src/test/java/org/hedgecode/chess/ascii/ASCIIBuilderTest.java
new file mode 100644 (file)
index 0000000..7c96f14
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link ASCIIBuilder}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class ASCIIBuilderTest extends AbstractPositionTest implements ASCIIPositionTest {
+
+    private static final String LINE_REGEX = "\\r?\\n";
+
+    @Test
+    public void testBuild() throws Exception {
+        Builder asciiBuilder = ASCIIBuilder.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_ASCII_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String ascii = testConfig.getString(ASCII_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            if (testConfig.containsKey(ASCII_TYPE)) {
+                ASCII.setType(
+                        ASCIIBoardType.valueOf(
+                                testConfig.getString(ASCII_TYPE)
+                        )
+                );
+            }
+            boolean withNotation = false;
+            if (testConfig.containsKey(ASCII_NOTATION)) {
+                withNotation = testConfig.getBoolean(ASCII_NOTATION);
+            }
+            ASCII.setNotation(withNotation);
+            assertStringEquals(
+                    ascii,
+                    asciiBuilder.build(position)
+            );
+        }
+    }
+
+    @Override
+    protected void assertStringEquals(String expected, String actual) {
+        assertNotNull(expected);
+        assertNotNull(actual);
+        String[] expectedLines = expected.split(LINE_REGEX);
+        String[] actualLines = actual.split(LINE_REGEX);
+        assertEquals(
+                expectedLines.length,
+                actualLines.length
+        );
+        for (int i = 0; i < expectedLines.length; ++i) {
+            super.assertStringEquals(
+                    expectedLines[i].trim(),
+                    actualLines[i].trim()
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/ascii/ASCIIParserTest.java b/src/test/java/org/hedgecode/chess/ascii/ASCIIParserTest.java
new file mode 100644 (file)
index 0000000..87e2698
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link ASCIIParser}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class ASCIIParserTest extends AbstractPositionTest implements ASCIIPositionTest {
+
+    @Test
+    public void testParse() throws Exception {
+        Parser asciiParser = ASCIIParser.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_ASCII_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String ascii = testConfig.getString(ASCII_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            assertDiagramEquals(
+                    position,
+                    asciiParser.parse(ascii)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/ascii/ASCIIPositionTest.java b/src/test/java/org/hedgecode/chess/ascii/ASCIIPositionTest.java
new file mode 100644 (file)
index 0000000..18a52f1
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.ascii;
+
+import java.util.List;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+/**
+ * ASCII Position Test Common Interface.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface ASCIIPositionTest {
+
+    String ASCII_TAG = "ascii";
+    String ASCII_TYPE = "asciiType";
+    String ASCII_NOTATION = "withNotation";
+    String MSG_ASCII_POSITION = "ASCII: %s";
+
+    List<XMLConfiguration> getXMLPositions() throws Exception;
+
+}
diff --git a/src/test/java/org/hedgecode/chess/fen/FENBuilderTest.java b/src/test/java/org/hedgecode/chess/fen/FENBuilderTest.java
new file mode 100644 (file)
index 0000000..26e2ce8
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link FENBuilder}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class FENBuilderTest extends AbstractPositionTest implements FENPositionTest {
+
+    @Test
+    public void testBuild() throws Exception {
+        Builder fenBuilder = FENBuilder.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_FEN_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String fen = testConfig.getString(FEN_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            assertStringEquals(
+                    fen,
+                    fenBuilder.build(position)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/fen/FENParserTest.java b/src/test/java/org/hedgecode/chess/fen/FENParserTest.java
new file mode 100644 (file)
index 0000000..4cb6f87
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link FENParser}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class FENParserTest extends AbstractPositionTest implements FENPositionTest {
+
+    @Test
+    public void testParse() throws Exception {
+        Parser fenParser = FENParser.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_FEN_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String fen = testConfig.getString(FEN_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            assertPositionEquals(
+                    position,
+                    fenParser.parse(fen)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/fen/FENPositionTest.java b/src/test/java/org/hedgecode/chess/fen/FENPositionTest.java
new file mode 100644 (file)
index 0000000..842e26a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.fen;
+
+import java.util.List;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+/**
+ * FEN Position Test Common Interface.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface FENPositionTest {
+
+    String FEN_TAG = "fen";
+    String MSG_FEN_POSITION = "FEN: %s";
+
+    List<XMLConfiguration> getXMLPositions() throws Exception;
+
+}
diff --git a/src/test/java/org/hedgecode/chess/tcd/TCDBuilderTest.java b/src/test/java/org/hedgecode/chess/tcd/TCDBuilderTest.java
new file mode 100644 (file)
index 0000000..0818e31
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link TCDBuilder}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class TCDBuilderTest extends AbstractPositionTest implements TCDPositionTest {
+
+    @Test
+    public void testBuild() throws Exception {
+        Builder tcdBuilder = TCDBuilder.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_TCD_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String tcd = testConfig.getString(TCD_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            assertStringEquals(
+                    tcd,
+                    tcdBuilder.build(position)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/tcd/TCDParserTest.java b/src/test/java/org/hedgecode/chess/tcd/TCDParserTest.java
new file mode 100644 (file)
index 0000000..6472765
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link TCDParser}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class TCDParserTest extends AbstractPositionTest implements TCDPositionTest {
+
+    @Test
+    public void testParse() throws Exception {
+        Parser tcdParser = TCDParser.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_TCD_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String tcd = testConfig.getString(TCD_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            assertPositionEquals(
+                    position,
+                    tcdParser.parse(tcd)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/tcd/TCDPositionTest.java b/src/test/java/org/hedgecode/chess/tcd/TCDPositionTest.java
new file mode 100644 (file)
index 0000000..33e66d3
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.tcd;
+
+import java.util.List;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+/**
+ * TCD Position Test Common Interface.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface TCDPositionTest {
+
+    String TCD_TAG = "tcd";
+    String TCD_DIAGRAM = "isDiagram";
+    String MSG_TCD_POSITION = "TCD: %s";
+
+    List<XMLConfiguration> getXMLPositions() throws Exception;
+
+}
diff --git a/src/test/java/org/hedgecode/chess/uci/command/AbstractCommandTest.java b/src/test/java/org/hedgecode/chess/uci/command/AbstractCommandTest.java
new file mode 100644 (file)
index 0000000..f732337
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.uci.command;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.uci.Acceptor;
+import org.hedgecode.chess.uci.Engine;
+
+/**
+ * Tests for {@link AbstractCommand}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class AbstractCommandTest extends Assert {
+
+    @Test
+    public void testParseMove() throws Exception {
+
+        String[] correctMoves = {"e2e4", "e7e5", "e1g1", "e7e8q", "0000"};
+        for (String correctMove : correctMoves) {
+            Move move = new AbstractCommand() {
+                @Override
+                public void exec(Engine engine, Acceptor acceptor, String params) {
+                    throw new RuntimeException("Don't supported in test class!");
+                }
+            }.parseMove(correctMove);
+
+            assertNotNull(move);
+        }
+
+        String[] incorrectMoves = {"00e2", "e0e5", "e1j1", "a1a9", "e7e8k"};
+        for (String incorrectMove : incorrectMoves) {
+            Move move = new AbstractCommand() {
+                @Override
+                public void exec(Engine engine, Acceptor acceptor, String params) {
+                    throw new RuntimeException("Don't supported in test class!");
+                }
+            }.parseMove(incorrectMove);
+
+            assertNull(move);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/wiki/WikiBuilderTest.java b/src/test/java/org/hedgecode/chess/wiki/WikiBuilderTest.java
new file mode 100644 (file)
index 0000000..cbb2f2c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link WikiBuilder}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class WikiBuilderTest extends AbstractPositionTest implements WikiPositionTest {
+
+    private static final String LINE_REGEX = "\\r?\\n";
+
+    @Test
+    public void testBuild() throws Exception {
+        Builder wikiBuilder = WikiBuilder.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_WIKI_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String wiki = testConfig.getString(WIKI_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            boolean withTemplate = false;
+            if (testConfig.containsKey(WIKI_TEMPLATE)) {
+                withTemplate = testConfig.getBoolean(WIKI_TEMPLATE);
+            }
+            Wiki.setTemplate(withTemplate);
+            assertStringEquals(
+                    wiki,
+                    wikiBuilder.build(position)
+            );
+        }
+    }
+
+    @Override
+    protected void assertStringEquals(String expected, String actual) {
+        assertNotNull(expected);
+        assertNotNull(actual);
+        String[] expectedLines = expected.split(LINE_REGEX);
+        String[] actualLines = actual.split(LINE_REGEX);
+        assertEquals(
+                expectedLines.length,
+                actualLines.length
+        );
+        for (int i = 0; i < expectedLines.length; ++i) {
+            super.assertStringEquals(
+                    expectedLines[i].trim(),
+                    actualLines[i].trim()
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/wiki/WikiParserTest.java b/src/test/java/org/hedgecode/chess/wiki/WikiParserTest.java
new file mode 100644 (file)
index 0000000..eb397ee
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.hedgecode.chess.AbstractPositionTest;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Tests for {@link WikiParser}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class WikiParserTest extends AbstractPositionTest implements WikiPositionTest {
+
+    @Test
+    public void testParse() throws Exception {
+        Parser wikiParser = WikiParser.getInstance();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            setPositionName(
+                    String.format(
+                            MSG_WIKI_POSITION, testConfig.getString(NAME)
+                    )
+            );
+            String wiki = testConfig.getString(WIKI_TAG);
+            Position position = assignPosition(
+                    testConfig.configurationAt(POSITION)
+            );
+            boolean withTemplate = false;
+            if (testConfig.containsKey(WIKI_TEMPLATE)) {
+                withTemplate = testConfig.getBoolean(WIKI_TEMPLATE);
+            }
+            Wiki.setTemplate(withTemplate);
+            assertDiagramEquals(
+                    position,
+                    wikiParser.parse(wiki)
+            );
+        }
+    }
+
+    @Override
+    public List<XMLConfiguration> getXMLPositions() throws Exception {
+        List<XMLConfiguration> xmlPositions = new LinkedList<>();
+        for (HierarchicalConfiguration testConfig : getXMLConfig().configurationsAt(TESTS)) {
+            xmlPositions.add(
+                    new XMLConfiguration(
+                            testConfig.configurationAt(POSITION)
+                    )
+            );
+        }
+        return xmlPositions;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/hedgecode/chess/wiki/WikiPositionTest.java b/src/test/java/org/hedgecode/chess/wiki/WikiPositionTest.java
new file mode 100644 (file)
index 0000000..f298620
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.wiki;
+
+import java.util.List;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+/**
+ * Wiki Position Test Common Interface.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface WikiPositionTest {
+
+    String WIKI_TAG = "wiki";
+    String WIKI_TEMPLATE = "withTemplate";
+    String MSG_WIKI_POSITION = "WIKI: %s";
+
+    List<XMLConfiguration> getXMLPositions() throws Exception;
+
+}
diff --git a/src/test/resources/org/hedgecode/chess/ascii/ASCIIBuilderTest.xml b/src/test/resources/org/hedgecode/chess/ascii/ASCIIBuilderTest.xml
new file mode 100644 (file)
index 0000000..364cc97
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+ +-------------------------------+
+ | r | n | b | q | k | b | n | r |
+ +-------------------------------+
+ | p | p | p | p | p | p | p | p |
+ +-------------------------------+
+ |   |   |   |   |   |   |   |   |
+ +-------------------------------+
+ |   |   |   |   |   |   |   |   |
+ +-------------------------------+
+ |   |   |   |   |   |   |   |   |
+ +-------------------------------+
+ |   |   |   |   |   |   |   |   |
+ +-------------------------------+
+ | P | P | P | P | P | P | P | P |
+ +-------------------------------+
+ | R | N | B | Q | K | B | N | R |
+ +-------------------------------+
+                ]]>
+            </ascii>
+            <asciiType>HYPHEN</asciiType>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+  ___ ___ ___ ___ ___ ___ ___ ___
+ |_r_|_n_|_b_|_q_|_k_|_b_|_n_|_r_|
+ |_p_|_p_|_p_|_p_|_p_|_p_|_p_|_p_|
+ |___|___|___|___|___|___|___|___|
+ |___|___|___|___|___|___|___|___|
+ |___|___|___|___|___|___|___|___|
+ |___|___|___|___|___|___|___|___|
+ |_P_|_P_|_P_|_P_|_P_|_P_|_P_|_P_|
+ |_R_|_N_|_B_|_Q_|_K_|_B_|_N_|_R_|
+                ]]>
+            </ascii>
+            <asciiType>TIGHT</asciiType>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+      a  b  c  d  e  f  g  h
+    +------------------------+
+  8 | r  n  b  q  k  b  n  r | 8
+  7 | p  p  p  p  p  p  p  p | 7
+  6 | .  .  .  .  .  .  .  . | 6
+  5 | .  .  .  .  .  .  .  . | 5
+  4 | .  .  .  .  .  .  .  . | 4
+  3 | .  .  .  .  .  .  .  . | 3
+  2 | P  P  P  P  P  P  P  P | 2
+  1 | R  N  B  Q  K  B  N  R | 1
+    +------------------------+
+      a  b  c  d  e  f  g  h
+                ]]>
+            </ascii>
+            <asciiType>DOT</asciiType>
+            <withNotation>true</withNotation>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+ ╔═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╗
+ ║ r │ n │ b │ q │ k │ b │ n │ r ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║ p │ p │ p │ p │ p │ p │ p │ p ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║   │   │   │   │   │   │   │   ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║   │   │   │   │   │   │   │   ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║   │   │   │   │   │   │   │   ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║   │   │   │   │   │   │   │   ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║ P │ P │ P │ P │ P │ P │ P │ P ║
+ ╟───┼───┼───┼───┼───┼───┼───┼───╢
+ ║ R │ N │ B │ Q │ K │ B │ N │ R ║
+ ╚═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝
+                ]]>
+            </ascii>
+            <asciiType>CP866</asciiType>
+            <withNotation>false</withNotation>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Reti's Etude</name>
+            <ascii>
+                <![CDATA[
+ +------------------------+
+ | .  .  .  .  .  .  .  K |
+ | .  .  .  .  .  .  .  . |
+ | k  .  P  .  .  .  .  . |
+ | .  .  .  .  .  .  .  p |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ +------------------------+
+                ]]>
+            </ascii>
+            <asciiType>DOT</asciiType>
+            <position>
+                <squares>
+                    <h8>WHITE_KING</h8>
+                    <c6>WHITE_PAWN</c6>
+                    <a6>BLACK_KING</a6>
+                    <h5>BLACK_PAWN</h5>
+                </squares>
+                <move>WHITE</move>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <ascii>
+                <![CDATA[
+ +------------------------+
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ +------------------------+
+                ]]>
+            </ascii>
+            <asciiType>DOT</asciiType>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/ascii/ASCIIParserTest.xml b/src/test/resources/org/hedgecode/chess/ascii/ASCIIParserTest.xml
new file mode 100644 (file)
index 0000000..0a84219
--- /dev/null
@@ -0,0 +1,186 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
++-------------------------------+
+| r | n | b | q | k | b | n | r |
++-------------------------------+
+| p | p | p | p | p | p | p | p |
++-------------------------------+
+|   |   |   |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   |   |   |   |
++-------------------------------+
+| P | P | P | P | P | P | P | P |
++-------------------------------+
+| R | N | B | Q | K | B | N | R |
++-------------------------------+
+                ]]>
+            </ascii>
+            <asciiType>HYPHEN</asciiType>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+ ___ ___ ___ ___ ___ ___ ___ ___
+|_r_|_n_|_b_|_q_|_k_|_b_|_n_|_r_|
+|_p_|_p_|_p_|_p_|_p_|_p_|_p_|_p_|
+|___|___|___|___|___|___|___|___|
+|___|___|___|___|___|___|___|___|
+|___|___|___|___|___|___|___|___|
+|___|___|___|___|___|___|___|___|
+|_P_|_P_|_P_|_P_|_P_|_P_|_P_|_P_|
+|_R_|_N_|_B_|_Q_|_K_|_B_|_N_|_R_|
+                ]]>
+            </ascii>
+            <asciiType>TIGHT</asciiType>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+     a  b  c  d  e  f  g  h
+   +------------------------+
+ 8 | r  n  b  q  k  b  n  r | 8
+ 7 | p  p  p  p  p  p  p  p | 7
+ 6 | .  .  .  .  .  .  .  . | 6
+ 5 | .  .  .  .  .  .  .  . | 5
+ 4 | .  .  .  .  .  .  .  . | 4
+ 3 | .  .  .  .  .  .  .  . | 3
+ 2 | P  P  P  P  P  P  P  P | 2
+ 1 | R  N  B  Q  K  B  N  R | 1
+   +------------------------+
+     a  b  c  d  e  f  g  h
+                ]]>
+            </ascii>
+            <asciiType>DOT</asciiType>
+            <withNotation>true</withNotation>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Initial Position</name>
+            <ascii>
+                <![CDATA[
+╔═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╗
+║ r │ n │ b │ q │ k │ b │ n │ r ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║ p │ p │ p │ p │ p │ p │ p │ p ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║   │   │   │   │   │   │   │   ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║   │   │   │   │   │   │   │   ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║   │   │   │   │   │   │   │   ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║   │   │   │   │   │   │   │   ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║ P │ P │ P │ P │ P │ P │ P │ P ║
+╟───┼───┼───┼───┼───┼───┼───┼───╢
+║ R │ N │ B │ Q │ K │ B │ N │ R ║
+╚═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝
+                ]]>
+            </ascii>
+            <asciiType>CP866</asciiType>
+            <withNotation>false</withNotation>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Dilaram's Mate</name>
+            <ascii>
+                <![CDATA[
++-------------------------------+
+|   | r |   |   |   |   | k |   |
++-------------------------------+
+|   |   |   |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   | P | P |   |
++-------------------------------+
+| K |   | n |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   |   | N | R |
++-------------------------------+
+|   |   |   |   |   |   |   | B |
++-------------------------------+
+|   | r |   |   |   |   |   |   |
++-------------------------------+
+|   |   |   |   |   |   |   | R |
++-------------------------------+
+                ]]>
+            </ascii>
+            <asciiType>HYPHEN</asciiType>
+            <position>
+                <squares>
+                    <a5>WHITE_KING</a5>
+                    <h4>WHITE_ROOK</h4>
+                    <h1>WHITE_ROOK</h1>
+                    <h3>WHITE_BISHOP</h3>
+                    <g4>WHITE_KNIGHT</g4>
+                    <f6>WHITE_PAWN</f6>
+                    <g6>WHITE_PAWN</g6>
+                    <g8>BLACK_KING</g8>
+                    <b8>BLACK_ROOK</b8>
+                    <b2>BLACK_ROOK</b2>
+                    <c5>BLACK_KNIGHT</c5>
+                </squares>
+                <move>WHITE</move>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <ascii>
+                <![CDATA[
+ +------------------------+
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ | .  .  .  .  .  .  .  . |
+ +------------------------+
+                ]]>
+            </ascii>
+            <asciiType>DOT</asciiType>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/fen/FENBuilderTest.xml b/src/test/resources/org/hedgecode/chess/fen/FENBuilderTest.xml
new file mode 100644 (file)
index 0000000..a68df57
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <fen>rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1</fen>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Morphy's Opera Game</name>
+            <fen>4kb1r/p2n1ppp/4q3/4p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 16</fen>
+            <position>
+                <squares>
+                    <c1>WHITE_KING</c1>
+                    <b3>WHITE_QUEEN</b3>
+                    <d1>WHITE_ROOK</d1>
+                    <g5>WHITE_BISHOP</g5>
+                    <a2>WHITE_PAWN</a2>
+                    <b2>WHITE_PAWN</b2>
+                    <c2>WHITE_PAWN</c2>
+                    <e4>WHITE_PAWN</e4>
+                    <f2>WHITE_PAWN</f2>
+                    <g2>WHITE_PAWN</g2>
+                    <h2>WHITE_PAWN</h2>
+                    <e8>BLACK_KING</e8>
+                    <e6>BLACK_QUEEN</e6>
+                    <h8>BLACK_ROOK</h8>
+                    <f8>BLACK_BISHOP</f8>
+                    <d7>BLACK_KNIGHT</d7>
+                    <a7>BLACK_PAWN</a7>
+                    <e5>BLACK_PAWN</e5>
+                    <f7>BLACK_PAWN</f7>
+                    <g7>BLACK_PAWN</g7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>KING</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>16</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Immortal Game</name>
+            <fen>r1bk2nr/p2p1pNp/n2B4/1p1NP2P/6P1/3P1Q2/P1P1K3/q5b1 w - - 1 22</fen>
+            <position>
+                <squares>
+                    <e2>WHITE_KING</e2>
+                    <f3>WHITE_QUEEN</f3>
+                    <d6>WHITE_BISHOP</d6>
+                    <d5>WHITE_KNIGHT</d5>
+                    <g7>WHITE_KNIGHT</g7>
+                    <a2>WHITE_PAWN</a2>
+                    <c2>WHITE_PAWN</c2>
+                    <d3>WHITE_PAWN</d3>
+                    <e5>WHITE_PAWN</e5>
+                    <g4>WHITE_PAWN</g4>
+                    <h5>WHITE_PAWN</h5>
+                    <d8>BLACK_KING</d8>
+                    <a1>BLACK_QUEEN</a1>
+                    <a8>BLACK_ROOK</a8>
+                    <h8>BLACK_ROOK</h8>
+                    <c8>BLACK_BISHOP</c8>
+                    <g1>BLACK_BISHOP</g1>
+                    <a6>BLACK_KNIGHT</a6>
+                    <g8>BLACK_KNIGHT</g8>
+                    <a7>BLACK_PAWN</a7>
+                    <b5>BLACK_PAWN</b5>
+                    <d7>BLACK_PAWN</d7>
+                    <f7>BLACK_PAWN</f7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>1</halfmove>
+                <fullmove>22</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <fen>8/8/8/8/8/8/8/8 w - - 0 1</fen>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+                <halfmove>0</halfmove>
+                <fullmove>1</fullmove>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/fen/FENParserTest.xml b/src/test/resources/org/hedgecode/chess/fen/FENParserTest.xml
new file mode 100644 (file)
index 0000000..cd6a652
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <fen>rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1</fen>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Evergreen Game</name>
+            <fen>1r2k1r1/pbppnp1p/1b3P2/8/Q7/B1PB1q2/P4PPP/3R2K1 w - - 0 21</fen>
+            <position>
+                <squares>
+                    <g1>WHITE_KING</g1>
+                    <a4>WHITE_QUEEN</a4>
+                    <d1>WHITE_ROOK</d1>
+                    <a3>WHITE_BISHOP</a3>
+                    <d3>WHITE_BISHOP</d3>
+                    <a2>WHITE_PAWN</a2>
+                    <c3>WHITE_PAWN</c3>
+                    <f6>WHITE_PAWN</f6>
+                    <f2>WHITE_PAWN</f2>
+                    <g2>WHITE_PAWN</g2>
+                    <h2>WHITE_PAWN</h2>
+                    <e8>BLACK_KING</e8>
+                    <f3>BLACK_QUEEN</f3>
+                    <b8>BLACK_ROOK</b8>
+                    <g8>BLACK_ROOK</g8>
+                    <b7>BLACK_BISHOP</b7>
+                    <b6>BLACK_BISHOP</b6>
+                    <e7>BLACK_KNIGHT</e7>
+                    <a7>BLACK_PAWN</a7>
+                    <c7>BLACK_PAWN</c7>
+                    <d7>BLACK_PAWN</d7>
+                    <f7>BLACK_PAWN</f7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>21</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Immortal Zugzwang Game</name>
+            <fen>6k1/3q2p1/p2bp2p/3p1r2/1p1Pp3/3bQ1PP/PP1B1rB1/1N2R1RK w - - 0 26</fen>
+            <position>
+                <squares>
+                    <h1>WHITE_KING</h1>
+                    <e3>WHITE_QUEEN</e3>
+                    <e1>WHITE_ROOK</e1>
+                    <g1>WHITE_ROOK</g1>
+                    <d2>WHITE_BISHOP</d2>
+                    <g2>WHITE_BISHOP</g2>
+                    <b1>WHITE_KNIGHT</b1>
+                    <a2>WHITE_PAWN</a2>
+                    <b2>WHITE_PAWN</b2>
+                    <d4>WHITE_PAWN</d4>
+                    <g3>WHITE_PAWN</g3>
+                    <h3>WHITE_PAWN</h3>
+                    <g8>BLACK_KING</g8>
+                    <d7>BLACK_QUEEN</d7>
+                    <f5>BLACK_ROOK</f5>
+                    <f2>BLACK_ROOK</f2>
+                    <d6>BLACK_BISHOP</d6>
+                    <d3>BLACK_BISHOP</d3>
+                    <a6>BLACK_PAWN</a6>
+                    <b4>BLACK_PAWN</b4>
+                    <d5>BLACK_PAWN</d5>
+                    <e6>BLACK_PAWN</e6>
+                    <e4>BLACK_PAWN</e4>
+                    <g7>BLACK_PAWN</g7>
+                    <h6>BLACK_PAWN</h6>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>26</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <fen>8/8/8/8/8/8/8/8 w - - 0 1</fen>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+                <halfmove>0</halfmove>
+                <fullmove>1</fullmove>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/tcd/TCDBuilderTest.xml b/src/test/resources/org/hedgecode/chess/tcd/TCDBuilderTest.xml
new file mode 100644 (file)
index 0000000..07a0713
--- /dev/null
@@ -0,0 +1,125 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <tcd>xG</tcd>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Morphy's Opera Game</name>
+            <tcd>AwNMIHJL1FdCEAVBGLvF+dnP4Xo6-hepgGH0yP0XP</tcd>
+            <position>
+                <squares>
+                    <c1>WHITE_KING</c1>
+                    <b3>WHITE_QUEEN</b3>
+                    <d1>WHITE_ROOK</d1>
+                    <g5>WHITE_BISHOP</g5>
+                    <a2>WHITE_PAWN</a2>
+                    <b2>WHITE_PAWN</b2>
+                    <c2>WHITE_PAWN</c2>
+                    <e4>WHITE_PAWN</e4>
+                    <f2>WHITE_PAWN</f2>
+                    <g2>WHITE_PAWN</g2>
+                    <h2>WHITE_PAWN</h2>
+                    <e8>BLACK_KING</e8>
+                    <e6>BLACK_QUEEN</e6>
+                    <h8>BLACK_ROOK</h8>
+                    <f8>BLACK_BISHOP</f8>
+                    <d7>BLACK_KNIGHT</d7>
+                    <a7>BLACK_PAWN</a7>
+                    <e5>BLACK_PAWN</e5>
+                    <f7>BLACK_PAWN</f7>
+                    <g7>BLACK_PAWN</g7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>KING</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>16</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Immortal Game</name>
+            <tcd></tcd>
+            <position>
+                <squares>
+                    <e2>WHITE_KING</e2>
+                    <f3>WHITE_QUEEN</f3>
+                    <d6>WHITE_BISHOP</d6>
+                    <d5>WHITE_KNIGHT</d5>
+                    <g7>WHITE_KNIGHT</g7>
+                    <a2>WHITE_PAWN</a2>
+                    <c2>WHITE_PAWN</c2>
+                    <d3>WHITE_PAWN</d3>
+                    <e5>WHITE_PAWN</e5>
+                    <g4>WHITE_PAWN</g4>
+                    <h5>WHITE_PAWN</h5>
+                    <d8>BLACK_KING</d8>
+                    <a1>BLACK_QUEEN</a1>
+                    <a8>BLACK_ROOK</a8>
+                    <h8>BLACK_ROOK</h8>
+                    <c8>BLACK_BISHOP</c8>
+                    <g1>BLACK_BISHOP</g1>
+                    <a6>BLACK_KNIGHT</a6>
+                    <g8>BLACK_KNIGHT</g8>
+                    <a7>BLACK_PAWN</a7>
+                    <b5>BLACK_PAWN</b5>
+                    <d7>BLACK_PAWN</d7>
+                    <f7>BLACK_PAWN</f7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>1</halfmove>
+                <fullmove>22</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Reti's Etude</name>
+            <tcd></tcd>
+            <isDiagram>true</isDiagram>
+            <position>
+                <squares>
+                    <h8>WHITE_KING</h8>
+                    <c6>WHITE_PAWN</c6>
+                    <a6>BLACK_KING</a6>
+                    <h5>BLACK_PAWN</h5>
+                </squares>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <tcd>GG</tcd>
+            <isDiagram>true</isDiagram>
+            <position>
+                <empty>true</empty>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/tcd/TCDParserTest.xml b/src/test/resources/org/hedgecode/chess/tcd/TCDParserTest.xml
new file mode 100644 (file)
index 0000000..59f5133
--- /dev/null
@@ -0,0 +1,130 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <tcd>xG</tcd>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Evergreen Game</name>
+            <tcd>AoJVMHMkKPTCE04FGLFV+4nPfYMNawOhjpgGH0xP0XU</tcd>
+            <position>
+                <squares>
+                    <g1>WHITE_KING</g1>
+                    <a4>WHITE_QUEEN</a4>
+                    <d1>WHITE_ROOK</d1>
+                    <a3>WHITE_BISHOP</a3>
+                    <d3>WHITE_BISHOP</d3>
+                    <a2>WHITE_PAWN</a2>
+                    <c3>WHITE_PAWN</c3>
+                    <f6>WHITE_PAWN</f6>
+                    <f2>WHITE_PAWN</f2>
+                    <g2>WHITE_PAWN</g2>
+                    <h2>WHITE_PAWN</h2>
+                    <e8>BLACK_KING</e8>
+                    <f3>BLACK_QUEEN</f3>
+                    <b8>BLACK_ROOK</b8>
+                    <g8>BLACK_ROOK</g8>
+                    <b7>BLACK_BISHOP</b7>
+                    <b6>BLACK_BISHOP</b6>
+                    <e7>BLACK_KNIGHT</e7>
+                    <a7>BLACK_PAWN</a7>
+                    <c7>BLACK_PAWN</c7>
+                    <d7>BLACK_PAWN</d7>
+                    <f7>BLACK_PAWN</f7>
+                    <h7>BLACK_PAWN</h7>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>21</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Immortal Zugzwang Game</name>
+            <tcd>AfSYHORACKNLLFEDVGGNvE9K2ecYZ3alih4pwGH0xP0XZ</tcd>
+            <position>
+                <squares>
+                    <h1>WHITE_KING</h1>
+                    <e3>WHITE_QUEEN</e3>
+                    <e1>WHITE_ROOK</e1>
+                    <g1>WHITE_ROOK</g1>
+                    <d2>WHITE_BISHOP</d2>
+                    <g2>WHITE_BISHOP</g2>
+                    <b1>WHITE_KNIGHT</b1>
+                    <a2>WHITE_PAWN</a2>
+                    <b2>WHITE_PAWN</b2>
+                    <d4>WHITE_PAWN</d4>
+                    <g3>WHITE_PAWN</g3>
+                    <h3>WHITE_PAWN</h3>
+                    <g8>BLACK_KING</g8>
+                    <d7>BLACK_QUEEN</d7>
+                    <f5>BLACK_ROOK</f5>
+                    <f2>BLACK_ROOK</f2>
+                    <d6>BLACK_BISHOP</d6>
+                    <d3>BLACK_BISHOP</d3>
+                    <a6>BLACK_PAWN</a6>
+                    <b4>BLACK_PAWN</b4>
+                    <d5>BLACK_PAWN</d5>
+                    <e6>BLACK_PAWN</e6>
+                    <e4>BLACK_PAWN</e4>
+                    <g7>BLACK_PAWN</g7>
+                    <h6>BLACK_PAWN</h6>
+                </squares>
+                <move>WHITE</move>
+                <castle>
+                    <white>NONE</white>
+                    <black>NONE</black>
+                </castle>
+                <halfmove>0</halfmove>
+                <fullmove>26</fullmove>
+            </position>
+        </test>
+        <test>
+            <name>Reti's Etude Variation</name>
+            <tcd>ABnGgJv9mpEG</tcd>
+            <isDiagram>true</isDiagram>
+            <position>
+                <squares>
+                    <h5>WHITE_KING</h5>
+                    <c6>WHITE_PAWN</c6>
+                    <a6>BLACK_KING</a6>
+                    <f6>BLACK_PAWN</f6>
+                    <g7>BLACK_PAWN</g7>
+                    <h6>BLACK_PAWN</h6>
+                </squares>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <tcd>GG</tcd>
+            <isDiagram>true</isDiagram>
+            <position>
+                <empty>true</empty>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/wiki/WikiBuilderTest.xml b/src/test/resources/org/hedgecode/chess/wiki/WikiBuilderTest.xml
new file mode 100644 (file)
index 0000000..065d2a9
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <wiki>
+                <![CDATA[
+{{Шахматная диаграмма
+|
+|
+|rd|nd|bd|qd|kd|bd|nd|rd
+|pd|pd|pd|pd|pd|pd|pd|pd
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|pl|pl|pl|pl|pl|pl|pl|pl
+|rl|nl|bl|ql|kl|bl|nl|rl
+|
+}}
+                ]]>
+            </wiki>
+            <withTemplate>true</withTemplate>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Reti's Etude Variation</name>
+            <wiki>
+                <![CDATA[
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |pd|
+|kd|  |pl|  |  |pd|  |pd
+|  |  |  |  |  |  |  |kl
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+                ]]>
+            </wiki>
+            <withTemplate>false</withTemplate>
+            <position>
+                <squares>
+                    <h5>WHITE_KING</h5>
+                    <c6>WHITE_PAWN</c6>
+                    <a6>BLACK_KING</a6>
+                    <f6>BLACK_PAWN</f6>
+                    <g7>BLACK_PAWN</g7>
+                    <h6>BLACK_PAWN</h6>
+                </squares>
+                <move>WHITE</move>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <wiki>
+                <![CDATA[
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+               ]]>
+            </wiki>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+            </position>
+        </test>
+    </tests>
+</config>
diff --git a/src/test/resources/org/hedgecode/chess/wiki/WikiParserTest.xml b/src/test/resources/org/hedgecode/chess/wiki/WikiParserTest.xml
new file mode 100644 (file)
index 0000000..3cdcd7e
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2018. Developed by Hedgecode.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+    <tests>
+        <test>
+            <name>Initial Position</name>
+            <wiki>
+                <![CDATA[
+|rd|nd|bd|qd|kd|bd|nd|rd
+|pd|pd|pd|pd|pd|pd|pd|pd
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|pl|pl|pl|pl|pl|pl|pl|pl
+|rl|nl|bl|ql|kl|bl|nl|rl
+                ]]>
+            </wiki>
+            <withTemplate>false</withTemplate>
+            <position>
+                <initial>true</initial>
+            </position>
+        </test>
+        <test>
+            <name>Anderssen's Mate</name>
+            <wiki>
+                <![CDATA[
+|  |  |  |bl|bl|  |  |kl
+|  |  |  |  |  |  |  |pd
+|  |  |  |  |  |  |  |kd
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |pl|  |pd
+|  |  |  |  |  |  |  |pl
+|  |  |  |  |  |  |  |
+                ]]>
+            </wiki>
+            <withTemplate>false</withTemplate>
+            <position>
+                <squares>
+                    <h8>WHITE_KING</h8>
+                    <d8>WHITE_BISHOP</d8>
+                    <e8>WHITE_BISHOP</e8>
+                    <f3>WHITE_PAWN</f3>
+                    <h2>WHITE_PAWN</h2>
+                    <h6>BLACK_KING</h6>
+                    <h7>BLACK_PAWN</h7>
+                    <h3>BLACK_PAWN</h3>
+                </squares>
+                <move>WHITE</move>
+            </position>
+        </test>
+        <test>
+            <name>Empty Position</name>
+            <wiki>
+                <![CDATA[
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+|  |  |  |  |  |  |  |
+               ]]>
+            </wiki>
+            <position>
+                <empty>true</empty>
+                <move>WHITE</move>
+            </position>
+        </test>
+    </tests>
+</config>