Lukas Domagala

  Home  |   Contact  |   Syndication    |   Login
  6 Posts | 0 Stories | 7 Comments | 0 Trackbacks

News

I have joined Anti-IF Campaign
Subscribe in a reader

Tag Cloud


Archives

Post Categories

Thursday, June 25, 2009 #

While I was building the position heuristic function for Connect Four I ran into an interesting gotcha with F# pattern matching. Lets see if you see it before I tell you what it is:

let rec heuristic (positions: (int * int) list) (pos: int*int) =
    match positions with
    | [] -> 0
    | position::_ -> 1 + (heuristic (List.tl positions) pos)
    | _ -> heuristic (List.tl positions) pos

Looking at it, it seems the code should tell me how often value is in position, right? Well it doesn’t! And there is a good reason for it. When pattern matching you are capturing a new value instead of testing for one. So in line 4 this code defines pos to be the head of position, now matter what is in there. That’s the reason the compiler warns us that last rule will never match.

What we should have done is this:

let rec heuristic (positions: (int * int) list) (pos: int*int) =
    match positions with
    | [] -> 0
    | position::_ when pos = position -> 1 + (heuristic (List.tl positions) pos)
    | _ -> heuristic (List.tl positions) pos

But I have to say, this looks a little ugly compared to the first version. There is a blog post by Chris Smith about this, where he tells us to use the Literal attribute if we want it match against a value. Well this does not work for anything other then primitive types…

So how do we get our pattern matching code to look nice again? Active Patterns to the rescue! Active Patterns are a little hard to grasp at first, but they can save you a lot of code and make your designs much nicer, if used right. I always think of an Active Pattern as pattern matching against a function application.

Lets see what happens when we use them in our example:

let rec heuristic (positions: (int * int) list) (pos: int*int) =
    let (|Same|Different|) position = if pos = position then Same else Different
    match positions with
    | [] -> 0
    | Same ::_  -> 1 + (heuristic (List.tl positions) pos)
    | Different ::_ -> (heuristic (List.tl positions) pos)

I think this code is a lot clearer then what the second version and works with any type you throw at it.

Active Patterns are good for a lot more then this but I’ll get into other applications of it some other time. Just remember this pattern matching gotcha so you don’t waste any time pulling out your hair;)

 

Update:

Just to complete this, here is the version that you should be using, if you want to copy paste this code. It uses tail recursion with an accumulator argument so that the stack doesn’t grow.

let rec heuristic (positions: (int * int) list) (pos: int*int) =
    let (|Same|Different|) position = if pos = position then Same else Different
    let rec loop positions acc =        
        match positions with
        | [] -> acc
        | Same :: _  -> (loop (List.tl positions) (acc + 1))
        | Different :: _ -> (loop (List.tl positions) acc)
    loop positions 0

 


Tuesday, June 23, 2009 #

I ran into some rather interesting numbers while trying to optimize my Connect Four implementation. Try to guess what this code will print out:

let test=
    let stop1 = Stopwatch.StartNew()
    let list = [1..1000000]     
    let bla = list |> List.fold (fun state x -> state + x) 0
    stop1.Stop()
    let stop2 = Stopwatch.StartNew()
    let seq = seq{1..1000000} |> Seq.fold (fun state x -> state + x) 0
    stop2.Stop()  
    let stop3 = Stopwatch.StartNew()
    let arr = [|1..1000000|]     
    let arr1 = arr|> Array.fold (fun state x -> state + x) 0
    stop3.Stop()
    let stop4 = Stopwatch.StartNew()
    let seqList = [1..1000000] |> Seq.fold (fun state x -> state + x) 0
    stop4.Stop()  
    printfn "list: %d seq: %d array: %d seqList: %d" 
            stop1.ElapsedTicks 
            stop2.ElapsedTicks 
            stop3.ElapsedTicks 
            stop4.ElapsedTicks
list: 18357402 seq: 10837883 array: 8420279 seqList: 15246287

