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 0000000..660e3e2 Binary files /dev/null and b/games/Minesweeper/Minesweeper_0.gif differ diff --git a/games/Minesweeper/Minesweeper_1.gif b/games/Minesweeper/Minesweeper_1.gif new file mode 100644 index 0000000..03e2c32 Binary files /dev/null and b/games/Minesweeper/Minesweeper_1.gif differ diff --git a/games/Minesweeper/Minesweeper_2.gif b/games/Minesweeper/Minesweeper_2.gif new file mode 100644 index 0000000..28b77fa Binary files /dev/null and b/games/Minesweeper/Minesweeper_2.gif differ diff --git a/games/Minesweeper/Minesweeper_3.gif b/games/Minesweeper/Minesweeper_3.gif new file mode 100644 index 0000000..a5ac0ab Binary files /dev/null and b/games/Minesweeper/Minesweeper_3.gif differ diff --git a/games/Minesweeper/Minesweeper_4.gif b/games/Minesweeper/Minesweeper_4.gif new file mode 100644 index 0000000..3d37f94 Binary files /dev/null and b/games/Minesweeper/Minesweeper_4.gif differ diff --git a/games/Minesweeper/Minesweeper_5.gif b/games/Minesweeper/Minesweeper_5.gif new file mode 100644 index 0000000..8e93e9e Binary files /dev/null and b/games/Minesweeper/Minesweeper_5.gif differ diff --git a/games/Minesweeper/Minesweeper_6.gif b/games/Minesweeper/Minesweeper_6.gif new file mode 100644 index 0000000..1285915 Binary files /dev/null and b/games/Minesweeper/Minesweeper_6.gif differ diff --git a/games/Minesweeper/Minesweeper_7.gif b/games/Minesweeper/Minesweeper_7.gif new file mode 100644 index 0000000..e243b3e Binary files /dev/null and b/games/Minesweeper/Minesweeper_7.gif differ diff --git a/games/Minesweeper/Minesweeper_8.gif b/games/Minesweeper/Minesweeper_8.gif new file mode 100644 index 0000000..3de7987 Binary files /dev/null and b/games/Minesweeper/Minesweeper_8.gif differ diff --git a/games/Minesweeper/Minesweeper_bomb.gif b/games/Minesweeper/Minesweeper_bomb.gif new file mode 100644 index 0000000..8fb96dc Binary files /dev/null and b/games/Minesweeper/Minesweeper_bomb.gif differ diff --git a/games/Minesweeper/Minesweeper_bomb.png b/games/Minesweeper/Minesweeper_bomb.png new file mode 100644 index 0000000..67c65df Binary files /dev/null and b/games/Minesweeper/Minesweeper_bomb.png differ diff --git a/games/Minesweeper/Minesweeper_flag.gif b/games/Minesweeper/Minesweeper_flag.gif new file mode 100644 index 0000000..96f2155 Binary files /dev/null and b/games/Minesweeper/Minesweeper_flag.gif differ diff --git a/games/Minesweeper/Minesweeper_questionmark.gif b/games/Minesweeper/Minesweeper_questionmark.gif new file mode 100644 index 0000000..b16f943 Binary files /dev/null and b/games/Minesweeper/Minesweeper_questionmark.gif differ diff --git a/games/Minesweeper/Minesweeper_unopened_square.gif b/games/Minesweeper/Minesweeper_unopened_square.gif new file mode 100644 index 0000000..8734399 Binary files /dev/null and b/games/Minesweeper/Minesweeper_unopened_square.gif differ diff --git a/games/Minesweeper/sketch.properties b/games/Minesweeper/sketch.properties new file mode 100644 index 0000000..2456b0a --- /dev/null +++ b/games/Minesweeper/sketch.properties @@ -0,0 +1,2 @@ +mode=Python +mode.id=jycessing.mode.PythonMode