didier/cogs/minesweeper.py

158 lines
5.7 KiB
Python
Raw Normal View History

from decorators import help
from discord.ext import commands
from enums.help_categories import Category
from functions import checks
import itertools
import random
class Minesweeper(commands.Cog):
def __init__(self, client):
self.client = client
# Don't allow any commands to work when locked
def cog_check(self, ctx):
return not self.client.locked
@commands.command(name="Minesweeper", aliases=["Ms"], usage="[Niveau]*")
@commands.check(checks.allowedChannels)
@help.Category(category=Category.Games)
async def minesweeper(self, ctx, difficulty="m"):
if difficulty[0].lower() not in "emh":
await ctx.send("Geef een geldige moeilijkheidsgraad op.")
return
await ctx.send(self.createGame(difficulty[0].lower()))
def createGame(self, difficutly):
# [X, Y, BombCount]
dimensions = {
"e": [5, 5, 4],
"m": [10, 10, 20],
"h": [13, 8, 35]
}
numbers = {
0: "||:zero:||",
1: "||:one:||",
2: "||:two:||",
3: "||:three:||",
4: "||:four:||",
5: "||:five:||",
6: "||:six:||",
7: "||:seven:||",
8: "||:eight:||",
}
# Initialize an empty grid
grid = [[" " for _ in range(dimensions[difficutly][0])] for _ in range(dimensions[difficutly][1])]
# Generate every possible position on the grid
positions = set(itertools.product([x for x in range(len(grid))], repeat=2))
# Place the bombs in the grid randomly
for i in range(dimensions[difficutly][2]):
bombPosition = random.choice(list(positions))
positions.remove(bombPosition)
grid[bombPosition[0]][bombPosition[1]] = "||:boom:||"
# Add in the numbers representing the amount of bombs nearby
for i, row in enumerate(grid):
for j, cell in enumerate(row):
if cell == " ":
grid[i][j] = numbers[self.countBombs(grid, [i, j])]
# Reveal the biggest empty space to the player
self.revealSpaces(grid, self.findBiggestEmptySpace(grid))
# Join the grid into a string
li = [" ".join(row) for row in grid]
print("\n".join(li))
return "\n".join(li)
# Counts the amount of bombs near a given cell
def countBombs(self, grid, cell):
positions = [
[1, -1], [1, 0], [1, 1],
[0, -1], [0, 1],
[-1, -1], [-1, 0], [-1, 1]
]
count = 0
for position in positions:
if 0 <= cell[0] + position[0] < len(grid) and 0 <= cell[1] + position[1] < len(grid[0]):
if "boom" in grid[cell[0] + position[0]][cell[1] + position[1]]:
count += 1
return count
# Finds the biggest spot of 0's on the grid to reveal at the start
def findBiggestEmptySpace(self, grid):
spaces = []
biggest = []
# Floodfill
for i, row in enumerate(grid):
for j, cell in enumerate(row):
# Only check cells that aren't part of a space yet
if not any(cell in space for space in spaces) and "zero" in cell:
li = [[i, j]]
changed = True
while changed:
changed = False
for added in li:
neighb = self.neighbours(grid, added)
# Add all neighbours that have not yet been added to this space
for neighbour in neighb:
if neighbour not in li:
li.append(neighbour)
changed = True
spaces.append(li)
# If it's bigger than the current biggest, make it the new biggest
if len(li) > len(biggest):
biggest = li
return biggest
# Returns all neighbouring cells containing a 0
def neighbours(self, grid, cell):
positions = [
[1, 0],
[0, -1], [0, 1],
[-1, 0]
]
neighb = []
for position in positions:
if 0 <= cell[0] + position[0] < len(grid) and 0 <= cell[1] + position[1] < len(grid[0]):
if "zero" in grid[cell[0] + position[0]][cell[1] + position[1]]:
neighb.append([cell[0] + position[0], cell[1] + position[1]])
return neighb
# Take away the spoiler marks from the biggest empty space to help the player start
def revealSpaces(self, grid, emptySpaces):
positions = [
[1, -1], [1, 0], [1, 1],
[0, -1], [0, 1],
[-1, -1], [-1, 0], [-1, 1]
]
for space in emptySpaces:
grid[space[0]][space[1]] = ":zero:"
# Reveal all spaces around this one
for position in positions:
# Check if the space is not zero & is contained inside the grid & the space hasn't been cut before
if 0 <= space[0] + position[0] < len(grid) and \
0 <= space[1] + position[1] < len(grid[0]) and \
"zero" not in grid[space[0] + position[0]][space[1] + position[1]] and \
"||" in grid[space[0] + position[0]][space[1] + position[1]]:
# Cut the spoiler from this cell
grid[space[0] + position[0]][space[1] + position[1]] = grid[space[0] + position[0]][
space[1] + position[1]][2:-2]
def setup(client):
client.add_cog(Minesweeper(client))