ChatGPT AI Building a Turn Based Strategy Game, Part 1

Like half the world, it seems, I thought I would play with OpenAI’s ChatGPT 4. I have toyed with various things to see how it interacts and what it can produce. Much excitement is around social media is leveraging AI as a developer code assistant. As a software developer myself, I figure it is a good area for me to evaluate.

My objective is to create a turn-based strategy game, something in the vein of Antiyoy, which is one of the few computer games I enjoy playing. I don’t want to copy it. I want to have something similar but with different features and resources. I also don’t want to write the code. I expect this process to take a while. I will post progress as I pass notable milestones.

I am fully capable of writing the code, depending on the language. While my strengths are in database development, I sometimes write user-interface and application code for server management systems and cybersecurity tools. I am not, by any measure, a game developer. This game will be browse-based using JavaScript, a language I have used but not extensively. Thusly I forge ahead.

Version 0.1

After a few hours, with maybe half the time spent setting up a development environment on my personal laptop, my ChatGPT partner and I managed something very basic, but oddly functional.

I started with this:

Me:

are you familiar with the game Antiyoy?

Chat:

Yes, I am familiar with the game Antiyoy. Antiyoy is a turn-based strategy game developed by Yiotro. It is available on Android and iOS platforms. The game features simple graphics and intuitive gameplay, which focuses on capturing territories and managing resources.

The objective of the game is to capture all the territories on the map and eliminate your opponents. Players take turns moving their units and constructing buildings. Each player has a limited number of resources, and careful resource management is essential for success. The game offers various levels of difficulty and can be played in both single-player and multiplayer modes.

The game has been praised for its simplicity, depth of strategy, and replayability, making it a popular choice for fans of turn-based strategy games.

Me:

write p5.js code for Antiyoy I want to use my own textures for the objects.

I didn’t mince words.

Chat immediately generated an html page and a JavaScript code block for me to leverage. I created the specified files in a localhost web and opened a browser to the index.html page as instructed. It didn’t do much, and I didn’t expect it to create a game from whole cloth.

I won’t go through every step, as there are many and most are not very informative. The pattern, however, has been the same. I state what I want or see, and I get feedback.

Me:

how do I add game logic to select a unit on the board and move it to a different location?

Chat:

To add game logic for selecting a unit and moving it to a different location, you'll need to modify the sketch.js file to include the following changes:
1.	Add a variable to keep track of the selected unit.
2.	Update the mouseClicked() function to handle unit selection and movement.
3.	Add a function to check if a move is valid.
4.	Optionally, add visual feedback for the selected unit.
Here's an updated version of the sketch.js file with these changes:
// ... (previous code)

let selectedUnit = null;

// ... (previous code)

function drawUnits() {
  for (let y = 0; y < gridSize; y++) {
    for (let x = 0; x < gridSize; x++) {
      if (board[y][x].unit) {
        if (selectedUnit && x === selectedUnit.x && y === selectedUnit.y) {
          tint(255, 127); // Add transparency to the selected unit
        } else {
          noTint(); // Reset tint for other units
        }
        image(unitTexture, x * tileSize, y * tileSize, tileSize, tileSize);
      }
    }
  }
}

function mouseClicked() {
  let x = int(mouseX / tileSize);
  let y = int(mouseY / tileSize);

  if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) {
    console.log(`Cell (${x}, ${y}) clicked`);

    let cell = board[y][x];

    if (selectedUnit) {
      if (isValidMove(selectedUnit.x, selectedUnit.y, x, y)) {
        moveUnit(selectedUnit.x, selectedUnit.y, x, y);
        selectedUnit = null;
      }
    } else if (cell.unit) {
      selectedUnit = { x, y };
    }
  }
}

function isValidMove(fromX, fromY, toX, toY) {
  let fromCell = board[fromY][fromX];
  let toCell = board[toY][toX];

  // Check if the destination cell is unoccupied and on grass terrain
  if (toCell.unit || toCell.terrain !== 0) {
    return false;
  }

  // Check if the move is within a 1-cell range (orthogonally or diagonally)
  let dx = abs(fromX - toX);
  let dy = abs(fromY - toY);
  if (dx <= 1 && dy <= 1) {
    return true;
  }

  return false;
}

function moveUnit(fromX, fromY, toX, toY) {
  let fromCell = board[fromY][fromX];
  let toCell = board[toY][toX];

  toCell.player = fromCell.player;
  toCell.unit = true;
  fromCell.unit = false;
}

// ... (previous code)
This updated code allows you to click on a unit to select it, and then click on a valid destination cell to move it. The selected unit will be shown with a transparent tint to indicate it's selected. The isValidMove() function checks if the move is within a 1-cell range and ensures that the destination cell is unoccupied and on grass terrain. You can modify the isValidMove() function to implement more complex movement rules and game logic as needed.

Much of the interaction is that simple. I would state things like, “nothing happens when I click on a unit”. ChatGPT would respond (often with apologies, which I find amusing) and then suggest fixes to previously presented functions.

One time it asked for the entire script to find an error, which turned out to be a single missing declaration.

Iterations from ChatGPT often include partial code:

// ... (previous code)

let currentPlayer = 1;

let lastAction = null;

// ... (previous code)

Which is fine and generally better than reposting an entire script or lengthy function. The problem is it is not always clear where that supplied updates need to go, and a mistake is made. I would send something like the following to ChatGPT:

Me:

This is my current draw() function. Did I make an error:
function draw() {
    background(220);
  
    for (let y = 0; y < gridSize; y++) {
      for (let x = 0; x < gridSize; x++) {

        drawBoard();
        drawUnits();
  
        if (selectedUnit && selectedUnit.x === x && selectedUnit.y === y) {
          fill(255, 255, 255, 100); // Add a transparent white tint
          rect(x * tileSize, y * tileSize, tileSize, tileSize);
        }
      }
    }
}

Chat:

I apologize for the confusion. The issue is that you have placed the drawboard() and drawUnits() functions inside the nested loops, which causes them to be called multiple times, and the transparent tint is drawn beneath the units. To fix this, move the drawBoard() and drawUnits() functions outside the loops. Also, move the code for drawing the transparent tint outside the loops and call it after drawUnits().

Chat then provided a correctly updated function.

After a couple of hours, I have written no code myself, only replacing blocks of script as instructed after each iteration. While this may sound tedious and perhaps boring, it is quite instructive and engaging.

The engagement is sparked by the need to think about what to ask and how to ask it. The process is instructive because it is a development area and language that I am not well-versed in.

Currently, I have something that surprisingly works. It is not pretty, but it could in fact be a rather silly game. In this current form it is more like the board game “Go”. This is definitely not what I want in the end, yet the trajectory is right.

ChatGPT AI Game Version 0.1.0

In my next session I intend to ask ChatGPT to refactor the code into classes and provide more sophisticated functionality. I want this to work and be maintainable. I know what well written code should look like, and I will ask that ChatGPT write it as such.

As I approach a more useful and functional game, I will provide the code in a git repository.