Tic-Tac-Toe Game With GUI (python) (pygame)

Tic Tac Toe game is a very good project for Beginners and intermediates.

This Tic Tac Toe game has a Graphical User Interface and that makes it a good project for intermediates. 

In this game I will be using the so-called "pygame" module to implement a good GUI.

This project will extremely help you to enhance your skills with python.


To Install pygame module:

type the following in the command line:
pip install pygame

 

Video from the game:




📢RecommendedPlease watch the video first of all. Try to understand the game and the idea behind it and then try to implement  the game on your own first, before looking at the code down below.


📥Download the Code📥



package structure:




















___________________________________________________

🎁Code:



main.py:



"""
AUTHOR: KHALED BADRAN
"""

import pygame 
import buttons 
import random
import time

pygame.init()

# Defining some important constants and variables:-
#############################################
(WIDTHHEIGHT= (600700)
DISPLAY_SCREEN = pygame.display.set_mode((WIDTHHEIGHT))
OFFSET_HEIGHT_UP = 100

SAILOR_BLUE = TILE_OCCUPIED_COLOR = (43066#SAILOR_BLUE
ORANGE = (249870#ORANGE 
GRAY = (959596#GRAY
BLUE = (107,142,35)

BUTTON_WIDTH = 200
BUTTON_HEIGHT = 50
TILE_WIDTH = 195
TILE_Height = 195
X_COORDINATE_BUTTONS = WIDTH/2 - BUTTON_WIDTH//2  #X_COORDINATE_BUTTONS

background_img = pygame.image.load("images/background.png")
icon_img = pygame.image.load("images/icon.png")
horizontal_line_img = pygame.image.load("images/horizontal_line.png")
vertical_line_img = pygame.image.load("images/vertical_line.png")
x_img = pygame.image.load("images/x.png")
o_img = pygame.image.load("images/o.png")
player_x_img = pygame.image.load("images/player_x.png")
player_o_img = pygame.image.load("images/player_o.png")

pygame.display.set_icon(icon_img)
pygame.display.set_caption("Tic-Tac-Toe")
#############################################

 
def select_player(difficulty): 
    """ to enable the user to select either X or O.
    Args:
        difficulty (str): difficulty level of the game.
    """
    player_x_button = buttons.SelectPlayerButton(-300OFFSET_HEIGHT_UP+3300-3HEIGHT-OFFSET_HEIGHT_UP-5SAILOR_BLUEORANGE, player_x_img)
    player_o_button = buttons.SelectPlayerButton(600OFFSET_HEIGHT_UP+3300-3HEIGHT-OFFSET_HEIGHT_UP-5SAILOR_BLUEORANGE, player_o_img)

    while True:
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

        #to draw the buttons in an animated way
        while player_x_button.x < 0 or player_o_button.x > WIDTH/2:
            DISPLAY_SCREEN.blit(background_img, (00))
            pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()

            player_x_button.x += 2.5
            player_o_button.x -= 2.5
            
            player_x_button.blit(DISPLAY_SCREEN)
            player_o_button.blit(DISPLAY_SCREEN)
            pygame.display.update()

    
        mouse_position = pygame.mouse.get_pos() # get the position of the mouse
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                player_x_button.is_clicked(mouse_position)
                player_o_button.is_clicked(mouse_position)

        player_x_button.blit(DISPLAY_SCREEN, mouse_position)
        player_o_button.blit(DISPLAY_SCREEN, mouse_position)
        pygame.display.update()

        if player_x_button.selected or player_o_button.selected:
            
            if player_x_button.selected:
                player_img = x_img
                computer_img = o_img
            else:
                player_img = o_img
                computer_img = x_img

            while player_x_button.x > 0 or player_o_button.x < WIDTH:
                DISPLAY_SCREEN.blit(background_img, (00))
                pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()

                player_x_button.x -= 2.5
                player_o_button.x += 2.5
                
                player_x_button.blit(DISPLAY_SCREEN)
                player_o_button.blit(DISPLAY_SCREEN)
                pygame.display.update()

            start_game(player_img, computer_img, difficulty)


def start_intro(status = ""):
    """start the introduction of the game and show if the user won, lost or draw in the previous game.

    Args:
        status (str): status of the previous game. Either "YOU WoN" or "YOU LOST" or "DRAW". Defaults to "".
    """
    play_button = buttons.Button(SAILOR_BLUEORANGE-400HEIGHT/2BUTTON_WIDTHBUTTON_HEIGHTORANGESAILOR_BLUE"PLAY")
    quit_button = buttons.Button(SAILOR_BLUEORANGEWIDTH+200HEIGHT/2+BUTTON_HEIGHT+10BUTTON_WIDTHBUTTON_HEIGHTORANGESAILOR_BLUE"QUIT")

    if status: # to blit the status of the previous game if available. 
        font = pygame.font.SysFont("comicsansms"40)
        rendered_text = font.render(status, 1BLUE)
        rendered_text_y = HEIGHT
        
        #to draw the status of the previous game in an animated way.
        while rendered_text_y > OFFSET_HEIGHT_UP+20
            DISPLAY_SCREEN.blit(background_img, (00))
            pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()

            rendered_text_y -= 0.8
            DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))
            pygame.display.update()

    #to draw the buttons in an animated way.
    while play_button.x < X_COORDINATE_BUTTONS or quit_button.x > X_COORDINATE_BUTTONS :
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line
        if status:
            DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
        
        if play_button.x < X_COORDINATE_BUTTONS:
            play_button.x += 2
        if quit_button.x > X_COORDINATE_BUTTONS:    
            quit_button.x -= 2

        play_button.blit(DISPLAY_SCREENGRAY)
        quit_button.blit(DISPLAY_SCREENGRAY)
        pygame.display.update()


    run = True
    while run:
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

        play_button.blit(DISPLAY_SCREENGRAY)
        quit_button.blit(DISPLAY_SCREENGRAY)

        if status:
            DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))

        mouse_pos = pygame.mouse.get_pos() 
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            if play_button.is_clicked(mouse_pos, event):
                run = False
                select_difficulty()
            if quit_button.is_clicked(mouse_pos, event):
                pygame.quit()


        # change the color of the buttons when the user hovers over them.
        if play_button.is_hovered_over(mouse_pos):
            play_button.blit_hovered_over(DISPLAY_SCREEN)
        elif quit_button.is_hovered_over(mouse_pos):
            quit_button.blit_hovered_over(DISPLAY_SCREEN)

        pygame.display.update()


