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 31 — Guess the Number
This is project 31 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. The player's answers are piped in:
fifty
50
99
75
97
Run it with:
BIGBOOK_SEED=1 ek9 guessthenumber.ek9 < input.txt
It prints:
Guess the Number, by Al Sweigart al@inventwithpython.com
I am thinking of a number between 1 and 100.
You have 10 guesses left. Take a guess.
Please enter a number between 1 and 100.
Your guess is too low.
You have 9 guesses left. Take a guess.
Your guess is too high.
You have 8 guesses left. Take a guess.
Your guess is too low.
You have 7 guesses left. Take a guess.
Yay! You guessed my number!
The program
#!ek9 defines module bigbook.guessthenumber defines constant Highest <- 100 Guesses <- 10 defines type //What a wrong guess is told: the game decides which, the Console words it. Hint TooLow TooHigh defines class //THE input idiom of this book, copied unchanged from lib/LineInput.ek9 (the reasons are //there): read with next(), and every input loop also stops when ended() is true. LineInput stdin as Stdin: Stdin() inputEnded as Boolean: false next() <- rtn as String: stdin.next() if not rtn? inputEnded: true ended() as pure <- rtn as Boolean: Boolean(inputEnded) override operator ? as pure <- rtn as Boolean: true //THE INPUT AND OUTPUT: everything the player sees or types goes through here, worded by Say. Console stdout as Stdout: Stdout() input as LineInput: LineInput() say as Say: Say("en") Console() -> words as Say say: words showIntroduction() stdout.println(say.introduction()) //Asks until the reply is a number; unset only if the input ends first. nextGuess() -> guessesLeft as Integer <- rtn as Integer: Integer() stdout.println(say.askGuess(guessesLeft)) while not rtn? and not input.ended() rtn: guessFrom(input.next()) if not rtn? and not input.ended() stdout.println(say.notANumber()) showHint() -> hint as Hint stdout.println(hint == Hint.TooLow <- say.tooLow() : say.tooHigh()) showResult() -> guessed as Boolean secret as Integer stdout.println(guessed <- say.guessedIt() : say.gameOver(secret)) ended() as pure <- rtn as Boolean: input.ended() override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: its rules, with no input or output. //Integer(text) is unset when the text is not a number, so the parse is the validation: //no isdecimal() first. guessFrom() as pure -> entry as String <- rtn as Integer: Integer(entry) hintFor() as pure -> guess as Integer secret as Integer <- rtn as Hint: guess < secret <- Hint.TooLow : Hint.TooHigh 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 player sees. defines text for "en" Say introduction() `Guess the Number, by Al Sweigart al@inventwithpython.com I am thinking of a number between 1 and ${Highest}.` askGuess() -> guessesLeft as Integer `You have ${guessesLeft} guesses left. Take a guess.` notANumber() `Please enter a number between 1 and ${Highest}.` tooLow() "Your guess is too low." tooHigh() "Your guess is too high." guessedIt() "Yay! You guessed my number!" gameOver() -> secret as Integer `Game over. The number I was thinking of was ${secret}` defines program //Guess the Number (Big Book project 31). Ten guesses at a number from 1 to 100. The game //decides; the Console asks and tells, in the player's words. The golden replays a seeded game //(seed.txt). GuessTheNumber() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) rng <- randomSource() secret <- rng.next(1, Highest) console.showIntroduction() //The loop condition carries the book's 'break': it stops on the right guess. guessed <- false guessesLeft <- Guesses while guessesLeft > 0 and not guessed and not console.ended() if guess <- console.nextGuess(guessesLeft) guessed: guess == secret if not guessed console.showHint(hintFor(guess, secret)) guessesLeft -= 1 console.showResult(guessed, secret)