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 40 — Leetspeak

This is project 40 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 leetspeak.ek9 "elite hacker"

It prints:

elite ]-[a(]<3r

The program

#!ek9
defines module bigbook.leetspeak

  defines constant

    ReplaceChancePercent <- 70

  defines class

    //THE OUTPUT: everything the reader sees goes through here. The port shows no words of its
    //own, only the translation, so there is no Say; the message is a program argument, so
    //there is no LineInput.
    Console
      stdout as Stdout: Stdout()

      //The translation is computed, not worded: it is shown as the game made it.
      showTranslated()
        -> translated as String
        stdout.println(translated)

      override operator ? as pure
        <- rtn as Boolean: true

  defines function

    //THE GAME: the translation, with no output.

    //The book's table: the leetspeak spellings each letter may become.
    leetTable() as pure
      <- rtn as Dict of (Character, List of String): {
        'a': ["4", "@", "/-\\"],
        'c': ["("],
        'd': ["|)"],
        'e': ["3"],
        'f': ["ph"],
        'h': ["]-[", "|-|"],
        'i': ["1", "!", "|"],
        'k': ["]<"],
        'o': ["0"],
        's': ["$", "5"],
        't': ["7", "+"],
        'u': ["|_|"],
        'v': ["\\/"]
      }

    //A letter with spellings is replaced ReplaceChancePercent of the time, by one of them
    //chosen at random; anything else is kept. `and` stops at the first false operand, so a
    //character with no spellings draws no random number - the sequence matches the book's.
    leetOf()
      ->
        original as Character
        spellings as List of String
        rng as Random
      <- rtn as String: original

      if not spellings.empty() and rng.next(1, 100) <= ReplaceChancePercent
        rtn: spellings.getOrDefault(rng.next(0, length spellings - 1), rtn)

    leet()
      ->
        message as String
        rng as Random
      <- rtn as String: ""

      table <- leetTable()
      noSpellings <- List() of String
      for ch in message
        rtn += leetOf(ch, table.getOrDefault(ch.lowerCase(), noSpellings), rng)

  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()

  defines program

    //Leetspeak (Big Book project 40). Rewrites the message argument in leetspeak, choosing at
    //random which letters to replace and with which spelling. The game decides; the Console
    //shows it. The golden replays a seeded run (seed.txt).
    Leetspeak()
      -> message as String
      console <- Console()
      rng <- randomSource()

      console.showTranslated(leet(message, rng))

About this port