140 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from typing import List
 | |
| from discord.ext import commands
 | |
| import discord
 | |
| 
 | |
| # Defines a custom button that contains the logic of the game.
 | |
| # The ['TicTacToe'] bit is for type hinting purposes to tell your IDE or linter
 | |
| # what the type of `self.view` is. It is not required.
 | |
| class TicTacToeButton(discord.ui.Button['TicTacToe']):
 | |
|     def __init__(self, x: int, y: int):
 | |
|         # A label is required, but we don't need one so a zero-width space is used
 | |
|         # The row parameter tells the View which row to place the button under.
 | |
|         # A View can only contain up to 5 rows -- each row can only have 5 buttons.
 | |
|         # Since a Tic Tac Toe grid is 3x3 that means we have 3 rows and 3 columns.
 | |
|         super().__init__(style=discord.ButtonStyle.secondary, label='\u200b', row=y)
 | |
|         self.x = x
 | |
|         self.y = y
 | |
| 
 | |
|     # This function is called whenever this particular button is pressed
 | |
|     # This is part of the "meat" of the game logic
 | |
|     async def callback(self, interaction: discord.Interaction):
 | |
|         assert self.view is not None
 | |
|         view: TicTacToe = self.view
 | |
|         state = view.board[self.y][self.x]
 | |
|         if state in (view.X, view.O):
 | |
|             return
 | |
| 
 | |
|         if view.current_player == view.X:
 | |
|             self.style = discord.ButtonStyle.danger
 | |
|             self.label = 'X'
 | |
|             self.disabled = True
 | |
|             view.board[self.y][self.x] = view.X
 | |
|             view.current_player = view.O
 | |
|             content = "It is now O's turn"
 | |
|         else:
 | |
|             self.style = discord.ButtonStyle.success
 | |
|             self.label = 'O'
 | |
|             self.disabled = True
 | |
|             view.board[self.y][self.x] = view.O
 | |
|             view.current_player = view.X
 | |
|             content = "It is now X's turn"
 | |
| 
 | |
|         winner = view.check_board_winner()
 | |
|         if winner is not None:
 | |
|             if winner == view.X:
 | |
|                 content = 'X won!'
 | |
|             elif winner == view.O:
 | |
|                 content = 'O won!'
 | |
|             else:
 | |
|                 content = "It's a tie!"
 | |
| 
 | |
|             for child in view.children:
 | |
|                 child.disabled = True
 | |
| 
 | |
|             view.stop()
 | |
| 
 | |
|         await interaction.response.edit_message(content=content, view=view)
 | |
| 
 | |
| 
 | |
| # This is our actual board View
 | |
| class TicTacToe(discord.ui.View):
 | |
|     # This tells the IDE or linter that all our children will be TicTacToeButtons
 | |
|     # This is not required
 | |
|     children: List[TicTacToeButton]
 | |
|     X = -1
 | |
|     O = 1
 | |
|     Tie = 2
 | |
| 
 | |
|     def __init__(self):
 | |
|         super().__init__()
 | |
|         self.current_player = self.X
 | |
|         self.board = [
 | |
|             [0, 0, 0],
 | |
|             [0, 0, 0],
 | |
|             [0, 0, 0],
 | |
|         ]
 | |
| 
 | |
|         # Our board is made up of 3 by 3 TicTacToeButtons
 | |
|         # The TicTacToeButton maintains the callbacks and helps steer
 | |
|         # the actual game.
 | |
|         for x in range(3):
 | |
|             for y in range(3):
 | |
|                 self.add_item(TicTacToeButton(x, y))
 | |
| 
 | |
|     # This method checks for the board winner -- it is used by the TicTacToeButton
 | |
|     def check_board_winner(self):
 | |
|         for across in self.board:
 | |
|             value = sum(across)
 | |
|             if value == 3:
 | |
|                 return self.O
 | |
|             elif value == -3:
 | |
|                 return self.X
 | |
| 
 | |
|         # Check vertical
 | |
|         for line in range(3):
 | |
|             value = self.board[0][line] + self.board[1][line] + self.board[2][line]
 | |
|             if value == 3:
 | |
|                 return self.O
 | |
|             elif value == -3:
 | |
|                 return self.X
 | |
| 
 | |
|         # Check diagonals
 | |
|         diag = self.board[0][2] + self.board[1][1] + self.board[2][0]
 | |
|         if diag == 3:
 | |
|             return self.O
 | |
|         elif diag == -3:
 | |
|             return self.X
 | |
| 
 | |
|         diag = self.board[0][0] + self.board[1][1] + self.board[2][2]
 | |
|         if diag == 3:
 | |
|             return self.O
 | |
|         elif diag == -3:
 | |
|             return self.X
 | |
| 
 | |
|         # If we're here, we need to check if a tie was made
 | |
|         if all(i != 0 for row in self.board for i in row):
 | |
|             return self.Tie
 | |
| 
 | |
|         return None
 | |
| 
 | |
| 
 | |
| class TicTacToeBot(commands.Bot):
 | |
|     def __init__(self):
 | |
|         super().__init__(command_prefix=commands.when_mentioned_or('$'))
 | |
| 
 | |
|     async def on_ready(self):
 | |
|         print(f'Logged in as {self.user} (ID: {self.user.id})')
 | |
|         print('------')
 | |
| 
 | |
| 
 | |
| bot = TicTacToeBot()
 | |
| 
 | |
| 
 | |
| @bot.command()
 | |
| async def tic(ctx: commands.Context):
 | |
|     """Starts a tic-tac-toe game with yourself."""
 | |
|     await ctx.send('Tic Tac Toe: X goes first', view=TicTacToe())
 | |
| 
 | |
| 
 | |
| bot.run('token')
 |