23
\$\begingroup\$

><>, or Fish, is a two-dimensional esoteric programming language where the instruction pointer (IP) toroidally moves around the codebox, wrapping when it hits an edge.

><> has four basic movement instructions - <, >, ^, and v, which respectively mean to change the IP's direction to left, right, up and down. It also has the termination instruction ;, which halts the program.

For any program, some characters may never have the IP move through them. For example, with the program

> v
   
; <

The program starts at the >, changes direction at the v and <, and halts at the ;. The squares marked with # are never passed through:

> v
## 
; <

So if we wanted to obfuscate this program and make it harder to read, we could replace these with any printable ASCII characters and the program would still do exactly the same thing!

In this challenge, we will be working with a very limited set of <>< - Just the four movement instructions, spaces, the terminator (;) and newlines.

Your challenge is to, when given a program in this subset of ><> as input, output the same program with all characters that will never be passed through replaced with random printable ASCII characters - 0x20 to 0x7e. For each position, each printable ASCII character should have an equal chance of being printed, including ><> instructions and spaces.

Note that this means replacing all characters that are never passed through, not just spaces.

You can assume that the ><> program will always halt, and will always be a rectangle.

You may take input as a list of lines, a matrix of characters, ascii-art, etc.

The program will only ever contain said instructions.

The IP starts at 0,0, moving left to right.

Scoring

This is , shortest wins!

Testcases

In these testcases, # represent characters that should be replaced with random characters.

v; 
> v
 ^< =>
v;#
> v
#^<

; v
  v
<<< =>
;##
###
###

> v
 ; 
; < => 
> v
## 
; <

>  v
v  >
> ^ 
  >; =>

>  v
v# >
> ^#
##>;

>v> ^
v<^v<
> v>; =>

>v> ^
v<###
> v#;
\$\endgroup\$
6
  • \$\begingroup\$ I think you're missing a # in the lower left of the first example output. \$\endgroup\$
    – chunes
    Commented Aug 8, 2021 at 5:34
  • \$\begingroup\$ @chunes Fixed.. \$\endgroup\$
    – emanresu A
    Commented Aug 8, 2021 at 5:35
  • \$\begingroup\$ Can we assume that the top left cell will contain a direction, or can it be blank? If the latter, which way should we start moving? I’ve currently assumed left-to-right. Secondly, do we need to ensure that duplicate random characters are possible, ensure they don’t occur or doesn’t it matter? \$\endgroup\$ Commented Aug 8, 2021 at 20:35
  • \$\begingroup\$ @NickKennedy Your assumptions about direction are correct. Each character should be randomly chosen independentyl of all the others. \$\endgroup\$
    – emanresu A
    Commented Aug 9, 2021 at 2:16
  • \$\begingroup\$ @emanresuA thanks but which assumption about direction is correct? Is the cell always filled, or should we start left-to-right if it’s blank? \$\endgroup\$ Commented Aug 9, 2021 at 6:24

8 Answers 8

20
\$\begingroup\$

><>, 555 bytes

