dimanche 24 mars 2019

Quickchecking with a typeclass constraint and reporting the generated values?

I'm trying to do a property-based test for a chess game. I have set up the following typeclass

class Monad m => HasCheck m where                                                   
    isCollision :: Coord -> m Bool                                                  

which checks if a given coordinate contains a collision or out of bounds.

Now I have a function that generates the moveset of allowed actions for a knight like the following

collisionKnightRule :: HasCheck m => Coord -> m (Set Coord)                      
collisionKnightRule =                                                            
    Set.filterM isCollision . knightMoveSet                                      


-- | Set of all moves, legal or not                                              
knightMoveSet :: Coord -> Set Coord                                              
knightMoveSet (x,y) =                                                            
    Set.fromList                                                                 
        [ (x+2,y-1),(x+2,y+1),(x-2,y-1),(x-2,y+1)                                
        , (x+1,y-2),(x+1,y+2),(x-1,y-2),(x-1,y+2)                                
        ]                                                                        



knightMoves :: HasCheck m => Coord -> m (Set Coord)                              
knightMoves pos =                                                                
    do  let moveSet =                                                            
                knightMoveSet pos                                                
        invalidMoves <- collisionKnightRule pos                                        
        return $ Set.difference moveSet invalidMoves                             


and an instance for the HasCheck class for an arbitrary coordinate

instance HasCheck Gen where                                                      
    isCollision _ =                                                              
         Quickcheck.arbitrary                                                    


and so afterwards to test this I want to ensure that the generated moveset is a proper subset of all possible moves.

knightSetProperty :: Piece.HasCheck Gen                                          
    => (Int,Int)                                                                 
    -> Gen Bool                                                                  
knightSetProperty position =                                                     
    do  moves <- Piece.knightMoves position                                      
        return $ moves `Set.isProperSubsetOf` (Piece.knightMoveSet position)

-- ... later on

it "Knight ruleset is subset" $                                          
            quickCheck knightSetProperty

Of course this fails because it could be that the knight can't move anywhere, which would mean that it's not a proper subset but the same set. However the error reported is not particularly helpful

*** Failed! Falsifiable (after 14 tests and 3 shrinks):  
(0,0)

This is because quickcheck doesn't report the generated value of isCollision. Therefore I wonder, how can I make quickCheck report the generated value of isCollision?

Aucun commentaire:

Enregistrer un commentaire