Skip to content

Commit

Permalink
Merge pull request #54 from VincentAuriau/v0.0.3
Browse files Browse the repository at this point in the history
V0.0.3
  • Loading branch information
VincentAuriau authored Nov 19, 2023
2 parents 4d99b52 + a11a1f2 commit 17fd9be
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 47 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/black_action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: black-action
on: [pull_request]
on:
pull_request:
branches:
- master

jobs:
linter_name:
name: runner / black
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: PyPI Release

on:
push:
tags:
- "*"

jobs:
release:
runs-on: ubuntu-latest

steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.6"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build the dist files
run: python setup.py sdist bdist_wheel
- name: Publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: twine upload dist/*
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ venv/
.idea/
.DS_Store
__pycache__/
settings.py
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
## PyAlapin, your customized chess engine
# PyAlapin, your customized chess engine
Is it the best, most efficient and state of the art chess engine ? I'm pretty sure not.

However, driven by passion and madness, I have developed my own chess game in Python.
For your pretty eyes and your devilish smile, I share it with you. But only with you.

Special thanks and dedication to LeMerluche, crushing its opponents on chess.com with alapin openings ❤️

## How to install
Simply use:
```bash
pip install pyalapin
```
You only need [numpy](https://numpy.org/) to play with with terminal interface and with Python.
You will need [kivy](https://kivy.org/) to play with the interface.

## How to play with interface
```python
from pyalapin.interface import ChessApp
Expand All @@ -16,9 +24,20 @@ if __name__ == '__main__':
).run()

```

![](docs/scholars_mate_interface.gif)


You can play against Stockfish by installing the official [Python interface](https://github.com/zhelyabuzhsky/stockfish).
```python
from pyalapin.player.player import Player
from pyalapin.player.stockfish_player import StockfishPlayer
from pyalapin.interface import ChessApp

sfp = StockfishPlayer(path_to_stockfish_engine, white_side=False)
app = ChessApp(w_player=Player(True), b_player=sfp, play_with_ai=True)
app.run()
```

## How to play with Python commands

<img align="right" src="docs/scholars_mate_command.gif">
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion pyalapin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"""
# from .interface import interface as interface

__version__ = "0.0.2"
__version__ = "0.0.3"
__author__ = "Vincent Auriau"
102 changes: 95 additions & 7 deletions pyalapin/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ def to_fen(self):
fen representation and 'KQkq'
"""
fen = ""
for line in self.board:
for line in reversed(self.board):
no_piece_count = 0
for cell in line:
piece = cell.get_piece()
Expand All @@ -609,13 +609,13 @@ def to_fen(self):
fen += str(no_piece_count)
no_piece_count = 0
letter = piece.get_str().replace(" ", "")
if piece.is_white():
letter = letter.lower()
# if piece.is_white():
# letter = letter.lower()
fen += letter
if no_piece_count > 0:
fen += str(no_piece_count)
fen += "/"
return fen[:-1], "KQkq"
return fen[:-1]

def one_hot_encode(self, white_side=True):
"""Method to create a representation of the board with OneHot encode of the pieces.
Expand Down Expand Up @@ -936,6 +936,11 @@ def draw(self, printing=True):
whole_text += current_line
whole_text += "\n"
whole_text += boarder_line
whole_text += "\n"
whole_text += " | a | b | c | d | e | f | g | h |"

whole_text += "\n"
whole_text += boarder_line
if printing:
print(whole_text + "\n")
return whole_text
Expand Down Expand Up @@ -1017,13 +1022,15 @@ def __init__(
self.history = []

self.automatic_draw = automatic_draw
self.half_move_clock = 0

def reset_game(self):
"""Method to reset the game. Recreates the borad, the pieces and restarts the game."""
self.board.reset()
self.played_moves = []
self.history = []
self.to_play_player = self.player1
self.half_move_clock = 0

def to_fen(self):
"""
Expand All @@ -1034,9 +1041,83 @@ def to_fen(self):
str
fen representation of the board.
"""
pieces, castling = self.board.to_fen()
board_fen = self.board.to_fen()
color_playing = "w" if self.to_play_player.is_white_side() else "b"
return pieces + " " + color_playing + " " + castling + " - 0 1"
castling = self.is_castling_possible(True) + self.is_castling_possible(False)
full_moves_nb = len(self.played_moves) // 2 + 1
return f"{board_fen} {color_playing} {castling} {self.fen_en_passant()} {full_moves_nb} {self.half_move_clock}"

def is_castling_possible(self, is_white_player):
"""Creates FEN representation of possible castling
Parameters
----------
is_white_player: bool
If castling possible checked for white player
Returns
-------
str:
FEN representation of possible castling
"""

fen_possible_castling = ""
if is_white_player:
piece = self.board.get_cell(0, 4).get_piece()
if not isinstance(piece, material.King):
return ""
elif piece.has_moved or piece.castling_done:
return ""
else:
kingside_rook = self.board.get_cell(0, 7).get_piece()
if isinstance(kingside_rook, material.Rook):
if not kingside_rook.has_moved:
fen_possible_castling += "K"
queenside_rook = self.board.get_cell(0, 0).get_piece()
if isinstance(kingside_rook, material.Rook):
if not kingside_rook.has_moved:
fen_possible_castling += "Q"
else:
piece = self.board.get_cell(0, 4).get_piece()
if not isinstance(piece, material.King):
return ""
elif piece.has_moved or piece.castling_done:
return ""
else:
kingside_rook = self.board.get_cell(7, 7).get_piece()
if isinstance(kingside_rook, material.Rook):
if not kingside_rook.has_moved:
fen_possible_castling += "k"
queenside_rook = self.board.get_cell(7, 0).get_piece()
if isinstance(kingside_rook, material.Rook):
if not kingside_rook.has_moved:
fen_possible_castling += "q"
return fen_possible_castling

def fen_en_passant(self):
"""
Creates the part of the FEN representation about En Passant.
Returns
-------
str
'-' or coordinate of en-passant cell if last move was double
"""
try:
last_move = self.played_moves[-1]
except:
return "-"
if not isinstance(last_move.move_piece, material.Pawn):
return "-"

dx = last_move.start.get_x() - last_move.end.get_x()
x_cell = 8 - int(last_move.start.get_x() - last_move.end.get_x()) / 2
y_cell = ["a", "b", "c", "d", "e", "f", "g", "h"][last_move.start.get_y()]

if dx == 2:
return f"{y_cell}{x_cell}"
else:
return "-"

def is_finished(self):
"""
Expand Down Expand Up @@ -1148,6 +1229,8 @@ def check_pat_mat(self, player):
can_player_move = self.can_player_move(player)

if can_player_move:
if self.half_move_clock >= 50:
return 1
return 0
# If player cannot move any piece
else:
Expand Down Expand Up @@ -1205,7 +1288,11 @@ def move(self, move, player):
else:
assert moved_piece.is_white() == player.is_white_side()
# Actually move pieces
move.move_pieces()
reset_half_move_clock = move.move_pieces()
if reset_half_move_clock:
self.half_move_clock = 0
else:
self.half_move_clock += 1

# Store move
self.played_moves.append(move)
Expand Down Expand Up @@ -1248,6 +1335,7 @@ def update_status(self):
"""
game_status = self.check_pat_mat(self.player1)
self.game_status.append(game_status)
# Pat
if game_status == 1:
return False, "black&white"
Expand Down
18 changes: 12 additions & 6 deletions pyalapin/engine/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,8 @@ def get_str(self):
str
String representation of the piece
"""
return " P "
repr = " P "
return repr if self.is_white() else repr.lower()


class Bishop(Piece):
Expand Down Expand Up @@ -661,7 +662,8 @@ def get_str(self):
str
String representation of the piece
"""
return " B "
repr = " B "
return repr if self.is_white() else repr.lower()


class Rook(Piece):
Expand Down Expand Up @@ -830,7 +832,8 @@ def get_str(self):
str
String representation of the piece
"""
return " R "
repr = " R "
return repr if self.is_white() else repr.lower()


class Knight(Piece):
Expand Down Expand Up @@ -927,7 +930,8 @@ def get_str(self):
str
String representation of the piece
"""
return " N "
repr = " N "
return repr if self.is_white() else repr.lower()

def get_potential_moves(self, x, y):
"""Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a
Expand Down Expand Up @@ -1178,7 +1182,8 @@ def get_str(self):
str
String representation of the piece
"""
return " Q "
repr = " Q "
return repr if self.is_white() else repr.lower()


class King(Piece):
Expand Down Expand Up @@ -1410,7 +1415,8 @@ def get_str(self):
str
String representation of the piece
"""
return " K "
repr = " K "
return repr if self.is_white() else repr.lower()

def is_checked(self, board):
"""Method to verify that the king at its current position is not threatened / checked by opponent material.
Expand Down
16 changes: 8 additions & 8 deletions pyalapin/engine/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,9 @@ def _is_castling(self):
Whether self is castling or not.
"""
if not isinstance(self.moved_piece, material.King):
###print("not castling becasuse king not moved")
return False

elif self.moved_piece.castling_done or self.moved_piece.has_moved:
###print(self.moved_piece.castling_done)
###print(self.moved_piece.has_moved)
return False

else:
Expand Down Expand Up @@ -215,7 +213,6 @@ def _is_castling(self):
self.board.get_cell(self.start.x, 4),
]
else:
###print('king did not move to a castling position')
return False

# Verifying that the cells between rook and king are empty and that starting
Expand Down Expand Up @@ -253,10 +250,6 @@ def _is_castling(self):
return True

else:
###print('Conditions for castling:')
###print('Rook has not moved:', rook_to_move.has_moved)
###print('Cells in between empty:', empty_cells_check)
###print('Cells in between not threatened:', not_threatened_cells)
return False

def _is_en_passant(self):
Expand Down Expand Up @@ -348,9 +341,15 @@ def move_pieces(self):
"""
# Do everything from coordinates so that only board needs to be copied in self.deepcopy() ?

if isinstance(self.moved_piece, material.Pawn):
reset_halfmove_clock = True
else:
reset_halfmove_clock = False

# Kills Piece on landing Cell if needed.
if self.killed_piece is not None:
self.board.kill_piece_from_coordinates((self.end.x, self.end.y))
reset_halfmove_clock = True
# Moves Piece on the Board
self.board.move_piece_from_coordinates(
(self.start.x, self.start.y), (self.end.x, self.end.y)
Expand All @@ -372,6 +371,7 @@ def move_pieces(self):

# Sets the different movement related attributes of Pieces
self._set_moved_attribute()
return reset_halfmove_clock

def is_possible_move(self, check_chess=True):
# REFONDRE, particulièrement, faire en sorte qu'on ne vérifie chaque condition qu'une seule fois
Expand Down
Loading

0 comments on commit 17fd9be

Please sign in to comment.