I'm writing a Sokoban program in Haskell using the Gloss library, and I'm reaching the point where I'd like to, when the player beats a level, load in a new level from a text file and have the program continue on.
I'm having a little bit of difficulty with this because of Gloss's limitations -- the play
function to set up a game and have it continuously update looks like this:
play :: forall world
. Display -- ^ Display mode.
-> Color -- ^ Background color.
-> Int -- ^ Number of simulation steps to take for each second of real time.
-> world -- ^ The initial world.
-> (world -> Picture) -- ^ A function to convert the world a picture.
-> (Event -> world -> world) -- ^ A function to handle input events.
-> (Float -> world -> world) -- ^ A function to step the world one iteration.
-- It is passed the period of time (in seconds) needing to be advanced.
-> IO ()
(Copied directly from http://hackage.haskell.org/packages/archive/gloss/1.7.4.1/doc/html/Graphics-Gloss-Interface-Pure-Game.html)
Here's the world
type I'm using:
data Game = Game
{ levelNumber :: Int,
currentLevel :: Level Square,
won :: Bool }
where Level
s contain the blocks in the current level. I'm reading in Game
s using something like this (haven't actually made a generalized one yet, but this is essentially all it would be with a filename argument):
startGame = do
lvl <- readFile "levels/level001.lvl"
let lvl' = parseLevel lvl
return $ Game 1 lvl' False
So, my difficulty is arising because of the update functions in play
. I can easily take a Game
and produce a Picture
(and a Game
, etc) without having to read any data in from the file system if I'm just operating on a single level, but since I'm loading levels from files during the middle of the game, I don't know how to avoid making all of my Game
s IO Game
s. Maybe this isn't possible in this circumstance, and maybe that's for a good reason? I will always be operating on a Game
pulled from a file but I don't know if it's avoidable at any given point, and if it is, I'd like to avoid it.
So, What is an IO Monad? IO Monad is simply a Monad which: Allows you to safely manipulate effects. Transform the effects into data and further manipulate it before it actually gets evaluated.
The I/O monad contains primitives which build composite actions, a process similar to joining statements in sequential order using `;' in other languages. Thus the monad serves as the glue which binds together the actions in a program.
The functional language Haskell eliminates side effects such as I/O and other stateful computations by replacing them with monadic actions. Functional languages such as Standard ML, Scheme and Scala do not restrict side effects, but it is customary for programmers to avoid them.
IO is the way how Haskell differentiates between code that is referentially transparent and code that is not. IO a is the type of an IO action that returns an a . You can think of an IO action as a piece of code with some effect on the real world that waits to get executed.
I ended up using Gloss's playIO
from Graphics.Gloss.Interface.IO.Game. I needed to change my a couple of my functions to operate on pure data types and output them wrapped in the IO
monad. Here are the types I had to change:
worldToPicture :: World -> IO Picture
eventHandler :: Event -> World -> IO Picture
stepWorld :: Float -> World -> IO World
For the most part, this only resulted in adding some return
s to my currently existing functions, but it ended up adding a LOT of functionality (like saving on the fly, loading new levels, using BMP files for graphics, etc). I was also able to keep almost all of my currently existing code free from IO
since the new functions still took pure data types as parameters. It ended up being a really easy refactor and solved my problem perfectly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With