Reputation: 21
I get the feeling I'm fundamentally misunderstanding something about the way in which Haskell is written. My code is intended to serve as an evaluation function for a primitive AI for the game Othello. Essentially I want my code to cycle through a list of pieces, each piece represented by a tuple composed of a Position (Int, Int) and a Col (Colour, either Black or White), and given a prospective move identify how good the move is.
The way I'm judging the value of a move is based on a few factors:
Since Haskell has no loop structures it seems like I need to implement this recursively, as such my code is as follows:
eval :: Position -> [(Position, Col)] -> Col -> GameState -> Int -> Int
eval move pieces moveColour gameState score = do
let moveX = fst move
let moveY = snd move
let piece = head(pieces)
let pieceColour = snd piece
let pieceX = fst fst piece
let pieceY = snd fst piece
if (moveColour == pieceColour) then
if (moveX == pieceX) then
if (moveY > pieceY) then
let newScore = score + (countOtherColour 0 moveY pieceY (pieces gameState) moveColour 0)
--recurse
if (tail(pieces) == []) then
return newScore
else
return eval move tail(pieces) moveColour gameState newScore
else
let newScore = score + (countOtherColour 0 pieceY moveY (pieces gameState) moveColour 0)
--recurse
if (tail(pieces) == []) then
return newScore
else
return eval move tail(pieces) moveColour gameState newScore
else
if (moveY == pieceY) then
if (moveX > pieceX) then
let newScore = score + (countOtherColour 1 moveX pieceX (pieces gameState) moveColour 0)
--recurse
if (tail(pieces) == []) then
return newScore
else
return eval move tail(pieces) moveColour gameState newScore
else
let newScore = score + (countOtherColour 1 pieceX moveX (pieces gameState) moveColour 0)
--recurse
if (tail(pieces) == []) then
return newScore
else
return eval move tail(pieces) moveColour gameState newScore
else
--recurse
if (tail(pieces) == []) then
return score
else
return eval move tail(pieces) moveColour gameState score
else
--recurse
if (tail(pieces) == []) then
return score
else
return eval move tail(pieces) moveColour gameState score
countOtherColour :: Int -> Int -> Int -> [(Position, Col)] -> Col -> Int -> Int
countOtherColour xyFlag upper lower pieces turnColour score = do
--if xyFlag == 0 it's y aligned if 1 it's x aligned
let piece = head(pieces)
let pieceColour = other (snd piece)
let x = fst fst piece
let y = snd fst piece
if (pieceColour == turnColour) then
if (xyFlag == 0) then
if (upper > x && x > lower) then
let newScore = score+1
--recurse
if (tail(pieces) == []) then
return newScore
else
return countOtherColour xyFlag upper lower tail(pieces) turnColour newScore
else
--recurse
if (tail(pieces) == []) then
return score
else
return countOtherColour xyFlag upper lower tail(pieces) turnColour score
else
if (upper > y && y > lower) then
let newScore = score+1
--recurse
if (tail(pieces) == []) then
return newScore
else
return countOtherColour xyFlag upper lower tail(pieces) turnColour newScore
else
--recurse
if (tail(pieces) == []) then
return score
else
return countOtherColour xyFlag upper lower tail(pieces) turnColour score
else
--recurse
if (tail(pieces) == []) then
return score
else
return countOtherColour xyFlag upper lower tail(pieces) turnColour score
However, this code does not compile. I get a "parse error on "if"" on the first line that reads:
if (tail(pieces) == []) then
This leads me to believe that something fundamental about the way in which I've structured this code is faulty. I would like to clarify I'm NOT looking for someone to solve the implementation for me, only for someone to explain to me how my implementation is flawed, and a general guide on how I can implement recursion within the correct way to structure my code.
If you've read this far thank you, and I look forward to reading your replies.
Upvotes: 2
Views: 94
Reputation: 80744
When let
is outside a do
, it requires an in
, for example:
-- correct
x =
let a = 5
in a + 37
-- incorrect
x =
let a = 5
a + 37
So when your if
comes right after a let
, there should be an in
right in front of it:
let newScore = score+1
-- recurse
in if (tail(pieces) == []) then
...
Besides this, there are quite a few other things in your code that could be cleaner. I won't go through all of them, will just give a few examples.
One example is that multiple let
bindings in a row don't need a separate let
for each:
x =
let a = 5
b = 37
in a + b
Similarly, the condition of if
doesn't need parens, and neither do function calls:
if tail pieces == [] then
And while we're at it: test for empty list via the null
function instead of comparing == []
:
if null (tail pieces) then
Upvotes: 4