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
10 Answers 10
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.
-
\$\begingroup\$ Charcoal is really suitable for challenges of this type. \$\endgroup\$TwilightSparkle– TwilightSparkle2025年04月13日 05:18:56 +00:00Commented Apr 13 at 5:18
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.
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.
-
1\$\begingroup\$ Really nice.... \$\endgroup\$doubleunary– doubleunary2025年04月14日 06:34:49 +00:00Commented Apr 14 at 6:34
-
1\$\begingroup\$ You're using
filter(), somid("/"&A1,row(1:999),1)to save 9. \$\endgroup\$doubleunary– doubleunary2025年04月15日 06:17:33 +00:00Commented Apr 15 at 6:17
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
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`.
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:
- Length of the lines we want to draw
- Character/string to draw
- 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:
- Lengths:
[1,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] - 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.
-
1\$\begingroup\$ 28 bytes:
εY"//\"yk©di®>·^Võëˆ]Jā≠>s¯Λ. \$\endgroup\$Neil– Neil2025年04月11日 09:34:10 +00:00Commented Apr 11 at 9:34 -
\$\begingroup\$ @Neil Thanks! The
"//\"can be...//\for an additional -1. :) \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2025年04月11日 14:40:07 +00:00Commented 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\$Neil– Neil2025年04月11日 15:34:33 +00:00Commented Apr 11 at 15:34
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
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.
-
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\$emanresu A– emanresu A2025年04月11日 23:21:17 +00:00Commented Apr 11 at 23:21
-
1\$\begingroup\$ Welcome on board, hope you have a nice stay. :) \$\endgroup\$Steve Bennett– Steve Bennett2025年04月11日 23:52:50 +00:00Commented Apr 11 at 23:52
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]}}
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.
-
1\$\begingroup\$ You can drop the parens around the argument to
indexfor -1 byte. \$\endgroup\$Jordan– Jordan2025年04月12日 02:13:34 +00:00Commented 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\$Level River St– Level River St2025年04月12日 11:03:53 +00:00Commented Apr 12 at 11:03
-
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 "
╯
☾, (削除) 83 (削除ここまで) 77 chars
s2y∈⟝⭜xᖘᴙᣆyᐸᐸ⭝x+xϜ+→ᴙ1:≕c⍉ᴍ⟷ꟿ≺⊞ᴍ2cᔨx≕i≡□しろいしかく⭜s⭝⟞∖⟝→ᴙiᴍ⨁ᴍ☾ᐘ‹/\›
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.
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;
-
\$\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\$Steve Bennett– Steve Bennett2025年04月13日 23:28:08 +00:00Commented Apr 13 at 23:28 -
\$\begingroup\$ Yes, it's like Javascript. \$\endgroup\$dharr– dharr2025年04月13日 23:32:55 +00:00Commented Apr 13 at 23:32
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
TigeR\attleE...I think you have an extraein the input (or are missing anein the output) at the end ofRattle\$\endgroup\$