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 |


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 ID001
and operation+126
[002:+32]
: An arrow with ID002
and operation+32
[003:-1]
: An arrow with ID003
and operation-1
[004:O(0)]
: An arrow with ID004
and operationO
(Output character)[005:>1]
: An arrow with ID005
and operation>
(move pointer by 1 to the right)[006:<1]
: An arrow with ID006
and operation<
(move pointer by 1 to the left)[007:i(32)[<:004e][<:005w][<:004w][<:006s][<:004s][<:010n][<:004n][<:009n][<:003s]]
: An arrow with ID007
and operationi(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 ID008
and operation+119
[009:<2]
: An arrow with ID009
and operation<
(move pointer by 2 to the left)[010:>2]
: An arrow with ID009
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 fordo the operation and then nothing
(
, which meansdo 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.