I'm writing an AI for the game 2048 using Python. It's going a lot slower than I expected. I set the depth limit to just 5 and it still took several seconds to get an answer. At first I thought my implementations of all the functions were crap, but I figured out the real reason why. There are way more leaves on the search tree than there even possibly should be.
Here is a typical result (I counted the leaves, branches, and number of expansions):
111640 leaves, 543296 branches, 120936 expansions
Branching factor: 4.49242574585
Expected max leaves = 4.49242574585^5 = 1829.80385192 leaves
and another, for good measure:
99072 leaves, 488876 branches, 107292 expansions
Branching factor: 4.55650001864
Expected max leaves = 4.55650001864^5 = 1964.06963743 leaves
As you can see, there are way more leaves on the search tree than how many there would be if I used naive minimax. What is going on here? My algorithm is posted below:
# Generate constants
import sys
posInfinity = sys.float_info.max
negInfinity = -sys.float_info.max
# Returns the direction of the best move given current state and depth limit
def bestMove(grid, depthLimit):
global limit
limit = depthLimit
moveValues = {}
# Match each move to its minimax value
for move in Utils2048.validMoves(grid):
gridCopy = [row[:] for row in grid]
Utils2048.slide(gridCopy, move)
moveValues[move] = minValue(grid, negInfinity, posInfinity, 1)
# Return move that have maximum value
return max(moveValues, key = moveValues.get)
# Returns the maximum utility when the player moves
def maxValue(grid, a, b, depth):
successors = Utils2048.maxSuccessors(grid)
if len(successors) == 0 or limit < depth:
return Evaluator.evaluate(grid)
value = negInfinity
for successor in successors:
value = max(value, minValue(successor, a, b, depth + 1))
if value >= b:
return value
a = max(a, value)
return value
# Returns the minimum utility when the computer moves
def minValue(grid, a, b, depth):
successors = Utils2048.minSuccessors(grid)
if len(successors) == 0 or limit < depth:
return Evaluator.evaluate(grid)
value = posInfinity
for successor in successors:
value = min(value, maxValue(successor, a, b, depth + 1))
if value <= a:
return value
b = min(b, value)
return value
Someone please help me out. I looked over this code several times and I can't pin down what's wrong.