December Adventure 2024

The following in my journal for the December adventure. The most recent post will always be a the top. You can jump to the start here.


The End

Well, I had a lot of fun doing this. But unfortunately, I kinda forgot about it. Like all things that demand routine, I eventually lose the interest to up keep them. I made a lot of progress on LVERA and learning how to use Vera to make games.

I ended up getting side tracked making a different project in Godot. The new year is here, thus the December Adventure is over.


December 22th (Day 22)

(back to latest day)

I don't think I did any coding yesterday? Did do some art.

Anyways, Chrono Inertia used a lot of Lua to iterate over my game objects. It was convient at the time, but I'm feeling myself out grow it. I have one especially nasty loop to handle bullet collisions. Additionally, I want to flatten down my object handling so I can see ways to generalize it.

Also we really need to rename "zombies" to "enemies". They aren't really zombies anymore.

I decided my first targets would be some easy loops for drawing and moving zombies:

|#port, on draw zombies, needs, @draw zombies|
|#port body, on draw zombies, lua|
    local zombies = state.zombies
    for _, zombie in ipairs(zombies) do
        love.graphics.setColor(0.145, 0.133, 0.169)
        love.graphics.circle("fill", zombie.x + 5, zombie.y + 5, zombie.r)
        
        love.graphics.setColor(0.882, 0.314, 0.282)
        love.graphics.circle("fill", zombie.x, zombie.y, zombie.r)
        love.graphics.setColor(1, 1, 1)
    end

|#port, on delete killed zombies, needs, @delete killed zombies|
|#port body, on delete killed zombies, lua|
    local zombies = state.zombies
    local survivers = {}
    for _, zombie in ipairs(zombies) do
        if not zombie.dead then
            table.insert(survivers, zombie)
        end
    end
    state.zombies = survivers

|#port, on track player, needs, @track player|
|#port body, on track player, lua|
    local player = state.player
    local zombies = state.zombies
    for _, zombie in ipairs(zombies) do
        zombie.angle = math.atan2(player.y - zombie.y, player.x - zombie.x)
    end

|#port, on move zombies, needs, @move zombies|
|#port body, on move zombies, lua|
    local zombies = state.zombies
    for _, zombie in ipairs(zombies) do
        zombie.x = zombie.x + state.dt * math.cos(zombie.angle) * 310
        zombie.y = zombie.y + state.dt * math.sin(zombie.angle) * 310
    end

|#port, on do player collisions, needs, @do player collisions|
    @player hit

|#port body, on do player collisions, lua|
    local player = state.player
    local zombies = state.zombies
    for _, zombie in ipairs(zombies) do
        if not zombie.dead then
            local min_distance = player.r + zombie.r
            local distance = math.sqrt((player.x - zombie.x) ^ 2 + (player.y - zombie.y) ^ 2)
            if distance < min_distance then
                counters["@player hit"] = 1
                zombie.dead = true
                break
            end 
         end
    end
    

I don't like there ports for a few reason:

The first one is just uncessary waste. The second one allows me to thin my ports out, reducing them to the smallest bits of logic I need to do in Lua. Finally, the last point lets my write out my logic using "plain english" rather than code.

I ended up making the process harder than it needed to be cause I wanted to implement comparisons in Vera. But, after debugging that, the rest flowed smoothly. I'm quite happy with the code describing my looping process:


|_| Generic loop set up and tear down
|set up zombies loop|
    , @zombie index
    , @get zombie count
    , compare @zombie index to @zombie count

|clean up zombies loop|
    , clear @zombie index
    , clear @zombie count


|_| The loop needed for drawing zombies
|drawing zombie, @zombie index > @zombie count|
|drawing zombie, @zombie index = @zombie count|
    , @draw zombie

|drawing zombie, @zombie index < @zombie count|
    , @draw zombie
    , draw next zombie

|draw next zombie|
    , @zombie index
    , drawing zombie
    , compare @zombie index to @zombie count

|clean up draw zombies loop| 
    , clean up zombies loop


