23
\$\begingroup\$

Write a function or program which writes an input string out as a snake.

The snake starts heading to the right. If a / or \ character is reached in the input, the snake changes direction as if the character represented a wall placed diagonally across the path.

That is it changes direction as follows (before -> after):

  • /: right -> up; up -> right; left -> down; down -> left
  • \: right -> down; down -> right; left -> up; up -> left

Every character other than / or \ is output literally.

So given the input sn/ak\e the output is:

ek 
 a
sn

If the snake crosses its own path, the later characters replace earlier ones:

boa/co\ns/t\rictor

sno
trictor
boa

Input

A string of characters from the set [-a-zA-Z_/\]. There may be consecutive direction change characters (/ and \) including of the same type (////). The first character may be a direction change character. There will be at least one non-direction-change character.

Output

Multiple lines of output as described above, in any reasonable format. Extra whitespace is not allowed:

  • Each line must contain the minimal amount of left-padding such that (at least) one line has zero left padding.
  • There should be either no right-padding, or the minimal amount to produce lines of equal length.
  • No extra blank lines are allowed at the start or end. The final new line character is optional.

Scoring

This is code golf. Standard rules apply.

Sample output

/\backwards

sdrawkcab

_oo/o/oOoo\o\oo/O/_

 ooOoo O_
_oo ooo

TigeR\attlE\gg-eateR/ibboN/ose-horneD\warf-beakeD/esertWomA\nacondA\ddeR/aceR/aT\aipan

TigeR Nose-horneD
 a o w
 t b a
 t n b r
 l a ReddA f
 EggpeateRd -
 i c n b
 a e o e
 TaR c a
 a k
 n e
 AmoWtreseD

\Mamba/Nu\mberF\ive

eviF
 r M
 e a
 b m
 m b
 uNa

There/\and\/back

Tdback
asked Apr 11 at 4:07
\$\endgroup\$
1
  • \$\begingroup\$ for TigeR\attleE... I think you have an extra e in the input (or are missing an e in the output) at the end of Rattle \$\endgroup\$ Commented Apr 14 at 0:46

10 Answers 10

11
\$\begingroup\$

Charcoal, 18 bytes

F⮌S≡ι\‖↗/‖↘←∨−KKψι

Try it online! Link is to verbose version of code. Explanation: Charcoal really wants to know the direction to print before printing the character, so having the \/ after the character is really bad for it. Furthermore, although Charcoal has a function to rotate the pivot, it doesn't have one to reflect the pivot, so a separate variable would be needed to track the direction. All in all it turns out to be easier to print the snake tail first.

F⮌S≡ι

Switch over each input character in reverse order.

\‖↗

For a \ reflect the output so far along the main diagonal.

/‖↘

For a / reflect the output so far along the antidiagonal.

←∨−KKψι

Otherwise, output the current character, but keeping any character previously printed (as that one is later in the string and later characters have priority), then move left.

Would be 16 bytes if the turns were marked before the turning letter:

F2Fθ≡κ\‖↗/‖↘¿¬ικ

Try it online! Link is to verbose version of code. Explanation:

F2

Repeat twice. The second pass is necessary to reflect the canvas back into the original direction.

Fθ≡κ\‖↗/‖↘

Handle turns as above.

¿¬ικ

Only print the characters on the first pass.

answered Apr 11 at 8:45
\$\endgroup\$
1
  • \$\begingroup\$ Charcoal is really suitable for challenges of this type. \$\endgroup\$ Commented Apr 13 at 5:18
8
\$\begingroup\$

Google Sheets, (削除) 391 (削除ここまで) 382 bytes

-9 bytes thanks to @doubleunary

=INDEX(LET(y,MID("/"&A1,ROW(1:999),1),q,FILTER({REDUCE({1,"i"},QUERY(y,"offset 1"),LAMBDA(a,c,LET(l,CHOOSEROWS(a,-1),w,INDEX(l,1),e,INDEX(l,2),{a;IF(c="/",{w,IMDIV("-i",e)},IF(c="\",{w,IMDIV("i",e)},{IMSUM(w,e),e}))}))),y},REGEXMATCH(y,"[^\\/]")),d,INDEX(q,,1),F,LAMBDA(a,SEQUENCE(MAX(a)-MIN(a)+1,1,MIN(a))),XLOOKUP(COMPLEX(F(IMREAL(d)),TOROW(F(IMAGINARY(d)))),d,INDEX(q,,3),,,-1)))

Expects input in A1.

enter image description here

It works by keeping track of the current position and the current direction as complex numbers.

Number Direction
\$+i\$ right
\$-i\$ left
\$+1\$ down
\$-1\$ up

The direction is initialized to \$+i\$ (right).

When we encounter \, we get the new direction by swapping the real and imaginary parts of the previous direction.

Prev direction New direction
\$+i\$ \$+1\$
\$-i\$ \$-1\$
\$+1\$ \$+i\$
\$-1\$ \$-i\$

When we encounter /, we get the new direction by swapping the real and imaginary parts of the previous direction and changing the sign.

Prev direction New direction
\$+i\$ \$-1\$
\$-i\$ \$+1\$
\$+1\$ \$-i\$
\$-1\$ \$+i\$

If the current character is not / or \, the new position is the sum of the current position and the current direction, otherwise it's the current position (it remains unchanged).

Once we have all the positions, we place the characters on the grid with a simple lookup.

answered Apr 12 at 5:41
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Really nice.... \$\endgroup\$ Commented Apr 14 at 6:34
  • 1
    \$\begingroup\$ You're using filter(), so mid("/"&A1,row(1:999),1) to save 9. \$\endgroup\$ Commented Apr 15 at 6:17
8
\$\begingroup\$

JavaScript (Node.js), 195 bytes

f=(s,x=X=W=H='',y=Y=0,d=1,D=q=0,o=[...H].map(_=>[...W]))=>s.some(c=>([d,D]=c=='\\'?[-D,-d]:c=='/'?[D,d]:[d,D,q&&(x-=d,y-=D),y<0?++Y:H[y]?x>0?--X:W[-x]?![q=o[y][-x]=c]:W+=' ':H+=0])[3])?f(s,X,Y):o

Try it online!

Lots of work on exacting boundary

Note that x is negated, as it shortens 1 bytes for d, and all use of x need explicit convert into number

// Somehow outdated
/// followed by manual comment, // by AI comment, // <s> AI's incorrect comment
f = (
 s, // `s` is the input string or array to process.
 x = X = W = H = '', // `x` and `X` track horizontal positions, initialized as empty strings.
 /// which is treated as zero in `x` and `X`, string for counter in `W` and `H`
 y = Y = 0, // `y` and `Y` track vertical positions, while `H` acts as the first "row" with a default 0.
 d = 1, // `d` represents horizontal direction (1 for right, -1 for left).
 D = 0, // `D` represents vertical direction (1 for down, -1 for up).
 o = [...H] // `o` is a 2D array derived from `H`. Mapping the string `H` into an array with sub-arrays.
 .map(_ => [...W]) // For every element `H`, create rows initialized with the current width `W`.
 /// `W` would contain only spaces, so `o` is full of spaces, as empty canvas.
) => 
 s.some((c, i) => // Begin processing the input array `s` via `.some` for iteration with conditions.
 ( // For each character `c` in `s`, compute its movement based on the character and update variables.
 [d, D] = // Update direction or positions based on the character.
 c == '\\' ? [-D, -d] // A backslash `\` inverts directions.
 /// also swaps, so it works as mirror. Same below.
 : c == '/' ? [D, d] // A forward slash `/` swaps directions.
 : [d, D, // Otherwise, maintain position and attempt movement.
 i && (x -= d, y -= D) // For all iterations (except the first), decrement by `d` and `D`.
 /// Use `-=` to handle empty string(initial value) well
 ],
 // Handle boundaries and updates based on row and column positions.
 y < 0 ? ++Y // If beyond the top, <s> extend a new row at the top.
 /// then would restart but initial Y increases
 : H[y] /// If inside
 ? x > 0 // If within a row:
 ? --X // <s> Shift left in the current row.
 /// then would restart but initial X increases
 : W[-x] // <s> If at/near a boundary:
 /// If inside
 ? ![o[y][-x] = c] // Place character `c` into the 2D grid `o`.
 /// and return false (other operations modifying initial condition return true)
 : W += ' ' // Otherwise, grow the row's width by one.
 : H += 0 // If beyond the existing rows, <s> add a new "0" row.
 /// add a row, content irrelevant
 )[3] // Process the last element of the computed array.
 ) /// aka. is it a restart request
 ? f(s, X, Y) // If `.some` returns `true` (i.e., a condition matches), recurse with updated values.
 : o // When all input has been processed, return the final 2D array `o`.
answered Apr 11 at 5:40
\$\endgroup\$
7
\$\begingroup\$

05AB1E, (削除) 30 (削除ここまで) 27 bytes

εY.../ \yk©di®>·^Võëˆ]Jā≠>s ̄Λ

-3 bytes thanks to @Neil.

Try it online. (No test suite with all test cases at once, because the will overlap previous drawings and there isn't a way to reset it.)

Explanation:

ε # Map over each character `y` of the (implicit) input:
 # (implicitly push the current character `y`)
 Y # Push variable `Y` (which starts at 2)
 .../ \yk # Get the (0-based) index of character `y` in "/ \",
 # or -1 if it's not present in this string
 © # Store this index in variable `®` (without popping)
 di # Pop and if this index is non-negative (aka `y` is "/" or "\"):
 ® # Push index `®` (0 for "/" or 2 for "\")
 > # Increase it by 1 (1 for "/" or 3 for "\")
 · # Double it (2 for "/" or 6 for "\")
 ^ # Bitwise-XOR Y by this
 V # Pop and store this value as new `Y`
 õ # Push an empty string ""
 ë # Else (aka `y` is a letter, "_", or "-"):
 ˆ # Pop and add the current `Y` to the global array
] # Close both the outer if-else statement and map
 J # Join the characters (and empty strings) together,
 # which will be the input-string without the "\" and "/"
 ā # Push a list in the range [1,length] (without popping)
 ≠ # Check for each integer that it's NOT 1: [0,1,1,1,...]
 > # Increase each by 1: [1,2,2,2,...]
 s # Swap so the string is at the top of the stack again
 ̄ # Push the global array
 Λ # Use the Canvas builtin with these three arguments
 # (after which the result is implicitly output immediately)

Additional explanation of the Canvas builtin Λ/:

It takes 3 arguments to draw an ASCII shape:

  1. Length of the lines we want to draw
  2. Character/string to draw
  3. The direction to draw in, where each digit represents a certain direction:
7 0 1
 ↖ ↑ ↗
6 ← X → 2
 ↙ ↓ ↘
5 4 3

εY.../ \yk©di®>·^Võëˆ]Jā≠>s ̄ with example input boa/co\ns/t\rictor creates the following three Canvas arguments:

  1. Lengths: [1,2,2,2,2,2,2,2,2,2,2,2,2,2]
  2. Chars to draw: [b,o,a,c,o,n,s,t,r,i,c,t,o,r]
  3. Directions: [2,2,2,0,0,6,6,4,2,2,2,2,2,2]

So it'll draw b of length 1 in direction 2/→:

b

Then o of length 2-1 still in direction 2/→:

bo

Likewise for a:

boa

Then for c it changes direction to 0/↑:

 c
boa

Then o of length 2-1 still in direction 0/↑:

 o
 c
boa

Then for n it changes directions again, this time to 6/←:

 no
 c
boa

Etc.

See this 05AB1E tip of mine to learn more about the Canvas builtin.

As for how I change directions based on the / or \:

  • For / I do a bitwise-XOR with 2, changing the directions as: 0⇒2; 2⇒0; 4⇒6; 6⇒4 - try it online;
  • For \ I do a bitwise-XOR with 6, changing the directions as: 0⇒6; 2⇒4; 4⇒2; 6⇒0 - try it online;
  • For letters, -, or _, the direction remains the same.
answered Apr 11 at 8:17
\$\endgroup\$
3
  • 1
    \$\begingroup\$ 28 bytes: εY"//\"yk©di®>·^Võëˆ]Jā≠>s¯Λ. \$\endgroup\$ Commented Apr 11 at 9:34
  • \$\begingroup\$ @Neil Thanks! The "//\" can be ...//\ for an additional -1. :) \$\endgroup\$ Commented Apr 11 at 14:40
  • \$\begingroup\$ What with compressed strings I I didn't feel like checking to see what the most efficient way of coding the string was... \$\endgroup\$ Commented Apr 11 at 15:34
5
\$\begingroup\$

Julia, 341 bytes

f(s)=begin
a,b,c,d=1,1,1,1
D=[]
x,y=1,0
i,j=0,1
L()=i,j=j,-i
R()=i,j=-j,i
F(z)=begin x+=i;y+=j;push!(D,(x,y,z));a=min(a,x);b=max(b,x);c=min(c,y);d=max(d,y) end
g(z)=(z=='/') ? (i!=0 ? L() : R()) : (z=='\\' ? (i!=0 ? R() : L()) : F(z))
g.(collect(s))
M=fill(' ',b-a+1,d-c+1)
map(z->M[z[1]-a+1,z[2]-c+1]=z[3],D)
println.(prod.(eachrow(M)))
end

Attempt This Online!

Julia isn't really a golf language but its built-in matrix type is lovely for working with grids of chars like this. The ternary statements could be more compact but Julia syntax requires whitespace around the ?s and :s.

This is my first golf submission, so I'm sure there's lots more small optimizations that could be done, too.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to Code Golf, and nice first answer! We have a Tips for golfing in Julia page that may be of use - in particular, it looks like replacing some of your functions with overloaded operators might save a few bytes. \$\endgroup\$ Commented Apr 11 at 23:21
  • 1
    \$\begingroup\$ Welcome on board, hope you have a nice stay. :) \$\endgroup\$ Commented Apr 11 at 23:52
4
\$\begingroup\$

Ruby, (削除) 173 (削除ここまで) 163 bytes

->s{u=1;m=v=0
a=(0..w=n=3*x=y=s.size).map{Q=' '*w}
s.chars{|i|(j='\ /'.index i)?(u,v=v*k=1-j,u*k):(a[y+=v][x+=u]=i
m=x>m ?x:m;n=x<n ?x:n)}
(a-[Q]).map{|h|h[n..m]}}

Try it online!

Commented code

->s{u=1;m=v=0 #Setup u,v as a direction vector 1,0. m=max value of x position.
a=(0..w=n=3*x=y=s.size #w=width of board, 3*input length. n=min value of x position.
).map{Q=' '*w} #Make board a of w strings Q of w spaces. Move to x,y both equal input length.
s.chars{|i| #iterate through characters in input
 (j='\ /'.index i)? #j=index of \ or / (either 0 or 2, true) or false if other character.
 (u,v=v*k=1-j,u*k): #If true, k=1 or -1. swap u and v, and negate with k if /.
 (a[y+=v][x+=u]=i #If false, update x,y by moving by u,v. Edit the character into a and...
 m=x>m ?x:m;n=x<n ?x:n)#...if x outside the bounds n..m, update n or m 
} #(conditional 1 byte shorter than min/max.) Close loop.
(a-[Q]).map{|h|h[n..m]}#delete blank rows Q from a. Cut section h[n..m] from each remaining string. 
} #return from function with result of last calculation.
answered Apr 12 at 1:11
\$\endgroup\$
3
  • 1
    \$\begingroup\$ You can drop the parens around the argument to index for -1 byte. \$\endgroup\$ Commented Apr 12 at 2:13
  • \$\begingroup\$ @Jordan thanks, I went to bed and left a lot of golfing to do. Total 10 bytes saved. \$\endgroup\$ Commented Apr 12 at 11:03
  • \$\begingroup\$ You can upgrade to Ruby 2.7+ for -3 bytes by using numbered arguments (TIO is stuck on 2.5) ATO \$\endgroup\$ Commented Apr 23 at 1:23
2
\$\begingroup\$

Uiua, (削除) 50 (削除ここまで) (削除) 46 (削除ここまで) 45 bytes

⬚@ ⌝⊡∩⌞▽⊸⍜⇌◰-¤⊸/↧\+ ̃⊏⍜↘2⇌A2°⋯∩⌞▽¬⊙\≠⊸⊃∊=⌞$ \/

Try it here!

Consider the input boa/co\ns/t\rictor.

The first step is to take sections of the input between corresponding slashes and backslashes.

 # compare each character to \/
 ⍉ =⌞$ \/
╭─ 
╷ 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 
 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 
 ╯
 # scan by ≠ so areas between ones are all 1
 ⍉ \≠ =⌞$ /\
╭─ 
╷ 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 
 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 
 ╯
 # remove all /\ from both the input and this array
 ⍉ ∩⌞▽¬ ⊙\≠⊸⊃∊=⌞$ /\
"boaconstrictor"
╭─ 
╷ 0 0 0 0 0 1 1 1 0 0 0 0 0 0 
 0 0 0 1 1 1 1 0 0 0 0 0 0 0 
 ╯
 # read each vector of 2 elements as binary
 °⋯ ▽¬⊙\≠⊃∊=⌞$ \/
[0 0 0 2 2 3 3 1 0 0 0 0 0 0]

Now we have assigned a number to each of the four directions -- 0 for right, 1 for down, 2 for up, 3 for left. We can't use this format to easily get coordinate pairs, though; we need right to be represented as [0 1], down as [ ̄1 0], etc. Fortunately, Uiua has a certain built-in constant which comes in very handy: A2, the 2-dimensional adjacent neighbor offsets:

 A2
╭─ 
╷ 0 1 
 1 0 
 0 ̄1 
 ̄1 0 
 ╯

The only problem is that this isn't in quite the right order, but swapping the last two rows with ⍜↘2⇌ solves that. ̃⊏ uses the original numbers as indices into this list:

 ⍉ ̃⊏⍜↘2⇌A2 °⋯▽¬⊙\≠⊃∊=⌞$ \/
╭─ 
╷ 0 0 0 ̄1 ̄1 0 0 1 0 0 0 0 0 0 
 1 1 1 0 0 ̄1 ̄1 0 1 1 1 1 1 1 
 ╯
 # cumulative sums
 ⍉ \+ ̃⊏⍜↘2⇌A2°⋯▽¬⊙\≠⊃∊=⌞$ \/
╭─ 
╷ 0 0 0 ̄1 ̄2 ̄2 ̄2 ̄1 ̄1 ̄1 ̄1 ̄1 ̄1 ̄1 
 1 2 3 3 3 2 1 1 2 3 4 5 6 7 
 ╯

Now we have an array of coordinates, and we can use inverse of ⊡ pick to make an array with corresponding values at those coordinates, but first we need to make all coordinates positive and remove duplicate positions.

 # subtract from each the minimum of all the coordinates
 ⍉ -¤⊸/↧ \+ ̃⊏⍜↘2⇌A2°⋯▽¬⊙\≠⊃∊=⌞$ \/
╭─ 
╷ 2 2 2 1 0 0 0 1 1 1 1 1 1 1 
 0 1 2 2 2 1 0 0 1 2 3 4 5 6 
 ╯
 # remove duplicate coordinates from the string, counting from the back
 ∩⌞▽⊸⍜⇌◰ -¤⊸/↧\+ ̃⊏⍜↘2⇌A2°⋯∩⌞▽¬⊙\≠⊸⊃∊=⌞$ \/
"boaonstrictor"
╭─ 
╷ 2 2 2 0 0 0 1 1 1 1 1 1 1 
 0 1 2 2 1 0 0 1 2 3 4 5 6 
 ╯
 # put characters at these coordinates, filling with spaces
 ⬚@ ⌝⊡ ∩⌞▽⊸⍜⇌◰-¤⊸/↧\+ ̃⊏⍜↘2⇌A2°⋯∩⌞▽¬⊙\≠⊸⊃∊=⌞$ \/
╭─ 
╷ "sno " 
 "trictor" 
 "boa " 
 ╯
answered Apr 18 at 14:40
\$\endgroup\$
2
\$\begingroup\$

☾, (削除) 83 (削除ここまで) 77 chars

󷺹s􋄎􋅈2󰲣y∈⟝⭜xᖘ􍨅󷺹ᴙᣆy􋁤􋂝􋑬󷺺󷹒ᐸᐸ⭝x+x􍩬󰅂Ϝ󷺺+󷺾→ᴙ1:≕c⍉ᴍ⟷ꟿ󷸻≺⊞ᴍ2󰲡c󰑅ᔨx≕i≡しろいしかく⭜s⭝⟞∖⟝→ᴙi󰅂ᴍ⨁ᴍ☾ᐘ‹/\›

code in proper font

try it!

The first part of this solution is constructing an array of coordinates corresponding to each character of the string. First, the string is reduced, starting with an initial value of [[0,1]].In each iteration, if the character is one of /\, the last item is reversed, and if it is / it is also negated. If the character wasn't one of those, the last item of the list is duplicated. This reduction gives a direction to each character, and taking the cumulative summation gives the intended array of coordinates.

The second part is going from this list of coordinates to a 2-dimensional array with each character at the right position. The idea is to make a table of all coordinates in the range, and map each to the last index of that coordinate in the list. If the coordinate isn't there, then a space is put there, otherwise the corresponding character from the original string with all slashes removed is used.

After all that, it's a simple process of joining each line to a string and printing them.

answered Apr 26 at 17:44
\$\endgroup\$
1
\$\begingroup\$

Maple, 189 bytes

proc(s)c:=0,0;d:=0,1;seq(`if`(i="/",(d:=-d[2],-d[1]),`if`(i="\\",(d:=d[2],d[1]),[(c+=d),(T[c]=i)])),i=s);printf("%{s}s",Array(seq(`..`(min,max)(op~(i,[indices(T)])),i=1..2),T,fill=" "))end;

Input string needs \ doubled or it is interpreted as a control character; otherwise the task is not possible in Maple. Coordinates (y,x); directions (0,1) is +x direction etc.. Golfed version condenses the for loop to a seq, and if-then-else statement to nested if expressions.

Ungolfed version:

proc(s)
c:=0,0;d:=0,1; # initial coords and direction
for i in s do # for each character
 if i="/" then
 d:=-d[2],-d[1] # change direction 
 elif i="\\" then
 d:=d[2],d[1] # change direction
 else
 c+=d; # update coords
 T[c]:=i # record character at these coords
 fi
od;
printf("%{s}s", # print string, newlines are automatic at end of each row
 # {s} means no spaces between chars
 Array( # next line calcs x and y ranges for array
 seq(`..`(min,max)(op~(i,[indices(T)])),i=1..2),
 T,fill=" ")) # use table T to initialize; " " if no assigned value
end;
answered Apr 13 at 2:13
\$\endgroup\$
2
  • \$\begingroup\$ Curious when you say "input string needs \ doubled". Is the double \ actually present in the string, or is that just how you write a literal ` in Maple? (Like in JavaScript, "\\"` creates a string with a single backslash.) Either way, it's fine. \$\endgroup\$ Commented Apr 13 at 23:28
  • \$\begingroup\$ Yes, it's like Javascript. \$\endgroup\$ Commented Apr 13 at 23:32
1
\$\begingroup\$

Perl 5, 254 bytes

eval'sub{(X,Y,I,J,%g)=(1,0,-1,0);/\w|-/?($g{I+=X,J+=Y}=$_):(S=1-2*/\//,(X,Y)=(X?0:S*Y,Y?0:S*X))for pop=~/./g;(I,X,J,Y)=map{R=$_;(sort{$a-$b}map s/R//r,keys%g)[0,-1]}"$;.+",".+$;";join"",map{/.+/;join("",map$g{$_,$&}//$",I..X)."\n"}J..Y}'=~s/[A-Z]/\$$&/gr

Try it online!

answered Apr 15 at 18:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.