Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V0.0.3 #54

Merged
merged 36 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
28a0a98
ADD: pip install in readme
VincentAuriau Nov 7, 2023
4f039d7
ADD: StockFish Player support
VincentAuriau Nov 10, 2023
b26ef33
small fixes
VincentAuriau Nov 13, 2023
4cd8f22
FIX: StockfishPlayer has now right coordinates
VincentAuriau Nov 13, 2023
a5a7ff6
ADD: local stockfish test example
VincentAuriau Nov 13, 2023
ba09da5
ADD: stockfish in dependancies
VincentAuriau Nov 13, 2023
ef19d14
env mangemetn
VincentAuriau Nov 13, 2023
c2d9585
ADD: black material str is now in lowercase
VincentAuriau Nov 15, 2023
54930aa
ADD: halfmove clock reset checks in Move
VincentAuriau Nov 15, 2023
95ce2e2
ENH: FEN now exactly generated'
VincentAuriau Nov 15, 2023
d70af82
ADD: min missing doc
VincentAuriau Nov 15, 2023
27a5c20
Merge pull request #47 from VincentAuriau/enh-doc
VincentAuriau Nov 17, 2023
356830e
setup files go to v0.0.3
VincentAuriau Nov 17, 2023
f7a612d
ADD: automated release github action
VincentAuriau Nov 17, 2023
a83d23b
:art: Format Python code with psf/black
VincentAuriau Nov 17, 2023
80c0623
ADD: letters coordinates on board
VincentAuriau Nov 17, 2023
79ff5bc
Merge pull request #50 from VincentAuriau/actions/black
VincentAuriau Nov 17, 2023
f457535
Merge pull request #51 from VincentAuriau/enh-draw
VincentAuriau Nov 17, 2023
a95f155
Merge branch 'v0.0.3' into stockfish
VincentAuriau Nov 17, 2023
78f0e9c
:art: Format Python code with psf/black
VincentAuriau Nov 17, 2023
6fbc102
Merge pull request #53 from VincentAuriau/actions/black
VincentAuriau Nov 17, 2023
946fbcb
Update black_action.yml
VincentAuriau Nov 17, 2023
335cc4b
Merge branch 'v0.0.3' of github.com:VincentAuriau/pyalapin into stock…
VincentAuriau Nov 17, 2023
918a47c
FIX: board.__str__
VincentAuriau Nov 19, 2023
037cce7
ADD: possibility to add StockFish player and specify players in inter…
VincentAuriau Nov 19, 2023
73ba6c2
ADD: stockfish example
VincentAuriau Nov 19, 2023
d813f70
merge
VincentAuriau Nov 19, 2023
0959a2f
ADD: settings file
VincentAuriau Nov 19, 2023
5c49a34
update readme
VincentAuriau Nov 19, 2023
30b4c6c
Merge pull request #49 from VincentAuriau/stockfish
VincentAuriau Nov 19, 2023
74dc6ec
:art: Format Python code with psf/black
VincentAuriau Nov 19, 2023
40d966f
ADD: engine tests from new fen method
VincentAuriau Nov 19, 2023
ff5db54
Merge branch 'stockfish' into v0.0.3
VincentAuriau Nov 19, 2023
9b560aa
Merge pull request #55 from VincentAuriau/actions/black
VincentAuriau Nov 19, 2023
f14d124
:art: Format Python code with psf/black
VincentAuriau Nov 19, 2023
a11a1f2
Merge pull request #56 from VincentAuriau/actions/black
VincentAuriau Nov 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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