|_| trigger for drawing zombies
|draw zombies|
    , clear @zombie index
    , set up zombies loop
    , drawing zombie
    , clean up draw zombies loop

|_| The loop needed for updating zombies
|updating zombie, @zombie index > @zombie count|
|updating zombie, @zombie index = @zombie count|
    , zombie updates

|updating zombie, @zombie index < @zombie count|
    , zombie updates 
    , update next zombie

|zombie updates|
    , zombie tracks the player
    , zombie moves towards player
    , zombie checks for collision with player

|zombie tracks the player, game is paused|
    , game is paused

|zombie tracks the player|
    , @zombie track player

|zombie moves towards player| 
    , @move zombie

|zombie checks for collision with player| 
    , @check zombie hit player

|update next zombie, @zombie hit player|
    , @player hit

|update next zombie|
    , @zombie index
    , updating zombie
    , compare @zombie index to @zombie count

|clean up update zombies loop| 
    , clean up zombies loop

|_| trigger for updating zombies
|update zombies|
    , clear @zombie index
    , set up zombies loop
    , updating zombie
    , clean up update zombies loop
    

Without Mira, generalizing this further is gonna take some deeper redesigning. I don't want to do that just yet, tho. Yumaikas might have just found the missing piece we need to progress on Mira. Additionally, I want to clean up my other loops and implement some debugging tools.

We should definitely add some of the creature comfort code gen passes to LVERA. Having to hand write clear operations is a bit tedious.
Granted, I haven't found anything truely painful enough to warent the side tracking. The old experience with ports was so bad it made serious usage of LVERA hard. Also, hand writing constants sucked. Viznut was so tedious to do.
(back to latest day)

December 20th (Day 11 through Day 20)

(back to latest day)
Suli, a brown lizard with a diamond pattern down their tail, a pink t-shirt with a triangle and two stripes on one arm, and some sneakers, leaps downwards with multiple blades trailing behind them.
A redraw of Blade Storm (2019)

Whoops, I forgot to do my log. So, [paper shuffling] I've been busy. On the 11th, I decided to try remaking an old demo from the original Nova prototypes. It was also a remake of an old Scratch game. Well, fast forward 10 days, and it's kinda ballooned into a whole game:

A dot moves about the screen shooing at on coming enemies. The screen periodically says words like pause, halt, stop, ect when the player activates the pause ability.
Chrono Inertia
I also started work on a sideventure in Godot. I've been wanting to make a little VN thing again.

It honestly started out as an accident. My pause system was bugged and the player could keep shooting. But, then I realized it was kind fun, so I leaned into it. Despite being so early into development, adding features and refining my game was quite easy with LVERA.

I have plans to develop Chrono Inertia out into a fuller game. Quality of life improvements, enemy variety, and special effects. I've also got some idea for better entity handling. I didn't expect LVERA to be so powerful so earily into it's existance, but it's got me gunning for a full release of Chrono Inertia.

I don't think I could be happier about getting derailed.

(back to latest day)

December 10th (Day 10)

(back to latest day)
A lizard clings to a blue moon that says LVERA on it. The moon is a reference to the Lua programming language.
The LVERA icon and mascot

Today was a boat load of rewriting (pun intended) of LVERA. I've finally cleaned up it's fact representation. Additionally, I removed computing the initial state of the program from the parser to the compiler, so the parse step is even simpler.

This change also meant removing the hack and work arounds I had been using for annotations as well as constants. I'll have to rebuild them in the code generator, but I decided to try out Wryl's approach for breaking down large constants. The idea is similiar to trying to find what coins you need to form a number. This process can turn a impossible to represent number into a more compressed form.

The compiler output is pretty neat to see. However, this process introduces noticable overhead. So, I'll be working on making the code generator aware of constants. The preformance benefits are to good to lose.

(back to latest day)

December 9th (Day 9)

I did not get enough sleep. Didn't do much. Did start a small sketch of a game. But, it was mostly just fiddling with UI in Godot until I felt I had a base I could build up from.


December 8th (Day 8)

(back to latest day)

