Python can't play games? The of game practice ---- ink spin

Keywords: Python Game Development pygame

        Hi, guys, good morning, good noon and good evening,

        Through the study of Pygame in the first two chapters, we understand its basic use, and attach the link:

        1. Python can't play games? Make a game in an hour!

        2. Python can't play games? Do you know the objects in Pygame?

         Now, let's really start writing a game.

        The name of the game is: ink spill, Chinese Name: ink overflow. This is a very typical game in Python. Let's first look at what the game looks like and how to play it:

        After watching it, the little friends should understand how to play the game. Now, let's consider how to make this game from the perspective of "designer".  

        Three aspects should be considered in game production:

  • Game props (pictures, sound effects, etc.)
  • Logic control (design of game state)
  • UI design (game interface design)

        Game props are mainly obtained in two ways: one is to load pictures, and the other is to draw directly with code. In this game, the following pictures are needed:

inkspilllogo.png​​

 

inkspillresetbutton.png

 

inkspillsettings.png

 

inkspillsettingsbutton.png

inkspillspot.png

         

        The rest are drawn directly with code.

        Analyze the game interface, which is mainly composed of the following parts:

  • Large grid in the center of the interface
  • Life meter on the left of the interface
  • Six different color palettes directly below the interface
  • The "re game" button and the "Settings" button at the bottom right of the interface

         Before writing a game, we consider a question: how to design the difficulty of the game?

        The first thing we think of must be to control the number of small grids. The more the number, the more difficult it is.

        In addition, as for the difficulty of the game, we can also write a boxtochange variable to randomly change the surrounding lattices of a small lattice into the same color, and the number of such lattices is described by boxtochange. In this way, the value of boxtochange will be set higher for "simple difficulty" (the more the same color), and the value of boxtochange will be set lower for "difficult difficulty" (make fewer of the same color) (or set it directly to 0).

        First, let's take a look at what variables are needed to store what game states in this game (you can think about it yourself first):

# According to the setting interface, there are different overall grid sizes, number of small grids and life

# Small grid size
SMALL_BOX_SIZE = 60  # Size in pixels
MEDIUM_BOX_SIZE = 20
LARGE_BOX_SIZE = 11

# The size of the entire grid
SMALL_BOARD_SIZE = 6  # The size is in a small grid
MEDIUM_BOARD_SIZE = 17
LARGE_BOARD_SIZE = 30

# Maximum number of operations (life)
SMALL_MAX_LIFE = 10
MEDIUM_MAX_LIFE = 30
LARGE_MAX_LIFE = 64

FPS = 30
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
boxSize = MEDIUM_BOX_SIZE
PALETTE_GAP_SIZE = 10  # Palette interval size
PALETTE_SIZE = 45
EASY = 0  # Difficulty: simple
MEDIUM = 1  # Difficulty: medium
HARD = 2  # Difficulty: difficulty

difficulty = MEDIUM  # The game starts in "medium" mode
maxLife = MEDIUM_MAX_LIFE
boardWidth = MEDIUM_BOARD_SIZE
boardHeight = MEDIUM_BOARD_SIZE

#            R    G    B
WHITE = (255, 255, 255)
DARKGRAY = (70, 70, 70)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 128, 0)
PURPLE = (255, 0, 255)

# The first color in each scheme is the background color, and the next six are palette colors.
COLOR_SCHEMES = (((150, 200, 255), RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE),
                 ((0, 155, 104), (97, 215, 164), (228, 0, 69), (0, 125, 50), (204, 246, 0), (148, 0, 45),
                  (241, 109, 149)),
                 ((195, 179, 0), (255, 239, 115), (255, 226, 0), (147, 3, 167), (24, 38, 176), (166, 147, 0),
                  (197, 97, 211)),
                 ((85, 0, 0), (155, 39, 102), (0, 201, 13), (255, 118, 0), (206, 0, 113), (0, 130, 9), (255, 180, 115)),
                 ((191, 159, 64), (183, 182, 208), (4, 31, 183), (167, 184, 45), (122, 128, 212), (37, 204, 7),
                  (88, 155, 213)),
                 ((200, 33, 205), (116, 252, 185), (68, 56, 56), (52, 238, 83), (23, 149, 195), (222, 157, 227),
                  (212, 86, 185)))
# Treatment of color
for i in range(len(COLOR_SCHEMES)):
    assert len(COLOR_SCHEMES[i]) == 7, 'Color scheme %s No 7 colors!.' % (i)
# Background color, palette setting default color
bgColor = COLOR_SCHEMES[0][0]
paletteColors = COLOR_SCHEMES[0][1:]

          You will see that there are so many variables just to record the game state. But don't worry, little friends. We can easily deduce the function of this variable according to the name of the variable. The subsequent game logic control is actually changing the value of these variables.        

        Next, let's consider writing the framework of the whole game:

def main():
    # Variables other than functions are required
    global FPS_CLOCK, DISPLAY_SURF, LOGO_IMAGE, SPOT_IMAGE, SETTINGS_IMAGE, SETTINGS_BUTTON_IMAGE, RESET_BUTTON_IMAGE
    
    # initialization
    pygame.init()

    # Control frame rate
    FPS_CLOCK = pygame.time.Clock()
    
    # create a window
    DISPLAY_SURF = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))

    # Load picture
    LOGO_IMAGE = pygame.image.load('inkspilllogo.png')
    SPOT_IMAGE = pygame.image.load('inkspillspot.png')
    SETTINGS_IMAGE = pygame.image.load('inkspillsettings.png')
    SETTINGS_BUTTON_IMAGE = pygame.image.load('inkspillsettingsbutton.png')
    RESET_BUTTON_IMAGE = pygame.image.load('inkspillresetbutton.png')

    # Set window title
    pygame.display.set_caption('Ink overflow')

    # Generate interface
    mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
    life = maxLife

    # Record the color of the palette at the bottom of the last click interface
    lastPaletteClicked = None

    while True:  # Main game loop
        paletteClicked = None
        resetGame = False

        # Draw screen
        DISPLAY_SURF.fill(bgColor)

        # Load icons and related buttons
        drawLogoAndButtons()

        # Load the large grid in the center and randomly initialize the color of each small grid
        drawBoard(mainBoard)

        # Load the life meter on the left
        drawLifeMeter(life)

        # Loads the six color palettes at the bottom of the screen
        drawPalettes()

        # Determine whether the player wants to quit the game
        checkForQuit()

        There are many functions in the code, such as

        generateRandomBoard(),

         We haven't implemented them yet. Let's implement them:

I   generateRandomBoard()  :

         Generate the main game window - the whole large grid in the center:  

def generateRandomBoard(width, height, difficulty=MEDIUM):

    # Create a data structure with random colors for each small grid in the whole large grid.
    board = []
    for x in range(width):
        column = []
        for y in range(height):
            column.append(random.randint(0, len(paletteColors) - 1))
        board.append(column)

    # By setting some small grids to the same color as adjacent grids, it is easier to make the whole large grid with the same color.

    # Determine the number of cells to change.
    if difficulty == EASY:
        if boxSize == SMALL_BOX_SIZE:
            boxesToChange = 100
        else:
            boxesToChange = 1500
    elif difficulty == MEDIUM:
        if boxSize == SMALL_BOX_SIZE:
            boxesToChange = 5
        else:
            boxesToChange = 200
    else:
        boxesToChange = 0

    # Color of hanging grid:
    for i in range(boxesToChange):
        # Randomly select the box whose color you want to copy
        x = random.randint(1, width - 2)
        y = random.randint(1, height - 2)

        # Randomly select the neighbor grid to change
        direction = random.randint(0, 3)
        if direction == 0:  # Left, down
            board[x - 1][y] = board[x][y]
            board[x][y - 1] = board[x][y]
        elif direction == 1:  # Right, up
            board[x + 1][y] = board[x][y]
            board[x][y + 1] = board[x][y]
        elif direction == 2:  # Left, down
            board[x][y - 1] = board[x][y]
            board[x + 1][y] = board[x][y]
        else:  # Left, up
            board[x][y + 1] = board[x][y]
            board[x - 1][y] = board[x][y]

    return board

II   drawLogoAndButtons()  :

        Add some buttons and icons to the interface:

def drawLogoAndButtons():
    # Draw the ink overflow logo, set and reset buttons.
    # The bait() function is used to load the image onto the Surface object of distribute_surf
    DISPLAY_SURF.blit(LOGO_IMAGE, (WINDOW_WIDTH - LOGO_IMAGE.get_width(), 0))
    DISPLAY_SURF.blit(SETTINGS_BUTTON_IMAGE,
                      (WINDOW_WIDTH - SETTINGS_BUTTON_IMAGE.get_width(),
                       WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height()))
    DISPLAY_SURF.blit(RESET_BUTTON_IMAGE, (WINDOW_WIDTH - RESET_BUTTON_IMAGE.get_width(),
                                           WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height() - RESET_BUTTON_IMAGE.get_height()))

        A large number of coordinate operations are used in the code to control the position of the picture. Please feel it carefully.  

III   drawBoard(mainBoard)  :

         Load the large grid in the center of the interface and randomly initialize the color of each small grid:

def drawBoard(board, transparency=255):  # The transparency setting defaults to 255
    # The colored squares will be drawn to the temporary surface and then to the DISPLAY_SURF surface. In this way, we can draw a transparent square at the top of DISPLAY_SURF.
    tempSurf = pygame.Surface(DISPLAY_SURF.get_size())  # Get window size
    tempSurf = tempSurf.convert_alpha()   # Support transparency
    tempSurf.fill((0, 0, 0, 0))   # First, all are treated as black

    for x in range(boardWidth):
        for y in range(boardHeight):
            # Get the coordinates of the upper left corner of each small grid
            left, top = leftTopPixelCoordOfBox(x, y)
            r, g, b = paletteColors[board[x][y]]
            pygame.draw.rect(tempSurf, (r, g, b, transparency), (left, top, boxSize, boxSize))
    left, top = leftTopPixelCoordOfBox(0, 0)
    # In order to prevent the color of the small grid on the edge from being the same as the background color of the window, a layer of thin black lines with a thickness of 1px is wrapped outside the whole large grid
    pygame.draw.rect(tempSurf, BLACK, (left - 1, top - 1, boxSize * boardWidth + 1, boxSize * boardHeight + 1), 1)
    DISPLAY_SURF.blit(tempSurf, (0, 0))

        In code, leftTopPixelCoordOfBox(x,y) is called.

def leftTopPixelCoordOfBox(boxx, boxy):
    # Returns the x and y of the top left pixel of a small grid.
    # Notice that the entire grid is in the middle of the window
    x_margin = int((WINDOW_WIDTH - (boardWidth * boxSize)) / 2)  # x_margin is the distance between the leftmost of the whole large grid and the leftmost of the window
    y_margin = int((WINDOW_HEIGHT - (boardHeight * boxSize)) / 2)  # y_margin is the distance between the top edge of the whole large grid and the top edge of the window
    return boxx * boxSize + x_margin, boxy * boxSize + y_margin

IV   drawLifeMeter(life)  :

        Draw the life "count" on the left of the game interface and perform a simple life "logical calculation":

def drawLifeMeter(currentLife):
    # The 'life meter' is placed vertically, 20px away from the upper and lower edges
    lifeBoxSize = int((WINDOW_HEIGHT - 40) / maxLife)

    # Draw the background color of the "life meter". The upper left corner of the "life meter" is located at the coordinates (20,20), with a width of 20px and a height of 20 + (maxLife * lifeBoxSize) px
    pygame.draw.rect(DISPLAY_SURF, bgColor, (20, 20, 20, 20 + (maxLife * lifeBoxSize)))

    for i in range(maxLife):
        if currentLife >= (maxLife - i):  # Draw a solid red box
            pygame.draw.rect(DISPLAY_SURF, RED, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize))
        # Add 1px white border
        pygame.draw.rect(DISPLAY_SURF, WHITE, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize), 1)

        We have used a lot of coordinate operations in the positioning of graphics. Please feel it carefully.  

V   drawPalettes()  :

        Add six color palettes directly below the interface and carry out simple logic control:  