It seems creating a list enumerable takes about double the time it takes to create either a sequence or an array. Usually this does not matter that much, but it can give you worse performance if you are using fold/map instead of manual looping. I guess there is a performance hit for simplicity…

Lets see how well we can do in the imperative world:

let test2=
    let stop1 = Stopwatch.StartNew()
    let mutable i = 1
    let mutable y = 0
    while i < 1000000 do
        y <- y+i
        i <- i+1
    stop1.Stop()
    printfn "loop: %d" stop1.ElapsedTicks
loop: 40852

Interesting, it seems most of the time is wasted creating the data structure we want to loop over, so what happens if we don’t count the creation of the array?

let test3=
    let array = [|1..1000000|] 
    let stop = Stopwatch.StartNew()
    let result = array |> Array.fold (fun state x -> state + x) 0
    stop.Stop()  
    printfn "array: %d" stop.ElapsedTicks

array: 65653

Well we are getting closer. Although we have to problem now that we have to cache the arrays we want to loop over. Lets try not being so wasteful and doing it all in a nice recursive loop:

let test4=
    let sum x=
        let rec loop x y =
            if y = 0 then x
            else loop (x+y) (y-1)
        loop 0 x
    let stop = Stopwatch.StartNew()
    let result =sum 1000000
    stop.Stop()
    printfn "recursive: %d" stop.ElapsedTicks
recursive: 28952

Nice, its faster then the imperative version. The problem with this code is that it does not state its intent at all. When i folded over the array version it took a second and i knew what the code was supposed to do. The same thing can’t be said for the imperative or the recursive version. It seems you have to sacrifice a lot of performance for readability sometimes. I´m not really looking forward to rewriting all my tight loops into these recursive monsters…


Friday, June 19, 2009 #

Last time we saw how to implement the basic miniMax algorithm, this time we will continue by designing game board representation.

Basically I want to start out with the “simplest thing that might possibly work” and optimize from there. The simplest thing to use for this in F# is a 2 dimensional list. The problem is that we get some really bad performance for random access into them, because unlike C# Lists, called ResizeArray in F#, they really are represented by lists internally. To be precise they are actually just this:

type list<'T> =
  | ( [] )
  | ( :: ) of 'T * 'T list

Ok so what do we choose instead of lists? We could take Array2D which is just what we need, but we would loose all the nice functional methods we have on other F# collections. So we will have to stick with an array of arrays I guess. So what put into those lists? In C# i would probably have started with something Byte, but F# has the nice concept of discriminated unions which are basically types which can be in one of the given states. Here is what we will be using for now:

type field = Yellow | Red | Empty

Usually we could just stick with our nice discriminated unions, but again we have to think at least a little about performance. Discriminated unions are represented by classes that hold integers for every state the union can be in, which would mean we had references which are huge compared to a small byte. Well I guess we cant always get the the easiest thing possible, so we will go with bytes this time. –1y for one player, 0y for the other and 1y for the second player.

Now we need some way to initialize a 2D list to be filled with 0y, to get our starting game board. In an imperative language i might have used something like to nested loops to fill in the lists. There is nothing wrong with this approach but its not really functional and kind of discouraged in the F# world. The closest you might see would be something like:

let createEmptyBoard rows cols =
    [for i in 0..(rows-1) do
        for y in 0..(cols-1) -> 0y]

This is using something called sequence expressions which is really useful for creating all kinds of sequences, lists and so on. There is an easier way though:

static member EmptyPosition cols rows =
    Position((Array.create 7 (Array.create 6 0y)),1y)

Now that we can create an empty board lets think about how we would drop in a disc to create a new board. Notice that we do not want to change the old board, but to create a new one. We already know what our function definition should look like so lets work from there:

let toggle player=
    player * -1y
member p.CanDropIn x =
    if board.[x] |> Array.tryFindIndex (fun y -> y = 0y) = None then false
    else true