I managed to power through a game of Tic-Tac-Toe in Maude. I don't have many positive things to say about the language. It's documentation is absurdly obtuse. Eventually, I just kinda pulled the parts I needed to make a game of Tic-Tac-Toe.

I ran into a frustrating footgun. As you go through the Maude manual, you get used to variables just binding data. But when you get to objects, they suddenly become identifiers for those objects. However, this only applies to variables who's name is longer than a letter.

I lost like an hour of my life to this asinine decision. Serious what the fuck? Why the fuck???
So, uh, no that was a me error. I uh... did op RowX . when I should have var RowX ....

I also now thoroughly appreciate Nova's eval order. Maude's order is effectively non-deterministic. By it's nature all rules are concurrently active. There is some definition of "fairness" I could never figure out. But it made reasoning about things painfully hard.

I worked around this by turning my Game object into a state machine. Transitioning it through out my application. But, anytime I looked away for just a little to long, my place was totally lost. It's definitely made me strong believer that practicle and usable rewriting systems need user defined orders.

I will say the object system was kinda nice. But, it's definitely weighed down by the rest of the language.

Also, I cannot believe this language allows you to forget to fully initialize objects... All this formal verification mumbo jumbo you let me make that mistake?

My curiousity with Maude has been sated. I'm heading back to Vera ._.

(back to latest day)

December 7th (Day 6 and Day 7)

(back to latest day)
Me, a afro'd monitor lizard, under a nice blanket. I'm playing video games on some nondescript computing device.
I decided today (Day 7) would be a rest day.
But I did do a *bit* of coding.

Last night was a bit of an intense sketch session with LVERA. I wanted to figure out a means of working with objects from Vera. Additionally, I wanted to experiment with giving Vera as much control over Lua as I could. The first step in this involved rewriting my main loop a little bit. For the text editor shown previously (Day 4 and Day 5), I was using this ugly bit of code for polling events:

function machine.on_poll_inputs(counters)
    events = 
        { key_presses = {}
        , key_releases = {}
        , text_inputed = {}
        }
    love.event.pump()
    for name, a, b, c, d, e, f in love.event.poll() do
        if name == "quit" then
            counters["@quit"] = 1
            events["quit"] = {a}
        elseif name == "keypressed" then
            events.key_presses[a] = {b, c}
        elseif name == "keyreleased" then
            events.key_releases[a] = {b}
        elseif name == "textinput" then
            table.insert(events.text_inputed, a)
        else
            events[name] = {a, b, c, d , e, f}
        end
    end
end
    

What matters is it got the job done. However, there is a lot to desire here. Firstly, this bit of code is extremely wasteful. It makes dozens of tables a frame. Additionally, it depends on Lua to gather all the events. I would rather have Vera control this dispatching. Additionally, giving control to Vera grants me a chance to reduce the garbage generated from event polling. After some sketching, I arrived at this solution:

function machine.on_start_polling(_)
    love.event.pump()
    state.event_iter = love.event.poll()
end

function machine.on_poll_input(counters)
    local name, a, b, c, d, e, f = state.event_iter()
    if name then
        local counter = "@event " .. name
        if counters[counter] then
            counters[counter] = 1
            state.event_args[1] = a
            state.event_args[2] = b
            state.event_args[3] = c
            state.event_args[4] = d
            state.event_args[5] = e
            state.event_args[6] = f
        end
    else
        counters["@no events left"] = 1
    end
end
    

This bit of code (should) be as wasteful as love.run (only wasting an iterator and the values from it). The code now constructs LÖVE's event iterator, stores it in my global state, then polls it in a different event. Each call to on_poll_input will do the following:

The other challenge was working with objects. For this task I choose to keep it simple and just work with circles. I came up with the following scheme for interacting with them:

|#port, on new circle,
        , needs, @new circle
        , takes, @new circle x
               , @new circle y
               , @new circle r
               , @new circle speed|

|#port, on draw circle, needs, @draw circle
        , reads, @circle index|

|#port, on move circle, needs, @move circle
        , reads, @circle index|
    
