2
\$\begingroup\$

I designed a game of Minesweeper where the board was displayed using text, and it worked fine. Using the same pseudocode, I tried to write the game with a GUI using Tkinter, and made a few modifications to account for having a GUI. Here is my code:

import random
import tkinter as tk
from tkinter import messagebox


def create_game_gui():
    for widget in root.slaves():
        widget.destroy()

    for row in range(board_width):
        for column in range(board_height):
            if display_array[row][column]:
                tile_button = tk.Button(root, text=str(data_array[row][column]), height=tile_size, width=tile_size)
                tile_button["state"] = tk.DISABLED
                tile_button.grid(row=row, column=column)
                tile_button.bind("<Button-2>", lambda event, row=row, column=column: quick_uncover(event, row, column))
                tile_button.bind("<Button-3>", lambda event, row=row, column=column: quick_uncover(event, row, column))
            else:
                if flag_array[row][column]:
                    text = "F"
                else:
                    text = " "
                if data_array[row][column] == -1:
                    tile_button = tk.Button(root, text=text, height=tile_size, width=tile_size, command=game_over)
                elif data_array[row][column] == 0:
                    tile_button = tk.Button(root, text=text, height=tile_size, width=tile_size, command=lambda row=row, column=column: flip_zero(row, column))
                else:
                    tile_button = tk.Button(root, text=text, height=tile_size, width=tile_size, command=lambda row=row, column=column: flip_number(row, column))
                tile_button.bind("<Button-2>", lambda event, row=row, column=column: flag(event, row, column))
                tile_button.bind("<Button-3>", lambda event, row=row, column=column: flag(event, row, column))
                tile_button.grid(row=row, column=column)


def game_over():
    messagebox.showerror("Game Over", "You lost!")
    exit()


def flip_adjacent_zeros(start_row, start_column):
    display_array[start_row][start_column] = True
    for row in range(max(0, start_row - 1), min(board_height - 1, start_row + 1) + 1):
        for column in range(max(0, start_column - 1), min(board_width - 1, start_column + 1) + 1):
            if data_array[row][column] == 0 and not display_array[row][column]:
                flip_adjacent_zeros(row, column)
            display_array[row][column] = True


def flip_zero(start_row, start_column):
    flip_adjacent_zeros(start_row, start_column)
    create_game_gui()


def flip_number(row, column):
    display_array[row][column] = True

    if data_array[row][column] == -1:
        game_over()

    create_game_gui()


def quick_uncover(event, start_row, start_column):
    for row in range(max(0, start_row - 1), min(board_height - 1, start_row + 1) + 1):
        for column in range(max(0, start_column - 1), min(board_width - 1, start_column + 1) + 1):
            if not flag_array[row][column]:
                if data_array[row][column] == -1:
                    game_over()
                elif data_array[row][column] == 0:
                    flip_adjacent_zeros(row, column)
                else:
                    display_array[row][column] = True
    create_game_gui()


def flag(event, row, column):
    if flag_array[row][column]:
        flag_array[row][column] = False
    else:
        flag_array[row][column] = True
    create_game_gui()


def generate_board():
    board = [[0 for x in range(board_width)] for y in range(board_height)]

    generated_mines = 0
    while generated_mines < mines:
        random_row = random.randint(0, board_height - 1)
        random_column = random.randint(0, board_height - 1)

        if board[random_row][random_column] != -1:
            generated_mines += 1
            board[random_row][random_column] = -1

            for row in range(max(0, random_row - 1), min(board_height - 1, random_row + 1) + 1):
                for column in range(max(0, random_column - 1), min(board_width - 1, random_column + 1) + 1):
                    if board[row][column] != -1:
                        board[row][column] += 1

    return board


if __name__ == "__main__":
    root = tk.Tk()
    root.title("Minesweeper")

    board_width = 10
    board_height = 10
    mines = 10

    tile_size = 3

    unopened_safe_tiles = board_width * board_height - mines

    data_array = generate_board()
    display_array = [[False for x in range(board_width)] for y in range(board_height)]
    flag_array = [[False for x in range(board_width)] for y in range(board_height)]

    create_game_gui()

    tk.mainloop()

Right now, the code that draws the game state onto the screen is incredibly slow. I can see each individual Button being deleted and placed again. If I change the board size to anything bigger, the game gets very laggy as the game goes on. How can I optimize my program?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Readability

  • place constants at the top of your script with uppercase letters, according to PEP8
  • don't exceed the maximum line length of 78 characters, according to PEP8
  • add extra white space to align and group variables (it doesn't cost you anything, since python is an interpreted language)

Shrinking code

  • use conditional assignment instead of if and else blocks

Efficiency

  • just store variables that are used, unlike unopened_safe_tiles
  • don't create multiple lambda expressions that do the same thing, instead create one and use it for both
  • don't look up the same variable multiple times, store it instead. Unlike data_array[row][column]
  • do not hard code every option, when they are all the same, use a dictionary instead
  • For every click event and button command you call create_game_gui() which creates a new layer of buttons on top of the old ones, instead configure existing ones!
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.