Files
chessParser/test/ParserTest.php
2019-07-06 22:05:35 +02:00

2711 lines
83 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Created by JetBrains PhpStorm.
* User: Alf Magne
* Date: 03.11.12
* Time: 19:50
*
*/
require_once(__DIR__ . "/../autoload.php");
class ParserTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
parent::setUp(); // TODO: Change the autogenerated stub
error_reporting(E_ALL);
}
private function getNumericSquare($square)
{
return isset(Board0x88Config::$mapping[$square]) ? Board0x88Config::$mapping[$square] : null;
}
/**
* @test
*/
/**
* @test
*/
public function shouldCreateParser()
{
// given
$parser = $this->getParser();
// when
$pieces = $parser->getPiecesOfAColor('white');
// then
$this->assertEquals(16, count($pieces));
// when
$pieces = $parser->getPiecesOfAColor('black');
// then
$this->assertEquals(16, count($pieces));
}
/**
* @test
*/
public function shouldFindEnPassantSquare()
{
// given
$fen = '5k2/8/8/3pP3/8/8/8/7K w - d6 0 1';
$parser = $this->getParser($fen);
// then
$this->assertEquals('d6', $parser->getEnPassantSquare());
}
/**
* @test
*/
public function shouldGenerateEnPassantSquareInFen()
{
// given
$parser = new FenParser0x88();
$parser->newGame();
// when
$parser->move("e2e4");
$parser->move("c7c5");
// then
$this->assertEquals("c6", $parser->getEnPassantSquare());
$parser->move("e4e5");
$parser->move("f7f5");
// then
$this->assertEquals("f6", $parser->getEnPassantSquare());
}
/**
* @test
*/
public function shouldSolveProblematicFen1()
{
$fen = '[Event "Bundesliga 2014/15"]
[Site "Solingen GER"]
[Date "2014.10.18"]
[Round "1"]
[White "Jakovenko, Dmitry"]
[Black "Navara, David"]
[Result "1/2-1/2"]
[ECO "D11"]
[WhiteElo "2747"]
[BlackElo "2718"]
[PlyCount "74"]
[EventDate "2014.10.18"]
[EventType "team"]
[WhiteTeam "SK Schwaebisch Hall"]
[BlackTeam "SV Muelheim Nord"]
1. Nf3 d5 2. d4 Nf6 3. c4 c6 4. e3 Bg4 5. h3 Bxf3 6. Qxf3 e6 7. Nc3 Nbd7 8. Bd2
Bb4 9. Bd3 O-O 10. a3 Bxc3 11. Bxc3 Re8 12. O-O e5 13. dxe5 Nxe5 14. Bxe5 Rxe5
15. Rfd1 Qe7 16. cxd5 Rxd5 17. Bc4 Rdd8 18. Rxd8+ Rxd8 19. Rd1 g6 20. Rxd8+
Qxd8 21. g4 h6 22. Qf4 Kg7 23. Kg2 Qe7 24. h4 c5 25. a4 b6 26. b3 Qb7+ 27. f3
Qe7 28. e4 Nh7 29. h5 Nf8 30. Qb8 g5 31. Qc8 Ne6 32. Bxe6 Qxe6 33. Qxe6 fxe6
34. e5 a6 35. Kf2 b5 36. axb5 axb5 37. Ke3 Kf7 1/2-1/2';
$pgnParser = new PgnParser();
$pgnParser->setPgnContent($fen);
$pgnParser->getFirstGame();
$parser = new FenParser0x88();
$parser->setFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
$parser->move("g1f3");
$notation = $parser->getNotation();
$this->assertEquals("Nf3", $notation);
$parser = new FenParser0x88();
$parser->newGame();
$parser->move("Nf3");
$notation = $parser->getNotation();
$this->assertEquals("Nf3", $notation);
}
/**
* @test
*/
public function shouldParseProblematic3()
{
$pgn = '[Event "Moscow Championship (blitz) 2015"]
[Site "Moscow RUS"]
[Date "2015.09.06"]
[Round "19"]
[White "Morozevich, Alexander"]
[Black "Dubov, Daniil"]
[Result "0-1"]
[ECO "B20"]
[WhiteElo "2711"]
[BlackElo "2661"]
[PlyCount "146"]
[EventDate "2015.09.06"]
1. e4 c5 2. g3 g6 3. Bg2 Bg7 4. d3 Nc6 5. f4 e6 6. Nf3 d5 7. O-O Nf6 8. e5 Nd7
9. c4 Nb6 10. Qe2 O-O 11. Nc3 f6 12. exf6 Bxf6 13. Kh1 Bd7 14. Bd2 Nd4 15. Nxd4
cxd4 16. Nd1 dxc4 17. dxc4 Bc6 18. Bxc6 bxc6 19. Nf2 c5 20. Rae1 Qd7 21. b3
Rfe8 22. Nd3 Rac8 23. Qe4 Qc6 24. g4 Qxe4+ 25. Rxe4 Nd7 26. Be1 h5 27. gxh5
gxh5 28. Rf3 Kh7 29. Bf2 Kg6 30. Rxe6 Kf5 31. Rd6 Nb6 32. Rh3 h4 33. Kg2 Be7
34. Rh6 Ke4 35. Re6+ Kf5 36. Re5+ Kg6 37. Kf3 Nd7 38. Re6+ Kf7 39. f5 Bf6 40.
Bxh4 Rxe6 41. fxe6+ Kxe6 42. Bg3 Rf8 43. Rh5 Bg5+ 44. Kg2 Be3 45. Rd5 Re8 46.
Rd6+ Ke7 47. Ra6 Ra8 48. h4 Nf6 49. Nxc5 Nd7 50. Nd3 Rg8 51. Kf3 Rf8+ 52. Ke2
Rg8 53. Bf4 Bxf4 54. Nxf4 Nc5 55. Rxa7+ Kd6 56. b4 Re8+ 57. Kf3 Re3+ 58. Kg4
Ne4 59. Ra6+ Kd7 60. Ra5 Nf2+ 61. Kf5 d3 62. Rd5+ Kc7 63. h5 Rf3 64. Ke5 Re3+
65. Kf5 Rf3 66. h6 Nh3 67. h7 Rxf4+ 68. Kg6 Rh4 69. Kg7 Nf4 70. Rc5+ Kd6 71.
Rc8 Ne6+ 72. Kf6 d2 73. c5+ Kd7 0-1';
$parser = new PgnParser();
$parser->setPgnContent($pgn);
$game = $parser->getFirstGame();
}
/**
* @test
*/
public function shouldParseProblematic2()
{
$game = '[Event "ProofOfConcept"]
[Site "Exploit"]
[Date "2015.??.??"]
[Round "?"]
[White "N.N."]
[Black "N.N."]
[Result "1-0"]
[Annotator ""]
[SetUp "1"]
[FEN "8/7P/8/8/1k15/8/P7/K7 w - - 0 1"]
[PlyCount "1"]
[EventDate "2015.??.??"]
[EventType "game"]
[EventCountry "GER"]
1. a3+';
$pgnParser = new PgnParser();
$pgnParser->setPgnContent($game);
$pgnParser->getFirstGame();
}
/**
* @test
*/
public function shouldFindFullMoves()
{
// given
$fen = '5k2/8/8/3pP3/8/8/8/7K w - d6 0 25';
$parser = $this->getParser($fen);
// then
$this->assertEquals('25', $parser->getFullMoves());
}
/**
* @test
*/
public function shouldFindHalfMoves()
{
// given
$fen = '5k2/8/8/3pP3/8/8/8/7K w - d6 12 25';
$parser = $this->getParser($fen);
// then
$this->assertEquals('12', $parser->getHalfMoves());
}
/**
* @test
*/
public function shouldDetermineIfTwoSquaresAreOnSameRank()
{
// given
$parser = $this->getParser();
$this->assertTrue($parser->isOnSameRank($this->getNumericSquare('a1'), $this->getNumericSquare('h1')));
$this->assertFalse($parser->isOnSameRank($this->getNumericSquare('a1'), $this->getNumericSquare('a2')));
}
/**
* @test
*/
public function shouldDetermineIfTwoSquaresAreOnSameFile()
{
// given
$parser = $this->getParser();
$this->assertFalse($parser->isOnSameFile($this->getNumericSquare('a1'), $this->getNumericSquare('h1')));
$this->assertTrue($parser->isOnSameFile($this->getNumericSquare('a1'), $this->getNumericSquare('a2')));
}
/**
* @test
*/
public function shouldNotBeAbleToCastleInInvalidPositions()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
// when
$parser = $this->getParser($fen);
$legalMoves = $parser->getValidMovesAndResult("white");
$moves = $legalMoves['moves'];
// then
$this->assertEquals(array(), $moves[4]);
}
/**
* @test
*/
public function shouldSetCastleCode()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
// when
$parser = $this->getParser($fen);
// then
$this->assertEquals(8 + 4 + 2 + 1, $parser->getCastleCode());
}
/**
* @test
*/
public function shouldFindCastle()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
// when
$parser = new FenParser0x88($fen);
// then
$this->assertTrue($parser->canCastleKingSide('white') ? true : false, 'Castle options: ' . $parser->getCastleCode());
$this->assertTrue($parser->canCastleKingSide('black') ? true : false, 'Castle options: ' . $parser->getCastleCode());
$this->assertTrue($parser->canCastleQueenSide('white') ? true : false, $parser->getCastle());
$this->assertTrue($parser->canCastleQueenSide('black') ? true : false, $parser->getCastle());
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Kq - 0 1';
// when
$parser = $this->getParser($fen);
// then
$this->assertTrue($parser->canCastleKingSide('white') ? true : false);
$this->assertFalse($parser->canCastleKingSide('black') ? true : false);
$this->assertFalse($parser->canCastleQueenSide('white') ? true : false);
$this->assertTrue($parser->canCastleQueenSide('black') ? true : false);
}
/**
* @test
*/
public function shouldFindColorToMove()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
// when
$parser = $this->getParser($fen);
// then
$this->assertEquals('white', $parser->getColor());
}
private function getValidMovesForSquare($moves, $square)
{
return $moves[Board0x88Config::$mapping[$square]];
}
/**
* @test
*/
public function shouldFindLegalPawnMoves()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
// when
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'a2');
// then
$this->assertEquals(2, count($pawnMoves));
// when
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'a7');
// then
$this->assertEquals(2, count($pawnMoves));
$parser = $this->getParser('6r1/4pk2/8/8/8/5p2/6P1/6K1 b - - 0 1');
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'e7');
// then
$this->assertEquals(2, count($pawnMoves));
$parser = $this->getParser('7k/7p/7P/8/8/8/8/3K2R1 b - - 0 1');
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'h7');
// then
$this->assertEquals(0, count($pawnMoves));
$parser = $this->getParser('r1bq1rk1/ppppbppp/2n2n2/4p3/2B1P3/2N2N1P/PPPP1PP1/R1BQ1RK1 b - - 0 1');
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'h7');
// then
$this->assertEquals(2, count($pawnMoves));
$parser = $this->getParser('rnbq1rk1/pppp1pp1/5n1p/2b1p3/2BPP3/2P2N2/PP3PPP/RNBQ1RK1 b - - 0 6');
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'e5');
// then
$this->assertEquals(1, count($pawnMoves));
$parser = $this->getParser('r1bq3r/ppp3pp/1b6/n2nk3/2B5/B1P2Q2/P2P1PPP/RN4K1 w - - 0 14');
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'd2');
$expectedSquares = array('d3', 'd4');
// then
$this->assertHasSquares($expectedSquares, $pawnMoves);
$parser = $this->getParser('6r1/2p1kp1p/p1Bp1p2/bp6/4P3/5bB1/Pp3P1P/R4RK1 b - - 3 20');
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'b2');
$expectedSquares = array('a1', 'b1');
// then
$this->assertHasSquares($expectedSquares, $pawnMoves);
}
private function assertHasSquares($expectedSquares, $moves)
{
if (is_array($moves)) $moves = implode(",", $moves);
$newMoves = explode(",", $moves);
$moves = array();
foreach ($newMoves as $move) {
if (isset($move) && strlen($move)) {
$moves[] = $move;
}
}
for ($i = 0; $i < count($expectedSquares); $i++) {
$this->assertTrue($this->isSquareInPaths($expectedSquares[$i], $moves), $expectedSquares[$i] . ' is not in path(' . $this->getReadableSquares($moves) . "), expected squares: " . implode(",", $expectedSquares));
}
$this->assertEquals(count($expectedSquares), count($moves));
}
private function isSquareInPaths($square, $paths)
{
for ($i = 0, $count = count($paths); $i < $count; $i++) {
if (isset($paths[$i]) && $paths[$i] == Board0x88Config::$mapping[$square]) {
return true;
}
}
return false;
}
private function getReadableSquares($squares)
{
if (!isset($squares) || !is_array($squares)) return $squares;
$ret = array();
foreach ($squares as $square) {
$ret[] = isset(Board0x88Config::$numberToSquareMapping[$square]) ? $square . ":" . Board0x88Config::$numberToSquareMapping[$square] : 'Wrong:' . $square;
}
return implode(", ", $ret);
}
/**
* @test
*/
public function shouldFindLegalCapturePawnMoves()
{
// given
$fenWithPawnOnF2AndOpponentPieceOnG3 = '6k1/8/8/8/8/6p1/5P2/6K1 w - - 0 1';
$parser = $this->getParser($fenWithPawnOnF2AndOpponentPieceOnG3);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'f2');
// then
$this->assertEquals(3, count($pawnMoves));
$this->assertTrue($this->isSquareInPaths('f3', $pawnMoves));
}
/**
* @test
*/
public function shouldFindLegalBishopMoves()
{
// given
$fenWithBishopOnC2OwnPawnOnB3AndOpponentPieceOnG6 = '6k1/8/6p1/8/8/1P6/2B5/5K2 w - - 0 1';
$parser = $this->getParser($fenWithBishopOnC2OwnPawnOnB3AndOpponentPieceOnG6);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$bishopMoves = $this->getValidMovesForSquare($pLegal, 'c2');
// then
$this->assertTrue($this->isSquareInPaths('b1', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('d1', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('d3', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('e4', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('f5', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('g6', $bishopMoves));
$this->assertFalse($this->isSquareInPaths('h7', $bishopMoves));
# $this->assertEquals(6, $bishopMoves.flatten().length)
}
/**
* @test
*/
public function shouldFindLegalBlackBishopMoves()
{
// given
$fenWithBishopOnC2OpponentPawnOnB3AndOwnPieceOnG6 = '6k1/8/6p1/8/8/1P6/2b5/5K2 w - - 0 1';
$parser = $this->getParser($fenWithBishopOnC2OpponentPawnOnB3AndOwnPieceOnG6);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$bishopMoves = $this->getValidMovesForSquare($pLegal, 'c2');
// then
$this->assertTrue($this->isSquareInPaths('b1', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('b3', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('d1', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('d3', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('e4', $bishopMoves));
$this->assertTrue($this->isSquareInPaths('f5', $bishopMoves));
$this->assertFalse($this->isSquareInPaths('g6', $bishopMoves));
#$this->assertEquals(6, $bishopMoves.flatten().length)
}
/**
* @test
*/
public function shouldFindLegalRookMoves()
{
$fenWithRookOnC2BlackOna2g3WhiteOnC6 = '6k1/8/2P5/8/8/8/p1R3p1/6K1 w - - 0 1';
$parser = $this->getParser($fenWithRookOnC2BlackOna2g3WhiteOnC6);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$rookMoves = $this->getValidMovesForSquare($pLegal, 'c2');
$expectedSquares = array('b2', 'a2', 'd2', 'e2', 'f2', 'g2', 'c1', 'c3', 'c4', 'c5');
// then
$this->assertHasSquares($expectedSquares, $rookMoves);
}
/**
* @test
*/
public function shouldFindLegalBlackRookMoves()
{
$fen = '3p2k1/1p1r1p2/8/3P4/8/8/8/6K1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$rookMoves = $this->getValidMovesForSquare($pLegal, 'd7');
$expectedSquares = array('c7', 'e7', 'd6', 'd5');
// then
$this->assertHasSquares($expectedSquares, $rookMoves);
}
/**
* @test
*/
public function shouldFindLegalKnightSquares()
{
$fen = '6k1/8/8/8/2P1p3/8/3N4/6K1 w - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$knightMoves = $this->getValidMovesForSquare($pLegal, 'd2');
$expectedSquares = array('b1', 'f1', 'b3', 'f3', 'e4');
// then
$this->assertHasSquares($expectedSquares, $knightMoves);
// given
$fen = 'rnb1qrk1/ppp3pp/3b4/3pN1BN/3Pp1n1/8/PPPQ1P1P/R3KB1R w KQ - 0 12';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$knightMoves = $this->getValidMovesForSquare($pLegal, 'e5');
$expectedSquares = array('d7', 'f7', 'g6', 'g4', 'f3', 'd3', 'c6', 'c4');
// then
$this->assertHasSquares($expectedSquares, $knightMoves);
}
/**
* @test
*/
public function shouldFindLegalBlackKnightSquares()
{
$fen = '6k1/8/2P5/5p2/3n4/8/2P5/6K1 w - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$knightMoves = $this->getValidMovesForSquare($pLegal, 'd4');
$expectedSquares = array('c2', 'e2', 'b3', 'f3', 'b5', 'c6', 'e6');
// then
$this->assertHasSquares($expectedSquares, $knightMoves);
}
/**
* @test
*/
public function shouldFindLegalKingMoves()
{
$fen = '5k2/8/8/8/8/8/5P2/6K1 w - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'g1');
$expectedSquares = array('f1', 'g2', 'h1', 'h2');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
$fen = 'Rbkq4/1p6/1BP4p/4p3/4B3/1QPP1P2/6rP/6K1 w - - 0 29';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'g1');
$expectedSquares = array('f1', 'g2', 'h1');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldFindLegalBlackKingMoves()
{
$fen = '8/5k2/5p2/8/8/8/5P2/6K1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'f7');
$expectedSquares = array('e8', 'e7', 'e6', 'f8', 'g8', 'g7', 'g6');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldFindLegalCastleMoves()
{
$fen = '8/5k2/5p2/8/8/8/5P2/R3K2R b KQ - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'e1');
$expectedSquares = array('f1', 'd1', 'e2', 'd2', 'g1', 'c1');
// then
$this->assertHasSquares($expectedSquares, $kingMoves, $kingMoves);
}
/**
* @test
*/
public function shouldFindLegalBlackCastleMoves()
{
$fen = 'r3k2r/8/5p2/8/8/8/5P2/R3K2R b KQk - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'e8');
$expectedSquares = array('d8', 'd7', 'e7', 'f8', 'f7', 'g8');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldFindOpponentsCaptureAndProtectiveMoves()
{
// given
$fen = '7k/4b2p/8/8/8/8/8/5K2 w - - 0 1';
$parser = $this->getParser($fen);
// when
$pLegal = $parser->getCaptureAndProtectiveMoves('black');
$pLegal = explode(",", substr($pLegal, 1, strlen($pLegal) - 2));
$expectedSquares = 'd6,c5,b4,a3,d8,f8,f6,g5,h4,g6,g8,g7,h7';
$expectedSquares = explode(",", $expectedSquares);
// then
$this->assertHasSquares($expectedSquares, $pLegal);
}
/**
* @test
*/
public function shouldFindOpponentsCaptureAndProtectiveMovesContinued()
{
// given
$fen = '6k1/8/8/2b5/8/8/5p2/5K2 w - - 0 1';
$parser = $this->getParser($fen);
// when
$pLegal = $parser->getCaptureAndProtectiveMoves('black');
$expectedSquares = 'e1,g1,b6,a7,b4,a3,d6,e7,f8,d4,e3,f2,f8,f7,g7,h7,h8';
$expectedSquares = explode(",", $expectedSquares);
// then
$this->assertHasSquares($expectedSquares, $pLegal);
}
/**
* @test
*/
public function shouldExcludeInvalidKingMoves()
{
$fen = '6k1/8/8/2b5/8/8/5p2/5K2 w - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'f1');
$expectedSquares = array('e2', 'g2');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldExcludeInvalidBlackKingMoves()
{
$fen = '6k1/5p2/5P2/2B5/8/8/5p2/5K2 b - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'g8');
$expectedSquares = array('h7', 'h8');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldFindQueenMoves()
{
// given
$fen = '6k1/6pp/3P2p1/8/8/3Q1P2/8/1P3K2 w - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$queenMoves = $this->getValidMovesForSquare($pLegal, 'd3');
$expectedSquares = 'c2,d2,d1,e2,d4,d5,c4,b5,a6,e4,f5,g6,c3,b3,a3,e3';
$expectedSquares = explode(',', $expectedSquares);
// then
$this->assertHasSquares($expectedSquares, $queenMoves);
}
/**
* @test
*/
public function shouldExcludeInvalidKingCastleMoves()
{
$fen = '1k4r1/8/3r4/8/8/1b6/4P3/4K2R w K - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'e1');
$expectedSquares = array('f1', 'f2');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function shouldLegalEnPassantMoves()
{
$fen = '7k/4b2p/8/3pP3/8/8/8/5K2 w - d6 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'e5');
$expectedSquares = array('d6', 'e6');
// then
$this->assertHasSquares($expectedSquares, $pawnMoves);
}
/**
* @test
*/
public function shouldFindSlidingPiecesInPathOfKing()
{
// given
$fen = '6k1/5pp1/8/8/8/8/BB6/5KR1 w - - 0 1';
$parser = $this->getParser($fen);
// when
$pieces = $parser->getSlidingPiecesAttackingKing('white');
// then
$this->assertEquals(2, count($pieces));
$this->assertEquals(Board0x88Config::$mapping['g1'], $pieces[1]['s']);
$this->assertEquals(Board0x88Config::$mapping['a2'], $pieces[0]['s']);
//given
$fen = '6k1/Q5n1/4p3/8/8/8/B7/5KR1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$pieces = $parser->getSlidingPiecesAttackingKing('white');
// then
$this->assertEquals(2, count($pieces));
// given
$fen = 'R5k1/8/8/8/8/8/8/5K2 b - - 0 1';
$parser = $this->getParser($fen);
// when
$pieces = $parser->getSlidingPiecesAttackingKing('white');
// then
$this->assertEquals(1, count($pieces));
$this->assertEquals(1, $pieces[0]['p']);
}
/**
* @test
*/
public function shouldFindCheckPositions()
{
// given
$fen = '6k1/6pp/5p2/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fen);
$moves = $parser->getCaptureAndProtectiveMoves('white');
$this->assertEquals(1, $parser->getCountChecks('black', $moves));
}
/**
* @test
*/
public function shouldFindDoubleChecks()
{
// given
$fen = '3R2k1/6pp/5p2/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fen);
$moves = $parser->getCaptureAndProtectiveMoves('white');
$this->assertEquals(2, $parser->getCountChecks('black', $moves));
}
/**
* @test
*/
public function OnlyKingShouldbeAbleToMoveOnDoubleCheck()
{
// given
$fen = '3R2k1/6p1/5p1p/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$kingMoves = $this->getValidMovesForSquare($pLegal, 'g8');
$expectedSquares = array('h7');
// then
$this->assertHasSquares($expectedSquares, $kingMoves);
}
/**
* @test
*/
public function AttackingMovesShouldIncludeSquaresAfterKing()
{
// given
$fen = '3R2k1/6p1/5p1p/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$moves = $parser->getCaptureAndProtectiveMoves('white');
if (!is_array($moves)) $moves = explode(",", $moves);
$this->assertTrue(array_search(Board0x88Config::$mapping['h8'], $moves) >= 0);
}
/**
* @test
*/
public function shouldbeAbleToFindDistanceBetweenTwoSquares()
{
// given
$parser = $this->getParser();
// when
$square2 = $this->getNumericSquare('e1');
$square1 = $this->getNumericSquare('f3');
// then
$this->assertEquals(2, $parser->getDistance($square1, $square2));
// when
$square2 = $this->getNumericSquare('h5');
$square1 = $this->getNumericSquare('b1');
// then
$this->assertEquals(6, $parser->getDistance($square1, $square2));
// when
$square2 = $this->getNumericSquare('a1');
$square1 = $this->getNumericSquare('b2');
$this->assertEquals(1, $parser->getDistance($square1, $square2));
// when
$square2 = $this->getNumericSquare('b6');
$square1 = $this->getNumericSquare('e1');
// then
$this->assertEquals(5, $parser->getDistance($square1, $square2), 'a6 vs e1');
// when
$square2 = $this->getNumericSquare('f3');
$square1 = $this->getNumericSquare('e1');
// then
$this->assertEquals(2, $parser->getDistance($square1, $square2), 'f3 vs e1');
// when
$square2 = $this->getNumericSquare('a1');
$square1 = $this->getNumericSquare('h8');
// then
$this->assertEquals(7, $parser->getDistance($square1, $square2));
// when
$square2 = $this->getNumericSquare('h1');
$square1 = $this->getNumericSquare('a8');
// then
$this->assertEquals(7, $parser->getDistance($square1, $square2));
$square1 = $this->getNumericSquare('a1');
for ($i = 2; $i <= 8; $i++) {
$square2 = $this->getNumericSquare('a' . $i);
$this->assertEquals($i - 1, $parser->getDistance($square1, $square2), 'a' . $i);
}
$square1 = $this->getNumericSquare('a1');
for ($i = 2; $i <= 8; $i++) {
$square2 = $this->getNumericSquare('b' . $i);
$this->assertEquals($i - 1, $parser->getDistance($square1, $square2), 'b' . $i);
}
$square1 = $this->getNumericSquare('a8');
for ($i = 7; $i >= 1; $i--) {
$square2 = $this->getNumericSquare('b' . $i);
$this->assertEquals(8 - $i, $parser->getDistance($square1, $square2), 'b' . $i);
}
}
private function assertSquareIsPinnedBy($square, $pinnedBy, $pinned)
{
$this->assertEquals($this->getNumericSquare($pinnedBy), $pinned[$this->getNumericSquare($square)]['by']);
}
/**
* @test
*/
public function shouldFindPinningPieces()
{
// given
$fen = '6k1/Q5n1/4p3/8/8/1B6/B7/5KR1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$pinned = $parser->getPinned('black');
// then
$this->assertSquareIsPinnedBy('e6', 'b3', $pinned);
$this->assertSquareIsPinnedBy('g7', 'g1', $pinned);
}
/**
* @test
*/
public function shouldGetValidMovesInBoardCoordinates()
{
// given
$parser = new FenParser0x88('6k1/6p1/4n3/8/8/8/B7/6K1 b - - 0 1');
// when
$validBlackMoves = $parser->getValidMovesBoardCoordinates("black");
$validKingMoves = $validBlackMoves["g8"];
// then
$expectedSquares = array("f7", "h7", "f8", "h8");
$this->assertEquals(count($expectedSquares), count($validKingMoves));
foreach ($validKingMoves as $move) {
$this->assertTrue(in_array($move, $expectedSquares));
}
}
/**
* @test
*/
public function knightShouldNotbeableToMoveWhenPinned()
{
// given
$fen = '6k1/6p1/4n3/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fen);
// when
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$knightMoves = $this->getValidMovesForSquare($pLegal, 'e6');
$expectedSquares = array();
// then
$this->assertHasSquares($expectedSquares, $knightMoves);
}
/**
* @test
*/
public function PawnShouldNotbeAbleToMoveWhenPinnedByRook()
{
// given
$fenPawnOnG2KingOnH2BlackRookOnA2 = '5k2/8/8/8/8/8/r5PK/8 w - - 0 1';
$parser = $this->getParser($fenPawnOnG2KingOnH2BlackRookOnA2);
$pinned = $parser->getPinned('white');
$pinned2 = $parser->getPinnedBoardCoordinates('white');
// then
$this->assertSquareIsPinnedBy('g2', 'a2', $pinned);
// when
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'g2');
$expectedSquares = array();
// then
$this->assertHasSquares($expectedSquares, $pawnMoves);
// when
$fen = '5kr1/8/8/8/8/5p2/6P1/6K1 w - - 0 1';
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('white');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'g2');
$expectedSquares = array('g3', 'g4');
// then
$this->assertHasSquares($expectedSquares, $pawnMoves);
// when
$fen = '6r1/R3pk2/8/8/8/5p2/6P1/6K1 b - - 0 1';
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'e7');
$expectedSquares = array();
// then
$this->assertEquals(0, count($pawnMoves));
$this->assertHasSquares($expectedSquares, $pawnMoves);
// when
$fen = '4k1r1/4p3/3P4/8/8/5p2/6P1/4R1K1 b - - 0 1';
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
$pawnMoves = $this->getValidMovesForSquare($pLegal, 'e7');
$expectedSquares = array('e6', 'e5');
// then
$this->assertEquals(2, count($pawnMoves));
$this->assertHasSquares($expectedSquares, $pawnMoves);
}
/**
* @test
*/
public function PinnedBishopSlidingPiecesShouldOnlybeAbleToBetweenPinningAndKing()
{
// given
$fenBishopA2AndE6KingOng8 = '6k1/8/4b3/8/8/8/B7/6K1 b - - 0 1';
$parser = $this->getParser($fenBishopA2AndE6KingOng8);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
// when
$bishopMoves = $this->getValidMovesForSquare($pLegal, 'e6');
$expectedSquares = array('d5', 'c4', 'b3', 'a2', 'f7');
// then
$this->assertHasSquares($expectedSquares, $bishopMoves);
}
/**
* @test
*/
public function PinnedRookSlidingPiecesShouldOnlybeAbleToBetweenPinningAndKing()
{
// given
$fenRookOnE5AndE2KingOnE8 = '4k3/8/8/4r3/8/8/4R3/6K1 b - - 0 1';
$parser = $this->getParser($fenRookOnE5AndE2KingOnE8);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
// when
$rookMoves = $this->getValidMovesForSquare($pLegal, 'e5');
$expectedSquares = array('e4', 'e3', 'e2', 'e6', 'e7');
// then
$this->assertHasSquares($expectedSquares, $rookMoves);
}
/**
* @test
*/
public function shouldFindPawnCheckMoves()
{
// given
$fenPawnOnE6CheckingKingOnF7 = '8/rn3k2/1b2P3/8/8/8/1QN5/2BRK3 b - - 0 1';
$parser = $this->getParser($fenPawnOnE6CheckingKingOnF7);
// when
$checks = $parser->getValidSquaresOnCheck('black');
// then
$this->assertEquals(1, count($checks));
$this->assertEquals($this->getNumericSquare('e6'), $checks[0]);
}
/**
* @test
*/
public function shouldFindBlackPawnCheckMoves()
{
// given
$fenPawnOng2CheckingKingOng1 = '5k2/8/8/8/8/8/5p2/6K1 w - - 0 1';
$parser = new FenParser0x88($fenPawnOng2CheckingKingOng1);
// when
$checks = $parser->getValidSquaresOnCheck('white');
// then
$this->assertEquals(1, count($checks));
$this->assertEquals($this->getNumericSquare('f2'), $checks[0]);
}
/**
* @test
*/
public function shouldFindValidSquaresWhenCheckedByKnight()
{
// given
$fenKnightOnF6CheckingKingOnG8 = '5rk1/5pp1/5N2/8/8/8/8/5KR1 b - - 0 1';
$parser = $this->getParser($fenKnightOnF6CheckingKingOnG8);
// when
$checks = $parser->getValidSquaresOnCheck('black');
// then
$this->assertEquals(1, count($checks));
$this->assertEquals($this->getNumericSquare('f6'), $checks[0]);
}
/**
* @test
*/
public function shouldFindKingSquare()
{
// King on g1(white) and g8(black)
$parser = new FenParser0x88('6k1/6pp/8/8/8/1B6/8/6K1 b - - 0 1');
// when
$blackKing = $parser->getBlackKingSquare();
// then
$this->assertEquals("g8", $blackKing);
// when
$whiteKing = $parser->getWhiteKingSquare();
// then
$this->assertEquals("g1", $whiteKing);
}
/**
* @test
*/
public function shouldFindValidSquaresWhenCheckedByBishop()
{
// given
$fenBishopOnB3CheckingKingOnG7 = '6k1/6pp/8/8/8/1B6/8/6K1 b - - 0 1';
$parser = new FenParser0x88($fenBishopOnB3CheckingKingOnG7);
$blackKing = $parser->getKing('black');
$this->assertEquals(Board0x88Config::$mapping['g8'], $blackKing['s']);
$sq = Board0x88Config::$mapping['b3'];
$bishop = $parser->getPieceOnSquare($sq);
$bishopCheckPaths = $parser->getBishopCheckPath($bishop, $blackKing);
$this->assertTrue(($blackKing['s'] - $bishop['s']) % 17 === 0);
$this->assertEquals(5, $parser->getDistance($bishop['s'], $blackKing['s']));
$this->assertEquals(17, ($blackKing['s'] - $bishop['s']) / $parser->getDistance($bishop['s'], $blackKing['s']));
$this->assertEquals(5, count($bishopCheckPaths), 'bishop:' . json_encode($bishop, true) . ", king: " . json_encode($blackKing, true));
// when
$checks = $parser->getValidSquaresOnCheck('black');
$expectedSquares = array('b3', 'c4', 'd5', 'e6', 'f7');
// then
$this->assertEquals(5, count($checks), 'invalid length for ' . isset($checks) && is_array($checks) ? implode(',', $checks) : $checks);
$this->assertHasSquares($expectedSquares, $checks);
}
/**
* @test
*/
public function shouldFindValidSquaresWhenCheckedByRook()
{
// given
$fenRookOnF3CheckingKingOnF8 = '5kb1/4p3/3p4/2p5/1p6/p4R2/8/7K b - - 0 1';
$parser = $this->getParser($fenRookOnF3CheckingKingOnF8);
// when
$checks = $parser->getValidSquaresOnCheck('black');
$expectedSquares = array('f3', 'f4', 'f5', 'f6', 'f7');
// then
$this->assertHasSquares($expectedSquares, $checks);
}
/**
* @test
*/
public function shouldFindValidSquaresWhenCheckedByRookOnSameRank()
{
// given
$fenRookOnA8CheckingKingOnF8 = 'R4kb1/4p3/3p4/2p5/1p6/p7/8/7K b - - 0 1';
$parser = $this->getParser($fenRookOnA8CheckingKingOnF8);
// when
$checks = $parser->getValidSquaresOnCheck('black');
$expectedSquares = array('a8', 'b8', 'c8', 'd8', 'e8');
// then
$this->assertHasSquares($expectedSquares, $checks);
}
/**
* @test
*/
public function shouldFindValidSquaresWhenCheckedByQueen()
{
// given
$fenQueenOnF3CheckingKingOnF8 = '5kb1/4p3/3p4/2p5/1p6/p4Q2/8/7K b - - 0 1';
$parser = $this->getParser($fenQueenOnF3CheckingKingOnF8);
// when
$checks = $parser->getValidSquaresOnCheck('black');
$expectedSquares = array('f3', 'f4', 'f5', 'f6', 'f7');
// then
$this->assertHasSquares($expectedSquares, $checks);
// given
$fenQueenOnB3CheckingKingOnG7 = '6k1/6pp/8/8/8/1Q6/8/6K1 b - - 0 1';
$parser = $this->getParser($fenQueenOnB3CheckingKingOnG7);
// when
$checks = $parser->getValidSquaresOnCheck('black');
$expectedSquares = array('b3', 'c4', 'd5', 'e6', 'f7');
// then
$this->assertHasSquares($expectedSquares, $checks);
}
public function PieceShouldOnlybeableToMoveToValidSquaresOnCheck()
{
// given
// Queen on f3 checkign king on f8
// Bishop on g8 shouldonly beAbleto$move to f7
$fen = '5kb1/4p3/3p4/2p5/1p6/p4Q2/8/7K b - - 0 1';
$parser = $this->getParser($fen);
$validMoves = $parser->getValidMovesAndResult('black');
$pLegal = $validMoves['moves'];
// when
$moves = $this->getValidMovesForSquare($pLegal, 'g8');
$expectedSquares = array('f7');
// then
$this->assertHasSquares($expectedSquares, $moves);
}
/**
* @test
*/
public function shouldFindCheckMate()
{
// given
$notCheckMateFens = array('4k3/5p2/2B3p1/8/8/8/4R3/5K2 b - - 0 1',
'5k2/5p1p/6p1/2B5/8/8/4R3/5K2 b - - 0 1');
for ($i = 0; $i < count($notCheckMateFens); $i++) {
// when
$parser = $this->getParser($notCheckMateFens[$i]);
$moves = $parser->getValidMovesAndResult('black');
// then
$this->assertNotEquals(1, $moves['result']);
}
// given
$checkMateFens = array('5kr1/5ppp/8/2B5/8/8/4R3/5K2 b - - 0 1',
'3qkbn1/2rpp3/8/5n1Q/8/8/8/5K2 b - - 0 1',
'3qkr2/3ppp2/3N4/8/8/8/8/4R1K1 b - - 0 1',
'6rk/5Npp/8/8/8/1P6/2PP4/3K4 b - - 0 1',
'6pk/6p1/8/8/8/8/8/2K4R b - - 0 1',
'4b1pk/5rpp/6N1/8/8/8/8/2K4R b - - 0 1'
);
for ($i = 0; $i < count($checkMateFens); $i++) {
// when
$parser = $this->getParser($checkMateFens[$i]);
$moves = $parser->getValidMovesAndResult();
$color = $parser->getColor();
$protectiveMoves = $parser->getCaptureAndProtectiveMoves($color);
// then
$this->assertEquals(1, $moves['result'], 'Position(' . $i . '): ' . $checkMateFens[$i] . ', moves: ' . json_encode($protectiveMoves, true));
}
}
/**
* @test
*/
public function shouldFindStalemate()
{
// given
$stalematePos = array('7k/7p/7P/8/8/8/8/3K2R1 b - - 0 1',
'1R4bk/6pp/7P/8/8/8/1B6/5K2 b - - 0 1');
// when
for ($i = 0; $i < count($stalematePos); $i++) {
$parser = $this->getParser($stalematePos[$i]);
$moves = $parser->getValidMovesAndResult();
$this->assertEquals(.5, $moves['result'], $stalematePos[$i]);
}
}
/**
* @test
*/
public function shouldGetFenForAMove()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e2', 'to' => 'e4'));
$newFen = $parser->getFen();
$expectedFen = 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1';
// then
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function ShouldGetFenForAPromotionMove()
{
// given
$fen = '7k/2P3p1/7p/8/8/4p3/8/7K w - - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'c7', 'to' => 'c8', 'promoteTo' => 'q'));
$newFen = $parser->getFen();
$expectedFen = '2Q4k/6p1/7p/8/8/4p3/8/7K b - - 0 1';
// then
$this->assertEquals($expectedFen, $newFen);
// given
$fen = '7k/8/8/8/8/8/2p5/7K b - - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'c2', 'to' => 'c1', 'promoteTo' => 'q'));
$newFen = $parser->getFen(array('from' => 'c2', 'to' => 'c1', 'promoteTo' => 'q'));
$expectedFen = '7k/8/8/8/8/8/8/2q4K w - - 0 2';
// then
$this->assertEquals($expectedFen, $newFen);
}
public function ShoulludoetFenForEnPassantMoves()
{
// given
$fen = 'rnbqkbnr/1ppppppp/p7/4P3/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'd7', 'to' => 'd5'));
$newFen = $parser->getFen();
$expectedFen = 'rnbqkbnr/1pp1pppp/p7/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 3';
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldbeAbleToGetFenForCastleMoves()
{
// given
$fen = 'r3k2r/4pppp/8/8/8/8/4PPPP/R3K2R w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e1', 'to' => 'g1'));
$newFen = $parser->getFen();
$expectedFen = 'r3k2r/4pppp/8/8/8/8/4PPPP/R4RK1 b kq - 1 1';
$this->assertEquals($expectedFen, $newFen);
// given
$fen = '3k4/4p3/5p2/6p1/7p/8/8/R3K3 w Q - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e1', 'to' => 'c1'));
$newFen = $parser->getFen(array('from' => 'e1', 'to' => 'c1'));
$expectedFen = '3k4/4p3/5p2/6p1/7p/8/8/2KR4 b - - 1 1';
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldbeAbleToGetFenForBlackCastleMoves()
{
// given
$fen = 'r3k2r/6pp/8/8/8/8/8/6K1 b kq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e8', 'to' => 'c8'));
$newFen = $parser->getFen(array('from' => 'e8', 'to' => 'c8'));
$expectedFen = '2kr3r/6pp/8/8/8/8/8/6K1 w - - 1 2';
$this->assertEquals($expectedFen, $newFen);
// given
$fen = 'r3k2r/6pp/8/8/8/8/8/6K1 b kq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e8', 'to' => 'g8'));
$newFen = $parser->getFen(array('from' => 'e8', 'to' => 'g8'));
$expectedFen = 'r4rk1/6pp/8/8/8/8/8/6K1 w - - 1 2';
$this->assertEquals($expectedFen, $newFen);
}
public function ShoulludoetFenForCaptureMoves()
{
$fen = '5rk1/4Qppp/8/8/8/1B6/8/5RK1 w - - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'b3', 'to' => 'f7'));
$newFen = $parser->getFen();
$expectedFen = '5rk1/4QBpp/8/8/8/8/8/5RK1 b - - 0 1';
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldIncrementFullMoves()
{
// given
$fen = 'r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 0 3';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'g8', 'to' => 'f6'));
$newFen = $parser->getFen();
$expectedFen = 'r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 1 4';
// then
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldIncrementHalfMoves()
{
// given
$fen = 'r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/2N2N2/PPPP1PPP/R1BQK2R b KQkq - 0 4';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'f8', 'to' => 'c5'));
$newFen = $parser->getFen();
$expectedFen = 'r1bqk2r/pppp1ppp/2n2n2/2b1p3/2B1P3/2N2N2/PPPP1PPP/R1BQK2R w KQkq - 1 5';
// then
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldExcludeCastleSquaresWhenMovingKing()
{
$fen = '5k2/5p2/6p1/7p/8/8/8/R3K2R w KQ - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e1', 'to' => 'f1'));
$newFen = $parser->getFen();
$expectedFen = '5k2/5p2/6p1/7p/8/8/8/R4K1R b - - 1 1';
// then
$this->assertEquals($expectedFen, $newFen);
// given
$fen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R b KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e8', 'to' => 'e7'));
$newFen = $parser->getFen();
$expectedFen = 'r6r/1p2kp2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R w KQ - 1 2';
// then
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldExcludeCastleWhenRookIsMoving()
{
// given
$fen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'a1', 'to' => 'b1'));
$newFen = $parser->getFen();
$expectedFen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/1R2K2R b Kkq - 1 1';
// then
$this->assertEquals($expectedFen, $newFen);
// given
$fen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'h1', 'to' => 'g1'));
$newFen = $parser->getFen();
$expectedFen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K1R1 b Qkq - 1 1';
// then
$this->assertEquals($expectedFen, $newFen);
// given
$fen = 'r3k2r/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R b KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'h8', 'to' => 'g8'));
$newFen = $parser->getFen();
$expectedFen = 'r3k1r1/1p3p2/p1p1p1p1/3p4/P6P/1P4P1/2P2P2/R3K2R w KQq - 1 2';
// then
$this->assertEquals($expectedFen, $newFen);
}
/**
* @test
*/
public function shouldFindMovedAndRemovedPiecesForAMove()
{
// given
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$parser->move(array('from' => 'e2', 'to' => 'e4'));
$moves = $parser->getPiecesInvolvedInLastMove();
// then
$this->assertEquals(1, count($moves));
$this->assertEquals('e2', $moves[0]['from']);
$this->assertEquals('e4', $moves[0]['to']);
// given
$fen = '7k/8/8/3Pp3/8/8/8/7K w - e6 0 1';
$parser = $this->getParser($fen);
// when
$moves = $parser->getPiecesInvolvedInMove(array('from' => 'd5', 'to' => 'e6'));
// then
$this->assertEquals(2, count($moves));
$this->assertEquals('d5', $moves[0]['from']);
$this->assertEquals('e6', $moves[0]['to']);
$this->assertEquals('e5', $moves[1]['capture']);
// given
$fen = '8/P6k/8/8/8/8/8/5K2 w - - 0 1';
$parser = $this->getParser($fen);
// when
$moves = $parser->getPiecesInvolvedInMove(array('from' => 'a7', 'to' => 'a8', 'promoteTo' => 'q'));
$this->assertEquals(2, count($moves));
}
/**
* @test
*/
public function shouldFindMovedPiecesForACastleMove()
{
// given
$fen = 'r3k2r/5ppp/8/8/8/1B6/5PPP/R3K2R w KQkq - 0 1';
$parser = $this->getParser($fen);
// when
$moves = $parser->getPiecesInvolvedInMove(array('from' => 'e1', 'to' => 'g1'));
// then
$this->assertEquals(2, count($moves));
$this->assertEquals('e1', $moves[0]['from']);
$this->assertEquals('g1', $moves[0]['to']);
$this->assertEquals('h1', $moves[1]['from'], json_encode($moves, true));
$this->assertEquals('f1', $moves[1]['to']);
}
/**
* @test
*/
public function shouldFindCorrectNotationForAMove()
{
$fens = array(
'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
'rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 1',
'7k/2P2ppp/8/8/8/8/8/7K w - - 0 1',
'4r2k/6p1/7p/8/8/8/8/4R2K w - - 0 1',
'6k1/1R3p2/6p1/7p/8/8/5R2/6K1 w - - 0 1',
'6k1/1R3p2/6p1/7p/8/8/5R2/6K1 w - - 0 1',
'6k1/5p2/1N4p1/3p3p/1N6/8/5R2/6K1 w - - 0 1',
'2k5/8/8/8/8/8/8/R3K2R w KQ - 0 1',
'3k4/4p3/5p2/6p1/7p/8/8/R3K3 w Q - 0 1'
);
$moves = array('e2e4', 'e5d6', array('from' => 'c7', 'to' => 'c8', 'promoteTo' => 'q'), 'e1e8', 'f2f7', 'b7f7', 'b6d5', 'e1g1', 'e1c1');
$expected = array('e4', 'exd6', 'c8=Q#', 'Rxe8+', 'Rfxf7', 'Rbxf7', 'N6xd5', 'O-O', 'O-O-O+');
for ($i = 0; $i < count($fens); $i++) {
$parser = $this->getParser($fens[$i]);
if (!is_array($moves[$i])) {
$moves[$i] = array(
'from' => substr($moves[$i], 0, 2),
'to' => substr($moves[$i], 2, 2)
);
}
$parser->move($moves[$i]);
$notation = $parser->getNotation();
$this->assertEquals($expected[$i], $expected[$i], $notation);
}
}
/**
* @test
*/
public function shouldFindCorrectLongNotationForAMove()
{
$fens = array(
'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
'rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 1',
'7k/2P2ppp/8/8/8/8/8/7K w - - 0 1',
'4r2k/6p1/7p/8/8/8/8/4R2K w - - 0 1',
'6k1/1R3p2/6p1/7p/8/8/5R2/6K1 w - - 0 1',
'6k1/1R3p2/6p1/7p/8/8/5R2/6K1 w - - 0 1',
'6k1/5p2/1N4p1/3p3p/1N6/8/5R2/6K1 w - - 0 1',
'2k5/8/8/8/8/8/8/R3K2R w KQ - 0 1',
'3k4/4p3/5p2/6p1/7p/8/8/R3K3 w Q - 0 1'
);
$moves = array('e2e4', 'e5d6', array('from' => 'c7', 'to' => 'c8', 'promoteTo' => 'q'), 'e1e8', 'f2f7', 'b7f7', 'b6d5', 'e1g1', 'e1c1');
$expected = array('e2-e4', 'e5xd6', 'c7-c8=Q#', 'Re1xe8+', 'Rf2xf7', 'Rb7xf7', 'Nb6xd5', 'O-O', 'O-O-O+');
for ($i = 0; $i < count($fens); $i++) {
$parser = $this->getParser($fens[$i]);
if (!is_array($moves[$i])) {
$moves[$i] = array(
'from' => substr($moves[$i], 0, 2),
'to' => substr($moves[$i], 2, 2)
);
}
$parser->move($moves[$i]);
$notation = $parser->getLongNotation();
$this->assertEquals($expected[$i], $expected[$i], $notation);
}
}
/**
* @test
*/
public function shouldbeableToMakeSeveralMovesAndThenGetFen()
{
// given
$parser = $this->getParser();
$parser->move(array('from' => 'e2', 'to' => 'e4'));
$parser->move(array('from' => 'e7', 'to' => 'e5'));
$parser->move(array('from' => 'g1', 'to' => 'f3'));
$parser->move(array('from' => 'g8', 'to' => 'f6'));
$parser->move(array('from' => 'f1', 'to' => 'c4'));
$parser->move(array('from' => 'f8', 'to' => 'c5'));
$parser->move(array('from' => 'e1', 'to' => 'g1'));
$parser->move(array('from' => 'e8', 'to' => 'g8'));
$parser->move(array('from' => 'c2', 'to' => 'c3'));
$parser->move(array('from' => 'h7', 'to' => 'h6'));
$parser->move(array('from' => 'd2', 'to' => 'd4'));
$parser->move(array('from' => 'e5', 'to' => 'd4'));
$parser->move(array('from' => 'c3', 'to' => 'd4'));
$parser->move(array('from' => 'c5', 'to' => 'b4'));
$parser->move(array('from' => 'a2', 'to' => 'a3'));
$parser->move(array('from' => 'b4', 'to' => 'a5'));
$parser->move(array('from' => 'b2', 'to' => 'b4'));
$parser->move(array('from' => 'a5', 'to' => 'b6'));
$parser->move(array('from' => 'f1', 'to' => 'e1'));
$expectedFen = 'rnbq1rk1/pppp1pp1/1b3n1p/8/1PBPP3/P4N2/5PPP/RNBQR1K1 b - - 2 10';
$fen = $parser->getFen();
// then
$this->assertEquals($expectedFen, $fen);
// given
$parser = $this->getParser('3r2k1/pp1r4/1b3Q1P/5B2/8/8/P2p1PK1/8 b - - 3 41');
$parser->move(array('from' => 'd2', 'to' => 'd1', 'promoteTo' => 'q'));
$expectedFen = '3r2k1/pp1r4/1b3Q1P/5B2/8/8/P4PK1/3q4 w - - 0 42';
$fen = $parser->getFen();
$this->assertEquals($expectedFen, $fen);
}
/**
* @test
*/
public function shouldbeableToMakePromotionMove()
{
// given
$fen = '6k1/8/8/8/8/8/1p5P/7K b - - 0 1';
$parser = $this->getParser($fen);
// when
$expectedFen = '6k1/8/8/8/8/8/7P/1q5K w - - 0 2';
$parser->move(array('from' => 'b2', 'to' => 'b1', 'promoteTo' => 'q'));
// then
$this->assertEquals($expectedFen, $parser->getFen());
}
/**
* @test
*/
public function shouldbeableToFindFromAndToFromNotation()
{
// given
$parser = $this->getParser();
$moves = array('e2e4', 'e7e5', 'g1f3', 'g8f6', 'f1c4', 'f8c5', 'e1g1', 'e8g8', 'c2c3', 'h7h6', 'd2d4', 'e5d4', 'c3d4', 'c5b4');
$notations = array('e4', 'e5', 'Nf3', 'Nf6', 'Bc4', 'Bc5', 'O-O', 'O-O', 'c3', 'h6', 'd4', 'exd4', 'cxd4', 'Bb4');
for ($i = 0; $i < count($moves); $i++) {
$move = $parser->getFromAndToByNotation($notations[$i]);
// Then
$this->assertEquals(substr($moves[$i], 0, 2), $move['from'], $notations[$i]);
$this->assertEquals(substr($moves[$i], 2, 2), $move['to'], $notations[$i]);
$parser->makeMove(array('from' => substr($moves[$i], 0, 2), 'to' => substr($moves[$i], 2, 2)));
}
// Given
$parser = $this->getParser('6k1/R4p2/6p1/7p/8/8/B4R2/7K w - - 0 1');
// when
$move = $parser->getFromAndToByNotation('Rfxf7');
// then
$this->assertEquals('f2', $move['from']);
$this->assertEquals('f7', $move['to']);
// Given
$parser = $this->getParser('4nkn1/R4pn1/6p1/7p/8/8/B4R2/7K w - - 0 1');
// when
$move = $parser->getFromAndToByNotation('Raxf7#');
// then
$this->assertEquals('a7', $move['from']);
$this->assertEquals('f7', $move['to']);
// Given
$parser = $this->getParser('Rbkq4/1p6/1BP4p/4p3/4B3/1QPP1P2/6rP/6K1 w - - 0 29');
// when
$move = $parser->getFromAndToByNotation('Kxg2');
// then
$this->assertEquals('g1', $move['from']);
$this->assertEquals('g2', $move['to']);
// Given
$parser = $this->getParser('r1bqkb1r/ppp3pp/2n5/3nppN1/2B5/2NP4/PPP2PPP/R1BQ1RK1 b kq - 1 8');
// when
$move = $parser->getFromAndToByNotation('Nce7');
// then
$this->assertEquals('c6', $move['from']);
$this->assertEquals('e7', $move['to']);
// Given
$parser = $this->getParser('3r2k1/pp1r4/1b3Q1P/5B2/8/8/P2p1PK1/8 b - - 3 41');
// when
$move = $parser->getFromAndToByNotation('d1=Q');
// then
$this->assertEquals('d2', $move['from']);
$this->assertEquals('d1', $move['to']);
$this->assertEquals('q', $move['promoteTo']);
// Given
$parser = $this->getParser('r1bqk1nr/ppp2ppp/1b1p2n1/3PP1N1/2B5/8/P4PPP/RNBQ1RK1 b kq - 2 11');
// when
$move = $parser->getFromAndToByNotation('N8e7');
// then
$this->assertEquals('g8', $move['from']);
$this->assertEquals('e7', $move['to']);
// Given
$parser = $this->getParser('r1bq3r/ppp3pp/1b6/n2nk3/2B5/B1P2Q2/P2P1PPP/RN4K1 w - - 0 14');
// when
$move = $parser->getFromAndToByNotation('d4+');
// then
$this->assertEquals('d2', $move['from']);
$this->assertEquals('d4', $move['to']);
}
/**
* @test
*/
public function shouldgetpiecetypebynotation()
{
$parser = $this->getParser();
$notations = array('Nf3', 'Nxf6', 'Rxf8=Q', 'Nfxf8', 'O-O', 'exe5', 'Kxg2', 'Nxd6', 'd1=Q');
$expected = 'NNRNKPKNP';
for ($i = 0; $i < count($notations); $i++) {
$expectedValue = substr($expected, $i, 1);
if ($expectedValue === 'P') {
$expectedValue = '';
}
$this->assertEquals($expectedValue, Board0x88Config::$notationMapping[$parser->getPieceTypeByNotation($notations[$i])]);
}
}
/**
* @test
*/
public function shouldFindFromRankByNotation()
{
// given
$parser = $this->getParser();
// when
$notations = array('R7e4', 'N5xf5', 'Ne5', 'N8e7');
$expected = array(6, 4, null, 7);
for ($i = 0; $i < count($notations); $i++) {
if ($expected[$i] !== null) {
$expectedValue = $expected[$i] * 16;
} else {
$expectedValue = null;
}
$this->assertEquals($expectedValue, $parser->getFromRankByNotation($notations[$i]));
}
}
/**
* @test
*/
public function shouldFindFromFileByNotation()
{
// given
$parser = $this->getParser();
// when
$notations = array('Ree4', 'Naxf5', 'Ne5', 'exd8=Q', 'axb5', 'Nce7', 'bxa1');
$expected = array(4, 0, null, 4, 0, 2, 1);
for ($i = 0; $i < count($notations); $i++) {
$this->assertEquals($expected[$i], $parser->getFromFileByNotation($notations[$i]), $notations[$i]);
}
}
/**
* @test
*/
public function shouldFindFromToSquareByNotation()
{
// given
$parser = $this->getParser();
// when
$notations = array('Ree4', 'Naxf5', 'Ne5', 'exd8=Q');
$expected = array('e4', 'f5', 'e5', 'd8');
for ($i = 0; $i < count($notations); $i++) {
$expectedValue = Board0x88Config::$mapping[$expected[$i]];
$this->assertEquals($expectedValue, $parser->getToSquareByNotation($notations[$i]), $notations[$i]);
}
}
/**
* @test
*/
public function should_find_promotion_when_no_equal_sign_in_notation()
{
// given
$parser = $this->getParser();
$notations = array('a8=R+', 'g8Q', 'axb1=R', 'b8');
$colors = array('white', 'white', 'black', 'white');
$expectedResults = array('r', 'q', 'r', '');
// when
for ($i = 0; $i < count($notations); $i++) {
$this->assertEquals($expectedResults[$i], $parser->getPromoteByNotation($notations[$i]), 'Notation ' . $notations[$i] . ' failed ');
}
}
/**
* @test
*/
public function shouldBeAbleToCreateGameMoveByMove()
{
// given
$parser = new FenParser0x88();
$parser->newGame();
$parser->move(array('from' => 'e2', 'to' => 'e4'));
$this->assertEquals('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1', $parser->getFen());
$parser->move(array('from' => 'e7', 'to' => 'e5'));
$this->assertEquals('rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2', $parser->getFen());
}
/**
* @test
*/
public function shouldBeAbleToMoveByNotation()
{
$parser = $this->getSpasskyFischerGameWith3FoldReptition();
$this->assertEquals('8/1p2ppk1/p1np4/6p1/2R1P3/1P4KP/P1R1r1P1/8 b - - 7 45', $parser->getFen());
}
/**
* @test
*/
public function shouldDetermine3FoldRepetition()
{
// given
$parser = new FenParser0x88();
// when
$fens = $this->getFenFromSpasskyFischer();
// then
$this->assertTrue($parser->hasThreeFoldRepetition($fens));
}
/**
* @test
*/
public function shouldParseProblematicGame()
{
// given
$pgnParser = new PgnParser("pgn/problematic.pgn");
// when
$game = $pgnParser->getFirstGame();
// then
$this->assertEquals((36 * 2) + 1, count($game['moves']));
}
/**
* @test
*/
public function shouldParseProblemCurio()
{
$pgnParser = new PgnParser("pgn/CURIO.pgn");
$games = $pgnParser->getGames();
}
/**
* @test
*/
public function shouldParseClockComments()
{
// given
$pgnParser = new PgnParser("pgn/lcc2016.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
// then
$this->assertNotEmpty($game['white'], $game);
$this->assertNotEmpty($game['moves'][0]);
$m = json_encode($game['moves'][0]);
$this->assertNotEmpty($game['moves'][0]['clk'], "Move: " . $m);
$this->assertEquals('1:59:56', $game['moves'][0]['clk']);
}
/* START action tests */
/**
* @test
*/
public function shouldHandleStandardArrowComments(){
$pgnParser = new PgnParser("pgn/pgn-with-arrows2.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$expectedMoves = array("", "Nf3", "c5", "c4", "Nc6", "Nc3", "e5", "e3", "Nf6", "Be2", "d5");
for ($i = 1; $i < count($expectedMoves); $i++) {
$m = $game["moves"][$i];
$this->assertEquals($expectedMoves[$i], $m["m"]);
}
$m = $game["moves"][7];
// then
$this->assertEquals("e3", $m["m"]);
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(2, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$this->assertFalse(isset($m[CHESS_JSON::MOVE_COMMENT]), json_encode($game));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("a1", $actions[0]["from"]);
$this->assertEquals("a8", $actions[0]["to"]);
$this->assertEquals("a8", $actions[1]["from"]);
$this->assertEquals("h8", $actions[1]["to"]);
$this->assertEquals("R", $actions[0]["color"]);
$this->assertEquals("G", $actions[1]["color"]);
}
/**
* @test
*/
public function shouldHandleArrowsInComments()
{
// given
$pgnParser = new PgnParser("pgn/pgn-with-arrows.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$expectedMoves = array("", "Nf3", "c5", "c4", "Nc6", "Nc3", "e5", "e3", "Nf6", "Be2", "d5");
for ($i = 1; $i < count($expectedMoves); $i++) {
$m = $game["moves"][$i];
$this->assertEquals($expectedMoves[$i], $m["m"]);
}
$m = $game["moves"][7];
// then
$this->assertEquals("e3", $m["m"]);
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(2, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$this->assertFalse(isset($m[CHESS_JSON::MOVE_COMMENT]));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("a1", $actions[0]["from"]);
$this->assertEquals("a8", $actions[0]["to"]);
$this->assertEquals("a8", $actions[1]["from"]);
$this->assertEquals("h8", $actions[1]["to"]);
}
/**
* @test
*/
public function shouldParseColorsOfArrows()
{
// given
$pgnParser = new PgnParser("pgn/pgn-with-arrows.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$m = $game["moves"][10];
// then
$this->assertEquals("d5", $m["m"]);
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(5, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("h1", $actions[0]["from"]);
$this->assertEquals("h8", $actions[0]["to"]);
$this->assertEquals("#f00", $actions[0]["color"]);
}
/**
* @test
*/
public function shouldHandlePrefaceArrows()
{
// given
$pgnParser = new PgnParser("pgn/pgn-with-arrows.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$m = $game["moves"][0];
// then
$this->assertFalse(isset($m['m']));
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(4, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("e2", $actions[0]["from"]);
$this->assertEquals("e4", $actions[0]["to"]);
}
/**
* @test
*/
public function shouldHandleStandardPrefaceArrows()
{
// given
$pgnParser = new PgnParser("pgn/pgn-with-arrows2.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$m = $game["moves"][0];
// then
$this->assertFalse(isset($m['m']));
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(4, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("e2", $actions[0]["from"]);
$this->assertEquals("e4", $actions[0]["to"]);
}
/**
* @test
*/
public function shouldHandleHighlightedSquaresBeforeFirstMove()
{
// given
$pgnParser = new PgnParser("pgn/pgn-with-arrows.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
$m = $game["moves"][0];
// then
$this->assertFalse(isset($m['m']));
$this->assertNotEmpty($m[CHESS_JSON::MOVE_ACTIONS]);
$this->assertCount(4, $m[CHESS_JSON::MOVE_ACTIONS], json_encode($m));
$actions = $m[CHESS_JSON::MOVE_ACTIONS];
$this->assertEquals("e2", $actions[2]["square"]);
$this->assertEquals("highlight", $actions[2]["type"]);
}
/**
* @test
*/
public function shouldHandleDifficultPgnWithArrowsAndSquares()
{
// given
$pgnParser = new PgnParser("pgn/arrowtest.pgn");
// when
$game = $pgnParser->getGameByIndex(0);
// then
$this->assertEquals(8, count($game["moves"]));
$this->assertNotEmpty($game["moves"][0]);
$m = $game["moves"][1];
$this->assertEquals("Qxf7+", $m["m"], "MOVE: " . json_encode($game["moves"][0]));
}
/**
* @test
*/
public function shouldParseProblematicGame2()
{
// given
$pgnParser = new PgnParser("pgn/problematic.pgn");
// when
$game = $pgnParser->getGameByIndex(1);
// then
$this->assertEquals(52, count($game['moves']));
}
/**
* @test
*/
public function shouldParseProblematicGame3()
{
// given
$pgnParser = new PgnParser("pgn/problematic.pgn");
// when
$game = $pgnParser->getGameByIndex(3);
// then
$this->assertEquals(82, count($game['moves']));
}
/**
* @test
*/
public function shouldSplitPgnIntoCorrectGames()
{
// given
$pgnParser = new PgnParser("pgn/1001-brilliant-checkmates.pgn");
// when
$games = $pgnParser->getUnparsedGames();
// then
$this->assertEquals(995, count($games));
}
private function getSpasskyFischerGameWith3FoldReptition()
{
$parser = $this->getParser();
$moves = 'e4,d6,d4,g6,Nc3,Nf6,f4,Bg7,Nf3,c5,dxc5,Qa5,Bd3,Qxc5,Qe2,O-O,Be3,Qa5,O-O,Bg4,Rad1,Nc6,Bc4,Nh5,Bb3,Bxc3,bxc3,Qxc3,f5,Nf6,h3,Bxf3,Qxf3,Na5,Rd3,Qc7,Bh6,Nxb3,cxb3,Qc5+,Kh1,Qe5,Bxf8,Rxf8,Re3,Rc8,fxg6,hxg6,Qf4,Qxf4,Rxf4,Nd7,Rf2,Ne5,Kh2,Rc1,Ree2,Nc6,Rc2,Re1,Rfe2,Ra1,Kg3,Kg7,Rcd2,Rf1,Rf2,Re1,Rfe2,Rf1,Re3,a6,Rc3,Re1,Rc4,Rf1,Rdc2,Ra1,Rf2,Re1,Rfc2,g5,Rc1,Re2,R1c2,Re1,Rc1,Re2,R1c2';
$moves = explode(",", $moves);
foreach ($moves as $move) {
$parser->makeMoveByNotation($move);
}
return $parser;
}
private function getFenFromSpasskyFischer()
{
$parser = $this->getParser();
$moves = 'e4,d6,d4,g6,Nc3,Nf6,f4,Bg7,Nf3,c5,dxc5,Qa5,Bd3,Qxc5,Qe2,O-O,Be3,Qa5,O-O,Bg4,Rad1,Nc6,Bc4,Nh5,Bb3,Bxc3,bxc3,Qxc3,f5,Nf6,h3,Bxf3,Qxf3,Na5,Rd3,Qc7,Bh6,Nxb3,cxb3,Qc5+,Kh1,Qe5,Bxf8,Rxf8,Re3,Rc8,fxg6,hxg6,Qf4,Qxf4,Rxf4,Nd7,Rf2,Ne5,Kh2,Rc1,Ree2,Nc6,Rc2,Re1,Rfe2,Ra1,Kg3,Kg7,Rcd2,Rf1,Rf2,Re1,Rfe2,Rf1,Re3,a6,Rc3,Re1,Rc4,Rf1,Rdc2,Ra1,Rf2,Re1,Rfc2,g5,Rc1,Re2,R1c2,Re1,Rc1,Re2,R1c2';
$moves = explode(",", $moves);
$ret = array();
foreach ($moves as $move) {
$parser->makeMoveByNotation($move);
$ret[] = $parser->getFen();
}
return $ret;
}
/**
* @param null $fen
* @return FenParser0x88
*/
private function getParser($fen = null)
{
if (!isset($fen)) {
$fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
}
return new FenParser0x88($fen);
}
/**
* @test
*/
public function shouldParseArtAttack()
{
$pgnParser = new PgnParser("pgn/art_attack.pgn");
// when
$game = $pgnParser->getGames();
}
/**
* @test
*/
public function shouldParseTwoLinesPgn()
{
// given
$pgnParser = new PgnParser("pgn/greatgames-twolines.pgn");
// when
$games = $pgnParser->getGames();
// then
$this->assertCount(10, $games);
$this->assertEquals("McDonnell,A", $games[0]["white"]);
}
/**
* @test
*/
public function shouldParseGreatGamesSameAsTwoLine()
{
$parser1 = new PgnParser("pgn/greatgames-twolines.pgn");
$parser2 = new PgnParser("pgn/greatgames.pgn");
$games1 = $parser1->getGames();
$games2 = $parser2->getGames();
$this->assertCount(count($games2), $games1);
$count = count($games2);
for ($i = 0; $i < $count; $i++) {
$expected = $games2[$i];
$game = $games1[$i];
$this->assertEquals($expected["white"], $game["white"]);
$this->assertEquals($expected["site"], $game["site"]);
$this->assertEquals($expected["black"], $game["black"]);
$moves = count($expected["moves"]);
$this->assertCount($moves, $game["moves"]);
}
}
/**
* @test
*/
public function shouldParseGreatGamesSameAsOneLine()
{
$parser1 = new PgnParser("pgn/greatgames-onelines.pgn");
$parser2 = new PgnParser("pgn/greatgames.pgn");
$games1 = $parser1->getGames();
$games2 = $parser2->getGames();
$this->assertCount(count($games2), $games1);
$count = count($games2);
$this->assertEquals("Immortal game", $games1[1]["event"]);
for ($i = 0; $i < $count; $i++) {
$expected = $games2[$i];
$game = $games1[$i];
$this->assertEquals($expected["white"], $game["white"]);
$this->assertEquals($expected["site"], $game["site"]);
$this->assertEquals($expected["black"], $game["black"]);
$moves = count($expected["moves"]);
$this->assertCount($moves, $game["moves"]);
}
}
/**
* @test
*/
public function shouldHandleNullMoves()
{
$parser = new PgnParser("pgn/nullmoves.pgn");
$game = $parser->getGameByIndex(0);
// No exception
$this->assertCount(5, $game["moves"]);
}
/**
* @test
*/
public function shouldParseProblematic_3()
{
// given
$parser = new PgnParser("pgn/problematic3.pgn");
// when
$games = $parser->getGames();
// then
$this->assertEquals(30, count($games));
}
/**
* @test
*/
public function shouldParseProblematic_4()
{
// given
$parser = new PgnParser("pgn/problematic4.pgn");
// when
$games = $parser->getGames();
// then
$this->assertEquals(1, count($games));
}
/**
* @test
*/
public function shouldBeAbleToImportWithoutFenForEachMove()
{
// given
$parser = new PgnParser("pgn/greatgames.pgn");
// when
$games = $parser->getGamesShort();
$game = $games[0];
// then
$this->assertEquals(10, count($games));
$this->assertArrayNotHasKey("fen", $game["moves"][0], json_encode($game["moves"][0]));
$this->assertEquals("e2e4", $game["moves"][0]["n"], json_encode($game["moves"][0]));
}
/**
* @test
*/
public function shouldBeAbleToHandleVariationWhenUsingShortVersion()
{
// given
$parser = new PgnParser("pgn/variation.pgn");
// when
$games = $parser->getGamesShort();
$this->assertEquals(1, count($games), json_encode($games));
$game = $games[0];
$variations = $game["moves"][0]["v"];
$msg = json_encode($variations);
// then
$this->assertEquals(3, count($variations), $msg);
$this->assertEquals(2, count($variations[0]), $msg);
$this->assertEquals(1, count($variations[1]), $msg);
$this->assertEquals(1, count($variations[2]), $msg);
$var1 = $variations[0];
$this->assertEquals("d2d4", $var1[0]["n"]);
$this->assertEquals("d2d4", $var1[0]["n"]);
$var2 = $variations[1];
$this->assertEquals("c2c4", $var2[0]["n"]);
$var2 = $variations[2];
$this->assertEquals("f2f4", $var2[0]["n"]);
$var3 = $game["moves"][2]["v"][0];
$this->assertEquals("b1c3", $var3[0]["n"], json_encode($game));
}
/**
* @test
*/
public function shouldHandleProblematic()
{
// given
$pgn = '[Event "?"]
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "?"]
[Black "?"]
[Result "*"]
[FEN "7k/8/8/8/8/8/8/R6K w - - 0 1"]
{In this position the power of the Rook is demonstrated by the first move,} 1.Ra7 {which immediately confines the Black King to the last rank, and the mate is quickly accomplished by:} 1...Kg8 2.Kg2 {The combined action of King and Rook is needed to arrive at a position in which mate can be forced. The general principle for a beginner to follow is to keep his King as much as possible on the same rank, or, as in this case, file, as the opposing King.
When, in this case, the King has been brought to the sixth rank, it is better to place it, not on the same file, but on the next to it towards the centre.} 2...Kf8 3.Kf3 Ke8 4.Ke4 Kd8 5.Kd5 Kc8
( {Black could have played} 5...Ke8 {and, according to the principle, White would have continued} 6.Kd6 Kf8 {(the Black King will ultimately be forced
to move in front of the White King and be mated by R - R 8);} 7.Ke6 Kg8 8.Kf6 Kh8 9.Kg6 Kg8 10.Ra8# )
6.Kd6
( {Not} 6.Kc6 {because then the Black King will go back to d8 and it will take much longer to mate. } )
6...Kb8
( {If now the King moves back to d8,} 6...Kd8 7.Ra8# {mates at once} )
7.Rc7 Ka8 8.Kc6 Kb8 9.Kb6 Ka8 10.Rc8# *';
// when
$parser = new PgnParser();
$parser->setPgnContent($pgn);
$content = $parser->getUnparsedGames();
// when
$games = $parser->getGames();
// then
$this->assertEquals(1, count($games));
$game = $games[0];
$moves = $game["moves"];
$this->assertEquals(20, count($moves), json_encode($content));
}
/**
* @test
*/
public function shouldHandleProblematic2()
{
// given
$pgn = '[Event "URS-ch23"]
[Site "Leningrad"]
[Date "1956.??.??"]
[Round "?"]
[White "Boleslavsky, Isaak"]
[Black "Lisitsin, Georgy"]
[Result "1-0"]
[ECO "B76"]
[PlyCount "59"]
[EventDate "1956.01.10"]
[EventRounds "17"]
[EventCountry "URS"]
[Source "ChessBase"]
[SourceDate "1999.07.01"]
{Knight´s Outpost at d5 A good grip on the center almost always guarantees
the succes of the King-side attack. Boleslavsky´s plan was to anchor his
Knight on d5 square so firmly that it can never be driven away. Once his
Knight reaches the magic square d5, combinations appear out of the air as a
reward, allowing the King-side attack.} 1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4
Nf6 5. Nc3 g6 6. Be3 Bg7 7. f3 {This move does many things: (a) it stregthens
the center; (b) it prevents an attack on e3 Bishop (and its exchange) by 7...
Ng4; (c) it prepares for a later Pawn storm by g4 and h4.} O-O 8. Qd2 Nc6 9.
O-O-O Nxd4 (9... d5 10. Nxc6 bxc6 11. exd5 cxd5 12. Nxd5 Nxd5 13. Qxd5 Qc7 14.
Qxa8 Bf5 15. Qxf8+ Kxf8 16. Rd2 $14) 10. Bxd4 Qa5 11. Kb1 {Threatens 12.Nd5} e5
(11... -- 12. Nd5 Qd8 (12... Qxd2 13. Nxe7+ Kh8 14. Rxd2) 13. Nxf6+ Bxf6 14.
Bxf6 exf6 15. Qxd6) 12. Be3 Be6 13. a3 Rfd8 {Prepares for an eventual d5,
which would free his game.} 14. Nb5 {This powerful move interferes with
Black´s plan.} Qa4 {At this point Boleslavsky had two objectis in mind: (1)
prevent Black from freeing himself by ...d5; (2) Establish his Knight firmly
at the outpost station d5, but to bring this about it is necessary to rid the
board of the two black pieces that guard the square d5: the e6 Bishop and the
f6 Knight.} (14... Qxd2 15. Rxd2 {thereatening 16.Rxd6 as well as 16.Nc7, Rac8;
17.Nxe6 and White will have advantage of two Bishops against Knight and Bishop.
}) 15. c4 $1 {A brilliant sacrifice that must be accepted. Refusing the Pawn
means that Black could never free himself by ... d5. It would also enable
White to play 16.Nc3 (attacking the Queen) and thus gain time for 17.Nd5,
establishing a strongly-supported outpost.} (15. Nxd6 Ne8 $2 (15... a6) 16. Bc5
$2 (16. Bb5 $1 Qxb5 17. Nxb5 Rxd2 18. Rxd2 $18 {and White has won the exchange.
}) 16... Nxd6 17. Bxd6 Bf8 18. Qb4 Rxd6 19. Rxd6 (19. Qxa4 $4 Rxd1#) 19... Qxb4
20. axb4 Bxd6 {and Black has won a piece.}) 15... Bxc4 16. Nc3 Qb3 17. Bxc4
Qxc4 {One black piece has been disposed of. Now to get rid of the other.} 18.
Bg5 {White pins the Knight to keep it from running away.} Qe6 19. Bxf6 Qxf6 20.
Nd5 {Now we shall see whether Boleslavsky´s imaginative strategy is justified.
The Knight now dominates the board and cannot be driven off, but was that
worth a Pawn?} Qh4 {Black tries to prevent the advance of the adverse
King-side Pawns. He intends to meet 21.g3 with 21... Qh6, while the reply to
21.h3 would be 21... Bh6, followed by 22... Bf4.} 21. Qe2 Bf8 22. Qf1 $1 {A
subtle preperatory move.} ({the immediate} 22. g3 Qh3 {and black Queen
blocades the h2 Pawn.}) 22... Rac8 23. g3 {The Pawns begin their advance to
break up Black´s King-side.} Qg5 24. h4 Qh6 (24... Qxg3 25. Rh3 $1 {and
Black´s Queen has no escape.} (25. Rd2 $2 Bh6 26. Rg2 $2 (26. Rh3 Bxd2 27.
Rxg3 Rc1+ 28. Qxc1 Bxc1 29. Kxc1 $18) 26... Rc1+ 27. Qxc1 Qxg2 $11)) 25. g4 {
Now White intends 26.g5} g5 (25... -- 26. g5 Qg7 (26... Qh5 27. Nf6+) 27. Nf6+
Kh8 28. h5 {Thretens to win the Queen by 29.h6} gxh5 29. Rxh5 {and the attack
on h7 Pawn will force Black to give up his Queen.}) 26. hxg5 Qxg5 27. Rh5 Qg6
28. g5 $1 {The threat is 29.Nf6+ followed by 30.Rxh7+} h6 (28... Qxh5 29. Nf6+
Kh8 30. Nxh5) (28... Bg7 29. Ne7+ Kh8 30. Nxg6+ fxg6) 29. Rxh6 Qxg5 (29... Bxh6
30. Ne7+ Kf8 31. Nxg6+) 30. Rh5 (30. Rh5 Qg6 31. Qh1 {threatens 32.Rg1 winning
the Queen.} Qe6 32. Rh8+ Kg7 33. Qh7# {The King-side Pawn did an amazing job
of opening up files for the benefit of the heavy pieces.}) 1-0';
// when
$parser = new PgnParser();
$parser->setPgnContent($pgn);
$content = $parser->getUnparsedGames();
// when
$games = $parser->getGames();
// then
$this->assertEquals(1, count($games));
$game = $games[0];
$moves = $game["moves"];
$this->assertEquals(60, count($moves), json_encode($content));
$m = $moves[59];
$this->assertEquals($m["m"], "Rh5");
}
/**
* @test
*/
public function shouldHandleAnotherProblematicPgn(){
// given
$pgn = '[Event "URS-ch23"]
[Site "Leningrad"]
[Date "1956.??.??"]
[Round "?"]
[White "Boleslavsky, Isaak"]
[Black "Lisitsin, Georgy"]
[Result "1-0"]
[ECO "B76"]
[PlyCount "59"]
[EventDate "1956.01.10"]
[EventRounds "17"]
[EventCountry "URS"]
[Source "ChessBase"]
[SourceDate "1999.07.01"]
1. d4 Nf6 2. c4 e6 3.Nf3 Bb4 4.Nbd2 O-O 5.a3 Be7 6.e4 d6 7.Qc2 Nbd7 8.b3 c6 9.Bb2 Qc7 10.g3 e5 11.Bg2 a5 12.O-O Re8 13.c5 ed4 14.cd6 Bd6 15.Nd4 Bf8 16.Rae1 Nc5 17.e5 Nfd7 18.Nc4 Ne6 19.Ne6 Re6 20.f4 b5 21.f5 Rh6 22.Nd6 Bd6 23.ed6 Rd6 24.Re8 Nf8 25.Qc5 f6 26.Bd5';
// when
$parser = new PgnParser();
$parser->setPgnContent($pgn);
$content = $parser->getUnparsedGames();
// when
$games = $parser->getGames();
// then
$this->assertEquals(1, count($games));
$game = $games[0];
$moves = $game["moves"];
$this->assertEquals(51, count($moves), json_encode($content));
}
/**
* @test
*/
public function shouldImportAllGames(){
$pgn = file_get_contents("pgn/philadelphiainternational2019.pgn");
// when
$parser = new PgnParser();
$parser->setPgnContent($pgn);
$content = $parser->getUnparsedGames();
// when
// then
$this->assertEquals(132, $parser->countGames());
}
}