diff options
| author | 2020-04-12 19:14:18 +0300 | |
|---|---|---|
| committer | 2020-04-12 19:14:18 +0300 | |
| commit | 85ed1469cefd408233fcdb84643089b4b6a36d88 (patch) | |
| tree | 2c0666257d2a7cd85a14e0a7f8c229d9d0487556 /bot | |
| parent | (TicTacToe): Created `AI` class for Tic Tac Toe against computer playing. (diff) | |
(TicTacToe): Implemented AI to game and cog.
Diffstat (limited to 'bot')
| -rw-r--r-- | bot/exts/evergreen/tic_tac_toe.py | 82 | 
1 files changed, 54 insertions, 28 deletions
| diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index b306e803..fc8edd70 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -71,13 +71,13 @@ class AI:      async def get_move(self, board: t.Dict[int, str], _: discord.Message) -> t.Tuple[bool, int]:          """Get move from AI. AI use Minimax strategy.""" -        possible_moves = [i for i, emoji in board.items() if emoji in Emojis.number_emojis] +        possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())]          for symbol in (Emojis.x, Emojis.o):              for move in possible_moves:                  board_copy = board.copy()                  board_copy[move] = symbol -                if self.check_win(board_copy): +                if await self.check_win(board_copy):                      return False, move          open_corners = [i for i in possible_moves if i in (1, 3, 7, 9)] @@ -94,7 +94,7 @@ class AI:  class Game:      """Class that contains information and functions about Tic Tac Toe game.""" -    def __init__(self, players: t.List[Player], ctx: Context): +    def __init__(self, players: t.List[t.Union[Player, AI]], ctx: Context):          self.players = players          self.ctx = ctx          self.board = { @@ -112,10 +112,11 @@ class Game:          self.current = self.players[0]          self.next = self.players[1] -        self.winner: t.Optional[Player] = None -        self.loser: t.Optional[Player] = None +        self.winner: t.Optional[t.Union[Player, AI]] = None +        self.loser: t.Optional[t.Union[Player, AI]] = None          self.over = False          self.canceled = False +        self.draw = False      async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]:          """Ask does user want to play TicTacToe against requester. First player is always requester.""" @@ -211,9 +212,11 @@ class Game:          await self.add_reactions(board)          for _ in range(9): -            announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.") +            if isinstance(self.current, Player): +                announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.")              timeout, pos = await self.current.get_move(self.board, board) -            await announce.delete() +            if isinstance(self.current, Player): +                await announce.delete()              if timeout:                  await self.ctx.send(f"{self.current.user.mention} ran out of time. Canceling game.")                  self.over = True @@ -225,11 +228,16 @@ class Game:              if await self.check_for_win():                  self.winner = self.current                  self.loser = self.next -                await self.ctx.send(f":tada: {self.current.user.mention} is won this game! :tada:") +                await self.ctx.send( +                    f":tada: {self.current.user.mention if isinstance(self.current, Player) else 'AI'} " +                    f"is won this game! :tada:" +                )                  await board.clear_reactions()                  break              self.current, self.next = self.next, self.current - +        if not self.winner: +            self.draw = True +            await self.ctx.send("It's DRAW!")          self.over = True @@ -260,27 +268,34 @@ class TicTacToe(Cog):      @is_channel_free()      @is_requester_free()      @group(name="tictactoe", aliases=("ttt",), invoke_without_command=True) -    async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: -        """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" +    async def tic_tac_toe(self, ctx: Context, opponent: t.Optional[discord.User]) -> None: +        """Tic Tac Toe game. Play agains friends or AI. Use reactions to add your mark to field."""          if opponent == ctx.author:              await ctx.send("You can't play against yourself.")              return -        if not all( +        if opponent is not None and not all(              opponent not in (player.user for player in g.players) for g in ctx.cog.games if not g.over          ):              await ctx.send("Opponent is already in game.")              return -        game = Game( -            [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], -            ctx -        ) +        if opponent is None: +            game = Game( +                [Player(ctx.author, ctx, Emojis.x), AI(Emojis.o)], +                ctx +            ) +        else: +            game = Game( +                [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], +                ctx +            )          self.games.append(game) -        confirmed, msg = await game.get_confirmation() +        if opponent is not None: +            confirmed, msg = await game.get_confirmation() -        if not confirmed: -            if msg: -                await ctx.send(msg) -            return +            if not confirmed: +                if msg: +                    await ctx.send(msg) +                return          await game.play()      @tic_tac_toe.group(name="history", aliases=("log",), invoke_without_command=True) @@ -289,13 +304,21 @@ class TicTacToe(Cog):          if len(self.games) < 1:              await ctx.send("No recent games.")              return +        log_games = [] +        for i, game in enumerate(self.games): +            if game.over and not game.canceled: +                if game.draw: +                    log_games.append( +                        f"**#{i+1}**: {game.players[0].user.mention} vs " +                        f"{game.players[1].user.mention if isinstance(game.players[1], Player) else 'AI'} (draw)" +                    ) +                else: +                    log_games.append( +                        f"**#{i+1}**: {game.winner.user.mention if isinstance(game.winner, Player) else 'AI'} :trophy: " +                        f"vs {game.loser.user.mention if isinstance(game.loser, Player) else 'AI'}" +                    )          await LinePaginator.paginate( -            ( -                f"**#{i+1}**: {game.winner.user.mention} :trophy: vs {game.loser.user.mention}" -                for i, game in enumerate(self.games) -                if game.over -                and not game.canceled -            ), +            log_games,              ctx,              discord.Embed(title="Most recent Tic Tac Toe games")          ) @@ -307,7 +330,10 @@ class TicTacToe(Cog):              await ctx.send("Game don't exist.")              return          game = self.games[game_id - 1] -        await ctx.send(f"{game.winner.user} :trophy: vs {game.loser.user}") +        await ctx.send( +            f"{game.winner.user if isinstance(game.winner, Player) else 'AI'} " +            f":trophy: vs {game.loser.user if isinstance(game.winner, Player) else 'AI'}" +        )          await game.send_board(ctx.channel) | 
