Skip to content

Commit

Permalink
Merge pull request #11 from kraktus/test_rep
Browse files Browse the repository at this point in the history
3fold repetition: Fix generation
  • Loading branch information
ornicar committed Sep 30, 2021
2 parents 618f95e + 4517b2f commit ab3dec3
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 16 deletions.
13 changes: 6 additions & 7 deletions generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from util import get_next_move_pair, material_count, material_diff, is_up_in_material, maximum_castling_rights, win_chances
from server import Server

version = 47
version = 48

logger = logging.getLogger(__name__)
logging.basicConfig(format='%(asctime)s %(levelname)-4s %(message)s', datefmt='%m/%d %H:%M')
Expand Down Expand Up @@ -123,16 +123,15 @@ def analyze_game(self, game: Game, tier: int) -> Optional[Puzzle]:
logger.debug(f'Analyzing tier {tier} {game.headers.get("Site")}...')

prev_score: Score = Cp(20)
seen_fens: Set[str] = set()
seen_epds: Set[str] = set()
board = game.board()
skip_until_irreversible = False

for node in game.mainline():

if skip_until_irreversible:
if board.is_irreversible(node.move):
skip_until_irreversible = False
seen_fens.clear()
seen_epds.clear()
else:
board.push(node.move)
continue
Expand All @@ -144,11 +143,11 @@ def analyze_game(self, game: Game, tier: int) -> Optional[Puzzle]:
return None

board.push(node.move)
fen = board.fen()
if fen in seen_fens:
epd = board.epd()
if epd in seen_epds:
skip_until_irreversible = True
continue
seen_fens.add(fen)
seen_epds.add(epd)

if board.castling_rights != maximum_castling_rights(board):
continue
Expand Down
27 changes: 18 additions & 9 deletions generator/test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
import logging
import chess
from model import Puzzle
from generator import logger
from server import Server
Expand All @@ -8,13 +9,16 @@
from chess.pgn import Game, GameNode
from typing import List, Optional, Tuple, Literal, Union

import generator
from generator import Generator, Server, make_engine

class TestGenerator(unittest.TestCase):

engine = generator.make_engine("stockfish", 6) # don't use more than 6 threads! it fails at finding mates
server = Server(logger, "", "", 0)
logger.setLevel(logging.DEBUG)
@classmethod
def setUpClass(cls):
cls.engine = make_engine("stockfish", 6) # don't use more than 6 threads! it fails at finding mates
cls.server = Server(logger, "", "", 0)
cls.gen = Generator(cls.engine, cls.server)
logger.setLevel(logging.DEBUG)

def test_puzzle_1(self) -> None:
# https://lichess.org/analysis/standard/3q1k2/p7/1p2Q2p/5P1K/1P4P1/P7/8/8_w_-_-_5_57#112
Expand Down Expand Up @@ -150,12 +154,18 @@ def test_not_puzzle_16(self) -> None:
self.not_puzzle("8/Pkp3pp/8/4p3/1P2b3/4K3/1P3r1P/R7 b - - 1 30",
Cp(0), "f2f3", Cp(5000))

def test_not_puzzle_17(self) -> None:
with open("test_pgn_3fold_uDMCM.pgn") as pgn:
game = chess.pgn.read_game(pgn)
puzzle = self.gen.analyze_game(game, tier=10)
self.assertEqual(puzzle, None)

def get_puzzle(self, fen: str, prev_score: Score, move: str, current_score: Score, moves: str) -> None:
board = Board(fen)
game = Game.from_board(board)
node = game.add_main_variation(Move.from_uci(move))
current_eval = PovScore(current_score, not board.turn)
result = generator.analyze_position(self.server, self.engine, node, prev_score, current_eval)
result = self.gen.analyze_position(node, prev_score, current_eval, tier=10)
self.assert_is_puzzle_with_moves(result, [Move.from_uci(x) for x in moves.split()])


