pssssssssssssst

31

0

Introduction

This is one is pretty straightforward. We will be drawing a snake in ascii. This was inspired by that old snake game where you have to collect the fruit and you continuously grow.

Definition

Given a positive integer N that represents the snake's length, draw a snake so that it has a body of n plus a head and a tail.

Parts:

  • head: <, >, ^, v
  • tail: @
  • vertical: |
  • horizonal: -

All corners should be satisfied with a \ or / respectively. Unless the head ends on a corner in which case the head <, >, ^, v takes priority in the direction the snake is curled. i.e. for the length 1 example, it is turned counter clockwise and so the head is turned that way. For a clockwise solution it would be to the right >.

The snake must start in the middle with its tail but it may go outwards in any direction you choose either clockwise or counter-clockwise. It must also wrap tightly around itself as it expands outwards in a circular fashion.

Example

/--\
|/\|
||@|
|\-/
\--->

Where the @ is the tail and starting position. As seen above the tail starts in the middle, goes up to the left in a counter-clockwise rotation outward.

Here the length is 19 plus a tail and a head.

As another example, here is length 1:

<\
 @

Winning

This is code-golf so the answer that is submitted with the smallest number of bytes wins, with time to be used as a tie breaker.

Have fun!

jacksonecac

Posted 8 years ago

Reputation: 2 584

2It's not very clear that I'm not just allowed to draw a straight snake like @---->. You probably intend more strict conditions about the snake shape. Also make clear how much whitespace is or isn't allowed – Ton Hospel – 8 years ago

1"The snake must start in the middle with it's tail but it may go outwards in any direction you choose and either clockwise or counter-clockwise" – jacksonecac – 8 years ago

1So I say @ is the middle (possible add some spaces to make it so), declare "to the right" to be the direction and make just the head point down and declare that clockwise. Your terms may seem clear to you, but they are actually ambiguous. I realize you probably mean an as tightly as possible coiled snake, but you should make that clear – Ton Hospel – 8 years ago

@TonHospel Else what kind of challenge is this? – Erik the Outgolfer – 8 years ago

http://codegolf.stackexchange.com/q/55819/21348 related – edc65 – 8 years ago

I haven't seen this :( – jacksonecac – 8 years ago

1Don't worry. That one is a lot harder due to the offsets in that challenge. – Martin Ender – 8 years ago

Your challenge could use a few more test cases though. Especially the minimum input would be good to know. – Martin Ender – 8 years ago

can you please give an output for n == 1 – Kishan Kumar – 8 years ago

I added a N = 1 example and clarified that the head takes priority over a corner – jacksonecac – 8 years ago

Related (but simpler) – DLosc – 8 years ago

Can we optionally take length including head and tail? The examples would be inputs 21 and 3 respectively – Luis Mendo – 8 years ago

No, sorry :). There are already solutions posted. – jacksonecac – 8 years ago

2Nice first challenge! Welcome to the site! – Luis Mendo – 8 years ago

"Unless the head ends on a corner in which case the head <, >, ^, v takes priority in the direction the snake is curled" Should not the head be curved upwards in the example? It's in a corner. – Emilio M Bumachar – 8 years ago

@EmilioMBumachar are you referring to the N=1 example? The direction being clockwise vs counter-clockwise not N, S, E and W. – jacksonecac – 8 years ago

@LuisMendo thank you! I lurked for a long time :) – jacksonecac – 8 years ago

1why the hurry? you can wait how long you want to accept an answer (in fact you don't even need to accept one). If you accept an answer, it MAY discourage new answers – Rod – 8 years ago

@Rod Ok I'll keep it open awhile. I wasn't sure the protocol for these :) – jacksonecac – 8 years ago

Answers

10

MATL, 85 83 bytes

And I thought that having a spiral builtin would make for short code...

