diff options
author | 2019-08-10 15:06:45 +0200 | |
---|---|---|
committer | 2019-08-10 15:06:45 +0200 | |
commit | 1cb1ec544a918d585bc2160c5a822906d6c0c1d7 (patch) | |
tree | 56cdf8c92784488ccf9535acb85aac77d3595ee9 /bot/seasons/evergreen/minesweeper.py | |
parent | fixed a small bug (diff) |
changed thing after requests.
Diffstat (limited to 'bot/seasons/evergreen/minesweeper.py')
-rw-r--r-- | bot/seasons/evergreen/minesweeper.py | 127 |
1 files changed, 64 insertions, 63 deletions
diff --git a/bot/seasons/evergreen/minesweeper.py b/bot/seasons/evergreen/minesweeper.py index b1d27735..9cd35faa 100644 --- a/bot/seasons/evergreen/minesweeper.py +++ b/bot/seasons/evergreen/minesweeper.py @@ -1,42 +1,41 @@ import typing from random import random -import discord from discord.ext import commands +GameBoard = typing.List[typing.List[typing.Union[str, int]]] +DictOfGames = typing.Dict[int, typing.Dict] + class Minesweeper(commands.Cog): """Play a game of minesweeper.""" def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - self.games: typing.Dict[discord.member, typing.Dict] = {} # Store the currently running games + self.games: DictOfGames = {} # Store the currently running games @staticmethod def is_bomb(cell: typing.Union[str, int]) -> int: """Returns 1 if `cell` is a bomb if not 0""" - return 1 if cell == "bomb" else 0 + return cell == "bomb" - def generate_board(self, bomb_chance: float) -> typing.List[typing.List[typing.Union[str, int]]]: + def generate_board(self, bomb_chance: float) -> GameBoard: """Generate a 2d array for the board.""" - board: typing.List[typing.List[typing.Union[str, int]]] = [ - ["bomb" if random() <= bomb_chance else "number" for _ in range(10)] for _ in range(10)] + board: GameBoard = [["bomb" if random() <= bomb_chance else "number" for _ in range(10)] for _ in range(10)] for y, row in enumerate(board): for x, cell in enumerate(row): if cell == "number": # calculate bombs near it - to_check = [] - for xt in [x - 1, x, x + 1]: - for yt in [y - 1, y, y + 1]: - if xt != -1 and xt != 10 and yt != -1 and yt != 10: - to_check.append(board[yt][xt]) + bombs = 0 + for x_ in [x - 1, x, x + 1]: + for y_ in [y - 1, y, y + 1]: + if x_ != -1 and x_ != 10 and y_ != -1 and y_ != 10 and board[y_][x_] == "bomb": + bombs += 1 - bombs = sum(map(self.is_bomb, to_check)) board[y][x] = bombs return board @staticmethod - def format_for_discord(board: typing.List[typing.List[typing.Union[str, int]]]) -> str: + def format_for_discord(board: GameBoard) -> str: """Format the board to a string for discord.""" mapping = { 0: ":stop_button:", @@ -55,9 +54,11 @@ class Minesweeper(commands.Cog): "flag": ":triangular_flag_on_post:" } - discord_msg = ":stop_button: :regional_indicator_a::regional_indicator_b::regional_indicator_c:" \ - ":regional_indicator_d::regional_indicator_e::regional_indicator_f::regional_indicator_g:" \ - ":regional_indicator_h::regional_indicator_i::regional_indicator_j:\n\n" + discord_msg = ( + ":stop_button: :regional_indicator_a::regional_indicator_b::regional_indicator_c:" + ":regional_indicator_d::regional_indicator_e::regional_indicator_f::regional_indicator_g:" + ":regional_indicator_h::regional_indicator_i::regional_indicator_j:\n\n" + ) rows: typing.List[str] = [] for row_number, row in enumerate(board): new_row = mapping[row_number + 1] + " " @@ -71,26 +72,27 @@ class Minesweeper(commands.Cog): @commands.command(name="minesweeper") async def minesweeper_command(self, ctx: commands.Context, bomb_chance: float = .2) -> None: """Start a game of minesweeper.""" - if ctx.author in self.games.keys(): # Player is already playing + if ctx.author.id in self.games.keys(): # Player is already playing msg = await ctx.send(f"{ctx.author.mention} you already have a game running") await msg.delete(delay=2) await ctx.message.delete(delay=2) return # Add game to list - board = self.generate_board(bomb_chance) - reveled_board = [["hidden" for _ in range(10)] for _ in range(10)] + board: GameBoard = self.generate_board(bomb_chance) + revealed_board: GameBoard = [["hidden" for _ in range(10)] for _ in range(10)] await ctx.send(f"{ctx.author.mention} is playing minesweeper") - chat_msg = await ctx.send(self.format_for_discord(reveled_board)) + chat_msg = await ctx.send(self.format_for_discord(revealed_board)) - await ctx.author.send("play by typing: `.reveal xy xy ...` or `.flag xy xy ...` \nclose the game with `.end`\n" - "cords must be in format `<letter><number>") - dm_msg = await ctx.author.send(self.format_for_discord(reveled_board)) + await ctx.author.send("play by typing: `.reveal xy [xy]` or `.flag xy [xy]` \n" + "close the game with `.end`\n" + "cords must be in format `<letter><number>`") + dm_msg = await ctx.author.send(self.format_for_discord(revealed_board)) - self.games[ctx.author] = { + self.games[ctx.author.id] = { "board": board, - "reveled": reveled_board, + "revealed": revealed_board, "dm_msg": dm_msg, "chat_msg": chat_msg } @@ -102,16 +104,16 @@ class Minesweeper(commands.Cog): async def reload_board(self, ctx: commands.Context) -> None: """Update both playing boards.""" - game = self.games[ctx.author] + game = self.games[ctx.author.id] await game["dm_msg"].delete() - game["dm_msg"] = await ctx.author.send(self.format_for_discord(game["reveled"])) - await game["chat_msg"].edit(content=self.format_for_discord(game["reveled"])) + game["dm_msg"] = await ctx.author.send(self.format_for_discord(game["revealed"])) + await game["chat_msg"].edit(content=self.format_for_discord(game["revealed"])) @commands.dm_only() @commands.command(name="flag") async def flag_command(self, ctx: commands.Context, *cords) -> None: """Place multiple flags on the board""" - board = self.games[ctx.author]["reveled"] + board: GameBoard = self.games[ctx.author.id]["revealed"] for cord in cords: x, y = self.get_cords(cord[0], cord[1:]) if board[y][x] == "hidden": @@ -121,74 +123,73 @@ class Minesweeper(commands.Cog): async def lost(self, ctx: commands.Context) -> None: """The player lost the game""" - game = self.games[ctx.author] - game["reveled"] = game["board"] + game = self.games[ctx.author.id] + game["revealed"] = game["board"] await self.reload_board(ctx) await ctx.author.send(":fire: You lost :fire: ") await game["chat_msg"].channel.send(f":fire: {ctx.author.mention} just lost minesweeper :fire:") - del self.games[ctx.author] + del self.games[ctx.author.id] - async def won(self, ctx: commands.Context): + async def won(self, ctx: commands.Context) -> None: """The player won the game""" - game = self.games[ctx.author] - game["reveled"] = game["board"] + game = self.games[ctx.author.id] + game["revealed"] = game["board"] await self.reload_board(ctx) await ctx.author.send(":tada: You won! :tada: ") await game["chat_msg"].channel.send(f":tada: {ctx.author.mention} just won minesweeper :tada:") - del self.games[ctx.author] + del self.games[ctx.author.id] - def reveal_zeros(self, reveled: typing.List, board: typing.List, x: int, y: int) -> None: + def reveal_zeros(self, revealed: GameBoard, board: GameBoard, x: int, y: int) -> None: """Used when a 0 is encountered to do a flood fill""" for x_ in [x - 1, x, x + 1]: for y_ in [y - 1, y, y + 1]: - if x_ == -1 or x_ == 10 or y_ == -1 or y_ == 10 or reveled[y_][x_] != "hidden": + if x_ == -1 or x_ == 10 or y_ == -1 or y_ == 10 or revealed[y_][x_] != "hidden": continue - reveled[y_][x_] = board[y_][x_] + revealed[y_][x_] = board[y_][x_] if board[y_][x_] == 0: - self.reveal_zeros(reveled, board, x_, y_) + self.reveal_zeros(revealed, board, x_, y_) + + async def check_if_won(self, ctx, revealed: GameBoard, board: GameBoard) -> None: + """Checks if a player has won""" + for x_ in range(10): + for y_ in range(10): + if revealed[y_][x_] == "hidden" and board[y_][x_] != "bomb": + return + else: + await self.won(ctx) - async def reveal_one(self, ctx: commands.Context, reveled: typing.List, board: typing.List, x: int, y: int) -> None: + async def reveal_one(self, ctx: commands.Context, revealed: GameBoard, board: GameBoard, x: int, y: int) -> None: """Reveal one square.""" - reveled[y][x] = board[y][x] + revealed[y][x] = board[y][x] if board[y][x] == "bomb": await self.lost(ctx) elif board[y][x] == 0: - self.reveal_zeros(reveled, board, x, y) + self.reveal_zeros(revealed, board, x, y) + await self.check_if_won(ctx, revealed, board) - # check if won - break_ = False - for x_ in range(10): - for y_ in range(10): - if reveled[y_][x_] == "hidden" and board[y_][x_] != "bomb": - break_ = True - break - if break_: - break - else: - await self.won(ctx) await self.reload_board(ctx) @commands.dm_only() @commands.command(name="reveal") - async def reveal_command(self, ctx: commands.Context, *cords): + async def reveal_command(self, ctx: commands.Context, *cords) -> None: """Reveal multiple cells""" - game = self.games[ctx.author] - reveled = game["reveled"] - board = game["board"] + game = self.games[ctx.author.id] + revealed: GameBoard = game["revealed"] + board: GameBoard = game["board"] for cord in cords: x, y = self.get_cords(cord[0], cord[1:]) - await self.reveal_one(ctx, reveled, board, x, y) + await self.reveal_one(ctx, revealed, board, x, y) @commands.dm_only() @commands.command(name="end") async def end_command(self, ctx: commands.Context): """End the current game""" - game = self.games[ctx.author] - game["reveled"] = game["board"] + game = self.games[ctx.author.id] + game["revealed"] = game["board"] await self.reload_board(ctx) await ctx.author.send(":no_entry: you canceled the game :no_entry:") await game["chat_msg"].channel.send(f"{ctx.author.mention} just canceled minesweeper") - del self.games[ctx.author] + del self.games[ctx.author.id] def setup(bot: commands.Bot) -> None: |