16
\$\begingroup\$

aka. implement an easier version of 05ab1e's canvas element.

Description

The canvas element is used to draw ASCII lines on the screen.
The (easier version of the) canvas element takes a list of integers and returns a multi-line string (or a list of strings). The integers map to the following directions:

7   0   1
  ↖ ↑ ↗
6 ← X → 2
  ↙ ↓ ↘
5   4   3

0: upwards
1: top-right
2: right
3: bottom-right
4: downwards
5: bottom-left
6: left
7: top-left

If given eg. [6, 4, 2], it outputs this:

###
#
###

Explanation Image

  1. Start with one hash already drawn (X)
  2. Draw two hashes towards the left (direction 6)
  3. Draw two hashes downwards (direction 4)
  4. Draw two hashes towards the right (direction 2)

Rules

  • Default Loopholes apply
  • Multiple integers or a list of integers will be given as the input
  • Output must be a string, a list of strings, or a matrix of characters (like a binary matrix)
  • You will not receive directions with intersecting lines
  • You can replace # with any other constant character, but you'll always have to draw 2 hashes per direction and start with a hash
  • Excess whitespace is allowed
  • The shortest code, as this is , wins

Examples

In: [1, 5, 3, 7, 5, 1, 7, 3]
Out:
#   #
 # # 
  #  
 # # 
#   #

In: [2, 2, 2, 2, 4, 4, 7, 7]
Out:
#########
     #  #
      # #
       ##
        #

In: [1, 2, 3, 6, 6, 2, 4, 2, 2, 4, 2, 6, 4, 2, 1, 3, 2, 6, 7, 5, 6, 6, 4, 0, 6, 4, 0, 6, 0, 6, 2, 0, 2]
Out:
  ###            
 #   #           
# #####          
    #            
  #######        
  #     #        
###     ### #    
  #     #  # #   
  #########   ###
    # #          
    # #          

\$\endgroup\$
6
  • \$\begingroup\$ Sandbox \$\endgroup\$
    – mathcat
    Jun 5 at 19:25
  • 2
    \$\begingroup\$ How strict are the whitespace rules? Is trailing whitespace required, and is excess whitespace allowed? \$\endgroup\$ Jun 5 at 21:50
  • 1
    \$\begingroup\$ Also you should consider adding an example that draws a cat. \$\endgroup\$
    – Jonah
    Jun 5 at 23:00
  • 1
    \$\begingroup\$ @ConorO'Brien excess whitespace is allowed, I'll edit it \$\endgroup\$
    – mathcat
    Jun 6 at 5:09
  • \$\begingroup\$ What's the winning criterion? The questions seems to be silent about it. \$\endgroup\$ Jun 23 at 18:17

8 Answers 8

13
\$\begingroup\$

Vyxal, 4 bytes

×3ø^

Try it Online!

05AB1E, 3 bytes

3$Λ

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ lol yeah I expected this one \$\endgroup\$
    – mathcat
    Jun 5 at 19:02
10
\$\begingroup\$

Charcoal, 9 bytes

FA✳ι##‖↘#

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

FA

Loop over the directions.

✳ι##

Print two #s in that direction.

‖↘

Reflect to switch from Charcoal to 05AB1E coordinates. This is because Charcoal's coordinate system has 0 as right and 2 as up etc.

#

Print the final #.

Boring built-in version:

GH✳✳⁻²A³#

Try it online! Link is to verbose version of code Unfortunately the version of Charcoal on TIO has a bug in its deverbosifier and it can't handle Directions(), so here is what the verbose version should be:

PolygonHollow(Directions(Minus(2, Input())), 3, "#");

Note that the PolygonHollow line drawing code takes the lengths of the lines rather than the number of additional #s to draw.

\$\endgroup\$
8
\$\begingroup\$

Ruby, 106 bytes

->d{m=2*x=y=d.size*2;c=[]
[-5,*d].map{|k|2.times{(c[y+=k==6?0:k%7<=>2]||=[0]*m)[x+=k<1?0:4<=>k]=1}};c-[p]}