def drawPalettes():
    # Draw six color palettes at the bottom of the screen
    numColors = len(paletteColors)
    x_margin = int((WINDOW_WIDTH - ((PALETTE_SIZE * numColors) + (PALETTE_GAP_SIZE * (numColors - 1)))) / 2)
    for i in range(numColors):
        left = x_margin + (i * PALETTE_SIZE) + (i * PALETTE_GAP_SIZE)
        top = WINDOW_HEIGHT - PALETTE_SIZE - 10
        pygame.draw.rect(DISPLAY_SURF, paletteColors[i], (left, top, PALETTE_SIZE, PALETTE_SIZE))
        # For beauty, 2px is added to the corresponding color area with 2px thickness outside the grid
        pygame.draw.rect(DISPLAY_SURF, bgColor, (left + 2, top + 2, PALETTE_SIZE - 4, PALETTE_SIZE - 4), 2)

Vi    checkForQuit():

        What happens when a player wants to exit the game:

def checkForQuit():
    # If there are any exit events, terminate the program
    for event in pygame.event.get(QUIT):  # Get all exit events
        pygame.quit()  # Terminate if there are any exit events
        sys.exit()
    for event in pygame.event.get(KEYUP):  # Get all KEYUP events
        if event.key == K_ESCAPE:
            pygame.quit()  # If the KEYUP event is for the Esc key, it terminates
            sys.exit()
        pygame.event.post(event)  # Replace the other KEYUP event objects

          Next, let's consider the main logical control of the game.

         Since we did not create a button, when the "mouse click" Event occurs, we try to obtain the location coordinates of its click (using the coordinates of the Event object)   pos   Attribute: mousex, mousey = event.pos), compare this coordinate with the position area of an icon on the interface (use pygame.rest.colleendpoint (mousex, mousey) function). If it is in this area, it means that the player wants to click the icon to realize the corresponding function (for example, clicking the "reset" icon with the mouse means that the player wants to restart the game), We can write code to respond. Based on this idea, we can complete the main() function:  

def main():
    global FPS_CLOCK, DISPLAY_SURF, LOGO_IMAGE, SPOT_IMAGE, SETTINGS_IMAGE, SETTINGS_BUTTON_IMAGE, RESET_BUTTON_IMAGE

    pygame.init()
    FPS_CLOCK = pygame.time.Clock()
    DISPLAY_SURF = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))

    # Load picture
    LOGO_IMAGE = pygame.image.load('inkspilllogo.png')
    SPOT_IMAGE = pygame.image.load('inkspillspot.png')
    SETTINGS_IMAGE = pygame.image.load('inkspillsettings.png')
    SETTINGS_BUTTON_IMAGE = pygame.image.load('inkspillsettingsbutton.png')
    RESET_BUTTON_IMAGE = pygame.image.load('inkspillresetbutton.png')

    # Set window title
    pygame.display.set_caption('Ink overflow')
    mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
    life = maxLife
    lastPaletteClicked = None

    while True:  # Main game loop
        paletteClicked = None
        resetGame = False

        # Draw screen
        DISPLAY_SURF.fill(bgColor)
        # Load icons and related buttons
        drawLogoAndButtons()
        # Load the large grid in the center and randomly initialize the color of each small grid
        drawBoard(mainBoard)
        # Load the life meter on the left
        drawLifeMeter(life)
        # Loads the six color palettes at the bottom of the screen
        drawPalettes()
        # Determine whether the player wants to quit the game
        checkForQuit()

        for event in pygame.event.get():  # Event handling loop
            if event.type == MOUSEBUTTONUP:  # If the event is a mouse click
                mousex, mousey = event.pos   # Get the coordinates of mouse click
                # If you click "SETTINGS"
                if pygame.Rect(WINDOW_WIDTH - SETTINGS_BUTTON_IMAGE.get_width(),
                               WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height(),
                               SETTINGS_BUTTON_IMAGE.get_width(),
                               SETTINGS_BUTTON_IMAGE.get_height()).collidepoint(mousex, mousey):
                    # Display the setting interface
                    resetGame = showSettingsScreen()   # showSettingsScreen() to be implemented
                # If you click "RESET"
                elif pygame.Rect(WINDOW_WIDTH - RESET_BUTTON_IMAGE.get_width(),
                                 WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height() - RESET_BUTTON_IMAGE.get_height(),
                                 RESET_BUTTON_IMAGE.get_width(),
                                 RESET_BUTTON_IMAGE.get_height()).collidepoint(mousex, mousey):
                    # Start the game again
                    resetGame = True
                else:
                    # Check whether the palette button is clicked. If so, return to the index of the color clicked in the six palettes
                    paletteClicked = getColorOfPaletteAt(mousex, mousey)  # getColorOfPaletteAt() to be implemented

        if paletteClicked is not None and paletteClicked != lastPaletteClicked:

            # The palette button you clicked is different from the one you clicked last time, preventing the mouse from accidentally clicking the same palette twice
            lastPaletteClicked = paletteClicked

            # Fill the large grid with color, and floodAnimation() is to be implemented
            floodAnimation(mainBoard, paletteClicked)
            # Click once to reduce "life" by 1
            life -= 1

            resetGame = False
            if hasWon(mainBoard):  # If you win, hasWon() will be implemented
                for i in range(4):  # Successful interface effect: flash the border 4 times
                    flashBorderAnimation(WHITE, mainBoard)   # flashBorderAnimation() to be implemented
                # After "flash", restart the game
                resetGame = True
                # Pause for 2s before starting the game
                pygame.time.wait(2000)
            elif life == 0:
                # Life is reduced to zero and the player fails
                drawLifeMeter(0)
                # Update interface
                pygame.display.update()
                # Wait for 0.4s
                pygame.time.wait(400)
                # Failed end effect: use black "flash" 4 times
                for i in range(4):
                    flashBorderAnimation(BLACK, mainBoard).  # flashBorderAnimation() to be implemented
                resetGame = True
                # Restart the game after a pause of 2s
                pygame.time.wait(2000)

        if resetGame:
            # Restart the game
            mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
            life = maxLife
            lastPaletteClicked = None
        # Update interface
        pygame.display.update()
        # Control frame rate
        FPS_CLOCK.tick(FPS)

        In the above main logic control codes, some functions are not implemented:

         showSettingsScreen(): displays the "Settings" interface and provides corresponding logic control:

def showSettingsScreen():
    # Get global variables
    global difficulty, boxSize, boardWidth, boardHeight, maxLife, paletteColors, bgColor

    # The pixel coordinates in this function are obtained by loading the inkspearsettings.png image into the graphics editor and reading the pixel coordinates from it

    origDifficulty = difficulty
    origBoxSize = boxSize
    screenNeedsRedraw = True

    while True:
        if screenNeedsRedraw:
            DISPLAY_SURF.fill(bgColor)
            # Load settings picture
            DISPLAY_SURF.blit(SETTINGS_IMAGE, (0, 0))

            # Place the ink picture marker to the left of the selected color
            if difficulty == EASY:
                DISPLAY_SURF.blit(SPOT_IMAGE, (30, 4))
            if difficulty == MEDIUM:
                DISPLAY_SURF.blit(SPOT_IMAGE, (8, 41))
            if difficulty == HARD:
                DISPLAY_SURF.blit(SPOT_IMAGE, (30, 76))

            # Places an ink marker next to the selected size
            if boxSize == SMALL_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (22, 150))
            if boxSize == MEDIUM_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (11, 185))
            if boxSize == LARGE_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (24, 220))

            # The color selection box on the right of the loading setting interface
            for i in range(len(COLOR_SCHEMES)):
                drawColorSchemeBoxes(500, i * 60 + 30, i)  # To be realized

            # Update interface
            pygame.display.update()

        screenNeedsRedraw = False  # By default, the screen is not redrawn
        # Event handling loop
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYUP:
                if event.key == K_ESCAPE:
                    # Set the Esc key on the screen to return to the game
                    return not (origDifficulty == difficulty and origBoxSize == boxSize)
            elif event.type == MOUSEBUTTONUP:
                screenNeedsRedraw = True  # The screen should be redrawn
                mousex, mousey = event.pos  # Coordinates of mouse click

                # Check for a click on the difficulty button
                if pygame.Rect(74, 16, 111, 30).collidepoint(mousex, mousey):
                    difficulty = EASY
                elif pygame.Rect(53, 50, 104, 29).collidepoint(mousex, mousey):
                    difficulty = MEDIUM
                elif pygame.Rect(72, 85, 65, 31).collidepoint(mousex, mousey):
                    difficulty = HARD

                # Check whether there is a click on the size button
                elif pygame.Rect(63, 156, 84, 31).collidepoint(mousex, mousey):
                    # Small board size setting:
                    boxSize = SMALL_BOX_SIZE
                    boardWidth = SMALL_BOARD_SIZE
                    boardHeight = SMALL_BOARD_SIZE
                    maxLife = SMALL_MAX_LIFE
                elif pygame.Rect(52, 192, 106, 32).collidepoint(mousex, mousey):
                    # Middle plate size setting:
                    boxSize = MEDIUM_BOX_SIZE
                    boardWidth = MEDIUM_BOARD_SIZE
                    boardHeight = MEDIUM_BOARD_SIZE
                    maxLife = MEDIUM_MAX_LIFE
                elif pygame.Rect(67, 228, 58, 37).collidepoint(mousex, mousey):
                    # Large plate size setting:
                    boxSize = LARGE_BOX_SIZE
                    boardWidth = LARGE_BOARD_SIZE
                    boardHeight = LARGE_BOARD_SIZE
                    maxLife = LARGE_MAX_LIFE
                elif pygame.Rect(178, 418, 215, 34).collidepoint(mousex, mousey):
                    # Click the "Back To Game" button
                    return not (origDifficulty == difficulty and origBoxSize == boxSize)

                for i in range(len(COLOR_SCHEMES)):
                    # Click the color scheme button
                    if pygame.Rect(500, 30 + i * 60, MEDIUM_BOX_SIZE * 3, MEDIUM_BOX_SIZE * 2).collidepoint(mousex,
                                                                                                            mousey):
                        bgColor = COLOR_SCHEMES[i][0]
                        paletteColors = COLOR_SCHEMES[i][1:]

          The above code is called.   drawColorSchemeBoxes(): the implementation is as follows:

def drawColorSchemeBoxes(x, y, schemeNum):
    # Draws the color scheme box displayed on the settings screen
    for boxy in range(2):
        for boxx in range(3):
            pygame.draw.rect(DISPLAY_SURF, COLOR_SCHEMES[schemeNum][3 * boxy + boxx + 1],
                             (x + MEDIUM_BOX_SIZE * boxx, y + MEDIUM_BOX_SIZE * boxy, MEDIUM_BOX_SIZE, MEDIUM_BOX_SIZE))
            if paletteColors == COLOR_SCHEMES[schemeNum][1:]:
                # Place the ink picture next to the selected color scheme
                DISPLAY_SURF.blit(SPOT_IMAGE, (x - 50, y))

          getColorOfPaletteAt(mousex, mousey): get the index of the clicked palette color:

def getColorOfPaletteAt(x, y):
    # Returns the index of the palette color covered by the x and y parameters
    numColors = len(paletteColors)
    xmargin = int((WINDOW_WIDTH - ((PALETTE_SIZE * numColors) + (PALETTE_GAP_SIZE * (numColors - 1)))) / 2)
    # The bottom of the palette is 10px from the bottom of the window
    top = WINDOW_HEIGHT - PALETTE_SIZE - 10
    for i in range(numColors):
        # Coordinates of the upper left corner of the six palettes: (left,top)
        left = xmargin + (i * PALETTE_SIZE) + (i * PALETTE_GAP_SIZE)
        r = pygame.Rect(left, top, PALETTE_SIZE, PALETTE_SIZE)
        # Find out if the mouse click area is in any palette
        if r.collidepoint(x, y):
            return i  # If yes, returns the sequence number of the clicked palette
    # If x and y are not on any palette, return None
    return None

          hasWon():   Judge whether the player wins or not, and realize the following:

