Kotlin 21 - Scenarios
Now that the game can be played, more or less, it’s time to think of some scenarios that we need our DSL to support.
Let’s remember our purpose here. This is an initial Kotlin learning exercise, in which I wanted to focus on the DSL / builder idea, which was particularly new to me. After some general thrashing about, I picked the idea of building a simple text game: enter a command; get a text response back. The idea is to specify the game in the DSL / builder style.
We now have the loop closed: we have a tiny game and you can type commands into a field in the game window, and the game’s response appears in a text pane above. In principle, we could stop now and move on to something different, perhaps something graphical like Spacewar! or Asteroids. But when it comes to learning about DSLs, we’ve just scratched the surface. I think there’s more for me to learn by extending the game’s capability.
My purpose this morning is to think of some things we’d like the game to do, scenarios if you will, and to speculate a bit on how they might be described in our DSL and implemented in the game. I don’t plan to trust my speculation very much, just enough to decide what would be interesting to try.
My very tentative plan will be to flesh out the DSL more rapidly than the implementation, and to build up a small world that expresses the chosen scenarios, even if they are not yet fully implemented. Then the growing game will serve as a growing specification.
Along the way, I’d like to be able to test each new bit with executable tests, not just by playing the game.
Anyway let’s get started.
Possible Scenarios
- Items in Rooms / Inventory
-
We’ve touched on there being items in the rooms, and speculated about an
items
token in the DSL to specify them. There will need to be a command liketake keys
to get the item. When that command is done, the item needs to leave the room and enter the player’s “Inventory”. - Must Have Something
- There is a locked grating blocking the entrance. The command
open grating
will work, only if you have the keys. If you do not have them, the game responds1 “The grating is locked”. If you do, the game responds “You unlock the grating and swing it aside”. -
The DSL will need some way to specify a command that might occur, like
open grate
, the condition that must hold for the command, and both a “true” and “false” branch as to what happens. What happens may include textual output, but will also need to change the state of the room and game. This may become more clear as we consider further scenarios.
- Must Do Something
- “A huge ferocious snake blocks your way to the west!”
Free bird
“In an astounding flurry, the bird drives the snake away.” The snake might be no more than an item with a florid description. Freeing the bird might just remove that item from the room. -
“A deep and frightening chasm blocks your path to the west.”
Wave rod
“A beautiful crystal bridge has appeared, spanning the chasm.” Here, the action has made the commandwest
work, where it would not have worked before. Thego
command must have the ability to specify a condition and check it. In this case the bridge might be an item in the room. But note that coming back the other way, you’ll be in a different room, and the bridge must appear there as well. - An Event May Occur
- There are dwarves in the cave. (Why is it always dwarves?) In certain rooms there is a chance that you will encounter a dwarf. The first time you encounter a dwarf, he throws an axe at you and flees. The axe remains in the room. The game enters a new state where dwarves may attack frequently. There are N of them. When they attack, if you
throw axe
at them, they go away or die or something. If you don’t throw the axe at them, they’ll kill you or knock you out and steal your stuff. Something bad. After N dwarves have been driven away, attacks stop. -
It seems there must be rooms where dwarves are enabled, and rooms where they are not. There need to be at least three dwarf state: never seen, seen one, seen them all. Entry into a dwarf-enabled room allows the dwarf logic to run. What would be truly nifty would be if the DSL could specify all this behavior with little or no custom programming. There might need to be a general entity notion, but it would be ideal if there was no dwarf-specific code in the game, only DSL.
- Specialized Vocabulary
- Perhaps there is a dog in the cave. The DSL specifying the dog’s behavior might want to create new commands
pet dog
,feed dog
kick dog
, and so on, with appropriate responses. (If you kick the dog, John Wick appears and shoots you forty-eleven times and stabs you a couple more.) -
Ideally this could all be done in the DSL.
- An Important Observation
- Remember that the DSL isn’t a language that we are parsing and writing a compiler for. It is just Kotlin. This suggests that specialized things, perhaps most things, can be specified by providing Kotlin syntax for doing them. This is, I would argue, not cheating. This is part of the power of a DSL written directly as executable code in the language of the system. It multiplies your capability while not taking anything away.
- Things Not To Be Done
- There might be tempting items in the game that are supposed to be left alone. If you completely ignore the spider, you’ll get points at the end of the game. If you do anything at all with it, even look at it, you won’t. This is a particularly nasty trick in the game, by the way. No one ever thinks things in the game are to be ignored. They always tamper with them. It is best not to be in the same room with a game user when you reveal that they were supposed to leave the spider alone and that’s why they only got 349 out of 350 points.
- Pirate
- In the real ADVENTure game, there was a pirate who stole your stuff. Very deep in the game, you could some across him. I don’t recall whether you had to fight him or what, but once you got past him, all your stuff was there and you could get it again.
- Variable Responses
- For many of the things you encounter, there may be variations on standard messages. If you go south from the well house, perhaps the game says “After wandering southward, you find yourself in a clearing”, but when you enter the clearing from the western cornfield, you get the standard clearing description. The responses might be in addition to the standard room response, or override it.
-
This facility may be useful for errors or conditions that vary from room to room:
-
Wave
“Who are you waving at, you tiny fool?”
Wave
“The shadowy figure waves back.”
XYZZY
“Nothing happens here.”
Summary
We may come up with scenarios that are somehow different from these, but these will surely be enough to be challenging. At this point, I’ll mumble some ideas for how these things might be done, and then go see about Sunday breakfast.
It seems clear that a “room” needs some kind of inventory of things that are there, mostly represented by their key word, “keys” or “axe” or whatever. A thing might have a long and short description. “There are some keys here.” “There is a ring of keys here, glowing with a eerie light. One of the keys has the shape of a bat. There are mysterious runes on the bat … saying ‘Babe Ruth’.”
While the default response to Take thing
might just be “Taken”, some things can have special responses, such as the complex behavior around capturing the bird. “You can get the bird but how will you keep it?” “You put the bird in the gilded cage.”
Because we would like to have as much as possible specified in the DSL, but to have things done in a standard fashion, for ease of creating scenarios, there may be standard places and access for saving game information. Perhaps each room has a contents variable and a dictionary of details. Perhaps there are conventional notions for long and short descriptions, or for messages that appear only on certain paths.
Because I know my own methods, at least sometimes, I anticipate that what we’ll do is pick a story, from the story decide what game entities need to be involved, implement some fairly ad hoc solution, and then, when we’ve done a few similar things, extract out general capabilities to make the job easier. The alternative would be to try to design all the capabilities first. While I’m sure we’re perfectly capable of doing that, I’m also sure that we’d invent features we would never use, while missing out valuable capabilities whose lack make the DSL writer’s job three times harder owing to the necessity of jumping through their own orifices to get something done.
Incrementally. That’s how we do things here chez Ron2, because it usually works out just fine and we learn the limits of creating very simple solutions and then growing them into larger solutions as needed.
Tomorrow, we’ll pick a story or two and get started laying out the DSL, and making it work.
Should be fun. I hope you’ll come along.