Simulation-based AI

About a year ago, I started the implementation of a Simulation-based AI algorithm. The idea is to model it after Chess AIs - i.e. instead of using heuristics to figure out what to play (which is what the current AI does), making the AI simulate (using Forge's rules engine) what would happen as a result of a move and then evaluate that game state.
This way, the AI would be able to see the full effects of its moves and use that information in its decision making process. A simple example would be seeing that playing a creature when the opponent has a Soul Warden and Serra Ascendant with 29 life would cause the Serra Ascendant to become a 6/6 flier.
Some discussion regarding my initial implementation can be found in the Traversable Game State thread, but I'm starting this new thread, since I don't want to hijack that thread more than I already have. Although the goals of the two are similar, the distinction is my Simulation AI uses the existing Forge code base and rules engine, whereas that thread is focused on essentially building a new engine.
Anyway, with the above out of the way, I'd like to start the conversation about the current state of the Simulation AI and potential future work.
First, let me summarize the algorithm briefly. When deciding what Spell or Ability to play, the Simulation AI does the following:
So, back to the current state of its implementation in Forge:
All the code for the current implementation is landed in Forge and it is generally functional (i.e. doesn't crash very often and is able to make reasonable decisions in most cases). While it's generally not ready for end user use, it can be experimented with in the desktop client by right-clicking on the "AI" radio-button and activating the Simulation AI checkbox that appears.
However, this current implementation does have a number of problems:
- There are still game states that it chokes on. You might see this as a "Game copy error". This happens when the simulation code detects that it didn't accurately copy the game state - i.e. when eval(game) != eval(gameCopy). When this happens, the code will print a diff output of the two states, which helps in figuring out the discrepancy. The fix is usually to update the copying code to take into account the extra state.
- Speed. Because the algorithm is recursive and abilities can have many targets it can take a long time for the AI to make a move when there are a lot of things on the battlefield. There are a number of ways to improve this, which I'll cover in a separate post.
- Decisions. Sometimes it will still make sub-optimal decisions. Often, this is because there are aspects that the new AI doesn't yet take into account (e.g. what phase to play spells, or how to best interact with combat, etc) - and sometimes the old heuristic based AI still makes better decisions in those circumstances.
This way, the AI would be able to see the full effects of its moves and use that information in its decision making process. A simple example would be seeing that playing a creature when the opponent has a Soul Warden and Serra Ascendant with 29 life would cause the Serra Ascendant to become a 6/6 flier.
Some discussion regarding my initial implementation can be found in the Traversable Game State thread, but I'm starting this new thread, since I don't want to hijack that thread more than I already have. Although the goals of the two are similar, the distinction is my Simulation AI uses the existing Forge code base and rules engine, whereas that thread is focused on essentially building a new engine.
Anyway, with the above out of the way, I'd like to start the conversation about the current state of the Simulation AI and potential future work.
First, let me summarize the algorithm briefly. When deciding what Spell or Ability to play, the Simulation AI does the following:
- Code: Select all
findBestSpellOrAbilityToPlay(game):
for sa in all valid spells & abilities:
if sa.needsTargets:
for each possible set of targets:
gameCopy = makeCopy(game)
gameCopy.play(spellOrAbility, targets)
score = eval(gameCopy)
else
gameCopy = makeCopy(game)
gameCopy.play(spellOrAbility)
score = eval(gameCopy)
return sa with best score
So, back to the current state of its implementation in Forge:
All the code for the current implementation is landed in Forge and it is generally functional (i.e. doesn't crash very often and is able to make reasonable decisions in most cases). While it's generally not ready for end user use, it can be experimented with in the desktop client by right-clicking on the "AI" radio-button and activating the Simulation AI checkbox that appears.
However, this current implementation does have a number of problems:
- There are still game states that it chokes on. You might see this as a "Game copy error". This happens when the simulation code detects that it didn't accurately copy the game state - i.e. when eval(game) != eval(gameCopy). When this happens, the code will print a diff output of the two states, which helps in figuring out the discrepancy. The fix is usually to update the copying code to take into account the extra state.
- Speed. Because the algorithm is recursive and abilities can have many targets it can take a long time for the AI to make a move when there are a lot of things on the battlefield. There are a number of ways to improve this, which I'll cover in a separate post.
- Decisions. Sometimes it will still make sub-optimal decisions. Often, this is because there are aspects that the new AI doesn't yet take into account (e.g. what phase to play spells, or how to best interact with combat, etc) - and sometimes the old heuristic based AI still makes better decisions in those circumstances.