35
\$\begingroup\$

So, I wrote myself a one-liner which printed out a snake on the console. It's a bit of fun, and I wondered how I might condense my code...

Here's a (short) example output:

                +
                 +
                  +
                   +
                   +
                   +
                  +
                   +
                    +
                    +
                    +
                     +
                     +
                      +
                      +
                       +
                      +
                     +
                     +
                    +

Here's the specs:

  • In each line, a single non-whitespace character (whichever you like) is printed to the console, initially with 29 to 31 spaces padding to the left of it.
  • Each iteration, a random decision is made between these three actions
    • The amount of padding decreases by 1
    • The amount of padding remains the same
    • The amount of padding increases by 1

Do this 30 times, to print 30-segment long a snake to the console.

The shortest answer in bytes wins.

\$\endgroup\$
9
  • \$\begingroup\$ Can we return an array of lines? Are leading/trailing spaces or newlines allowed? \$\endgroup\$
    – Shaggy
    Commented Feb 20, 2018 at 12:07
  • 1
    \$\begingroup\$ I assume trailing spaces on each line are allowed, right? \$\endgroup\$
    – Luis Mendo
    Commented Feb 20, 2018 at 13:20
  • 2
    \$\begingroup\$ What does "random" mean? Uniformly random? Random from any distribution with positive support on each of the three outcomes? Random from any distribution whatsoever? Do the results have to be different on each run of the program? \$\endgroup\$
    – N. Virgo
    Commented Feb 21, 2018 at 9:02
  • 2
    \$\begingroup\$ Note that, by default, random does not mean uniformly so. For example, the second part of my brainfuck answer is (as of now) perfectly valid, despite it generating a straight line most of the time. \$\endgroup\$
    – Jo King
    Commented Feb 21, 2018 at 15:11
  • 1
    \$\begingroup\$ Right now, there are two points that are unclear: 1) Does random mean uniformly random? (affects this answer) and 2) Must the output be different every time? (i.e., can the random number generator be unseeded? Affects this answer) \$\endgroup\$
    – DJMcMayhem
    Commented Mar 2, 2018 at 20:41

51 Answers 51

1
2
2
\$\begingroup\$

Red, 54 bytes

loop n: 30[loop n[prin" "]print"+"n: n - 2 + random 3]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Python 2, 73 bytes

from random import*
l=c=30
while c>0:l+=randint(-1,1);print' '*l+"+";c-=1

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I really like the idea of creating a snake in Python \$\endgroup\$
    – Luuuud
    Commented Feb 21, 2018 at 13:56
  • \$\begingroup\$ I must have been half asleep yesterday - I didn't even think about that ;-) \$\endgroup\$
    – ElPedro
    Commented Feb 21, 2018 at 14:14
2
\$\begingroup\$

Pyth, 21 19 16 15 bytes

Thanks to Rod for saving 2 bytes and Mr. Xcoder for saving another 3 :)

Uses 0

VK30p*KdZ=+KtO3

Try it online!

\$\endgroup\$
0
2
\$\begingroup\$

Mathematica, 74 bytes

""<>(" "&~Array~#<>"+
"&)/@Accumulate@Prepend[{-1,1}~RandomInteger~29,30]&

Pure function. Takes no input and returns a string as output.

\$\endgroup\$
2
\$\begingroup\$

K4, 26 bytes

Solution:

-1(-31+\0,-1+29?3)$\:,"$";

Example:

q)k)-1(-31+\0,-1+29?3)$\:,"$";
                              $
                             $
                             $
                            $
                           $
                            $
                             $
                              $
                              $
                              $
                              $
                              $
                               $
                                $
                               $
                              $
                               $
                              $
                              $
                               $
                                $
                                 $
                                 $
                                $
                                 $
                                $
                               $
                                $
                                $
                                 $

Explanation:

Build a list of negative numbers to left-pad "$", perform padding and print to stdout.

-1(-31+\0,-1+29?3)$\:,"*"; / the solution
-1                       ; / print to stdout and swallow return
                     ,"$"  / enlist "$" (e.g. ["$"])
  (              )$\:      / pad right with each-left
             29?3          / 29 choose 3, takes from 0 1 2
          -1+              / subtract 1, so range is -1 0 1
        0,                 / join 0
      +\                   / sums
   -31                     / add to -31
        

