99 Bottles of Beer (Inform 7)
From LiteratePrograms
By shifting phase, mid-verse to mid-verse, a typical repeat of the 99 bottles of beer song is:
Take one down and pass it around, 98 bottles of beer on the wall. 98 bottles of beer on the wall, 98 bottles of beer.
Contents[hide] |
theory
We implement "99 bottles" recursively, using the phrase precedence rules of Inform 7 (in case of ambiguity, the most specific match is chosen) to avoid any explicit alternation (if-thens) in the code.
practice
all in all, you're just another bottle on the wall
Inform 7 has a relatively sophisticated condition matching system, but it seems to only work for objects, not numeric values. Therefore we create a wall class as a thin wrapper around the variable (inventory) we use to keep track of the number of bottles.
<<wall definition>>= A wall is a kind of room. A wall has a number called the inventory. To stock (w - a wall) with (n - a number) bottles: change the inventory of w to n. To drink from (w - a wall): decrease the inventory of w by 1. typical verses base case
we don't need no alternation
The typical verse translates straightforwardly. A peculiarity of Inform is the use of the built-in [s] for simple automatic pluralization.
Question: in which position is the recursive call for act on?
<<typical verses>>= To say beer count of (w - a wall): say "[inventory of w] bottle[s] of beer". To act on (w - a wall): say "take one down and pass it around, "; drink from w; say "[beer count of w].[line break]"; say "[beer count of w] on the wall, [beer count of w]."; act on w.
By defining empty wall, we can add phrases to handle the base case separately.
<<base case>>= Definition: a wall is empty if its inventory is zero. To say beer count of (w - an empty wall): say "no more bottles of beer". To act on (w - an empty wall): say "go to the store to buy some more, ".
Question: how similar are Inform 7's definitions and conditions to COBOL 88-levels?
(re)stocking
The (slight) advantage of choosing the recursive over the iterative is that we need neither save nor pass on the initial bottle count for restocking.
<<stocking>>= To versify on (w - a wall) with (n - a number) bottles: stock w with n bottles; say "You start to sing:[line break]"; say "'[beer count of w] on the wall, [beer count of w]."; act on w; stock w with n bottles; say "[beer count of w].'".
understanding
Of course, now that we've implemented this behavior, we need some way for the player to trigger it. We define a new action, caterwauling to go along with the versify on ... with ... phrase, then retarget the built-in sing verb to trigger our behavior.
<<hook up actions>>= caterwauling is an action applying to a number. understand "sing [a number]" as caterwauling. understand "[a number]" as caterwauling. instead of singing: change the number understood to 99; try caterwauling. carry out caterwauling: let n be the number understood; versify on the stage with n bottles.
Exercise: this is suitable for our example, but not very good Inform 7.
- text output should occur in the "report" phase instead of the "carry out" phase for an action.
- normally, new code-level actions are paired with new verbs.
Write an alternative to this section by re-implementing the verb "to sing", with "check", "carry out" and "report" phases for the singing action behaving appropriately.
wrapping up
Finally, we add the header boilerplate, an initial location with instructions, and a minimal test suite.
<<99bottles.inform7>>= "99 bottles" by Dave wall definition stocking hook up actions The stage is a wall. "type 'sing' to sing '99 bottles of beer'". test me with "sing".
We can test this either with test me for the full 99 bottles, or sing 3 (or even 3) for a shorter test.
>sing 3 You start to sing: "3 bottles of beer on the wall, 3 bottles of beer. take one down and pass it around, 2 bottles of beer. 2 bottles of beer on the wall, 2 bottles of beer. take one down and pass it around, 1 bottle of beer. 1 bottle of beer on the wall, 1 bottle of beer. take one down and pass it around, no more bottles of beer. no more bottles of beer on the wall, no more bottles of beer. go to the store to buy some more, 3 bottles of beer."