diff options
| author | 2019-08-10 15:06:45 +0200 | |
|---|---|---|
| committer | 2019-08-10 15:06:45 +0200 | |
| commit | 1cb1ec544a918d585bc2160c5a822906d6c0c1d7 (patch) | |
| tree | 56cdf8c92784488ccf9535acb85aac77d3595ee9 /bot | |
| parent | fixed a small bug (diff) | |
changed thing after requests.
Diffstat (limited to 'bot')
| -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: | 