I also wrote some code to generate them randomly.

The idea here is that I feed @new circle the initial state of the circle. It calls out to some Lua code to construct the data. Then, I have two other built-in rules handle moving and drawing. Those two rules expect @circle index to point towards what entity you want to interact with. I feel you could generalize this further, but you will absolutely need a compiler pass for it to not suck.

This is where I had much more ambitious goals, but turns out I needed to figure out the basics first. This day will be important for later.

When I got up this morning, I decided to mess with the new event handling and add key press logging:

...
|#port, on save key pressed, needs, @save key pressed|
|#port, on print key pressed, needs, @print key pressed|
...
|draw each circle, iteration|
    , @circle index
    , @draw circle
    , draw each circle
|draw each circle|

|draw circles|
    , reset @circle index
    , copy circle count to iteration
    , draw each circle

|print fps| @print fps

|print key pressed|
    , @print key pressed

|drawing scene|
    , draw circles
    , print fps
    , print key pressed
...
|handling input, @event keypressed|
    , @save key pressed
    , spawn 100 circles

|spawn 100 circles|
    , make circles:100

|make circles, @once|
    make circle, circle count, @once
|make circles, @once|
...
    

The end result is this fun little demo:

Spawning in 100 circles each time a key is pressed. Additionally, I show the last key pressed on the screen for a bit of fun. When I stress tested this program by spawning 50,000 circles it managed to keep a solid 60fps (97FPS with vsync off). Before, that number would have been impossible for Nova.

(source code)

That's basically all for Day 6, and that small bit of coding is my Day 7. Not gonna do anything big for today. I still want to do hot reloading of rules. So, I might extend this program for my Day 8.

(back to latest day)

December 5th (Day 5)

(back to latest day)
A afro'd monitor lizard laying on the ground. It's got some holes dug, some symbols drawn, a rule list made, and is moving pebbles between the wholes. It looks down at its computer with intense focuse.
It's computing.

I decided I wasn't satisfied with my small text editor experiment. So, I went back and expanded upon it. It now supports fully improved 2D motion. I wrote about it on my mastodon account.

This originally started as a scrappy demo, but I decided I wanted to implement more than just lateral movement. My first attempt at any kind of text editing was a train-wreck. Managing my state, as well as handling the events and conditions, was a ball of barbwire. However, this time around things feel like they are going smoothly.

LVERA's built-in rules allow me to cleanly pull my events and stateful actions apart. I remember struggling to keep my code legible. Wires from different parts of my state were getting tangled everywhere. There is a good reason why my code has so many line by line comments. The actions I wanted to do were intractable from their implementation.

Using Vera as my scaffolding, I got to enjoy having a "plain english" descriptions of my logic. Then using built-in rules, I could fill in the gaps as needed bit by bit. This was a much nicer experience than having to deal with the whole hog all at once. I like to think the code for it all is pretty easy to follow:

|_| Handle text events
|push text to buffer| @push text
|pop text from buffer| @pop text
|break text| @break text

|_| Handle cursor events
|move cursor left|
    , @move cursor left

|move cursor up|
    , @move cursor up

|move cursor down|
    , @move cursor down

|move cursor right|
    , @move cursor right


|_| Draw the text buffer to the screen
|drawing scene|
    , draw buffer
    , draw cursor

|draw buffer|
    , @draw buffer

|draw cursor| 
    , @draw cursor

|_| Handle our input events
|handling inputs|
    , handle text edit
    , handle cursor motion 

|_| Do some light text processing
|handle text edit|
    , handle text input
    , handle backspace
    , handle new line

|handle text input|
    , check for text input event
    , push received text to buffer

|check for text input event|
    , @text input

|push received text to buffer, @text received| 
    , push text to buffer
    , move cursor right
    , handle text input

|push received text to buffer|

|handle backspace|
    , @is backspace pressed
    , back character from buffer

|back character from buffer, @backspace was pressed|
    , move cursor left
    , then pop text from buffer

|then pop text from buffer|
    , pop text from buffer
    
|back character from buffer|

