Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into multiple-move-com…
Browse files Browse the repository at this point in the history
…ments
  • Loading branch information
MarkZH committed May 26, 2024
2 parents f840f8e + eaa6eb3 commit 198e649
Show file tree
Hide file tree
Showing 6 changed files with 1,183 additions and 1,034 deletions.
268 changes: 199 additions & 69 deletions chess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,28 @@

from typing import ClassVar, Callable, Counter, Dict, Generic, Hashable, Iterable, Iterator, List, Literal, Mapping, Optional, SupportsInt, Tuple, Type, TypeVar, Union

if typing.TYPE_CHECKING:
from typing_extensions import TypeAlias


EnPassantSpec = Literal["legal", "fen", "xfen"]


Color = bool
COLORS = [WHITE, BLACK] = [True, False]
Color: TypeAlias = bool
WHITE: Color = True
BLACK: Color = False
COLORS: List[Color] = [WHITE, BLACK]
ColorName = Literal["white", "black"]
COLOR_NAMES: List[ColorName] = ["black", "white"]

PieceType = int
PIECE_TYPES = [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING] = range(1, 7)
PieceType: TypeAlias = int
PAWN: PieceType = 1
KNIGHT: PieceType = 2
BISHOP: PieceType = 3
ROOK: PieceType = 4
QUEEN: PieceType = 5
KING: PieceType = 6
PIECE_TYPES: List[PieceType] = [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING]
PIECE_SYMBOLS = [None, "p", "n", "b", "r", "q", "k"]
PIECE_NAMES = [None, "pawn", "knight", "bishop", "rook", "queen", "king"]

Expand Down Expand Up @@ -157,17 +169,72 @@ class AmbiguousMoveError(ValueError):
"""Raised when the attempted move is ambiguous in the current position"""


Square = int
SQUARES = [
A1, B1, C1, D1, E1, F1, G1, H1,
A2, B2, C2, D2, E2, F2, G2, H2,
A3, B3, C3, D3, E3, F3, G3, H3,
A4, B4, C4, D4, E4, F4, G4, H4,
A5, B5, C5, D5, E5, F5, G5, H5,
A6, B6, C6, D6, E6, F6, G6, H6,
A7, B7, C7, D7, E7, F7, G7, H7,
A8, B8, C8, D8, E8, F8, G8, H8,
] = range(64)
Square: TypeAlias = int
A1: Square = 0
B1: Square = 1
C1: Square = 2
D1: Square = 3
E1: Square = 4
F1: Square = 5
G1: Square = 6
H1: Square = 7
A2: Square = 8
B2: Square = 9
C2: Square = 10
D2: Square = 11
E2: Square = 12
F2: Square = 13
G2: Square = 14
H2: Square = 15
A3: Square = 16
B3: Square = 17
C3: Square = 18
D3: Square = 19
E3: Square = 20
F3: Square = 21
G3: Square = 22
H3: Square = 23
A4: Square = 24
B4: Square = 25
C4: Square = 26
D4: Square = 27
E4: Square = 28
F4: Square = 29
G4: Square = 30
H4: Square = 31
A5: Square = 32
B5: Square = 33
C5: Square = 34
D5: Square = 35
E5: Square = 36
F5: Square = 37
G5: Square = 38
H5: Square = 39
A6: Square = 40
B6: Square = 41
C6: Square = 42
D6: Square = 43
E6: Square = 44
F6: Square = 45
G6: Square = 46
H6: Square = 47
A7: Square = 48
B7: Square = 49
C7: Square = 50
D7: Square = 51
E7: Square = 52
F7: Square = 53
G7: Square = 54
H7: Square = 55
A8: Square = 56
B8: Square = 57
C8: Square = 58
D8: Square = 59
E8: Square = 60
F8: Square = 61
G8: Square = 62
H8: Square = 63
SQUARES: List[Square] = list(range(64))

SQUARE_NAMES = [f + r for r in RANK_NAMES for f in FILE_NAMES]

Expand Down Expand Up @@ -230,53 +297,106 @@ def square_mirror(square: Square) -> Square:
"""Mirrors the square vertically."""
return square ^ 0x38

SQUARES_180 = [square_mirror(sq) for sq in SQUARES]


Bitboard = int
BB_EMPTY = 0
BB_ALL = 0xffff_ffff_ffff_ffff