Oli2+XH:UQXItH:+XJh(YsXKoQ3I(4J(5l(H:)0HX^XkU(6H(3M1YL3X!P)'|-\/@' '<v>^'KHq))h0hw)

Try it online!

Explanation

Let N denote the input. We will create a vector of length ceil(sqrt(N+2))^2, that is, the smallest perfect square that equals or exceeds N+2. This vector will be filled with numeric values, rolled into a spiral (that's why its length needs to be a perfect square), and then the numeric values will be replaced by characters.

Let n denote each step starting from 1 at the center of the spiral. The steps where the snake turns are given by n2+1 (that is: 2, 5, 10, ...) for \ symbols and n2+n+1 (that is: 3, 7, 13, ...) for /. The steps between a \ and a / should be -, and those between a / and a \ should be |.

The vector is created such that it contains 1 at the turn points (2,3,5,7,10,13...) and 0 at the rest. The parity of the cumulative sum tells if each entry should be a - or a |. Adding 1 to this result we get a vector containing 1 (for |) or 2 (for -). But this makes the turn points themselves become 1 or 2 too. So the turn points, whose positions we know, are overwritten: positions n2+1 are filled with 3 and positions n2+n+1 are filled with 4. The tail and head are also special cases: the first element of the vector (tail) is set to 5, and the element with index N+2 (head) is set to 6. Finally, elements with indices exceeding N+2 are set to 0.

Taking input N=19 as an example, we now have a vector with length 25:

5 3 4 1 3 2 4 1 1 3 2 2 4 1 1 1 3 2 2 2 6 0 0 0 0

We need to roll this vector into a spiral. For this we use a builtin function that generates a spiral matrix, followed by a reflection and a transposition to produce:

13 12 11 10 25
14  3  2  9 24
15  4  1  8 23
16  5  6  7 22
17 18 19 20 21 

Indexing the vector with the matrix gives

4 2 2 3 0
1 4 3 1 0
1 1 5 1 0
1 3 2 4 0
3 2 2 2 6

where 0 corresponds to space, 1 corresponds to |, 2to -, 3 to \, 4 to /, 5 to @, and 6 to the head.

To know which of the four characters ^, <, v, or > the head should have, we use the cumulative sum of turn points that we previously computed. Specifically, the second-last value of this cumulative sum (i.e. the N+1-th value) modulo 4 tells us which character should be used for the head. We take the second-last value of the cumulative sum, not the last, because of the requirement "if the head ends on a corner the head <, >, ^, v takes priority in the direction the snake is curled". For the N=19 example the head is >.

Now we can build a string containing all the snake characters, including the appropriate character for the head at the sixth position: '|-\/@> '. We then index this string with the above matrix (indexing is 1-based and modular, so space goes last), which gives

/--\ 
|/\| 
||@| 
|\-/ 
\--->

Luis Mendo

Posted 8 years ago

Reputation: 87 464

1awesome job! thanks for participating! – jacksonecac – 8 years ago

8

Python 2, 250 233 191 bytes

n=input()
l=[''],
a=x=0
b='@'
while a<=n:x+=1;l+=b,;l=zip(*l[::-1]);m=x%2;b='\/'[m]+x/2*'-|'[m];k=len(b);a+=k
l+=b[:n-a]+'>v'[m]+' '*(k-n+a-1),
if m:l=zip(*l[::-1])
for i in l:print''.join(i)
  • Saved 39 bytes thanks to @JonathanAllan

repl.it

Draw the snake by rotating the entire snake 90º clockwise and adding the bottom segment, this way the snake will always be anticlockwise.
The new segment will always start with \ and have - as body for even sides and / - for odd sides. The segments sizes (without corners) are 0,1,1,2,2,3... which is floor(side/2).
If the segment is the last, it remove the excess characters, add the head and complete with spaces.

desired_size=input()
snake=[['']]
snake_size=side=0
new_segment='@'
while snake_size<=desired_size:
    side+=1
    snake+=[new_segment]
    snake=zip(*snake[::-1])
    odd_side=side%2
    new_segment='\/'[odd_side]+side/2*'-|'[odd_side]
    snake_size+=len(new_segment)
diff=desired_size-snake_size
snake+=[new_segment[:diff]+'>v'[odd_side]+' '*(len(new_segment)-diff-1)]
if odd_side:
    snake=zip(*snake[::-1])

for line in snake:print ''.join(line)

Rod

Posted 8 years ago

Reputation: 17 588

Nice Job! you have the tiebreaker win. Lets see what else people come up with. – jacksonecac – 8 years ago

2Surely this is the ideal language to solve this challenge in. – Neil – 8 years ago

+1. Only glitch is that when the head is on a corner it is meant to point straight, not around the corner. – Jonathan Allan – 8 years ago

1Save 16 bytes by indexing into strings like so: '\/'[m], '-|'[m] and '>v'[m] – Jonathan Allan – 8 years ago

Save another 8 by using a string instead of a list: '\/'[m]+x/2*'-|'[m] and, l+=b[:n-a]+'>v'[m]+' '*(k-n+a-1) – Jonathan Allan – 8 years ago

1Save 1 more by removing the space between print and ''.join – Jonathan Allan – 8 years ago

Save another 14 using a while loop: http://ideone.com/SfIfeQ

– Jonathan Allan – 8 years ago

In the ungolfed code, snake should be equal to ([''],), not [['']], technically. – Erik the Outgolfer – 8 years ago

The head orientation after a turn is different respect to examples (see input 1 for instance). I made some effort in my answer to closely follow the examples ... But as OP is happy with that, you have my vote – edc65 – 8 years ago

7

JavaScript (ES6), 193 201 203 215 220 224

Edit saved 4 bytes thx @Arnauld
Edit2 changed logic, not storing the current increments for x and y, just get them from the current direction
Edit3 having saved a few bytes, I decided to use them for a better management of the blank space
Edit4 8 bytes saved not following exactly the examples about the head direction - like other answers

The current version works with Chrome, Firefox and MS Edge

This answer gives some trailing and leading space (and blank lines).

n=>(t=>{for(x=y=-~Math.sqrt(++n)>>1,g=[i=t];(g[y]=g[y]||Array(x).fill` `)[x]='^<v>|-/\\@'[t?n?i-t?4+t%2:x-y?7:6:t%4:8],n--;i=i>1?i-2:++t)d=t&2,t&1?x+=d-1:y+=d-1})(0)||g.map(x=>x.join``).join`
`

Slightly less golfed

n=>
{
  g = [],
  // to save a few bytes, change line below (adds a lot of spaces)
  // w = ++n,
  w = -~Math.sqrt(++n)
  x = y = w>>1,
  s=c=>(g[y] = g[y] || Array(x).fill(' '))[x] = c, // function to set char in position
  s('@'); // place tail
  for (
     i = t = 0; // t increases at each turn, t%4 is the current direction
     n--;
     i = i > 0 ? i - 2 : t++ // side length increases every 2 turns
  )
     d = t & 2,
     t & 1 ? x += d-1: y += d-1
     s(!n ? '^<v>' [t % 4] // head
          : '|-/\\' [i > 0 ? t % 2 : x-y ? 3 : 2]) // body
  return g.map(x=>x.join``).join`\n`
}

f=
n=>(t=>{for(x=y=-~Math.sqrt(++n)>>1,g=[i=t];(g[y]=g[y]||Array(x).fill` `)[x]='^<v>|-/\\@'[t?n?i-t?4+t%2:x-y?7:6:t%4:8],n--;i=i>1?i-2:++t)d=t&2,t&1?x+=d-1:y+=d-1})(0)||g.map(x=>x.join``).join`
`

function update() {
  O.textContent=f(+I.value);
}

update()
<input type=number id=I value=19 oninput='update()' 
 onkeyup='update() /* stupid MS browser, no oninput for up/down keys */'>
<pre id=O>

edc65

Posted 8 years ago

Reputation: 31 086

You can save a few bytes by replacing (' ') with \ ` and('@')with`@` ` – Arnauld – 8 years ago

@Arnauld Array(2).fill ==> [ Array[1], Array[1] ], while Array(2).fill(' ') ==> [' ',' '] – usandfriends – 8 years ago

@usandfriends - True. But that should not make any difference once join'ed. – Arnauld – 8 years ago

@Arnauld at first I agreed with usandfriends, but it works indeed. Thanks – edc65 – 8 years ago

@TravisJ It doesn't work in Chrome, but Firefox seems to work. – Adnan – 8 years ago

@Adnan current version works with Firefox 49 and Chrome 53. It fails with MSIE 10, did not try 11 or Edge – edc65 – 8 years ago

Ah, I had to enable the Experimental JavaScript flag (chrome://flags/#enable-javascript-harmony) on chrome to make it work. Nice answer though :). – Adnan – 8 years ago

Nice Job! Thanks for participating! – jacksonecac – 8 years ago

3

JavaScript (ES7), 200 bytes

(n,s=(n*4+1)**.5|0,i=+`1201`[s%4],d=i=>`-`.repeat(i))=>[...Array(s-2>>2)].reduce(s=>`/-${d(i)}\\
${s.replace(/^|$/gm,`|`)}
|\\${d(i,i+=2)}/`,[`/\\
|@`,`/-\\
|@/`,`@`,`/@`][s%4])+`
\\${d(n-(s*s>>2))}>`

ES6 version for ease of testing:

f=(n,s=Math.sqrt((n*4+1))|0,i=+`1201`[s%4],d=i=>`-`.repeat(i))=>[...Array(s-2>>2)].reduce(s=>`/-${d(i)}\\
${s.replace(/^|$/gm,`|`)}
|\\${d(i,i+=2)}/`,[`/\\
|@`,`/-\\
|@/`,`@`,`/@`][s%4])+`
\\${d(n-(s*s>>2))}>`;
<input type=number min=1 oninput=o.textContent=f(this.value)><pre id=o>

Neil

Posted 8 years ago

Reputation: 95 035

Interesting implementation. I had not thought of doing that. Thanks for contributing and nice job!! – jacksonecac – 8 years ago

3

Perl, 111 110 bytes

Includes +1 for -p

Give size on STDIN

snake.pl:

#!/usr/bin/perl -p
s%> %->%+s%\^ %/>%||s/
/  
/g+s%.%!s/.$//mg<//&&join"",//g,$/%seg+s/ /^/+y%/\\|>-%\\/\-|%for($\="/
\@
")x$_}{

Ton Hospel

Posted 8 years ago

Reputation: 14 114

Awesome! Nice Job! Thanks for contributing! – jacksonecac – 8 years ago

0

Batch, 563 bytes

@echo off
if %1==1 echo /@&echo v&exit/b
set w=1
:l
set/ah=w,a=w*w+w
if %a% gtr %1 goto g
set/aw+=1,a=w*w
if %a% leq %1 goto l
:g
call:d
set r=/%r%\
set/ae=h%%2,w=%1-h*w+2
for /l %%i in (1,1,%h%)do call:r
call:d
echo \%r%^>
exit/b
:d
set r=
for /l %%i in (3,1,%w%)do call set r=%%r%%-
exit/b
:r
echo %r:!=^|%
if %e%==0 set r=%r:@!=\/%
set r=%r:@/=\/%
set r=%r:!\=\-%
set r=%r:/@=\/%
set r=%r:/!=-/%
set r=%r:@!=\/%
set r=%r:/\=!@%
set r=%r:/-=!/%
if %e%==1 set r=%r:/\=@!%
set r=%r:/\=@/%
set r=%r:-\=\!%
if %e%==1 set r=%r:/\=/@%

Explanation: Special-cases 1 as the rest of the code requires a snake width of at least two. Next, calculates the largest quarter square (either an exact square or a rectangle 1 wider than it is high) whose area is less than the length of the snake. The snake will be coiled into this rectangle starting at the bottom left corner and ending with the tail in the middle, and the remaining length will run under the bottom of the rectangle. The rectangle is actually generated from simple string replacements; most of the time each line is generated from the previous line by moving the diagonals 1 step, but obviously the tail also has to be dealt with, and there are slight differences depending on whether the height of the rectangle is even or odd.

Neil

Posted 8 years ago

Reputation: 95 035

Awesome! Thanks for contributing! – jacksonecac – 8 years ago

-1

Python 2.7, A WHOPPING 1230 bytes

I am new to python and code golf but I felt I had to answer my own question and sulk away in shame after the fact. A lot of fun working on it though!

def s(n):
x = []
l = 0
if n % 2 == 1:
    l = n
else:
    l = n + 1
if l < 3:
    l = 3
y = []
matrix = [[' ' for x in range(l)] for y in range(l)] 
slash = '\\'
newx = l/2
newy = l/2
matrix[l/2][l/2] = '@'
newx = newx-1
matrix[newx][newy] = slash
#newx = newx-1
dir = 'West'

for i in range(0, n-1):    
    newx = xloc(newx, dir)
    newy = yloc(newy, dir)
    sdir = dir
    dir = cd(matrix, newx, newy, dir)
    edir = dir

    if (sdir == 'West' or sdir == 'East') and sdir != edir:
        matrix[newx][newy] = '/'
    else:
        if (sdir == 'North' or sdir == 'South') and sdir != edir:
            matrix[newx][newy] = '\\'
        else:
            if dir == 'East' or dir == 'West':
                matrix[newx][newy] = '-'
            else:
                matrix[newx][newy] = '|'
newx = xloc(newx, dir)
newy = yloc(newy, dir)
sdir = dir
dir = cd(matrix, newx, newy, dir)
edir = dir
print 'eDir: ' + dir
if dir == 'North':
    matrix[newx][newy] = '^'
if dir == 'South':
     matrix[newx][newy] = 'v'
if dir == 'East':
     matrix[newx][newy] = '>'
if dir == 'West':
     matrix[newx][newy] = '<'    


p(matrix, l)

def cd(matrix, x, y, dir):    
if dir == 'North':
    if matrix[x][y-1] == ' ':
        return 'West'
if dir == 'West':
    if matrix[x+1][y] == ' ':
        return 'South'
if dir == 'South':
    if matrix[x][y+1] == ' ':    
        return 'East'
if dir == 'East':
    if matrix[x-1][y] == ' ':        
        return 'North'
return dir

def p(a, n):
for i in range(0, n):
    for k in range(0, n):
        print a[i][k],
    print ' '

def xloc(x, dir):
if dir == 'North':
    return x -1
if dir == 'West':
    return x
if dir == 'East':
    return x 
if dir == 'South':
    return x + 1
 def yloc(y, dir):
if dir == 'North':
    return y
if dir == 'West':
    return y - 1
if dir == 'East':
    return y + 1
if dir == 'South':
    return y

s(25)

https://repl.it/Dpoy

jacksonecac

Posted 8 years ago

Reputation: 2 584

5This can massively be reduced just by removing unnecessary spaces, newlines, comments, functions, etc.. – Addison Crump – 8 years ago