Last active
September 23, 2020 00:05
-
-
Save sean-m/c92df7fe89ad72d4429c5a48936dc117 to your computer and use it in GitHub Desktop.
sloppy minesweeper in with python3 tkinter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
import random as rand | |
import tkinter as tk | |
from tkinter import ttk | |
colors = [ "white", "blue", "green", "red", "purple", "brown", "grey", "black" ] | |
class Tile: | |
def __init__(self, bomb=False): | |
self.bomb = bomb | |
self.flag = False | |
self.state = "hidden" | |
#self.state = "cleared" | |
class Game: | |
def __init__(self, x, y, w, h, b): | |
''' | |
x: board columns | |
y: board rows | |
w: canvas width | |
h: cangas height | |
''' | |
#print(f"x:{x}, y:{y}, w:{w}, h:{h}") | |
self.x = x | |
self.y = y | |
self.w = w | |
self.h = h | |
self.r = 5 | |
self.b = (x * y / b) / 100 | |
self.size = 20 | |
self.board = [] | |
self.end = False; | |
self.bomb_count=0 | |
# Add all the tiles to the board | |
for i in range(0, x, 1): | |
self.board.append([]) | |
for j in range(0, y, 1): | |
self.board[i].append(Tile()) | |
while self.bomb_count < b: | |
rx = rand.randint(0, x-1) | |
ry = rand.randint(0, y-1) | |
#print(f"{rx} {ry}") | |
spot = self.board[rx][ry] | |
if not spot.bomb: | |
self.board[rx][ry].bomb = True | |
self.bomb_count = self.bomb_count + 1 | |
def get_stats(self): | |
return f"Bombs:{self.bomb_count}" | |
def draw_board(self, canvas): | |
canvas.delete("all") | |
for i in range(0, self.w, int(self.w/self.x)): | |
canvas.create_line([(i, 0), (i, self.h)], tag='grid_line') | |
# Creates all horizontal lines at intevals of 100 | |
for i in range(0, self.h, int(self.h/self.y)): | |
canvas.create_line([(0, i), (self.w, i)], tag='grid_line') | |
cw = int(self.w / self.x) | |
ch = int(self.h / self.y) | |
for i in range(0, self.x, 1): | |
for j in range(0, self.y, 1): | |
spot=self.board[i][j] | |
# coordinates for tile locations | |
x = i * (self.w/self.x) | |
y = j * (self.h/self.y) | |
# coordinates for bomb/number locations, center of tile | |
cx = x + (self.w/self.x/2) | |
cy = y + (self.h/self.y/2) | |
if spot.state == "exploded": | |
canvas.create_oval(cx-self.r, cy-self.r, cx+self.r, cy+self.r, fill="red", outline="#DDD", width=1) | |
elif spot.state == "hidden": | |
canvas.create_rectangle(x, y, x+cw, y+ch, fill="grey") | |
elif spot.state == "cleared": | |
if spot.bomb: | |
#print(f"i:{i} j:{j} x:{x} y:{y}") | |
canvas.create_oval(cx-self.r, cy-self.r, cx+self.r, cy+self.r, fill="black", outline="#DDD", width=1) | |
else: | |
count = self.find_bombs(i, j) | |
canvas.create_text(cx, cy, text=str(count), fill=colors[count]) | |
elif spot.state == "flagged": | |
r = self.r # vertical unit | |
xoff = cx-(2 * r) | |
canvas.create_line(xoff, cy - (r * 2.5), xoff, cy + (r * 2.5), fill="black", width=2) # flagpole | |
canvas.create_rectangle(xoff+1, cy - (r * 2.5), cx + (r * 1.5), cy, fill="red") | |
#print(f"draw flag at {x} {y}") | |
def find_bombs(self, x, y): | |
''' | |
Walk around the x, y value and count the bombs found | |
''' | |
count = 0 | |
for i in range(-1,2,1): | |
for j in range(-1,2,1): | |
x1 = x + i | |
y1 = y + j | |
#print(f"x:{x} y:{y} i:{i} j:{j} x1:{x1} y1:{y1} {count}") | |
if 0 <= x1 < self.x and 0 <= y1 < self.y: | |
try: | |
if self.test_bomb(x1, y1): | |
count += 1 | |
except Exception as e: | |
pass | |
#print("") | |
return count | |
def test_bomb(self, x, y): | |
return self.board[x][y].bomb | |
def flag_tile(self, x, y): | |
if self.end: | |
return | |
cw = int(self.w / self.x) | |
ch = int(self.h / self.y) | |
tx = int(x / cw) | |
ty = int(y / ch) | |
#print(f"{tx} {ty}") | |
if self.board[tx][ty].state != "flagged" \ | |
and self.board[tx][ty].state != "hidden": | |
return | |
if self.board[tx][ty].state == "flagged": | |
self.board[tx][ty].state = "hidden" | |
else: | |
self.board[tx][ty].state = "flagged" | |
def test_tile(self, x, y): | |
if self.end: | |
return | |
cw = int(self.w / self.x) | |
ch = int(self.h / self.y) | |
tx = int(x / cw) | |
ty = int(y / ch) | |
#print(f"{tx} {ty}") | |
if self.test_bomb(tx, ty): | |
self.board[tx][ty].state = "exploded" | |
self.end = True | |
else: | |
self.flood_clear(tx, ty) | |
def flood_clear(self, x, y): | |
queue = [] | |
queue.append([x, y, self.find_bombs(x, y)]) | |
while queue: | |
px, py, count = queue.pop() | |
if self.board[px][py].state == "cleared": | |
# already cleared, skip | |
continue | |
#print(f"px:{px} py:{py} count:{count}") | |
self.board[px][py].state = "cleared" | |
if count != 0: | |
# found a number, stop walking | |
continue; | |
for i in range(-1,2,1): | |
for j in range(-1,2,1): | |
nx, ny = px-i, py+j | |
if 0 <= nx < self.x and 0 <= ny < self.y and not self.test_bomb(nx, ny): | |
nc = self.find_bombs(nx, ny) | |
if nc == 0: | |
queue.append([nx, ny, nc]) | |
else: | |
self.board[nx][ny].state = "cleared" | |
class Application(tk.Frame): | |
def __init__(self, game, master=None): | |
super().__init__(master) | |
self.master = master | |
self.game = game | |
self.pack() | |
self.create_widgets() | |
def create_widgets(self): | |
h = self.game.h | |
w = self.game.w | |
self.canvas = tk.Canvas(self, width=w, height=h, bg="white") | |
self.canvas.bind("<Button-1>", self.left_clicked) | |
self.canvas.bind("<Button-2>", self.right_clicked) | |
self.canvas.bind("<Button-3>", self.right_clicked) | |
self.canvas.pack(side="top") | |
self.hi_there = ttk.Button(self) | |
self.hi_there["text"] = "New Game" | |
self.hi_there["command"] = self.new_game | |
self.hi_there.pack(side="top") | |
self.stats = tk.Label(self, text=self.game.get_stats()) | |
self.stats["textvariable"] = self.game.get_stats() | |
self.stats.pack(side="left") | |
self.game.draw_board(self.canvas) | |
def left_clicked(self, event): | |
#print(event) | |
self.game.test_tile(event.x, event.y) | |
self.game.draw_board(self.canvas) | |
def right_clicked(self, event): | |
#print(event) | |
self.game.flag_tile(event.x, event.y) | |
self.game.draw_board(self.canvas) | |
def new_game(self): | |
game = Game(10,10,480,480, 15) | |
self.canvas.delete("all") | |
self.game = game | |
self.game.draw_board(self.canvas) | |
self.stats = self.game.get_stats() | |
game = Game(20,20,480,480, 45) | |
root = tk.Tk() | |
def main(): | |
app = Application(master=root, game=game) | |
root.mainloop() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment