Source code for cshogi.PGN

from typing import List, Optional
import cshogi
from datetime import datetime

PGN_SQUARE_NAMES = [
    'i9', 'i8', 'i7', 'i6', 'i5', 'i4', 'i3', 'i2', 'i1',
    'h9', 'h8', 'h7', 'h6', 'h5', 'h4', 'h3', 'h2', 'h1',
    'g9', 'g8', 'g7', 'g6', 'g5', 'g4', 'g3', 'g2', 'g1',
    'f9', 'f8', 'f7', 'f6', 'f5', 'f4', 'f3', 'f2', 'f1',
    'e9', 'e8', 'e7', 'e6', 'e5', 'e4', 'e3', 'e2', 'e1',
    'd9', 'd8', 'd7', 'd6', 'd5', 'd4', 'd3', 'd2', 'd1',
    'c9', 'c8', 'c7', 'c6', 'c5', 'c4', 'c3', 'c2', 'c1',
    'b9', 'b8', 'b7', 'b6', 'b5', 'b4', 'b3', 'b2', 'b1',
    'a9', 'a8', 'a7', 'a6', 'a5', 'a4', 'a3', 'a2', 'a1',
]

PGN_HAND_PIECES = [
    'P', 'L', 'N', 'S', 'G', 'B', 'R',
]

PGN_PIECE_TYPES = [
    None,
    'P', 'L', 'N', 'S', 'B', 'R', 'G', 'K',
    '+P', '+L', '+K', '+S', '+B', '+R',
]

[docs]def move_to_san(move): move_to = PGN_SQUARE_NAMES[cshogi.move_to(move)] if cshogi.move_is_drop(move): return PGN_HAND_PIECES[cshogi.move_drop_hand_piece(move)] + '@' + move_to move_from = PGN_SQUARE_NAMES[cshogi.move_from(move)] promotion = '+' if cshogi.move_is_promotion(move) else '' return PGN_PIECE_TYPES[cshogi.move_from_piece_type(move)] + move_from + move_to + promotion
[docs]class Exporter: """A class to handle the exporting of a game to PGN (Portable Game Notation) format. :param path: Optional path to the file where the PGN formatted game will be written. If None, no file is opened initially. :param append: Whether to append to the existing file or create a new one. Defaults to False. """ def __init__(self, path: Optional[str] = None, append: bool = False): if path: self.open(path, append) else: self.f = None
[docs] def open(self, path: str, append: bool = False): """Open a file for writing the PGN formatted game. :param path: Path to the file. :param append: Whether to append to the existing file or create a new one. Defaults to False. """ self.f = open(path, 'a' if append else 'w', newline='\n')
[docs] def close(self): """Close the file.""" self.f.close()
[docs] def tag_pair(self, names: List[str], result: int, event: str = '?', site: str = '?', starttime: Optional[datetime] = None, round: int = 1): """Write the tag pairs section of the PGN format, containing metadata about the game. :param names: List of player names for White and Black. :param result: Game result code. :param event: Event name, defaults to "?". :param site: Site of the game, defaults to "?". :param starttime: Start time of the game, defaults to current time. :param round: Round number, defaults to 1. """ if starttime is None: starttime = datetime.now() self.f.write('[Event "' + event +'"]\n') self.f.write('[Site "' + site + '"]\n') self.f.write('[Date "' + (starttime.strftime('%Y.%m.%d') if starttime else '????.??.??') + '"]\n') self.f.write('[Round "' + str(round) + '"]\n') self.f.write('[White "' + names[0] + '"]\n') self.f.write('[Black "' + names[1] + '"]\n') if result == cshogi.BLACK_WIN: self.result_str = '1-0' elif result == cshogi.WHITE_WIN: self.result_str = '0-1' elif result == cshogi.DRAW: self.result_str = '1/2-1/2' else: self.result_str = '*' self.f.write('[Result "' + self.result_str + '"]\n\n') self.f.flush()
[docs] def movetext(self, moves: List[int]): """Write the move text section of the PGN format, containing the actual moves of the game. :param moves: List of moves in the game. """ line = '' for i, move in enumerate(moves): if i % 2 == 0: part = str(i // 2 + 1) + '. ' else: part = '' part += move_to_san(moves[i]) if i + 1 == len(moves): part += ' ' + self.result_str if len(line) + len(part) <= 80: if line != '': line += ' ' line += part else: self.f.write(line + '\n') line = part self.f.write(line + '\n\n') self.f.flush()