My algorithm and design for the Google AI Challenge

In short: modified min-max + STM

I know multithreading is not allowed, but still I programed it in a way such that it has the potential to scale to arbitrary cores. There's one benefit even on the current system, that is the worker thread is detached from the main thread, so I can kill the worker thread when time is running out, I get an incorrect solution, but the program will not be rejected by the system.

I read the min-max tutorial on their website, and the article on wikipedia, from my understanding, it's just a function of:

1
2
3
4
eval :: State -> Int
eval = undefined

measure = eval my_state - eval your_state

Then this number is used as a measurement to pick the best move.

I had to tweak it in the following ways:

eval your move twice:

eval my_state and eval your_state have to be used independently, since two players move concurrently, how the other moves is unknown respectively. I cheated by calculating your state twice, where the latter takes into account this particular move of mine being considered.

dynamic depth of recursion

Depending on the size of the board, the number of moves I can look ahead various. This is a potential timeout. I simply hardcode a function ( Int -> Int ) that takes the number of free spot on the board and returns a recursion depth, that's been passed to the main worker function.

TVar board

Since the goal of this recursive thinking is to figure out if a free spot is worth to move to, i.e. comparing it's current weight to the weight we get from measure, there has to be a way to reference the original spot and set a value to it when the function thinks that this route actually worth more then it was previously considered. My solution is to put the board to a TVar and pass this reference around along with a pivot of the spot being considered.

(Bonus) The actual eval function

I first hacked up my own algorithm to determine if a spot leads to more space. After some tweaks, it started to perform pretty well, despite the fact I don't know why it works exactly, and there's no hope for me to reason the correctness of it. After some look around, I found out the proper name for the algorithm is call Flood Fill. Then I implemented a smaller, cuter version of flood fill, and it turned out that, despite being correct, it sucks.

Implicit or explicit -ly, I'm just too cheap to go with correctness.

That's pretty much it, btw, my data structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data V2 = V2
{
x2 :: Int
, y2 :: Int
}
deriving (Show, Eq, Ord, Ix)

data Spot =
Me
| You
| Space Int
| Wall
deriving (Show, Eq)

type Grid = Map V2 Spot

data Board = Board
{
grid :: Grid
, bounds :: (V2, V2)
}
deriving (Show, Eq)

Pretty stupid, whatever.

blog comments powered by Disqus