~kylep/visual-cell-lang

Esoteric visual event based programming language
Various fixes and new opcodes
Fix bug with multiple results processed in single frame
Add ability to select boxes and show data

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~kylep/visual-cell-lang
read/write
git@git.sr.ht:~kylep/visual-cell-lang

You can also use your local clone with git send-email.

#Visual Cell Lang

An experimental visual programming simulation exploring event based systems.

Favors networks over trees, messages over control.

Try it here: stuff.kyleperik.com/vcl/

#Background

The idea is there ideally should be 2 languages embedded together, neither are Turing complete alone, but together they are.

The higher level language just coordinates events transmitted to and from lower level one (within cells). So very basic, can be thought of as the conveyor belts moving events and hooking up inputs and outputs.

The lower level language is what operates within the cells. Every time an input is received into the cell it runs a function which may mutate it's internal state and/or emit new outputs. The lower level lang should be a Decider (AKA total Turing machine), essentially a program which executes right away after each input, and does not allow infinite loops/recursion (therefore is not Turing complete).

#Reasoning and Inspiration

The core of this idea is that is combines imperative and functional paradigms in a way that is easier to work with. This is a proposed solution to general programming which provides a "pit of success", that is it actually makes it easy to write programs that work and aren't terrible.

While cells aren't actually purely functional, they are special in a way. Becasue they are "total turing machines", they will always complete after giving some input. Additionally, they only operate on the inputs given, and output to their consumers (no global variables).

In a way, cells are purely functional, if you think of their state and outputs as purely a function of the given inputs over time. Internal state is treated as an aggregation and mutation is just an optimization. In which case, this paradigm is a lot like Functional Reactive Programming.

In addition, this system is inspired by event sourcing, in that the state and result of a cell can be reevaluated by running the data through in the same order to get the same result every time. Events in this paradigm are "facts" in the same way, as pieces of data representing a projection or slice of data at any given time.

Also in very similar vein is Reactive FE systems such as Vuex, Redux etc.

The UI takes inspiration from the fantastic game Mindustry

I created the cell language after feeling inspired by the language uxntal by hundredrabbits. In fact I'd like to soon implement this language with uxn if possible for the sake of portability.

#Usage

All interaction takes place with the mouse alone:

  • use the dropdown below to add new cells
  • drag and drop cells or conveyors
  • click and drag from cell outputs to inputs to form a connection
  • right click to remove connections or boxes
  • user the slider at the top to change the overall speed

Your program is autosaved every 5 seconds or so.

There's a code editor on the side that you can use to define your own cells. Checkout the documentation here: Lang

#Developer setup

Make sure you have node js installed, then run

npm install
npm run serve

This will start the server, then you can connect at http://localhost:8080

#Example

Let's breakdown an example of a basic snake game.

To start, we begin with the name of our cell

snake

Then define the inputs and outputs, which are always single letters (for the gui), separated by ->.

snake
d i -> p c

At this point, your program will save, but it won't do anything.

Code boxes can do nothing by themselves, and require a layer on top to give inputs and handle outputs for the code boxes to do anything. Here we define a rough protocol which if followed, will produce the desired result.

For this cell, we require two inputs

  • d is a vector representing the direction (ultimately coming from the keyboard -> wasdDir)
  • i can be anything, it just keeps time for the game (tick plugged in here)

For outputs, p and c are both vectors which represent pixel and clear respectively. These can be routed to a screen box which will display the ps and clear the cs

Because the snake cell must keep track of it's given direction and tail, we need to initialize some data. Program initialization takes place directly after the input/output definition.

#1 int #0 int vec
#direction kep

#tail arr
#0 int #0 int vec
#tail que

This creates a vector (1, 0) and saves it under the key direction. This serves as the default direction of the snake.

Then we initialize an array under the key tail, and push a single vector (0, 0) onto that array. This serves as the starting point for the snake's position.

Finally we get to the input handlers. When inputs are recieved, cells automatically run the function that matches the name of the input.

First we'll define the input handler for d.

def d:
  val #direction kep

This simply records the given value as a vector to replace the current direction.

The final input handler is what should run on "tick" which controls most of the logic of the cell as it represents time moving forward 1 frame.

We start with a function declaration:

def i:

First, we evaluate the new head by getting the last value of the tail, and adding the direction vector.

  #tail lst #direction get vecadd

Next, we want to output that vector to be printed. But because we want to keep this value for later, we duplicate it first.

  dup #p out

Then we can queue the value onto the tail array.

  #tail que

The new head has been output and registered, now we must check to see if the oldest tail needs to be popped off. For this we must check the length of the tail.

  #tail len #10 int gth

If it is greater than 10, then the first value on the stack will be true, otherwise false. We can use this boolean to conditionally jump to a nested function which removes the last tail.

  #pop jcn

Then the declaration for pop, note that this function must be nested 2 spaces within the other function, otherwise it won't be found.

def pop:
  #tail pop #c out

This simply pops the last tail and outputs that to be cleared on the screen.

Now that's it, the snake game box is complete, here's the full snippet.

snake
d i -> p c

#0 int #1 int vec
#direction kep

#tail arr
#0 int #1 int vec
#tail que

def d:
  val #direction kep

def i:
  #direction get #tail lst vecadd
  dup #tail que
  #p out
  #11 int #tail len equ #pop jcn
  def pop:
    #tail pop #c out

snakeexample

You can load the snake example in by copying and pasting the json from this example (or others) with the "Load Program" button.

snake.json

#Credits

#Fonts used

MatchupPro, CompassPro: https://somepx.itch.io/humble-fonts-free

PixelZim by Zeh Fernando: https://www.fontzillion.com/fonts/zeh-fernando/pixelzim-3x5

mini-wakuwaku: https://www.freejapanesefont.com/mini-wakuwaku/

Proggy fonts: [http://www.proggyfonts.net/