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 46 — Million Dice Roll Statistics Simulator
This is project 46 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 milliondiceroll.ek9 2
It prints:
Million Dice Roll Statistics Simulator
By Al Sweigart al@inventwithpython.com
Simulating 1000000 rolls of 2 dice...
TOTAL - ROLLS - PERCENTAGE
2 - 27778 rolls - 2.8%
3 - 55953 rolls - 5.6%
4 - 83379 rolls - 8.3%
5 - 110777 rolls - 11.1%
6 - 139502 rolls - 14.0%
7 - 166404 rolls - 16.6%
8 - 137901 rolls - 13.8%
9 - 111401 rolls - 11.1%
10 - 83361 rolls - 8.3%
11 - 55664 rolls - 5.6%
12 - 27880 rolls - 2.8%
The program
#!ek9 defines module bigbook.milliondiceroll defines constant Rolls <- 1000000 Sides <- 6 defines class //THE OUTPUT: everything the player sees goes through here, worded by Say. The number of //dice is a program argument, 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()) showTooFewDice() stdout.println(say.tooFewDice()) showSimulating() -> dice as Integer stdout.println(say.simulating(dice)) showTableHeading() stdout.println(say.tableHeading()) //The numbers are the simulation's; the words around them ("rolls", "%") are Say's. showTableRow() -> total as Integer count as Integer percentage as Float stdout.println(say.tableRow(total, count, percentage)) override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the simulation, with no output or words. lowestTotal() as pure -> dice as Integer <- rtn as Integer: Integer(dice) highestTotal() as pure -> dice as Integer <- rtn as Integer: dice * Sides //The total of one throw of every die. throwTotal() -> dice as Integer rng as Random <- rtn as Integer: 0 for die in 1 ... dice rtn += rng.next(1, Sides) //How often each total came up in a million throws. Every possible total starts at zero, //so a total that is never thrown still has a count. rollCounts() -> dice as Integer rng as Random <- rtn as Dict of (Integer, Integer): Dict() for total in lowestTotal(dice) ... highestTotal(dice) rtn += DictEntry(total, 0) for roll in 1 ... Rolls total <- throwTotal(dice, rng) rtn += DictEntry(total, rtn.getOrDefault(total, 0) + 1) //The book's round(count / 10000, 1): the share of all the rolls, as a percentage. percentageOf() as pure -> count as Integer <- rtn as Float: Float() share <- Float(count) * 100.0 / Float(Rolls) rtn: share.round(1) 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. The numbers in the table are not here: the //simulation counts them and the Console shows them. defines text for "en" Say introduction() `Million Dice Roll Statistics Simulator By Al Sweigart al@inventwithpython.com` tooFewDice() "Roll at least one die." simulating() -> dice as Integer `Simulating ${Rolls} rolls of ${dice} dice...` tableHeading() "TOTAL - ROLLS - PERCENTAGE" tableRow() -> total as Integer count as Integer percentage as Float ` ${total} - ${count} rolls - ${percentage}%` defines program //Million Dice Roll Statistics Simulator (Big Book project 46). Rolls the dice a million times //and shows how often each total comes up. The game decides; the Console shows, in the //reader's words. The golden replays a seeded run (seed.txt). MillionDiceRoll() -> dice as Integer //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) console.showIntroduction() if dice < 1 console.showTooFewDice() else rng <- randomSource() console.showSimulating(dice) counts <- rollCounts(dice, rng) console.showTableHeading() for total in lowestTotal(dice) ... highestTotal(dice) count <- counts.getOrDefault(total, 0) console.showTableRow(total, count, percentageOf(count))
About this port
- Faithful to the book: a million throws of however many six-sided dice the player chooses; a
count for every possible total (
dicetodice * 6), set to zero before rolling; the tableTOTAL - ROLLS - PERCENTAGEwith lines{total} - {count} rolls - {pct}%and the percentage rounded to one decimal place (the book'sround(count / 10000, 1)). - Deliberate divergences: the argument replaces the book's
input()prompt, and EK9 checks that it is an Integer before the program starts; fewer than one die gets a message instead of the book's crash. The "X% done..." progress lines are omitted (they depend on the clock). The book prints1,000,000; the port prints theRollsconstant as it is. - Idioms: ranges for every loop (
1 ... Rolls,1 ... dice,lowest ... highest, which include both ends);throwTotalfor one throw;percentageOfmarkedas pure.