Search code examples
haskellchess

Haskell - Chess - which next moves are possible?


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]

Solution

  • 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.

    Filtering

    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)]