diff --git a/DroidFishTest/.classpath b/DroidFishTest/.classpath
new file mode 100644
index 0000000..2604b8a
--- /dev/null
+++ b/DroidFishTest/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/DroidFishTest/.project b/DroidFishTest/.project
new file mode 100644
index 0000000..57d4422
--- /dev/null
+++ b/DroidFishTest/.project
@@ -0,0 +1,34 @@
+
+
+ DroidFishTest
+
+
+ DroidFish
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/DroidFishTest/AndroidManifest.xml b/DroidFishTest/AndroidManifest.xml
new file mode 100644
index 0000000..b87b05e
--- /dev/null
+++ b/DroidFishTest/AndroidManifest.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png b/DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..882eb14
Binary files /dev/null and b/DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png differ
diff --git a/DroidFishTest/bin/res/drawable-ldpi/ic_launcher.png b/DroidFishTest/bin/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..18689f6
Binary files /dev/null and b/DroidFishTest/bin/res/drawable-ldpi/ic_launcher.png differ
diff --git a/DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png b/DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..02e96b9
Binary files /dev/null and b/DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png differ
diff --git a/DroidFishTest/proguard.cfg b/DroidFishTest/proguard.cfg
new file mode 100644
index 0000000..b1cdf17
--- /dev/null
+++ b/DroidFishTest/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native ;
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
diff --git a/DroidFishTest/project.properties b/DroidFishTest/project.properties
new file mode 100644
index 0000000..f049142
--- /dev/null
+++ b/DroidFishTest/project.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-10
diff --git a/DroidFishTest/res/drawable-hdpi/ic_launcher.png b/DroidFishTest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..8074c4c
Binary files /dev/null and b/DroidFishTest/res/drawable-hdpi/ic_launcher.png differ
diff --git a/DroidFishTest/res/drawable-ldpi/ic_launcher.png b/DroidFishTest/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..1095584
Binary files /dev/null and b/DroidFishTest/res/drawable-ldpi/ic_launcher.png differ
diff --git a/DroidFishTest/res/drawable-mdpi/ic_launcher.png b/DroidFishTest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/DroidFishTest/res/drawable-mdpi/ic_launcher.png differ
diff --git a/DroidFishTest/res/layout/main.xml b/DroidFishTest/res/layout/main.xml
new file mode 100644
index 0000000..bc12cd8
--- /dev/null
+++ b/DroidFishTest/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DroidFishTest/res/values/strings.xml b/DroidFishTest/res/values/strings.xml
new file mode 100644
index 0000000..3a99398
--- /dev/null
+++ b/DroidFishTest/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+
+
+ Hello World!
+ DroidFishTestTest
+
+
\ No newline at end of file
diff --git a/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java b/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java
new file mode 100644
index 0000000..e39444d
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java
@@ -0,0 +1,78 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.engine;
+
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.petero.droidfish.gamelogic.ChessParseError;
+import org.petero.droidfish.gamelogic.Move;
+import org.petero.droidfish.gamelogic.MoveGen;
+import org.petero.droidfish.gamelogic.Position;
+import org.petero.droidfish.gamelogic.TextIO;
+
+/**
+ *
+ * @author petero
+ */
+public class BookTest extends TestCase {
+
+ public BookTest() {
+ }
+
+ /**
+ * Test of getBookMove method, of class Book.
+ */
+ public void testGetBookMove() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ DroidBook book = DroidBook.getInstance();
+ Move move = book.getBookMove(pos);
+ checkValid(pos, move);
+
+ // Test "out of book" condition
+ pos.setCastleMask(0);
+ move = book.getBookMove(pos);
+ assertEquals(null, move);
+ }
+
+ /**
+ * Test of getAllBookMoves method, of class Book.
+ */
+ public void testGetAllBookMoves() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ DroidBook book = DroidBook.getInstance();
+ String moveListString = book.getAllBookMoves(pos).first;
+ String[] strMoves = moveListString.split(":[0-9]* ");
+ assertTrue(strMoves.length > 1);
+ for (String strMove : strMoves) {
+ Move m = TextIO.stringToMove(pos, strMove);
+ checkValid(pos, m);
+ }
+ }
+
+ /** Check that move is a legal move in position pos. */
+ private void checkValid(Position pos, Move move) {
+ assertTrue(move != null);
+ ArrayList moveList = new MoveGen().pseudoLegalMoves(pos);
+ moveList = MoveGen.removeIllegal(pos, moveList);
+ assertTrue(moveList.contains(move));
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java b/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java
new file mode 100644
index 0000000..3f7e379
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java
@@ -0,0 +1,82 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.engine;
+
+
+import junit.framework.TestCase;
+
+import org.petero.droidfish.gamelogic.ChessParseError;
+import org.petero.droidfish.gamelogic.Position;
+import org.petero.droidfish.gamelogic.TextIO;
+
+
+public class PolyglotBookTest extends TestCase {
+ public PolyglotBookTest() {
+ }
+
+ /**
+ * Test of getBookMove method, of class Book.
+ */
+ public void testGetHashKey() throws ChessParseError {
+ // starting position
+ Position pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
+ long key = 0x463b96181691fc9cL;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4
+ pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
+ key = 0x823c9b50fd114196L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4 d75
+ pos = TextIO.readFEN("rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2");
+ key = 0x0756b94461c50fb0L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4 d7d5 e4e5
+ pos = TextIO.readFEN("rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2");
+ key = 0x662fafb965db29d4L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4 d7d5 e4e5 f7f5
+ pos = TextIO.readFEN("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3");
+ key = 0x22a48b5a8e47ff78L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4 d7d5 e4e5 f7f5 e1e2
+ pos = TextIO.readFEN("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPPKPPP/RNBQ1BNR b kq - 0 3");
+ key = 0x652a607ca3f242c1L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after e2e4 d7d5 e4e5 f7f5 e1e2 e8f7
+ pos = TextIO.readFEN("rnbq1bnr/ppp1pkpp/8/3pPp2/8/8/PPPPKPPP/RNBQ1BNR w - - 0 4");
+ key = 0x00fdd303c946bdd9L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after a2a4 b7b5 h2h4 b5b4 c2c4
+ pos = TextIO.readFEN("rnbqkbnr/p1pppppp/8/8/PpP4P/8/1P1PPPP1/RNBQKBNR b KQkq c3 0 3");
+ key = 0x3c8123ea7b067637L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+
+ // position after a2a4 b7b5 h2h4 b5b4 c2c4 b4c3 a1a3
+ pos = TextIO.readFEN("rnbqkbnr/p1pppppp/8/8/P6P/R1p5/1P1PPPP1/1NBQKBNR b Kkq - 0 4");
+ key = 0x5c3f9b829b279560L;
+ assertEquals(key, PolyglotBook.getHashKey(pos));
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java
new file mode 100644
index 0000000..40a106d
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java
@@ -0,0 +1,461 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+
+/**
+ *
+ * @author petero
+ */
+public class GameTest extends TestCase {
+
+ public GameTest() {
+ }
+
+ /**
+ * Test of haveDrawOffer method, of class Game.
+ */
+ public void testHaveDrawOffer() {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(false, game.haveDrawOffer());
+
+ boolean res = game.processString("e4");
+ assertEquals(true, res);
+ assertEquals(false, game.haveDrawOffer());
+
+ res = game.processString("draw offer e5");
+ assertEquals(true, res);
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw
+ assertEquals(Piece.BPAWN, game.currPos().getPiece(Position.getSquare(4, 4))); // e5 move made
+
+ res = game.processString("draw offer Nf3");
+ assertEquals(true, res);
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw
+ assertEquals(Piece.WKNIGHT, game.currPos().getPiece(Position.getSquare(5, 2))); // Nf3 move made
+
+ res = game.processString("Nc6");
+ assertEquals(true, res);
+ assertEquals(false, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move made
+
+ res = game.processString("draw offer Bb5");
+ assertEquals(true, res);
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ assertEquals(Piece.WBISHOP, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move made
+
+ res = game.processString("draw accept");
+ assertEquals(true, res);
+ assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Draw by agreement
+
+ game.undoMove(); // Undo "draw accept"
+ assertEquals(Piece.WBISHOP, game.currPos().getPiece(TextIO.getSquare("b5")));
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.undoMove(); // Undo "Bb5"
+ assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move undone
+ assertEquals(false, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.undoMove();
+ assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move undone
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+
+ game.redoMove();
+ assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move redone
+ assertEquals(false, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.redoMove();
+ assertEquals(Piece.WBISHOP, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move redone
+ assertEquals(true, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.redoMove();
+ assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Can redo draw accept
+
+ // Test draw offer in connection with invalid move
+ game.newGame();
+ assertEquals(false, game.haveDrawOffer());
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+
+ res = game.processString("draw offer e5");
+ assertEquals(true, res);
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); // Move invalid, not executed
+ res = game.processString("e4");
+ assertEquals(true, res);
+ assertEquals(true, game.haveDrawOffer()); // Previous draw offer still valid
+ assertEquals(Piece.WPAWN, game.currPos().getPiece(Position.getSquare(4, 3))); // e4 move made
+
+ // Undo/redo shall clear "pendingDrawOffer".
+ game.newGame();
+ game.processString("e4");
+ game.processString("draw offer e4"); // Invalid black move
+ assertEquals(true, game.pendingDrawOffer);
+ game.undoMove();
+ game.redoMove();
+ game.processString("e5");
+ assertEquals(true,game.currPos().whiteMove);
+ assertEquals(false, game.haveDrawOffer());
+ }
+
+ /**
+ * Test of draw by 50 move rule, of class Game.
+ */
+ public void testDraw50() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(false, game.haveDrawOffer());
+ boolean res = game.processString("draw 50");
+ assertEquals(true, res);
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid
+ res = game.processString("e4");
+ assertEquals(true, game.haveDrawOffer()); // Invalid claim converted to draw offer
+
+ String fen = "8/4k3/8/P7/8/8/8/1N2K2R w K - 99 83";
+ game.setPos(TextIO.readFEN(fen));
+ res = game.processString("draw 50");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid
+
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("draw 50 Nc3");
+ assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Draw claim valid
+ assertEquals("Game over, draw by 50 move rule! [Nc3]", game.getGameStateString());
+
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("draw 50 a6");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Pawn move resets counter
+ assertEquals(Piece.WPAWN, game.currPos().getPiece(Position.getSquare(0, 5))); // Move a6 made
+
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("draw 50 O-O");
+ assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Castling doesn't reset counter
+
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("draw 50 Kf2");
+ assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Loss of castling right doesn't reset counter
+
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("draw 50 Ke3");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Ke3 is invalid
+ assertEquals(true, game.currPos().whiteMove);
+ game.processString("a6");
+ assertEquals(true, game.haveDrawOffer()); // Previous invalid claim converted to offer
+ game.processString("draw 50");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // 50 move counter reset.
+ res = game.processString("draw accept");
+ assertEquals(true, res);
+ assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Can accept previous implicit offer
+
+ fen = "3k4/R7/3K4/8/8/8/8/8 w - - 99 78";
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("Ra8");
+ assertEquals(Game.GameState.WHITE_MATE, game.getGameState());
+ game.processString("draw 50");
+ assertEquals(Game.GameState.WHITE_MATE, game.getGameState()); // Can't claim draw when game over
+ }
+
+ /**
+ * Test of draw by repetition, of class Game.
+ */
+ public void testDrawRep() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(false, game.haveDrawOffer());
+ game.processString("Nc3");
+ game.processString("Nc6");
+ game.processString("Nb1");
+ game.processString("Nb8");
+ game.processString("Nf3");
+ game.processString("Nf6");
+ game.processString("Ng1");
+ assertEquals(false, game.haveDrawOffer());
+ game.processString("draw rep");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, one more move needed
+ game.processString("draw rep Nc6");
+ assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, wrong move claimed
+ assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Move Nc6 made
+ assertEquals(true, game.haveDrawOffer());
+ game.undoMove();
+ assertEquals(false, game.haveDrawOffer());
+ assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(2, 5)));
+ game.processString("draw rep Ng8");
+ assertEquals(Game.GameState.DRAW_REP, game.getGameState());
+ assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(6, 7))); // Ng8 not played
+
+ // Test draw by repetition when a "potential ep square but not real ep square" position is present.
+ game.newGame();
+ game.processString("e4"); // e3 is not a real epSquare here
+ game.processString("Nf6");
+ game.processString("Nf3");
+ game.processString("Ng8");
+ game.processString("Ng1");
+ game.processString("Nf6");
+ game.processString("Nf3");
+ game.processString("Ng8");
+ game.processString("draw rep Ng1");
+ assertEquals(Game.GameState.DRAW_REP, game.getGameState());
+
+ // Now check the case when e3 *is* an epSquare
+ game.newGame();
+ game.processString("Nf3");
+ game.processString("d5");
+ game.processString("Ng1");
+ game.processString("d4");
+ game.processString("e4"); // Here e3 is a real epSquare
+ game.processString("Nf6");
+ game.processString("Nf3");
+ game.processString("Ng8");
+ game.processString("Ng1");
+ game.processString("Nf6");
+ game.processString("Nf3");
+ game.processString("Ng8");
+ game.processString("draw rep Ng1");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+
+ // EP capture not valid because it would leave the king in check. Therefore
+ // the position has been repeated three times at the end of the move sequence.
+ game.setPos(TextIO.readFEN("4k2n/8/8/8/4p3/8/3P4/3KR2N w - - 0 1"));
+ game.processString("d4");
+ game.processString("Ng6");
+ game.processString("Ng3");
+ game.processString("Nh8");
+ game.processString("Nh1");
+ game.processString("Ng6");
+ game.processString("Ng3");
+ game.processString("Nh8");
+ game.processString("draw rep Nh1");
+ assertEquals(Game.GameState.DRAW_REP, game.getGameState());
+ }
+
+ /**
+ * Test of draw offer/accept/request command.
+ */
+ public void testDrawBug() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(false, game.haveDrawOffer());
+ game.processString("e4");
+ game.processString("c5");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("draw accept");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("draw rep");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("draw 50");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ assertEquals(Piece.EMPTY, game.tree.currentPos.getPiece(TextIO.getSquare("e5")));
+ }
+
+ /**
+ * Test of resign command, of class Game.
+ */
+ public void testResign() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("f3");
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("resign");
+ assertEquals(Game.GameState.RESIGN_BLACK, game.getGameState());
+ game.undoMove();
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("f3");
+ game.processString("e5");
+ game.processString("resign");
+ assertEquals(Game.GameState.RESIGN_WHITE, game.getGameState());
+ game.undoMove();
+ game.processString("e5");
+ game.processString("g4");
+ game.processString("Qh4");
+ assertEquals(Game.GameState.BLACK_MATE, game.getGameState());
+ game.processString("resign");
+ assertEquals(Game.GameState.BLACK_MATE, game.getGameState()); // Can't resign after game over
+
+ String fen = "8/1p6/2rp2p1/8/p3Qqk1/6R1/PP4PK/8 b - - 3 42";
+ game.setPos(TextIO.readFEN(fen));
+ game.processString("resign");
+ assertEquals(Game.GameState.RESIGN_BLACK, game.getGameState());
+ }
+
+ /**
+ * Test of processString method, of class Game.
+ */
+ public void testProcessString() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos()));
+ boolean res = game.processString("Nf3");
+ assertEquals(true, res);
+ assertEquals(1, game.currPos().halfMoveClock);
+ assertEquals(1, game.currPos().fullMoveCounter);
+ res = game.processString("d5");
+ assertEquals(true, res);
+ assertEquals(0, game.currPos().halfMoveClock);
+ assertEquals(2, game.currPos().fullMoveCounter);
+
+ game.undoMove();
+ assertEquals(1, game.currPos().halfMoveClock);
+ assertEquals(1, game.currPos().fullMoveCounter);
+ game.undoMove();
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos()));
+ game.undoMove();
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos()));
+
+ game.redoMove();
+ assertEquals(1, game.currPos().halfMoveClock);
+ assertEquals(1, game.currPos().fullMoveCounter);
+ game.redoMove();
+ assertEquals(0, game.currPos().halfMoveClock);
+ assertEquals(2, game.currPos().fullMoveCounter);
+ game.redoMove();
+ assertEquals(0, game.currPos().halfMoveClock);
+ assertEquals(2, game.currPos().fullMoveCounter);
+
+ game.newGame();
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos()));
+
+ String fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68";
+ Position pos = TextIO.readFEN(fen);
+ game.setPos(TextIO.readFEN(fen));
+ assertEquals(pos, game.currPos());
+
+ res = game.processString("junk");
+ assertEquals(false, res);
+
+ game.newGame();
+ res = game.processString("e7e5");
+ assertEquals(false, res);
+ }
+
+ /**
+ * Test of getGameState method, of class Game.
+ */
+ public void testGetGameState() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.processString("f3");
+ game.processString("e5");
+ game.processString("g4");
+ game.processString("Qh4");
+ assertEquals(Game.GameState.BLACK_MATE, game.getGameState());
+
+ game.setPos(TextIO.readFEN("5k2/5P2/5K2/8/8/8/8/8 b - - 0 1"));
+ assertEquals(Game.GameState.BLACK_STALEMATE, game.getGameState());
+ }
+
+ /**
+ * Test of insufficientMaterial method, of class Game.
+ */
+ public void testInsufficientMaterial() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ game.setPos(TextIO.readFEN("4k3/8/8/8/8/8/8/4K3 w - - 0 1"));
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+ final int a1 = Position.getSquare(0, 0);
+ Position pos = new Position(game.currPos());
+ pos.setPiece(a1, Piece.WROOK); game.setPos(pos);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ pos.setPiece(a1, Piece.BQUEEN); game.setPos(pos);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ pos.setPiece(a1, Piece.WPAWN); game.setPos(pos);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ pos.setPiece(a1, Piece.BKNIGHT); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+ pos.setPiece(a1, Piece.WBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+
+ final int c1 = Position.getSquare(2, 0);
+ pos.setPiece(c1, Piece.WKNIGHT); game.setPos(pos);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ pos.setPiece(c1, Piece.BBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+ pos.setPiece(c1, Piece.WBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+
+ final int b2 = Position.getSquare(1, 1);
+ pos.setPiece(b2, Piece.WBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+ pos.setPiece(b2, Piece.BBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState());
+
+ final int b3 = Position.getSquare(1, 2);
+ pos.setPiece(b3, Piece.WBISHOP); game.setPos(pos);
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+
+ // Can't force mate with KNNK, but still not an automatic draw.
+ game.setPos(TextIO.readFEN("8/8/8/8/8/8/8/K3nnk1 w - - 0 1"));
+ assertEquals(Game.GameState.ALIVE, game.getGameState());
+ }
+
+ /** Test that UCI history is not longer than necessary.
+ * We can't expect engines to handle null moves, for example. */
+ public void testUCIHistory() throws ChessParseError {
+ Game game = new Game(null, null, 0, 0, 0);
+
+ Pair> hist = game.getUCIHistory();
+ assertEquals(0, hist.second.size());
+ Position expectedPos = new Position(game.currPos());
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("Nf3");
+ hist = game.getUCIHistory();
+ assertEquals(1, hist.second.size());
+ assertEquals(TextIO.UCIstringToMove("g1f3"), hist.second.get(0));
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("e5");
+ hist = game.getUCIHistory();
+ expectedPos = new Position(game.currPos());
+ assertEquals(0, hist.second.size());
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("Nc3");
+ hist = game.getUCIHistory();
+ assertEquals(1, hist.second.size());
+ assertEquals(TextIO.UCIstringToMove("b1c3"), hist.second.get(0));
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("Nc6");
+ hist = game.getUCIHistory();
+ assertEquals(2, hist.second.size());
+ assertEquals(TextIO.UCIstringToMove("b1c3"), hist.second.get(0));
+ assertEquals(TextIO.UCIstringToMove("b8c6"), hist.second.get(1));
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("--");
+ hist = game.getUCIHistory();
+ expectedPos = new Position(game.currPos());
+ assertEquals(0, hist.second.size());
+ assertEquals(expectedPos, hist.first);
+
+ game.processString("Nf6");
+ hist = game.getUCIHistory();
+ assertEquals(1, hist.second.size());
+ assertEquals(TextIO.UCIstringToMove("g8f6"), hist.second.get(0));
+ assertEquals(expectedPos, hist.first);
+
+ for (int i = 0; i < 6; i++)
+ game.undoMove();
+ hist = game.getUCIHistory();
+ assertEquals(0, hist.second.size());
+ expectedPos = TextIO.readFEN(TextIO.startPosFEN);
+ assertEquals(expectedPos, hist.first);
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java
new file mode 100644
index 0000000..becb87c
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java
@@ -0,0 +1,651 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.petero.droidfish.PGNOptions;
+import org.petero.droidfish.gamelogic.Game.GameState;
+import org.petero.droidfish.gamelogic.GameTree.Node;
+import org.petero.droidfish.gamelogic.GameTree.PgnScanner;
+
+public class GameTreeTest extends TestCase {
+
+ public final void testGameTree() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ Position expectedPos = TextIO.readFEN(TextIO.startPosFEN);
+ assertEquals(expectedPos, gt.currentPos);
+
+ List varList = gt.variations();
+ assertEquals(0, varList.size());
+
+ int varNo = gt.addMove("e4", "", 0, "", "");
+ assertEquals(0, varNo);
+ assertEquals(expectedPos, gt.currentPos);
+
+ gt.goForward(varNo);
+ Move move = TextIO.UCIstringToMove("e2e4");
+ UndoInfo ui = new UndoInfo();
+ expectedPos.makeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+
+ gt.goBack();
+ expectedPos.unMakeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+
+ varNo = gt.addMove("d4", "", 0, "", "");
+ assertEquals(1, varNo);
+ assertEquals(expectedPos, gt.currentPos);
+ varList = gt.variations();
+ assertEquals(2, varList.size());
+
+ gt.goForward(varNo);
+ move = TextIO.UCIstringToMove("d2d4");
+ expectedPos.makeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+
+ varNo = gt.addMove("g8f6", "", 0, "", "");
+ assertEquals(0, varNo);
+ assertEquals(expectedPos, gt.currentPos);
+ varList = gt.variations();
+ assertEquals(1, varList.size());
+
+ gt.goForward(-1);
+ Move move2 = TextIO.UCIstringToMove("g8f6");
+ UndoInfo ui2 = new UndoInfo();
+ expectedPos.makeMove(move2, ui2);
+ assertEquals(expectedPos, gt.currentPos);
+ assertEquals("Nf6", gt.currentNode.moveStr);
+
+ gt.goBack();
+ assertEquals("d4", gt.currentNode.moveStr);
+ gt.goBack();
+ expectedPos.unMakeMove(move2, ui2);
+ expectedPos.unMakeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+ assertEquals("", gt.currentNode.moveStr);
+
+ gt.goForward(-1); // Should remember that d2d4 was last visited branch
+ expectedPos.makeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+
+ byte[] serialState = gt.toByteArray();
+ gt = new GameTree(null);
+ gt.fromByteArray(serialState);
+ assertEquals(expectedPos, gt.currentPos);
+
+ gt.goBack();
+ expectedPos.unMakeMove(move, ui);
+ assertEquals(expectedPos, gt.currentPos);
+ varList = gt.variations();
+ assertEquals(2, varList.size());
+ }
+
+ private final String getMoveListAsString(GameTree gt) {
+ StringBuilder ret = new StringBuilder();
+ Pair, Integer> ml = gt.getMoveList();
+ List lst = ml.first;
+ final int numMovesPlayed = ml.second;
+ for (int i = 0; i < lst.size(); i++) {
+ if (i == numMovesPlayed)
+ ret.append('*');
+ if (i > 0)
+ ret.append(' ');
+ ret.append(lst.get(i).moveStr);
+ }
+ if (lst.size() == numMovesPlayed)
+ ret.append('*');
+ return ret.toString();
+ }
+
+ public final void testGetMoveList() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ gt.addMove("e4", "", 0, "", "");
+ gt.addMove("d4", "", 0, "", "");
+ assertEquals("*e4", getMoveListAsString(gt));
+
+ gt.goForward(0);
+ assertEquals("e4*", getMoveListAsString(gt));
+
+ gt.addMove("e5", "", 0, "", "");
+ gt.addMove("c5", "", 0, "", "");
+ assertEquals("e4* e5", getMoveListAsString(gt));
+
+ gt.goForward(1);
+ assertEquals("e4 c5*", getMoveListAsString(gt));
+
+ gt.addMove("Nf3", "", 0, "", "");
+ gt.addMove("d4", "", 0, "", "");
+ assertEquals("e4 c5* Nf3", getMoveListAsString(gt));
+
+ gt.goForward(1);
+ assertEquals("e4 c5 d4*", getMoveListAsString(gt));
+
+ gt.goBack();
+ assertEquals("e4 c5* d4", getMoveListAsString(gt));
+
+ gt.goBack();
+ assertEquals("e4* c5 d4", getMoveListAsString(gt));
+
+ gt.goBack();
+ assertEquals("*e4 c5 d4", getMoveListAsString(gt));
+
+ gt.goForward(1);
+ assertEquals("d4*", getMoveListAsString(gt));
+
+ gt.goBack();
+ assertEquals("*d4", getMoveListAsString(gt));
+
+ gt.goForward(0);
+ assertEquals("e4* c5 d4", getMoveListAsString(gt));
+ }
+
+ public final void testReorderVariation() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ gt.addMove("e4", "", 0, "", "");
+ gt.addMove("d4", "", 0, "", "");
+ gt.addMove("c4", "", 0, "", "");
+ assertEquals("e4 d4 c4", getVariationsAsString(gt));
+ assertEquals(0, gt.currentNode.defaultChild);
+
+ gt.reorderVariation(1, 0);
+ assertEquals("d4 e4 c4", getVariationsAsString(gt));
+ assertEquals(1, gt.currentNode.defaultChild);
+
+ gt.reorderVariation(0, 2);
+ assertEquals("e4 c4 d4", getVariationsAsString(gt));
+ assertEquals(0, gt.currentNode.defaultChild);
+
+ gt.reorderVariation(1, 2);
+ assertEquals("e4 d4 c4", getVariationsAsString(gt));
+ assertEquals(0, gt.currentNode.defaultChild);
+
+ gt.reorderVariation(0, 1);
+ assertEquals("d4 e4 c4", getVariationsAsString(gt));
+ assertEquals(1, gt.currentNode.defaultChild);
+ }
+
+ public final void testDeleteVariation() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ gt.addMove("e4", "", 0, "", "");
+ gt.addMove("d4", "", 0, "", "");
+ gt.addMove("c4", "", 0, "", "");
+ gt.addMove("f4", "", 0, "", "");
+ gt.deleteVariation(0);
+ assertEquals("d4 c4 f4", getVariationsAsString(gt));
+ assertEquals(0, gt.currentNode.defaultChild);
+
+ gt.reorderVariation(0, 2);
+ assertEquals("c4 f4 d4", getVariationsAsString(gt));
+ assertEquals(2, gt.currentNode.defaultChild);
+ gt.deleteVariation(1);
+ assertEquals("c4 d4", getVariationsAsString(gt));
+ assertEquals(1, gt.currentNode.defaultChild);
+
+ gt.addMove("g4", "", 0, "", "");
+ gt.addMove("h4", "", 0, "", "");
+ assertEquals("c4 d4 g4 h4", getVariationsAsString(gt));
+ assertEquals(1, gt.currentNode.defaultChild);
+ gt.reorderVariation(1, 2);
+ assertEquals("c4 g4 d4 h4", getVariationsAsString(gt));
+ assertEquals(2, gt.currentNode.defaultChild);
+ gt.deleteVariation(2);
+ assertEquals("c4 g4 h4", getVariationsAsString(gt));
+ assertEquals(0, gt.currentNode.defaultChild);
+ }
+
+ private final String getVariationsAsString(GameTree gt) {
+ StringBuilder ret = new StringBuilder();
+ List vars = gt.variations();
+ for (int i = 0; i < vars.size(); i++) {
+ if (i > 0)
+ ret.append(' ');
+ String moveStr = TextIO.moveToString(gt.currentPos, vars.get(i), false);
+ ret.append(moveStr);
+ }
+ return ret.toString();
+ }
+
+ public final void testGetRemainingTime() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ int initialTime = 60000;
+ assertEquals(initialTime, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.addMove("e4", "", 0, "", "");
+ gt.goForward(-1);
+ assertEquals(initialTime, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+ gt.setRemainingTime(45000);
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.addMove("e5", "", 0, "", "");
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.goForward(-1);
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.addMove("Nf3", "", 0, "", "");
+ gt.goForward(-1);
+ gt.addMove("Nc6", "", 0, "", "");
+ gt.goForward(-1);
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.setRemainingTime(30000);
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(30000, gt.getRemainingTime(false, initialTime));
+
+ gt.addMove("Bb5", "", 0, "", "");
+ gt.goForward(-1);
+ gt.setRemainingTime(20000);
+ assertEquals(20000, gt.getRemainingTime(true, initialTime));
+ assertEquals(30000, gt.getRemainingTime(false, initialTime));
+
+ gt.addMove("a6", "", 0, "", "");
+ gt.goForward(-1);
+ gt.setRemainingTime(15000);
+ assertEquals(20000, gt.getRemainingTime(true, initialTime));
+ assertEquals(15000, gt.getRemainingTime(false, initialTime));
+
+ gt.goBack();
+ assertEquals(20000, gt.getRemainingTime(true, initialTime));
+ assertEquals(30000, gt.getRemainingTime(false, initialTime));
+
+ gt.goBack();
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(30000, gt.getRemainingTime(false, initialTime));
+
+ gt.goBack();
+ assertEquals(45000, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+
+ gt.goBack();
+ gt.goBack();
+ gt.goBack();
+ assertEquals(initialTime, gt.getRemainingTime(true, initialTime));
+ assertEquals(initialTime, gt.getRemainingTime(false, initialTime));
+ }
+
+ private final List getAllTokens(String s) {
+ PgnScanner sc = new PgnScanner(s);
+ List ret = new ArrayList();
+ while (true) {
+ PgnToken tok = sc.nextToken();
+ if (tok.type == PgnToken.EOF)
+ break;
+ ret.add(tok);
+ }
+ return ret;
+ }
+
+ public final void testPgnScanner() throws ChessParseError {
+ List lst = getAllTokens("a\nb\n%junk\nc3"); // a b c3
+ assertEquals(3, lst.size());
+ assertEquals(PgnToken.SYMBOL, lst.get(0).type);
+ assertEquals("a", lst.get(0).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(1).type);
+ assertEquals("b", lst.get(1).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(2).type);
+ assertEquals("c3", lst.get(2).token);
+
+ lst = getAllTokens("e2 ; e5\nc5"); // e2 comment c5
+ assertEquals(3, lst.size());
+ assertEquals("e2", lst.get(0).token);
+ assertEquals(PgnToken.COMMENT, lst.get(1).type);
+ assertEquals(" e5", lst.get(1).token);
+ assertEquals("c5", lst.get(2).token);
+
+ lst = getAllTokens("e4?? { comment ; } e5!?"); // e4?? comment e5!?
+ assertEquals(3, lst.size());
+ assertEquals("e4??", lst.get(0).token);
+ assertEquals(" comment ; ", lst.get(1).token);
+ assertEquals("e5!?", lst.get(2).token);
+
+ lst = getAllTokens("e4! { comment { } e5?"); // e4! comment e5?
+ assertEquals(3, lst.size());
+ assertEquals("e4!", lst.get(0).token);
+ assertEquals(" comment { ", lst.get(1).token);
+ assertEquals("e5?", lst.get(2).token);
+
+ lst = getAllTokens("e4(c4 {(()\\} c5 ( e5))Nf6"); // e4 ( c4 comment c5 ( e5 ) ) Nf6
+ assertEquals(10, lst.size());
+ assertEquals("e4", lst.get(0).token);
+ assertEquals(PgnToken.LEFT_PAREN, lst.get(1).type);
+ assertEquals("c4", lst.get(2).token);
+ assertEquals("(()\\", lst.get(3).token);
+ assertEquals("c5", lst.get(4).token);
+ assertEquals(PgnToken.LEFT_PAREN, lst.get(5).type);
+ assertEquals("e5", lst.get(6).token);
+ assertEquals(PgnToken.RIGHT_PAREN, lst.get(7).type);
+ assertEquals(PgnToken.RIGHT_PAREN, lst.get(8).type);
+ assertEquals("Nf6", lst.get(9).token);
+
+ lst = getAllTokens("[a \"string\"]"); // [ a string ]
+ assertEquals(4, lst.size());
+ assertEquals(PgnToken.LEFT_BRACKET, lst.get(0).type);
+ assertEquals("a", lst.get(1).token);
+ assertEquals(PgnToken.STRING, lst.get(2).type);
+ assertEquals("string", lst.get(2).token);
+ assertEquals(PgnToken.RIGHT_BRACKET, lst.get(3).type);
+
+ lst = getAllTokens("[a \"str\\\"in\\\\g\"]"); // [ a str"in\g ]
+ assertEquals(4, lst.size());
+ assertEquals(PgnToken.LEFT_BRACKET, lst.get(0).type);
+ assertEquals("a", lst.get(1).token);
+ assertEquals(PgnToken.STRING, lst.get(2).type);
+ assertEquals("str\"in\\g", lst.get(2).token);
+ assertEquals(PgnToken.RIGHT_BRACKET, lst.get(3).type);
+
+ lst = getAllTokens("1...Nf6$23Nf3 12 e4_+#=:-*"); // 1 . . . Nf6 $23 Nf3 12 e4_+#=:- *
+ assertEquals(10, lst.size());
+ assertEquals(PgnToken.INTEGER, lst.get(0).type);
+ assertEquals("1", lst.get(0).token);
+ assertEquals(PgnToken.PERIOD, lst.get(1).type);
+ assertEquals(PgnToken.PERIOD, lst.get(2).type);
+ assertEquals(PgnToken.PERIOD, lst.get(3).type);
+ assertEquals("Nf6", lst.get(4).token);
+ assertEquals(PgnToken.NAG, lst.get(5).type);
+ assertEquals("23", lst.get(5).token);
+ assertEquals("Nf3", lst.get(6).token);
+ assertEquals(PgnToken.INTEGER, lst.get(7).type);
+ assertEquals("12", lst.get(7).token);
+ assertEquals("e4_+#=:-", lst.get(8).token);
+ assertEquals(PgnToken.ASTERISK, lst.get(9).type);
+
+ lst = getAllTokens("1/2-1/2 1-0 0-1");
+ assertEquals(3, lst.size());
+ assertEquals(PgnToken.SYMBOL, lst.get(0).type);
+ assertEquals("1/2-1/2", lst.get(0).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(1).type);
+ assertEquals("1-0", lst.get(1).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(2).type);
+ assertEquals("0-1", lst.get(2).token);
+
+ // Test invalid data, unterminated tokens
+ lst = getAllTokens("e4 e5 ; ( )"); // e4 e5 comment
+ assertEquals(3, lst.size());
+ assertEquals(PgnToken.SYMBOL, lst.get(0).type);
+ assertEquals("e4", lst.get(0).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(1).type);
+ assertEquals("e5", lst.get(1).token);
+ assertEquals(PgnToken.COMMENT, lst.get(2).type);
+ assertEquals(" ( )", lst.get(2).token);
+
+ lst = getAllTokens("e4 e5 {"); // e4 e5 ?
+ assertTrue(lst.size() >= 2);
+ assertEquals(PgnToken.SYMBOL, lst.get(0).type);
+ assertEquals("e4", lst.get(0).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(1).type);
+ assertEquals("e5", lst.get(1).token);
+
+ lst = getAllTokens("e4 e5 \""); // e4 e5 ?
+ assertTrue(lst.size() >= 2);
+ assertEquals(PgnToken.SYMBOL, lst.get(0).type);
+ assertEquals("e4", lst.get(0).token);
+ assertEquals(PgnToken.SYMBOL, lst.get(1).type);
+ assertEquals("e5", lst.get(1).token);
+
+ // Test that reading beyond EOF produces more EOF tokens
+ PgnScanner sc = new PgnScanner("e4 e5");
+ assertEquals(PgnToken.SYMBOL, sc.nextToken().type);
+ assertEquals(PgnToken.SYMBOL, sc.nextToken().type);
+ assertEquals(PgnToken.EOF, sc.nextToken().type);
+ assertEquals(PgnToken.EOF, sc.nextToken().type);
+ assertEquals(PgnToken.EOF, sc.nextToken().type);
+ }
+
+ public final void testReadPGN() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ PGNOptions options = new PGNOptions();
+ options.imp.variations = true;
+ options.imp.comments = true;
+ options.imp.nag = true;
+ boolean res = gt.readPGN("", options);
+ assertEquals(false, res);
+
+ res = gt.readPGN("[White \"a\"][Black \"b\"] {comment} e4 {x}", options);
+ assertEquals(true, res);
+ assertEquals("a", gt.white);
+ assertEquals("b", gt.black);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("comment", gt.currentNode.preComment);
+ assertEquals("x", gt.currentNode.postComment);
+
+ res = gt.readPGN("e4 e5 Nf3", options);
+ assertEquals(true, res);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("e5", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("Nf3", getVariationsAsString(gt));
+
+ res = gt.readPGN("e4 e5 (c5 (c6) d4) (d5) Nf3", options);
+ assertEquals(true, res);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("e5 c5 c6 d5", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("Nf3", getVariationsAsString(gt));
+
+ res = gt.readPGN("e4 e5 (c5 (c3) d4 (Nc3)) (d5) Nf3", options); // c3 invalid, should be removed
+ assertEquals(true, res);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("e5 c5 d5", getVariationsAsString(gt));
+ gt.goForward(1);
+ assertEquals("d4 Nc3", getVariationsAsString(gt));
+
+ res = gt.readPGN("e4 + e5", options); // Extra + should be ignored
+ assertEquals(true, res);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("e5", getVariationsAsString(gt));
+
+ // Test for broken PGN headers: [White "A "good" player"]
+ res = gt.readPGN("[White \"A \"good\" player\"]\ne4", options);
+ assertEquals(true, res);
+ assertEquals("A \"good\" player", gt.white);
+ assertEquals("e4", getVariationsAsString(gt));
+
+ // Test for broken PGN headers: [White "A "good" player"]
+ res = gt.readPGN("[White \"A \"good old\" player\"]\ne4", options);
+ assertEquals(true, res);
+ assertEquals("A \"good old\" player", gt.white);
+ assertEquals("e4", getVariationsAsString(gt));
+ }
+
+ public final void testStringEscape() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ PGNOptions options = new PGNOptions();
+ gt.white = "test \"x\"";
+ String pgn = gt.toPGN(options);
+ gt.white = "";
+ boolean res = gt.readPGN(pgn, options);
+ assertEquals(true, res);
+ assertEquals("test \"x\"", gt.white);
+ }
+
+ public final void testNAG() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ PGNOptions options = new PGNOptions();
+ options.imp.variations = true;
+ options.imp.comments = true;
+ options.imp.nag = true;
+ boolean res = gt.readPGN("e4! e5 ? Nf3?! Nc6 !? Bb5!! a6?? Ba4 $14", options);
+ assertEquals(true, res);
+
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(1, gt.currentNode.nag);
+
+ assertEquals("e5", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(2, gt.currentNode.nag);
+
+ assertEquals("Nf3", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(6, gt.currentNode.nag);
+
+ assertEquals("Nc6", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(5, gt.currentNode.nag);
+
+ assertEquals("Bb5", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(3, gt.currentNode.nag);
+
+ assertEquals("a6", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(4, gt.currentNode.nag);
+
+ assertEquals("Ba4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(14, gt.currentNode.nag);
+ }
+
+ public final void testTime() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ PGNOptions options = new PGNOptions();
+ options.imp.variations = true;
+ options.imp.comments = true;
+ options.imp.nag = true;
+ boolean res = gt.readPGN("e4 { x [%clk 0:0:43] y} e5 {[%clk\n1:2:3]} Nf3 Nc6 {[%clk -1:2 ]}", options);
+ assertEquals(true, res);
+
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(43000, gt.currentNode.remainingTime);
+ assertEquals(" x y", gt.currentNode.postComment);
+
+ assertEquals("e5", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(((1*60+2)*60+3)*1000, gt.currentNode.remainingTime);
+ assertEquals("", gt.currentNode.postComment);
+
+ assertEquals("Nf3", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(Integer.MIN_VALUE, gt.currentNode.remainingTime);
+ assertEquals("", gt.currentNode.postComment);
+
+ assertEquals("Nc6", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(-(1*60+2)*1000, gt.currentNode.remainingTime);
+ assertEquals("", gt.currentNode.postComment);
+ }
+
+ public final void testPlayerAction() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ int varNo = gt.addMove("--", "resign", 0, "", "");
+ assertEquals(0, varNo);
+
+ PGNOptions options = new PGNOptions();
+
+ String pgn = gt.toPGN(options);
+ assertEquals(-1, pgn.indexOf("--"));
+
+ options.exp.playerAction = true;
+ pgn = gt.toPGN(options);
+ assertTrue(pgn.indexOf("--") >= 0);
+
+ gt = new GameTree(null);
+ gt.readPGN(pgn, options);
+ assertEquals("--", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(GameState.RESIGN_WHITE, gt.getGameState());
+
+ gt = new GameTree(null);
+ gt.readPGN("1. -- {[%playeraction resign]}", options);
+ assertEquals("--", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(GameState.RESIGN_WHITE, gt.getGameState());
+
+ gt.readPGN("1. e4 -- {[%playeraction resign]}", options);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals("--", getVariationsAsString(gt));
+ gt.goForward(0);
+ assertEquals(GameState.RESIGN_BLACK, gt.getGameState());
+
+ // Even if playerActions are not exported, moves corresponding
+ // to draw offers must still be exported
+ gt = new GameTree(null);
+ varNo = gt.addMove("e4", "draw offer", 0, "", "");
+ assertEquals(0, varNo);
+ assertEquals("e4", getVariationsAsString(gt));
+ gt.goForward(0);
+ varNo = gt.addMove("e5", "", 0, "", "");
+ assertEquals(0, varNo);
+ assertEquals("e5", getVariationsAsString(gt));
+ gt.goForward(0);
+ options.exp.playerAction = false;
+ pgn = gt.toPGN(options);
+ assertTrue(pgn.indexOf("e4") >= 0);
+ }
+
+ public final void testGameResult() throws ChessParseError {
+ GameTree gt = new GameTree(null);
+ int varNo = gt.addMove("e4", "", 0, "", "");
+ gt.goForward(varNo);
+ varNo = gt.addMove("e5", "", 0, "", "");
+ gt.goForward(varNo);
+ varNo = gt.addMove("--", "resign", 0, "", "");
+ gt.goBack();
+ gt.goBack();
+ varNo = gt.addMove("d4", "", 0, "", "");
+ gt.goForward(varNo);
+ varNo = gt.addMove("--", "resign", 0, "", "");
+ gt.goForward(varNo);
+
+ // Black has resigned in a variation, but white resigned in the mainline,
+ // so the PGN result should be "0-1";
+ PGNOptions options = new PGNOptions();
+ options.exp.variations = true;
+// options.exp.playerAction = true;
+ String pgn = gt.toPGN(options);
+ assertTrue(pgn.indexOf("1-0") < 0);
+ assertTrue(pgn.indexOf("0-1") >= 0);
+ }
+
+ public final void testPGNPromotion() throws ChessParseError {
+ // PGN standard specifies that promotion moves shall have an = sign
+ // before the promotion piece
+ GameTree gt = new GameTree(null);
+ gt.setStartPos(TextIO.readFEN("rnbqkbnr/ppPppppp/8/8/8/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1"));
+ int varNo = gt.addMove("cxb8N", "", 0, "", "");
+ assertEquals(0, varNo);
+ varNo = gt.addMove("cxd8R+", "", 0, "", "");
+ assertEquals(1, varNo);
+ assertEquals("cxb8N cxd8R+", getVariationsAsString(gt)); // Normal short alg notation does not have =
+ PGNOptions options = new PGNOptions();
+ options.exp.variations = true;
+ String pgn = gt.toPGN(options);
+ assertTrue(pgn.indexOf("cxb8=N") >= 0); // ... but PGN promotions do have the = sign
+ assertTrue(pgn.indexOf("cxd8=R+") >= 0);
+
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java
new file mode 100644
index 0000000..556698f
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java
@@ -0,0 +1,204 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author petero
+ */
+public class MoveGenTest extends TestCase {
+
+ public MoveGenTest() {
+ }
+
+ /**
+ * Test of pseudoLegalMoves method, of class MoveGen.
+ */
+ public void testPseudoLegalMoves() throws ChessParseError {
+ String fen = "8/3k4/8/2n2pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 2";
+ Position pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ List strMoves = getMoveList(pos, false);
+ assertTrue(strMoves.contains("Ra1-d1"));
+ assertTrue(!strMoves.contains("Ra1-e1"));
+ assertTrue(!strMoves.contains("Ra1-f1"));
+ assertTrue(strMoves.contains("Ra1-a7+"));
+ assertTrue(strMoves.contains("Ke1-f2"));
+ assertTrue(!strMoves.contains("Ke1-g3"));
+ assertTrue(strMoves.contains("Bc3-f6"));
+ assertTrue(!strMoves.contains("Nb3xd2"));
+
+ // Test castling
+ assertTrue(strMoves.contains("O-O"));
+ assertTrue(strMoves.contains("O-O-O"));
+ assertEquals(49, strMoves.size());
+
+ pos.setPiece(Position.getSquare(4,3), Piece.BROOK);
+ strMoves = getMoveList(pos, false);
+ assertTrue(!strMoves.contains("O-O")); // In check, not castling possible
+ assertTrue(!strMoves.contains("O-O-O"));
+
+ pos.setPiece(Position.getSquare(4, 3), Piece.EMPTY);
+ pos.setPiece(Position.getSquare(5, 3), Piece.BROOK);
+ strMoves = getMoveList(pos, false);
+ assertTrue(!strMoves.contains("O-O")); // f1 attacked, short castle not possible
+ assertTrue(strMoves.contains("O-O-O"));
+
+ pos.setPiece(Position.getSquare(5, 3), Piece.EMPTY);
+ pos.setPiece(Position.getSquare(6, 3), Piece.BBISHOP);
+ strMoves = getMoveList(pos, false);
+ assertTrue(strMoves.contains("O-O")); // d1 attacked, long castle not possible
+ assertTrue(!strMoves.contains("O-O-O"));
+
+ pos.setPiece(Position.getSquare(6, 3), Piece.EMPTY);
+ pos.setCastleMask(1 << Position.A1_CASTLE);
+ strMoves = getMoveList(pos, false);
+ assertTrue(!strMoves.contains("O-O")); // short castle right has been lost
+ assertTrue(strMoves.contains("O-O-O"));
+ }
+
+ /**
+ * Test of pseudoLegalMoves method, of class MoveGen. Pawn moves.
+ */
+ public void testPawnMoves() throws ChessParseError {
+ String fen = "1r2k3/P1pppp1p/8/1pP3p1/1nPp2P1/n4p1P/1P2PP2/4KBNR w K b6 0 1";
+ Position pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ List strMoves = getMoveList(pos, false);
+ assertTrue(strMoves.contains("c5xb6")); // En passant capture
+ assertTrue(strMoves.contains("a7-a8Q")); // promotion
+ assertTrue(strMoves.contains("a7-a8N")); // under promotion
+ assertTrue(strMoves.contains("a7xb8R#")); // capture promotion
+ assertTrue(strMoves.contains("b2-b3")); // pawn single move
+ assertTrue(strMoves.contains("b2xa3")); // pawn capture to the left
+ assertTrue(strMoves.contains("e2-e4")); // pawn double move
+ assertTrue(strMoves.contains("e2xf3")); // pawn capture to the right
+ assertEquals(22, strMoves.size());
+
+ pos.setEpSquare(-1);
+ strMoves = getMoveList(pos, false);
+ assertEquals(21, strMoves.size()); // No ep, one less move possible
+
+ // Check black pawn moves
+ pos.setWhiteMove(false);
+ strMoves = getMoveList(pos, false);
+ assertTrue(strMoves.contains("f3xe2"));
+ assertTrue(strMoves.contains("d4-d3"));
+ assertTrue(strMoves.contains("e7-e6"));
+ assertTrue(strMoves.contains("e7-e5"));
+ assertEquals(28, strMoves.size());
+
+ // Check black pawn promotion
+ pos.setPiece(Position.getSquare(0,1), Piece.BPAWN);
+ strMoves = getMoveList(pos, false);
+ assertTrue(strMoves.contains("a2-a1Q+"));
+ assertTrue(strMoves.contains("a2-a1R+"));
+ assertTrue(strMoves.contains("a2-a1N"));
+ assertTrue(strMoves.contains("a2-a1B"));
+ }
+
+ /**
+ * Test of inCheck method, of class MoveGen.
+ */
+ public void testInCheck() {
+ Position pos = new Position();
+ pos.setPiece(Position.getSquare(4,2), Piece.WKING);
+ pos.setPiece(Position.getSquare(4,7), Piece.BKING);
+ assertEquals(false, MoveGen.inCheck(pos));
+
+ pos.setPiece(Position.getSquare(3,3), Piece.BQUEEN);
+ assertEquals(true, MoveGen.inCheck(pos));
+ pos.setPiece(Position.getSquare(3,3), Piece.BROOK);
+ assertEquals(false, MoveGen.inCheck(pos));
+ pos.setPiece(Position.getSquare(3,3), Piece.BPAWN);
+ assertEquals(true, MoveGen.inCheck(pos));
+
+ pos.setPiece(Position.getSquare(3,3), Piece.EMPTY);
+ pos.setPiece(Position.getSquare(5,3), Piece.WQUEEN);
+ assertEquals(false, MoveGen.inCheck(pos));
+
+ pos.setPiece(Position.getSquare(4, 6), Piece.BROOK);
+ assertEquals(true, MoveGen.inCheck(pos));
+ pos.setPiece(Position.getSquare(4, 4), Piece.WPAWN);
+ assertEquals(false, MoveGen.inCheck(pos));
+
+ pos.setPiece(Position.getSquare(2, 3), Piece.BKNIGHT);
+ assertEquals(true, MoveGen.inCheck(pos));
+
+ pos.setPiece(Position.getSquare(2, 3), Piece.EMPTY);
+ pos.setPiece(Position.getSquare(0, 4), Piece.BKNIGHT);
+ assertEquals(false, MoveGen.inCheck(pos));
+ }
+
+ /**
+ * Test of removeIllegal method, of class MoveGen.
+ */
+ public void testRemoveIllegal() throws ChessParseError {
+ Position pos = TextIO.readFEN("8/3k4/8/2n1rpP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 1");
+ List strMoves = getMoveList(pos, true);
+ assertTrue(strMoves.contains("Qc2-e4"));
+ assertTrue(strMoves.contains("Bc3xe5"));
+ assertTrue(strMoves.contains("Ke1-d1"));
+ assertTrue(strMoves.contains("Ke1-f1"));
+ assertTrue(strMoves.contains("Ke1-f2"));
+ assertEquals(5, strMoves.size());
+ }
+
+ /**
+ * Test that if king capture is possible, only a king capture move is returned in the move list.
+ */
+ public void testKingCapture() throws ChessParseError {
+ Position pos = TextIO.readFEN("8/4k3/8/8/8/8/8/4RK2 b - - 0 1");
+ pos.setWhiteMove(true);
+ List strMoves = getMoveList(pos, false);
+ assertEquals(1, strMoves.size());
+ assertEquals("Re1xe7", strMoves.get(0));
+
+ pos.setPiece(Position.getSquare(0, 2), Piece.WBISHOP);
+ pos.setPiece(Position.getSquare(4, 1), Piece.WPAWN);
+ strMoves = getMoveList(pos, false);
+ assertEquals(1, strMoves.size());
+ assertEquals("Ba3xe7", strMoves.get(0));
+
+ pos.setPiece(Position.getSquare(1, 3), Piece.WPAWN);
+ pos.setPiece(Position.getSquare(5, 5), Piece.WPAWN);
+ strMoves = getMoveList(pos, false);
+ assertEquals(1, strMoves.size());
+ assertEquals("f6xe7", strMoves.get(0));
+ }
+
+ private List getMoveList(Position pos, boolean onlyLegal) {
+ ArrayList moves = new MoveGen().pseudoLegalMoves(pos);
+ if (onlyLegal) {
+ moves = MoveGen.removeIllegal(pos, moves);
+ }
+ ArrayList strMoves = new ArrayList();
+ for (Move m : moves) {
+ String mStr = TextIO.moveToString(pos, m, true);
+ strMoves.add(mStr);
+// System.out.println(mStr);
+ }
+ return strMoves;
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java
new file mode 100644
index 0000000..22cbafc
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java
@@ -0,0 +1,59 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author petero
+ */
+public class MoveTest extends TestCase {
+
+ public MoveTest() {
+ }
+
+ /**
+ * Test of move constructor, of class Move.
+ */
+ public void testMoveConstructor() {
+ int f = Position.getSquare(4, 1);
+ int t = Position.getSquare(4, 3);
+ int p = Piece.WROOK;
+ Move move = new Move(f, t, p);
+ assertEquals(move.from, f);
+ assertEquals(move.to,t);
+ assertEquals(move.promoteTo, p);
+ }
+
+ /**
+ * Test of equals, of class Move.
+ */
+ public void testEquals() {
+ Move m1 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK);
+ Move m2 = new Move(Position.getSquare(0, 6), Position.getSquare(0, 7), Piece.WROOK);
+ Move m3 = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK);
+ Move m4 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WKNIGHT);
+ Move m5 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK);
+ assertTrue(!m1.equals(m2));
+ assertTrue(!m1.equals(m3));
+ assertTrue(!m1.equals(m4));
+ assertTrue(m1.equals(m5));
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java
new file mode 100644
index 0000000..380fa34
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java
@@ -0,0 +1,42 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author petero
+ */
+public class PieceTest extends TestCase {
+
+ public PieceTest() {
+ }
+
+ /**
+ * Test of isWhite method, of class Piece.
+ */
+ public void testIsWhite() {
+ assertEquals(false, Piece.isWhite(Piece.BBISHOP));
+ assertEquals(true , Piece.isWhite(Piece.WBISHOP));
+ assertEquals(true , Piece.isWhite(Piece.WKING));
+ assertEquals(false, Piece.isWhite(Piece.BKING));
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java
new file mode 100644
index 0000000..f0ef483
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java
@@ -0,0 +1,455 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author petero
+ */
+public class PositionTest extends TestCase {
+
+ public PositionTest() {
+ }
+
+ /**
+ * Test of getPiece method, of class Position.
+ */
+ public void testGetPiece() throws ChessParseError {
+ Position pos = new Position();
+ int result = pos.getPiece(0);
+ assertEquals(result, Piece.EMPTY);
+
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ result = pos.getPiece(0);
+ assertEquals(result, Piece.WROOK);
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 2; y++) {
+ int p1 = pos.getPiece(Position.getSquare(x, y));
+ int p2 = pos.getPiece(Position.getSquare(x, 7-y));
+ int bwDiff = Piece.BPAWN - Piece.WPAWN;
+ assertEquals(p2, p1 + bwDiff);
+ }
+ }
+ }
+
+ /**
+ * Test of getIndex method, of class Position.
+ */
+ public void testGetIndex() {
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ int sq = Position.getSquare(x, y);
+ int x2 = Position.getX(sq);
+ int y2 = Position.getY(sq);
+ assertEquals(x, x2);
+ assertEquals(y, y2);
+ }
+ }
+ }
+
+ /**
+ * Test of setPiece method, of class Position.
+ */
+ public void testSetPiece() {
+ Position instance = new Position();
+ assertEquals(Piece.EMPTY, instance.getPiece(Position.getSquare(0, 0)));
+ instance.setPiece(Position.getSquare(3, 4), Piece.WKING);
+ assertEquals(Piece.WKING, instance.getPiece(Position.getSquare(3, 4)));
+ }
+
+ /**
+ * Test of makeMove method, of class Position.
+ */
+ public void testMakeMove() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ Position origPos = new Position(pos);
+ assertTrue(pos.equals(origPos));
+ Move move = new Move(Position.getSquare(4,1), Position.getSquare(4,3), Piece.EMPTY);
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(move, ui);
+ assertEquals(pos.whiteMove, false);
+ assertEquals(-1, pos.getEpSquare());
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,1)));
+ assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,3)));
+ assertTrue(!pos.equals(origPos));
+ int castleMask = (1 << Position.A1_CASTLE) |
+ (1 << Position.H1_CASTLE) |
+ (1 << Position.A8_CASTLE) |
+ (1 << Position.H8_CASTLE);
+ assertEquals(castleMask,pos.getCastleMask());
+ pos.unMakeMove(move, ui);
+ assertEquals(pos.whiteMove, true);
+ assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,1)));
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,3)));
+ assertTrue(pos.equals(origPos));
+
+ String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 2";
+ pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ origPos = new Position(pos);
+ assertEquals(Position.getSquare(1,5), pos.getEpSquare());
+
+ // Test capture
+ move = new Move(Position.getSquare(0, 3), Position.getSquare(1,4), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ assertEquals(-1, pos.getEpSquare());
+ assertEquals(Piece.WBISHOP, pos.getPiece(Position.getSquare(1,4)));
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(0,3)));
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos));
+
+ // Test castling
+ move = new Move(Position.getSquare(4, 0), Position.getSquare(6,0), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ assertEquals(Piece.WROOK, pos.getPiece(Position.getSquare(5,0)));
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(7,0)));
+ castleMask = (1 << Position.A8_CASTLE) |
+ (1 << Position.H8_CASTLE);
+ assertEquals(castleMask,pos.getCastleMask());
+ assertEquals(-1, pos.getEpSquare());
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos));
+
+ // Test castling rights (king move)
+ move = new Move(Position.getSquare(4, 0), Position.getSquare(4,1), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ castleMask = (1 << Position.A8_CASTLE) |
+ (1 << Position.H8_CASTLE);
+ assertEquals(castleMask,pos.getCastleMask());
+ assertEquals(-1, pos.getEpSquare());
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos));
+
+ // Test castling rights (rook move)
+ move = new Move(Position.getSquare(7, 0), Position.getSquare(6,0), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ castleMask = (1 << Position.A1_CASTLE) |
+ (1 << Position.A8_CASTLE) |
+ (1 << Position.H8_CASTLE);
+ assertEquals(castleMask,pos.getCastleMask());
+ assertEquals(-1, pos.getEpSquare());
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos));
+
+ // Test en passant
+ move = new Move(Position.getSquare(2, 4), Position.getSquare(1,5), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(1,5)));
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(2,4)));
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,4)));
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos));
+
+ // Test castling rights loss when rook captured
+ pos.setPiece(Position.getSquare(6,2), Piece.BKNIGHT);
+ pos.setWhiteMove(false);
+ Position origPos2 = new Position(pos);
+ move = new Move(Position.getSquare(6,2), Position.getSquare(7,0), Piece.EMPTY);
+ pos.makeMove(move, ui);
+ castleMask = (1 << Position.A1_CASTLE) |
+ (1 << Position.A8_CASTLE) |
+ (1 << Position.H8_CASTLE);
+ assertEquals(castleMask,pos.getCastleMask());
+ assertEquals(-1, pos.getEpSquare());
+ pos.unMakeMove(move, ui);
+ assertTrue(pos.equals(origPos2));
+ }
+
+ /**
+ * Test of makeMove method, of class Position.
+ */
+ public void testPromotion() throws ChessParseError {
+ String fen = "r1bqk2r/1Pppbppp/p1n2n2/2P1p3/B3P3/5N2/Pp1P1PPP/R1BQK2R w KQkq - 0 1";
+ Position pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ Position origPos = new Position(pos);
+ assertEquals(origPos, pos);
+
+ Move move = new Move(Position.getSquare(1, 6), Position.getSquare(0,7), Piece.WQUEEN);
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(move, ui);
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6)));
+ assertEquals(Piece.WQUEEN, pos.getPiece(Position.getSquare(0,7)));
+ pos.unMakeMove(move, ui);
+ assertEquals(origPos, pos);
+
+ move = new Move(Position.getSquare(1, 6), Position.getSquare(1,7), Piece.WKNIGHT);
+ ui = new UndoInfo();
+ pos.makeMove(move, ui);
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6)));
+ assertEquals(Piece.WKNIGHT, pos.getPiece(Position.getSquare(1,7)));
+ pos.unMakeMove(move, ui);
+ assertEquals(origPos, pos);
+
+ pos.setWhiteMove(false);
+ origPos = new Position(pos);
+
+ move = new Move(Position.getSquare(1, 1), Position.getSquare(2, 0), Piece.BROOK);
+ ui = new UndoInfo();
+ pos.makeMove(move, ui);
+ assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,1)));
+ assertEquals(Piece.BROOK, pos.getPiece(Position.getSquare(2,0)));
+ pos.unMakeMove(move, ui);
+ assertEquals(origPos, pos);
+ }
+
+ /**
+ * Test move counters, of class Position.
+ */
+ public void testMoveCounters() throws ChessParseError {
+ String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 7";
+ Position pos = TextIO.readFEN(fen);
+
+ Move move = TextIO.stringToMove(pos, "Nc3");
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(move, ui);
+ assertEquals(1, pos.halfMoveClock);
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ move = TextIO.stringToMove(pos, "O-O");
+ pos.makeMove(move, ui);
+ assertEquals(1, pos.halfMoveClock); // Castling does not reset 50 move counter
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ move = TextIO.stringToMove(pos, "a3");
+ pos.makeMove(move, ui);
+ assertEquals(0, pos.halfMoveClock); // Pawn move resets 50 move counter
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ move = TextIO.stringToMove(pos, "Nxe5");
+ pos.makeMove(move, ui);
+ assertEquals(0, pos.halfMoveClock); // Capture move resets 50 move counter
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ move = TextIO.stringToMove(pos, "cxb6");
+ pos.makeMove(move, ui);
+ assertEquals(0, pos.halfMoveClock); // EP capture move resets 50 move counter
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ move = TextIO.stringToMove(pos, "Kf1");
+ pos.makeMove(move, ui);
+ assertEquals(1, pos.halfMoveClock); // Loss of castling rights does not reset 50 move counter
+ assertEquals(7, pos.fullMoveCounter);
+ pos.unMakeMove(move, ui);
+
+ Move firstMove = TextIO.stringToMove(pos, "Nc3");
+ UndoInfo firstUi = new UndoInfo();
+ pos.makeMove(move, firstUi);
+ move = TextIO.stringToMove(pos, "O-O");
+ pos.makeMove(move, ui);
+ assertEquals(2, pos.halfMoveClock);
+ assertEquals(8, pos.fullMoveCounter); // Black move increases fullMoveCounter
+ pos.unMakeMove(move, ui);
+ pos.unMakeMove(firstMove, firstUi);
+
+ fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68";
+ pos = TextIO.readFEN(fen);
+ move = TextIO.stringToMove(pos, "c1Q");
+ pos.makeMove(move, ui);
+ assertEquals(0, pos.halfMoveClock); // Pawn promotion resets 50 move counter
+ assertEquals(69, pos.fullMoveCounter);
+ }
+
+ /**
+ * Test of drawRuleEquals, of class Position.
+ */
+ public void testDrawRuleEquals() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ Position origPos = new Position(pos);
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(TextIO.stringToMove(pos, "Nf3"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Nf6"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Ng1"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Ng8"), ui);
+ assertEquals(true, pos.drawRuleEquals(origPos));
+ assertEquals(false, pos.equals(origPos)); // Move counters have changed
+
+ String fen = "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1";
+ pos = TextIO.readFEN(fen);
+ origPos = new Position(pos);
+ pos.makeMove(TextIO.stringToMove(pos, "Ke2"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Be7"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Ke1"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos));
+ pos.makeMove(TextIO.stringToMove(pos, "Bf8"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, castling rights lost
+
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ pos.makeMove(TextIO.stringToMove(pos, "c4"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "a6"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "c5"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "b5"), ui);
+ assertEquals(Position.getSquare(1, 5), pos.getEpSquare());
+ origPos = new Position(pos);
+ pos.makeMove(TextIO.stringToMove(pos, "Nc3"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "Nc6"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "Nb1"), ui);
+ pos.makeMove(TextIO.stringToMove(pos, "Nb8"), ui);
+ assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, en passant rights lost
+ }
+
+ /**
+ * Test of hashCode method, of class Position.
+ */
+ public void testHashCode() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ long h1 = pos.zobristHash();
+ assertEquals(h1, pos.computeZobristHash());
+ UndoInfo ui = new UndoInfo();
+ Move move = TextIO.stringToMove(pos, "e4");
+ pos.makeMove(move, ui);
+ assertTrue(h1 != pos.zobristHash());
+ pos.unMakeMove(move, ui);
+ assertTrue(h1 == pos.zobristHash());
+
+ pos.setWhiteMove(!pos.whiteMove);
+ long h4 = pos.zobristHash();
+ assertEquals(h4, pos.computeZobristHash());
+ assertTrue(h1 != pos.zobristHash());
+ pos.setWhiteMove(!pos.whiteMove);
+ assertTrue(h1 == pos.zobristHash());
+
+ pos.setCastleMask(0);
+ assertTrue(h1 != pos.zobristHash());
+
+ pos = TextIO.readFEN("rnbqkbnr/pppp1ppp/8/2P1p3/8/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1");
+ h1 = pos.zobristHash();
+ assertEquals(h1, pos.computeZobristHash());
+
+ String[] moves = {
+ "b5", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "d5",
+ "cxd6", "Qxd6", "h4", "Be6", "h5", "Nc6", "h6", "o-o-o", "hxg7", "Nf6", "gxh8Q", "Be7"
+ };
+ List uiList = new ArrayList();
+ List hashList = new ArrayList();
+ List moveList = new ArrayList();
+ for (int i = 0; i < moves.length; i++) {
+ uiList.add(new UndoInfo());
+ Move m = TextIO.stringToMove(pos, moves[i]);
+ moveList.add(m);
+ pos.makeMove(m, uiList.get(i));
+ long h = pos.zobristHash();
+ assertEquals(h, pos.computeZobristHash());
+ hashList.add(h);
+ }
+ assertTrue(!hashList.get(0).equals(hashList.get(4)));
+ assertTrue(hashList.get(4).equals(hashList.get(8)));
+ for (int i = moves.length - 1; i >= 0; i--) {
+ pos.unMakeMove(moveList.get(i), uiList.get(i));
+ long h = pos.zobristHash();
+ assertEquals(h, pos.computeZobristHash());
+ assertEquals(h, i > 0 ? hashList.get(i - 1) : h1);
+ }
+ }
+
+ /**
+ * Test of getKingSq method, of class Position.
+ */
+ public void testGetKingSq() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true));
+ assertEquals(TextIO.getSquare("e8"), pos.getKingSq(false));
+ pos = TextIO.readFEN("r1bq1bnr/ppppkppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQ - 0 4");
+ assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true));
+ assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false));
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(TextIO.stringToMove(pos, "o-o"), ui);
+ assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true));
+ assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false));
+ pos.makeMove(TextIO.stringToMove(pos, "Kd6"), ui);
+ assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true));
+ assertEquals(TextIO.getSquare("d6"), pos.getKingSq(false));
+ }
+
+ public void testNullMove() throws ChessParseError {
+ Move nullMove = new Move(0, 0, 0);
+ UndoInfo ui = new UndoInfo();
+
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ Position origPos = new Position(pos);
+ pos.makeMove(nullMove, ui);
+ assertEquals(false, pos.whiteMove);
+ assertEquals(true, pos.a1Castle());
+ assertEquals(Piece.WROOK, pos.getPiece(0));
+ pos.unMakeMove(nullMove, ui);
+ assertTrue(pos.equals(origPos));
+
+ pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Kkq - 5 1");
+ origPos = new Position(pos);
+ pos.makeMove(nullMove, ui);
+ assertEquals(false, pos.whiteMove);
+ assertEquals(false, pos.a1Castle());
+ assertEquals(Piece.WROOK, pos.getPiece(0));
+ assertEquals(0, pos.halfMoveClock);
+ pos.unMakeMove(nullMove, ui);
+ assertTrue(pos.equals(origPos));
+
+ pos = TextIO.readFEN("7k/8/8/8/8/8/4P3/K7 w - - 5 1");
+ origPos = new Position(pos);
+ pos.makeMove(nullMove, ui);
+ assertEquals(false, pos.whiteMove);
+ assertEquals(false, pos.a1Castle());
+ assertEquals(0, pos.halfMoveClock);
+ assertEquals(0, pos.getKingSq(true));
+ assertEquals(63, pos.getKingSq(false));
+
+ UndoInfo ui2 = new UndoInfo();
+ pos.makeMove(nullMove, ui2);
+ assertEquals(true, pos.whiteMove);
+ assertEquals(false, pos.a1Castle());
+ assertEquals(0, pos.halfMoveClock);
+ assertEquals(0, pos.getKingSq(true));
+ assertEquals(63, pos.getKingSq(false));
+
+ pos.unMakeMove(nullMove, ui2);
+ assertEquals(false, pos.whiteMove);
+ assertEquals(false, pos.a1Castle());
+ assertEquals(0, pos.halfMoveClock);
+ assertEquals(0, pos.getKingSq(true));
+ assertEquals(63, pos.getKingSq(false));
+
+ pos.unMakeMove(nullMove, ui);
+ assertTrue(pos.equals(origPos));
+
+ pos = TextIO.readFEN("5k2/8/4R1K1/8/8/8/8/8 b - - 5 1");
+ origPos = new Position(pos);
+ pos.makeMove(nullMove, ui);
+ assertEquals(true, pos.whiteMove);
+ assertEquals(false, pos.a1Castle());
+ assertEquals(0, pos.halfMoveClock);
+
+ pos.unMakeMove(nullMove, ui);
+ assertTrue(pos.equals(origPos));
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java
new file mode 100644
index 0000000..77622b5
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java
@@ -0,0 +1,382 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author petero
+ */
+public class TextIOTest extends TestCase {
+
+ public TextIOTest() {
+ }
+
+ /**
+ * Test of readFEN method, of class TextIO.
+ */
+ public void testReadFEN() throws ChessParseError {
+ String fen = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R b kq - 0 1";
+ Position pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ assertEquals(pos.getPiece(Position.getSquare(0, 3)), Piece.WQUEEN);
+ assertEquals(pos.getPiece(Position.getSquare(4, 7)), Piece.BKING);
+ assertEquals(pos.getPiece(Position.getSquare(4, 1)), Piece.WKING);
+ assertEquals(pos.whiteMove, false);
+ assertEquals(pos.a1Castle(), false);
+ assertEquals(pos.h1Castle(), false);
+ assertEquals(pos.a8Castle(), true);
+ assertEquals(pos.h8Castle(), true);
+
+ fen = "8/3k4/8/5pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 1 2";
+ pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ assertEquals(1, pos.halfMoveClock);
+ assertEquals(2, pos.fullMoveCounter);
+
+ // Must have exactly one king
+ boolean wasError = testFENParseError("8/8/8/8/8/8/8/kk1K4 w - - 0 1");
+ assertEquals(true, wasError);
+
+ // Must not be possible to capture the king
+ wasError = testFENParseError("8/8/8/8/8/8/8/k1RK4 w - - 0 1");
+ assertEquals(true, wasError);
+
+ // Make sure bogus en passant square information is removed
+ fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1";
+ pos = TextIO.readFEN(fen);
+ assertEquals(-1, pos.getEpSquare());
+
+ // Test for too many rows (slashes)
+ wasError = testFENParseError("8/8/8/8/4k3/8/8/8/KBN5 w - - 0 1");
+ assertEquals(true, wasError);
+
+ // Test for too many columns
+ wasError = testFENParseError("8K/8/8/8/4k3/8/8/8 w - - 0 1");
+ assertEquals(true, wasError);
+
+ // Pawns must not be on first/last rank
+ wasError = testFENParseError("kp6/8/8/8/8/8/8/K7 w - - 0 1");
+ assertEquals(true, wasError);
+
+ wasError = testFENParseError("kr/pppp/8/8/8/8/8/KBR w");
+ assertEquals(false, wasError); // OK not to specify castling flags and ep square
+
+ wasError = testFENParseError("k/8/8/8/8/8/8/K");
+ assertEquals(true, wasError); // Error side to move not specified
+
+ wasError = testFENParseError("");
+ assertEquals(true, wasError);
+
+ wasError = testFENParseError(" |");
+ assertEquals(true, wasError);
+
+ wasError = testFENParseError("1B1B4/6k1/7r/7P/6q1/r7/q7/7K b - - acn 6; acs 0;");
+ assertEquals(false, wasError); // Extra stuff after FEN string is allowed
+
+ pos = TextIO.readFEN("3r2k1/p4p1p/1ppq2p1/8/2PpQ3/1P5P/P4PP1/3R1K2 b - - 1 26\n");
+ assertEquals(26, pos.fullMoveCounter);
+
+ // Test that insane move numbers are rejected. Otherwise, could cause problems
+ // (excessive memory usage) for the clock history class.
+ pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 10000");
+ assertEquals(10000, pos.fullMoveCounter);
+ }
+
+ /**
+ * Test that readFEN removes bogus castle flags.
+ */
+ public void testReadFENCastleFlags() throws ChessParseError {
+ String fenBogus = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R w KQkq - 0 1";
+ Position pos = TextIO.readFEN(fenBogus);
+ String fenCorrect = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R w kq - 0 1";
+ assertEquals(fenCorrect, TextIO.toFEN(pos));
+ }
+
+ /** Tests if trying to parse a FEN string causes an error. */
+ private boolean testFENParseError(String fen) {
+ boolean wasError;
+ wasError = false;
+ try {
+ TextIO.readFEN(fen);
+ } catch (ChessParseError err) {
+ wasError = true;
+ }
+ return wasError;
+ }
+
+ /**
+ * Test of moveToString method, of class TextIO.
+ */
+ public void testMoveToString() throws ChessParseError {
+ Position pos = TextIO.readFEN(TextIO.startPosFEN);
+ assertEquals(TextIO.startPosFEN, TextIO.toFEN(pos));
+ Move move = new Move(Position.getSquare(4, 1), Position.getSquare(4, 3),
+ Piece.EMPTY);
+ boolean longForm = true;
+ String result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("e2-e4", result);
+
+ move = new Move(Position.getSquare(6, 0), Position.getSquare(5, 2), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Ng1-f3", result);
+
+ move = new Move(Position.getSquare(4, 7), Position.getSquare(2, 7),
+ Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("O-O-O", result);
+
+ String fen = "1r3k2/2P5/8/8/8/4K3/8/8 w - - 0 1";
+ pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ move = new Move(Position.getSquare(2,6), Position.getSquare(1,7), Piece.WROOK);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("c7xb8R+", result);
+
+ move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WKNIGHT);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("c7-c8N", result);
+
+ move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WQUEEN);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("c7-c8Q+", result);
+
+ // Test null move
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ Move nullMove = new Move(0, 0, 0);
+ result = TextIO.moveToString(pos, nullMove, false);
+ assertEquals("--", result);
+ result = TextIO.moveToString(pos, nullMove, true);
+ assertEquals("--", result);
+ }
+
+ /**
+ * Test of moveToString method, of class TextIO, mate/stalemate tests.
+ */
+ public void testMoveToStringMate() throws ChessParseError {
+ Position pos = TextIO.readFEN("3k4/1PR5/3N4/8/4K3/8/8/8 w - - 0 1");
+ boolean longForm = true;
+
+ Move move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK);
+ String result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("b7-b8R+", result); // check
+
+ move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WQUEEN);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("b7-b8Q#", result); // check mate
+
+ move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WKNIGHT);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("b7-b8N", result);
+
+ move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WBISHOP);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("b7-b8B", result); // stalemate
+ }
+
+ /**
+ * Test of moveToString method, of class TextIO, short form.
+ */
+ public void testMoveToStringShortForm() throws ChessParseError {
+ String fen = "r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1";
+ Position pos = TextIO.readFEN(fen);
+ assertEquals(fen, TextIO.toFEN(pos));
+ boolean longForm = false;
+
+ Move move = new Move(Position.getSquare(4,5), Position.getSquare(4,3), Piece.EMPTY);
+ String result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Qee4", result); // File disambiguation needed
+
+ move = new Move(Position.getSquare(2,5), Position.getSquare(4,3), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Qc6e4", result); // Full disambiguation needed
+
+ move = new Move(Position.getSquare(2,3), Position.getSquare(4,3), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Q4e4", result); // Row disambiguation needed
+
+ move = new Move(Position.getSquare(2,3), Position.getSquare(2,0), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Qc1+", result); // No disambiguation needed
+
+ move = new Move(Position.getSquare(0,1), Position.getSquare(0,0), Piece.BQUEEN);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("a1Q", result); // Normal promotion
+
+ move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BQUEEN);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("axb1Q#", result); // Capture promotion and check mate
+
+ move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BKNIGHT);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("axb1N", result); // Capture promotion
+
+ move = new Move(Position.getSquare(3,6), Position.getSquare(4,4), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Ne5", result); // Other knight pinned, no disambiguation needed
+
+ move = new Move(Position.getSquare(7,6), Position.getSquare(7,4), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("h5", result); // Regular pawn move
+
+ move = new Move(Position.getSquare(5,7), Position.getSquare(3,7), Piece.EMPTY);
+ result = TextIO.moveToString(pos, move, longForm);
+ assertEquals("Rfd8", result); // File disambiguation needed
+ }
+
+ /**
+ * Test of stringToMove method, of class TextIO.
+ */
+ public void testStringToMove() throws ChessParseError {
+ Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1");
+
+ Move mNe5 = new Move(Position.getSquare(3, 6), Position.getSquare(4, 4), Piece.EMPTY);
+ Move m = TextIO.stringToMove(pos, "Ne5");
+ assertEquals(mNe5, m);
+ m = TextIO.stringToMove(pos, "ne");
+ assertEquals(mNe5, m);
+ m = TextIO.stringToMove(pos, "N");
+ assertEquals(null, m);
+
+ Move mQc6e4 = new Move(Position.getSquare(2, 5), Position.getSquare(4, 3), Piece.EMPTY);
+ m = TextIO.stringToMove(pos, "Qc6-e4");
+ assertEquals(mQc6e4, m);
+ m = TextIO.stringToMove(pos, "Qc6e4");
+ assertEquals(mQc6e4, m);
+ m = TextIO.stringToMove(pos, "Qce4");
+ assertEquals(null, m);
+ m = TextIO.stringToMove(pos, "Q6e4");
+ assertEquals(null, m);
+
+ Move maxb1Q = new Move(Position.getSquare(0, 1), Position.getSquare(1, 0), Piece.BQUEEN);
+ m = TextIO.stringToMove(pos, "axb1Q");
+ assertEquals(maxb1Q, m);
+ m = TextIO.stringToMove(pos, "axb1Q#");
+ assertEquals(maxb1Q, m);
+ m = TextIO.stringToMove(pos, "axb1Q+");
+ assertEquals(maxb1Q, m);
+
+ Move mh5= new Move(Position.getSquare(7, 6), Position.getSquare(7, 4), Piece.EMPTY);
+ m = TextIO.stringToMove(pos, "h5");
+ assertEquals(mh5, m);
+ m = TextIO.stringToMove(pos, "h7-h5");
+ assertEquals(mh5, m);
+ m = TextIO.stringToMove(pos, "h");
+ assertEquals(null, m);
+
+ pos = TextIO.readFEN("r1b1k2r/1pqpppbp/p5pn/3BP3/8/2pP4/PPPBQPPP/R3K2R w KQkq - 0 12");
+ m = TextIO.stringToMove(pos, "bxc3");
+ assertEquals(TextIO.getSquare("b2"), m.from);
+ m = TextIO.stringToMove(pos, "Bxc3");
+ assertEquals(TextIO.getSquare("d2"), m.from);
+ m = TextIO.stringToMove(pos, "bxc");
+ assertEquals(TextIO.getSquare("b2"), m.from);
+ m = TextIO.stringToMove(pos, "Bxc");
+ assertEquals(TextIO.getSquare("d2"), m.from);
+
+ // Test castling. o-o is a substring of o-o-o, which could cause problems.
+ pos = TextIO.readFEN("5k2/p1pQn3/1p2Bp1r/8/4P1pN/2N5/PPP2PPP/R3K2R w KQ - 0 16");
+ Move kCastle = new Move(Position.getSquare(4,0), Position.getSquare(6,0), Piece.EMPTY);
+ Move qCastle = new Move(Position.getSquare(4,0), Position.getSquare(2,0), Piece.EMPTY);
+ m = TextIO.stringToMove(pos, "o");
+ assertEquals(null, m);
+ m = TextIO.stringToMove(pos, "o-o");
+ assertEquals(kCastle, m);
+ m = TextIO.stringToMove(pos, "O-O");
+ assertEquals(kCastle, m);
+ m = TextIO.stringToMove(pos, "0-0");
+ assertEquals(kCastle, m);
+ m = TextIO.stringToMove(pos, "O-O-O");
+ assertEquals(qCastle, m);
+ m = TextIO.stringToMove(pos, "o-o-o");
+ assertEquals(qCastle, m);
+ m = TextIO.stringToMove(pos, "0-0-0");
+ assertEquals(qCastle, m);
+
+ // Test 'o-o+'
+ pos.setPiece(Position.getSquare(5,1), Piece.EMPTY);
+ pos.setPiece(Position.getSquare(5,5), Piece.EMPTY);
+ m = TextIO.stringToMove(pos, "o");
+ assertEquals(null, m);
+ m = TextIO.stringToMove(pos, "o-o");
+ assertEquals(kCastle, m);
+ m = TextIO.stringToMove(pos, "o-o-o");
+ assertEquals(qCastle, m);
+ m = TextIO.stringToMove(pos, "o-o+");
+ assertEquals(kCastle, m);
+
+ // Test d8=Q+ syntax
+ pos = TextIO.readFEN("1r3r2/2kP2Rp/p1bN1p2/2p5/5P2/2P5/P5PP/3R2K1 w - -");
+ m = TextIO.stringToMove(pos, "d8=Q+");
+ Move m2 = TextIO.stringToMove(pos, "d8Q");
+ assertEquals(m2, m);
+
+ // Test null move
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ Move nullMove = new Move(0, 0, 0);
+ m = TextIO.stringToMove(pos, "--");
+ assertEquals(nullMove, m);
+
+ // Test extra characters
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ Move mNf3 = new Move(TextIO.getSquare("g1"), TextIO.getSquare("f3"), Piece.EMPTY);
+ assertEquals(mNf3, TextIO.stringToMove(pos, "Ngf3"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "Ng1f3"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "Ng1-f3"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "g1f3"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "N1f3"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "Ngf"));
+ assertEquals(mNf3, TextIO.stringToMove(pos, "Nf"));
+ }
+
+ /**
+ * Test of getSquare method, of class TextIO.
+ */
+ public void testGetSquare() throws ChessParseError {
+ assertEquals(Position.getSquare(0, 0), TextIO.getSquare("a1"));
+ assertEquals(Position.getSquare(1, 7), TextIO.getSquare("b8"));
+ assertEquals(Position.getSquare(3, 3), TextIO.getSquare("d4"));
+ assertEquals(Position.getSquare(4, 3), TextIO.getSquare("e4"));
+ assertEquals(Position.getSquare(3, 1), TextIO.getSquare("d2"));
+ assertEquals(Position.getSquare(7, 7), TextIO.getSquare("h8"));
+ }
+
+ /**
+ * Test of squareToString method, of class TextIO.
+ */
+ public void testSquareToString() {
+ assertEquals("a1", TextIO.squareToString(Position.getSquare(0, 0)));
+ assertEquals("h6", TextIO.squareToString(Position.getSquare(7, 5)));
+ assertEquals("e4", TextIO.squareToString(Position.getSquare(4, 3)));
+ }
+
+ /**
+ * Test of asciiBoard method, of class TextIO.
+ */
+ public void testAsciiBoard() throws ChessParseError {
+ Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1");
+ String aBrd = TextIO.asciiBoard(pos);
+// System.out.print(aBrd);
+ assertEquals(12, aBrd.length() - aBrd.replaceAll("\\*", "").length()); // 12 black pieces
+ assertEquals(3, aBrd.length() - aBrd.replaceAll("\\*Q", " ").length()); // 3 black queens
+ assertEquals(3, aBrd.length() - aBrd.replaceAll(" P", " ").length()); // 3 white pawns
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java
new file mode 100644
index 0000000..fab2c24
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java
@@ -0,0 +1,136 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gamelogic;
+
+import junit.framework.TestCase;
+
+
+public class TimeControlTest extends TestCase {
+ public TimeControlTest() {
+ }
+
+ public void testElapsedTime() {
+ TimeControl tc = new TimeControl();
+ long totTime = 5 * 60 * 1000;
+ long t0 = 1000;
+ tc.setTimeControl(totTime, 0, 0);
+ tc.setCurrentMove(1, true, totTime, totTime);
+ assertEquals(0, tc.getMovesToTC());
+ assertEquals(0, tc.getIncrement());
+ assertEquals(totTime, tc.getRemainingTime(true, 0));
+ tc.startTimer(t0);
+ int remain = tc.moveMade(t0 + 1000, true);
+ assertEquals(totTime - 1000, remain);
+
+ tc.setCurrentMove(2, true, totTime - 1000, totTime);
+ assertEquals(0, tc.getMovesToTC());
+ assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(totTime, tc.getRemainingTime(false, t0 + 4711));
+
+ tc.setCurrentMove(1, false, totTime - 1000, totTime);
+ assertEquals(0, tc.getMovesToTC());
+ assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(totTime, tc.getRemainingTime(false, t0 + 4711));
+
+ tc.startTimer(t0 + 3000);
+ assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 5000));
+ assertEquals(totTime - 2000, tc.getRemainingTime(false, t0 + 5000));
+ tc.stopTimer(t0 + 8000);
+ assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(totTime - 5000, tc.getRemainingTime(false, t0 + 4711));
+ remain = tc.moveMade(t0 + 8000, true);
+ assertEquals(totTime - 5000, remain);
+ tc.setCurrentMove(2, true, totTime - 1000, totTime - 5000);
+ assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(totTime - 5000, tc.getRemainingTime(false, t0 + 4711));
+ }
+
+ /** Test getMovesToTC */
+ public void testTimeControl() {
+ TimeControl tc = new TimeControl();
+ tc.setTimeControl(2 * 60 * 1000, 40, 0);
+ tc.setCurrentMove(1, true, 0, 0);
+ assertEquals(40, tc.getMovesToTC());
+ tc.setCurrentMove(1, false, 0, 0);
+ assertEquals(40, tc.getMovesToTC());
+
+ tc.setCurrentMove(2, true, 0, 0);
+ assertEquals(39, tc.getMovesToTC());
+
+ tc.setCurrentMove(40, true, 0, 0);
+ assertEquals(1, tc.getMovesToTC());
+
+ tc.setCurrentMove(41, true, 0, 0);
+ assertEquals(40, tc.getMovesToTC());
+
+ tc.setCurrentMove(80, true, 0, 0);
+ assertEquals(1, tc.getMovesToTC());
+
+ tc.setCurrentMove(81, true, 0, 0);
+ assertEquals(40, tc.getMovesToTC());
+ }
+
+ public void testExtraTime() {
+ TimeControl tc = new TimeControl();
+ final long timeCont = 60 * 1000;
+ int wBaseTime = (int)timeCont;
+ int bBaseTime = (int)timeCont;
+ final long inc = 700;
+ tc.setTimeControl(timeCont, 5, inc);
+ tc.setCurrentMove(5, true, wBaseTime, bBaseTime);
+ long t0 = 1342134;
+ assertEquals(timeCont, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711));
+
+ tc.startTimer(t0 + 1000);
+ wBaseTime = tc.moveMade(t0 + 2000, true);
+ tc.setCurrentMove(5, false, wBaseTime, bBaseTime);
+ assertEquals(timeCont - 1000 + timeCont + inc, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711));
+
+ tc.startTimer(t0 + 2000);
+ bBaseTime = tc.moveMade(t0 + 6000, true);
+ tc.setCurrentMove(6, true, wBaseTime, bBaseTime);
+ assertEquals(timeCont - 1000 + timeCont + inc, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont - 4000 + timeCont + inc, tc.getRemainingTime(false, t0 + 4711));
+
+ tc.startTimer(t0 + 6000);
+ wBaseTime = tc.moveMade(t0 + 9000, true);
+ tc.setCurrentMove(6, false, wBaseTime, bBaseTime);
+ assertEquals(timeCont - 1000 + timeCont + inc - 3000 + inc, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont - 4000 + timeCont + inc, tc.getRemainingTime(false, t0 + 4711));
+
+ // No increment when move made int paused mode, ie analysis mode
+ tc.startTimer(t0 + 9000);
+ bBaseTime = tc.moveMade(t0 + 10000, false);
+ tc.setCurrentMove(7, true, wBaseTime, bBaseTime);
+ assertEquals(timeCont - 1000 + timeCont + inc - 3000 + inc, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont - 4000 + timeCont + inc - 1000, tc.getRemainingTime(false, t0 + 4711));
+
+ // No extra time when passing time control in analysis mode
+ tc.setTimeControl(timeCont, 1, inc);
+ wBaseTime = bBaseTime = (int)timeCont;
+ tc.setCurrentMove(1, true, wBaseTime, bBaseTime);
+ tc.startTimer(t0 + 1000);
+ wBaseTime = tc.moveMade(t0 + 3000, false);
+ tc.setCurrentMove(1, false, wBaseTime, bBaseTime);
+ assertEquals(timeCont - 2000 + (timeCont + inc)*0, tc.getRemainingTime(true, t0 + 4711));
+ assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711));
+ }
+}