def hasWon(board):
    # If the color of the whole chessboard is the same, it means that the player wins
    for x in range(boardWidth):
        for y in range(boardHeight):
            # As long as you find a color different from the color in the upper left corner, the player has not won yet
            if board[x][y] != board[0][0]:
                return False
    return True

          Flood animation (mainBoard, paletteClicked): fill the mainBoard (large grid in the center) with the color corresponding to paletteClicked:

def floodAnimation(board, paletteClicked, animationSpeed=25):
    origBoard = copy.deepcopy(board)  # Deep copy of the whole grid
    # Start the same color filling from the grid in the upper left corner, and the floodFill() will be implemented
    floodFill(board, board[0][0], paletteClicked, 0, 0)

    for transparency in range(0, 255, animationSpeed):
        # The "new" big grid slowly becomes opaque on the big grid
        drawBoard(origBoard)
        # Update transparency
        drawBoard(board, transparency)
        # Update interface
        pygame.display.update()
        # Control frame rate
        FPS_CLOCK.tick(FPS)

          floodFill(): the filling algorithm is implemented as follows:

def floodFill(board, oldColor, newColor, x, y):
    # Flood filling algorithm
    if oldColor == newColor or board[x][y] != oldColor:
        # If the color in the upper left corner is not adjacent to the click color, return directly
        return
    board[x][y] = newColor  # Change the color of the current grid
    # Recursively call any adjacent lattice:
    if x > 0:
        floodFill(board, oldColor, newColor, x - 1, y)
    if x < boardWidth - 1:
        floodFill(board, oldColor, newColor, x + 1, y)
    if y > 0:
        floodFill(board, oldColor, newColor, x, y - 1)
    if y < boardHeight - 1:
        floodFill(board, oldColor, newColor, x, y + 1)

          Flashborder animation (color, mainboard): end screen of game victory or failure:

def flashBorderAnimation(color, board, animationSpeed=30):
    # After the "flash" effect ends, you need to return to the original interface (the game has just ended). Here, save the original interface first
    origSurf = DISPLAY_SURF.copy()
    flashSurf = pygame.Surface(DISPLAY_SURF.get_size())
    flashSurf = flashSurf.convert_alpha()
    # Achieve "flash" effect
    for start, end, step in ((0, 256, 1), (255, 0, -1)):
        # The first iteration of the outer loop sets the transparency of the inner loop from 0 to 255, and the second iteration sets the transparency from 255 to 0. This is "flash".
        for transparency in range(start, end, animationSpeed * step):
            DISPLAY_SURF.blit(origSurf, (0, 0))
            r, g, b = color
            flashSurf.fill((r, g, b, transparency))
            DISPLAY_SURF.blit(flashSurf, (0, 0))
            drawBoard(board)  # Draw the board at the top of the transparent layer
            # Update interface
            pygame.display.update()
            # Control frame rate
            FPS_CLOCK.tick(FPS)
    # Draw the original interface (just after the game)
    DISPLAY_SURF.blit(origSurf, (0, 0))

         ...... I guess the little friends may have been confused. When playing games, they will consider a lot of things. We can really make our own games only after we have a good understanding of every detail.

        Finally, attach the complete game code:

import random, sys, copy, pygame
from pygame.locals import *  # Import all Pygame constants

# According to the setting interface, there are different large grid sizes, small grid numbers and service life

# Small grid size
SMALL_BOX_SIZE = 60  # Size in pixels
MEDIUM_BOX_SIZE = 20
LARGE_BOX_SIZE = 11

# The size of the entire grid
SMALL_BOARD_SIZE = 6  # The size is in a small grid
MEDIUM_BOARD_SIZE = 17
LARGE_BOARD_SIZE = 30

# Maximum number of operations (life)
SMALL_MAX_LIFE = 10
MEDIUM_MAX_LIFE = 30
LARGE_MAX_LIFE = 64

FPS = 30
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
boxSize = MEDIUM_BOX_SIZE
PALETTE_GAP_SIZE = 10  # Palette interval size
PALETTE_SIZE = 45
EASY = 0  # Difficulty: simple
MEDIUM = 1  # Difficulty: medium
HARD = 2  # Difficulty: difficulty

difficulty = MEDIUM  # The game starts in "medium" mode
maxLife = MEDIUM_MAX_LIFE
boardWidth = MEDIUM_BOARD_SIZE
boardHeight = MEDIUM_BOARD_SIZE

#            R    G    B
WHITE = (255, 255, 255)
DARKGRAY = (70, 70, 70)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 128, 0)
PURPLE = (255, 0, 255)

# The first color in each scheme is the background color, and the next six are palette colors.
COLOR_SCHEMES = (((150, 200, 255), RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE),
                 ((0, 155, 104), (97, 215, 164), (228, 0, 69), (0, 125, 50), (204, 246, 0), (148, 0, 45),
                  (241, 109, 149)),
                 ((195, 179, 0), (255, 239, 115), (255, 226, 0), (147, 3, 167), (24, 38, 176), (166, 147, 0),
                  (197, 97, 211)),
                 ((85, 0, 0), (155, 39, 102), (0, 201, 13), (255, 118, 0), (206, 0, 113), (0, 130, 9), (255, 180, 115)),
                 ((191, 159, 64), (183, 182, 208), (4, 31, 183), (167, 184, 45), (122, 128, 212), (37, 204, 7),
                  (88, 155, 213)),
                 ((200, 33, 205), (116, 252, 185), (68, 56, 56), (52, 238, 83), (23, 149, 195), (222, 157, 227),
                  (212, 86, 185)))
# Treatment of color
for i in range(len(COLOR_SCHEMES)):
    assert len(COLOR_SCHEMES[i]) == 7, 'Color scheme %s No 7 colors!.' % (i)
# Background color, palette setting default color
bgColor = COLOR_SCHEMES[0][0]
paletteColors = COLOR_SCHEMES[0][1:]


