LVERA

A drawing of a Komodo Dragon clinging to the back of a blue moon. The moon has the LVERA writte on it.

The LVERA Logo

LVERA (/ˈlueː.ra/) was on of the old prototypes for Nova at the time called Vera. Programs in LVERA menipulated labelled counters. It was basically a nice skin over a counter machine.

|add x to y, x|
    add x to y, y
|add x to y|

Additionally, to make it fast, LVERA used an optimization discovered by Yumaikas: "Min Optimization". Min Optimization was a way to preform that "maximum" amount of work ever rule. When a rule had multiple conditions,

|a, b, c| d

Min Optimzation would fin the "smallest" counter, then update all other counters by that value.

local work = math.min(a, b, c)
    a = a - work
    b = b - work
    c = c - work
    d = d + work

This made LVERA fast enough to run a whole game: Chrono Inertia .

Depsite such minimalist design and esoteric foundation, code could be facinatingly literate.

|_| The main application loop
|run main loop|
    , start polling inputs
    , poll input
    , handle input
    , run current frame

Challenges of LVERA

A core down side to Vera, was how coarse it was to work in. The only data you had was labelled counters. Everything else was externalized in cumbersome ways.

Code was unusual. If you wanted to transfer values between counters, you needed to load up a "work" counter with the maximum interger value of Lua. Then, a rule would operate off that "work" counter.

|_| We have to supply enough gas for 
    this to be O(1) under Min Optimization.
|move x to y|
    (running) move x to y:9007199254740991

|_| The actual work to be done
|(running) move x to y, x|
    (running) move x to y, y
|(running) move x to y|

|| x:12345, move x to y

Min Optimization additionally made one-by-one computation tricky. For example, this roman numeral program utilizes a token called @once. This token is a normal counter, however it exist to prevent Min Optimzation from occuring.

|C 100, C| C 200
|C 200, C| C 300
|C 300, C| CD 400

|CD 400, D 500| CM 900
|CM 900, C| M
|CD 400, C| D 500

|X 10, X| X 20
|X 20, X| X 30
|X 30, X| XL 40

|XL 40, L 50| XC 90
|XC 90, X| C
|XL 40, X| L 50

|I 1, I| I 2
|I 2, I| I 3
|I 3, I| IV 4

|V 5, IV 4| IX 9 
|IX 9, I| X
|IV 4, I| V 5

|@once, I| @once, I 1
|@once, X| @once, X 10
|@once, C| @once, C 100

|| @once, I:999999

Without the @once counter, this code would catastrophically break. I would be completely converted to I 1 before any of the above rules could process and reduce it. This side effect made iterators tricky to handle.

A Tangent About Mira

The previous and first prototype of Nova (from now on out called Mira) utilized a tuple space. This space was unordered and unstructure. It was fun cause this made code feel magical. However, when you needed hard, sequentual logic with multiple computations. It stopped being magical.

Internally Mira was structred as a prefix tree, but from a programming perspective it was effectively unorderd.

Rules search for data. This data would then trigger the production of new information. As mention, data is unordered. This means that you effectively have non-deterministic execution of all commands. This led to me writing highly defensive code.

The worst example from an old project:

compute new enemy $id location using $vx-token $vy-token
, tick $dt?
, $id at x $x and y $y
, @expr success $vx $vx-token
, @expr success $vy $vy-token:
    , complete enemy $id update using $x-token $y-token
    , @expr eval $x-token [%1 + %2 * %3] $x $vx $dt
    , @expr eval $y-token [%1 + %2 * %3] $y $vy $dt
    .

Most of these variables (anything ending in "token") exist to coordinate effectively parrallel computations. It was not pretty, additionally, it was catostrophically slow (exponential time) leading to the creation of the Vera model.

Vera Also Lacked Structure

Vera also lacked structure. The only order was the order of rules from top-to-bottom. There was no way to impose order-of-access to counters. However, the nature of text and the syntax of Vera at the time, lead to an illusion of that control. For example:

|a, b| eaten a and b
|b, a| eaten b and a
|| b, a

The above code appears to match for |b, a| however, the order of facts does not matter. Only the order of rules. At times, this made code hard to refactor. In Chrono Inertia, I eventually adopted this pattern:

|_| Dummy event handlers
|handling input|
|updating scene|
|drawing scene|

|run current frame|
    , handle input
    , update scene state
    , draw scene

|_| These rules define the order of execution.
    Their resulting consequences can be in
    any order, so long as they are above the
    dummy handlers.
| handle input          | handling input
| update scene state    | updating scene
| draw scene            | drawing scene

End of LVERA

LVERA was a neat project, and I even made a game with it. But, the above limitations, challenges, and quirks made pushing it further a challenge. There was no means of handling data, no means of indirection, and generalization. This made using LVERA for anything more than a coordiation language increasingly difficutl.

Additionally, a goal for the Nova project is to be "bootstrappable". For Vera, this would mean literally defining a computer arecture from circuits. For example, to implement just *one* array, you'd need to elaborate every single cell as rule.

My work on Nova has now been focused on the new implementation based around manipulating multiple stacks each containing arbatrary symbols.