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 52 — Numeral Systems Counters
This is project 52 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 player's answers are piped in:
8
10
Run it with:
ek9 numeralsystemcounters.ek9 < input.txt
It prints:
Numeral System Counters, by Al Sweigart al@inventwithpython.com
This program shows you equivalent numbers in decimal (base 10),
hexadecimal (base 16), and binary (base 2) numeral systems.
(Ctrl-C to quit.)
Enter the starting number (e.g. 0) >
Enter how many numbers to display (e.g. 1000) >
DEC: 8 HEX: 8 BIN: 1000
DEC: 9 HEX: 9 BIN: 1001
DEC: 10 HEX: A BIN: 1010
DEC: 11 HEX: B BIN: 1011
DEC: 12 HEX: C BIN: 1100
DEC: 13 HEX: D BIN: 1101
DEC: 14 HEX: E BIN: 1110
DEC: 15 HEX: F BIN: 1111
DEC: 16 HEX: 10 BIN: 10000
DEC: 17 HEX: 11 BIN: 10001
The program
#!ek9 defines module bigbook.numeralsystemcounters defines constant Hexadecimal <- 16 Binary <- 2 DefaultStart <- 0 DefaultAmount <- 1000 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()) //Unset only if the input ends before a usable answer. nextStart() <- rtn as Integer: askCount(say.askStart(), say.notAStart(), DefaultStart) nextAmount() <- rtn as Integer: askCount(say.askAmount(), say.notAnAmount(), DefaultAmount) showNumerals() -> decimal as Integer hexadecimal as String binary as String stdout.println(say.numerals(decimal, hexadecimal, binary)) //Asks until the game takes the reply as a count. private askCount() -> question as String refusal as String fallback as Integer <- rtn as Integer: Integer() while not rtn? and not input.ended() stdout.println(question) if entry <- input.next() rtn: countFrom(entry.trim(), fallback) if not rtn? stdout.println(refusal) override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the numbers and their numerals, with no input or output. //A count typed by the player: an empty reply takes the fallback, a whole number of zero or //more is taken as it is, anything else is unset so the caller asks again. countFrom() as pure -> reply as String fallback as Integer <- rtn as Integer: Integer() if reply == "" rtn: fallback else count <- Integer(reply) if count? and count >= 0 rtn: count //Integer.radix(base) does the conversion; the book shows hexadecimal in capitals. inHexadecimal() as pure -> number as Integer <- rtn as String: number.radix(Hexadecimal).upperCase() inBinary() as pure -> number as Integer <- rtn as String: number.radix(Binary) defines function //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() `Numeral System Counters, by Al Sweigart al@inventwithpython.com This program shows you equivalent numbers in decimal (base 10), hexadecimal (base 16), and binary (base 2) numeral systems. (Ctrl-C to quit.) ` askStart() "Enter the starting number (e.g. 0) >" notAStart() "Please enter a number greater than or equal to 0." askAmount() "Enter how many numbers to display (e.g. 1000) >" notAnAmount() "Please enter a number." numerals() -> decimal as Integer hexadecimal as String binary as String `DEC: ${decimal} HEX: ${hexadecimal} BIN: ${binary}` defines program //Numeral System Counters (Big Book project 52). Shows a run of numbers in decimal, //hexadecimal and binary. The game decides; the Console asks and tells, in the player's words. NumeralSystemCounters() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) console.showIntroduction() start <- console.nextStart() amount <- console.nextAmount() //A range runs in either direction, so `start ... start - 1` would count DOWN: guard the //empty case instead of relying on the range to be empty. if start? and amount? and amount > 0 for number in start ... start + amount - 1 console.showNumerals(number, inHexadecimal(number), inBinary(number))
About this port
- Faithful to the book: asks for a starting number and how many numbers to show (an empty reply takes 0 and
1000), re-asks on anything that is not a whole number of zero or more, then prints
DEC: n HEX: h BIN: bfor each number in the run, with the original's spacing. - Idioms: both prompt loops repeat until the game's
countFromgives them a value, and stop at the end of the input. The run is a range,start ... start + amount - 1. An EK9 range counts down as readily as up, so a count of zero is guarded rather than left to the range.