ffv   $@$f$+1~<
 $>i:1+?!v:a=?^}$:@@:@{@p$1+
@@:@(?$&$>~&l1=?v:
^}f-&0~~~~&<    >&1+$ff0       v
:@@=?v:@@=?^:@@=?v:@@=?v:@@=?vv>}$:@@:@{@g::0$-:@(?$~&">v<;^"&  
v/~~&</     2~~~&/ 1~~&/  0~&/>~" "&>}$:@@:@{@0&-@p:&1$3=?v&:&1=?v$@&:&0=?+&:&2=?-$
v>~~~3>          >     >      !^  !0^!   <                >0$-   >+
>  :e=?v>:{:}=?v$>     :e=?v>:{{:@}}=?v$&^
^-1}:{~<^    f~< ^-1}}@:{{~<^       f~<
>$:{{:@}}=?v>$:@@:@4[1+r]g:0(?!v0$-o
^!}:{:$f+1~<^$oa;?=  >!   ]! o!0! 
r v >l7=?v0           >   ~l]0$>$~[r
$>x0^>@:@0~(@:@)@@+?!^^
p >1^^@"~ "<
}{2*+l1= >?^

Try it online!

Note that there is a trailing space on line 11 (after o!0!).

><> programs can only access a stack of stacks for data storage, which would be difficult to use as a 2d array. Thus, this modifies the program grid to use as a 2d array, storing the input & tracing the path through it.

Additionally, the only source of randomness in ><> is the x instruction, which sends the IP in one of 4 random directions. Printable ASCII has 95 (19 * 5) characters, which is not divisible by numbers less than 5. Thus, to generate the random characters, it generates 7 random bits, joins them into a number from 0 to 128, and then checks if the number is within printable ASCII. If not, it discards it, generates a new random number, and repeats until it is.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ It would be amazing if this program was still functional after being run on its own source code - I suspect that might require a more fully functional ><> interpreter written in ><> to be a possibility... \$\endgroup\$ Commented Aug 9, 2021 at 19:13
  • 1
    \$\begingroup\$ @DarrelHoffman There are a couple of self-interpreters at the Interpret ><> question \$\endgroup\$
    – Jo King
    Commented Aug 10, 2021 at 2:19
10
\$\begingroup\$

Charcoal, 90 55 bytes

WS⊞υιP⪫Eυ⭆ι‽γ¶≔¹ηW¬⁼;ψ«≔§§υⅉⅈψ≔∨⊕⌕>^<vψηη✳⊗⊖ηψJ﹪ⅈLθ﹪ⅉLυ

Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated lines. Explanation:

WS⊞υι

Read the program.

P⪫Eυ⭆ι‽γ¶

Output a rectangle of random printable ASCII.

≔¹η

Start at the origin facing east.

W¬⁼;ψ«

Repeat until the end of the program is reached.

≔§§υⅉⅈψ

Get the current character of the program.

≔∨⊕⌕>^<vψηη

If the current character is a direction then update the direction variable.

✳⊗⊖ηψ

Replace the random character with the current character and update the position.

J﹪ⅈLθ﹪ⅉLυ

Wrap around the sides of the rectangle as necessary.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Wow, that was quite the edit \$\endgroup\$
    – Jonah
    Commented Aug 8, 2021 at 23:36
  • 2
    \$\begingroup\$ @Jonah Yeah, I suddenly had the inspiration that I was solving the problem backwards, and boom! \$\endgroup\$
    – Neil
    Commented Aug 8, 2021 at 23:45
6
\$\begingroup\$

J, 120 bytes

(([:u:32+94?@$~#@[)`[`]})~($#:,@i.@$);/@-.[:{."2]($@[|"1{.@](+,:])(]0,~((,-)=i.2),~{:){~' v>^<'i.({~<@{.))^:a:0 0,:0 1"_

Try it online!

It's late, and this one kind of got away from me, but, what the hell, looks like it works. I have some ideas to improve it tomorrow.

\$\endgroup\$
6
\$\begingroup\$

Python 3, 253 212 bytes

Takes input as a character matrix.

from random import*
def f(g):
 r=x=y=a=0;b=1;N=[[chr(randint(32,126))for _ in t]for t in g]
 while';'!=r:N[y][x]=r=g[y][x];a,b=[0,1,0,2,-1,a,-1,0,1,2,0,b][ord(r)%9%6::6];x=(x+b)%len(g[0]);y=(y+a)%len(g)
 return N

Try it online!

Commented:

from random import*
def f(g):
  r=0    # last instruction
  x=y=0  # current position in the grid
  a=0    # y-direction
  b=1    # x-direction
  # grid of random characters of the same dimensions as g
  N=[[chr(randint(32,126))for _ in t]for t in g]

  # until the last instruction was a ;
  while';'!=r:
    # update instruction and random character
    N[y][x]=r=g[y][x]
    # update direction, ord(r)%9%6 maps
    # < -> 0, v -> 1, ^ -> 2, ^ -> 4, ' ' -> 5
    a,b=[0,1,0,2,-1,a,-1,0,1,2,0,b][ord(r)%9%6::6]
    # update position, modulo takes care of the wrapping
    x=(x+b)%len(g[0]); y=(y+a)%len(g)
  # return random matrix where visited indices are replaced with the program
  return N

Try it online!

\$\endgroup\$
6
\$\begingroup\$

Jelly, 70 59 bytes

O%19ịı*Ɱ6ḊḊŻƲÆiḞŻ¤4Ṭs2¤‘œị¥@ȯƭƒ+ḷƭⱮ⁸%⁹,ZẈW¤ʋƬḢ€‘ŒṬ¬aØṖX¤€€o

Try it online!

A link that is called as a monad with a list of Jelly strings and returns a list of Jelly strings.

Here’s a version that shows # in place of the random characters.

Explanation (outdated)

Takes [[pos_x, pos_y], [dir_x, dir_y]] as its left argument and a converted grid as its right (see below) and returns an updated list of position and direction

   ¥1¦               | Do the following to the position component of the lost:
‘                    | - Add 1
 œị                  | - Multidimensional index into grid
      ȯ/             | Reduce using logical or
        ⁸            | Using the original left argument:
         +ṛƭ€        | - Add the new direction to the position, and append the new direction
             %       | Mod with:
              ⁹    ¤ | - The grid
               ,Z    | - Paired with its transpose
                 Ẉ   | - Lengths
                  W  | - Wrapped (which will mean only the position is affected)
“;>^<v”iⱮⱮ                                  | Positions of each character in the grid in ";>^<v"
          ‘                                 | Increase by 1
           ị          ¤                     | Index into:
            ı                               | - i
             *Ɱ4¤                           | - ** each of 1..4
                 Ż                          | - Prepend zero
                  Æi                        | - Convert from complex
                    Ḟ                       | - Floor
                     Ż                      | - Prepend zero
                           ¤                | Starting with:
                       4                    | - 4
                        Ṭ                   | - Untruthy ([0,0,0,1])
                         s2                 | - Split into lists length 2 ([[0,0],[0,1]])
                            çƬ              | Call the helper link with the grid as the right argument until no new value encountered
                              Ḣ€            | Head of each (i.e. positions)
                                ‘           | Add 1
                                 ŒṬ         | Multidimensional untruthy
                                   ¬        | Not
                                    aØṖX¤€€ | And with a list of lists of random printable characters for each original character in the grid
                                           o| Logical or with original grid
\$\endgroup\$
5
\$\begingroup\$

R, 217 175 178 173 155 154 bytes

function(A,B=A,j=1:0,i=t(j+!j)){B[]=intToUtf8(runif(A,,95)+32,T);while(";"!=(B[i]=a=A[i]))i=(i-(j=switch(a,">"=1:0,"<"=1:2,v=0:1,"^"=2:1,j)))%%dim(A)+1;B}

Try it online!

Thanks to @Nick Kennedy for very nice golfs resulting in -42 bytes (!) and clarifying some issues with the OP.

-5 bytes inspired by @Neil's Charcoal answer, but as I understand this approach is also used in some other answers.

Thanks to @Dominic van Essen for -17 bytes.

\$\endgroup\$
11
  • \$\begingroup\$ Hi, few comments: should be from 32 to 126 not 20; using dim, [ and prod for the dimensions saves a byte; sampling probably should be with replacement, though I agree that the question is ambiguous on this; doesn’t work if the top left cell is blank, though again question ambiguous on this (using F and T in place of w and h would solve this without costing bytes). \$\endgroup\$ Commented Aug 8, 2021 at 20:34
  • 1
    \$\begingroup\$ I’ve asked about the last two points, but presuming you’re allowed to assume a direction in the top-left cell and no dupes of random characters (and size < 94, also implied by your answer), here’s a shorter version \$\endgroup\$ Commented Aug 8, 2021 at 21:47
  • 1
    \$\begingroup\$ If we need to allow duplicate characters and cannot assume a non-blank top-left, this fixes those \$\endgroup\$ Commented Aug 8, 2021 at 21:49
  • \$\begingroup\$ @Nick thanks, nice trick with the column vector for indexing, I didn't know it works that way. \$\endgroup\$
    – pajonk
    Commented Aug 9, 2021 at 5:46
  • 1
    \$\begingroup\$ @Nick, right, thanks. Fortunately it didn't cost too many bytes ;-) \$\endgroup\$
    – pajonk
    Commented Aug 9, 2021 at 12:10
3
\$\begingroup\$

Excel VBA, 400 bytes

    Sub f()
    Set a=UsedRange
    m=a.Rows.Count
    n=a.Columns.Count
    r=1
    c=1
    Set u=[A1]
    While e=0
    Set x=Cells(r,c)
    Set u=Union(u,x)
    Select Case x
    Case">":s=0:d=1
    Case"<":s=0:d=-1
    Case"v":s=1:d=0
    Case"^":s=-1:d=0
    Case";":e=1
    End Select
    r=r+s
    c=c+d
    If r<1 Then r=m
    If r>m Then r=1
    If c<1 Then c=n
    If c>n Then c=1
    Wend
    For Each b In a.Cells
    If Intersect(b,u)Is Nothing Then b.Value=Chr(Int(113*Rnd+14))
    Next
    End Sub

Formatted code with comments explaining function:

    Sub f()
    
        Set a = UsedRange   'Save this here for shorter reference later
        m = a.Rows.Count    'Ditto
        n = a.Columns.Count 'Ditto
        r = 1               'Row
        c = 1               'Col
        Set u = [A1]        'Prime the collection with the first cell
        While e = 0
            ' Pick the cell we're going to examine and add it to the collection
            Set x = Cells(r, c)
            Set u = Union(u, x)
            
            ' Change the row / column shift if we hit a control character
            Select Case x
                Case ">": s = 0:  d = 1
                Case "<": s = 0:  d = -1
                Case "v": s = 1:  d = 0
                Case "^": s = -1: d = 0
                Case ";": e = 1   'Triggers the end of the While loop
            End Select
            
            ' Shift the row / cell and correct for edge (heh) cases
            r = r + s
            c = c + d
            If r < 1 Then r = m
            If r > m Then r = 1
            If c < 1 Then c = n
            If c > n Then c = 1
        Wend
        
        ' Loop through all the cells in the used range
        For Each b In a.Cells
            ' If that cell was not collected when moving through the program, fill it with a random character
            If Intersect(b, u) Is Nothing Then b.Value = Chr(Int(113 * Rnd + 14))
        Next
        
    End Sub

Input is one character per cell:

Input

Output is editing in-place:

Output

\$\endgroup\$
2
  • \$\begingroup\$ I was able to thin this down to 343 bytes link \$\endgroup\$ Commented Aug 16, 2021 at 17:33
  • \$\begingroup\$ @TaylorScott As far as I can tell, that works. I knew the SELECT CASE added bloat but I wouldn't have thought of the nested equalities you used or IIF which I haven't used since I was building Access databases. I welcome you to edit my post since I can't fully explain your code and certainly not as well. You can make it a wiki if you wish. \$\endgroup\$ Commented Aug 16, 2021 at 18:30
2
\$\begingroup\$

JavaScript (Node.js),  194  188 bytes

Expects and returns a matrix of characters.

m=>m.map((r,Y)=>r.map((C,X)=>(g=x=>(x%=W)-X|(y%=H)-Y?~(c="^<v> ".indexOf(m[y][x]))?g(x+(c<4?(v=--c%2,h=~-c%2):h)+W,y+=v+H):Buffer([Math.random()*95+32])+'':C)(W=m[0].length,y=H=m.length)))

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.