BB_SQUARES = [
BB_A1, BB_B1, BB_C1, BB_D1, BB_E1, BB_F1, BB_G1, BB_H1,
BB_A2, BB_B2, BB_C2, BB_D2, BB_E2, BB_F2, BB_G2, BB_H2,
BB_A3, BB_B3, BB_C3, BB_D3, BB_E3, BB_F3, BB_G3, BB_H3,
BB_A4, BB_B4, BB_C4, BB_D4, BB_E4, BB_F4, BB_G4, BB_H4,
BB_A5, BB_B5, BB_C5, BB_D5, BB_E5, BB_F5, BB_G5, BB_H5,
BB_A6, BB_B6, BB_C6, BB_D6, BB_E6, BB_F6, BB_G6, BB_H6,
BB_A7, BB_B7, BB_C7, BB_D7, BB_E7, BB_F7, BB_G7, BB_H7,
BB_A8, BB_B8, BB_C8, BB_D8, BB_E8, BB_F8, BB_G8, BB_H8,
] = [1 << sq for sq in SQUARES]

BB_CORNERS = BB_A1 | BB_H1 | BB_A8 | BB_H8
BB_CENTER = BB_D4 | BB_E4 | BB_D5 | BB_E5

BB_LIGHT_SQUARES = 0x55aa_55aa_55aa_55aa
BB_DARK_SQUARES = 0xaa55_aa55_aa55_aa55

BB_FILES = [
BB_FILE_A,
BB_FILE_B,
BB_FILE_C,
BB_FILE_D,
BB_FILE_E,
BB_FILE_F,
BB_FILE_G,
BB_FILE_H,
] = [0x0101_0101_0101_0101 << i for i in range(8)]

BB_RANKS = [
BB_RANK_1,
BB_RANK_2,
BB_RANK_3,
BB_RANK_4,
BB_RANK_5,
BB_RANK_6,
BB_RANK_7,
BB_RANK_8,
] = [0xff << (8 * i) for i in range(8)]

BB_BACKRANKS = BB_RANK_1 | BB_RANK_8
SQUARES_180: List[Square] = [square_mirror(sq) for sq in SQUARES]


Bitboard: TypeAlias = int
BB_EMPTY: Bitboard = 0
BB_ALL: Bitboard = 0xffff_ffff_ffff_ffff

BB_A1: Bitboard = 1 << A1
BB_B1: Bitboard = 1 << B1
BB_C1: Bitboard = 1 << C1
BB_D1: Bitboard = 1 << D1
BB_E1: Bitboard = 1 << E1
BB_F1: Bitboard = 1 << F1
BB_G1: Bitboard = 1 << G1
BB_H1: Bitboard = 1 << H1
BB_A2: Bitboard = 1 << A2
BB_B2: Bitboard = 1 << B2
BB_C2: Bitboard = 1 << C2
BB_D2: Bitboard = 1 << D2
BB_E2: Bitboard = 1 << E2
BB_F2: Bitboard = 1 << F2
BB_G2: Bitboard = 1 << G2
BB_H2: Bitboard = 1 << H2
BB_A3: Bitboard = 1 << A3
BB_B3: Bitboard = 1 << B3
BB_C3: Bitboard = 1 << C3
BB_D3: Bitboard = 1 << D3
BB_E3: Bitboard = 1 << E3
BB_F3: Bitboard = 1 << F3
BB_G3: Bitboard = 1 << G3
BB_H3: Bitboard = 1 << H3
BB_A4: Bitboard = 1 << A4
BB_B4: Bitboard = 1 << B4
BB_C4: Bitboard = 1 << C4
BB_D4: Bitboard = 1 << D4
BB_E4: Bitboard = 1 << E4
BB_F4: Bitboard = 1 << F4
BB_G4: Bitboard = 1 << G4
BB_H4: Bitboard = 1 << H4
BB_A5: Bitboard = 1 << A5
BB_B5: Bitboard = 1 << B5
BB_C5: Bitboard = 1 << C5
BB_D5: Bitboard = 1 << D5
BB_E5: Bitboard = 1 << E5
BB_F5: Bitboard = 1 << F5
BB_G5: Bitboard = 1 << G5
BB_H5: Bitboard = 1 << H5
BB_A6: Bitboard = 1 << A6
BB_B6: Bitboard = 1 << B6
BB_C6: Bitboard = 1 << C6
BB_D6: Bitboard = 1 << D6
BB_E6: Bitboard = 1 << E6
BB_F6: Bitboard = 1 << F6
BB_G6: Bitboard = 1 << G6
BB_H6: Bitboard = 1 << H6
BB_A7: Bitboard = 1 << A7
BB_B7: Bitboard = 1 << B7
BB_C7: Bitboard = 1 << C7
BB_D7: Bitboard = 1 << D7
BB_E7: Bitboard = 1 << E7
BB_F7: Bitboard = 1 << F7
BB_G7: Bitboard = 1 << G7
BB_H7: Bitboard = 1 << H7
BB_A8: Bitboard = 1 << A8
BB_B8: Bitboard = 1 << B8
BB_C8: Bitboard = 1 << C8
BB_D8: Bitboard = 1 << D8
BB_E8: Bitboard = 1 << E8
BB_F8: Bitboard = 1 << F8
BB_G8: Bitboard = 1 << G8
BB_H8: Bitboard = 1 << H8
BB_SQUARES: List[Bitboard] = [1 << sq for sq in SQUARES]

