Reputation: 3260
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.
Upvotes: 6
Views: 950
Reputation: 3260
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.
Upvotes: 3