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 2016-10-04T11:31:42.187

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 – 2016-10-04T11:57:58.273

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 – 2016-10-04T11:59:33.720

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 – 2016-10-04T12:10:02.523

@TonHospel Else what kind of challenge is this? – Erik the Outgolfer – 2016-10-04T13:08:58.643

http://codegolf.stackexchange.com/q/55819/21348 related – edc65 – 2016-10-04T13:54:22.073

I haven't seen this :( – jacksonecac – 2016-10-04T13:55:38.083

1Don't worry. That one is a lot harder due to the offsets in that challenge. – Martin Ender – 2016-10-04T14:07:32.280

Your challenge could use a few more test cases though. Especially the minimum input would be good to know. – Martin Ender – 2016-10-04T14:07:55.643

can you please give an output for n == 1 – Kishan Kumar – 2016-10-04T14:14:33.803

I added a N = 1 example and clarified that the head takes priority over a corner – jacksonecac – 2016-10-04T14:39:51.110

Related (but simpler) – DLosc – 2016-10-04T16:51:39.373

Can we optionally take length including head and tail? The examples would be inputs 21 and 3 respectively – Luis Mendo – 2016-10-04T17:36:46.947

No, sorry :). There are already solutions posted. – jacksonecac – 2016-10-04T17:39:14.340

2Nice first challenge! Welcome to the site! – Luis Mendo – 2016-10-04T18:14:40.210

"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 – 2016-10-04T18:37:07.867

@EmilioMBumachar are you referring to the N=1 example? The direction being clockwise vs counter-clockwise not N, S, E and W. – jacksonecac – 2016-10-04T18:41:24.883

@LuisMendo thank you! I lurked for a long time :) – jacksonecac – 2016-10-04T18:45:02.953

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 – 2016-10-04T19:04:32.540

@Rod Ok I'll keep it open awhile. I wasn't sure the protocol for these :) – jacksonecac – 2016-10-04T19:09:36.867

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 2016-10-04T11:31:42.187

Reputation: 87 464

1awesome job! thanks for participating! – jacksonecac – 2016-10-04T17:42:29.377

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 2016-10-04T11:31:42.187

Reputation: 17 588

Nice Job! you have the tiebreaker win. Lets see what else people come up with. – jacksonecac – 2016-10-04T17:06:14.937

2Surely this is the ideal language to solve this challenge in. – Neil – 2016-10-05T12:32:06.423

+1. Only glitch is that when the head is on a corner it is meant to point straight, not around the corner. – Jonathan Allan – 2016-10-05T23:56:18.617

1Save 16 bytes by indexing into strings like so: '\/'[m], '-|'[m] and '>v'[m] – Jonathan Allan – 2016-10-06T00:11:51.213

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 – 2016-10-06T00:22:04.570

1Save 1 more by removing the space between print and ''.join – Jonathan Allan – 2016-10-06T00:32:02.007

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

– Jonathan Allan – 2016-10-06T00:47:36.607

In the ungolfed code, snake should be equal to ([''],), not [['']], technically. – Erik the Outgolfer – 2016-10-08T11:08:11.527

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 – 2016-10-09T11:05:12.527

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 2016-10-04T11:31:42.187

Reputation: 31 086

You can save a few bytes by replacing (' ') with \ ` and('@')with`@` ` – Arnauld – 2016-10-04T22:03:17.210

@Arnauld Array(2).fill ==> [ Array[1], Array[1] ], while Array(2).fill(' ') ==> [' ',' '] – usandfriends – 2016-10-04T22:05:11.480

@usandfriends - True. But that should not make any difference once join'ed. – Arnauld – 2016-10-04T22:13:41.800

@Arnauld at first I agreed with usandfriends, but it works indeed. Thanks – edc65 – 2016-10-05T06:45:19.290

@TravisJ It doesn't work in Chrome, but Firefox seems to work. – Adnan – 2016-10-05T09:10:31.810

@Adnan current version works with Firefox 49 and Chrome 53. It fails with MSIE 10, did not try 11 or Edge – edc65 – 2016-10-05T10:02:07.780

Ah, I had to enable the Experimental JavaScript flag (chrome://flags/#enable-javascript-harmony) on chrome to make it work. Nice answer though :). – Adnan – 2016-10-05T10:21:59.833

Nice Job! Thanks for participating! – jacksonecac – 2016-10-05T11:34:16.600

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 2016-10-04T11:31:42.187

Reputation: 95 035

Interesting implementation. I had not thought of doing that. Thanks for contributing and nice job!! – jacksonecac – 2016-10-06T11:09:12.173

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 2016-10-04T11:31:42.187

Reputation: 14 114

Awesome! Nice Job! Thanks for contributing! – jacksonecac – 2016-10-11T11:19:09.323

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 2016-10-04T11:31:42.187

Reputation: 95 035

Awesome! Thanks for contributing! – jacksonecac – 2016-10-11T11:18:07.677

-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 2016-10-04T11:31:42.187

Reputation: 2 584

5This can massively be reduced just by removing unnecessary spaces, newlines, comments, functions, etc.. – Addison Crump – 2016-10-08T00:12:38.183