Try it online! Returns a binary matrix (1 for #, 0 for ). A semicolon replaced for a newline for readability's sake (and the same byte count). Produces a lot of leading/trailing spaces in each row (most of the time).

I'm sure there's some more bytes to be shaved but I can't currently see what they are.

Direction to coordinate formula

I'm not sure if this is the shortest, but I imagine this approach is shorter than an approach by complex exponentiation. For a given direction \$k\$, we want to produce the delta directions \$dx\$ and \$dy\$ to "nudge" our central coordinate along. We can see that, through some formula manipulation, we can do just that:

\begin{array} {|c|c|} \hline k & dx & dy & k<1 & 4\Leftrightarrow k& & k=6 & k\bmod7 & k\bmod7\Leftrightarrow2 \\ \hline 0 & 0 & -1 & T & 1 & \mathbf{0} & F & 0 & -1 & \mathbf{-1} \\ \hline 1 & 1 & -1 & F & 1 & \mathbf{1} & F & 1 & -1 & \mathbf{-1} \\ \hline 2 & 1 & 0 & F & 1 & \mathbf{1} & F & 2 & 0 & \mathbf{0} \\ \hline 3 & 1 & 1 & F & 1 & \mathbf{1} & F & 3 & 1 & \mathbf{1} \\ \hline 4 & 0 & 1 & F & 0 & \mathbf{0} & F & 4 & 1 & \mathbf{1} \\ \hline 5 & -1 & 1 & F & -1 & \mathbf{-1} & F & 5 & 1 & \mathbf{1} \\ \hline 6 & -1 & 0 & F & -1 & \mathbf{-1} & T & 6 & 1 & \mathbf{0} \\ \hline 7 & -1 & -1 & F & -1 & \mathbf{-1} & F & 0 & -1 & \mathbf{-1} \\ \hline -5 & & & T & 1 & \mathbf{0} & F & 2 & 0 & \mathbf{0} \\ \hline \end{array}

Bolded cells above indicate taking the previous expression, but instead choosing zero if the previous condition holds. In code, we update our coordinates as thus:

x += k < 1  ? 0 : 4 <=> k
y += k == 6 ? 0 : k % 7 <=> 2

Important to note is that \$-5\$, as well as any other number \$c<1\$ for which \$c\equiv 2\mod 7\$, produces a 0 in both coordinates, and is useful as a placeholder to mean "no action", and hence a useful initialization instruction.

Commented code

-> d {
  # x and y are initially set to d.size * 2, guaranteeing us sufficient room
  # so that our calculations will not step outside of bounds.
  # m is our minimum width for padding purposes for such a bound, and extends
  # the distance from 0 to x or y in each direction, that is, 2x or 2y
  m = 2 * x = y = d.size * 2
  c = []
  # to account for the initial cell, we prepend -5 to the input (see above table)
  [-5, *d].map { |k|
    # since each line is two characters long, we update each cell individually
    2.times {
      # update y, as per above
      y += k == 6 ? 0 : k % 7 <=> 2
      # get the current row, defaulting to an appropriate row of 0s
      row = c[y] ||= [0] * m
      # update x, as per above
      x += k < 1 ? 0 : 4 <=> k
      # update (x,y) in the output array
      row[x] = 1
    }
  }
  # remove all nil rows from the output array
  c - [p]
}
\$\endgroup\$
2
  • \$\begingroup\$ Is something wrong with the submission, @downvoter? or were there too many numbers in this post :P \$\endgroup\$ Jun 6 at 22:28
  • \$\begingroup\$ @downvoter ̷I̷'̷m̷ ̷p̷e̷r̷s̷o̷n̷a̷l̷l̷y̷ ̷o̷f̷f̷e̷n̷d̷e̷d̷ ̷b̷y̷ ̷t̷h̷i̷s̷ \$\endgroup\$
    – mathcat
    Jun 7 at 16:36
8
\$\begingroup\$

Jelly,  25  22 bytes

Saved three bytes using the allowance of excess whitespace (this also fixed a bug that meant an input with only left and/or right movements would have printed incorrectly since no space or newline characters were being introduced in such cases).

ı2½*Nx2ŻÆiNṠ+\+L$ŒṬo⁶Y

A full program that accepts the list of instructions and prints the result using 1 as the character.

Try it online!

How?

Jelly has no canvas drawing built-ins, so instead this builds a matrix of zeros and ones, with ones at visited locations and prints it with spaces in place of zeros joined with newline characters.

ı2½*Nx2ŻÆiNṠ+\+L$ŒṬo⁶Y - Main Link: list of integers from [0,7]:
ı2                     - 2i (twice the square root of -1)
  ½                    - square root
    N                  - negate the input (vectorises)
   *                   - exponentiate (vectorises)
     x2                - repeat each element twice
       Ż               - prefix with a zero
        Æi             - convert each to [real, imaginary] parts
          N            - negate (vectorises)
           Ṡ           - sign (vectorises)
             \         - cumulative reduce by:
            +          -   addition (vectorises)
                $      - last two links as a monad:
               L       -   length
              +        -   add (vectorises)
                 ŒṬ    - matrix with 1s at the indicated [row, column]s
                   o⁶  - logical OR with space character
                     Y - join with newline characters
                       - implicit, smashing print

Note: ı2 is a hack - ı should work but there are floating-point errors - for example, ı½*-2 gives 2.220446049250313e-16-1j rather than 0-1j.

\$\endgroup\$
3
  • \$\begingroup\$ woah that's big \$\endgroup\$
    – mathcat
    Jun 6 at 5:10
  • \$\begingroup\$ @mathcat yep, hence why this kind of thing is built-in in so many golfing languages! Slightly shorter now due to the excess whitespace allowance though. \$\endgroup\$ Jun 6 at 13:47
  • 1
    \$\begingroup\$ Heh, and I was thinking "Wow, for a language without a builtin, that's super short" ;) \$\endgroup\$
    – DLosc
    Jun 6 at 21:40
