Undyne Programming Language

From Esolang
Jump to navigation Jump to search
Undyne Programming Language
Designed by Joachim Rey
Appeared in 2025
Memory system Cell-based
Dimensions one-dimensional
Computational class Unknown
Reference implementation Unimplemented
Influenced by Brainfuck
File extension(s) .upl
UPL's logo
Execution of a Hello World program in UPL

Undyne Programming Language (UPL) is a programming language designed to resemble a battle against Undyne in the game Undertale. It shares similarities with Brainfuck but differs in its execution and syntax.
UPL uses an array (memory) with a minimum length of 64. Each value in this array is an unsigned 32-bit integer. When the program starts, all values are set to 0.
A UPL program is divided into two files: Head and Quiver. The Head file contains the definition of the operations (referred to as arrows), and the Quiver file defines how to use the arrows. There can be a maximum of 1000 arrows defined at any given time, ranging from 000 to 999. Arrow 000 is the default arrow; its usage is possible but not recommended as it can be confusing in an already complex program.
The program works by launching arrows at the player. The player has a shield and needs to defend themselves. The arrows are executed when they touch the shield, but if the player is hurt, the program is terminated.

Arrows creation

Arrows are defined in the Head file. The general syntax of an arrow is:

[ARROW ID:OPERATION ADDITIONAL INFO]

Spaces and carriage returns are not considered as characters (ignored), so you can abuse of them if it can make you program easier to read and understand (both in Head and Quiver files). There are six types of definable arrows for a total of seventeen possible commands.

Arithmetic operations arrows

Arithmetic operations arrows are defined using the following syntax :

[ARROW ID:+AMOUNT] [ Plus ]
[ARROW ID:-AMOUNT] [ Minus ]
[ARROW ID:*AMOUNT] [ Product ]
[ARROW ID:^AMOUNT] [ Power ]
[ARROW ID:/AMOUNT] [ Fraction ]
[ARROW ID:%AMOUNT] [ Modulo ]

They all return integers. Examples:

[001:+89754]
[002:-1]

> and < arrows

> and < arrows are defined using the following syntax :

[ARROW ID:>AMOUNT] [ Move memory pointer by AMOUNT to the right ]
[ARROW ID:<AMOUNT] [ Move memory pointer by AMOUNT to the left ]

Examples:

[001:>89754]
[002:<1]

If the amount is out of the range of the memory, the pointer is placed at the end or at the start of the memory.

I and O arrows

I (Input) and O (Output) arrows are defined using the following syntax :

[ARROW ID:I(0)] OR [ARROW ID:I(1)]
[ARROW ID:O(0)] OR [ARROW ID:O(1)]

Parentheses stand for the parameter of the I/O function. A parameter of 0 means the I/O is a character, and 1 stands for an integer.
For practical reasons, I will pause the program until the player input something. If you need multiple inputs (like a string) you can call multiple time the I arrow to only pause the program once. Example (in Quiver)(001 is a I arrow):

[<:001][<:001][<:001][<:001]

This will pause the program until the player have inputted four inputs. The inputs will be stored from current memory cell to current memory cell + 3. This syntax is equivalent to (002 is a > arrow):

[<:001][<:002][<:001][<:002][<:001][<:002][<:001]

which will pause the program each time to move the pointer to the right.

i arrows

i arrows are defined using the following syntax :

[ARROW ID:i(STOP)OPERATIONS]
[ARROW ID:i(STOP)OPERATIONS]

i arrows are while/if operations. For example:

[001:i(16)[<:001]]

This defines an i arrow with ID 001 that will execute arrow 001 with effect < while the current cell value is not 16. There can be an infinite number of operations.

# arrows

# arrows are memory pointers to a memory cell. Their syntax is:

[ARROW ID:#CELL LOCATION]

Their usage will increment the current cell's value by the pointed cell value.

! arrows

! arrows modify environment settings, their syntax is:

[ARROW ID:!N AMOUNT] [ This change the number of cells in the memory to the specified amount ]
[ARROW ID:!S SIZE] [ This will change all the cells values to signed integers of size SIZE ]
[ARROW ID:!U SIZE] [ This will change all the cells values to UNsigned integers of size SIZE ]
[ARROW ID:!s AMOUNT] [ Change the speed of all arrows ]
[ARROW ID:!n AMOUNT] [ Change the number of arrows displayed on the screen at the same time ]

Comments

Comments can be add to the code with square brackets enclosing anything which is not starting with a number or an arrow effect:

[ This is the recommended way to add comments ]

Comments works in both Head and Quiver files.

Example of a Head file

[001:+126][002:+32][003:-1][004:O(0)][005:>1][006:<1][007:i(32)[<:004e][<:005w][
<:004w][<:006s][<:004s][<:010n][<:004n][<:009n][<:003s]][008:+119][009:<2][010:>
2]

This will create the ten following arrows:

  • [001:+126] : An arrow with ID 001 and operation +126
  • [002:+32]  : An arrow with ID 002 and operation +32
  • [003:-1]  : An arrow with ID 003 and operation -1
  • [004:O(0)] : An arrow with ID 004 and operation O (Output character)
  • [005:>1]  : An arrow with ID 005 and operation > (move pointer by 1 to the right)
  • [006:<1]  : An arrow with ID 006 and operation < (move pointer by 1 to the left)
  • [007:i(32)[<:004e][<:005w][<:004w][<:006s][<:004s][<:010n][<:004n][<:009n][<:003s]] : An arrow with ID 007 and operation i(32) (while the current memory cell's value is not 32, call arrows [<:004e], [<:005w], [<:004w], [<:006s], [<:004s], [<:010n], [<:004n], [<:009n] and [<:003s])
  • [008:+119] : An arrow with ID 008 and operation +119
  • [009:<2]  : An arrow with ID 009 and operation < (move pointer by 2 to the left)
  • [010:>2]  : An arrow with ID 009 and operation > (move pointer by 2 to the right)

Note that the order does not matter when creating arrows or assigning them IDs. For example:

[512:>5][001:i(0)[<:856n]][856:-1]

Is a correct Head file.

Program execution

The program is executed by calling the arrows defined in the Head file, as specified in the Quiver file. The general syntax for arrow calls is:

[EFFECT:ARROW ID SHOOTING SIDE]

The shooting side (side of the screen) can be n (north), s (south), e (east), and w (west). This information can be omitted, but if omitted, the arrow will be shot from a random side. There are two effects:

  • <, which stands for do the operation and then nothing
  • (, which means do the operation and then destroy the arrows in the opposite direction for ten iterations

For instance:

[<:001][<:002][<:003]

will call arrows 001, 002, and 003. If they successfully touch the player's shield, their operation will be executed.

[(:001n][<:001w][<:001e][<:001w][<:002s][<:001n][<:001n][<:001w][<:001e][<:001w][<:001e][<:003s]

will execute 001n, do its operation, then delete arrow [<:002s] because it is in the opposite direction (south). It will not delete [<:003s] as it is out of the ten-arrow range.
The ( effect could be used for if-else statements.

Program end

The program ends when the player dies (touched by an arrow) or when there are no more arrows (empty quiver).

Display

The program displays on a 640 by 480 window, with a heart in the middle (player), a shield and a terminal like interface at the bottom (dialogue box). Arrows are shot from the four sides of the window as defined in the Quiver file. Arrow textures and sounds when touching the shield need to be defined in the Sounds & Arrows folders. An additional Dashs folder can be added for the ( effect. All these folders and files are compressed in a .upl file (zip file). Textures are in png/apng formats and sounds use ogg.
Here is the complete file hierarchy. Note that the Head and Quiver files are the only compulsory files. All other files are optional.

MyProgram.upl/
├─ Arrows/
│  ├─ 000.png
│
├─ Dashs/
│  ├─ 000.png
│
├─ Sounds/
│  ├─ 000.ogg
│
├─ Quiver
├─ Head  

000 files are the default texture, sound, and dash. You could only create 007.png in Arrows, 002.png in Dash, and 010.ogg in Sounds, and the program would still work fine, but all other arrows would not be displayed or have sound.

Example of a simple program

Head:

[001:+126][002:+32][003:-1][004:O(0)][005:>1][006:<1][007:i(32)[<:004e][<:005w][
<:004w][<:006s][<:004s][<:010n][<:004n][<:009n][<:003s]][008:+119][009:<2][010:>
2]

Quiver:

[<:001s][<:005e][<:008w][<:005n][<:002s][<:009e][<:007w]

Output:

~w~ }w} |w| {w{ zwz ywy xwx www vwv uwu twt sws rwr qwq pwp owo nwn mwm lwl kwk jwj iwi hwh gwg fwf ewe dwd cwc bwb awa `w` _w_ ^w^ ]w] \w\ [w[ ZwZ YwY XwX WwW VwV UwU TwT SwS RwR QwQ PwP OwO NwN MwM LwL KwK JwJ IwI HwH GwG FwF EwE DwD CwC BwB AwA @w@ ?w? >w> =w= <w< ;w; :w: 9w9 8w8 7w7 6w6 5w5 4w4 3w3 2w2 1w1 0w0 /w/ .w. -w- ,w, +w+ *w* )w) (w( 'w' &w& %w% $w$ #w# "w" !w!

The Head has already be explained in the Arrows creation section, but here is a pseudo-code translation of the Quiver.

Add 126 to current cell
Move the pointer by one to the right
add  119 to current cell
Move the pointer by one to the right
add 32 to current cell
Move the pointer by two to the left
While current cell value is not 32 do:
   Output character
   Move the pointer by one to the right
   Output character
   Move the pointer by one to the left
   Output character
   Move the pointer by two to the right
   Output character
   Move the pointer by two to the left
   Remove 1 from current cell

Computational class

UPL is Turing complete, as demonstrated here: Undyne Programming Language Turing-completeness Proof. It is also Plushie-complete.

External resources

  • Portable Network Graphics (PNG) - fmt/13
  • ZIP Format (zip) - x-fmt/263
  • Animated Portable Network Graphics (APNG) - fmt/935
  • Ogg Multimedia Container (ogg) - fmt/944