Skip to content

Commit

Permalink
Add includeBlunder argument
Browse files Browse the repository at this point in the history
  • Loading branch information
karol-brejna-i committed May 14, 2021
1 parent 920a12c commit a78d15f
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 59 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ To execute the generator execute this command. By default it will look for the `
- `--quiet` to reduce the screen output.
- `--depth=8` select the stockfish depth analysis. Default is `8` and will take some seconds to analyze a game, with `--depth=18` will take around 6 minutes.
- `--games=ruy_lopez.pgn` to select a specific pgn file. Default is `games.pgn`
- `strict=False` Use `False` to generate more tactics but a little more ambiguous. Default is `True`
- `--strict=False` Use `False` to generate more tactics but a little more ambiguous. Default is `True`
- `--threads=4` Stockfish argument, number of engine threads, default `4`
- `--memory=2048` Stockfish argument, memory in MB to use for engine hashtables, default `2048`
- `--includeBlunder=False` If False then generated puzzles won't include initial blunder move, default is `True`

Example:
`python3 main.py --quiet --depth=12 --games=ruy_lopez.pgn --strict=True --threads=2 --memory=1024`
Expand Down
55 changes: 38 additions & 17 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,41 @@
from modules.investigate.investigate import investigate
from modules.puzzle.puzzle import puzzle

parser = argparse.ArgumentParser(description=__doc__)

parser.add_argument("--threads", metavar="THREADS", nargs="?", type=int, default=4,
help="number of engine threads")
parser.add_argument("--memory", metavar="MEMORY", nargs="?", type=int, default=2048,
help="memory in MB to use for engine hashtables")
parser.add_argument("--depth", metavar="DEPTH", nargs="?", type=int, default=8,
help="depth for stockfish analysis")
parser.add_argument("--quiet", dest="loglevel",
default=logging.DEBUG, action="store_const", const=logging.INFO,
help="substantially reduce the number of logged messages")
parser.add_argument("--games", metavar="GAMES", default="games.pgn",
help="A specific pgn with games")
parser.add_argument("--strict", metavar="STRICT", default=True,
help="If False then it will be generate more tactics but maybe a little ambiguous")
settings = parser.parse_args()
def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')


def prepare_settings():
parser = argparse.ArgumentParser(description=__doc__)

parser.add_argument("--threads", metavar="THREADS", nargs="?", type=int, default=4,
help="number of engine threads")
parser.add_argument("--memory", metavar="MEMORY", nargs="?", type=int, default=2048,
help="memory in MB to use for engine hashtables")
parser.add_argument("--depth", metavar="DEPTH", nargs="?", type=int, default=8,
help="depth for stockfish analysis")
parser.add_argument("--quiet", dest="loglevel",
default=logging.DEBUG, action="store_const", const=logging.INFO,
help="substantially reduce the number of logged messages")
parser.add_argument("--games", metavar="GAMES", default="games.pgn",
help="A specific pgn with games")
parser.add_argument("--strict", metavar="STRICT", default=True,
help="If False then it will be generate more tactics but maybe a little ambiguous")
parser.add_argument("--includeBlunder", metavar="INCLUDE_BLUNDER", default=True,
type=str2bool, const=True, dest="include_blunder", nargs="?",
help="If False then generated puzzles won't include initial blunder move")

return parser.parse_args()


settings = prepare_settings()
try:
# Optionally fix colors on Windows and in journals if the colorama module
# is available.
Expand All @@ -45,6 +64,8 @@
logging.basicConfig(format="%(message)s", level=settings.loglevel, stream=sys.stdout)
logging.getLogger("requests.packages.urllib3").setLevel(logging.WARNING)
logging.getLogger("chess.uci").setLevel(logging.WARNING)
logging.getLogger("chess.engine").setLevel(logging.WARNING)
logging.getLogger("chess._engine").setLevel(logging.WARNING)

engine = chess.uci.popen_engine(stockfish_command())
engine.setoption({'Threads': settings.threads, 'Hash': settings.memory})
Expand Down Expand Up @@ -94,7 +115,7 @@
logging.debug(bcolors.WARNING + "Generating new puzzle..." + bcolors.ENDC)
i.generate(settings.depth)
if i.is_complete():
puzzle_pgn = post_puzzle(i)
puzzle_pgn = post_puzzle(i, settings.include_blunder)
tactics_file.write(puzzle_pgn)
tactics_file.write("\n\n")

Expand Down
10 changes: 7 additions & 3 deletions modules/api/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import logging

from modules.bcolors.bcolors import bcolors
from modules.exporters.pgn_exporter import PgnExporter


def post_puzzle(puzzle):
def post_puzzle(puzzle, include_blunder=True):
logging.debug(bcolors.WARNING + "NEW PUZZLE GENERATED" + bcolors.ENDC)
logging.info(bcolors.OKBLUE + str(puzzle.to_pgn()) + bcolors.ENDC)
return str(puzzle.to_pgn())

result = PgnExporter.export(puzzle, include_blunder)
logging.info(bcolors.OKBLUE + result + bcolors.ENDC)

return result
8 changes: 8 additions & 0 deletions modules/exporters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class BaseExporter:
@staticmethod
def export(puzzle, include_blunder=True):
"""
The method responsible for exporting Puzzle object into desired form.
:return: string representation of Puzzle object
"""
pass
45 changes: 45 additions & 0 deletions modules/exporters/pgn_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import chess
from chess import Move
from chess.pgn import Game

from modules.exporters import BaseExporter


class PgnExporter(BaseExporter):
@staticmethod
def determine_result_tag(board):
# In the generated tactics puzzle, the first to move is the one who lost
return '0-1' if board.turn else '1-0'

@staticmethod
def export(puzzle, include_first_move=True):
fen = puzzle.last_pos.fen()
board = chess.Board(fen)
game = Game().from_board(board)

result = PgnExporter.determine_result_tag(board)
moves = puzzle.positions.move_list()

if include_first_move:
first_move = puzzle.last_move
else:
# simulate the move (blunder)
board.push(puzzle.last_move)
board.clear_stack()
# take resulting board and create new game
game = Game().from_board(board)

first_move = Move.from_uci(moves.pop(0))

# start the line
node = game.add_main_variation(first_move)

# add the rest of the moves
for m in moves:
node = node.add_variation(Move.from_uci(m))

# copy headers from the original game and override result tag
for h in puzzle.game.headers:
game.headers[h] = puzzle.game.headers[h]
game.headers['Result'] = result
return str(game)
5 changes: 1 addition & 4 deletions modules/puzzle/analysed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ def __init__(self, move, evaluation):
self.evaluation = evaluation

def sign(self, val):
if val <= 0:
return -1
else:
return 1
return -1 if val <= 0 else 1

def sort_val(self):
if self.evaluation.cp is not None:
Expand Down
22 changes: 0 additions & 22 deletions modules/puzzle/puzzle.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import logging

import chess
import chess.pgn

from modules.bcolors.bcolors import bcolors
from modules.puzzle.position_list import position_list

Expand All @@ -25,25 +22,6 @@ def to_dict(self):
'move_list': self.positions.move_list()
}

def to_pgn(self):
fen = self.last_pos.fen()
board = chess.Board(fen)
game = chess.pgn.Game().from_board(board)

# In the tactic the first to move is the one who lost
result = '1-0' # result of the tactic not the game
if board.turn: # turn return true if white
result = '0-1'

node = game.add_variation(self.last_move)
for m in self.positions.move_list():
node = node.add_variation(chess.Move.from_uci(m))

for h in self.game.headers:
game.headers[h] = self.game.headers[h]
game.headers['Result'] = result
return game

def color(self):
return self.positions.position.turn

Expand Down
12 changes: 0 additions & 12 deletions opening_counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,8 @@
import chess.pgn

parser = argparse.ArgumentParser(description=__doc__)

parser.add_argument("threads", metavar="THREADS", nargs="?", type=int, default=4,
help="number of engine threads")
parser.add_argument("memory", metavar="MEMORY", nargs="?", type=int, default=2048,
help="memory in MB to use for engine hashtables")
parser.add_argument("--depth", metavar="DEPTH", nargs="?", type=int, default=8,
help="depth for stockfish analysis")
parser.add_argument("--quiet", dest="loglevel",
default=logging.DEBUG, action="store_const", const=logging.INFO,
help="substantially reduce the number of logged messages")
parser.add_argument("--games", metavar="GAMES", default="games.pgn",
help="A specific pgn with games")
parser.add_argument("--strict", metavar="STRICT", default=True,
help="If False then it will be generate more tactics but maybe a little ambiguous")
settings = parser.parse_args()

all_games = open(settings.games, "r")
Expand Down

0 comments on commit a78d15f

Please sign in to comment.