We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
User:PkmnQ/Wrong Wrap
Wrong Wrap is a two-dimensional esolang. The name is a double entendre, referring to both the language's unusual wrapping behavior and the term "wrong warp".
Structure
A program is made up of different areas, which have a width (a positive integer) and a content (a string). The command at a certain location is given by content[y*width+x]. If y*width+x is negative or greater than or equal to the length of the content, the command at that location is a NOP.
For example, say you have an area with a width of 3 and a content of ABCDEFGHI. Then, the command at (1, 2) would be content[2*3+1], which ends up being H. If you lay out the commands at every single location, you can observe how this creates a pseudo-wrapping behavior ((0, 0) is marked in bold red):
ABC...
ABCDEFGHI
ABCDEFGHI
ABCDEFGHI
...GHI
Switching between different areas with different widths while keeping the same position can lead an instruction pointer going far into the NOP region, creating a sort of "wrong warp" effect.
Syntax
Each area is represented by one line of the program. The line starts with decimal digits specifying the area's width, followed by a space, with the rest of the line's content being interpreted as the area's contents (The content may contain decimal digits and spaces of its own).
Behavior
There are two instruction pointers. They both always share the same area; if one instruction pointer switches the area, it affects both instruction pointers, not just the one that switched the area. They both start at (0, 0), but one starts going right, one starts going down. Each program step goes as follows:
- Both instruction pointers find the command at their current position in the current area.
- They both execute their observed commands. (This means that changes in area are not observed until the next program step.)
- For the I/O extension, since ordering matters, the IP that starts going rightward has priority over the IP that starts going downward.
- They move one step in their current direction.
- If they end up on the same location, the program halts. (The fact that they both start at (0, 0) is ignored, since this is only checked at the end of a program step.)
| Command | Action |
|---|---|
| > | Set this instruction pointer's direction to right (x increases by 1 each step). |
| < | Set this instruction pointer's direction to left (x decreases by 1 each step). |
| ^ | Set this instruction pointer's direction to up (y decreases by 1 each step). |
| v | Set this instruction pointer's direction to down (y increases by 1 each step). |
| # | Switch to the next area while keeping the current position and direction of both instruction pointers. If already on the last area, loop back to the first. If both instruction pointers run a # at the same time, the area is switched twice. |
Anything else is a NOP.
I/O extension
Extra commands are given for I/O in a similar manner to Black; digits output themselves, and N outputs a newline.
Examples
Simple example
3 >v ^<> # 4 #>35#1#4##2^
The areas "unrolled" are as follows:
>v ^< > #
#>35 #1#4 ##2^
In the first area, one instruction pointer enters a loop, while another makes a short journey to a # instruction. When the area switches to the second one, one instruction pointer is at (1, 1) going up, while the other is at (2, 2) going right. In the second area, this leads to them running directly into the arrows. The 1 and 2 are not run since the instruction pointers move before they can be observed, but the 3 and 4 are, leading them to colliding at (3, 0), where the program halts. The 5 is never observed, so the output is simply 34.
To aid with testing, the full trajectory of both instruction pointers are:
(0, 0) -> (1, 0) -> (2, 0) -> (2, 1) -> (1, 1) -> (1, 0) -> (2, 0) -> (3, 0) (0, 0) -> (0, 1) -> (0, 2) -> (1, 2) -> (2, 2) -> (3, 2) -> (3, 1) -> (3, 0)
Interpreter
Here is a basic Python interpreter:
import itertools
def get_command(width, area, x, y):
position = y * width + x
return area[position] if 0 <= position < len(area) else "NOP"
program = [(3, " >v ^<> #"), (4, "#>35#1#4##2^")]
area_iterator = itertools.cycle(program)
width, area = next(area_iterator)
ip_1, ip_2 = (0, 0), (0, 0)
dir_1, dir_2 = (1, 0), (0, 1)
while True:
command_1, command_2 = get_command(width, area, *ip_1), get_command(width, area, *ip_2)
if command_1 == ">":
dir_1 = (1, 0)
if command_1 == "<":
dir_1 = (-1, 0)
if command_1 == "^":
dir_1 = (0, -1)
if command_1 == "v":
dir_1 = (0, 1)
if command_1 == "#":
width, area = next(area_iterator)
if command_1.isnumeric():
print(command_1, end="")
if command_1 == "N":
print()
if command_2 == ">":
dir_2 = (1, 0)
if command_2 == "<":
dir_2 = (-1, 0)
if command_2 == "^":
dir_2 = (0, -1)
if command_2 == "v":
dir_2 = (0, 1)
if command_2 == "#":
width, area = next(area_iterator)
if command_2.isnumeric():
print(command_2, end="")
if command_2 == "N":
print()
ip_1, ip_2 = (ip_1[0] + dir_1[0], ip_1[1] + dir_1[1]), (ip_2[0] + dir_2[0], ip_2[1] + dir_2[1])
if ip_1 == ip_2:
break
See also
- Liquid DownRight, which also takes advantage of the same wrapping behavior