├── .gitignore
├── MIT.md
├── Pipfile
├── Pipfile.lock
├── README.md
├── assets
├── bb.png
├── be.png
├── bk.png
├── blank.png
├── bn.png
├── bp.png
├── bq.png
├── br.png
├── wb.png
├── we.png
├── wk.png
├── wn.png
├── wp.png
├── wq.png
└── wr.png
├── game
├── __init__.py
├── board.py
├── gui.py
└── tile.py
├── main.py
└── screenshots
├── chess_gui_demo.gif
└── chess_gui_demo2.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | *.exe
--------------------------------------------------------------------------------
/MIT.md:
--------------------------------------------------------------------------------
1 | Copyright (c) Hein Thant
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | pysimplegui = "*"
8 | chess = "*"
9 |
10 | [dev-packages]
11 |
12 | [requires]
13 | python_version = "3.10"
14 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "4283dce8b772f68ee1db0f709c6faf268b02c4dc523a3c8c01b8aff7f02d847e"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.10"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "chess": {
20 | "hashes": [
21 | "sha256:6a430cd4666ad6c757d62576fb3ae2112502620922fd904df1af680dfcd07692",
22 | "sha256:a27c45707671d413d045ab2ee8b765c98bfb1bf7ba093a704cfed7e4f758fd58"
23 | ],
24 | "index": "pypi",
25 | "version": "==1.9.3"
26 | },
27 | "pysimplegui": {
28 | "hashes": [
29 | "sha256:e133fbd21779f0f125cebbc2a4e1f5a931a383738661013ff33ad525d5611eda",
30 | "sha256:f88c82c301a51aea35be605dc060bcceb0dcb6682e16280544884701ab4b23ba"
31 | ],
32 | "index": "pypi",
33 | "version": "==4.60.4"
34 | }
35 | },
36 | "develop": {}
37 | }
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.codefactor.io/repository/github/indiecodermm/chess-gui)
2 | [](https://opensource.org/licenses/MIT)
3 |
4 | # ♟ Simple Chess GUI
5 |
6 | A simple, user-friendly chess graphical user interface (GUI) built using [PySimpleGUI](https://www.pysimplegui.org/en/latest/).
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## 🎨 Features
14 |
15 | - Play against a friend
16 | - User-friendly, intuitive interface
17 | - Legal moves highlighter
18 | - All chess moves are available:
19 | - Castling | Pawn Promotion | En passant
20 |
21 |
22 |
23 | ## ⚙ Tools
24 |
25 | - [Python Chess](https://python-chess.readthedocs.io/en/latest/): Move generations and validations are made using this powerful chess library
26 | - [PySimpleGUI](https://www.pysimplegui.org/en/latest/): Beautiful GUI is created within a few lines of code by the help of this amazing library
27 |
28 |
29 | ## 🐍 Requirements
30 |
31 | - Python 3.9 or higher
32 |
33 |
38 |
39 | ## 💡 Gameplay
40 |
41 | - To start a new game, click on the **New Game** button.
42 | - To make a move, click on the piece you want to move
43 | - Board will automatically highlight all the legal moves for the selected piece
44 | - Click on the highlighted square and the move will be made
45 | - The king will be highlighted in red if in check.
46 | - Game will end in one of the following conditions:
47 | 1. Checkmate
48 | 2. Stalemate
49 | 3. Insufficient materials
50 |
53 |
54 |
55 | ## 🤩 Starcatcher
56 |
57 | If you like this project, you can support me by giving a ⭐.
58 |
59 | ## 👨🚀 Contact
60 |
61 | - GitHub: [@IndieCoderMM](https://github.com/indiecodermm/)
62 | - Email: hthant00chk@gmail.com
63 | - LinkedIn: [@hthantoo](https://linkedin.com/in/hthantoo/)
64 |
65 | ## 🏗 Contributions
66 |
67 | Contributions are welcome! If you have any ideas for new features or improvements, please open an issue or submit a pull request.
68 |
69 | ## 📜 License
70 |
71 | This project is licensed under the [MIT](MIT.md) License.
72 |
--------------------------------------------------------------------------------
/assets/bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/bb.png
--------------------------------------------------------------------------------
/assets/be.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/be.png
--------------------------------------------------------------------------------
/assets/bk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/bk.png
--------------------------------------------------------------------------------
/assets/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/blank.png
--------------------------------------------------------------------------------
/assets/bn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/bn.png
--------------------------------------------------------------------------------
/assets/bp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/bp.png
--------------------------------------------------------------------------------
/assets/bq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/bq.png
--------------------------------------------------------------------------------
/assets/br.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/br.png
--------------------------------------------------------------------------------
/assets/wb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wb.png
--------------------------------------------------------------------------------
/assets/we.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/we.png
--------------------------------------------------------------------------------
/assets/wk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wk.png
--------------------------------------------------------------------------------
/assets/wn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wn.png
--------------------------------------------------------------------------------
/assets/wp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wp.png
--------------------------------------------------------------------------------
/assets/wq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wq.png
--------------------------------------------------------------------------------
/assets/wr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/assets/wr.png
--------------------------------------------------------------------------------
/game/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/game/__init__.py
--------------------------------------------------------------------------------
/game/board.py:
--------------------------------------------------------------------------------
1 | import chess
2 | import PySimpleGUI as sg
3 | from game.tile import Tile
4 |
5 | PIECES = {'K': 'wk.png', 'Q': 'wq.png', 'B': 'wb.png', 'R': 'wr.png', 'N': 'wn.png', 'P': 'wp.png', 'k': 'bk.png',
6 | 'q': 'bq.png', 'b': 'bb.png', 'r': 'br.png', 'n': 'bn.png', 'p': 'bp.png'}
7 | BLANK = 'blank.png'
8 |
9 | class ChessBoard(chess.Board):
10 | def __init__(self):
11 | super().__init__(chess960=False)
12 | self.table = [[Tile(file, rank) for file in range(8)] for rank in range(8)]
13 | self.pending_move = []
14 | self.available_squares = []
15 | self.squares_in_danger = []
16 |
17 | def get_layout(self):
18 | board_layout = []
19 | for rank in range(8):
20 | layout_row = [sg.Text(chess.RANK_NAMES[7 - rank])]
21 | for file in range(8):
22 | tile = self.table[rank][file]
23 | if self.piece_at(tile.square) is not None:
24 | tile.set_image(PIECES[str(self.piece_at(tile.square))])
25 | layout_row.append(tile.button)
26 | board_layout.append(layout_row)
27 | file_names = [sg.Text(chess.FILE_NAMES[i].upper(
28 | ), expand_x=True, justification='center') for i in range(8)]
29 | board_layout.append(file_names)
30 | return board_layout
31 |
32 | def get_piece_img(self, tile):
33 | if self.piece_at(tile.square):
34 | return PIECES[str(self.piece_at(tile.square))]
35 | else:
36 | return BLANK
37 |
38 | def update_display(self):
39 | for rank in self.table:
40 | for tile in rank:
41 | piece_img = self.get_piece_img(tile)
42 | self.highlight_tile(tile)
43 | tile.update_image(piece_img)
44 |
45 | def highlight_tile(self, tile):
46 | if tile.name in self.pending_move:
47 | bg_color = 'yellow'
48 | elif tile.name in self.available_squares:
49 | if self.piece_at(tile.square) and self.is_attacked_by(self.turn, tile.square):
50 | bg_color = 'orange'
51 | else:
52 | bg_color = 'lime'
53 | elif tile.name + 'q' in self.available_squares:
54 | bg_color = 'purple'
55 | else:
56 | bg_color = tile.bgcolor
57 | if self.is_check() and tile.square == self.king(self.turn):
58 | bg_color = 'red'
59 | tile.change_bg_color(bg_color)
60 |
61 | def get_available_squares(self, tile):
62 | legal_moves = [str(move) for move in self.legal_moves]
63 | for move in legal_moves:
64 | if move[:2] == tile.name:
65 | self.available_squares.append(move[2:])
66 |
67 | def handle_move(self, tile):
68 | if self.color_at(tile.square) == self.turn or len(self.pending_move) == 1:
69 | self.get_available_squares(tile)
70 | self.pending_move.append(tile.name)
71 | if len(self.pending_move) == 2:
72 | try:
73 | move = self.parse_uci(''.join(self.pending_move))
74 | self.push(move)
75 | except ValueError:
76 | try:
77 | self.pending_move.append('q')
78 | if self.parse_uci(''.join(self.pending_move)) in self.legal_moves:
79 | promote_to = sg.Window("Choose Your Promotion", [[sg.Button('Queen'), sg.Button(
80 | 'Rook'), sg.Button('Bishop'), sg.Button('Knight')]]).read(close=True)[0]
81 | if promote_to == 'Queen':
82 | promote = 'q'
83 | elif promote_to == 'Rook':
84 | promote = 'r'
85 | elif promote_to == 'Bishop':
86 | promote = 'b'
87 | elif promote_to == 'Knight':
88 | promote = 'n'
89 | self.pending_move[-1] = promote
90 | move = self.parse_uci(''.join(self.pending_move))
91 | self.push(move)
92 | except ValueError:
93 | sg.PopupQuickMessage('Invalid Move!')
94 | self.available_squares = []
95 | self.pending_move = []
--------------------------------------------------------------------------------
/game/gui.py:
--------------------------------------------------------------------------------
1 | import PySimpleGUI as sg
2 | from game.board import ChessBoard
3 |
4 | class ChessGUI(sg.Window):
5 | def __init__(self, title):
6 | self.board = ChessBoard()
7 | self.status_msg = 'None'
8 | super().__init__(title, self.get_layout())
9 |
10 | def get_layout(self):
11 | layout = [[sg.Text('Chess ', auto_size_text=True,
12 | key='-STATUS-', font='Default 20')]]
13 | layout += self.board.get_layout()
14 | layout += [[sg.Button('New Game', size=(8, 1), key='-RESTART-')]]
15 | return layout
16 |
17 | def update_status(self):
18 | msg = f'{"WHITE" if self.board.turn else "BLACK"} to move..'
19 |
20 | if self.board.is_game_over():
21 | if self.board.is_checkmate():
22 | winner = 'WHITE' if self.board.outcome().winner else 'BLACK'
23 | msg = f'CHECKMATE!!! {winner} wins!'
24 | elif self.board.is_stalemate():
25 | msg = 'Draw by STALEMATE!'
26 | elif self.board.is_insufficient_material():
27 | msg = 'Draw by INSUFFICIENT MATERIAL!'
28 |
29 | self.status_msg = msg
30 | self['-STATUS-'].update(self.status_msg)
31 |
32 | def update_board(self, event):
33 | if event == '-RESTART-':
34 | self.board.reset()
35 | for rank in self.board.table:
36 | for tile in rank:
37 | if tile.key == event:
38 | self.board.handle_move(tile)
39 | self.board.update_display()
--------------------------------------------------------------------------------
/game/tile.py:
--------------------------------------------------------------------------------
1 | import chess
2 | import PySimpleGUI as sg
3 |
4 | ASSET_PATH = "./assets/"
5 |
6 | class Tile:
7 | PRIMARY = 'light grey'
8 | SECONDARY = 'skyblue'
9 | EMPTY = "blank.png"
10 | def __init__(self, f, r):
11 | self.rank = 7 - r
12 | self.file = f
13 | self.square = chess.square(self.file, self.rank)
14 | self.name = chess.square_name(self.square)
15 | self.key = (self.file, self.rank)
16 | self.bgcolor = self.PRIMARY if (
17 | self.square + self.rank) % 2 else self.SECONDARY
18 | self.button = self.get_button()
19 |
20 | def get_button(self):
21 | return sg.Button(button_color=self.bgcolor, image_filename=ASSET_PATH + self.EMPTY, image_size=(64, 64),
22 | image_subsample=4, border_width=1, pad=(0, 0), tooltip=self.name.upper(), key=self.key)
23 |
24 | def set_image(self, img_path):
25 | self.button.ImageFilename = ASSET_PATH + img_path
26 |
27 | def update_image(self, img_path):
28 | self.button.update(image_filename=ASSET_PATH + img_path,
29 | image_size=(64, 64), image_subsample=4)
30 |
31 | def change_bg_color(self, color):
32 | self.button.update(button_color=color)
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from game.gui import ChessGUI
2 | import PySimpleGUI as sg
3 |
4 |
5 | def main():
6 | sg.theme('Python')
7 | sg.set_options(font="Cambria 15")
8 |
9 | window = ChessGUI('Chess')
10 |
11 | while True:
12 | event, values = window.read()
13 | if event in (sg.WIN_CLOSED, 'Exit'):
14 | break
15 | window.update_board(event)
16 | window.update_status()
17 | window.refresh()
18 |
19 | window.close()
20 |
21 |
22 | if __name__ == '__main__':
23 | main()
24 |
--------------------------------------------------------------------------------
/screenshots/chess_gui_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/screenshots/chess_gui_demo.gif
--------------------------------------------------------------------------------
/screenshots/chess_gui_demo2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndieCoderMM/chess-gui/a8c11a8f96d3e41041dd09ca33f1500bc0977213/screenshots/chess_gui_demo2.gif
--------------------------------------------------------------------------------