7
\$\begingroup\$

BQN, 79 77 71 67 58 53 51 bytes

Saved 5 bytes thanks to att

' '+(¬⊸-↑↑⟜1)∘⋈˜∘¬⟜-∘≠{𝕨(𝕨∨⌽)´-𝕩}4(⊢×○×-)2/8|-⟜2⊸⋈¨

Try it at BQN online

Explanation

New approach!

8|-⟜2⊸⋈¨

Turn each number in the initial list of directions into a list containing two numbers, the first number shifted two directions counterclockwise (i.e. minus 2, mod 8). These will become the y and x deltas, respectively.

2/

Repeat each direction twice.

4(⊢×○×-)

Turn the directions into deltas. We want to implement the following function:

  • 0 or 4 -> 0
  • 1, 2, or 3 -> 1
  • 5, 6, or 7 -> -1

We almost get the desired result if we subtract the input from 4 and take the sign. The only problem is that 0 becomes 1, which we can fix by multiplying by the sign of the input: sign(4-x) * sign(x), in C-like pseudocode. In BQN, ×○× is a dyadic function that takes the sign of each argument and multiplies them; (⊢×○×-) is a dyadic train that passes its right argument and the difference of its arguments to ×○×; and so if we give it 4 as its left argument and a direction number as its right argument, it will return the desired delta. Since all of the functions used here are pervasive, we can apply them to a list of lists of numbers and get a list of lists of results.

(¬⊸-↑↑⟜1)∘⋈˜∘¬⟜-∘≠

Call the length of the original list \$N\$. (The length of the list of deltas is \$2N\$ because we repeated each move twice.) Construct a \$4N+1\$ by \$4N+1\$ array that is filled with 0s but with a 1 in the exact center.

{𝕨(𝕨∨⌽)´-𝕩}

Negate each delta and right-fold the list of deltas using the following procedure: Start with the \$4N+1\$ array constructed above. At each step, rotate the partial result by the current delta, and then logical OR with another copy of the \$4N+1\$ array. The result is a \$4N+1\$ by \$4N+1\$ array of 0s and 1s.

' '+

Add a space character, converting the 0s to spaces and the 1s to !s.

\$\endgroup\$
8
  • \$\begingroup\$ I noticed you 1 byte shorter than mine originally and fought for the reduction I got... I'm not sure I can get under 67 though, but I'll make it my mission to try :P \$\endgroup\$ Jun 8 at 8:21
  • \$\begingroup\$ you can remove the in ' '⊸+ \$\endgroup\$
    – Razetime
    Jun 8 at 12:09
  • \$\begingroup\$ @DomHastings I see you got under 67. However, I just found a new approach and got down to 58... ;) \$\endgroup\$
    – DLosc
    Jun 8 at 18:35
  • 1
    \$\begingroup\$ can shorten the initial array generation further with (¬⊸-↑↑⟜1)∘≍˜∘¬⟜-∘≠ \$\endgroup\$
    – att
    Jun 8 at 19:54
  • 1
    \$\begingroup\$ another -2 bytes \$\endgroup\$
    – att
    Jun 8 at 22:57