def draw_playing_field(linestilesplayer_imgcomputer_imgdifficultyevent=None):
    """draw the playing field of the game.

    Args:
        lines (dictionary): dictionary of lines for the playing field 
        tiles (dictionary): dictionary of the tiles of the playing field.
        player_img (pygame.image): image of the user either x or o
        computer_img (pygame.image): image of the computer as a player either x or o 
        difficulty (str): difficulty level of the game.
        event (pygame.event): event of pygame. Defaults to None.
    """
    #to draw the lines of the playing field in an animated way.
    while lines["horizontal1_x"<= 0 or lines["horizontal2_x">= 0 or lines["vertical1_x"<= (WIDTH/3or lines["vertical2_x">= (WIDTH/3)*2:
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
        if lines["horizontal1_x"<= 0:
            lines["horizontal1_x"+= 1.2
        if lines["horizontal2_x">= 0:
            lines["horizontal2_x"-= 1.2
        
        if lines["vertical1_x"<= (WIDTH/3):
            lines["vertical1_x"+= 0.4
        if lines["vertical2_x">= (WIDTH/3)*2:
            lines["vertical2_x"-= 0.4

        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line
        DISPLAY_SCREEN.blit(horizontal_line_img, (lines["horizontal1_x"], lines["horizontal1_y"]))
        DISPLAY_SCREEN.blit(horizontal_line_img, (lines["horizontal2_x"], lines["horizontal2_y"]))
        DISPLAY_SCREEN.blit(vertical_line_img, (lines["vertical1_x"], lines["vertical1_y"]))
        DISPLAY_SCREEN.blit(vertical_line_img, (lines["vertical2_x"], lines["vertical2_y"]))
        pygame.display.update()

    DISPLAY_SCREEN.blit(background_img, (00))
    pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line
    DISPLAY_SCREEN.blit(horizontal_line_img, (lines["horizontal1_x"], lines["horizontal1_y"]))
    DISPLAY_SCREEN.blit(horizontal_line_img, (lines["horizontal2_x"], lines["horizontal2_y"]))
    DISPLAY_SCREEN.blit(vertical_line_img, (lines["vertical1_x"], lines["vertical1_y"]))
    DISPLAY_SCREEN.blit(vertical_line_img, (lines["vertical2_x"], lines["vertical2_y"]))

    if player_turn(tiles, player_img, event):
        pygame.display.update()
        is_game_over(tiles, player_img)
        if difficulty == "easy":
            computer_turn_easy(tiles, computer_img)
        else:
            computer_turn_hard(tiles, computer_img, player_img)
    pygame.display.update()
    is_game_over(tiles, player_img)


def player_turn(tilesplayer_imgevent):
    """to let the user play.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        player_img (pygame.image): image of the user either x or o
        event (pygame.event): event of pygame. Defaults to None.

    Returns:
        boolean: return True if the user played and is done with his turn. False otherwise. 
    """
    player_played = False
    
    mouse_position = pygame.mouse.get_pos() # get the position of the mouse
    if event and event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
        for tile in tiles.values():
            if tile.is_clicked(mouse_position):
                tile.symbol_img = player_img
                player_played = True

    print_tiles(tiles)
    
    return player_played


def computer_turn_easy(tilescomputer_img):
    """let the computer play. The computer will choose a random empty tile.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        computer_img (pygame.image): image of the computer as a player either x or o 
    """
    while True:
        rand_tile = random.randint(1,9#because the tiles have the numbers from 1 to 9.
        if not tiles[rand_tile].occupied:
            tiles[rand_tile].symbol_img = computer_img
            tiles[rand_tile].occupied = True
            break
    
    print_tiles(tiles)


def computer_can_win(tilescomputer_imgplayer_imgcurrent_list):
    """the computer tries to win the game.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        computer_img (pygame.image): image of the computer as a player either x or o 
        player_img (pygame.image): image of the user either x or o
        current_list (list): list of either a row or a column or a diagonol. 

    Returns:
        boolean: True if the computer could win the game. False otherwise.
    """
    for i in current_list:            
            for j in current_list:            
                if i!=and tiles[i].symbol_img == tiles[j].symbol_img == computer_img != None:
                    for k in current_list:
                        if k != i and k != j and not tiles[k].occupied:
                            tiles[k].symbol_img = computer_img
                            tiles[k].occupied = True
                            print_tiles(tiles)
                            return True
    return False


def computer_can_defend(tilescomputer_imgplayer_imgcurrent_list):
    """the computer tries to disable the user from winning the game.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        computer_img (pygame.image): image of the computer as a player either x or o 
        player_img (pygame.image): image of the user either x or o
        current_list (list): list of either a row or a column or a diagonol. 

    Returns:
        boolean: True if the computer could disable the user from winning the game. False otherwise.
    """
    for i in current_list:            
            for j in current_list:            
                if i!=and tiles[i].symbol_img == tiles[j].symbol_img == player_img != None:
                    for k in current_list:
                        if k != i and k != j and not tiles[k].occupied:
                            tiles[k].symbol_img = computer_img
                            tiles[k].occupied = True
                            print_tiles(tiles)
                            return True
    return False


def computer_turn_hard(tilescomputer_imgplayer_img):
    """let the computer play. The computer will try to win the game instaed of choosing a random tile.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        computer_img (pygame.image): image of the computer as a player either x or o 
        player_img (pygame.image): image of the user either x or o
    """
    #computer tries to win first of all
    for i in range(14):#columns 
        col = [i, i+3, i+6]
        if computer_can_win(tiles, computer_img, player_img, col):
            print_tiles(tiles)
            return

    for i in range(1103):#rows 
        row = [i, i+1, i+2]
        if computer_can_win(tiles, computer_img, player_img, row):
            print_tiles(tiles)
            return

    #left diagonol.   
    left_diagonol = [159]
    if computer_can_win(tiles, computer_img, player_img, left_diagonol):
        print_tiles(tiles)
        return

    #right diagonol.   
    right_diagonol = [357]
    if computer_can_win(tiles, computer_img, player_img, right_diagonol):
        print_tiles(tiles)
        return

    #####################################
    #if computer could't win the game, them it tries to disable the user from winning the game.

    for i in range(14):#columns 
        col = [i, i+3, i+6]
        if computer_can_defend(tiles, computer_img, player_img, col):
            print_tiles(tiles)
            return

    for i in range(1103):#rows 
        row = [i, i+1, i+2]
        if computer_can_defend(tiles, computer_img, player_img, row):
            print_tiles(tiles)
            return

    #left diagonol.   
    left_diagonol = [159]
    if computer_can_defend(tiles, computer_img, player_img, left_diagonol):
        print_tiles(tiles)
        return

    #right diagonol.   
    right_diagonol = [357]
    if computer_can_defend(tiles, computer_img, player_img, right_diagonol):
        print_tiles(tiles)
        return

    #if the situation is not dangerous and computer can't win and the player can't win as well, then choose a random tile.
    computer_turn_easy(tiles, computer_img)


def print_tiles(tiles):
    """print the tiles of the playing field.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
    """
    mouse_position = pygame.mouse.get_pos() # get the position of the mouse
    for tile in tiles.values():
        tile.blit(DISPLAY_SCREEN, mouse_position)


def is_game_over(tilesplayer_img):
    """check whether the game is over or not.
       If it is over, start the introduction again and let the user know if he won, lost or draw.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        player_img (pygame.image): image of the user either x or o
    """
    for i in range(14):#if one column is completely filled with X or with O. 
        if tiles[i].symbol_img == tiles[i+3].symbol_img == tiles[i+6].symbol_img != None:
            if tiles[i].symbol_img == player_img:
                print_why_game_over(tiles, tiles[i], tiles[i+3], tiles[i+6], "win")
                start_intro("YOU WON")
            else:
                print_why_game_over(tiles, tiles[i], tiles[i+3], tiles[i+6], "lose")
                start_intro("YOU LOST")

    for i in range(1,10,3):#if one row is completely filled with X or with O.    
        if tiles[i].symbol_img == tiles[i+1].symbol_img == tiles[i+2].symbol_img != None:
            if tiles[i].symbol_img == player_img:
                print_why_game_over(tiles, tiles[i], tiles[i+1], tiles[i+2], "win")
                start_intro("YOU WON")
            else:                
                print_why_game_over(tiles, tiles[i], tiles[i+1], tiles[i+2], "lose")
                start_intro("YOU LOST")

    #if one diagonol is completely filled with X or with O.    
    if tiles[1].symbol_img == tiles[5].symbol_img == tiles[9].symbol_img != None:
            if tiles[1].symbol_img == player_img:
                print_why_game_over(tiles, tiles[1],tiles[5], tiles[9], "win")
                start_intro("YOU WON")
            else:
                print_why_game_over(tiles, tiles[1],tiles[5], tiles[9], "lose")
                start_intro("YOU LOST")

    if tiles[3].symbol_img == tiles[5].symbol_img == tiles[7].symbol_img != None:
            if tiles[3].symbol_img == player_img:
                print_why_game_over(tiles, tiles[3], tiles[5], tiles[7], "win")
                start_intro("YOU WON")
            else:
                print_why_game_over(tiles, tiles[3], tiles[5], tiles[7], "lose")
                start_intro("YOU LOST")

    for tile in tiles.values(): # if no one won and there is still at least one empty cell, then the game is not over yet. 
        if not tile.occupied:
            return

    time.sleep(0.3)
    start_intro("DRAW")        


def print_why_game_over(tilesfirst_tilemiddle_tilelast_tilestate):
    """to show the user why the game is over and how he or the computer won.

    Args:
        tiles (dictionary): dictionary of the tiles of the playing field.
        first_tile (Tile): first_tile of either a row or a column or a diagonol. 
        middle_tile (Tile): middle_tile of either a row or a column or a diagonol. 
        last_tile (Tile): last_tile of either a row or a column or a diagonol. 
        state (str): state of the game. Either "win" or "lose" to indicate whether the player won or not.
    """
    if state == "win":
        color = (50200100)
    else:
        color = (25020050)


    first_tile.tile_occupied_color = color
    print_tiles(tiles)
    pygame.display.update()
    time.sleep(0.4)

    middle_tile.tile_occupied_color = color
    print_tiles(tiles)
    pygame.display.update()
    time.sleep(0.4)

    last_tile.tile_occupied_color = color
    print_tiles(tiles)
    pygame.display.update()
    time.sleep(1.2)


def start_game(player_imgcomputer_imgdifficulty):
    """start the game

    Args:
        player_img (pygame.image): image of the user either x or o
        computer_img (pygame.image): image of the computer as a player either x or o 
        difficulty (str): difficulty level of the game.
    """
    #horizontal lines
    horizontal1_x = -600 # first horizontal line. It comes from the left side
    horizontal1_y = OFFSET_HEIGHT_UP + ((HEIGHT-OFFSET_HEIGHT_UP)/3)
    horizontal2_x = WIDTH # second horizontal line. It comes from the right side
    horizontal2_y = horizontal1_y + ((HEIGHT-OFFSET_HEIGHT_UP)/3)
    #vertical lines
    vertical1_x = 0 # first vertical line. It comes from the left side
    vertical1_y = OFFSET_HEIGHT_UP
    vertical2_x = WIDTH # second vertical line. It comes from the right side
    vertical2_y = OFFSET_HEIGHT_UP
    lines = { #dictionary of the lines
        "horizontal1_x": horizontal1_x,
        "horizontal1_y": horizontal1_y,
        "horizontal2_x": horizontal2_x,
        "horizontal2_y": horizontal2_y, 
        "vertical1_x": vertical1_x,
        "vertical1_y": vertical1_y,
        "vertical2_x": vertical2_x,
        "vertical2_y": vertical2_y
    }

    tiles = { #dictionary of the tiles
        1: buttons.Tile(2OFFSET_HEIGHT_UP+3TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        2: buttons.Tile(203OFFSET_HEIGHT_UP+3TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        3: buttons.Tile(403OFFSET_HEIGHT_UP+3TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        4: buttons.Tile(2OFFSET_HEIGHT_UP+200+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        5: buttons.Tile(203OFFSET_HEIGHT_UP+200+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        6: buttons.Tile(403OFFSET_HEIGHT_UP+200+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        7: buttons.Tile(2OFFSET_HEIGHT_UP+400+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        8: buttons.Tile(203OFFSET_HEIGHT_UP+400+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR),
        9: buttons.Tile(403OFFSET_HEIGHT_UP+400+4TILE_WIDTHTILE_HeightORANGETILE_OCCUPIED_COLOR)
    }

    while True:
        DISPLAY_SCREEN.blit(background_img, (00))        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                draw_playing_field(lines, tiles, player_img, computer_img,difficulty, event)

        draw_playing_field(lines, tiles, player_img, computer_img, difficulty)

        pygame.display.update()


def select_difficulty():
    """enable the user to play the game in different difficulty levels.
    """
    easy_button  = buttons.Button(SAILOR_BLUEORANGE-400HEIGHT/2BUTTON_WIDTHBUTTON_HEIGHTORANGESAILOR_BLUE"EASY")
    hard_button  = buttons.Button(SAILOR_BLUEORANGEWIDTH+200HEIGHT/2+BUTTON_HEIGHT+10BUTTON_WIDTHBUTTON_HEIGHTORANGESAILOR_BLUE"HARD")

    font = pygame.font.SysFont("comicsansms"48)
    rendered_text = font.render("SELECT DIFFICULTY"1BLUE)
    rendered_text_y = HEIGHT
    
    #to draw the "select difficulty" text in an animated way.
    while rendered_text_y > OFFSET_HEIGHT_UP+20
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()

        rendered_text_y -= 0.8
        DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))
        pygame.display.update()

    #to draw the buttons in an animated way.
    while easy_button.x < X_COORDINATE_BUTTONS or hard_button.x > X_COORDINATE_BUTTONS :
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line
        DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
        
        if easy_button.x < X_COORDINATE_BUTTONS:
            easy_button.x += 2
        if hard_button.x > X_COORDINATE_BUTTONS:    
            hard_button.x -= 2

        easy_button.blit(DISPLAY_SCREENGRAY)
        hard_button.blit(DISPLAY_SCREENGRAY)
        pygame.display.update()


    run = True
    while run:
        DISPLAY_SCREEN.blit(background_img, (00))
        pygame.draw.line(DISPLAY_SCREEN, (100,0,0), (0,OFFSET_HEIGHT_UP), (WIDTH,OFFSET_HEIGHT_UP), 3#horizontal line
        DISPLAY_SCREEN.blit(rendered_text, (WIDTH/2-rendered_text.get_width()/2, rendered_text_y))
        easy_button.blit(DISPLAY_SCREENGRAY)
        hard_button.blit(DISPLAY_SCREENGRAY)

        mouse_pos = pygame.mouse.get_pos() 
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            if easy_button.is_clicked(mouse_pos, event):
                run = False # to stop running the select_difficulty screen
                select_player("easy")
            if hard_button.is_clicked(mouse_pos, event):
                run = False # to stop running the select_difficulty screen
                select_player("hard")


        # change the color of the buttons when the user hovers over them.
        if easy_button.is_hovered_over(mouse_pos):
            easy_button.blit_hovered_over(DISPLAY_SCREEN)
        elif hard_button.is_hovered_over(mouse_pos):
            hard_button.blit_hovered_over(DISPLAY_SCREEN)

        pygame.display.update()


def main():
    start_intro()


if __name__ == "__main__":  #to avoid runing the game, if this file is imported
    main()



____________________________________________________

buttons.py:



"""
AUHTHOR: KHALED BADRAN
"""

import pygame


class Button:

    def __init__(selfbutton_colorbutton_hover_over_colorxywidthheighttext_colortext_hover_over_color = Nonetext_str=""):
        """ constructor of Button class

        Args:
            button_color ((R,G,B) tuple): color of the button.
            button_hover_over_color ((R,G,B) tuple): temporary color of the button when the user hovers over it with the mouse.
            x (int): x-coordinate of start point of the button.
            y (int): y-coordinate of start point of the button.
            width (int): width of the button.
            height (int): height of the button.
            text_color ((R,G,B) tuple): color of the text inside the button.
            text_hover_over_color ((R,G,B) tuple): temporary color of the text when the user hovers over the button. Default = None.
            text_str (str): text inside the button. Default = "".
        """
        self.button_color = button_color
        self.button_hover_over_color = button_hover_over_color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text_color = text_color

        if text_hover_over_color:
            self.text_hover_over_color = text_hover_over_color
        else:
            self.text_hover_over_color =  text_color
 
        self.text_str = text_str


    def blit(selfdisplay_screenoutline_color=None):
        """ draw the button on the display_screen/display_window  

        Args:
            display_screen (pygame.display.set-mode): display_screen/display_window to draw the button on it.
            outline_color  ((R,G,B) tuple): color of the outline-borders of the button
        """
        if outline_color: 
            pygame.draw.rect(display_screen, outline_color,
                             (self.x-3self.y-3self.width+5self.height+5))
        
        pygame.draw.rect(display_screen, self.button_color,
                         (self.x, self.y, self.width, self.height))

        if self.text_str != ""
            font = pygame.font.Font("freesansbold.ttf"32)
            text = font.render(self.text_str, Trueself.text_color)
            # the text should be in the middle of the button
            text_position = (self.x + (self.width/2 - text.get_width()/2),
                             self.y + (self.height/2 - text.get_height()/2))
            display_screen.blit(text, text_position)


    def is_hovered_over(selfmouse_position):
        """ check whether the user hovers over the button with the mouse or not. 
        Args:
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user hovers over the button with the mouse. False otherwise.
        """
        if self.x < mouse_position[0< self.x+self.width and self.y < mouse_position[1< self.y+self.height:
            return True
        return False


    def is_clicked(selfmouse_positionevent):
        """ check whether the user clicks on the button with the left button of the mouse or not. 
        Args:
            event (pygame.event): event of pygame.
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user clicks on the button with the left button of the mouse. False otherwise.
        """
        if self.is_hovered_over(mouse_position):
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # if left button of the mouse is clicked
                return True
        return False


    def blit_hovered_over(selfdisplay_screen):
        """ draw the button on the display_screen/display_window, when the user hovers over it with the mouse.

        Args:
            display_screen (pygame.display.set-mode): display_screen/display_window to draw the button on it.
        """
        pygame.draw.rect(display_screen, self.button_hover_over_color,
                         (self.x, self.y, self.width, self.height))

        if self.text_str != "":
            font = pygame.font.Font("freesansbold.ttf"32)
            text = font.render(self.text_str, Trueself.text_hover_over_color)
            # the text should be in the middle of the button
            text_position = (self.x + (self.width/2 - text.get_width()/2),
                             self.y + (self.height/2 - text.get_height()/2))
            display_screen.blit(text, text_position)



class Tile:

    def __init__(selfxywidthheighttile_hover_over_colortile_occupied_colortile_color = None):
        """ constructor of Tile class

        Args:
            x (int): x-coordinate of start point of the tile.
            y (int): y-coordinate of start point of the tile.
            width (int): width of the tile.
            height (int): height of the tile.
            tile_hover_over_color ((R,G,B) tuple): temporary color of the tile when it is empty and the user hovers over it with the mouse.
            tile_occupied_color ((R,G,B) tuple): color of the tile when it is occupied.
            tile_color ((R,G,B) tuple): color of the tile when it is empty.

        """
        self.occupied = False
        self.symbol_img = None

        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.tile_hover_over_color = tile_hover_over_color
        self.tile_occupied_color = tile_occupied_color
        self.tile_color = tile_color


    def blit(selfdisplay_screenmouse_position):
        """ draw the tile on the display_screen/display_window
            when it is hovered over, when it is empty and when it is occupied.
        Args:
            display_screen (pygame.display.set-mode): display_screen/display_window to draw the tile on it.
            mouse_position ((x,y) tuple): position of the mouse on the screen.
        """
        if self.is_hovered_over(mouse_position)  and self.occupied == False#if hovered over
            pygame.draw.rect(display_screen, self.tile_hover_over_color, (self.x, self.y, self.width, self.height))
        elif self.occupied: #if occupied
            pygame.draw.rect(display_screen, self.tile_occupied_color, (self.x, self.y, self.width, self.height))
            display_screen.blit(self.symbol_img, (self.x, self.y))

    
    def is_hovered_over(selfmouse_position):
        """ check whether the user hovers over the tile with the mouse or not. 
        Args:
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user hovers over the tile with the mouse. False otherwise.
        """
        if self.x < mouse_position[0< self.x+self.width and self.y < mouse_position[1< self.y+self.height:
            return True
        return False


    def is_clicked(selfmouse_position):
        """ check whether the user clicks on the tile with the left button of the mouse or not. 
        Args:
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user clicks on the tile with the left button of the mouse. False otherwise.
        """ 
        if self.is_hovered_over(mouse_position) and self.occupied == False:
                # if left button of the mouse is clicked
                self.occupied = True
                return True
        return False



class SelectPlayerButton:
    
    def __init__(selfxywidthheightbutton_colorbutton_hover_over_colorplayer_img):
        """ constructor of Button class

        Args:
            x (int): x-coordinate of start point of the Button.
            y (int): y-coordinate of start point of the Button.
            width (int): width of the Button.
            height (int): height of the Button.
            button_color ((R,G,B) tuple): color of the Button.
            Button_hover_over_color ((R,G,B) tuple): temporary color of the Button when the user hovers over it with the mouse.
            player_img (pygame.image): image to be blitted on the button
        """
        self.selected = False

        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.button_color = button_color
        self.button_hover_over_color = button_hover_over_color
        self.player_img = player_img


    def blit(selfdisplay_screenmouse_position=None):
        """ draw the Button on the display_screen/display_window
            when it is hovered over, when it is empty and when it is occupied.
        Args:
            display_screen (pygame.display.set-mode): display_screen/display_window to draw the Button on it.
            mouse_position ((x,y) tuple): position of the mouse on the screen.
        """
        
        if self.is_hovered_over(mouse_position): #if hovered over
            pygame.draw.rect(display_screen, self.button_hover_over_color, (self.x, self.y, self.width, self.height))
        else:
            pygame.draw.rect(display_screen, self.button_color, (self.x, self.y, self.width, self.height))
        
        display_screen.blit(self.player_img, (self.x, self.y))

    
    def is_hovered_over(selfmouse_position):
        """ check whether the user hovers over the Button with the mouse or not. 
        Args:
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user hovers over the Button with the mouse. False otherwise.
        """
        if mouse_position:
            if self.x < mouse_position[0< self.x+self.width and self.y < mouse_position[1< self.y+self.height:
                return True
        return False


    def is_clicked(selfmouse_position):
        """ check whether the user clicks on the Button with the left button of the mouse or not. 
        Args:
            mouse_position ((x,y) tuple): position of the mouse on the screen.

        Returns:
            boolean: True if the user clicks on the Button with the left button of the mouse. False otherwise.
        """
        if self.is_hovered_over(mouse_position):
                # if left button of the mouse is clicked
                self.selected = True
                self.button_color = self.button_hover_over_color
                return True
        return False


____________________________________________________

Output:

please watch the video.

____________________________________________________



Comments