I am trying to implement a game with multiple levels in Haskell. Each level has different but generalized game state. We can define the game state like the following-
type Player = (Float, Float) -- Player coordinates
data State a = State Player a Bool
Here, a
is the environment. In one level it is defined as String
and in another level it is defined as ([Int], [Int])
. And the Bool determines if level is complete or not.
I have two different functions implemented that uses play
from Graphics.Gloss.Interface.Pure.Game
. I can inject any of this functions to the main
and play them individually. For example-
lv1 = play window black 90 (State (0,0) "Hello" False) drawLv1 handleLv1 updateLv1
lv2 = play window black 90 (State (0,0) ([1,2,3,4], [0,0,0,0]) False) drawLv2 handleLv2 updateLv2
main :: IO()
main = lv1
-- main = lv2
However, I want to go from lv1 to lv2 somehow. I know that Haskell functions can be passed through as values and there is obviously a way to change the main function from lv1
to lv2
but I cannot figure that out.
A solution or maybe an idea on how the problem can be generalized and handled properly, would be very nice.
I decided to write how I finally solved the problem since others might face the same problem as me-
First of all, I changed the State
data to make a generalized system for better transition between different levels. So instead of current State a
data, I now have State
but with a new parameter Level
type Player = (Float, Float) -- Player coordinates
-- data State a = State Player a Bool
data State = State {
getPlayer :: Player,
getLevel :: Level,
levelCompleted :: Bool
}
data Level = Lv1 String | Lv2 ([Int], [Int])
Then I needed to fix the level specific functions accordingly. For example-
updateLv1 :: Float -> State -> State
-- If com is true then level is complete
updateLv1 _ (State player (Lv1 st) com) =
if com then State (0,0) ([1,2,3,4], [0,0,0,0]) False
else
...
...
updateLv1 time state = commonUpdater time state
Same goes for drawing and event handlers.
Finally I had to fix the common handlers-
commonUpdater time state = case getLevel state of
Lv1 _ = updateLv1 time state
Lv2 _ = updateLv2 time state
I had to implement the commonHandler
and commonDrawer
too. The process is almost the same.
Finally the play function was easy. I just had to initialize the Lv1 state and rest were to be handled by common functions-
game :: IO()
game = play window black 90 (State (0,0) (Lv2 "Hello") False) commonDrawer commonHandler commonUpdater
main :: IO()
main = game
That's all!