def main():
    global FPS_CLOCK, DISPLAY_SURF, LOGO_IMAGE, SPOT_IMAGE, SETTINGS_IMAGE, SETTINGS_BUTTON_IMAGE, RESET_BUTTON_IMAGE

    pygame.init()
    FPS_CLOCK = pygame.time.Clock()
    DISPLAY_SURF = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))

    # Load picture
    LOGO_IMAGE = pygame.image.load('inkspilllogo.png')
    SPOT_IMAGE = pygame.image.load('inkspillspot.png')
    SETTINGS_IMAGE = pygame.image.load('inkspillsettings.png')
    SETTINGS_BUTTON_IMAGE = pygame.image.load('inkspillsettingsbutton.png')
    RESET_BUTTON_IMAGE = pygame.image.load('inkspillresetbutton.png')

    # Set window title
    pygame.display.set_caption('Ink overflow')
    mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
    life = maxLife
    lastPaletteClicked = None

    while True:  # Main game loop
        paletteClicked = None
        resetGame = False

        # Draw screen
        DISPLAY_SURF.fill(bgColor)
        # Load icons and related buttons
        drawLogoAndButtons()
        # Load the large grid in the center and randomly initialize the color of each small grid
        drawBoard(mainBoard)
        # Load the life meter on the left
        drawLifeMeter(life)
        # Loads the six color palettes at the bottom of the screen
        drawPalettes()
        # Determine whether the player wants to quit the game
        checkForQuit()

        for event in pygame.event.get():  # Event handling loop
            if event.type == MOUSEBUTTONUP:  # If the event is a mouse click
                mousex, mousey = event.pos   # Get the coordinates of mouse click
                # If you click "SETTINGS"
                if pygame.Rect(WINDOW_WIDTH - SETTINGS_BUTTON_IMAGE.get_width(),
                               WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height(),
                               SETTINGS_BUTTON_IMAGE.get_width(),
                               SETTINGS_BUTTON_IMAGE.get_height()).collidepoint(mousex, mousey):
                    # Display the setting interface
                    resetGame = showSettingsScreen()
                # If you click "RESET"
                elif pygame.Rect(WINDOW_WIDTH - RESET_BUTTON_IMAGE.get_width(),
                                 WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height() - RESET_BUTTON_IMAGE.get_height(),
                                 RESET_BUTTON_IMAGE.get_width(),
                                 RESET_BUTTON_IMAGE.get_height()).collidepoint(mousex, mousey):
                    # Start the game again
                    resetGame = True
                else:
                    # Check that the palette button is clicked
                    paletteClicked = getColorOfPaletteAt(mousex, mousey)

        if paletteClicked is not None and paletteClicked != lastPaletteClicked:

            # The palette button you clicked is different from the one you clicked last time, preventing the player from accidentally clicking the same palette twice
            lastPaletteClicked = paletteClicked

            # fill color
            floodAnimation(mainBoard, paletteClicked)
            # Click once to reduce "life" by 1
            life -= 1

            resetGame = False
            if hasWon(mainBoard):  # If you win
                for i in range(4):  # Successful interface effect: flash the border 4 times
                    flashBorderAnimation(WHITE, mainBoard)
                # After "flash", restart the game
                resetGame = True
                # Pause for 2s before starting the game
                pygame.time.wait(2000)
            elif life == 0:
                # Life is reduced to zero and the player fails
                drawLifeMeter(0)
                # Update interface
                pygame.display.update()
                # Wait for 0.4s
                pygame.time.wait(400)
                # Failed end effect: use black "flash" 4 times
                for i in range(4):
                    flashBorderAnimation(BLACK, mainBoard)
                resetGame = True
                # Restart the game after a pause of 2s
                pygame.time.wait(2000)

        if resetGame:
            # Restart the game
            mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
            life = maxLife
            lastPaletteClicked = None
        # Update interface
        pygame.display.update()
        # Control frame rate
        FPS_CLOCK.tick(FPS)


def checkForQuit():
    # If there are any exit events, terminate the program
    for event in pygame.event.get(QUIT):  # Get all exit events
        pygame.quit()  # Terminate if there are any exit events
        sys.exit()
    for event in pygame.event.get(KEYUP):  # Get all KEYUP events
        if event.key == K_ESCAPE:
            pygame.quit()  # If the KEYUP event is for the Esc key, it terminates
            sys.exit()
        pygame.event.post(event)  # Replace the other KEYUP event objects


def hasWon(board):
    # If the color of the whole chessboard is the same, it means that the player wins
    for x in range(boardWidth):
        for y in range(boardHeight):
            # As long as you find a color different from the color in the upper left corner, the player has not won yet
            if board[x][y] != board[0][0]:
                return False
    return True


