Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Minesweeper/Minesweeper.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
530 lines (393 sloc)
13.8 KB
This file contains 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
"""Minesweeper""" | |
### --- LIBRARIES/MODULES USED + INITIALIZATION --- ### | |
### Tkinter | |
from tkinter import * | |
root=Tk() | |
root.title('Minesweeper') | |
### Random | |
import random | |
### --- CORE FUNCTION EXPLANATIONS --- ### | |
### lock() - puts game in locked state for game over/win conditions | |
### reveal(button number, red mine number carried from click) - reveal of multiple buttons at once, text number colours | |
### click(button number) - initial response to click | |
### check_win() - check if winning condition is met | |
### flag(event, button number) - associated with flag placement and removal using right click ('<Button-3>') | |
### button_creation() - creates w*h grid with m mines, binds relevant commands onto left and right clicks | |
### adjacent(button number, algorithm type) - counts adjacent mines/zero tiles | |
### setter(button number, adjacency counter, algorithm type) - sets adjacency counter to mines or blocks, used within adj algorithm | |
### adjacency_algorithm(button number, algorithm type) - sets adjacent mines of tile or assigns block | |
### restart() - resets board to initial appearance | |
### newgame() - generates new board | |
### [REMOVED] settings() - opens settings window | |
### --- SECONDARY FUNCTIONS --- ### | |
### num_mines(mines) - creates specified number of mines (returns random numbers within grid size) | |
### make_lambda(i) - lambda for right click binding | |
### middle(width, height) - list of grid positions not on the edge | |
### zeros_func() - list of grid positions with zero mines in adjacency | |
### blocks_func() - list of grid positions a block assigned | |
### colours(button number) - configs button text to corresponding colour (cosmetic function) | |
### window_geometry(width, height) - makes window fit for given grid size | |
### check_size(integer) - ensures w,h,m are not below 10, if they are, 10 is returned | |
### line_loop(width, height) - grid number sequences used for chunk setting | |
### [REMOVED] make_int() - sets w,h,m to int (used for conversion after entry widget in settings) | |
### --- BUTTON DICTIONARY INDEX REFERENCE --- ### | |
button={} | |
### button[i][0]= Button Configuration | |
### button[i][1]= Grid Position | |
### button[i][2]= Mine presence or mine adjacency | |
### button[i][3]= Flag placement ("F"/"") | |
### button[i][4]= Block | |
### --- GRID VARIABLES --- ### | |
### Change w and h for different grid size | |
### Change m for number of mines | |
### Recommended: w/h<50 + m<20 for best performance | |
w=20 | |
h=20 | |
m=50 | |
### FONT | |
f = (" Courier ", 24, "bold") | |
### --- FUNCTIONS (CORE + SECONDARY) --- ### | |
def check_size(i): | |
if i<10: | |
return 10 | |
return i | |
def window_geometry(w,h): | |
width=((w-10)*24)+240 | |
height=((h-10)*26)+339 | |
geo=str(width)+"x"+str(height) | |
return geo | |
def lock(): | |
for i in button: | |
button[i][0].configure(command=lambda i=i:None) | |
button[i][0].bind('<Button-3>', lambda i=i: None) | |
def reveal(i, num): | |
global blocks | |
if button[i][2]=="M" and i!=num: | |
button[i][0].configure(text="M", bg="orange") | |
label.config(text="Game Over") | |
elif button[i][2]==0: | |
temp=button[i][4] | |
for i in button: | |
if button[i][3]=="F": | |
pass | |
elif button[i][4]==temp: | |
colours(i) | |
else: | |
colours(i) | |
def colours(i): | |
if button[i][2]==0: button[i][0].configure(bg="ivory4") | |
elif button[i][2]==1: button[i][0].configure(fg="cornflower blue",text=button[i][2],bg="ivory3") | |
elif button[i][2]==2: button[i][0].configure(fg="forest green",text=button[i][2],bg="ivory3") | |
elif button[i][2]==3: button[i][0].configure(fg="red",text=button[i][2],bg="ivory3") | |
elif button[i][2]==4: button[i][0].configure(fg="medium blue",text=button[i][2],bg="ivory3") | |
elif button[i][2]==5: button[i][0].configure(fg="red4",text=button[i][2],bg="ivory3") | |
def click(num): | |
button[num][0].configure(bg='ivory3') | |
check_win() | |
if button[num][2]== "M": | |
button[num][0].configure(text="M", bg="red") | |
label.config(text="M") | |
print("Game Over\n") | |
for i in button: | |
if button[i][2]=="M": | |
reveal(i, num) | |
lock() | |
else: | |
reveal(num, None) | |
def check_win(): | |
mine_positions=[] | |
blue_count=0 | |
mine_count=0 | |
for i in button: | |
if button[i][0]["bg"]=="light sky blue": | |
blue_count+=1 | |
if button[i][2]=="M": | |
mine_positions.append(i) | |
mine_count+=1 | |
if mine_count==blue_count: | |
label.config(text="Win") | |
print("Win\n") | |
for i in mine_positions: | |
button[i][0].configure(bg="lawn green") | |
lock() | |
def num_mines(m): | |
mines=[] | |
counter=0 | |
while counter<m: | |
randombit=random.randint(0,w*h) | |
if randombit not in mines: | |
mines.append(randombit) | |
counter+=1 | |
return mines | |
def flag(event,i): | |
global flags | |
if button[i][0]["bg"]=="ivory3" or button[i][0]["bg"]=="ivory4": | |
return None | |
else: | |
if button[i][3]=="F": | |
button[i][0].configure(text="",command= lambda i=i: click(i)) | |
button[i][3]="" | |
flags+=1 | |
elif button[i][3]=="" and flags!=0: | |
button[i][0].configure(text="F",command= lambda i=i: None) | |
button[i][3]="F" | |
flags-=1 | |
label.config(text=flags) | |
def make_lambda(i): | |
return lambda event: flag(event, i) | |
def button_creation(): | |
c=0 | |
for j in range(h): | |
j=Frame(root) | |
j.pack() | |
for i in range(0+c,w+c): | |
button[i]=[Button(j, text="", bg='light sky blue', width=2, command=lambda i=i: click(i)), i, "", "", ""] | |
button[i][0].bind('<Button-3>', make_lambda(i)) | |
if i in mines: | |
button[i][2]="M" | |
button[i][4]="B" | |
if i in range((w*h)-w,w*h): | |
button[i][0].pack(pady=0, side=LEFT) | |
else: | |
button[i][0].pack(side=LEFT) | |
c=c+w | |
def middle(w,h): | |
middle=[] | |
q=0 | |
for k in range(h-2): | |
for i in range((w+1)+q,(w*2)-1+q): | |
middle.append(i) | |
q=q+w | |
return middle | |
def adjacent(i, TYPE): | |
if button[i][2]==0 and TYPE=="SET_BLOCK": | |
return 1 | |
elif button[i][2]=="M" and TYPE=="SET_MINE": | |
return 1 | |
elif TYPE=="SET_BLOCK" or TYPE=="SET_MINE": | |
return 0 | |
elif TYPE=="SET_CHUNKS": | |
global surround | |
surround.append(button[i][4]) | |
return 1 | |
else: | |
return 0 | |
def zeros_func(): | |
zeros=[] | |
for i in button: | |
if button[i][2]==0: | |
zeros.append(i) | |
return zeros | |
def blocks_func(): | |
blocks=[] | |
for i in button: | |
if button[i][4]=="B": | |
blocks.append(i) | |
return blocks | |
def adjacent_algorithm(i, TYPE): | |
c=0 | |
if button[i][1]==0 and button[i][2]!="M": #TOP LEFT CORNER (0) | |
c+=adjacent(i+1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
c+=adjacent(i+w+1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1]==w-1 and button[i][2]!="M": #TOP RIGHT CORNER | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i+w-1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1]==(w*h)-w and button[i][2]!="M": #BOTTOM LEFT | |
c+=adjacent(i+1, TYPE) | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-w-1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1]==(w*h)-1 and button[i][2]!="M": #BOTTOM RIGHT | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-w-1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1] in range(1,w-1) and button[i][2]!="M": #TOP ROW | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i+1, TYPE) | |
c+=adjacent(i+w-1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
c+=adjacent(i+w+1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1] in range((w*h)-(w-1),(w*h)-1) and button[i][2]!="M": #BOTTOM ROW | |
c+=adjacent(i-w-1, TYPE) | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-w+1, TYPE) | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i+1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1] in range(w,(w*h)-(w-1),w) and button[i][2]!="M": #LEFT COLUMN | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-w+1, TYPE) | |
c+=adjacent(i+1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
c+=adjacent(i+w+1, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1] in range((w*2)-1,(w*h)-w,w) and button[i][2]!="M": #RIGHT COLUMN | |
c+=adjacent(i-w-1, TYPE) | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i+w-1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
setter(i, c, TYPE) | |
elif button[i][1] in middle and button[i][2]!="M": | |
c+=adjacent(i-w-1, TYPE) | |
c+=adjacent(i-w, TYPE) | |
c+=adjacent(i-w+1, TYPE) | |
c+=adjacent(i-1, TYPE) | |
c+=adjacent(i+1, TYPE) | |
c+=adjacent(i+w-1, TYPE) | |
c+=adjacent(i+w, TYPE) | |
c+=adjacent(i+w+1, TYPE) | |
setter(i, c, TYPE) | |
if TYPE=="SET_BLOCK" and button[i][2]==0: | |
button[i][4]="" | |
def setter(i, c, TYPE): | |
if TYPE is "SET_MINE": | |
button[i][2]=c | |
elif TYPE is "SET_BLOCK": | |
if c==0: | |
button[i][4]="B" | |
else: | |
button[i][4]="" | |
elif TYPE is "SET_CHUNKS": | |
global surround,n | |
if button[i][4]=="B": | |
surround=[] | |
return None | |
for k in surround: | |
if "C" in k: | |
button[i][4]=k | |
break | |
else: pass | |
if "C" not in button[i][4]: | |
chunk="C"+str(n) | |
n+=1 | |
button[i][4]=chunk | |
surround=[] | |
def restart(): | |
print("Restarting Game...") | |
for i in button: | |
button[i][0].configure(text="", bg="light sky blue", fg="black", command=lambda i=i: click(i)) | |
button[i][0].bind('<Button-3>', make_lambda(i)) | |
button[i][3]="" | |
global flags | |
flags=len(mines) | |
label.config(text=flags) | |
print("Restart Complete\n") | |
def newgame(): | |
print("Generating New Game...") | |
global zeros,blocks, mines, flags | |
flags=len(mines) | |
label.config(text=flags) | |
mines=num_mines(m) | |
for i in button: | |
button[i][0].configure(text="",bg="light sky blue", fg="black", command=lambda i=i: click(i)) | |
button[i][0].bind('<Button-3>', make_lambda(i)) | |
button[i][2]="" | |
button[i][3]="" | |
button[i][4]="" | |
if i in mines: | |
button[i][2]="M" | |
button[i][4]="B" | |
button[i][0].pack(side=LEFT) | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_MINE") | |
zeros=zeros_func() | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_BLOCK") | |
blocks=blocks_func() | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_CHUNKS") | |
print("Generation Complete\n") | |
##def settings(w,h,m): | |
## r=Toplevel(root) | |
## | |
## def save_changes(e1,e2,e3): | |
## global w,h,m | |
## w=e1.get() | |
## h=e2.get() | |
## m=e3.get() | |
## make_int() | |
## r.destroy() | |
## | |
## f=(" Courier ", 10) | |
## about="Minesweeper Settings" | |
## Label(r, font=f, text=about).grid(row=0) | |
## label1=Label(r, text="Width: ").grid(row=1) | |
## label2=Label(r, text="Height: ").grid(row=2) | |
## label3=Label(r, text="Mines: ").grid(row=3) | |
## e1=Entry(r, width=2) | |
## e1.insert(2, str(w)) | |
## e1.grid(row=1,column=1) | |
## e2=Entry(r, width=2) | |
## e2.insert(2, str(h)) | |
## e2.grid(row=2, column=1) | |
## e3=Entry(r, width=2) | |
## e3.insert(2, str(m)) | |
## e3.grid(row=3, column=1) | |
## | |
## quit_button=Button(r, text='Quit', command=r.destroy).grid(row=4, column=0, sticky=W, pady=4) | |
## apply_button=Button(r, text='Apply', command=lambda:save_changes(e1,e2,e3)).grid(row=4, column=1, sticky=W, pady=4) | |
def line_loop(w,h): | |
seq=[] | |
c=0 | |
for k in range(round(h/2)): | |
for j in range(0+c,w+c): | |
seq.append(j) | |
if j==(w*h)-1: | |
break | |
for i in range((w*2)-1+c,(w-1)+c,-1): | |
seq.append(i) | |
c+=(w*2) | |
c=0 | |
for k in range(round(h/2)): | |
for j in range(w-1+c,-1+c,-1): | |
seq.append(j) | |
if j==(w*h)-1: | |
break | |
for i in range(w+c,(w*2)+c): | |
seq.append(i) | |
c+=(w*2) | |
return seq | |
##def make_int(): | |
## global w,h,m | |
## w=int(w) | |
## h=int(h) | |
## m=int(m) | |
### --- MAIN --- ### | |
n=1 | |
surround=[] | |
w=int(check_size(w)) | |
h=int(check_size(h)) | |
m=int(check_size(m)) | |
geo=window_geometry(w,h) | |
root.geometry(geo) | |
mines=num_mines(m) | |
flags=len(mines) | |
frame0=Frame(root) | |
frame0.pack() | |
newgame=Button(frame0, text="New Game", bg="ivory3", command= newgame).pack(padx=5, pady=5, side=LEFT ) | |
restart=Button(frame0, text="Restart", bg="ivory3", command= restart ).pack(padx=5, pady=5, side=LEFT) | |
#settingsb=Button(frame0, text="Settings", bg="ivory3", command= lambda: settings(w,h,m)).pack(padx=5, pady=5, side=RIGHT) | |
label=Label(root, text=flags, font=f, fg="blue") | |
label.pack() | |
button_creation() | |
middle=middle(w,h) | |
zeros=zeros_func() | |
line_loop=line_loop(w,h) | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_MINE") | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_BLOCK") | |
blocks=blocks_func() | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_CHUNKS") | |
for i in line_loop: | |
adjacent_algorithm(i, "SET_CHUNKS") | |
root.mainloop() | |
### --- END --- ### | |