channels - how to Implement non-blocking, sequential AI moves in a multiplayer game?

I previously asked the question on Stack Overflow, but sadly without any response.

I’m developing a website to play a special with kind of chess with React and Django.

It pretty much requires two games to be played at the same time. The website allows to add AI players, so the common scenario is to have board 1 (human vs AI) and board 2 (AI vs AI). I want the computer players to behave in a manner that they play a move, wait a second, then let another one play, not blocking other games. The problem is that the following code makes the entire AI vs AI game play in a blink of an eye and makes it impossible to send and receive any event (even the game.start event does not get sent).

This is my code that triggers the computers playing against each other:

   for game in started_games:
            await self.channel_layer.group_send(
                self.room_group_name, {'type': 'game.start'})

            if self.is_ai_turn_in_game(game):
                if game.gamemode == GameMode.CLASSICAL.value:
                    board = chess.Board(fen=game.fen)
                else:
                    board = chess.variant.CrazyhouseBoard(fen=game.fen)

                print(self.is_ai_turn_in_game(game))
                while self.is_ai_turn_in_game(game):
                    await self.handle_ai_turn(game, board)
                    await self.send_move_to_clients(game)

self.send_move_to_clients processes some data, then calls

            await self.channel_layer.group_send(
                self.room_group_name, {'type': 'game.move', 'message': response_data}
            )

which executes the following code:

    async def game_move(self, event):
        move = event['message']
        await self.send(json.dumps(move))
        await asyncio.sleep(1)

I am wondering if I should use a thread here, or maybe I’m misunderstanding how async event loops function.

Welcome @albertodwutakt !

Working fully async requires a bit of a change of thinking.

You wrote:

This actually isn’t what happens.

The group_send call sends a message to the message channel. It does not execute game_move. What the await is waiting for is for the message to be sent. This does not have anything to do with game_move being executed.

After the message is sent, at some point in time in the future, the dispatcher will inspect the message and call the game_move method for every member of the group.

If you don’t want the flow of control to proceed past this point until after game_move has finished for every member of the group, then you need to engineer this into your code somehow. (How you do this is likely to depend upon whether this group is a “one-client” group or “multiple-client” group.)

Hello, thanks for the kind response!

I would like the AI players to play with, let’s say 1-second delay between each other player’s move, independently of what’s happening on other boards, which are the part of the game. I have no idea how to implement this, and that’s the main issue I’m having here.

It’s a multiple-client application.

I’d be grateful for help.

I don’t think I understand enough about your project and how it’s organized to really offer any specific advice. In general, if you want a task to be paused for a second, then you would include the await asyncio.sleep call in that particular task - you don’t want to send a message to a different task to do that.