Python Forum

Full Version: Adding the timer, smiley face, and flags in Minesweeper.
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi everyone, A friend of mine and I have been working on a Minesweeper game created in Python in Tkinter. We have almost completed programming the game and all we just need is to create the timer, smiley face and the flags and this is where we have been stuck on.
import random
import tkinter
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
 
root = Tk()
root.title("Minesweeper")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
 
mainframe = ttk.Frame(root, padding="12 12 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
 
 
def right_click():
  print("D")
 
 
def label_in(label):
    global label_list
    global keep_going
    row = label.widget.grid_info()["row"]
    column = label.widget.grid_info()["column"]
    number = row * columns + column
    counter = 0 
    if str(num_array[number]) == "X" and keep_going:
        for i in range(len(label_list)):
          if num_array[i] == "X":
            label_list[i].configure(text = "  X", relief = "sunken")
            ttk.Label(mainframe, text= "You lost! Try again").grid(column= 0, row=21, columnspan=20)
            keep_going = False
    elif str(num_array[number]) != "X" and keep_going:
        label.widget.configure(text = "  "  + str(num_array[number]), relief = "sunken")
        label_list[number] = "FIN"
    for i in range(len(label_list)):
        if label_list[i] != "FIN":
            counter +=1
    if counter == bomb_amount:
      for i in range(len(label_list)):
          if num_array[i] == "X":
            label_list[i].configure(text = "  X", relief = "sunken")
            ttk.Label(mainframe, text= "You Won! Play again").grid(column= 0, row=5, columnspan= 20)
            keep_going = False
 
 
def board_create(num_array):
  global label_list
  label_list = []
  count = 0
  column = 0
  row = 0
  for i in range(len(num_array)):
    if column == (columns):
      column = 0
      row += 1
    # label_creation = ttk.Label(mainframe, text = " ", relief = "raised", width= 3)
    label_list.append(ttk.Label(mainframe, text = " ", relief = "raised", width= 3))
    label_list[i].grid(column = column, row = row)
    column += 1
    count+=1
    label_list[i].bind("<Button-1>", label_in)
    label_list[i].bind("<Button-2>", right_click)
 
 
def get_array():
  rowxcolumn = rows * columns
  number_array = [0]*rowxcolumn
  for i in range(bomb_amount):
    bomb = random.randint(1,len(number_array) -1)
    if number_array[bomb] != "X":
      number_array[bomb] = "X"
  double_check = 1
  count = 0
  [print(number_array[rowxcolumn-columns-1])]
  print(len(number_array))
  for i in range(len(number_array)):
      if count == 0 and number_array[count] != "X"  and number_array[count] < double_check:
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count+columns+1] == "X":
              number_array[count] += 1
      if count == (columns) and number_array[count] != "X" and number_array[count] < double_check :
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count+columns-1] == "X":
              number_array[count] += 1
      if count == (rowxcolumn-1) and number_array[count] != "X"  and number_array[count] < double_check :
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns-1] == "X":
              number_array[count] += 1
      if count == (rowxcolumn-columns)and number_array[count] != "X"  and number_array[count] < double_check:
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns+1] == "X":
              number_array[count] += 1
      if count < (columns) and number_array[count] != "X" and  not(count == columns-1) and not(count == rowxcolumn-1)  and number_array[count] < double_check and (not count == 0):
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count+columns+1] == "X":
              number_array[count] += 1
          if number_array[count+columns-1] == "X":
              number_array[count] += 1
      if count > (rowxcolumn-columns-1) and number_array[count] != "X" and not(count == rowxcolumn-1) and not(count == rowxcolumn-columns) and number_array[count] < double_check :
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns-1] == "X":
              number_array[count] += 1
          if number_array[count-columns+1] == "X":
              number_array[count] += 1
      if count % (columns) == (columns -1) and not(count == 0) and not(count == columns-1) and not(count == rowxcolumn-1) and not(count == rowxcolumn-columns)  and number_array[count] != "X" and number_array[count] < double_check :
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns-1] == "X":
              number_array[count] += 1
          if number_array[count+columns-1] == "X":
              number_array[count] += 1
      if count % (columns) == 0  and not(count == 0) and not(count == columns-1) and not(count == rowxcolumn-1) and not(count == rowxcolumn-columns-1) and number_array[count] != "X" and count <  (rowxcolumn-columns-1) and number_array[count]< double_check:
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns+1] == "X":
              number_array[count] += 1
          if number_array[count+columns+1] == "X":
              number_array[count] += 1
      if not(count % (columns) == 0)  and not(count%columns == (columns-1)) and count > columns and count < (rowxcolumn-columns-1) and number_array[count] != "X" and number_array[count] < double_check:
          if number_array[count+1] == "X":
              number_array[count] += 1
          if number_array[count-1] == "X":
              number_array[count] += 1
          if number_array[count+columns] == "X":
              number_array[count] += 1
          if number_array[count-columns] == "X":
              number_array[count] += 1
          if number_array[count-columns+1] == "X":
              number_array[count] += 1
          if number_array[count-columns-1] == "X":
              number_array[count] += 1
          if number_array[count+columns+1] == "X":
              number_array[count] += 1
          if number_array[count+columns-1] == "X":
             number_array[count] += 1
      count += 1
      double_check += 1
 
 
  for i in range(len(number_array)):
    print(str(number_array[i]), end = " ")
    if i % columns == columns - 1:
      print("")
  count = 0
  return number_array
 
 
