I want to write a function that takes a chess figure with a position and tell me which moves this figure can make with his next move.
For the king I was able to do this.
type Position = (Int,Int)
data Piece = Piece {
_position :: Position,
_color :: String}
nextMoves :: Piece -> [Position]
nextMoves king = filter (onTheBoard) (filter (/= (xOld,yOld))
[(x,y) | x <- [(xOld-1)..(xOld+1)], y <- [(yOld-1)..(yOld+1)]])
where
xOld = fst(_position king)
yOld = snd(_position king)
He stands on (1,3) and my function gives me [(1,2),(1,4),(2,2),(2,3),(2,4)].
Now I want to do the same for a bishop or a knight. But how can I use filter for this issue? Thanks for a tipp.
//edit: I've changed my check to a help function:
onTheBoard :: Position -> Bool
onTheBoard (x,y) = elem x [1..8] && elem y [1..8]
You have no way to tell which piece (king, bishop, knight, etc.) a Piece
is. nextMoves king
matches all pieces, regardless of which type it is. To figure out the moves for other pieces, you'll need to be able to discriminate between them. You'll need something like
data PieceType = Pawn | Knight | Bishop | Rook | Queen | King
data Piece = Piece {
_position :: Position,
_color :: String,
_pieceType :: PieceType}
Then you can write more cases for nextMoves
nextMoves :: Piece -> [Position]
nextMoves piece =
case _pieceType piece of
King -> filter (<= (1,1)) (filter (>= (8,8)) (filter (/= (xOld,yOld))
[(x,y) | x <- [(xOld-1)..(xOld+1)], y <- [(yOld-1)..yOld+1)]]))(yOld+1)]]))
Queen -> ...
Rook -> ...
Bishop -> ...
Knight -> ...
Pawn -> ...
where
xOld = fst(_position piece)
yOld = snd(_position piece)
You could extract the filtering that the move must be on the board from the rest of the rules.
If you are aiming for the actual game of chess, there are other rules for whether a move is valid that depend on the rest of the state of the board.
You also have a problem with the filtering, as pointed out by Priyatham. The Ord
instance for typles (,)
first compares the first element of the tuple, then compares the second one. That means, for instance, that (1, 9) < (2, 0)
. This is called a lexographic ordering, like how entries are alphabetized: first by their first letter, then by their second, etc...
You need to check that each component is individually in the range, which is easily done with Data.Ix
's inRange
and the fst
and snd
functions, for example:
filter (inRange (1,8) . fst) . filter (inRange (1,8) . snd) . filter (/= (8,8)) $ [(7,9), 9,7), (7,7), (8, 8)]
The Ix
instance for tuples (,)
checks that both components are inRange
, so this is equivalent to
filter (inRange ((1,1), (8,8))) . filter (/= (8,8)) $ [(7,9), (9,7), (7,7), (8, 8)]