Source code for draughts.move

from __future__ import annotations

from typing import Generator

from draughts.models import Figure


[docs] class Move: """Move representation. End user should never interact with this class directly. As we can read on wikipedia: *Multiple jumps, such as a double or triple jump, require you to pay attention, as the convention is to just show the start and end squares and not the in-between or intermediate squares. So the notation 1-3 would mean a King does a double jump from 1 to 10 to 3. The intermediate square is only shown if there are two ways to jump and it would not be clear otherwise.* Note that always: n - number of visited squares (include source square) n - 2 - number of captured pieces """ def __init__( self, visited_squares: list[int], captured_list: list[int] = [], captured_entities: list[Figure.value] = [], is_promotion: bool = False, ) -> None: self.square_list = visited_squares self.captured_list = captured_list self.captured_entities = captured_entities self.is_promotion = is_promotion self.halfmove_clock = 0 def __str__(self) -> str: separator = "x" if self.captured_list else "-" return f"{self.square_list[0] + 1}{separator}{self.square_list[-1] + 1}" def __repr__(self) -> str: visited_squares = [str(s + 1) for s in self.square_list] return f"Move: {'->'.join(visited_squares)}" def __eq__(self, other: object) -> bool: """Check if two moves are equal. move created from string will have only visited squares definied.""" if not isinstance(other, Move): return False if ( self.square_list[0] == other.square_list[0] and self.square_list[-1] == other.square_list[-1] ): longer = ( self.square_list if len(self.square_list) >= len(other.square_list) else other.square_list ) shorter = ( self.square_list if len(self.square_list) < len(other.square_list) else other.square_list ) return all(square in longer for square in shorter) return False def __len__(self) -> int: return len(self.captured_list) + 1 def __add__(self, other: Move) -> Move: """Append moves""" if self.square_list[-1] != other.square_list[0]: raise ValueError( f"Cannot append moves {self} and {other}. Last square of first move should be equal to first square of second move." ) return Move( self.square_list + other.square_list[1:], self.captured_list + other.captured_list, self.captured_entities + other.captured_entities, self.is_promotion, )
[docs] @classmethod def from_uci(cls, move: str, legal_moves: Generator) -> Move: """ Converts string representation of move to ``Move`` object. This is generic method, so it can be used for any board size. Therefore, For different context different move object will be generated. Also we need to pass legal moves, to understand given move and check if it is legal. input format: * ``<square_number>-<square_number>`` for simple move * ``<square_number>x<square_number>`` for capture Examples: * ``24-19`` - means from 24 to 19 * ``24x19`` - means from 24 to 16 means capture of piece between 24 and 16 * ``1x10x19`` - means capture of two pieces between 1 and 19 """ move = move.lower() if "-" in move: # classic move steps = move.split("-") elif "x" in move: # this means capture steps = move.split("x") else: raise ValueError(f"Invalid move {move}.") move = Move([int(step) - 1 for step in steps]) for legal_move in legal_moves: if legal_move == move: return legal_move raise ValueError( f"{str(move)} is correct, but not legal in given position.\n Legal moves are: {list(map(str,legal_moves))}" )