I need to write a simple snake game for my final school project but i got stuck on my last function. I guess I don't really know how to use the datastructures perfectly. The funcition doInstruction should give back True for this testcase doInstruction (Turn East) (North, [(5, 7), (5, 6)], 2) == (East, [(5, 7), (5, 6)], 2) What am I doing wrong?
My code:
data Dir = West
| North
| East
| South deriving (Eq, Show)
type Position = (Int, Int)
type Snake = (Dir, [Position], Int)
data Instruction = Move | Turn Dir deriving (Eq, Show)
isOppositeDir :: Dir -> Dir -> Bool
isOppositeDir West East = True
isOppositeDir East West = True
isOppositeDir North South = True
isOppositeDir South North = True
isOppositeDir _ _ = False
oppositeDir :: Dir -> Dir
oppositeDir West = East
oppositeDir East = West
oppositeDir North = South
oppositeDir South = North
nextPos :: Dir -> Position -> Position
nextPos x (a,b)
| x == West = (a - 1 , b)
| x == East = (a + 1 , b)
| x == North = (a , b + 1)
| x == South = (a , b - 1)
| otherwise = (a , b)
doInstruction :: Instruction -> Snake -> Snake
doInstruction ins s
| ins == Turn Dir = (oppositeDir Dir, [Position], length [Position])
The errors I get:
E:\\ELTE\2019_1_Funkcprog\hf13.hs:34:19: error:
Data constructor not in scope: Dir :: Dir
|
34 | | ins == Turn Dir = (oppositeDir Dir, [Position], length [Position])
| ^^^
E:\\ELTE\2019_1_Funkcprog\hf13.hs:34:38: error:
Data constructor not in scope: Dir :: Dir
|
34 | | ins == Turn Dir = (oppositeDir Dir, [Position], length [Position])
| ^^^
E:\\ELTE\2019_1_Funkcprog\hf13.hs:34:44: error:
Data constructor not in scope: Position :: Position
|
34 | | ins == Turn Dir = (oppositeDir Dir, [Position], length [Position])
| ^^^^^^^^
E:\\ELTE\2019_1_Funkcprog\hf13.hs:34:63: error:
Data constructor not in scope: Position
|
34 | | ins == Turn Dir = (oppositeDir Dir, [Position], length [Position])
| ^^^^^^^^
You need to use pattern matching to "unpack" the data constructor. Variables in Haskell start with a lowercase. We thus can unpack the Instruction object, and the 3-tuple that represents the snake:
doInstruction :: Instruction -> Snake -> Snake
doInstruction (Turn dir) (_, p, l) = (dir, p, l)
Related
I have a list with elements of type LocalType (defined below), I wish to modify this list in function of which subtptype the element is belonging too.
An element of type End stays in the list of type End. For an element of type Prl (End Bar End), the first End shall stay in the list, the second End shall be appended to the list.
E.g [End, (Prl s Bar ss)] -> [End, s, ss]
E.g [End, End Bar End] -> [End, End, End]
This is how I thought of implementing it,
sepTimes :: [LocalType] -> [LocalType]
sepTimes(x:[]) = [x]
sepTimes(x:xs)
| x == End = sepTimes (x:xs)
| x == (Prl s Bar ss) = do
sepTimes (s:xs)
sepTimes (ss:xs)
As the error messages states, I am unable to retrive the elements s and ss corresponding to End and End in the example of Prl (End Bar End).
app\Sequents.hs:44:17: error: Variable not in scope: s :: LocalType
|
44 | | x == (Prl s Bar ss) = do
| ^
app\Sequents.hs:44:23: error:
* Variable not in scope: ss :: LocalType
* Perhaps you meant `xs' (line 42)
|
44 | | x == (Prl s Bar ss) = do
| ^^
app\Sequents.hs:45:19: error: Variable not in scope: s :: LocalType
|
45 | sepTimes (s:xs)
| ^
app\Sequents.hs:46:19: error:
* Variable not in scope: ss :: LocalType
* Perhaps you meant `xs' (line 42)
|
46 | sepTimes (ss:xs)
| ^^
Here are the defined datatypes :
data Seperator = Bar | BackAmpersand
deriving (Show, Eq, Ord, Read)
data LocalType = End
| Prl LocalType Seperator LocalType
deriving (Eq, Ord, Read)
You're confusing equality checks with pattern matching. Intuitively, all of the following should be the same:
f :: Either Int Char -> String
f (Left i) = show i
f (Right c) = [c]
f :: Either Int Char -> String
f x = case x of
Left i -> show i
Right c -> [c]
f :: Either Int Char -> String
f x
| x==Left i = show i -- what?
| x==Right c = [c]
But actually, only the first two are equivalent, the last one doesn't work. Why? You can't match a variable out of an equality statement. That may work in some logical languages where the equality is propositional, but Haskell's == operator is simply boolean: it takes two fully known values and tells you whether or not they're exactly the same. I.e., in order to be able to write x==Left i, the variable i must already be defined.
I need to do a project in Haskell, which does a snake program, but I'm stuck the last part.
These two test case should give True, but I dont have an idea how should I do that:
doInstruction Move (North, [(5, 7), (5, 6)], 2) == (North, [(5, 8), (5, 7)], 2)
doInstruction Move (East, [(2, 1), (1, 1), (1, 0)], 3) == (East, [(3, 1), (2, 1), (1, 1)], 3)
My code until that part
data Dir = West | North | East | South deriving (Eq, Show)
type Position = (Int, Int)
type Snake = (Dir, [Position], Int)
data Instruction = Move | Turn Dir deriving (Eq, Show)
isOppositeDir :: Dir -> Dir -> Bool
isOppositeDir West East = True
isOppositeDir East West = True
isOppositeDir North South = True
isOppositeDir South North = True
isOppositeDir _ _ = False
oppositeDir :: Dir -> Dir
oppositeDir West = East
oppositeDir East = West
oppositeDir North = South
oppositeDir South = North
nextPos :: Dir -> Position -> Position
nextPos x (a,b)
| x == West = (a - 1 , b)
| x == East = (a + 1 , b)
| x == North = (a , b + 1)
| x == South = (a , b - 1)
| otherwise = (a , b)
doInstruction :: Instruction -> Snake -> Snake
doInstruction (Turn dir) (x, p, y) = if isOppositeDir dir x then (x,p,y) else (dir,p,y)
The following is in no way a complete implementation of doInstruction, but should enable you to move forward. It passes your two test cases.
Just show me the code
Okay, here's a partial implementation of doInstruction:
doInstruction :: Instruction -> Snake -> Snake
doInstruction Move (currDir, p#(x:_), l) = (currDir, take l $ nextPos currDir x : p, l)
If you load it into GHCi, it'll pass the two test cases from the OP:
> doInstruction Move (North, [(5, 7), (5, 6)], 2) == (North, [(5, 8), (5, 7)], 2)
True
> doInstruction Move (East, [(2, 1), (1, 1), (1, 0)], 3) == (East, [(3, 1), (2, 1), (1, 1)], 3)
True
It'll still crash on lots of other input because it doesn't handle e.g. Turn instructions.
Explanation
doInstruction Move handles the Move instruction exclusively. (currentDir, p#(x:_), l) pattern-matches the snake's current direction, it's positions, and it's length (I'm assuming that the last integer is the length).
The p# syntax captures the entire position into the variable p. The function also needs the head of the list, so it also uses pattern matching on the list. Here, it matches only the head and ignores the tail: (x:_). The underscore is the wildcard character that ignores the match, in this case the tail.
This pattern match is also partial, because it's not going to match an empty list. You probably don't allow snakes of zero length, but if you did, this wouldn't match.
The function uses nextPos to calculate the next position of the head of the snake. It conses (uses :) the resulting Position value onto the p list. This in itself creates a list that's too long, but then the function uses take l to take only the first l elements of that list.
Next steps
You should add more cases to the doInstruction function, e.g.
doInstruction :: Instruction -> Snake -> Snake
doInstruction Move (currDir, p#(x:_), l) = (currDir, take l $ nextPos currDir x : p, l)
doInstruction (Turn dir) snake = -- More code goes here...
I'm solving the Brigde and torch problem
in Haskell.
I wrote a function that given a state of the puzzle, as in which people have yet to cross and those who have crossed, gives back a list of all possible moves from one side to the other (moving two people forwards and one person backwards).
module DarkBridgeDT where
data Crossing = Trip [Float] [Float] Float deriving (Show)
data RoundTrip = BigTrip Crossing Crossing deriving (Show)
trip :: [Float] -> [Float] -> Float -> Crossing
trip x y z = Trip x y z
roundtrip :: Crossing -> Crossing -> RoundTrip
roundtrip x y = BigTrip x y
next :: Crossing -> [RoundTrip]
next (Trip [] _ _) = []
next (Trip (a:b:[]) s _ )
|a>b = [BigTrip (Trip [] (a:b:s) a) (Trip [] [] 0)]
|otherwise = [BigTrip (Trip [] (b:a:s) b) (Trip [] [] 0)]
next (Trip d s _) = [BigTrip (Trip [x,z] (i:j:s) j) b | i <- d, j <- d, i < j, x <- d, z <- d, x < z, z /= i, z /= j, x /= z, x /= i, x /= j, b <- (back [x,z] (i:j:s))]
where
back [] s = []
back d s = [Trip (i:d) (filter (/= i) s) i | i <- s]
Now I need a function that given a state as the one above and a maximum amount of time gives back all possible solutions to the puzzle in less than that given time.
All I have for that is this:
cross :: Crossing -> Float -> [[RoundTrip]]
cross (Trip [] _ _) _ = []
cross (Trip _ _ acu) max
| acu > max = []
cross (Trip a b acu) max = map (cross (map (crec) (next (Trip a b acu)) acu)) max
where
crec (BigTrip (Trip _ _ t1) (Trip a b t2)) acu = (Trip a b (t1+t2+acu))
Of course that doesn't compile, the 5th line is the one that's driving me insane. Any input?
Edit:
The cross function is meant to apply the next function to every result of the last nextfunction called.
If the first result of next was something like: [A,B,C,D] then it would call next on A B C and D to see if any or all of those get to a solution in less than max (A B C and D would be Crossings inside which contain the floats that are the time that ads up and is compared to max).
My data structure is
Crossing: Contains the first side of the bridge (the people in it represented by the time they take to cross the bridge) the other side of the bridge (the same as the other) and a time that represents the greatest time that last crossed the bridge (either the greatest of the two in the first crossing or the only one in the second) or the amount of time acumulated crossing the bridge (in the cross function).
RoundTrip: Represents two crossings, the first and the second, the one getting to safety and the one coming back to danger.
cross (Trip [1,2,5,10] [] 0) 16 should give an empty list for there is no solution that takes less than 17 minutes (or whatever time unit).
cross (Trip [1,2,5,10] [] 0) 17 should give the normal solution to the puzzle as a list of roundtrips.
I hope that makes it clearer.
Edit2:
I finally got it. I read Carsten's solution before I completed mine and we laid it out practically the same. He used fancier syntax and more complex structures but it's really similar:
module DarkBridgeST where
data Torch = Danger | Safety deriving (Eq,Show)
data State = State
[Float] -- people in danger
[Float] -- people safe
Torch -- torch position
Float -- remaining time
deriving (Show)
type Crossing = [Float]
classic :: State
classic = State [1,2,5,10] [] Danger 17
next :: State -> [Crossing] -- List all possible moves
next (State [] _ _ _) = [] -- Finished
next (State _ [] Safety _) = [] -- No one can come back
next (State danger _ Danger rem) = [[a,b] | a <- danger, b <- danger, a /= b, a < b, max a b <= rem]
next (State _ safe Safety rem) = [[a] | a <- safe, a <= rem]
cross :: State -> Crossing -> State -- Crosses the bridge depending on where the torch is
cross (State danger safe Danger rem) cross = State (taking cross danger) (safe ++ cross) Safety (rem - (maximum cross))
cross (State danger safe Safety rem) cross = State (danger ++ cross) (taking cross safe) Danger (rem - (maximum cross))
taking :: [Float] -> [Float] -> [Float]
taking [] d = d
taking (x:xs) d = taking xs (filter (/=x) d)
solve :: State -> [[Crossing]]
solve (State [] _ _ _) = [[]]
solve sf = do
c <- next sf
let sn = cross sf c
r <- solve sn
return (c:r)
All in all thanks everyone. I'm new to Haskell programming and this helped me understand a lot of things. I hope this post can also help someone starting haskell like me one day :)
I'm not going to leave much of your code intact here.
The first problems are with the data structures. Crossing doesn't actually represent anything to do with crossing the bridge, but the state before or after a bridge crossing. And you can't use RoundTrip because the number of bridge crossings is always odd.
I'm renaming the data structure I'm actually keeping, but I'm not keeping it unmodified.
data Bank = Danger | Safety deriving (Eq,Show)
data PuzzleState = PuzzleState
[Float] -- people still in danger
[Float] -- people on the safe bank
Bank -- current location of the torch
Float -- remaining time
type Crossing = ([Float],Bank)
Modifying/writing these functions is left as an exercise for the reader
next :: PuzzleState -> [Crossing] -- Create a list of possible crossings
applyCrossing :: PuzzleState -> Crossing -> PuzzleState -- Create the next state
Then something like this function can put it all together (assuming next returns an empty list if the remaining time is too low):
cross (PuzzleState [] _ _ _) = [[]]
cross s1 = do
c <- next s1
let s2 = applyCrossing s1 c
r <- cross s2
return $ c : r
Just for the fun, an approach using a lazy tree:
import Data.List
import Data.Tree
type Pawn = (Char, Int)
data Direction = F | B
data Turn = Turn {
_start :: [Pawn],
_end :: [Pawn],
_dir :: Direction,
_total :: Int
}
type Solution = ([String], Int)
-- generate a tree
mkTree :: [Pawn] -> Tree Turn
mkTree p = Node{ rootLabel = s, subForest = branches s }
where s = Turn p [] F 0
-- generates a node for a Turn
mkNode :: Turn -> Tree Turn
mkNode t = Node{ rootLabel = t, subForest = branches t }
-- next possible moves
branches :: Turn -> [Tree Turn]
-- complete
branches (Turn [] e d t) = []
-- moving forward
branches (Turn s e F t) = map (mkNode.turn) (next s)
where
turn n = Turn (s\\n) (e++n) B (t+time n)
time = maximum . map snd
next xs = [x| x <- mapM (const xs) [1..2], head x < head (tail x)]
-- moving backward
branches (Turn s e B t) = map (mkNode.turn) e
where
turn n = Turn (n:s) (delete n e) F (t+time n)
time (_,b) = b
solve :: Int -> Tree Turn -> [Solution]
solve limit tree = solve' [] [] limit tree
where
solve' :: [Solution] -> [String] -> Int -> Tree Turn -> [Solution]
solve' sols cur limit (Node (Turn s e d t) f)
| and [t <= limit, s == []] = sols ++ [(cur++[step],t)]
| t <= limit = concat $ map (solve' sols (cur++[step]) limit) f
| otherwise = []
where step = "[" ++ (v s) ++ "|" ++ (v e) ++ "]"
v = map fst
Then you you can get a list of solutions:
solve 16 $ mkTree [('a',2), ('b',4), ('c',8)]
=> [(["[abc|]","[c|ab]","[ac|b]","[|bac]"],14),(["[abc|]","[c|ab]","[bc|a]","[|abc]"],16),(["[abc|]","[b|ac]","[ab|c]","[|cab]"],14),(["[abc|]","[a|bc]","[ba|c]","[|cab]"],16)]
Or also generate a tree of solutions:
draw :: Int -> Tree Turn -> Tree String
draw limit (Node (Turn s e d t) f)
| t > limit = Node "Time Out" []
| s == [] = Node ("Complete: " ++ step) []
| otherwise = Node step (map (draw limit) f)
where step = "[" ++ (v s) ++ "|" ++ (v e) ++ "]" ++ " - " ++ (show t)
v = map fst
Then:
putStrLn $ drawTree $ draw 16 $ mkTree [('a',2), ('b',4), ('c',8)]
Will result in:
[abc|] - 0
|
+- [c|ab] - 4
| |
| +- [ac|b] - 6
| | |
| | `- Complete: [|bac] - 14
| |
| `- [bc|a] - 8
| |
| `- Complete: [|abc] - 16
|
+- [b|ac] - 8
| |
| +- [ab|c] - 10
| | |
| | `- Complete: [|cab] - 14
| |
| `- [cb|a] - 16
| |
| `- Time Out
|
`- [a|bc] - 8
|
+- [ba|c] - 12
| |
| `- Complete: [|cab] - 16
|
`- [ca|b] - 16
|
`- Time Out
I tried to translate a (working !) solution of the cabbage-goat-wolf puzzle from Scala to Haskell, but the code throws and error when calling head in findSolutions because the solution list is empty, so the problem seems to be somewhere in loop. findMoves seems to work fine.
import Data.Maybe(fromMaybe)
data Item = Farmer | Cabbage | Goat | Wolf deriving (Eq, Show)
type Position = ([Item], [Item])
validPos :: Position -> Bool
validPos p = valid (fst p) && valid (snd p) where
valid list = elem Farmer list || notElem Goat list ||
(notElem Cabbage list && notElem Wolf list)
findMoves :: Position -> [Position]
findMoves (left,right) = filter validPos moves where
moves | elem Farmer left = map (\item -> (delItem item left, addItem item right)) left
| otherwise = map (\item -> (addItem item left, delItem item right)) right
delItem item = filter (\i -> notElem i [item, Farmer])
addItem Farmer list = Farmer:list
addItem item list = Farmer:item:list
findSolution :: Position -> Position -> [Position]
findSolution from to = head $ loop [[from]] where
loop pps = do
(p:ps) <- pps
let moves = filter (\x -> notElem x (p:ps)) $ findMoves p
if elem to moves then return $ reverse (to:p:ps)
else loop $ map (:p:ps) moves
solve :: [Position]
solve = let all = [Farmer, Cabbage, Goat, Wolf]
in findSolution (all,[]) ([],all)
Of course I would also appreciate hints concerning improvements not related to the actual error.
[Update]
Just for the record, I followed the suggestion to use a Set. Here is the working code:
import Data.Set
data Item = Farmer | Cabbage | Goat | Wolf deriving (Eq, Ord, Show)
type Position = (Set Item, Set Item)
validPos :: Position -> Bool
validPos p = valid (fst p) && valid (snd p) where
valid set = or [Farmer `member` set, Goat `notMember` set,
Cabbage `notMember` set && Wolf `notMember` set]
findMoves :: Position -> [Position]
findMoves (left,right) = elems $ Data.Set.filter validPos moves where
moves | Farmer `member` left = Data.Set.map (move delItem addItem) left
| otherwise = Data.Set.map (move addItem delItem) right
move f1 f2 item = (f1 item left, f2 item right)
delItem item = delete Farmer . delete item
addItem item = insert Farmer . insert item
findSolution :: Position -> Position -> [Position]
findSolution from to = head $ loop [[from]] where
loop pps = do
ps <- pps
let moves = Prelude.filter (\x -> notElem x ps) $ findMoves $ head ps
if to `elem` moves then return $ reverse $ to:ps
else loop $ fmap (:ps) moves
solve :: [Position]
solve = let all = fromList [Farmer, Cabbage, Goat, Wolf]
in findSolution (all, empty) (empty, all)
The call to head in findSolution could be made safer, and a better way to print out the solution should be used, but apart from that I'm quite happy with it.
[Update 2]
I think the former representations of the positions were suboptimal for this kind of problem. I switched to the following data model, which made moving etc slightly more verbose, but much more readable:
data Place = Here | There deriving (Eq, Show)
data Pos = Pos { cabbage :: Place
, goat :: Place
, wolf :: Place
, farmer :: Place
} deriving (Eq, Show)
The problem is that [Farmer,Goat,Cabbage,Wolf] is not the same that [Farmer,Cabbage,Goat,Wolf] and you don't check it when use elem and notElem. One solution is always sort the list of elements, e.g in the function findMoves you can use:
import Data.List(ord)
import Control.Arrow((***))
data Item = Farmer | Cabbage | Goat | Wolf deriving (Eq, Show, Ord)
findMoves (left,right) = map (sort***sort) $ filter validPos moves where
-- ....
solve = let all = sort [Farmer, Cabbage, Goat, Wolf]
-- ....
Or you can use a set of Item instead a list of Item.
I'm doing some homework and while I have some experience with SML, Haskell has some oddities. Consider this simple function:
type Pos = (Int, Int)
data Move = North | South | East | West
move :: Move -> Pos -> Pos
move North (x,y) = (x, y+1)
move South (x,y) = (x, y-1)
move East (x,y) = (x+1, y)
move West (x,y) = (x-1, y)
moves :: [Move] -> Pos -> Pos
moves (m:ms) (x,y) = moves ms (move m (x,y))
moves [] p = p
This code works. However, if I swap out the (x,y) tuple (which I dont use anyway) with a simple p it fails on invocation (the declaration works fine of course):
moves :: [Move] -> Pos -> Pos
moves (m:ms) p = moves ms (move m p)
moves [] p = p
*Main> let p = (1,1) :: Pos
*Main> move [North, North] p
<interactive>:1:5:
Couldn't match expected type `Move' against inferred type `[a]'
In the first argument of `move', namely `[North, North]'
In the expression: move [North, North] p
In the definition of `it': it = move [North, North] p
Which seems strange to me, as the second parameter is already typed as a Pos in the definition, so how come this chokes, and only on invocation? I'm using ghci btw.
You did forget an "s" at the end of moves call, didn't you?
*Main> move [North, North] p