bomb_amount = 30
rows = 20
columns = 20
num_array = get_array()
label_list = []
board_create(num_array)
keep_going = True

root.mainloop()
Use insert python and not pastebin.
You would get a lot of benefit from a couple of simple classes. This is my attempt at Minesweeper using classes to represent the map and a single cell of the map. The cell knows how to do all the cell things like placing a flag marker (I used "F" instead of an image) or exposing the bomb our bomb count info. The board class creates all the cells and could easily be enhanced to display a remaining safe cell count and win/lose messages.
import random
import tkinter as tk

class Cell(tk.Button):
    """A button for the Minsweeper game.  Press me to reveal a bomb or information
    about surrounding bombs.
    """
    def __init__(self, parent, row, column, count):
        super().__init__(parent, text=' ', width=2, relief=tk.RAISED, command=self.press)
        self.parent = parent
        self.row = row
        self.column = column
        self.count = count
        self.pressed = False
        self.flagged = False
        self.bind("<Button-3>", self.flag)

    def press(self):
        """Button pressed.  Show bomb info"""
        if not self.pressed:
            self.configure(relief=tk.SUNKEN)
            self.pressed = True
            self['text'] = 'X' if self.count < 0 else str(self.count)
            self.parent.clear_cell(self)

    def flag(self, _):
        """Right mouse button pressed.  Toggle flag marker"""
        if not self.pressed:
            self.flagged = not self.flagged
            self['text'] = 'F' if self.flagged else ' '


