Skip to content

Instantly share code, notes, and snippets.

@dom96
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dom96/3c5505f28260c4f20b71 to your computer and use it in GitHub Desktop.
Save dom96/3c5505f28260c4f20b71 to your computer and use it in GitHub Desktop.
import strutils
import lib
type
UpperScene = object of Scene
LowerScene = object of Scene
proc newUpperScene(): ref UpperScene =
result = new UpperScene
result.prompt = "U:"
proc newLowerScene(): ref LowerScene =
result = new LowerScene
result.prompt = "l:"
method enter(self: ref UpperScene) =
echo("UpperScene needed initialization!")
method leave(self: ref LowerScene) =
echo("LowerScene needed cleanup!")
method update(self: ref UpperScene, input: string): ref Scene =
if input == "":
return newLowerScene()
echo("UPPER: " & input.toUpper)
method update(self: ref LowerScene, input: string): ref Scene =
if input == "":
return newUpperScene()
echo("lower: " & input.toLower)
# initialize a scene
var lowerScene = newLowerScene()
# initialize app with scene
var app = newApp(lowerScene)
# fall into app main loop
app.run()
# this module implements a simple "framework".
# In this example, the framework supports the implementation of
# user programs that implement a basic input-output loop
# However, the system is implemented as a State Machine
# On each loop, the user input is passed to the *current*
# "Scene" object. There can only be one active Scene. Scene's have
# the capability to set the system to a different Scene thereby
# changing the behavior of the program.
# The base Scene class implements a simple interface of 4 methods:
# `enter` for initialization upon entering the Scene
# `update` for Scene specific behavior
# `leave` for cleanup upon leaving the Scene
# While the example usecase is odd, the metaphor is really for
# game engines. Each Scene might implement some "screen" in a
# game such as the main menu, the game play, a pause screen,
# an options menu, the highscore listing.
# Each Scene is essentially a completely different "program"
# that the overall application switches between. A menu screen
# might be implemented very differently than the gameplay screen.
# It would probably do different things during the frame update
# and draw differently too.
# The main reasoning behind the inheritance based implementation
# of this system is so that user's of this library can implement
# their Scene's by inheriting the base and implementing just the
# parts of the Scene lifetime that they need to overload.
# If the framework instead expected the user to provide types
# that simply implemented a "Scene interface" to adhere to some
# expected typeclass, then the user utilizing the library would
# be required to implement every proc prescribed by the interface
# whether any particular Scene actually needed to overload every
# method in the interface or not.
# In the example below, there are two "Scenes". One that converts
# all user input to UPPERCASE and the other to lowercase. You can
# see that each Scene has its own state (prompt) and they both
# implement `update` differently.
# Lastly, the UpperCase Scene implements `enter` to simulate the
# need for initialization and vice versa for LowerCase. But
# neither do, or are required, to implement all Scene methods.
import rdstdin
type
Scene* = object of RootObj
app*: ref App
prompt*: string
App* = object
scene*: ref Scene
closing*: bool
method enter*(self: ref Scene) =
discard
method leave*(self: ref Scene) =
discard
method update*(self: ref Scene, input: string): ref Scene =
discard
method set_scene*(self: ref App, scene: ref Scene) =
# leave old scene
self.scene.leave()
# enter new scene
self.scene = scene
self.scene.app = self
self.scene.enter()
proc newApp*(first_scene: ref Scene): ref App =
result = new App
result.closing = false
result.set_scene(first_scene)
proc run*(self: ref App) =
while not self.closing:
let str = readLineFromStdin(self.scene.prompt)
var new_scene = self.scene.update(str)
if new_scene != nil:
self.set_scene(new_scene)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment