Chess
Two Player Chess
This is a simple two-player chess game built in Python. It uses the standard rules of chess and allows two players to play against each other on the same computer.
Installation
To install and run the game, follow these steps:
Clone the repository to your local machine.
Navigate to the project directory in your terminal.
Install pygame , a Python library for creating games, by running the following command:
pip install pygameStart the game by running the following command:
python main.py
How to Play
The game is played using the standard rules of chess. Each player takes turns moving their pieces on the board until one player is in checkmate or a draw is declared.
To move a piece, select it with your mouse and drag it to the desired square. If the move is legal, the piece will be placed on the new square. If the move is not legal, the piece will return to its original position.
Features
En Passant: Special pawn capture move inclusionCastling: Ability to perform the castling maneuverCheckmate and Stalemate Detection: Logic for detecting game-ending statesUser Interface: Graphical representation of the board with mouse controlsStandard Chess Rules: Adherence to traditional chess rulesTwo-Player Mode: Enable two human players to compete on the same device
Code Overview
The game is built using two Python files:
engine.py: This file contains the logic for the chess game, including the rules for moving pieces and checking for checkmate and stalemate.main.py: This file contains the user interface for the game, including the graphical representation of the board and the mouse controls for moving pieces.
Build with :heart: by Purna Shrestha
Source Code: main.py
import pygame as p
import engine
# square window for our game.
# can change screen size from here
screen_width = screen_height = 550
screen_caption = "Two Player Chess by Purna"
icon = p.image.load(r"Images\icon.png")
# rows and columns
dimensions = 8
# making sqaures in the screen to display chess board boxes
sq_size = screen_height // dimensions
fps = 30
# to pass as an argument in clock.tick
# adjust if game become laggy
images = {}
def load_images():
# load all images once as it is cpu heavy task
pieces = ["wp", "wR", "wN", "wB", "wQ", "wK", "bp", "bR", "bN", "bB", "bQ", "bK"]
for piece in pieces:
image_path = r"Images" + "\\" + piece + ".png"
images[piece] = p.transform.scale(p.image.load(image_path).convert_alpha(), (sq_size, sq_size))
# pygame.transform.scale to adjust the image
def main():
p.init()
# os.system("welcome.mp3")
# setting screen with sizes
# closing our face detection window
screen = p.display.set_mode((screen_width,screen_height), p.HWSURFACE | p.DOUBLEBUF)
p.display.set_caption(screen_caption)
p.display.set_icon(icon)
p.display.update()
# clock object
clock = p.time.Clock()
# fps change karega to limit CPU in clock.tick(15)
screen.fill(p.Color("white"))
# aise hi
# creating a gamestate object joh ki constructor ko call karega apne
# dot operator to call gamestate() in engine
gs = engine.gamestate()
# to store valid moves
valid_moves = gs.getvalidmoves()
# print(gs.board)
move_made = False
# to update valid moves only when a move is made
# flag variable for when a move is made
# loading the images "once"
load_images()
# running variable to check start and quit
running = True
# tuple to keep the last square selected
sq_selected = ()
# no square is selected at start
# tuple: (row,col)
# playerClicks = []
# list to keep two inputs
player_clicks = []
# keep track of player clicks (two tuples: [(6, 4), (4, 4)])
done = True
chess = p.transform.scale_by(p.image.load(r"Images\chess.jpg"),0.25)
screen.fill(p.Color("black"))
while done:
screen.blit(chess,p.Rect(200-5*sq_size + 180,200-5*sq_size + 200,10,10))
screen.blit(p.transform.scale_by(icon,0.5),p.Rect(470-5*sq_size+15,screen_height/2-270,10,10))
showtext(screen, "Welcome to Chess", (screen_height/2 - 230,screen_height/2 - 10), 40)
showtext(screen, "Press any key to start the game", (screen_height/2 - 220,screen_height/2+50),25)
p.display.flip()
for event in p.event.get():
if event.type == p.QUIT:
p.quit()
if event.type == p.KEYDOWN:
done = False
# showtext(screen, predicted_name + " is playing")
# start of my gameloop
while running:
# lets keep a for loop to get events
for event in p.event.get():
# print(p.display.Info())
# if the type of event is this
if event.type == p.QUIT:
# to exit the whileloop
running = False
elif event.type == p.MOUSEBUTTONDOWN:
# mouse kaha h?
mouse_location = p.mouse.get_pos() # (x,y) location of mouse
# get x and y from list
column = mouse_location[0]//sq_size
row = mouse_location[1]//sq_size
# first click is select, second click is undo
if sq_selected == (row,column):
# user clicks same sqaure again
sq_selected = () # undo
player_clicks = []
else:
# store the square selected by the user now
sq_selected = (row,column)
player_clicks.append(sq_selected)
# first time it will append to empty list then it appends to list[0]
# hume pata karna hai user ka first click hai ya second
if len(player_clicks)==2:
# do clicks hogye toh bolenge make move
# so call the move class constructor
move = engine.Move(player_clicks[0],player_clicks[1],gs.board)
print(move.getChessNotation())
# player_clicks[0] is our source
# player_clicks[1] is our piece's destination
for i in range(len(valid_moves)):
# only get valid move object
# so check for it
if move == valid_moves[i]:
gs.makeMove(valid_moves[i])
user_choice = "Q"
while move.pawn_promotion:
p.display.set_caption("Choose a piece to promote to")
screen.fill(p.Color("black"))
screen.blit(p.transform.scale_by(p.image.load(r"Images\PromotionMenu.jpg"),0.2),p.Rect(200-sq_size,200-sq_size,10,10))
showtext(screen, "Enter the corresponding character of the piece you want to promote to :", (200,200),12)
p.display.flip()
user_choice = ""
for event in p.event.get():
if event.type == p.KEYDOWN:
if event.key == p.K_q:
gs.makePawnPromotion(move,"Q")
move.pawn_promotion=False
elif event.key == p.K_r:
gs.makePawnPromotion(move,"R")
move.pawn_promotion=False
elif event.key == p.K_b:
gs.makePawnPromotion(move,"B")
move.pawn_promotion=False
elif event.key == p.K_n:
gs.makePawnPromotion(move,"N")
move.pawn_promotion=False
else:
gs.makePawnPromotion(move,"Q")
move.pawn_promotion=False
p.display.set_caption("ChessAI")
# argument to makemove is generated by the engine
move_made = True
sq_selected = () # reset user clicks
player_clicks = []
# reset the user clicks after making the move each time
if not move_made:
player_clicks = [sq_selected]
#gs.makeMove(move)
# to make the move
elif event.type == p.KEYDOWN:
if event.key == p.K_z:
gs.undoMove()
move_made = True
# when the user undoes a move the valid moves change
# so change the flag variable to true
# to update the valid moves
if move_made:
valid_moves = gs.getvalidmoves()
move_made = False
# calling the draw boardand pieces fn
draw_game_state(screen,gs)
clock.tick(fps)
p.display.flip()
# to update the display
# method to draw sqs on board and graphics of a current gamestate
def draw_game_state(screen,gs):
# to draw squares on the board
drawboard(screen)
#board-->pieces order ofc matter karega nhi toh pieces piche chip jayenge
# to draw pieces
drawpieces(screen,gs.board) # board from engine gamestate ka object gs , isliye dot
def drawboard(screen):
# lets draw squares
# white and grey alternate
# make list to store white and grey switch karna easy hoga
# colors = [p.Color("white"), p.Color("dark gray")]
images = [p.image.load(r"images\ltb.jpg").convert_alpha(),p.image.load(r"images\dtb.jpg").convert_alpha()]
for rows in range(dimensions):
for columns in range(dimensions):
# [00,10,20,30,40,50,60,70]
# [01,11,21,31,41,51,61,71]
# [02,12,22,32,42,52,62,72]
# [03,13,23,33,43,53,63,73]
# [04,14,24,34,44,54,64,74]
# [05,15,25,35,45,55,65,75]
# [06,16,26,36,46,56,66,76]
# [07,17,27,37,47,57,67,77]
# trend we see here is that if we add rows and columns
# dark sqaures are odd
# light sqaures are even
# color = colors[(rows+columns)%2]
image = images[(rows+columns)%2]
# even --> colors[0] --> white
# odd --> colors[1] --> black
# smpart
# just draw rectangle (surface,color,)
custom_img = p.Surface((sq_size,sq_size))
screen.blit(image,p.Rect(columns*sq_size,rows*sq_size,sq_size,sq_size))
# p.draw.rect(screen, color, p.Rect(columns*sq_size,rows*sq_size, sq_size, sq_size))
def drawpieces(screen,board):
for rows in range(dimensions):
for columns in range(dimensions):
pieces = board[rows][columns]
if pieces != "--":
screen.blit(images[pieces],p.Rect(columns*sq_size,rows*sq_size,sq_size,sq_size))
# accessing our gs.board multi dim list by using [][]
# to assign each square a piece
# function to show a menu an ask the user the piece to promote to in pawn promotion
def showtext(screen,text,location,fontsize):
font = p.font.SysFont("Copperplate gothic", fontsize, True, False)
textObject = font.render(text, 0, p.Color('White'))
location1 = p.Rect(location, location)
# textLocation = p.Rect(0, 0, screen_width, screen_height).move(screen_width / 2 - textObject.get_width() / 2, screen_height / 2 - textObject.get_height() / 2)
# white = p.Color("black")
# screen.blit(white,p.rect(textLocation,textLocation,200,200))
screen.blit(textObject, location1)
# if we import something in the main code we need to do this cause it wont run otherwise
# THIS CODE WE HAVE TO RUN AS THIS IS OUR MAIN CODE AND WE IMPORT OTHER MODULES IN THIS CODE
# SO WE WRITE THIS
# The if __name__ == "__main__": construct is used to
# ensure that a specific block of code only runs when the Python script is executed directly,
# not when it's imported as a module in another script.
if __name__=="__main__":
main()