The Big Book of Small EK9 Programs
Adapted from Al Sweigart's The Big Book of Small Python Projects: 81 Easy Practice Programs (No Starch Press, 2021; copyright © 2021 Al Sweigart), which he makes freely available under a Creative Commons Attribution-NonCommercial-ShareAlike licence at inventwithpython.com/bigbookpython. Changes were made: each program was ported from Python to EK9 and remodelled in EK9's idioms.
Project 51 — niNety-nniinE bOttels
This is project 51 of the book, ported to EK9. Its chapter is still being written; until then, this page shows the port itself: a run of it, the whole program, and how it keeps to the original.
A sample run
The program is random, so the run fixes it by setting BIGBOOK_SEED=1.
Run it with:
BIGBOOK_SEED=1 ek9 ninetynniineboottels.ek9
It prints:
niNety-nniinE BoOttels, by Al Sweigart al@inventwithpython.com
(Press Ctrl-C to quit.)
99 bottles of milk on the wall,
99 bottles of milk,
Take one down, pass it around,
98 bottles of milk on the wall!
98 bottles of milko n the wall,
98 bottles of milk,
Take one down, pass it around,
97 bottles of milk on the wall!
97 bottles of milko n the wall,
97 bo tles of milk,
Take one down, pass it around,
96 bottles of milk on the wall!
96 bottles of milko n the wall,
96 bo tles of milk,
Take one down, pass it around,
95 bottles of milk on the wall!
95 bottles of milko n the wall,
95 bo tles of milk,
Take one down, pass it around,
94 bottles of milk on th wall!
94 bottles of milko n the wall,
94 bo tles of milk,
Take one down, pass it around,
93 bottles of milk on th wall!
93 bottles of milko n the wall,
93 bo tles of milk,
Take one down, pass it around,
92 bottles of milk on th wall!
92 bottles of milko n the waLl,
92 bo tles of milk,
Take one down, pass it around,
91 bottles of milk on th wall!
91 bottles of milko n the waLl,
91 bo tles of milk,
Take one dOwn, pass it around,
90 bottles of milk on th wall!
90 bottles of milko n the waLl,
90 bo tles of milk,
Take one dOwn, pass it around,
89 bottles of milk on th walll!
89 bottles of milko n the waLl,
89 bo tles of milk,
Take one dOwn, pass it around,
88 bottles of miilk on th walll!
88 bottles of milko n the waLl,
88 bo tles of milk,
Take one dOwn, pass it around,
87 bottles of miilk on th waalll!
87 bottles of milko n the wa l,
87 bo tles of milk,
Take one dOwn, pass it around,
86 bottles of miilk on th waalll!
86 bottles of milok n the wa l,
86 bo tles of milk,
Take one dOwn, pass it around,
85 bottles of miilk on th waalll!
85 bottles of milok n the wa l,
85 bo tles of milk,
Take one dOwn, paSs it around,
84 bottles of miilk on th waalll!
84 bottles of milok n the wa l,
84 bo tles of milk,
Take one dOwn, paSs it around,
83 bottlees of miilk on th waalll!
83 bottles of milok n the wa l,
83 bo tles of milk,
Take one dOwn, paSs it around,
82 bottlees of miilk on th waalll!
82 bottles of milok n the wa l,
82 bo tles of milk,
Take one dOwn, paSs it around,
81 bottlees of miilk on th waall !
81 bottles of milok n the wa l,
81 bo tles of milk,
Take one dOwn, paSs it around,
80 bott ees of miilk on th waall !
80 bottles of milok n the wa l,
80 bo tles of milk,
Take one dOwn, paSs it around,
79 bo t ees of miilk on th waall !
79 bottles of milok n the wa l,
79 boo tles of milk,
Take one dOwn, paSs it around,
78 bo t ees of miilk on th waall !
78 bottles of milok n the wa l,
78 boo tles of milk,
Take one dOwn, paSs it around,
77 bo t ees of miilk on th waall !
77 bottles of milok n the wa l,
77 boo tles of milk,
Take on edOwn, paSs it around,
76 bo t ees of miilk on th waall !
76 bottles of milok n the wa l,
76 boo tles of milk,
take on edOwn, paSs it around,
75 bo t ees of miilk on th waall !
75 bottles of milok n the wa l,
75 boo tles of milk,
take on edOwn, paSs it around,
74 bo t ees of miilk on th waall !
74 bottles of milok n the wa l,
74 boo tles oof milk,
take on edOwn, paSs it around,
73 bo t ees of miilk on th waall !
73 bottless of milok n the wa l,
73 boo tles oof milk,
take on edOwn, paSs it around,
72 bo t ees of miilk on th waall !
72 bottless of milok n the wa l,
72 boo tles oof milk,
take on edOwn, paSs it around,
71 bo t ees of miilk on th waall !
71 bottless of milok n the wa l,
71 boo tles oof milk,
take on edOwn, paSs it around,
70 bo t ees of miilk on th waall !
70 bottless of milok n the wa l,
70 boo tles oof milk,
take on edOwn, paSs it around,
69 bo t ees of miilk on th waall !
69 bottless of milok n the wa l,
69 boo tles oof milk,
take on edOwn, paSs it around,
68 bo t ees of miilk on th waall !
68 bottless of milok n the wa l,
68 boo tles oof milK,
take on edOwn, paSs it around,
67 bo t ees of miilk on th waall !
67 bottless of milok n the wa l,
67 boo tles oof milK,
take on edOwn, paSs it around,
66 bo t ees of miilk on th w aall !
66 bottless of milok n the wa l,
66 boo tles oof milK,
take on edOwn, paSs it aro nd,
65 bo t ees of miilk on th w aall !
65 bottless of milok n the wa l,
65 boo tles ooff milK,
take on edOwn, paSs it aro nd,
64 bo t ees of miilk on th w aall !
64 bottless of milok n the wa l,
64 boo tles ooff milK,
take no edOwn, paSs it aro nd,
63 bo t ees of miilk on th w aall !
63 bottless of milok n the wa l,
63 boo tles ooff milK,
take no edOwn, paSs it aro dn,
62 bo t ees of miilk on th w aall !
62 bottless of milok n the wa l,
62 boo tles ooff milK,
take no edOwn, paSs it aro dn,
61 bo t ees of miilk on th w aall !
61 bottless of milok n the wa l,
61 boo tles ooff milK,
take no edOwn, paSs it aro dn,
60 bo t ees of miilk on th w aall !
60 bottless of milok n the wa l,
60 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
59 bo t ees of miilk on th w aall !
59 bottless of milok n the wa l,
59 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
58 bo t ees of miilk on th w aall !
58 bottless of milok n the wa l,
58 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
57 bo t ees of miil kon th w aall !
57 bottless ofm ilok n the wa l,
57 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
56 bo t ees of miil kon th w aall !
56 bottless ofm il k n the wa l,
56 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
55 bo t ees of miil kon th w aall !
55 bottless ofm il k n the wa l,
55 bboo tles ooff milK,
take no edOwn, paSs it aro dn,
54 bo t ees of miil kon th w Aall !
54 bottless ofm il k n the wa l,
54 bboo tles ooff milK,
take no edOwn, apSs it aro dn,
53 bo t ees of miil kon th w Aall !
53 bottless ofm il k n the wa l,
53 bboo tles ooff milK,
take no edOwn, apSs it aro dn,
52 bo t ees of miil kon th w Aall !
52 bottless ofm il k n the wa l,
52 bboo tles ooff milK,
take no edOwn, apSs it aro dn,
51 boo t ees of miil kon th w Aall !
51 bottless ofm il k n the wa l,
51 bboo tles ooff milK,
take no edOwn, apSs it aro dn,
50 boo t ees of miil kon thw Aall !
50 bottless ofm il k n the wa l,
50 bboo tles ooff milK,
take no edOwn, apSs it aro dn,
49 boo t ees of miil kon thw Aall !
49 bottless ofm il k n the wa l,
49 bboo tles ooff milK,
take no edOwn, apSs it aro dnn,
48 boo t ees of miil kon thw Aall !
48 bottless ofm il k n the wa l,
48 bboO tles ooff milK,
take no edOwn, apSs it aro dnn,
47 boo t ees of miil kon thw Aall !
47 bottless ofm il k n the wa l,
47 bboOO tles ooff milK,
take no edOwn, apSs it aro dnn,
46 boo t ees of miil kon thw Aall !
46 bottless ofm il k n the wa l,
46 bboOO tles ooff milK,
take no edOwn, apSs it aro dnn,
45 boo t ees of miil kon thw Aall !
45 bottless ofm il k n the wa L,
45 bboOO tles ooff milK,
take no edOwn, apSs it aro dnn,
44 boo t ees of miil kon thw Aall !
44 bottless ofm il k n the wa L,
44 bboOO tles ooff milK,
take no edOwn ,apSs it aro dnn,
43 boo t ees of miil kon thw Aall !
43 bottless ofm il k n the wa L,
43 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
42 boo t ees of miil kon thw Aall !
42 bottless ofm il k n the wa L,
42 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
41 boo t ees of miil kon thw Aall !
41 bottless ofm il k n the aw L,
41 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
40 boo t ees of miil kon thw Aall !
40 bbottless ofm il k n the aw L,
40 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
39 boo t ees of miil kon thw Aall !
39 bbottless ofm il k n the aw L,
39 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
38 boo t eees of miil kon thw Aall !
38 bbottless ofm il k n the aw L,
38 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
37 boo t eees of miil kon thw Aall !
37 bbottless ofm il k n the aw L,
37 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
36 boo t eees of miil kon thw Aall !
36 bbottless ofm il k n the aw L,
36 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
35 boo t eees of miil kon thw A ll !
35 bbottless ofm il k n the aw L,
35 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
34 boo t eees of miil kon tHw A ll !
34 bbottless ofm il k n thE aw L,
34 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
33 boo t eees of miil kon tHw A ll !
33 bbottless ofm il k n thE aw L,
33 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
32 boo t eees of miil kon twH A ll !
32 bbottless ofm il k n thE aw L,
32 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
31 boo t eees of miil kon twH A ll !
31 bbottless ofm il k n thE aw L,
31 bboOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
30 oo t eees of miil kon twH A ll !
30 bbottless ofm il k n thE aw L,
30 b oOO tles ooff milK,
take nno edOwn ,apSs it aro dnn,
29 oo t eees of miil kon twH A ll !
29 bbottless ofm il k n thE aw L,
29 b oOO ttles ooff milK,
take nno edOwn ,apSs it aro dnn,
28 oo t eees of miil kon twH A ll !
28 bbottless ofm il k n thE aw L,
28 b oOO ttles ooff milK,
take nno edOwn ,apSs it aro dnn,
27 oo eees of miil kon twH A ll !
27 bbottless ofm il k n thE aw L,
27 b oOO ttles ooff milK,
take nno edOwn ,apSs it aro dnn,
26 oo eees of miil kon twH A ll !
26 bbottless ofm il k n thE aw L,
26 b oOO ttles ooff milK,
take nno edOwn ,apSs it aro dnn,
25 o eees of miil kon twH A ll !
25 bbottlEss ofm il k n thE aw L,
25 b oOO ttles ooff milK,
take nno edOwn ,apSs it aro dnn,
24 o eees of miil kon twH A ll !
24 bbottlEss ofm il k n thE aw L,
24 b oOO ttles ooff milK,
t ke nno edOwn ,apSs it aro dnn,
23 o eees of miil kon twH A ll !
23 bbottlEss ofm il k n thE aw L,
23 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
22 o eees of miil kon twH A ll !
22 bbottlEss ofm il k n thE aw L,
22 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
21 o eees of miil kon twH A ll !
21 bbottlEss ofm il k n thE aw L,
21 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
20 o eees of miil kon twH A ll !
20 bbottlEss ofm il k N thE aw L,
20 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
19 o eees of miil kon twH A ll !
19 bbottlEss ofm il k N thE aw L,
19 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
18 o eees of miil kon twH A ll !
18 bbottlEss ofm il k N thE aw L,
18 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
17 o eees of miil kon twH A ll !
17 bbottlEss ofm il k N thE aw L,
17 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
16 o eees of miill kon twH A ll !
16 bbottlEss ofm il k N thE aw L,
16 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
15 o eees of miill kon twH A ll !
15 bbottlEss ofm il k N thE aw L,
15 b oOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
14 o eees of miill kon twH A ll !
14 bbottlEss ofm il k N thE aw L,
14 b OOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
13 o eees of miill kon twH A ll !
13 bbottLEss ofm il k N thE aw L,
13 b OOO ttles ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
12 o eees of miill kon twH A ll !
12 bbottLEss ofm il k N thE aw L,
12 b OOO ttLes ooff imlK,
t ke nno edOwn ,apSs it aro dnn,
11 o eees of miill kon twH A ll !
11 bbottLEss ofm il k N thE aw L,
11 b OOO ttLes ooff imKl,
t ke nno edOwn ,apSs it aro dnn,
10 o eees of miill kon twH A ll !
10 bbottLEss ofm il k N thE aw L,
10 b OOO ttLes ooff imKl,
t ke nno edOwn ,apSs it aro dnn,
9 o eees of miill kon twH A ll !
9 bbottLEss ofm il k N thE aw L,
9 b OOO ttLes ooff imKl,
t ke nno edOwn ,apSs it aro dnn,
8 o eees of miill kon twH A ll !
8 bbottLEss ofm il k N thE aw L,
8 b OOO ttLes ooff imKl,
t ke nno edOwn ,apSs it aro dnn,
7 o eees of miill kon twH A ll !
7 bbottLEss ofm il k N thE aw L,
7 b OOO ttLes ooff imKl,
t ke nno edOwn ,paSs it aro dnn,
6 o eees of miill kon twH A ll !
6 bbottLEss ofm il k N thE aw L,
6 b OOO ttLes ooff imKl,
t ke nno e dOwn ,paSs it aro dnn,
5 o eees of miill kon twH A ll !
5 bbottLEss ofm il k N thE aw L,
5 b OOO ttLes ooff imKl,
t ke nno E dOwn ,paSs it aro dnn,
4 o eees of miill kon twH A ll !
4 bbottLEss ofm il k N thE aw L,
4 b OOO ttLes ooff imKl,
t ke nno E dOwn ,paSs it aro dnn,
3 o eees of miill kon twH A ll !
3 bbottLEss ofm il k N hE aw L,
3 b OOO ttLes ooff imKl,
t ke nno E dOwn ,paSs it aro dnn,
2 o eees of miill kon twH A ll !
2 bbottLEss ofm il k N hE aw L,
2 b OOO ttLes ooff imKl,
t ke nno E dOwn ,paSs it aro dnn,
1 o eees of miill kon twH A ll !
1 bbottLEss ofm il k N hE aw L,
1 b OOO ttLes ooff imKl,
t ke nno E dOwn ,paSs it aro dnn,
No more bottles of milk on the wall!
The program
#!ek9 defines module bigbook.ninetynniineboottels defines constant StartBottles <- 99 //The song's last line, after the last bottle. It never gets sillier, so it is not one of //the lyrics the game changes. NoMoreBottles <- "No more bottles of milk on the wall!" defines type //The four lines of a verse, in the order they are sung. Lyric OnTheWall OfMilk TakeOneDown OnTheWallStill //The four ways a line gets sillier after each verse. Effect Blank FlipCase Transpose Double List of Lyric List of Effect Dict of (Lyric, String) defines class //THE OUTPUT: everything the listener sees goes through here, worded by Say. The song reads //no input, so there is no LineInput. Console stdout as Stdout: Stdout() say as Say: Say("en") Console() -> words as Say say: words showIntroduction() stdout.println(say.introduction()) //The verse is the game's, sung as it now stands; the blank line after it is layout, so //the Console prints it. showVerse() -> lines as List of String for line in lines stdout.println(line) stdout.println("") override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the song and how it gets sillier, with no output or words. The lyrics are game //state, not text: the game changes them as the song goes on. //The lyrics as the song starts, before any line has been made sillier. startingLyrics() as pure <- rtn as Dict of (Lyric, String): { Lyric.OnTheWall: " bottles of milk on the wall,", Lyric.OfMilk: " bottles of milk,", Lyric.TakeOneDown: "Take one down, pass it around,", Lyric.OnTheWallStill: " bottles of milk on the wall!" } //The four lines of the verse for this many bottles, with the lyrics as they now are. verseOf() as pure -> bottles as Integer lyrics as Dict of (Lyric, String) <- rtn as List of String: List() onTheWall <- `${bottles}${lyrics.getOrDefault(Lyric.OnTheWall, "")}` ofMilk <- `${bottles}${lyrics.getOrDefault(Lyric.OfMilk, "")}` takeOneDown <- lyrics.getOrDefault(Lyric.TakeOneDown, "") remaining <- bottles - 1 onTheWallStill <- remaining > 0 <- `${remaining}${lyrics.getOrDefault(Lyric.OnTheWallStill, "")}` : NoMoreBottles rtn: [onTheWall, ofMilk, takeOneDown, onTheWallStill] flippedCase() as pure -> text as String <- rtn as String: text == text.upperCase() <- text.lowerCase() : text.upperCase() //How many characters from the chosen position an effect works on. As in the book, //doubling also stops one short of the end. charactersUsed() as pure -> effect as Effect <- rtn as Integer: 1 if effect == Effect.Transpose or effect == Effect.Double rtn: 2 //The line with one effect applied at one position. Strings are cut and rejoined with //subString rather than edited in place. sillier() as pure -> line as String effect as Effect position as Integer <- rtn as String: line before <- line.subString(0, position) here <- line.subString(position, position + 1) after <- line.subString(position + 1) switch effect case Effect.Blank rtn: `${before} ${after}` case Effect.FlipCase rtn: `${before}${flippedCase(here)}${after}` case Effect.Transpose next <- line.subString(position + 1, position + 2) rest <- line.subString(position + 2) rtn: `${before}${next}${here}${rest}` case Effect.Double rtn: `${before}${here}${here}${after}` default rtn: line //Pick a line, an effect and a position at random, in the book's order, and make that //line sillier. The choices come from the enumerations, so their sizes are not repeated. makeSillier() -> lyrics as Dict of (Lyric, String) rng as Random allLyrics <- cat Lyric | collect as List of Lyric allEffects <- cat Effect | collect as List of Effect lyric <- allLyrics.getOrDefault(rng.next(0, length allLyrics - 1), Lyric.OnTheWall) effect <- allEffects.getOrDefault(rng.next(0, length allEffects - 1), Effect.Blank) line <- lyrics.getOrDefault(lyric, "") position <- rng.next(0, length line - charactersUsed(effect)) lyrics += DictEntry(lyric, sillier(line, effect, position)) defines function //THE source of randomness, copied unchanged from lib/RandomSource.ek9: SystemRandom(), or //SeededRandom(seed) when BIGBOOK_SEED holds a number (run-and-diff.sh sets it from seed.txt). randomSource() <- rtn as Random: SeededRandom(Integer(EnvVars().get("BIGBOOK_SEED"))) ?: SystemRandom() //THE language of the words, copied unchanged from lib/Language.ek9: BIGBOOK_LANG when it //is set (fr, zh, ...), otherwise en. language() <- rtn as String: EnvVars().get("BIGBOOK_LANG") ?: "en" //THE WORDS: every word the listener sees that the game does not change. The song's lines are //not here: they are the game's lyrics. defines text for "en" Say introduction() `niNety-nniinE BoOttels, by Al Sweigart al@inventwithpython.com (Press Ctrl-C to quit.)` defines program //niNety-nniinE BoOttels (Big Book project 51). The bottles song, where every verse leaves one //line a little sillier than before. The game decides; the Console shows, in the reader's //words. The golden replays a seeded run (seed.txt). NiNetyNniineBoottels() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) rng <- randomSource() lyrics <- startingLyrics() console.showIntroduction() for bottles in StartBottles ... 1 console.showVerse(verseOf(bottles, lyrics)) makeSillier(lyrics, rng)
About this port
- Behaviour: the bottles song from 99 down, sung from four line templates. After each verse one random line gets one random effect: a character becomes a space, a character changes case, two neighbouring characters swap, or a character is doubled. The changes build up over the whole song.
- Idioms:
LyricandEffectare enumerations, and the lyrics are aDict of (Lyric, String), so the verse readslyrics.getOrDefault(Lyric.TakeOneDown, "")rather than a line number.- The random line and effect are drawn from the enumerations themselves
(
cat Effect | collect as List of Effect), so the number of choices is never written down. The draws are made in the book's order (line, effect, position), which keeps the golden. sillieris a pureswitchoverEffectthat cuts the line withsubStringand rejoins it with interpolation, where the book edits a list of characters in place.- The verse loop is a descending range,
for bottles in StartBottles ... 1.
- Omitted:
slowPrint's letter-by-letter printing and pauses.