Search code examples
pythonpython-3.xtkinterminesweeper

Python Tkinter destroying all Buttons next to chosen one with same text


I'm tying to recreate Minesweeper in python and i'm trying to recreate how in the game if the player chooses a tile without anything on it (In my code, a tile that has a 0) it will reveal all the tiles close to it that aren't mines (In my code, a tile that has an X). I've tried to to do this many times but nothing has worked. It would be written in the autoShow(i, j) function.

Can anyone help (Any other suggestions would be appreciated)?

import tkinter as tk
from tkinter import messagebox
from random import randint

def difficulty(diff):
    global rows, cols, mines, size
    if diff == 1:
        rows = 9
        cols = 9
        mines = 10
        size = "341x368"
    elif diff == 2:
        rows = 16
        cols = 16
        mines = 40
        size = "606x654"
    else:
        rows = 16
        cols = 30
        mines = 99
        size = "1139x606"
    play()

def firstT(l, m):
    global firstTurn
    if firstTurn:
        mPos = []
        firstTurn = False
        for x in range(mines):
            minesLoop = True
            while minesLoop:
                x = randint(0, rows - 1)
                y = randint(0, cols - 1)
                if grid_text[x][y].get() != "X":
                    near = False
                    if near == False:
                        mPos.append([x, y])
                        minesLoop = False
                        grid_text[x][y].set("X")
        [[labels[i][j].configure(text = grid_text[i][j].get()) for j in range(cols)] for i in range(rows)]
        for i in range(rows):
            for j in range(cols):
                nearby = 0
                for ii in range(i - 1, i + 2):
                    for jj in range(j - 1, j + 2):
                        if ii >= 0 and jj >= 0 and ii < rows and jj < cols:
                            if labels[i][j]["text"] == "" and labels[ii][jj]["text"] == "X" and not (i, j) in mPos:
                                nearby += 1
                if labels[i][j]["text"] != "X":
                    grid_text[i][j] = str(nearby)
                    labels[i][j].configure(text = grid_text[i][j])

def autoShow(i, j):
    pass

def show(i, j):
    global gameOver
    if gameOver:
        return
    firstT(i, j)
    if labels[i][j]["text"] == "0":
        autoShow(i, j)
    elif labels[i][j]["text"] == "X":
        messagebox.showinfo("Lose!", "You Lose")
        game.focus_force()
        gameOver = True
    else:
        grid[i][j].destroy()

def play():
    global firstTurn, gameOver, game, grid, grid_text, labels
    firstTurn = True
    gameOver = False
    game = tk.Toplevel(root)
    game.title("Minesweeper")
    game.geometry(size)
    grid_text = [[tk.StringVar() for j in range(cols)] for i in range(rows)]
    labels = [[tk.Label(game, text = grid_text[i][j].get()) for j in range(cols)] for i in range(rows)]
    [[labels[i][j].grid(row = i, column = j) for j in range(cols)] for i in range(rows)]
    grid = [[tk.Button(game, width = 4, height = 2, command = lambda i = i, j = j: show(i, j)) for j in range(cols)] for i in range(rows)]
    [[grid[i][j].grid(row = i, column = j) for j in range(cols)] for i in range(rows)]
    xoutside = [[tk.Button(game, width = 4, height = 2).grid(row = i, column = cols + 1) for j in range(cols)] for i in range(rows)]
    youtside = [[tk.Button(game, width = 4, height = 2).grid(row = rows + 1, column = j) for j in range(cols)] for i in range(rows)]

diff(1)

