Making a Map in Gloss - haskell

In PL class we were asked to make a pacman clone in Gloss, nevertheless I got stuck at making the map.
My approach was taking a .png of the classical pacman first level, and paste it in the render function so I don't need to draw everything by hand.
Nevertheless by doing so, the games lags horribly, I'm assuming that it's because the game renders the map in every step.
Is there some way to just render the map as a background a single time, or avoid the huge lag? Or am I taking a bad approach and it would it be better if I draw the components by hand using the Picture module?
I append the render function just in case I'm wiring it badly:
render :: PacmanGame -> IO Picture
render game = do
sprite <- fmap (head) (playerSprites $ player game)
let playerOne = uncurry translate (playerPos $ player game) $ sprite
map' <- pacmanMap
return $ pictures [map', playerOne]
Where pacmanMap :: IO Picture

It looks like you’re reloading the file in every call to render. You need to run the pacmanMap :: IO Picture action once, for example at startup in main; then you can just return the resulting static Picture from your render function. For example, you might store the reference to the current background image in the PacmanGame, or pass it as an additional argument to render.

Related

Can I have separate functions for reading and writing to a txt file in Haskell, without using a 'main' function?

I'm making a program using Haskell that requires simple save and load functions. When I call the save function, I need to put a string into a text file. When I call load, I need to pull the string out of the text file.
I'm aware of the complexities surrounding IO in Haskell. From some reading around online I have discovered that it is possible through a 'main' function. However, I seem to only be able to implement either save, or load... not both.
For example, I have the following function at the moment for reading from the file.
main = do
contents <- readFile "Test.txt"
putStrLn contents
How can I also implement a write function? Does it have to be within the same function? Or can I separate it? Also, is there a way of me being able to name the functions load/save? Having to call 'main' when I actually want to call 'load' or 'save' is rather annoying.
I can't find any examples online of someone implementing both, and any implementations I've found of either always go through a main function.
Any advice will be greatly appreciated.
I'm aware of the complexities surrounding IO in Haskell.
It's actually not that complex. It might seem a little intimidating at first but you'll quickly get the hang of it.
How can I also implement a write function?
The same way
Or can I separate it?
Yes
Also, is there a way of me being able to name the functions load/save?
Yes, for example you could do your loading like this:
load :: IO String
load = readFile "Test.txt"
All Haskell programs start inside main, but they don't have to stay there, so you can use it like this:
main :: IO ()
main = do
contents <- load -- notice we're using the thing we just defined above
putStrLn contents
Note the main is always what your program does; But your main doesn't only have to do a single thing. It could just as well do many things, including for instance reading a value and then deciding what to do; Here's a more complicated (complete) example - I expect you'll not understand all parts of it right off the bat, but it at least should give you something to play around with:
data Choice = Save | Load
pickSaveOrLoad :: IO Choice
pickSaveOrLoad = do
putStr "Do you want to save or load? "
answer <- getLine
case answer of
"save" -> return Save
"load" -> return Load
_ -> do
putStrLn "Invalid choice (must pick 'save' or 'load')"
pickSaveOrLoad
save :: IO ()
save = do
putStrLn "You picked save"
putStrLn "<put your saving stuff here>"
load :: IO ()
load = do
putStrLn "You picked load"
putStrLn "<put your loading stuff here>"
main :: IO ()
main = do
choice <- pickSaveOrLoad
case choice of
Save -> save
Load -> load
Of course it's a bit odd to want to do either save or load, most programs that can do these things want to do both, but I don't know what exactly you're going for so I kept it generic.

Haskell Gloss : Making zoom, pan, and all that kind of effects for module Graphics.Gloss.Interface.Pure.Game

Recently I have been working on making a game with Haskell, and we have been using the library Gloss in order to complete the graphic part. To make a game with Gloss, as you probably know, you have to use Graphics.Gloss.Interface.Pure.Game (or IO game, that's not the main thing to focus here). My doubt is simple. As they say on their description of this module : "Pressing ESC will still abort the program, but you don't get automatic pan and zoom controls like with displayInWindow.". I have been trying to build those two effects but with no sucess. I made an accumulator in my state, called v, that gets the value of 1 when 'reactEvent (EventKey (MouseButton WheelUp) Down _ _) ((px,py),p,mapas,i,players,b,c,d,v) = ((px,py),p,mapas,i,players,b,c,d,1)' and then the function that makes the picture appear would turn it back to 0 after applying the effect needed, creating a cicle. What's the mistake in my logic?
Edit : Added the prints, that's what I am trying to get to work. Also, if anyone has a clue on how to make different camera angles, I would also appreciate some help.
https://imgur.com/3PAqO2x
https://imgur.com/jk93lzQ

Haskell multiple actions within pattern match

For a game I want to use animations, for that I want to pick a picture from a list, and then go to the next picture. However, for that I would need to do two things within one function which is not possible I think.
getPicture :: [IO Picture] -> Int -> IO Picture
getPicture a i | i < length a = (!!) i a && getPicture a (i+1)
| otherwise = (!!) i a && getPicture a (0)
Obviously I cannot use && to proceed to the next part, but I was wondering if there was a possibility to do this?
Seeing as you're always recursively calling getPicture, it looks to me like you just want all the pictures in order. You also have a base case which goes back to the first picture. Don't you think getPictures :: [IO Picture] -> IO [Picture] looks like a more suitable signature? It would just get all pictures in a sequence, no need to manually keep track of i.
Luckily there's sequence from Control.Monad which can do exactly this for you. We'll throw in a cycle which accommodates the "go back to the beginning" part.
do
pictures <- cycle <$> sequence getPictures
… -- animate the pictures as you wish
Of course I'm just guessing what you might actually want. For example, this will keep all the pictures (frames?) loaded in memory.

function for getting window width and height with haskell gloss

Is there a function, similar to JS screen.width and screen.height in Haskell with the gloss graphic library, which returns the screen width and height?
There is the function is in Graphics.Gloss.Interface.Environment called getScreenSize it return a IO (Int,Int) the first value is width, the second is height.
I don't think gloss itself exports this capability, but you can use these calls from the GLFW package to determine the screen resolution:
import Graphics.UI.GLFW
main = do
initialize
desktopMode >>= print
putStrLn "all video modes:"
videoModes >>= mapM_ print
Note that gloss may be compiled to use either GLUT or GLFW. If gloss uses GLFW as its interface to Open GL it will call GLFW's initialize function when you create a window, and it's possible there is an issue with calling initialize twice in the same process but I kinda doubt it.
You can then use these dimensions to set the drawable area when creating a gloss window with the FullScreen constructor.

How to handle runtime errors in Haskell?

I'm trying to learn Haskell by writing a simple console chess game. It displays a chess board and gets moves from standard input in SAN notation. Here's what I've got so far:
import System.IO
import Chess
import Chess.FEN
main = do
putStrLn "Welcome to Console Chess!"
putStrLn $ show defaultBoard
gameLoop defaultBoard
gameLoop board = do
putStr "Your move: "
hFlush stdout
move <- getLine
let newBoard = moveSAN move board
case newBoard of
Left _ -> do
putStrLn "Invalid move, try again..."
gameLoop board
Right b -> do
putStrLn $ show b
gameLoop b
The problem is that the call to the moveSAN function sometimes crashes the program, losing all progress made in the game. For instance, pressing Enter at the "Your move:" prompt produces:
Your move:
cchess: Prelude.head: empty list
Otherwise, entering a two-digit number produces:
Your move: 11
cchess: Error in array index
Entering two periods gives:
Your move: ..
cchess: Char.digitToInt: not a digit '.'
I would like to catch these errors and inform the user that the move they have entered is not in valid SAN notation. How can I do that?
In an ideal world, you would avoid functions like head and not have runtime errors at all. Runtime errors are fundamentally awkward, especially in Haskell.
In this case, you want to catch the runtime error. If you're content to turn it into a Maybe, you can use the spoon package.
Exceptions in Haskell are subtle thanks to laziness. Particularly, if you don't evaluate the part of the structure that has the exception, it won't fire. This is handled in spoon with two different functions:
spoon, which evaluates a structure deeply but requires an instance of a special typeclass
teaspoon which only evaluates your structure part of the way to weak head normal form
For your case, I think teaspoon should be fine. Try checking the result of the parse with:
teaspoon (moveSAN move board)
which should give you a Maybe value.
If that doesn't work, it means you need to evaluate more of the structure to hit the exception. It looks like Board does not implement the typeclass needed to deeply evaluate it with spoon, so your best bet is a bit hacky: use spoon on the result of show:
spoon (show $ moveSAN move board)
This will give you a Maybe String. If it's Just, the move parsed correctly; if it's Nothing, there was an error.
It's worth noting that the spoon package doesn't really do much: each function it has is only a couple of lines. At some point, it's worth figuring out how to handle exceptions in Haskell yourself, but for now spoon is just a bit more convenient and should work properly for you.

Resources