excessum wrote:I need a little help understanding the code...
simulateSpellAbility()
- ComputerUtil.handlePlayingSpellAbility(player, sa, game) simulates playing "sa" in "game"
- resolveStack() places the effect(s) that result from the above onto the stack
- loop with "recursionDepth" tries to play other abilities after this
Is the above correct? For a start, I only wish to simulate a single SpellAbility so I can skip the third step entirely. After simulating "sa", I would return the resultant "game" for analysis. Where exactly does the simulation decide how to play subsequently triggered abilities with choices (ie. casting
Shock with
Shu Yun, the Silent Tempest and
Jeskai Ascendancy on board)?
EDIT: Figured out what "recursionDepth" is for, and it is a real resource hog for aggro decks with multiple possible plays in the mid-late game.
Right, recursionDepth is tracking how many moves ahead the AI looks. You can turn it back to just looking one move ahead (as I had it originally) by changing GameSimnulator.MAX_DEPTH constant.
Note that this will cause the AI to miss some move combinations where the sum of two moves is the best thing to do rather than any of the individual moves. Some examples include:
- Cracking a fetchland in order to be able to play e.g. a seige rhino that was previously not possible without it
- Casting two low-cost spells that together give a better benefit than one high cost spell for the same mana
With the recursion, the AI can find the above "automatically". However, I agree that in the current implementation, it can slow to a crawl if there's a lot of abilities available for AI to choose (especially targetted abilities). Right now it does all the possible simulation for the given depth in any order - so exponential complexity. The right way to fix this is to:
- Priority execution - i.e. choosing which recursion to go in first based on the score of the simulation of the first move
- Timing run time, so that the AI can "give up" and return the best move so far when the allocated time resolves
- Remembering best targets for abilities - so that targetted SAs don't explode on each recursion
- Pruning identical simulations - e.g. if two abilities A and B result in the same board state, then after simulating A->B, if you simulate B->A you should be able to determine that it's the same state and stop simulating that branch (i.e. stopping going down further) since you already have processed that state.
The above is just related to the recursion aspects of the code. Of course, there's many other things that need improvement - such as the things I mentioned earlier (e.g. simulating combat, scoring the game state) and other things - e.g. better deciding of spell timing (when it's better to hold a spell even if it would result in some benefit - e.g. saving a removal spell for when the opponent has something better than a 1/1).