From b73f1d477bc994b2f767e450f904c3766cc5d6ce Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 25 Dec 2016 21:38:25 +0100 Subject: [PATCH] DroidFish: A mating move could be added more than once to the game tree. --- .../org/petero/droidfish/gamelogic/Game.java | 22 +++++- .../petero/droidfish/gamelogic/GameTest.java | 76 +++++++++++++++++++ .../droidfish/gamelogic/GameTreeTest.java | 2 +- 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java index 6c68a97..ca7d83f 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java @@ -88,6 +88,7 @@ public class Game { updateTimeControl(false); } + /** Set game state from a PGN string. */ final public boolean readPGN(String pgn, PGNOptions options) throws ChessParseError { boolean ret = tree.readPGN(pgn, options); if (ret) { @@ -192,13 +193,18 @@ public class Game { nVars = varMoves.size(); } } + Boolean gameEndingMove = null; for (varNo = 0; varNo < nVars; varNo++) { if (varMoves.get(varNo).equals(m)) { boolean match = true; if (playerAction.isEmpty()) { - tree.goForward(varNo, false); - match = tree.getGameState() == GameState.ALIVE; - tree.goBack(); + if (gameEndingMove == null) + gameEndingMove = gameEndingMove(m); + if (!gameEndingMove) { + tree.goForward(varNo, false); + match = tree.getGameState() == GameState.ALIVE; + tree.goBack(); + } } if (match) { movePresent = true; @@ -222,6 +228,16 @@ public class Game { pendingDrawOffer = false; } + /** Return true if move "m" in the current position ends the game (mate or stalemate). */ + private boolean gameEndingMove(Move m) { + Position pos = currPos(); + UndoInfo ui = new UndoInfo(); + pos.makeMove(m, ui); + boolean gameEnd = MoveGen.instance.legalMoves(pos).isEmpty(); + pos.unMakeMove(m, ui); + return gameEnd; + } + private final void updateTimeControl(boolean discardElapsed) { Position currPos = currPos(); int move = currPos.fullMoveCounter; diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java index cf3bd22..ef8d939 100644 --- a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java @@ -20,6 +20,8 @@ package org.petero.droidfish.gamelogic; import java.util.ArrayList; +import org.petero.droidfish.PGNOptions; + import junit.framework.TestCase; @@ -485,4 +487,78 @@ public class GameTest extends TestCase { expectedPos = TextIO.readFEN(TextIO.startPosFEN); assertEquals(expectedPos, hist.first); } + + public final void testDuplicateMoves() throws ChessParseError { + PGNOptions options = new PGNOptions(); + options.imp.variations = true; + options.imp.comments = true; + options.imp.nag = true; + + { + Game game = new Game(null, new TimeControlData()); + boolean res = game.readPGN("[Event \"\"]\n[Result \"0-1\"]\n\ne4 0-1", options); + assertEquals(true, res); + assertEquals("e4", GameTreeTest.getVariationsAsString(game.tree)); + + Pair p = game.processString("e4"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("e2e4"), p.second); + game.undoMove(); + assertEquals("e4 e4", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("e4"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("e2e4"), p.second); + game.undoMove(); + assertEquals("e4 e4", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("d4"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("d2d4"), p.second); + game.undoMove(); + assertEquals("d4 e4 e4", GameTreeTest.getVariationsAsString(game.tree)); + } + { + Game game = new Game(null, new TimeControlData()); + game.setPos(TextIO.readFEN("k7/5R2/6R1/2K5/8/8/8/8 w - - 0 1")); + Pair p = game.processString("Rg8"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("g6g8"), p.second); + game.undoMove(); + assertEquals("Rg8#", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("Rg8"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("g6g8"), p.second); + game.undoMove(); + assertEquals("Rg8#", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("Rgg7"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("g6g7"), p.second); + game.undoMove(); + assertEquals("Rgg7 Rg8#", GameTreeTest.getVariationsAsString(game.tree)); + } + { + Game game = new Game(null, new TimeControlData()); + game.setPos(TextIO.readFEN("k7/8/1K6/8/8/8/2Q5/8 w - - 0 1")); + Pair p = game.processString("Qc7"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("c2c7"), p.second); + game.undoMove(); + assertEquals("Qc7", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("Qc7"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("c2c7"), p.second); + game.undoMove(); + assertEquals("Qc7", GameTreeTest.getVariationsAsString(game.tree)); + + p = game.processString("Qc8"); + assertEquals(true, (boolean)p.first); + assertEquals(TextIO.UCIstringToMove("c2c8"), p.second); + game.undoMove(); + assertEquals("Qc8# Qc7", GameTreeTest.getVariationsAsString(game.tree)); + } + } } diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java index b5ef803..faa022c 100644 --- a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java @@ -242,7 +242,7 @@ public class GameTreeTest extends TestCase { assertEquals(0, gt.currentNode.defaultChild); } - private final String getVariationsAsString(GameTree gt) { + final static String getVariationsAsString(GameTree gt) { StringBuilder ret = new StringBuilder(); List vars = gt.variations(); for (int i = 0; i < vars.size(); i++) {