class Board(tk.Frame):
    """Map for Minesweeper game.  I make a grid of buttons that are pressed to
    reveal bombs or information about surrounding bombs.
    """
    def __init__(self, parent, rows, columns, bomb_count):
        super().__init__(parent)
        self.safe_count = rows * columns - bomb_count

        # Make map of all the bombs
        bombs = [[0]*columns for row in range(rows)]
        for bomb in random.choices(range(rows*columns), k=bomb_count):
            bombs[bomb // columns][bomb % columns] = 1

        # Make the board map
        self.cells = []
        for row in range(rows):
            for column in range(columns):
                # Count bombs in surrounding cells.  Use -1 to indicate cell has bomb
                if bombs[row][column]:
                    count = -1
                else:
                    count = sum([bombs[r][c] for r in range(max(0, row-1), min(rows, row+2)) \
                        for c in range(max(0, column-1), min(columns, column+2))])
                cell = Cell(self, row, column, count)
                cell.grid(row=row, column=column)
                self.cells.append(cell)

    def clear_cell(self, cell):
        """Cell was selected"""
        if cell.count < 0:
            print('BOOM!!!')
        else:
            self.safe_count -= 1
            if self.safe_count <= 0:
                print('You won!!')

root = tk.Tk()
root.title("Minesweeper")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
board = Board(root, 20, 20, 30)
board.grid(column=0, row=0, sticky='NEWS')
root.mainloop()
Images in buttons is pretty easy. Just create an image for the flag, then when you flag a cell set the text to '' and set the image. I clipped this from a tk Yahtzee game.
import pathlib
import tkinter as tk
import random

IMAGE_DIR = pathlib.Path(__file__).parent

class DiceButton(tk.Button):
    """Roll a die"""
    def __init__(self, parent):
        # Create dice images
        self.images = [tk.PhotoImage(file=IMAGE_DIR/f'dice{i}.png') for i in range(7)]
        super().__init__(parent, image=self.images[0], command=self.roll)
        self.value = 0

    def roll(self):
        self.value = random.randint(1, 6)
        self['image'] = self.images[self.value]

ROOT = tk.Tk()
for _ in range(6):
    DiceButton(ROOT).pack(side=tk.LEFT)
ROOT.mainloop()
Thanks for the suggestions we are good at keeping the good long as we tend to learn more but I just want the right click function and the smiley face and numbers just like the real minesweeper.
For a timer you'll need a periodic event. For something like this the easiest way to do something periodically is use the "after()" method. Here I added a status line to the bottom of the window which is updated every second to show elapsed time.
import random
import time
import datetime
import tkinter as tk
from tkinter import messagebox

class Cell(tk.Button):
    """A button for the Minsweeper game.  Press me to reveal a bomb or information
    about surrounding bombs.
    """
    def __init__(self, parent, row, column, count):
        super().__init__(parent, text=' ', width=2, relief=tk.RAISED, command=self.press)
        self.parent = parent
        self.row = row
        self.column = column
        self.count = count
        self.pressed = False
        self.flagged = False
        self.bind("<Button-3>", self.flag)

    def press(self):
        """Button pressed.  Show bomb info"""
        if not self.pressed:
            self.configure(relief=tk.SUNKEN)
            self.pressed = False
            self['text'] = 'X' if self.count < 0 else str(self.count)
            self.parent.clear_cell(self)

    def flag(self, _):
        """Right mouse button pressed.  Toggle flag marker"""
        if not self.pressed:
            self.flagged = not self.flagged
            self['text'] = 'F' if self.flagged else ' '


class Board(tk.Frame):
    """Map for Minesweeper game.  I make a grid of buttons that are pressed to
    reveal bombs or information about surrounding bombs.
    """
    def __init__(self, parent, rows, columns, bomb_count):
        super().__init__(parent)
        self.safe_count = rows * columns - bomb_count

        # Make map of all the bombs
        bombs = [[0]*columns for row in range(rows)]
        for bomb in random.choices(range(rows*columns), k=bomb_count):
            bombs[bomb // columns][bomb % columns] = 1

        # Make the board map
        self.cells = []
        for row in range(rows):
            for column in range(columns):
                # Count bombs in surrounding cells.  Use -1 to indicate cell has bomb
                if bombs[row][column]:
                    count = -1
                else:
                    count = sum([bombs[r][c] for r in range(max(0, row-1), min(rows, row+2)) \
                        for c in range(max(0, column-1), min(columns, column+2))])
                cell = Cell(self, row, column, count)
                cell.grid(row=row, column=column)
                self.cells.append(cell)

        # Add status display to bottom of window
        self.done = False
        self.status = tk.Label(parent, text='')
        self.status.grid(row=rows, column=0, columnspan=columns)
        self.start_time = time.time()
        self.update_status()
        
    def clear_cell(self, cell):
        """Cell was selected"""
        if cell.count < 0:
            self.done = True
            messagebox.showinfo('Game Over', 'Today marks the passing of you')
        else:
            self.safe_count -= 1
            if self.safe_count <= 0:
                self.done = True
                messagebox.showinfo('Game Over', 'Winner, winner, chicken dinner!')

    def update_status(self):
        """Update status display"""
        delta = str(datetime.timedelta(seconds=int(time.time() - self.start_time)))
        self.status['text'] = f'{delta}   Remaining {self.safe_count}'
        if not self.done:
            self.after(1000, self.update_status)  # Run again after 1 second
        

root = tk.Tk()
root.title("Minesweeper")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
board = Board(root, 20, 20, 30)
board.grid(column=0, row=0, sticky='NEWS')
root.mainloop()