|handle new line|
    , @is new line pressed
    , insert new line

|insert new line, @new line was pressed|
    , break text
|insert new line|


|_| Handle cursor motions
|handle cursor motion|
    , @is left arrow pressed
    , @is up arrow pressed
    , @is down arrow pressed
    , @is right arrow pressed
    , move cursor

|move cursor, @left arrow was pressed|
    , move cursor, move cursor left
|move cursor, @up arrow was pressed|
    , move cursor, move cursor up
|move cursor, @down arrow was pressed|
    , move cursor, move cursor down
|move cursor, @right arrow was pressed|
    , move cursor, move cursor right
|move cursor|
        
The section of code dedicated to handling text events. Full source of the text editor can be found on the Vega repos.

I'll will have to revisit this. Basic text editing is something my little project will need. But I need to move onto a new challenge and more important challenge: entity management.

Originally, my work and interaction with Nova started in a language called Mira. I spent a few days remaking my old zombie game using Mira. It went quiet well, until performance problems happened. Bad performance problems happened. But, these problems resulted in Vera as a solution which has been much sturdier.

However, since Mira has been put in the background, handling and managing objects in Nova has been left unexplored for a couple months now. However, this project will need the ability to manage events for entities. So, maybe it's time to resurrect zombies.nv?

(back to latest day)

December 4th (Day 4)

(back to latest day)

Continuing to hack away at Vera stuff. Late last night / early this morning, I wrote an extremely basic text inputting system in LOVE using LVERA. After some fiddling around, I have extended it to support some more editing features. The cursor can be moved through the 2D space of the text, lines can be insert and deleted as well. This is going much, much better than my first attempt.

Text editing backed by LVERA

As always, I've pushed my changes to the LVERA repo. Slowly taking steps towards my grander goals.

(back to latest day)

December 3rd (Day 3)

(back to latest day)

Today's task was getting something working in LÖVE. This is the first step toward my goal for December Adventure. After a bit of fiddling and ironing out mistakes on the Lua side of things, I finally have a hello world screen:

A black window with white text that says "Hello World from Vera".
LÖVE being primarially controlled by Vera

Before you ask, I did *not* any kind of string handling in LVERA. That will have to happen in some form. The application I want to make requires it. I've got a few, application specific, ideas for it. It definitely won't be a general approach to string for Vera nor The Nova Family. Finally, I updated the my Collatz conjecture example to show off ports a bit more.

Oh, I also did a bunch of work on the Nova wiki.

(back to latest day)

December 2nd (Day 2)

(back to latest day)
I realized dating and numbering these is redundant but oop.

Some small work done on LVERA to add support for basic importing. I haven't yet pushed those changes, and it's already past bed time so, oop. However, I spent most of the day messing with the Vera playground. Sierra, recently added support for some basic UI elements.

This opened up the door for a small group collab session where we built a etch-a-sketch like program. Then, we later hot-patched a generated Vera program to add support for external controls. Being able to add a button that restarted, what for other languages would be, a while loop is a new realm of possibilities.

I'm hoping to get started on my actual project tomorrow. I think I have all the pieces set to build something interesting.

(back to latest day)

December 1st (Day 1)

(back to latest day)

Okay, technically I'm writing this at 12:51 AM on December 2nd, but nearly everything was done on December 1st.

Everyone knows tomorrow only starts when you go to bed and wake up :V

I've ripped appart LVERA's ports, annotations. and compiler. Ports have been replaced by stubs. Annotations have been replaced by compiler passes. And the compiler backend is now a module you give to lvera:compile(backend). The end result is the following experience on the Nova and Lua sides:

|_| Convert our parity encoding back into unary,
    looping as needed.

|show x|
    , copy x to @number
    , print number

|copy x to @number|
    x -> @number:9007199254740991

|x -> @number, x| @number, temporary x
|x -> @number|
|temporary x| x

|print number| @print number

|move, 2| move, x
|move, 1| x
|move   | hailstone

|_| Compute 3x + 1
|odd, 2| odd, x:6
|odd   | hailstone, x