Expand All @@ -164,18 +174,17 @@ def not_puzzle(self, fen: str, prev_score: Score, move: str, current_score: Scor
game = Game.from_board(board)
node = game.add_main_variation(Move.from_uci(move))
current_eval = PovScore(current_score, not board.turn)
result = generator.analyze_position(self.server, self.engine, node, prev_score, current_eval)
result = self.gen.analyze_position( node, prev_score, current_eval, tier=10)
self.assertIsInstance(result, Score)


def assert_is_puzzle_with_moves(self, puzzle: Union[Puzzle, Score], moves: List[Move]) -> None:
self.assertIsInstance(puzzle, generator.Puzzle)
self.assertIsInstance(puzzle, Puzzle)
if isinstance(puzzle, Puzzle):
self.assertEqual(puzzle.moves, moves)


@classmethod
def tearDownClass(cls) -> None:
def tearDownClass(cls):
cls.engine.close()


Expand Down
24 changes: 24 additions & 0 deletions generator/test_pgn_3fold_uDMCM.pgn
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[Event "Rated Blitz game"]
[Site "https://lichess.org/ZlCTzfMG"]
[Date "2021.06.28"]
[White "genassien"]
[Black "Freiheitskaempfer"]
[Result "1-0"]
[UTCDate "2021.06.28"]
[UTCTime "18:21:43"]
[WhiteElo "2370"]
[BlackElo "2441"]
[WhiteRatingDiff "+7"]
[BlackRatingDiff "-7"]
[WhiteTitle "FM"]
[BlackTitle "FM"]
[Variant "Standard"]
[TimeControl "300+0"]
[ECO "C24"]
[Opening "Bishop's Opening: Berlin Defense"]
[Termination "Time forfeit"]
[Annotator "lichess.org"]

1. e4 { [%eval 0.24] [%clk 0:05:00] } 1... e5 { [%eval 0.2] [%clk 0:05:00] } 2. Bc4 { [%eval 0.0] [%clk 0:04:58] } 2... Nf6 { [%eval 0.0] [%clk 0:04:59] } { C24 Bishop's Opening: Berlin Defense } 3. d3 { [%eval 0.0] [%clk 0:04:53] } 3... c6 { [%eval 0.0] [%clk 0:04:58] } 4. Nf3 { [%eval 0.0] [%clk 0:04:52] } 4... d5 { [%eval 0.02] [%clk 0:04:58] } 5. Bb3 { [%eval 0.0] [%clk 0:04:51] } 5... Bb4+ { [%eval 0.05] [%clk 0:04:57] } 6. c3 { [%eval 0.05] [%clk 0:04:45] } 6... Bd6 { [%eval 0.09] [%clk 0:04:56] } 7. Nbd2 { [%eval -0.35] [%clk 0:04:33] } 7... Nbd7 { [%eval -0.24] [%clk 0:04:54] } 8. O-O { [%eval -0.14] [%clk 0:04:26] } 8... O-O { [%eval -0.04] [%clk 0:04:50] } 9. Re1 { [%eval -0.33] [%clk 0:04:25] } 9... dxe4 { [%eval -0.19] [%clk 0:04:42] } 10. dxe4 { [%eval -0.01] [%clk 0:04:16] } 10... Qe7 { [%eval 0.0] [%clk 0:04:35] } 11. Nc4 { [%eval -0.08] [%clk 0:04:14] } 11... Bc7 { [%eval 0.0] [%clk 0:04:34] } 12. Ne3? { (0.00 → -1.25) Mistake. a4 was best. } { [%eval -1.25] [%clk 0:04:13] } (12. a4 Rd8 13. Qe2 Nb6 14. Na5 Nbd7) 12... Nc5 { [%eval -1.25] [%clk 0:04:28] } 13. Bc2 { [%eval -1.12] [%clk 0:04:08] } 13... Ncxe4 { [%eval -0.98] [%clk 0:04:23] } 14. Nc4?! { (-0.98 → -1.74) Inaccuracy. Nf1 was best. } { [%eval -1.74] [%clk 0:03:49] } (14. Nf1 Nc5 15. b4 Ncd7 16. Ng3 Re8 17. a4 Nb6 18. h3 Nbd5 19. Bd2 h6 20. b5 Qc5) 14... Qc5?! { (-1.74 → -0.99) Inaccuracy. Bf5 was best. } { [%eval -0.99] [%clk 0:03:42] } (14... Bf5 15. Ng5) 15. Bxe4?! { (-0.99 → -1.80) Inaccuracy. Qe2 was best. } { [%eval -1.8] [%clk 0:03:13] } (15. Qe2 Bf5) 15... Qxc4 { [%eval -1.69] [%clk 0:03:40] } 16. Bc2?! { (-1.69 → -2.37) Inaccuracy. Bd3 was best. } { [%eval -2.37] [%clk 0:03:06] } (16. Bd3) 16... Bg4?! { (-2.37 → -1.36) Inaccuracy. Rd8 was best. } { [%eval -1.36] [%clk 0:03:15] } (16... Rd8 17. Nd2 Qh4 18. Qe2 Nd5 19. Nf3 Qh5 20. a4 a5 21. h3 f6 22. Bd2 Qf7 23. c4) 17. Bg5 { [%eval -1.16] [%clk 0:03:01] } 17... Rad8 { [%eval -0.83] [%clk 0:03:08] } 18. Qb1 { [%eval -1.1] [%clk 0:02:54] } 18... Bxf3 { [%eval -1.38] [%clk 0:02:53] } 19. Bxf6?? { (-1.38 → Mate in 6) Checkmate is now unavoidable. gxf3 was best. } { [%eval #-6] [%clk 0:02:53] } (19. gxf3) 19... gxf6?? { (Mate in 6 → -3.06) Lost forced checkmate sequence. Qg4 was best. } { [%eval -3.06] [%clk 0:02:50] } (19... Qg4 20. Bxh7+ Kh8 21. Qg6 fxg6 22. g3 Qh3 23. Bxg7+ Kxg7 24. a3 Qg2#) 20. Bxh7+? { (-3.06 → -5.75) Mistake. gxf3 was best. } { [%eval -5.75] [%clk 0:02:50] } (20. gxf3 Kh8 21. Re4 Rg8+ 22. Kh1 Qd5 23. Bb3 Qd2 24. Rh4 Rg7 25. Rg4 Rxg4 26. fxg4 Qxf2) 20... Kg7 { [%eval -6.06] [%clk 0:02:47] } 21. gxf3 { [%eval -6.09] [%clk 0:02:42] } 21... Qh4 { [%eval -5.99] [%clk 0:02:34] } 22. Be4?! { (-5.99 → -10.07) Inaccuracy. Qf5 was best. } { [%eval -10.07] [%clk 0:02:38] } (22. Qf5 Qxh7 23. Qxh7+ Kxh7 24. Rad1 Kg6 25. Kf1 Rh8 26. Rxd8 Rxd8 27. h3 f5 28. Ke2 Rh8) 22... Rh8 { [%eval -7.71] [%clk 0:02:29] } 23. Kf1 { [%eval -8.01] [%clk 0:01:43] } 23... Rd2 { [%eval -6.46] [%clk 0:02:25] } 24. Re2 { [%eval -6.29] [%clk 0:01:42] } 24... Rhd8 { [%eval -6.04] [%clk 0:02:13] } 25. Rxd2 { [%eval -6.4] [%clk 0:01:32] } 25... Rxd2 { [%eval -6.33] [%clk 0:02:11] } 26. Qe1 { [%eval -6.46] [%clk 0:01:31] } 26... Rxb2 { [%eval -6.73] [%clk 0:02:07] } 27. Rb1 { [%eval -7.46] [%clk 0:01:27] } 27... Qh3+?? { (-7.46 → -2.67) Blunder. Rxa2 was best. } { [%eval -2.67] [%clk 0:01:56] } (27... Rxa2) 28. Kg1 { [%eval -2.84] [%clk 0:01:25] } 28... Rxb1?? { (-2.84 → -0.88) Blunder. Rxa2 was best. } { [%eval -0.88] [%clk 0:01:42] } (28... Rxa2 29. Qf1 Qxf1+ 30. Kxf1 f5 31. Bxf5 Bb6 32. Kg2 Kf6 33. Bc8 Rxf2+ 34. Kg3 Rc2 35. Bxb7) 29. Qxb1 { [%eval -0.77] [%clk 0:01:24] } 29... Bb6 { [%eval -0.75] [%clk 0:01:37] } 30. Qf1 { [%eval -0.91] [%clk 0:01:10] } 30... Qh6?! { (-0.91 → -0.30) Inaccuracy. Qe6 was best. } { [%eval -0.3] [%clk 0:01:31] } (30... Qe6 31. Qg2+ Kf8 32. Qg4 Ke7 33. c4 Kd8 34. Qf5 Kc7 35. h4 Bc5 36. f4 Qxc4 37. fxe5) 31. Qg2+ { [%eval -0.3] [%clk 0:00:56] } 31... Kf8 { [%eval -0.3] [%clk 0:01:29] } 32. Qg4 { [%eval -0.31] [%clk 0:00:53] } 32... Qc1+ { [%eval -0.12] [%clk 0:00:57] } 33. Kg2 { [%eval -0.17] [%clk 0:00:52] } 33... Qd2 { [%eval -0.01] [%clk 0:00:39] } 34. Qc8+ { [%eval 0.0] [%clk 0:00:48] } 34... Ke7 { [%eval 0.0] [%clk 0:00:38] } 35. Qxb7+ { [%eval 0.0] [%clk 0:00:47] } 35... Kf8 { [%eval 0.0] [%clk 0:00:35] } 36. Qc8+ { [%eval 0.0] [%clk 0:00:45] } 36... Ke7 { [%eval 0.0] [%clk 0:00:33] } 37. Bxc6 { [%eval 0.0] [%clk 0:00:37] } 37... Qxf2+ { [%eval 0.0] [%clk 0:00:32] } 38. Kh3 { [%eval 0.0] [%clk 0:00:35] } 38... Qf1+ { [%eval 0.0] [%clk 0:00:31] } 39. Kg4?? { (0.00 → Mate in 3) Checkmate is now unavoidable. Kh4 was best. } { [%eval #-3] [%clk 0:00:31] } (39. Kh4 Qc4+ 40. Kh5 Qxa2 41. Qd7+ Kf8 42. Qc8+) 39... Qg2+ { [%eval #-2] [%clk 0:00:30] } 40. Kf5 { [%eval #-1] [%clk 0:00:26] } 40... Qg5+ { [%eval #-2] [%clk 0:00:28] } 41. Ke4 { [%eval #-2] [%clk 0:00:25] } 41... Qe3+ { [%eval #-1] [%clk 0:00:27] } 42. Kd5 { [%eval #-1] [%clk 0:00:23] } 42... Qc5+ { [%eval #-2] [%clk 0:00:25] } 43. Ke4 { [%eval #-2] [%clk 0:00:23] } 43... Qe3+ { [%eval #-1] [%clk 0:00:22] } 44. Kd5 { [%eval #-1] [%clk 0:00:22] } 44... Qc5+ { [%eval #-2] [%clk 0:00:21] } 45. Ke4 { [%eval #-2] [%clk 0:00:21] } 45... f5+?? { (Mate in 2 → 0.00) Lost forced checkmate sequence. Qc4+ was best. } { [%eval 0.0] [%clk 0:00:17] } (45... Qc4+ 46. Kf5 Qf4#) 46. Kxf5 { [%eval 0.0] [%clk 0:00:20] } 46... e4+ { [%eval 0.0] [%clk 0:00:16] } 47. Kxe4 { [%eval 0.0] [%clk 0:00:17] } 47... Qe3+ { [%eval 0.0] [%clk 0:00:16] } 48. Kf5 { [%eval 0.0] [%clk 0:00:15] } 48... Qd3+ { [%eval 0.02] [%clk 0:00:09] } 49. Be4 { [%eval 0.0] [%clk 0:00:13] } 49... Qd6 { [%eval 0.03] [%clk 0:00:05] } 50. Qb7+ { [%eval 0.11] [%clk 0:00:09] } 50... Bc7 { [%eval 0.17] [%clk 0:00:04] } 51. Qb4 { [%eval 0.21] [%clk 0:00:07] } 51... Qxb4 { [%eval 0.16] [%clk 0:00:02] } 52. cxb4 { [%eval 0.21] [%clk 0:00:07] } 52... Bxh2 { [%eval 0.13] [%clk 0:00:01] } 53. Kg4 { [%eval 0.21] [%clk 0:00:07] } 53... Bd6 { [%eval 0.09] [%clk 0:00:00] } 54. f4 { [%eval -0.04] [%clk 0:00:07] } 54... Bxb4 { [%eval -0.04] [%clk 0:00:00] } 55. Kf5 { [%eval -0.03] [%clk 0:00:07] } 55... Bd6 { [%eval 0.0] [%clk 0:00:00] } 56. Kg4 { [%eval 0.0] [%clk 0:00:06] } 56... f6 { [%eval 0.0] [%clk 0:00:00] } 57. f5 { [%eval 0.0] [%clk 0:00:04] } 57... Bc5 { [%eval 0.0] [%clk 0:00:00] } 58. a3 { [%eval -0.12] [%clk 0:00:04] } 58... Bd6 { [%eval 0.0] [%clk 0:00:00] } 59. a4 { [%eval 0.0] [%clk 0:00:04] } { White wins on time. } 1-0


0 comments on commit ab3dec3

Please sign in to comment.