BB_CORNERS: Bitboard = BB_A1 | BB_H1 | BB_A8 | BB_H8
BB_CENTER: Bitboard = BB_D4 | BB_E4 | BB_D5 | BB_E5

BB_LIGHT_SQUARES: Bitboard = 0x55aa_55aa_55aa_55aa
BB_DARK_SQUARES: Bitboard = 0xaa55_aa55_aa55_aa55

BB_FILE_A: Bitboard = 0x0101_0101_0101_0101 << 0
BB_FILE_B: Bitboard = 0x0101_0101_0101_0101 << 1
BB_FILE_C: Bitboard = 0x0101_0101_0101_0101 << 2
BB_FILE_D: Bitboard = 0x0101_0101_0101_0101 << 3
BB_FILE_E: Bitboard = 0x0101_0101_0101_0101 << 4
BB_FILE_F: Bitboard = 0x0101_0101_0101_0101 << 5
BB_FILE_G: Bitboard = 0x0101_0101_0101_0101 << 6
BB_FILE_H: Bitboard = 0x0101_0101_0101_0101 << 7
BB_FILES: List[Bitboard] = [BB_FILE_A, BB_FILE_B, BB_FILE_C, BB_FILE_D, BB_FILE_E, BB_FILE_F, BB_FILE_G, BB_FILE_H]

BB_RANK_1: Bitboard = 0xff << (8 * 0)
BB_RANK_2: Bitboard = 0xff << (8 * 1)
BB_RANK_3: Bitboard = 0xff << (8 * 2)
BB_RANK_4: Bitboard = 0xff << (8 * 3)
BB_RANK_5: Bitboard = 0xff << (8 * 4)
BB_RANK_6: Bitboard = 0xff << (8 * 5)
BB_RANK_7: Bitboard = 0xff << (8 * 6)
BB_RANK_8: Bitboard = 0xff << (8 * 7)
BB_RANKS: List[Bitboard] = [BB_RANK_1, BB_RANK_2, BB_RANK_3, BB_RANK_4, BB_RANK_5, BB_RANK_6, BB_RANK_7, BB_RANK_8]

BB_BACKRANKS: Bitboard = BB_RANK_1 | BB_RANK_8


def lsb(bb: Bitboard) -> int:
Expand Down Expand Up @@ -393,9 +513,9 @@ def _sliding_attacks(square: Square, occupied: Bitboard, deltas: Iterable[int])
def _step_attacks(square: Square, deltas: Iterable[int]) -> Bitboard:
return _sliding_attacks(square, BB_ALL, deltas)

BB_KNIGHT_ATTACKS = [_step_attacks(sq, [17, 15, 10, 6, -17, -15, -10, -6]) for sq in SQUARES]
BB_KING_ATTACKS = [_step_attacks(sq, [9, 8, 7, 1, -9, -8, -7, -1]) for sq in SQUARES]
BB_PAWN_ATTACKS = [[_step_attacks(sq, deltas) for sq in SQUARES] for deltas in [[-7, -9], [7, 9]]]
BB_KNIGHT_ATTACKS: List[Bitboard] = [_step_attacks(sq, [17, 15, 10, 6, -17, -15, -10, -6]) for sq in SQUARES]
BB_KING_ATTACKS: List[Bitboard] = [_step_attacks(sq, [9, 8, 7, 1, -9, -8, -7, -1]) for sq in SQUARES]
BB_PAWN_ATTACKS: List[List[Bitboard]] = [[_step_attacks(sq, deltas) for sq in SQUARES] for deltas in [[-7, -9], [7, 9]]]