|_| compute the parity of x
|eval, x, 1| eval, 2
|eval, x   | eval, 1

|_| resolve our base cases 3x + 1 and x / 2
|eval, 2, 1| odd, 2, x:3
|eval      | move

|hailstone| show x, eval

|#port, on print number
        , needs, @print number, clears, @print number
        , takes, @number|

|| hailstone
|| x:27
    
package.path = package.path .. ";./src/?.lua"
local Lvera = require "lvera"
local lua_generator = require "generators.lua"

local lvera = Lvera.new()

lvera:load("samples/hailstone.nv")

lvera:add_pass(require "passes.ports" (lua_generator.ports))
lvera:add_pass(require "passes.constants")
lvera:add_pass(require "passes.eliminate-dead-code")

local compiled_lua = lvera:compile(lua_generator)

local out_file = io.open("generated.lua", "w+")
out_file:write(compiled_lua)

local machine = load(compiled_lua)()
function machine.on_print_number(_, number)
    print(number)
end

machine:run()
    

This should give me much more flexible control over extending Nova. Additionally, ports have been freed from twiddling counters by hand. My next task is probably going to be merging in other Nova files and starting some graphics. Next goal is gonna be something like:

|#include| notecards/graphics.nv

|set color to white|
    , @color r:255
    , @color g:255
    , @color b:255

|draw a circle|
    @draw a circle

|display the frame| @display

|| @circle x:400, @circle y:300,
 , set color to white
 , draw a circle
 , display the frame
    
(back to latest day)

November 27th (Day -4)

(back to latest day)

I've created this section in my wiki section for December Adventure. What I want to focus on is a bit of a double-hitter. My primary focus will be a small visual-narrative tool written in Lua. My secondary focus is refining my implementation of Nova: LVERA.

For a few years now, I've yearned for a tool that focused around turning drawings directly into interactive elements. I've tried various tools such as Bitsy, Flicksy 2, Decker. None have quite scratched the itch I have. They all have a bit too much "editor" to them. One too many menus to navigate. A weird bit of disjointedness.

I want some that feels more like a painting program than a game editor.

Each of them comes with a bit of scripting. Bitsy and Flicksy both use a bespoke and limited scripting language. These languages can do some basic logic, but I've found them cumbersome for logic-intensive projects. Decker utilizes the programming langauge called lil.

It's a compact yet powerful language. It doesn't hold you back from diving head on into complex logic. However, I've found lil pushed me into "thing like a programmer" more than I liked. I want something that doesn't force me to ponder design patterns and encapsulation.

I also had the misfortune of hitting a bug that ended my attempt at participating in Dec(k) Month. This bug has since been fixed, but I've found returning to Decker difficult after that.

However, there are a few "small tools" that have stuck with me: Flick Game and Kooltool. Flick Game presents itself as a canvas, some brushes, a fixed 16-color palette, and fixed 16-scene list. Each color can be assigned a sign to transition to when you click on it. This enables you to make tiny branching narratives.

Kooltool is a interactive tile-based world that you can navigate, explore, and edit. It's highly experimental, however, it provides fuel for the imagination. The editor provides you a sense of space and navigation. Every tile is a canvas you can draw on, automatically updating other copies. You can write text, draw, create tiles, and create entities seamlessly. It's rough around the edges, but I've been enamored with it.

Then, Devine completely blew me away with Tote. Tote is a visual rewriting system. It's fully capable of general computing while only using user created glyphs. It blended art and logic into a magical cocktail. I was completely swept off my feet.

Seeing Devine craft Tote has inspired me to try again. Additionally, I am now armed with knowledge on rewriting system. Interactive fiction relies heavily on managing and manipulating a chunk of global state to drive a story. This is a task that rewriting excells at.

Additionally, by leveraging Nova, I should have something that you could theoretically port and glue into other systems with (relative) ease.

November was supposed to be my time to build something. But, the distress from the US (that's where I live) elections washed out my motivation. However, life continues, I feel inspired. I'm going to create. Hopefully, I end up with something neat at the end.

(back to latest day)