From eb7a4c471782d30110aaf709c2335422beba99ce Mon Sep 17 00:00:00 2001 From: Xevion Date: Sat, 2 Nov 2019 15:37:13 -0500 Subject: [PATCH] Minesweeper sketch game --- games/Minesweeper/Minesweeper.pyde | 187 ++++++++++++++++++ games/Minesweeper/Minesweeper_0.gif | Bin 0 -> 85 bytes games/Minesweeper/Minesweeper_1.gif | Bin 0 -> 94 bytes games/Minesweeper/Minesweeper_2.gif | Bin 0 -> 95 bytes games/Minesweeper/Minesweeper_3.gif | Bin 0 -> 96 bytes games/Minesweeper/Minesweeper_4.gif | Bin 0 -> 97 bytes games/Minesweeper/Minesweeper_5.gif | Bin 0 -> 96 bytes games/Minesweeper/Minesweeper_6.gif | Bin 0 -> 96 bytes games/Minesweeper/Minesweeper_7.gif | Bin 0 -> 94 bytes games/Minesweeper/Minesweeper_8.gif | Bin 0 -> 96 bytes games/Minesweeper/Minesweeper_bomb.gif | Bin 0 -> 90 bytes games/Minesweeper/Minesweeper_bomb.png | Bin 0 -> 185 bytes games/Minesweeper/Minesweeper_flag.gif | Bin 0 -> 140 bytes .../Minesweeper/Minesweeper_questionmark.gif | Bin 0 -> 135 bytes .../Minesweeper_unopened_square.gif | Bin 0 -> 124 bytes games/Minesweeper/sketch.properties | 2 + 16 files changed, 189 insertions(+) create mode 100644 games/Minesweeper/Minesweeper.pyde create mode 100644 games/Minesweeper/Minesweeper_0.gif create mode 100644 games/Minesweeper/Minesweeper_1.gif create mode 100644 games/Minesweeper/Minesweeper_2.gif create mode 100644 games/Minesweeper/Minesweeper_3.gif create mode 100644 games/Minesweeper/Minesweeper_4.gif create mode 100644 games/Minesweeper/Minesweeper_5.gif create mode 100644 games/Minesweeper/Minesweeper_6.gif create mode 100644 games/Minesweeper/Minesweeper_7.gif create mode 100644 games/Minesweeper/Minesweeper_8.gif create mode 100644 games/Minesweeper/Minesweeper_bomb.gif create mode 100644 games/Minesweeper/Minesweeper_bomb.png create mode 100644 games/Minesweeper/Minesweeper_flag.gif create mode 100644 games/Minesweeper/Minesweeper_questionmark.gif create mode 100644 games/Minesweeper/Minesweeper_unopened_square.gif create mode 100644 games/Minesweeper/sketch.properties diff --git a/games/Minesweeper/Minesweeper.pyde b/games/Minesweeper/Minesweeper.pyde new file mode 100644 index 0000000..1129e89 --- /dev/null +++ b/games/Minesweeper/Minesweeper.pyde @@ -0,0 +1,187 @@ +import random + +# Simple object to manage all the colors on the board +# Use as a global object to access quickly +class Images(): + def __init__(self): + names = ["0", "1", "2", "3", "4", "5", "6", "7", "8", + "flag", "questionmark", "unopened_square", "bomb"] + self.images = {} + for name in names: + build = "Minesweeper_{}.gif".format(name) + img = loadImage(build, 'gif') + self.images[name] = img + +# A class showing the nodes on a board +# Despite it's inclusio, the `parent` parameter has no use in the code, at all. +# Based off a A* algorithm I found online, but has been tweaked and "optimized" personally +class MovingNode(): + def __init__(self, parent=None, position=None): + self.parent, self.position = parent, position + + def __eq__(self, other): + return self.position == other.position + +# The Board Object +# Manages and stores all node values, as well as performs actions such as flagging and tapping +class Board(): + def __init__(self, sizeX, sizeY, default=None, composition=0.1, showall=True): + self.sizeX, self.sizeY, self.composition, self.showall = sizeX, sizeY, composition, showall + self.board = [] + # Generate all bombs on the board, as well as create nodes for it. + self.generate() + # The calculated pixel size of every square is calculated here + self.xDiv = width / float(self.sizeX) + self.yDiv = height / float(self.sizeY) + + # Show all the nodes on the board IF the `showall` parameter is on + if self.showall: + for nodeList in self.board: + for node in nodeList: + node.hidden = False + print("{} bombs placed".format(len(self.bombLocations))) + + # A raw mouse position's tap, it is translated into a board node 'tap' + def tapRaw(self, x, y, type): + self.tap(int(x // self.xDiv), int(y // self.yDiv), type) + + # A 'tap' on the board + def tap(self, x, y, type): + if type == LEFT: + self.board[x][y].flagged = False + if self.board[x][y].bomb: + self.board[x][y].hidden = False + print("You tapped at bomb at {}".format((x,y))) + if self.board[x][y].hidden: + if self.board[x][y].value == 0: + self.traverse(x, y) + else: + self.board[x][y].hidden = False + elif type == RIGHT: + if self.board[x][y].hidden: + self.board[x][y].flagged = not self.board[x][y].flagged + def outOfBounds(self, x, y): + return x < 0 or y < 0 or x > (self.sizeX - 1) or y > (self.sizeY - 1) + + # Reveal all near board nodes if they are hidden + def traverse(self, x, y): + start_node = MovingNode(position=(x, y)) + openList, closedList = [start_node], [] + + # While open nodes are available + while len(openList) > 0: + current_node = openList.pop(0) + closedList.append(current_node) + + children = [] + cardinals = [(1, 0), (0, 1), (-1, 0), (0, -1)] + # Whether you want nodes to traverse diagonally or not, + # if not, simply remove the diagonals from being added to the cardinal directions arary + diagonals = [(1, 1), (-1, -1), (1, -1), (-1, 1)] + cardinals += diagonals + for offset in cardinals: + new_position = offset[0] + current_node.position[0], offset[1] + current_node.position[1] + new_node = MovingNode(parent=current_node, position=new_position) + if self.outOfBounds(new_position[0], new_position[1]): + continue + if new_node not in openList: + if new_node not in closedList: + if self.board[new_position[0]][new_position[1]].value != 0: + closedList.append(new_node) + else: + openList.append(new_node) + print("Traversal from {}, {} nodes opened".format((x, y), len(closedList))) + for closed_node in closedList: + x,y = closed_node.position + self.board[x][y].hidden = False + + # Function to render the board on the screen + def render(self): + for x in range(self.sizeX): + for y in range(self.sizeY): + global images + imageMode(CENTER) + offsetX, offsetY = (x + 0.5) * self.xDiv, (y + 0.5) * self.yDiv + # Draw a Unhidden bomb + if self.board[x][y].bomb and not self.board[x][y].hidden: + image(images.images['bomb'], offsetX, offsetY, self.xDiv, self.yDiv) + # Draw a Unhidden Node + elif not self.board[x][y].hidden: + image(images.images[str(self.board[x][y].value)], offsetX, offsetY, self.xDiv, self.yDiv) + # Draw a Flagged Node + elif self.board[x][y].flagged: + image(images.images['flag'], offsetX, offsetY, self.xDiv, self.yDiv) + # Draw a Hidden Node + elif self.board[x][y].hidden: + image(images.images['unopened_square'], offsetX, offsetY, self.xDiv, self.yDiv) + + # Draw the Rectangle + stroke(0) + fill(0, 0, 0, 0) + # rect(cornerX1, cornerY1, cornerX2, cornerY2) + + # Generates the board's elements, including the bomb's locations + def generate(self): + if self.composition < 1.0: + bombNum = self.sizeX * self.sizeY + bombNum *= self.composition + bombNum = min(self.sizeX * self.sizeY - 1, bombNum) + bombNum = int(bombNum) + else: + bombNum = int(self.composition) + self.bombLocations = set([]) + while len(self.bombLocations) < bombNum: + self.bombLocations.add((random.randint(0, self.sizeX), random.randint(0, self.sizeY))) + + self.board = list(range(self.sizeX)) + for x in range(self.sizeX): + self.board[x] = list(range(self.sizeY)) + for y in range(self.sizeY): + if (x, y) in self.bombLocations: + newNode = Node((x, y), self.bombLocations, self.sizeX, self.sizeY, bomb=True, flagged=False) + else: + newNode = Node((x, y), self.bombLocations, self.sizeX, self.sizeY, bomb=False, flagged=False) + self.board[x][y] = newNode +class Node(): + def __init__(self, position, bombLocations, sizeX, sizeY, hidden=True, bomb=False, flagged=False): + self.bomb, self.flagged, self.position, self.hidden = bomb, flagged, position, hidden + self.bombLocations, self.sizeX, self.sizeY, self.value = bombLocations, sizeX, sizeY, 0 + self.calc() + + def flag(self): + self.flagged = True + + def outOfBounds(self, x, y): + return x < 0 or y < 0 or x > self.sizeX or y > self.sizeY + + # Calculates the number of bombs around the node (not including itself) + def calc(self): + directions = [(-1, 0), (0, -1), (1, 0), (0, 1), (1, 1), (-1, -1), (1, -1), (-1, 1)] + for offset in directions: + checkPosition = self.position[0] + offset[0], self.position[1] + offset[1] + if self.outOfBounds(checkPosition[0], checkPosition[1]): + continue + if checkPosition in self.bombLocations: + self.value += 1 + +# The setup function, just creates the board, as well as inits the colorset +def setup(): + size(1920/2, 1080/2) + global board, images + images = Images() + board = Board(width/16, height/16, composition=0.1, showall=False) + noLoop() + redraw() + redraw() + +# The simple rendering loop +def draw(): + background(0) + global board + board.render() + +# Manages the tapping action +def mouseClicked(): + global board + board.tapRaw(mouseX, mouseY, mouseButton) + redraw() diff --git a/games/Minesweeper/Minesweeper_0.gif b/games/Minesweeper/Minesweeper_0.gif new file mode 100644 index 0000000000000000000000000000000000000000..660e3e20f3a7db87e833d6cf2f5bf5a1297b98ec GIT binary patch literal 85 zcmZ?wbhEHb6krfwSi}GV|NsB@^YvZ3b}f(r761{7KUo+V7+4r|Kpc=-24*RXU3dO@ dAMl*H=EvPLSKr-a4|`-W^@+mTtler1)&Mz^9K`?t literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_1.gif b/games/Minesweeper/Minesweeper_1.gif new file mode 100644 index 0000000000000000000000000000000000000000..03e2c328d6c764e78124b77e7adfcf658f3aaf55 GIT binary patch literal 94 zcmZ?wbhEHb6krfwSi}GV|NsBDnR3a`*LUsOwLmslj6v}y3nK#qD}xS*3sTF#tn9Jt o&cEyfo-NDt*nG*(t*1^B5Sc0b+I@e*gdg literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_2.gif b/games/Minesweeper/Minesweeper_2.gif new file mode 100644 index 0000000000000000000000000000000000000000..28b77faa8c1125d64fd939c2eadda8d2403f5f37 GIT binary patch literal 95 zcmZ?wbhEHb6krfwSi}GV|NsBzxWwV->$`UCS|A%N#-R9$`UCS|A%N#-R9hQ*Jwwismx-Fx?I0t16J0K=Lhj{pDw literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_5.gif b/games/Minesweeper/Minesweeper_5.gif new file mode 100644 index 0000000000000000000000000000000000000000..8e93e9e5eb2302cedcfdae1e152b4052d91b808d GIT binary patch literal 96 zcmZ?wbhEHb6krfwSi}GV|Ns9_(UA4?^3 q^UwQ$=gbn7>y8g%?=DM;`rMl!T9meVo$$*!Nxu0(b}wp}7_0%N^C08^ literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_7.gif b/games/Minesweeper/Minesweeper_7.gif new file mode 100644 index 0000000000000000000000000000000000000000..e243b3e3a625c9cbb25c34d97c86e0d64fbf65be GIT binary patch literal 94 zcmZ?wbhEHb6krfwSi}GV|NsB@^YvZ3b}f(r761{7KUo+V7+4r|Kpc=-24-c8U3dO@ mAMl)+qVT-yb}7U5mV3@!%Tv3YS8e+6VPlZ){cRH%7_0#`M<8+l literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_8.gif b/games/Minesweeper/Minesweeper_8.gif new file mode 100644 index 0000000000000000000000000000000000000000..3de79876b2caa51b5d3c672f42f710e0b72c3a6f GIT binary patch literal 96 zcmZ?wbhEHb6krfwSi}GV|NsB@{QuL>*LUsOwLmslj6v}y3nK#qD}xS*3sTF#tm?7r r&cEyfo-=cl!j;0d&)A+Se`1Pii`n!QOI97r+j99@kl%|MCI)K&_3R>- literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_bomb.gif b/games/Minesweeper/Minesweeper_bomb.gif new file mode 100644 index 0000000000000000000000000000000000000000..8fb96dc2a6034beb2668f4e48f9bf690a3c2d3f2 GIT binary patch literal 90 zcmZ?wbhEHb6krfw_{abPe!jkI*RBOJ6o0aS1$00pNS=X7drJRGHtE}8OjDMXubLM6 kLL(vWi|_rL9V?cuSzs|O=!{%}*>=Bu_H`UzL>U;Y0Tk~X3IG5A literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_bomb.png b/games/Minesweeper/Minesweeper_bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..67c65df11fc34006cb6d273f76a39a7117b2c7b4 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4W2HJAr}5yCr1l4C~&wI_Ub51 zPvcJL6+L%h1>>!NKR*;@^wzt$u(Rk?GPtz7zJKKXQU{I$DWV5vx?i5Mf@@1cW99@u zN9i-?j8*wrg%oC{HSA`emy^vh=SawdwMTETZ)T56`}w<$%PB5X?)Sg#oogpu&|M>w jIhX1Brl&F4Rln!$o6}hNx-xAM&@Bv}u6{1-oD!M<`L#yp literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_flag.gif b/games/Minesweeper/Minesweeper_flag.gif new file mode 100644 index 0000000000000000000000000000000000000000..96f2155a39a43e1421f09adb525c7be012ab0e21 GIT binary patch literal 140 zcmZ?wbhEHb6krfw*vtR||NsC0!@$tQz;OKd@xzA?uU)%#-n@C!r%x{{D~pehx3{;q zv9STF0jp(D{K>+|z`)C(1LA_rU|_MFFvT<5mhI))ou^_~)Xq+r*UYZ;oO4C0_8#e# mCyi@Pf7$WKw#8ROrmbuBV!tbfMz!bDk{s7Yt?l7pum%7n{WWR; literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_questionmark.gif b/games/Minesweeper/Minesweeper_questionmark.gif new file mode 100644 index 0000000000000000000000000000000000000000..b16f94353592c8bf4c928a4bf205b22531f76bd6 GIT binary patch literal 135 zcmZ?wbhEHb6krfw*vtR||Ns9N3T`}p{P^L+hu5xMJ8#~+>C>l|m6gTE$J^W6+t}Cu z6@V3j2*sZ)j0_As3_2hV$P5M+lY$AJ*``lloUJ?+yP|fHN1l^bOZxOO-CtD;_8i)3 e_E>ecy!EWt$6j;x&Yoe|7+^6!;Dr(sgEaszC^K0A literal 0 HcmV?d00001 diff --git a/games/Minesweeper/Minesweeper_unopened_square.gif b/games/Minesweeper/Minesweeper_unopened_square.gif new file mode 100644 index 0000000000000000000000000000000000000000..8734399b899722bc8a15d8702119046908eb12fd GIT binary patch literal 124 zcmZ?wbhEHb6krfw*vtR||Ns9#e*F01!-v