Create a tic-tac-toe game using custom commands bot

Md Shahriyar Alam

10 days ago

First go to your specific server ccbot.appDashboard and click Create > Slash Command

Give it any name, in our example it will be tic-tac-toe and in the Command Description field put anything you want

Now move to the Hooks section and we need 2 views, first view is to allow someone to click a button to accept your challenge

pyp1 = user.id
p2 = None

class StartView(View):
    def __init__(self):
        self.user_id = None

    @button(label="Accept", style="success")
    async def accept(self, i, b):
        if i.user.id == p1:
            await defer(i, action="create", ephemeral=True)
            return respond_interaction(i, content="You can't play with youself")
        
        self.user_id = i.user.id
        await defer(i, action="update")
        respond_interaction(i, empty=True)
        self.stop()
        
view = StartView()
respond_interaction(
    interaction, 
    content=f"{user.mention} is challenging you to play Tic-Tac-Toe", component=view
)

exit_code = await view.wait(scope=scope, timeout=60)
if exit_code == "timeout":
  set_error("No one accepted the challenge.")

p2 = view.user_id

Since this game need 2 player, we initialized p1 to be the user who ran the command and for p2 we initially made it None and then made a view where anyone can click Accept button to be the second player. And when someone accepts we assign p2 to be the user id of that person

In the next part we make another view that controls the whole logic of the command.

pyasync def check_winner(board):
    winning_combinations = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6] 
    ]
    
    is_game_over = False
    winner = None

    def check_combination(combination, index):
        nonlocal winner
        a, b, c = combination
        if board[a] is not None and board[a] == board[b] == board[c]:
            winner = board[a]
            break_loop() 
    
    loop(winning_combinations, check_combination)
    
    if (None not in board) or winner:
        is_game_over = True

    return [is_game_over, winner]

class GameView(View):
    def __init__(self):
        self.board = [
            None, None, None,
            None, None, None,
            None, None, None,
        ]

        self.players = [p1, p2]

        self.emojis = {
            p1: "⭕",
            p2: "❌"
        }
        
        self.current_player_index = random([0, 1]) # randomly select who goes first
        self.winner = None

    def get_current_player(self):
        return self.players[self.current_player_index]
    
    def change_player(self):
        self.current_player_index = 0 if self.current_player_index else 1
    
    async def validate_interaction(self, i):
        if i.user.id not in self.players:
            await defer(i, action="create", ephemeral=True)
            return respond_interaction(i, content="You are a member of this board") 

        if i.user.id != self.get_current_player():
            await defer(i, action="create", ephemeral=True)
            return respond_interaction(i, content="Its not your turn")

        return True
    
    async def update_board(self, i):
        await self.update_view()
        self.change_player()

        turn = get_member(self.get_current_player())
        respond_interaction(i, content=f"{turn.mention}'s turn")
    
    async def handle_turn(self, i, b):
        is_valid = await self.validate_interaction(i)
        if not is_valid:
            return

        await defer(i, action="update")
        self.board[b.metadata['index']] = i.user.id

        b.label = self.emojis[i.user.id]
        b.is_disabled = True

        is_game_over, winner = await check_winner(self.board)
        if winner:
            self.winner = winner
            self.stop()
            return

        if is_game_over:
            self.stop()
            return
        
        await self.update_board(i)

    @button(label="‎", row=1, index=0)
    async def one(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=1, index=1)
    async def two(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=1, index=2)
    async def three(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=2, index=3)
    async def four(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=2, index=4)
    async def five(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=2, index=5)
    async def six(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=3, index=6)
    async def seven(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=3, index=7)
    async def eight(self, i, b):
        await self.handle_turn(i, b)

    @button(label="‎", row=3, index=8)
    async def nine(self, i, b):
        await self.handle_turn(i, b)
    
    async def start_game(self, i):
        player = get_member(self.get_current_player())
        respond_interaction(i, content=f"{player.mention}'s turn", component=self)

view = GameView()
await view.start_game(interaction)

exit_code = await view.wait(scope=scope, timeout=60)

if exit_code == "timeout":
    set_error("The board has been abandoned.")

winner = None
if view.winner:
    winner = get_member(view.winner)

message = f"The winner is {winner.mention} 🎉" if winner else "It's a draw"

And finally in the Response section of the command we can put {message} and create the command

Example

example

Need help?

If you still need any kind of help, have a suggestion for us or having a question in mind? Feel free to reach us out.

DiscordJoin support server
logoCustom Commands

#1 Custom commands discord bot available on the internet

stripe

Contact

20-22 Wenlock RoadEnglandN1 7GU

© WEiRDSOFT LTD. All rights reserved.