Writing Interesting AI

By Frank Mitchell (Website,Twitter)

For a long time, the only kind of game AI I knew how to write was a mean one. You know, the kind that picks the best possible move every time. The kind no one wants to play against.

It wasn't until I found Dan Cook's work on game atoms, and player skill progression in games, that I started thinking there might be a way to write less mean AI. Maybe computers could learn to play games more like people, and I could write AI that were interesting game players.

Any rule book for a game has two things at its core:

1. The mechanics of how to play

2. The win and loss conditions

There is almost never anything about skill, or strategy, or tactics, or getting better. We learn these things as we play the game. So how do you build an AI that can learn those things too?

You start at the same place you do when teaching a person. These are the things you can do on your turn. This is how you know when the game is over. Code the game state as raw data. Code each "thing you can do on your turn" as a function that takes an existing game state and transforms it into a new game state. Code "how you know when the game is over" as a function that takes a game state and returns yes or no. Now you have enough for an AI.

In the beginning, your AI will play randomly. It will pick a thing to do on its turn and it will do that thing. Then it will check to see if the game is over. Think about a first time checkers player. They push pieces around the board. They follow the rules. Take a jump if you can. If there are no jumps to take, move diagonally to an empty space. This is what your AI does too. It is a beginner with no memory. It doesn't see forks or double jumps or king making moves. How do you teach it those things?

With just a rule book, the most basic assessment of a game you can make is, does this move cause me to win? So have your AI do that. Get a list of all the possible moves. If any of them are winning moves, pick a winning move to play. If none of them are winning moves, pick a random move to play.

If you can see moves that cause you to win, you can also see moves that cause your opponent to win. Have your AI do that too. Find all the winning moves that the opponent could make if it was their turn. Find all the moves the AI can make that will block the opponent from winning. Have your AI choose a random blocking move and play it.

Now you have an interesting AI. If it has a winning move, it plays one. If it has no winning moves, it plays a blocking move. If it has no blocking moves, it plays a random move. But it's not perfect. A good player can still beat it. When you set up a fork, where you have two winning moves, the AI can only block one of them.

Each of these decisions for move types (winning, blocking, random) is a function. Think of more functions to make your AI more interesting. How about a function to pick moves that result in double jumps? Or a function to pick moves that aren't back row pieces. Or a function to pick moves that make kings. Now your AI can do lots of things, and the order it chooses to do them in becomes its style of play.

Your AI might be flashy, choosing double jump moves and king making moves. Or it might be defensive, choosing blocking moves and moves that keep back row pieces immobile. Or it might be chaotic, trying every move function randomly until it finds something playable.

In the end, your AI might ignore winning moves entirely. So when it wins, it will be by accident, almost as if it didn't see the winning move. Almost as if it made a mistake. Almost as if it was human.

P.S. If you're looking for code examples, I made an interesting AI for Nine

Holes, one of the oldest games in the world.