Lacking Natural Simplicity

Random musings on books, code, and tabletop games.

Installing a recent version of the A2 operatinng system on UNIX

I recently ran across a document that explains how to install the A2 operating system (formerly Bluebottle, formerly Aos) in the form of UnixAos hosted on macOS, in the process of explaining how to make a installer for macOS. I think the same process will work for any supported Unix. [Later: indeed, I used the LinuxA2-64bit.tgz file and the same process worked fine on my Fedora box.]

I'll summarize what I did.

I went to and selected the most recent revision, rev.9799 at the time of this writing. There I downloaded the install script, install.UnixA2 and the version for 64 bit macOS, DarwinA2-64bit.tgz. (There were also Linux and Solaris versions for 32 and 64 bit systems.)

Then I executed chmod +x'ed the install script and ran it, passing the .tgz file to it.

First, it asks for the installation directory, which should already exist. It has a default, but I installed to the directory /usr/local/sw/versions/aos/rev.9799. Again, I had to create that directory first. (I don't like to write into /usr or /usr/local, because those directory hierarchies are often under the systems control. Nobody uses /usr/local/sw for anything except me.)

Second, it asks for the file name to copy the aos script to. I installed the aos command to /Users/tkb/local/rndbin, (/home/tkb/local/rndbin on non-macOS systems) which is in my path.

The install script assumes you are running as root, but I ran it first as my normal user, and since the directory I was installing it to was owned by me it seems to have worked ok, even though it complained about chgrp not being able to change the group of a bunch of files.

Then when I issued the command aos it started up and displayed a window with A2 running in it. (This requires X Windows, so you'll need to install XQuartz on macOS.)

Here's a screen shot from macOS:

Screenshot of A2 running under macOS

Screenshot of A2 running under macOS

It seems to work ok, both on macOS and Fedora, though I haven't investigated very much.

Last edited: 2022-06-12 15:39:49 EDT

The criticisms of Pascal in “Why Pascal is Not My Favorite Programming Language” applied to Oberon

I reread Brian W. Kernighan’s famous paper “Why Pascal is Not My Favorite Programming Language” (HTML, PDF) to see if those or similar criticisms can still be applied to the Oberon family of languages, Oberon, Oberon-2, and Revised Oberon. I found Oberon-2 to be particularly inspiring, but feel that both Oberon and Oberon-2 lack some of the practical aspects that made C such a revelation to me back in the 1980s when I first encountered it, after having programmed mostly in various BASICs, 6502 machine code, VAX MACRO, and Pascal.

So, here are Kernighan’s summary, nine criticisms, along with my comments.

  1. Since the size of an array is part of its type, it is not possible to write general-purpose routines, that is, to deal with arrays of different sizes. In particular, string handling is very difficult.

    This is still a problem in the original Oberon, but Oberon-2 introduced open arrays, which allows any size of array to be passed to a procedure. While you can’t return arrays of arbitrary sizes from a procedure, you can dynamically allocate any size array using POINTER TO ARRAY and NEW, and return the pointer. Revised Oberon adopted open arrays.

  2. The lack of static variables, initialization and a way to communicate non-hierarchically combine to destroy the “locality” of a program - variables require much more scope than they ought to.

    This criticism still applies. None of the Oberon languages include static variables or initialization.

    The lack of static variables is somewhat mitigated by the ability to divide things up using modules, since the tendency is to create a module for the shared variable and the procedures that depend on it, but is still clumsy if the only thing you need is a static variable in one procedure.

  3. The one-pass nature of the language forces procedures and functions to be presented in an unnatural order; the enforced separation of various declarations scatters program components that logically belong together.

    Oberon and Oberon-2 allow mixing CONST, TYPE and VAR declarations in any order and to appear multiple times, but still requires procedure and forward procedure declarations to come after all other declarations. In Revised Oberon CONST, TYPE, and VAR declarations must occur and in that order, followed by procedure declarations, so the original criticism applies entirely again.

  4. The lack of separate compilation impedes the development of large programs and makes the use of libraries impossible.

    Not a problem any more. All the Oberon languages use modules, introduced in Modula. Arguably, this is much superior to C’s model of separate compilation and using header files to ensure consistent function declarations are consistent across files.

  5. The order of logical expression evaluation cannot be controlled, which leads to convoluted code and extraneous variables.

    This appears to not apply to the Oberon languages.

  6. The ‘case’ statement is emasculated because there is no default clause.

    Original Oberon and Oberon-2 both have ELSE clauses for CASE statements. Revised Oberon does not.

  7. The standard I/O is defective. There is no sensible provision for dealing with files or program arguments as part of the standard language, and no extension mechanism.

    IO in the Oberon family of languages is mostly defined by the procedures provided in the Oberon System, the operating system written in Oberon (and later in Oberon-2). It has good facilities for dealing with files, considered by some to be a better API than the familiar Unix/C API, for the functionality it provides.

    None of the Oberon languages provide a standard method for access to program arguments in the Unix/C style, as the Oberon System used entirely different mechanisms. Those Oberon[-2] implementations I've used outside of the Oberon System all provided some access to the program arguments, usually as a procedure returns the number of program arguments and another that returns a specific argument, but none of them used the same API.

  8. The language lacks most of the tools needed for assembling large programs, most notably file inclusion.

    The addition of separately compiled modules that provide a defined interface mostly obviates this criticism and is superior to the kludge of separate complication and include files provided by C.

  9. There is no escape.

    All the Oberon languages include the module SYSTEM, which provides low level access to addresses of variables and to individual bits of memory, along with bit manipulation of integers. Revised Oberon adds access to sizes of types. Oberon and Oberon-2 provide a VAL function that allows interpreting a variable of one type as a variable of another type. Revised Oberon does not.

All the Oberon languages still have semicolon as separator instead of semicolon as terminator. I much prefer semicolon as terminator.

All-in-all, I'm disappointed in Revised Oberon. While I approve of a few of its changes, most of them seem to be a definite step backward. I think Wirth's minimalism does him a disservice here.

An Alien Creature for Breachworld

Here's an alien creature that created to have come through a nearby Breach in the Breachworld Adventure Folio #1 - The Big Score for the Breachworld RPG from Jason Richards Publishing when I ran that for my players.

I used The Random Esoteric Creature Generator for Classic Fantasy Role-Playing Games and Their Modern Simulacra to get inspiration for the creature. It wasn't hard to mung that into something for the Breachworld RPG, which uses Mini Six as the base for its rules. It should be easy to convert to any other OpenD6 game.


The creature is huge, as big as an elephant. It is semi-humanoid, having the head and wings of a immense dragonfly, two pairs of arms, a pair of legs, and a segmented body part coming out of the base of its spine that ends in a long stinger. It has three sets of mandibles for chewing. It is covered by chitinous armor. Its eyes glow blue, and its body is dark red on the head and torso, fading to a lighter red along its extremities.

Scale: +2D: PCs get +2D to hit and +6 to Dodge. It gets +2D to Damage and +6 to soak.
Might 3D — Brawl 5D, Stamina 4D
Agility 3D+1 — Athletics 5D+1, Dodge 5D
Wit 2D
Charm 1D
Static: Dodge 15, Soak 15 (21 vs. Scale +0D)
WL: S:1–3 □ W:4–8 □ SW:4–8 □ I:9–12 □ MW:13–15 □
Move: 15; Flying 25
Natural Armor: +6 Chitin Armor (included in Soak)
Natural Weapons: +2D Mandibles: 5D damage (7D vs. Scale +0D); +1D Stinger: 4D damage (6D vs. Scale +0D) with Poison (Resist with Stamina (or Might) instead of Soak), damage 5D; Successful hit by poison also requires a Moderate (11–15) Stamina check against the searing pain, which results in a penalty of –2D on all actions, in addition to any wounds.
Special Defenses: Resistant to Lightning: ½ damage

Mini Six Question: Why are Perdition’s Pilot and Mechanic Identical?

As part of my work making an SRD for Mini Six I was reminded that in the Perdition sample setting the character templates for the Pilot and the Mechanic are identical, which seems odd. Here, take a look:

Mini Six’s Pilot and Mechanic, from the Perdition sample setting

Mini Six’s Pilot and Mechanic, from the Perdition sample setting

Here’s a version of the original Mechanic in text format:

Might 2D+1
Agility 3D+2 — Dodge 5D+1, Pilot 5D+2
Wit 3D+1 — Computer 4D, Navigation 5D+1, Repair 4D+1
Charm 2D+2 — Diplomacy 4D
Static: Dodge 16, Block 7, Parry 7, Soak 7
Perks & Comps: None
Gear: Light Pistol

Anyway, I thought something like this might be better for the Mechanic:

Alternate Mechanic
Might 2D+1
Agility 3D+1 — Dodge 5D, Pilot 4D
Wit 3D+2 — Computer 6D, Navigation 4D+1, Repair 6D+1
Charm 2D+2 — Diplomacy 3D
Perks & Comps: None
Gear: Light Pistol
Static: Dodge 15; Block 7; Parry 7; Soak 7

Still has some skill at Pilot and Navigation, but not as good at those as the Pilot. Better Computer and Repair skills. Probably willing to pick up a pistol and shoot it if really necessary, but also probably no better at it that default, so Pistol isn’t listed.

What do you think?

Interestingly, the Pilot and the Mechanic templates are built on 12D in attributes and 8D+2 in skills, instead of the standard 7D in skills, so I made this alternate version with the same amount. I haven’t checked the other templates. (I’ll probably do that at some point. Rather than do the counting manually, I’ll probably type them up as YAML and feed them through sm6, a program I wrote that counts the costs of all the dice and prints out a summary. I’ve got a couple other programs, sm6rst and sm6troff-ms that can read that same YAML and produce output in reStructuredText or troff (T1, G1) formats, for including in my documents.)

I've also posted this question at a few online forums: the /r/OpenD6 and /r/rpg subreddits, the RPG Pub, and, which I'm listing here so I can be reminded of them.

Announcing Minimal OpenD6, an SRD for Mini Six

Anybody interested in an SRD for Mini Six? Since Mini Six is under the Open Game License, I have created one for it, Minimal OpenD6. (It does not include the Product Identity portions, of course.)

If you just want to read the rules I still think the original Mini Six: Bare Bones Edition is a great document, packing so much into a dense but readable layout, so download it for free or get a print copy. However, if you want a custom version of the rules, whether you are producing a completely new game or just want a version of the rules with all your house rules incorporated, this should be a great starting point.

Why start with Minimal OpenD6 instead of of OpenD6? Because you want to start with a minimal core and add things gradually, rather than starting with a very large document and cutting out everything you don’t want! (Different approaches work better for different folks.)

Minimal OpenD6 provides reStructuredText source for the document, as well as simple PDF, HTML, EPUB, Microsoft Word .docx, OpenOffice/LibreOffice .odt, and Markdown output. Conversions to other formats are possible using pandoc or other means.

If you just want the output files without any of the stuff used to build them, look at the releases page in the repository for a zip file.

Recording the books I read with org-mode and org-capture

I used to record the books that I read on this blog, but that took too much effort. However, if I don't record the books I read, I sometimes can't remember whether I read a particular book or not, especially with Kindle Unlimited books. (Was it the seventh book of the series that I read last, or the eighth?)

I record the books that I read in emacs using org-mode and — since today — org-capture. Here's what the org file looks like:

* 2022
** Read
*** : Title of Book I Haven't Finished Reading --
*** 2022-05-21: Title of Book I **Have** Finished Reading --
** Did not finish
*** : Title of Book I Won't Finish Reading --
* 2021
* Previously read, sometime
** Title of Book I Finished Reading at some indeterminate date in the past --

I keep the org file in a git repository hosted online so I can edit it on whatever computer is close at hand, or on my cell phone. (Having a git client with a simple editor on my phone is wonderful!)

When I start reading a book I create a new entry by selecting the title and author of the book on its Amazon page, use the Chrome extension Create Link to create a plain text link, which I then insert into the org file at the right heading level and with a :␢ (a colon followed by a blank space) after the asterisks of the heading and before the link. When I'm done with reading the book I put the ISO 8601 date (YYYY-MM-DD) before the colon.

I wrote a script today to count how many books I've read so far this year:

#! /usr/bin/env bash

YEAR=$(date '+%Y')

sed -E -n "/^\* $YEAR/,/^(\*\* Did not finish|\* $LAST_YEAR)/p" $READ_FILE |
    sed -e "/^* $YEAR/d" -e "/^** Read/d" \
        -e "/^(\*\* Did not finish|\* $LAST_YEAR)/d" |
    sed -E -n "/^\*\*\*[ \t]+[0-9]/p" |
    wc -l

This gets all the lines for this year, or just up to the ones that I did not finish, if there are any yet, then gets read of the leading

This works pretty well.

And so far I've read 227 books this year.

And then I got to thinking: I could probably use org-capture to automate finding the file, finding the right place to insert the information, and then copying the link from the clipboard and inserting it along with the heading formation!

Here's the org-capture-templates value I use for this:

(setq org-capture-templates
      `(("b" "Add book about to read" entry
         (file+olp ,(expand-file-name "~/Repos/tkb-org/Books/")
                   ,(format-time-string "%Y") "Read")
         "*** : %c" :prepend t)))

You'll want to read Capture templates to understand how this works.

Of course, then I thought: what if my emacs session lasts from one year into the other, and I then add a new book? It will have the wrong year!

And then I immediately thought of Advising Emacs Lisp Functions! (I feel old — it was the defadvice function when I started using it.)

So I added the following code:

  (defvar tkb-org-year (format-time-string "%Y")
    "The year the current emacs session was started, for use with org-capture.")
  (defun tkb-org-capture-advice-update-year (&optional goto keys)
    "Update ‘tkb-org-year’ and update the entry for adding a book in
‘org-capture-templates’ to use the new value."
    (let ((new-year (format-time-string "%Y")))
      (unless (string-equal tkb-org-year new-year)
        (setf tkb-org-year new-year)
        (setf (--> "b" (assoc it org-capture-templates)
                   (assoc 'file+olp it) (nth 2 it))
  (advice-add 'org-capture :before #'tkb-org-capture-advice-update-year)

I tested this by manually setting tkb-org-year to "2021" and capturing a new book. It worked fine!