1
\$\begingroup\$

C (gcc): 218 210 bytes

Edit: Thanks to ceilingcat for shortening the code to 210 bytes.

p=297,C[800]={[297]=3},d,j=800;f(D,n){d^D?:(C[p+=n]=C[p+=n]=3);}main(c,v)char**v;{for(;d=*v[--c]-48,c;f(0,-40))f(7,-41),f(6,-1),f(5,39),f(4,40),f(3,41),f(2,1),f(1,-39);for(;j--;j%40?:puts(""))putchar(C[j]+32);}

Try it online (with explanation)

Explanation:


// Thanks to ceilingcat for shortening the code to 210 bytes

// 'C[800]' is the 2D canvas ('C[20][40]') flattened out to 1D and contains the
// offset of the ASCII character code from 32 (e.g. '#' character code is 35
// but would be stored as 3). This is because initializing an array with any
// other value than 0 is not cheap (space-wise) and in this case it's better to
// just add 32 whenever needed.
// 'p' should point to the middle of the canvas (might be wrong :P).
p=297,C[800]={[297]=3},d,j=800;
// This moves the cursor and draws 2 '#'s in the specified direction 'D'.
f(D,n){d^D?:(C[p+=n]=C[p+=n]=3);}
main(c,v)char**v;{
        // Converts command line arguments to an 'int' and draws on the canvas.
        for(;d=*v[--c]-48,c;f(0,-40))f(7,-41),f(6,-1),f(5,39),f(4,40),f(3,41),f(2,1),f(1,-39);
        // Print the canvas.
        for(;j--;j%40?:puts(""))putchar(C[j]+32);
}
\$\endgroup\$
1
  • \$\begingroup\$ nice, C!~~~~~~~ \$\endgroup\$
    – mathcat
    Jun 6 at 16:05
1
\$\begingroup\$

Perl 5 + -pl043F, 63 bytes

$_=". "x@F.s/./(<#{DA,A,,B,BD,{B,,A}DD}>)[$&]x2/ger;s/\w/.[$&/g

Try it online!

Explanation

The solution utilises ANSI escape sequences to move the cursor to the required position based on the input commands. First $_ is reset to @F (a list containing each character of the input, which, when interpreted as a scalar, returns the length of the list) repetitions of \f (form-feed followed by space) to move the cursor to a position to allow enough space for the output (hopefully...), concatenated with the original input s///ubstituting each digit (.) with its index into the glob list (<#{DA,A,,B,BD,{B,,A}DD}>). This results in a string like: #A#A#BDD#BDD#B#B#DDA#DDA#BDD#BDD#A#A#DDA#DDA#B#B# where the letters correspond to the ANSI escape sequence to move either left, up or down. These letters are then replaced with the correct ANSI escape sequences in the final s///ubstitution.

Bonus "cat" drawing.

\$\endgroup\$
3
  • \$\begingroup\$ Ooh nice, Perl! \$\endgroup\$
    – mathcat
    Jun 6 at 19:34
  • \$\begingroup\$ @mathcat Bonus cat: Try it online! Maybe it looks a little more like an owl... \$\endgroup\$ Jun 6 at 19:51
  • \$\begingroup\$ wow, looks nice! \$\endgroup\$
    – mathcat
    Jun 7 at 7:13
1
\$\begingroup\$

Python 3, 125 bytes

from turtle import*
mode("logo")
def f(l,w=write):
 reset();ht();pu();w("#")
 for d in l:seth(d*45);fd(9);w("#");fd(9);w("#")   
\$\endgroup\$
4
  • \$\begingroup\$ Hmm yeah okay I'll let this count :P \$\endgroup\$
    – mathcat
    Jun 6 at 19:30
  • \$\begingroup\$ @mathcat Was quite surprised nobody had already done this! :D \$\endgroup\$
    – Noodle9
    Jun 6 at 19:47
  • \$\begingroup\$ What's the point of a TIO link if it doesn't have the module installed? \$\endgroup\$
    – Neil
    Jun 9 at 19:21
  • \$\begingroup\$ @Neil None what so ever, removed. \$\endgroup\$
    – Noodle9
    Jun 9 at 23:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.