Skip to content

Commit

Permalink
ENH pawn promotion (#11)
Browse files Browse the repository at this point in the history
* ADD: Pawn DocString

* ADD: material main classes descriptions

* ADD: full docstring for material

* 🎨 Format Python code with psf/black

* ADD: better test CI organization

* 🎨 Format Python code with psf/black

* Update ci.yaml

* Update ci.yaml

* ADD: dev specific env

* Update ci.yaml

* Create pyproject.toml

* Update ci.yaml

* Update ci.yaml

* Update pyproject.toml

* FIX: missing dev packages

* Update requirements-dev.txt

* Update requirements-dev.txt

* edit CI

CI now runs on python 3.9.2

* removed pywin32 version in requirements

* rm pywin32 from requirements

* rm pypiwin32

* fix .toml

* add pytest to requirements

* rm tensorflow import

* 🎨 Format Python code with psf/black (#10)

Co-authored-by: VincentAuriau <[email protected]>

* ENH: restructure of pawn promotion

* ADD: tests for pawn promotion

---------

Co-authored-by: VincentAuriau <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 22, 2023
1 parent 6e93540 commit ab49c96
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 22 deletions.
24 changes: 14 additions & 10 deletions python/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,6 @@ def kill_piece_from_coordinates(self, coordinates):
def transform_pawn(self, coordinates):
pawn = self.get_cell(coordinates[0], coordinates[1]).get_piece()
if not isinstance(pawn, material.Pawn):
###print(pawn)
raise ValueError("Transforming piece that is not a Pawn")
else:
color = "white" if pawn.is_white() else "black"
Expand All @@ -585,6 +584,18 @@ def transform_pawn(self, coordinates):
self.get_cell(pawn.x, pawn.y).set_piece(new_queen)
self.all_material[color]["alive"]["queen"].append(new_queen)

def promote_pawn(self, coordinates, promote_into="Queen"):
pawn = self.get_cell(coordinates[0], coordinates[1]).get_piece()
if not isinstance(pawn, material.Pawn):
raise ValueError("Transforming piece that is not a Pawn")
else:
color = "white" if pawn.is_white() else "black"
self.all_material[color]["alive"][pawn.type].remove(pawn)

new_piece = pawn.promote(promote_into=promote_into)
self.get_cell(pawn.x, pawn.y).set_piece(new_piece)
self.all_material[color]["alive"]["queen"].append(new_piece)

def draw(self, printing=True):
whole_text = " | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |"
# ###print(' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |')
Expand Down Expand Up @@ -644,11 +655,11 @@ def to_fen(self):
def is_finished(self):
return self.status != "ACTIVE"

def move_from_coordinates(self, player, start_x, start_y, end_x, end_y):
def move_from_coordinates(self, player, start_x, start_y, end_x, end_y, extras={}):
start_cell = self.board.get_cell(start_x, start_y)
end_cell = self.board.get_cell(end_x, end_y)

move = Move(player, self.board, start_cell, end_cell)
move = Move(player, self.board, start_cell, end_cell, extras=extras)

return self.move(move, player)

Expand Down Expand Up @@ -703,22 +714,17 @@ def move(self, move, player):
# List of checks
# To change if castling or en passant move
if moved_piece is None:
###print('There is no moved piece, move is aborted')
return False, 0
assert moved_piece is not None

if player != self.to_play_player:
###print('The wrong player has played, move is aborted')
###print(self.to_play_player, 'supposed to play and', player, 'has played')
return False, 0
assert player == self.to_play_player

allowed_move = move.is_possible_move()
if not allowed_move:
###print('Move method checking legality of move rejected it')
return False, 0
elif moved_piece.is_white() != player.is_white_side():
###print("Engine detected player to move other side piece")
return False, 0

else:
Expand All @@ -743,8 +749,6 @@ def move(self, move, player):
print("END OF THE GAME, WHITE HAS WON")
return False, "white"

###print('PLAYER TO PLAY:', self.to_play_player)

# Checking for PAT & MAT
check_status, winner = self.update_status()

Expand Down
32 changes: 31 additions & 1 deletion python/engine/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,37 @@ def get_potential_moves(self, x, y):
return possible_moves

def promote(self, promote_into="Queen"):
raise NotImplementedError
"""Method to promote a pawn to other material type. Only happens if the pawn reaches the other side of the board.
The player can choose which type of material he wants its pawn to be promoted into.
Parameters
----------
promote_into: str
Type of material to promote the pawn into.
Returns
-------
Piece
New piece with right promotion and same coordinates of current pawn.
"""
# Should we verify color and coordinates of Pawn ?
if self.is_white():
assert self.x == 7, "Pawn has not reached other side of the board"
else:
assert self.x == 0, "Pawn has not reached the other side of the board"

if promote_into.lower() == "queen":
return Queen(white=self.is_white(), x=self.x, y=self.y)
elif promote_into.lower() == "rook":
rook = Rook(white=self.is_white(), x=self.x, y=self.y)
rook.has_moved = True
return rook
elif promote_into == "knight":
return Knight(white=self.is_white(), x=self.x, y=self.y)
elif promote_into == "bishop":
return Bishop(white=self.is_white(), x=self.x, y=self.y)
else:
raise ValueError(f"Cannot promote piece into, {promote_into}, piece unknown")

def get_str(self):
"""Method to represent the piece as a string.
Expand Down
21 changes: 12 additions & 9 deletions python/engine/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@


class Move:
def __init__(self, player, board, start, end):
def __init__(self, player, board, start, end, extras={}):
self.player = player
self.board = board
self.start = start
self.end = end
self.extras = extras

self.moved_piece = start.get_piece()
if self.moved_piece is None:
###print("Empty cell selected as start of a move")
Expand All @@ -18,7 +20,6 @@ def __init__(self, player, board, start, end):
self.is_castling = False
self.complementary_castling = None
self.en_passant = False
self.transform_pawn = False

def deepcopy(self):
copied_board = self.board.deepcopy()
Expand All @@ -31,7 +32,6 @@ def deepcopy(self):
copied_move.is_castling = self.is_castling
copied_move.complementary_castling = self.complementary_castling
copied_move.en_passant = self.en_passant
copied_move.transform_pawn = self.transform_pawn
return copied_move

def _set_moved_attribute(self):
Expand Down Expand Up @@ -162,21 +162,24 @@ def _is_en_passant(self):
else:
return False

def _is_pawn_transformation(self):
def _is_pawn_promotion(self):
if not isinstance(self.moved_piece, material.Pawn):
return False
else:
if self.end.get_x() == 7 and self.moved_piece.is_white():
self.promote_into = self.extras.get("promote_into", "queen")
print(self.extras)
return True
elif self.end.get_x() == 0 and not self.moved_piece.is_white():
self.promote_into = self.extras.get("promote_into", "queen")
return True
else:
return False

def _transform_pawn(self):
###print("TRANSFORM PAWN 2", self.moved_piece, self.moved_piece.is_white())
def _promote_pawn(self):
coordinates = (self.end.get_x(), self.end.get_y())
self.board.transform_pawn(coordinates)
print("promote into", self.promote_into)
self.board.promote_pawn(coordinates=coordinates, promote_into=self.promote_into)

def move_pieces(self):
"""
Expand All @@ -198,8 +201,8 @@ def move_pieces(self):
###print("CASTLING DETECTED PPPPPPPPP")
self._set_castling_done()

if self._is_pawn_transformation():
self._transform_pawn()
if self._is_pawn_promotion():
self._promote_pawn()
self._set_moved_attribute()

def is_possible_move(self): # REFONDRE
Expand Down
38 changes: 36 additions & 2 deletions tests/unit_test/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,43 @@ def test_blocked_moves():
"""
Test that a blocked move does not happen.
"""
print("ok")
game = engine.Game(automatic_draw=False)
game.move_from_coordinates(game.player1, 1, 4, 3, 4)
_, winner = game.move_from_coordinates(game.player2, 7, 0, 5, 0)
print("win", winner)
assert winner == 0

def test_promotion_to_rook():
"""
Test that the promotion works well
"""
game = engine.Game(automatic_draw=False)
game.move_from_coordinates(game.player1, 1, 4, 3, 4)
game.move_from_coordinates(game.player2, 6, 5, 4, 5)
game.move_from_coordinates(game.player1, 3, 4, 4, 5)
game.move_from_coordinates(game.player2, 7, 6, 5, 7)
game.move_from_coordinates(game.player1, 4, 5, 5, 5)
game.move_from_coordinates(game.player2, 6, 0, 5, 0)
game.move_from_coordinates(game.player1, 5, 5, 6, 6)
game.move_from_coordinates(game.player2, 5, 0, 4, 0)
game.move_from_coordinates(game.player1, 6, 6, 7, 6, extras={"promote_into": "rook"})
assert game.board.to_fen()[0] == "rnbqkbnr/pppp1ppp/8/8/P7/7N/1PPPP2P/RNBQKBrR"


def test_default_promotion():
"""
Test that the promotion works well
"""
game = engine.Game(automatic_draw=False)
game.move_from_coordinates(game.player1, 1, 4, 3, 4)
game.move_from_coordinates(game.player2, 6, 5, 4, 5)
game.move_from_coordinates(game.player1, 3, 4, 4, 5)
game.move_from_coordinates(game.player2, 7, 6, 5, 7)
game.move_from_coordinates(game.player1, 4, 5, 5, 5)
game.move_from_coordinates(game.player2, 6, 0, 5, 0)
game.move_from_coordinates(game.player1, 5, 5, 6, 6)
game.move_from_coordinates(game.player2, 5, 0, 4, 0)
game.move_from_coordinates(game.player1, 6, 6, 7, 6)
assert game.board.to_fen()[0] == "rnbqkbnr/pppp1ppp/8/8/P7/7N/1PPPP2P/RNBQKBqR"

if __name__ == "__main__":
pass

0 comments on commit ab49c96

Please sign in to comment.