Solution

  • You have to use recursion to run show(i-1, j), etc.

    Because I run show(i-1, j) without clicking button so I need also grid[i][j] = None to recognize if button still exists.

    I don't know if it doesn't need other tests in which place is shown.

    def autoShow(i, j):
    
        grid[i][j].destroy()
        grid[i][j] = None
    
        if i > 0:        
            show(i-1, j)
        if j > 0:        
            show(i, j-1)
    
        if i < rows-1:
            show(i+1, j)
        if j < cols-1:
            show(i, j+1)
    
        if i > 0 and j > 0:        
            show(i-1, j-1)
        if i > 0 and j < cols-1:        
            show(i-1, j+1)
    
        if i < rows-1 and j < cols-1:        
            show(i+1, j+1)
    
        if i < rows-1 and j > 0:        
            show(i+1, j-1)
    
    def show(i, j):
        global gameOver
    
        if gameOver:
            return
    
        firstT(i, j)
    
        # check if button still exists    
        if grid[i][j] is None:
            return
    
        if labels[i][j]["text"] == "0":
            autoShow(i, j)
    
        elif labels[i][j]["text"] == "X":
            grid[i][j].destroy()
            grid[i][j] = None
            messagebox.showinfo("Lose!", "You Lose")
            game.focus_force()
            gameOver = True
        else:
            grid[i][j].destroy()
            grid[i][j] = None
    

    Full code which I tested

    import tkinter as tk
    from tkinter import messagebox
    from random import randint
    
    def difficulty(diff):
        global rows, cols, mines, size
        if diff == 1:
            rows = 9
            cols = 9
            mines = 10
            size = "341x368"
        elif diff == 2:
            rows = 16
            cols = 16
            mines = 40
            size = "606x654"
        else:
            rows = 16
            cols = 30
            mines = 99
            size = "1139x606"
        play()
    
    def firstT(l, m):
        global firstTurn
        if firstTurn:
            mPos = []
            firstTurn = False
            for x in range(mines):
                minesLoop = True
                while minesLoop:
                    x = randint(0, rows - 1)
                    y = randint(0, cols - 1)
                    if grid_text[x][y].get() != "X":
                        near = False
                        if near == False:
                            mPos.append([x, y])
                            minesLoop = False
                            grid_text[x][y].set("X")
            [[labels[i][j].configure(text = grid_text[i][j].get()) for j in range(cols)] for i in range(rows)]
            for i in range(rows):
                for j in range(cols):
                    nearby = 0
                    for ii in range(i - 1, i + 2):
                        for jj in range(j - 1, j + 2):
                            if ii >= 0 and jj >= 0 and ii < rows and jj < cols:
                                if labels[i][j]["text"] == "" and labels[ii][jj]["text"] == "X" and not (i, j) in mPos:
                                    nearby += 1
                    if labels[i][j]["text"] != "X":
                        grid_text[i][j] = str(nearby)
                        labels[i][j].configure(text = grid_text[i][j])
    
    def autoShow(i, j):
    
        grid[i][j].destroy()
        grid[i][j] = None
    
        if i > 0:        
            show(i-1, j)
        if j > 0:        
            show(i, j-1)
    
        if i < rows-1:
            show(i+1, j)
        if j < cols-1:
            show(i, j+1)
    
        if i > 0 and j > 0:        
            show(i-1, j-1)
        if i > 0 and j < cols-1:        
            show(i-1, j+1)
    
        if i < rows-1 and j < cols-1:        
            show(i+1, j+1)
    
        if i < rows-1 and j > 0:        
            show(i+1, j-1)
    
    def show(i, j):
        global gameOver
        if gameOver:
            return
        firstT(i, j)
    
        if grid[i][j] is None:
            return
    
        if labels[i][j]["text"] == "0":
            autoShow(i, j)
    
        elif labels[i][j]["text"] == "X":
            grid[i][j].destroy()
            grid[i][j] = None
            messagebox.showinfo("Lose!", "You Lose")
            game.focus_force()
            gameOver = True
        else:
            grid[i][j].destroy()
            grid[i][j] = None
    
    def play():
        global firstTurn, gameOver, game, grid, grid_text, labels
        firstTurn = True
        gameOver = False
        game = tk.Toplevel(root)
        game.title("Minesweeper")
        game.geometry(size)
        grid_text = [[tk.StringVar() for j in range(cols)] for i in range(rows)]
        labels = [[tk.Label(game, text = grid_text[i][j].get()) for j in range(cols)] for i in range(rows)]
        [[labels[i][j].grid(row = i, column = j) for j in range(cols)] for i in range(rows)]
        grid = [[tk.Button(game, width = 4, height = 2, command = lambda i = i, j = j: show(i, j)) for j in range(cols)] for i in range(rows)]
        [[grid[i][j].grid(row = i, column = j) for j in range(cols)] for i in range(rows)]
        xoutside = [[tk.Button(game, width = 4, height = 2).grid(row = i, column = cols + 1) for j in range(cols)] for i in range(rows)]
        youtside = [[tk.Button(game, width = 4, height = 2).grid(row = rows + 1, column = j) for j in range(cols)] for i in range(rows)]
    
    root = tk.Tk()
    difficulty(1)
    root.mainloop()