Creating AI Opponents: Dive into the Code

by Hexagon, 2 minutes read deno tetris guide deno-tetris-guide

Welcome back to part six of our Detris series! After getting our hands dirty with multiplayer, let's tackle another cool feature: AI opponents. This article will walk you through the TypeScript code behind our Tetris AI. We'll look at how the AI decides to move and rotate blocks, and how to plug this AI into your game. Let's dive in!



The source code for the ai player is found at server/aiplayer.ts.

The AIPlayer Class

We have an AIPlayer class extending from the BasePlayer class. This allows our AI to act like any other player in the game, with some extra methods to handle AI logic.

class AIPlayer extends BasePlayer {
    private aiInterval: number | undefined;
    ...
}

Decision Timing

The AI doesn't make decisions instantaneously. We introduce some delay to mimic human-like behavior.

private BASE_DECISION_DELAY_MS = 200;
private RANDOM_DECISION_DELAY_MAX_MS = 500;

AI Logic

The AI logic is encapsulated in the calculateAIControls method. This function evaluates the current game state and decides on the best move. It checks for space below the falling block, and selects the move that minimizes this space, effectively making the stack more compact.

private calculateAIControls(): { [key: string]: boolean } {
    ...
    let leastSpaceBelow = Infinity;
    ...
    for (let rotation = 0; rotation < 4; rotation++) {
    ...
    }
    ...
}

Connecting AI to the Game

To add the AI player to the game, we call the connect method. This sets up the game and starts the AI logic.

public connect(game: Game) {
    ...
    this.startAI();
}

Understanding the AI Algorithm

Calculating the Best Move

The calculateAIControls function serves as the brain of the AI. It calculates the best move by iterating through all possible rotations and positions for the current Tetromino. The function aims to find the position with the least amount of space below it, making sure the stack is as compact as possible.

private calculateAIControls(): { [key: string]: boolean } {
  ...
  let leastSpaceBelow = Infinity;
  let bestMove = null;

  for (let rotation = 0; rotation < 4; rotation++) {
    // Try different positions
    for (let x = -2; x < grid.width; x++) {
      ...
      // Check for least space below
      const spaceBelow = this.countEmptySpaces(grid, bogusSprite, {
        X: x,
        Y: y,
      });

      if (spaceBelow < leastSpaceBelow) {
        leastSpaceBelow = spaceBelow;
        bestMove = { x, rotation };
      }
      ...
    }
  }
  ...
}

And there you have it! Your Tetris game can now pit players against a computer-controlled opponent. In the next part, we'll wrap it up and discuss future enhancements. Stay tuned!


Adding Multiplayer: Server Logic Conclusion and Future Enhancements