söndag 15 mars 2009

Matchgame implementation #2: make a move

The game to implement is
Given four rows of matches:
|||||||
|||||
|||
|
Two players take turns removing any number of matches from ONE single row. The one to remove the last match looses.

Ok, so the next easy step in learning F#, focused on this game, was to define how to represent a move, and how to apply it on the state of the game.

I decided for a tuple to represent a move the first member the index to the row to remove matches from, the second member the number of matches to remove.
So the move "remove three matches from the first row" becomes:
let move=(0,3)


So how to turn this into action?
The code I came up with along with some lines to test it:


let ValidMove state (removeAtIndex, removeNumber) =
removeAtIndex >= 0 &&
removeAtIndex < List.length state &&
removeNumber > 0 &&
removeNumber <= List.nth state removeAtIndex

let MakeMove state (removeAtIndex, removeNumber) =
if ValidMove state (removeAtIndex, removeNumber) = false then failwith (sprintf "%A is not a valid move on %A" (removeAtIndex, removeNumber) state)
List.mapi (fun i numberOfMatches -> if removeAtIndex = i then numberOfMatches - removeNumber else numberOfMatches) state

let state = [7;5;3;1]

let validMove=(1,4)
let invalidMove = (0,8)

printf "The move %A applied to %A becomes %A\n" validMove state (MakeMove state validMove)
printf "The move %A applied to %A becomes %A\n" invalidMove state (MakeMove state invalidMove)


So what does it do?

The first function

let ValidMove state (removeAtIndex, removeNumber) =
removeAtIndex >= 0 &&
removeAtIndex < List.length state &&
removeNumber > 0 &&
removeNumber <= List.nth state removeAtIndex

simply verifies that the move is valid to perform on the state. It verifies that the row (index) is within the list, and that the number of matches to remove is at least one, but no more than the number of available matches in the given row.

The second function

let MakeMove state (removeAtIndex, removeNumber) =
if ValidMove state (removeAtIndex, removeNumber) = false then failwith (sprintf "%A is not a valid move on %A" (removeAtIndex, removeNumber) state)
List.mapi (fun i numberOfMatches -> if removeAtIndex = i then numberOfMatches - removeNumber else numberOfMatches) state

first calls ValidMove to verify that it is a valid move (really?), and fails (throws exception) if not.
It then performs a function on all elements in the state to generate the new state. List.mapi, in addition to List.map, also provides the index the function is called on. So the function takes the row number (index), and the number of matches in that row. If it is the row to remove at, it returns the number minus the number to remove. Otherwise it just returns the original number of matches.

Executing the provided code should not give any surprises. Except perhaps that last row seems to execute before the next last, because the exception printout comes before the valid printout.

Inga kommentarer:

Skicka en kommentar