Bonus:

27 byte version in K (oK):

`0:(-31+\0,-1+29?3)$\:,"*";

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ oK 22 bytes: `0:(-31+\1-30?3)$\:$0; \$\endgroup\$
    – zgrep
    Commented Feb 27, 2018 at 8:40
  • \$\begingroup\$ @zgrep, probably worth posting as a separate oK solution :) stringing 0 in K4 gives an atom rather than an enlisted char (so you can't pad it) and the above solution always pads the first line with 30 chars - a requirement which was removed as a comment. \$\endgroup\$
    – mkst
    Commented Feb 28, 2018 at 22:11
2
\$\begingroup\$

Icon, 51 bytes

-2 bytes thanks to Kevin Cruijssen

n:=30;every 1to 30do{write(repl(" ",n),8);n+:=?3-2}

Try it online!

\$\endgroup\$
0
2
\$\begingroup\$

Clojure, 88 77 bytes

Always happy to provide a Clojure answer. Always sad, it's quite long.

(loop[x 30 p x](when(> x 0)(printf(str "%"p"d\n")1)(recur(dec x)(+(-(rand-int 3)1)p))))

Ungolfed:

(loop [x 30 p x]
  (when (> x 0) 
    (printf (str "%" p "d\n") 1)
    (recur (dec x) (+ (- (rand-int 3) 1) p))))

Update 1

Managed to shave off 11 bytes by making the code more idiomatic. Who would have thought that making things "more right" would end up being more effective in a Code Golf challenge.

(doseq[p(take 30(iterate #(+ %(-(rand-int 3)1))30))](printf(str "%"p"d\n")1))

Ungolfed:

(doseq [p (take 30
            (iterate
              #(+ % (- (rand-int 3) 1))
              30))]
  (printf (str "%" p "d\n") 1))

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Hi, I added a TIO link of your solution to your post. According to TIO, it's 77 bytes long, so I updated this also. \$\endgroup\$ Commented Feb 21, 2018 at 11:55
2
\$\begingroup\$

Julia 0.6, 52 49 bytes

Full program, prints a snake with 0. I usually prefer comprehensions to for...end blocks because comprehensions return things. But since I'm printing anyway this is shorter, and you can use newlines at no cost (meaning equal to ;) for a nice readable golf. Saved a byte by using 1:n instead of 1:30 and two bytes with println(" "^n,0) instead of println(" "^n*"+").

n=30
for i=1:n
println(" "^n,0)
n+=rand(-1:1)
end

Try it online!

\$\endgroup\$
2
\$\begingroup\$

SmileBASIC, 40 bytes

This answer turned out to be pretty boring.

X=30FOR I=1TO 30?" "*X;0X=X+RND(3)-1NEXT
\$\endgroup\$
2
\$\begingroup\$

Pip, 21 bytes

Ln:30{PsXn.0--n+:RR3}

Try it online!

Explanation

                       s is space (implicit)
 n:30                  Set n to 30
L    {              }  Loop that many times:
      P                 Print, with trailing newline:
       sXn               space, repeated n times
          .0             with "0" concatenated
            --n         Decrement n
               +:       and add to it (in-place)
                 RR3    randrange(3), i.e. a random integer between 0 and 2
\$\endgroup\$
2
\$\begingroup\$

PHP, 58 56 bytes

for($a=$b=30;$a--;)echo str_pad('',$b+=rand(-1,1))."+
";

Try it online!

-2 bytes thanks to @Shaggy

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to PPCG! :) Seeing as the first line can contain 29-31 spaces, I think you could do this to save 2 bytes. \$\endgroup\$
    – Shaggy
    Commented Feb 23, 2018 at 11:59
  • \$\begingroup\$ Thx @Shaggy. Yeah, I only just read the comments on the question saying I could do that xD. Thanks for helping me shave 2 bytes! \$\endgroup\$
    – Ethan
    Commented Feb 23, 2018 at 12:01
1
\$\begingroup\$

Jelly, 18 bytes

30µ3ẋX€_2+\+⁸Ṭ€o⁶Y

Uses 1 as the character.

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Your first line does not always start with 30 spaces. \$\endgroup\$
    – chocochaos
    Commented Feb 20, 2018 at 12:31
  • \$\begingroup\$ See my comment under the OP \$\endgroup\$ Commented Feb 20, 2018 at 12:35
1
\$\begingroup\$

Jelly, 17 bytes

30µ’r‘Xµ’С⁶ẋp”.Y

Try it online!

Full program.

\$\endgroup\$
1
\$\begingroup\$

Haskell, 108 bytes

import System.Random
0%_=pure()
n%p=putStrLn(([1..p]>>" ")++"+")>>randomRIO(-1,1::Int)>>=((n-1)%).(p+)
30%30

Try it online! I was surprised to find that this code works in GHCi without the explicit type annotation but not on TIO, until BMO told me about the ExtendedDefaultRules flag which is set by GHCi.

\$\endgroup\$
1
\$\begingroup\$

SOGL V0.12, 14 bytes

'∑:{:@*O2ψH+╗p

Try it Here!

\$\endgroup\$
1
\$\begingroup\$

AWK, 54 bytes

{for(f=30;a++<30;f+=int(4*rand()-2))printf"%"f"s\n",1}

Try it online!

Very straightforward. This requires a 1 line input to run. Could make it a "program" by using a BEGIN label.

\$\endgroup\$
1
\$\begingroup\$

CJam, 31 bytes

{S*TN}:F;30:V{3,{V1-+}%mRz:VF}*

Try it online!

Sort of a port of @Datboi's answer

Explanation

{S*TN}:F                         -The padded string function
{                                -Start block
 S*                              -Multiply space string by argument and push to stack
   T                             -Push integer 0 to stack
    N                            -Push newline string to stack
     }                           -End block
      :F                         -Store top of stack(the block) in variable F
        ;                        -Pop from stack
         30:V                    -Push integer 30 to stack and store in variable V
             {3,{V1-+}%mRz:VF}   -The main loop
              3                  -Push integer 3 to stack
               ,                 -Array range from 0 to n-1
                {V1-+}%          -Map to array
                 V               -Push variable V to stack
                  1              -Push integer 1
                   -+            -subtract values from each other and add to array index
                       mR        -Random choice
                         z       -Absolute value
                          :V     -Store in variable V
                            F    - Execute function in F
                              *  -pop stack and repeat block
\$\endgroup\$
2
  • \$\begingroup\$ 21 bytes: Try it online! \$\endgroup\$ Commented Feb 22, 2018 at 7:23
  • \$\begingroup\$ Clearly, I still have a lot of practicing to do. \$\endgroup\$
    – Marcos
    Commented Feb 24, 2018 at 12:23
1
\$\begingroup\$

Perl 6, 39 bytes

$_=30;say(" "x($_+=^3 .roll-1)~"x")xx$_

Explanation:

$_=30: Sets the number of spaces to prefix to 30.

say(" "x($_+=^3 .roll-1)~"x"): Outputs that number of spaces and updates it (^3 .roll-1: pick a random integer from 0 to 2 and subtract it by 1), followed by x and a newline.

xx$_: Does this 30 times. Using $_ instead of 30 saves one space before 30.

\$\endgroup\$
1
\$\begingroup\$

Go, 123 107 94 bytes

import(."fmt"
."math/rand")
func s(){s:=30
for i:=0;i<30;i++{Printf(`%*d
`,s,4)
s+=Intn(3)-1}}

call function s

Try it online!

\$\endgroup\$
0
1
\$\begingroup\$

Commodore BASIC (Tested on a Commodore 64, but recommended 128 80 columns mode) 72 tokenised BASIC bytes

0X=29:FORI=.TOX:PRINTSPC(X)"O":X%=3*RND(.):ONX%GOSUB1,2:NEXT:END
1X=X-1:RETURN
2X=X+1:RETURN

How it works

X is declared as the spacing and is reused in the FOR/NEXT loop - the . is a slightly faster way of using 0 in Commodore BASIC, so the loop counts from 0 to the initialised value in X (30 iterations). PRINT SPC(X) will print X number of spaces, so on the first iteration, this is 29, and the "O" will print the O character. X% = 3 * RND(.) declares (loosely) an integer variable (the % means integer, though internally it's a floating point number) - a number from 0 to 2 inclusive is decided (as a floating point number, but will be used as an integer). the ON X% GOSUB 1, 2 will branch to lines 1 or 2 depending on the value. a 0 will not branch and fall through to the next instruction (which instigates the next iteration with the NEXT command). Finally the END will gracefully end the program (otherwise it will continue to line 1 and there will be an error because of a RETRURN statement without a GOSUB call).

Note that although X is manipulated in the loop, the number of iterations is set by the value that it's initialised to. Also, the Commodore C64 screen (as well as the PET, C128 and C16/+4) only has 25 rows of text in BASIC, so that's why you don't see the full 30 character PETSCII Snake.

Commodore PET ASCII Snake Challenge

\$\endgroup\$
1
\$\begingroup\$

><>, 53 bytes

 "+ ␞␞"r4[r}:}>1-$:\
<~x<1 }-1}o:}+^?:$o/oa;!?:{
^ \-

(Replace with the corresponding byte 0x1E; it is replaced with a printable unicode character here for clarity.)

Try it online! The first + always starts at position 30.

Explanation

Initialization and Iteration Cleanup

 "+ ␞␞"r4[r}:}

Let's examine this section bit-by-bit, as it's a bit more complicated than a simple stack initialization.

 "+ ␞␞"

This initializes the stack with [43, 32, 30, 30], which are the [OutputChar, PadChar, InitialPosition, IterationCount] respectively.

       r4[r

This is a no-op on the first iteration, but on each next iteration, it only retrieves the bottom-most four entries on the stack. Our method of repeating the iteration here is to simply return to the first line, which will re-push the initial string. We want to ignore this on every subsequent iteration, hence these four characters. (While I could remove them and simply jump to the corresponding position on line 1, this should require more bytes, given the rather delecate structure of the code.)

           }:}

The last thing that needs to be done before our output loop is to move the iteration count to the bottom of the stack (}), and save a copy of the current horizontal position to the bottom of the stack (:}).

Horizontal Position Output

              >1-$:\
              ^?:$o/

This is a relatively simple loop. We first subtract 1 from the horizontal position (1-), output a copy of our padding character ($:o$, which is split across the mirrors to save bytes), and, if the counter has reached zero, we skip repeating this loop; otherwise, we repeat this process (:?).

Post-loop procedures

  +                \
<~x<1 }-1}o:}+     /oa;!?:{
^ \-

I've kept in only the bits necessary to illustrate the function of the more delicate part of the code. Starting at the + in the middle, going left, we do a few things. We discard the 0 counter we do not need anymore (+), output a copy of the snake character (}:o), subtract 1 from the copy of the iteration counter we saved earlier (}1-), and then we prepare to do the random procedure }1. This pushes a 1 to the stack, which we will randomly add, subtract, or ignore relative to the horizontal position we saved earlier.

  +
<~x<1
^ \-

Remember the + is part of the initialization string from earlier. The x instruction is ><> source of randomness; it chooses a random direction and sets the IP to go that way. This means we have four options: left, right, up, and down. If it goes right, it simply encounters the < instruction and we are redirected back to the x, once again obtaining a random direction. Effectively, we limit the random directions to be only left, up, or down.

If we go left, we'll simply pop the 1; the horizontal position does not change. We then continue left and wrap around to the rest of the post-loop procedures.

If we go up, we encounter the + from earlier, adding 1 to the horizontal position. We will then continue up and wrap around to the mirror, reflecting left, then following the arrows to the rest of the post-loop procedures.

If we go down, we find that same mirror, but reflect right instead, encountering the -, and subtracting 1 from the horizontal position. We continue and wrap around to the right, following the arrows as before and continuing on.

                   \
                   /oa;!?:{

After modifying the horizontal position, we now want to examine the loop counter ({). If it is 0, we terminate (:?!;). Otherwise, we continue on, outputting a newline (ao), hitting the mirror from before, going down, wrapping around, hitting the other mirror, and going to the left. This brings us back to the very start of the code, and the process begins again.

\$\endgroup\$
1
2

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.