member p.DropIn x =
    if not (p.CanDropIn x) then failwith "you tryed to put into a full slot"
    else
        let idx = board.[x] |> Array.findIndex (fun y -> y = 0y)
        let updatedRow = board.[x] |> Array.mapi ( fun y field -> if y = idx then player else field)
        let newFields = board |> Array.mapi (fun y row -> if y = x then updatedRow else row)
        Position (newFields, (toggle player))

Firs we check if its possible to dropIn at that position we create the new board by mapping the Empty slot we found with the color we were provided. We already have most of the things we need to finish our board representation, which will make writing the AI really easy.

Next time we will encapsulate all this into a class so that it is easier to use and I will show you how we can calculate a reasonable score for a board position. I´m actually still not sure if i should build a class or put all of this into a module. Some input would be very welcome.

Technorati Tags: ,,,

Thursday, June 18, 2009 #

Ok, so do you have any idea what happens when you evaluate this?

printfn "%d" -Int32.MinValue;;

Well I would have guessed I´d get Int32.MaxValue, but well I was wrong. Actually it overflows again and prints Int32.MinValue. Interesting behavior that drove me mad while i was trying to implement Alpha-Beta pruning.

There is a simple reason this happens: “The Int32 value type represents signed integers with values ranging from negative 2,147,483,648 through positive 2,147,483,647.” – MSDN

Ok so there is no positive version of Int32.MinValue and we Overflow. The C# compiler wont even compile code that has –Int32.MinValue anywhere. I guess its smarter then me, because i would have been fine with an Int32 that is actually intuitive to use and has one less number;). Ah well I guess I am just to used to using non-overflowing Ruby numbers…

 

Technorati Tags: ,

Wednesday, June 17, 2009 #

Lately there has been a lot of buzz about functional programming, mostly because it is supposed to be the cure to all of our concurrency troubles. The answer to this from MS has been to productize F#, a functional, object oriented language, running on .Net . There have been a lot of great blog posts and articles about F# in the internet, so if you are looking for a basic introduction this is not really the place.

This series is supposed to fill in the whole i saw while searching for F# content. It seems to me that there are no real step by step guides to building anything bigger then a little script, so I will be trying to build a Connect Four game using F#. I will be concentrating on the AI part of the program but might implement some kind of GUI too if I feel like it any some is reading this:)

I will start with the basic implementation of the AI, because its not that hard to do in F#. Basically we will be using the MiniMax algorithm and we will be making it better over time. So here is mine:

let rec minMax (position:Position) depth player=
let heuristic = (runHeuristic position) * player
if depth = 0 || heuristic = Int32.MaxValue || heuristic = Int32.MinValue then heuristic
else
let
newPositions = position.NewPositions
if newPositions |> Seq.length = 0 then heuristic
else
newPositions |> Seq.map (fun pos -> -(minMax pos (depth-1) -player)) |> Seq.max


let miniMax (position:Position) depth =
let positions = position.Moves
let result = positions |> Seq.map (fun pos -> -(minMax (position.DropIn pos) (depth-1) -1),
pos)
|> Seq.maxBy fst
|> snd
result

The miniMax function takes a position, which is just a representation of the game board, and the depth it should search to. It asks the position for all possible moves from here and then calls minMax with the moves applied, returning the move with the highest score. MinMax on the other hand recurs until either the maximum depth is reached or the game is won. It switches players on every recursion so that we do not have to write two nearly identical functions.

What is really nice here is that the F# code is very close to the Pseudo Code used to describe the algorithm. If we checked for a full board in the runHeuristic function we might even be shorter. I will show you the heuristic function next time, but it does not do much more then check if someone has won the game and if not it gives a basic score for the board. We will use the score to our advantage when we switch to a better miniMax implementation.

If you have any comments, they very welcome:)

 

 


Tuesday, June 16, 2009 #

Coding would be the right answer I guess, but more about that later.
I will be starting out this blog with a totally useless post just to try out the GWB blogging software, so if you are reading this spare yourself the pain...
Other then that I will soon start with a series on F# programming and hope to continue bringing in some .Net content.

Cheers to you all and please don't flame a new blogger to much;)

 

Technorati Tags: ,,