def showSettingsScreen():
    # Get global variables
    global difficulty, boxSize, boardWidth, boardHeight, maxLife, paletteColors, bgColor

    # The pixel coordinates in this function are obtained by loading the inkspearsettings.png image into the graphics editor and reading the pixel coordinates from it

    origDifficulty = difficulty
    origBoxSize = boxSize
    screenNeedsRedraw = True

    while True:
        if screenNeedsRedraw:
            DISPLAY_SURF.fill(bgColor)
            # Load settings picture
            DISPLAY_SURF.blit(SETTINGS_IMAGE, (0, 0))

            # Place the ink picture marker to the left of the selected color
            if difficulty == EASY:
                DISPLAY_SURF.blit(SPOT_IMAGE, (30, 4))
            if difficulty == MEDIUM:
                DISPLAY_SURF.blit(SPOT_IMAGE, (8, 41))
            if difficulty == HARD:
                DISPLAY_SURF.blit(SPOT_IMAGE, (30, 76))

            # Places an ink marker next to the selected size
            if boxSize == SMALL_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (22, 150))
            if boxSize == MEDIUM_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (11, 185))
            if boxSize == LARGE_BOX_SIZE:
                DISPLAY_SURF.blit(SPOT_IMAGE, (24, 220))

            # The color selection box on the right of the loading setting interface
            for i in range(len(COLOR_SCHEMES)):
                drawColorSchemeBoxes(500, i * 60 + 30, i)

            # Update interface
            pygame.display.update()

        screenNeedsRedraw = False  # By default, the screen is not redrawn
        # Event handling loop
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYUP:
                if event.key == K_ESCAPE:
                    # Set the Esc key on the screen to return to the game
                    return not (origDifficulty == difficulty and origBoxSize == boxSize)
            elif event.type == MOUSEBUTTONUP:
                screenNeedsRedraw = True  # The screen should be redrawn
                mousex, mousey = event.pos  # Coordinates of mouse click

                # Check for a click on the difficulty button
                if pygame.Rect(74, 16, 111, 30).collidepoint(mousex, mousey):
                    difficulty = EASY
                elif pygame.Rect(53, 50, 104, 29).collidepoint(mousex, mousey):
                    difficulty = MEDIUM
                elif pygame.Rect(72, 85, 65, 31).collidepoint(mousex, mousey):
                    difficulty = HARD

                # Check whether there is a click on the size button
                elif pygame.Rect(63, 156, 84, 31).collidepoint(mousex, mousey):
                    # Small board size setting:
                    boxSize = SMALL_BOX_SIZE
                    boardWidth = SMALL_BOARD_SIZE
                    boardHeight = SMALL_BOARD_SIZE
                    maxLife = SMALL_MAX_LIFE
                elif pygame.Rect(52, 192, 106, 32).collidepoint(mousex, mousey):
                    # Middle plate size setting:
                    boxSize = MEDIUM_BOX_SIZE
                    boardWidth = MEDIUM_BOARD_SIZE
                    boardHeight = MEDIUM_BOARD_SIZE
                    maxLife = MEDIUM_MAX_LIFE
                elif pygame.Rect(67, 228, 58, 37).collidepoint(mousex, mousey):
                    # Large plate size setting:
                    boxSize = LARGE_BOX_SIZE
                    boardWidth = LARGE_BOARD_SIZE
                    boardHeight = LARGE_BOARD_SIZE
                    maxLife = LARGE_MAX_LIFE
                elif pygame.Rect(178, 418, 215, 34).collidepoint(mousex, mousey):
                    # Click the "Back To Game" button
                    return not (origDifficulty == difficulty and origBoxSize == boxSize)

                for i in range(len(COLOR_SCHEMES)):
                    # Click the color scheme button
                    if pygame.Rect(500, 30 + i * 60, MEDIUM_BOX_SIZE * 3, MEDIUM_BOX_SIZE * 2).collidepoint(mousex,
                                                                                                            mousey):
                        bgColor = COLOR_SCHEMES[i][0]
                        paletteColors = COLOR_SCHEMES[i][1:]


def drawColorSchemeBoxes(x, y, schemeNum):
    # Draws the color scheme box displayed on the settings screen
    for boxy in range(2):
        for boxx in range(3):
            pygame.draw.rect(DISPLAY_SURF, COLOR_SCHEMES[schemeNum][3 * boxy + boxx + 1],
                             (x + MEDIUM_BOX_SIZE * boxx, y + MEDIUM_BOX_SIZE * boxy, MEDIUM_BOX_SIZE, MEDIUM_BOX_SIZE))
            if paletteColors == COLOR_SCHEMES[schemeNum][1:]:
                # Place the ink picture next to the selected color scheme
                DISPLAY_SURF.blit(SPOT_IMAGE, (x - 50, y))


def flashBorderAnimation(color, board, animationSpeed=30):
    # After the "flash" effect ends, you need to return to the original interface (the game has just ended). Here, save the original interface first
    origSurf = DISPLAY_SURF.copy()
    flashSurf = pygame.Surface(DISPLAY_SURF.get_size())
    flashSurf = flashSurf.convert_alpha()
    # Achieve "flash" effect
    for start, end, step in ((0, 256, 1), (255, 0, -1)):
        # The first iteration of the outer loop sets the transparency of the inner loop from 0 to 255, and the second iteration sets the transparency from 255 to 0. This is "flash".
        for transparency in range(start, end, animationSpeed * step):
            DISPLAY_SURF.blit(origSurf, (0, 0))
            r, g, b = color
            flashSurf.fill((r, g, b, transparency))
            DISPLAY_SURF.blit(flashSurf, (0, 0))
            drawBoard(board)  # Draw the board at the top of the transparent layer
            # Update interface
            pygame.display.update()
            # Control frame rate
            FPS_CLOCK.tick(FPS)
    # Draw the original interface (just after the game)
    DISPLAY_SURF.blit(origSurf, (0, 0))


def floodAnimation(board, paletteClicked, animationSpeed=25):
    origBoard = copy.deepcopy(board)  # Deep copy of the whole grid
    # Fill in the same color from the grid in the upper left corner
    floodFill(board, board[0][0], paletteClicked, 0, 0)

    for transparency in range(0, 255, animationSpeed):
        # The "new" big grid slowly becomes opaque on the big grid
        drawBoard(origBoard)
        # Update transparency
        drawBoard(board, transparency)
        # Update interface
        pygame.display.update()
        # Control frame rate
        FPS_CLOCK.tick(FPS)


def generateRandomBoard(width, height, difficulty=MEDIUM):
    # Create a data structure with random colors for each small grid in the whole large grid.
    board = []
    for x in range(width):
        column = []
        for y in range(height):
            column.append(random.randint(0, len(paletteColors) - 1))
        board.append(column)

    # By setting some small grids to the same color as adjacent grids, it is easier to solve the whole large grid.

    # Determine the number of cells to change.
    if difficulty == EASY:
        if boxSize == SMALL_BOX_SIZE:
            boxesToChange = 100
        else:
            boxesToChange = 1500
    elif difficulty == MEDIUM:
        if boxSize == SMALL_BOX_SIZE:
            boxesToChange = 5
        else:
            boxesToChange = 200
    else:
        boxesToChange = 0

    # Color of hanging grid:
    for i in range(boxesToChange):
        # Randomly select the box whose color you want to copy
        x = random.randint(1, width - 2)
        y = random.randint(1, height - 2)

        # Randomly select the neighbor grid to change
        direction = random.randint(0, 3)
        if direction == 0:  # Left, down
            board[x - 1][y] = board[x][y]
            board[x][y - 1] = board[x][y]
        elif direction == 1:  # Right, up
            board[x + 1][y] = board[x][y]
            board[x][y + 1] = board[x][y]
        elif direction == 2:  # Left, down
            board[x][y - 1] = board[x][y]
            board[x + 1][y] = board[x][y]
        else:  # Left, up
            board[x][y + 1] = board[x][y]
            board[x - 1][y] = board[x][y]
    return board


