├── .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 | [![CodeFactor](https://www.codefactor.io/repository/github/indiecodermm/chess-gui/badge)](https://www.codefactor.io/repository/github/indiecodermm/chess-gui) 2 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 --------------------------------------------------------------------------------