def _edges(square: Square) -> Bitboard:
Expand Down Expand Up @@ -2619,9 +2739,7 @@ def _epd_operations(self, operations: Mapping[str, Union[None, str, int, float,
first_op = True

for opcode, operand in operations.items():
assert opcode != "-", "dash (-) is not a valid epd opcode"
for blacklisted in [" ", "\n", "\t", "\r"]:
assert blacklisted not in opcode, f"invalid character {blacklisted!r} in epd opcode: {opcode!r}"
self._validate_epd_opcode(opcode)

if not first_op:
epd.append(" ")
Expand Down Expand Up @@ -2699,6 +2817,17 @@ def epd(self, *, shredder: bool = False, en_passant: EnPassantSpec = "legal", pr

return " ".join(epd)

def _validate_epd_opcode(self, opcode: str) -> None:
if not opcode:
raise ValueError("empty string is not a valid epd opcode")
if opcode == "-":
raise ValueError("dash (-) is not a valid epd opcode")
if not opcode[0].isalpha():
raise ValueError(f"expected epd opcode to start with a letter, got: {opcode!r}")
for blacklisted in [" ", "\n", "\t", "\r"]:
if blacklisted in opcode:
raise ValueError(f"invalid character {blacklisted!r} in epd opcode: {opcode!r}")

def _parse_epd_ops(self: BoardT, operation_part: str, make_board: Callable[[], BoardT]) -> Dict[str, Union[None, str, int, float, Move, List[Move]]]:
operations: Dict[str, Union[None, str, int, float, Move, List[Move]]] = {}
state = "opcode"
Expand All @@ -2712,6 +2841,7 @@ def _parse_epd_ops(self: BoardT, operation_part: str, make_board: Callable[[], B
if opcode == "-":
opcode = ""
elif opcode:
self._validate_epd_opcode(opcode)
state = "after_opcode"
elif ch is None or ch == ";":
if opcode == "-":
Expand Down Expand Up @@ -3816,7 +3946,7 @@ def __repr__(self) -> str:
return f"<LegalMoveGenerator at {id(self):#x} ({sans})>"


IntoSquareSet = Union[SupportsInt, Iterable[Square]]
IntoSquareSet: TypeAlias = Union[SupportsInt, Iterable[Square]]

class SquareSet:
"""
Expand Down
17 changes: 15 additions & 2 deletions chess/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from types import TracebackType
from typing import Any, Callable, Coroutine, Deque, Dict, Generator, Generic, Iterable, Iterator, List, Literal, Mapping, MutableMapping, Optional, Tuple, Type, TypedDict, TypeVar, Union

WdlModel = Literal["sf", "sf16", "sf15.1", "sf15", "sf14", "sf12", "lichess"]
WdlModel = Literal["sf", "sf16.1", "sf16", "sf15.1", "sf15", "sf14", "sf12", "lichess"]


T = TypeVar("T")
Expand Down Expand Up @@ -516,6 +516,16 @@ def __ge__(self, other: object) -> bool:
else:
return NotImplemented

def _sf16_1_wins(cp: int, *, ply: int) -> int:
# https://github.com/official-stockfish/Stockfish/blob/sf_16.1/src/uci.cpp#L48
NormalizeToPawnValue = 356
# https://github.com/official-stockfish/Stockfish/blob/sf_16.1/src/uci.cpp#L383-L384
m = min(120, max(8, ply / 2 + 1)) / 32
a = (((-1.06249702 * m + 7.42016937) * m + 0.89425629) * m) + 348.60356174
b = (((-5.33122190 * m + 39.57831533) * m + -90.84473771) * m) + 123.40620748
x = min(4000, max(cp * NormalizeToPawnValue / 100, -4000))
return int(0.5 + 1000 / (1 + math.exp((a - x) / b)))

def _sf16_wins(cp: int, *, ply: int) -> int:
# https://github.com/official-stockfish/Stockfish/blob/sf_16/src/uci.h#L38
NormalizeToPawnValue = 328
Expand Down Expand Up @@ -594,9 +604,12 @@ def wdl(self, *, model: WdlModel = "sf", ply: int = 30) -> Wdl:
elif model == "sf15.1":
wins = _sf15_1_wins(self.cp, ply=ply)
losses = _sf15_1_wins(-self.cp, ply=ply)
else:
elif model == "sf16":
wins = _sf16_wins(self.cp, ply=ply)
losses = _sf16_wins(-self.cp, ply=ply)
else:
wins = _sf16_1_wins(self.cp, ply=ply)
losses = _sf16_1_wins(-self.cp, ply=ply)
draws = 1000 - wins - losses
return Wdl(wins, draws, losses)

Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Sphinx==6.1.2
Sphinx==7.3.6
sphinxcontrib-jquery==4.1
sphinx-rtd-theme==1.3.0
Loading

0 comments on commit 198e649

Please sign in to comment.