def drawLogoAndButtons():
    # Draw the ink overflow logo, set and reset buttons.
    DISPLAY_SURF.blit(LOGO_IMAGE, (WINDOW_WIDTH - LOGO_IMAGE.get_width(), 0))
    DISPLAY_SURF.blit(SETTINGS_BUTTON_IMAGE,
                      (WINDOW_WIDTH - SETTINGS_BUTTON_IMAGE.get_width(),
                       WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height()))
    DISPLAY_SURF.blit(RESET_BUTTON_IMAGE, (WINDOW_WIDTH - RESET_BUTTON_IMAGE.get_width(),
                                           WINDOW_HEIGHT - SETTINGS_BUTTON_IMAGE.get_height() - RESET_BUTTON_IMAGE.get_height()))


def drawBoard(board, transparency=255):  # The transparency setting defaults to 255
    # Colored squares are drawn to the temporary surface and then to DISPLAY_SURF surface. So we can display_ The top of surf draws a transparent square.
    tempSurf = pygame.Surface(DISPLAY_SURF.get_size())  # Get window size
    tempSurf = tempSurf.convert_alpha()   # Support transparency
    tempSurf.fill((0, 0, 0, 0))   # First, all are treated as black

    for x in range(boardWidth):
        for y in range(boardHeight):
            left, top = leftTopPixelCoordOfBox(x, y)
            r, g, b = paletteColors[board[x][y]]
            pygame.draw.rect(tempSurf, (r, g, b, transparency), (left, top, boxSize, boxSize))
    left, top = leftTopPixelCoordOfBox(0, 0)
    # In order to prevent the color of the small grid on the edge from being the same as the background color of the window, a layer of thin black lines with a thickness of 1px is wrapped outside the whole large grid
    pygame.draw.rect(tempSurf, BLACK, (left - 1, top - 1, boxSize * boardWidth + 1, boxSize * boardHeight + 1), 1)
    DISPLAY_SURF.blit(tempSurf, (0, 0))


def drawPalettes():
    # Draw six color palettes at the bottom of the screen
    numColors = len(paletteColors)
    x_margin = int((WINDOW_WIDTH - ((PALETTE_SIZE * numColors) + (PALETTE_GAP_SIZE * (numColors - 1)))) / 2)
    for i in range(numColors):
        left = x_margin + (i * PALETTE_SIZE) + (i * PALETTE_GAP_SIZE)
        top = WINDOW_HEIGHT - PALETTE_SIZE - 10
        pygame.draw.rect(DISPLAY_SURF, paletteColors[i], (left, top, PALETTE_SIZE, PALETTE_SIZE))
        # For beauty, 2px is added to the corresponding color area with 2px thickness outside the grid
        pygame.draw.rect(DISPLAY_SURF, bgColor, (left + 2, top + 2, PALETTE_SIZE - 4, PALETTE_SIZE - 4), 2)


def drawLifeMeter(currentLife):
    # The 'life meter' is placed vertically, 20px away from the upper and lower edges
    lifeBoxSize = int((WINDOW_HEIGHT - 40) / maxLife)

    # Draw the background color of the "life meter". The upper left corner of the "life meter" is located at the coordinates (20,20), with a width of 20px and a height of 20 + (maxLife * lifeBoxSize) px
    pygame.draw.rect(DISPLAY_SURF, bgColor, (20, 20, 20, 20 + (maxLife * lifeBoxSize)))

    for i in range(maxLife):
        if currentLife >= (maxLife - i):  # Draw a solid red box
            pygame.draw.rect(DISPLAY_SURF, RED, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize))
        # Add 1px white border
        pygame.draw.rect(DISPLAY_SURF, WHITE, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize), 1)


def getColorOfPaletteAt(x, y):
    # Returns the index of the palette color covered by the x and y parameters
    numColors = len(paletteColors)
    xmargin = int((WINDOW_WIDTH - ((PALETTE_SIZE * numColors) + (PALETTE_GAP_SIZE * (numColors - 1)))) / 2)
    # The bottom of the palette is 10px from the bottom of the window
    top = WINDOW_HEIGHT - PALETTE_SIZE - 10
    for i in range(numColors):
        # Coordinates of the upper left corner of the six palettes: (left,top)
        left = xmargin + (i * PALETTE_SIZE) + (i * PALETTE_GAP_SIZE)
        r = pygame.Rect(left, top, PALETTE_SIZE, PALETTE_SIZE)
        # Find out if the mouse click area is in any palette
        if r.collidepoint(x, y):
            return i  # If yes, returns the sequence number of the clicked palette
    # If x and y are not on any palette, return None
    return None


def floodFill(board, oldColor, newColor, x, y):
    # Flood filling algorithm
    if oldColor == newColor or board[x][y] != oldColor:
        # If the color in the upper left corner is not adjacent to the click color, return directly
        return
    board[x][y] = newColor  # Change the color of the current grid
    # Recursively call any adjacent lattice:
    if x > 0:
        floodFill(board, oldColor, newColor, x - 1, y)
    if x < boardWidth - 1:
        floodFill(board, oldColor, newColor, x + 1, y)
    if y > 0:
        floodFill(board, oldColor, newColor, x, y - 1)
    if y < boardHeight - 1:
        floodFill(board, oldColor, newColor, x, y + 1)


def leftTopPixelCoordOfBox(boxx, boxy):
    # Returns the x and y of the top left pixel of a small grid.
    # Notice that the entire grid is in the middle of the window
    x_margin = int((WINDOW_WIDTH - (boardWidth * boxSize)) / 2)  # x_margin is the distance between the leftmost of the whole large grid and the leftmost of the window
    y_margin = int((WINDOW_HEIGHT - (boardHeight * boxSize)) / 2)  # y_margin is the distance between the top edge of the whole large grid and the top edge of the window
    return boxx * boxSize + x_margin, boxy * boxSize + y_margin


if __name__ == '__main__':
    main()

          Like the little friends point a praise, encourage and support it ~

 

 

        

Posted by cigardude on Sun, 10 Oct 2021 07:06:25 -0700