Mark My Mail! - ASCII Barcodes

39

6

4-state barcodes

Many postal services (Royal Mail UK, Canada Post, US Mail, etc) use a 4-state barcode to encode information about their mail. Rendered in ASCII, it may look something like this:

|   | |   | | | |   |     |   |
| | | | | | | | | | | | | | | | |
    | |   |   | | |   |     |

A 4-state barcode is a row of bars. Each bar can be extended upwards, downwards, or both, allowing 4 possibilities. This means that each bar essentially represents a base 4 digit:

            |       |
Bar:    |   |   |   |
                |   |

Digit:  0   1   2   3

The problem with this symbology is that it each barcode is a valid, different barcode upside down: drastically changing the meaning if the orientation is incorrect. Therefore, a start and stop sequence are normally implemented so the scanner can calculate which way it is supposed to be read.

For the purpose of this challenge, we will be using the start/stop sequence specified by Australia Post: each barcode begins and ends with a 1 0 sequence.


The Challenge

Your task is to write a program or function which, given a positive integer N, converts it to an ASCII 4-state barcode, where each bar (except for the start/stop sequences) represents a digit in the base-4 representation of N.

Example:

Given the integer 19623, we would first convert it to its base-4 representation, 10302213.

We would then map each digit to the corresponding bar:

1 0 3 0 2 2 1 3

|   |       | |
| | | | | | | |
    |   | |   |

Finally, we would add the start/stop sequences:

Start:              End:
1 0                 1 0

|   |   |       | | |
| | | | | | | | | | | |
        |   | |   |

The resulting barcode should be the program's output.


Rules:

  • The input will be a positive integer, within the range of your language's standard integer size.
  • The output:
    • May be either a list of lines, or a string containing newlines.
    • May contain leading or trailing newlines/spaces, as long as the shape remains intact.
    • Should show the barcode with the above format - it must use the pipe character (|) and space character () when drawing bars, and there should be 1 space in between each upright bar.
  • This is , so the shortest program (in bytes) wins!

Test Cases

4095:

|   | | | | | | |  
| | | | | | | | | |
    | | | | | |    

4096:

|   |             |  
| | | | | | | | | | |

7313145:

|   |   | |   | |   | |   | |  
| | | | | | | | | | | | | | | |
      | | | |     | | | |      

FlipTack

Posted 2017-12-01T18:41:34.193

Reputation: 13 242

Leading spaces allowed? ;) – Erik the Outgolfer – 2017-12-01T20:16:13.607

@FlipTack The problem with this symbology - you haven't seen The Boondock Saints, have you? – Lord Farquaad – 2017-12-01T21:48:56.127

@EriktheOutgolfer As long as the actual barcode, as a 2D matrix of characters, is intact, it can have as many spaces before or after it as necessary. – FlipTack – 2017-12-01T22:24:03.260

Other barcode related challenges: 1, 2, 3

– FlipTack – 2017-12-01T22:30:42.917

Can the output have leading zeros? – user230118 – 2017-12-01T23:34:46.147

@user230118 sorry, but the only leading/trailing characters should be spaces or newlines. – FlipTack – 2017-12-02T09:30:25.420

@FlipTack Are we allowed to output the barcode upside down? This comment seems to suggest that this would be acceptable.

– wizzwizz4 – 2017-12-02T13:01:49.413

@wizzwizz4 It should be the right way around, as in the test cases. – FlipTack – 2017-12-02T13:56:25.387

Answers

9

MATL, 34 30 29 28 bytes

TFiK_YAyhhH&\EQE+t~vB!P'|'*c

Try it online!

Explanation

TF      % Push array [1 0] (start sequence)
i       % Push input
K_YA    % Convert to base 4. Gives an array of 4-ary digits
y       % Duplicate from below: pushes [1 0] again (stop sequence)
hh      % Concatenate horizontally twice. Gives array of 4-ary digits
        % including start and stop sequences
H&\     % Two-output modulo 2: pushes array with remainders and array
        % with quotients of dividing by 2
EQE     % Times 2, plus 1, times 2, element-wise. This effectively
        % multiplies each entry by 4 and adds 2
+       % Add element-wise to the array of remainders. The stack now 
        % contains an array of numbers 2, 3, 6 or 7. Each number
        % encodes, in binary form, a column of the output. The
        % previous multiplication of the quotients by 4 will have the
        % effect of shifting one row down (one binary digit upwards),
        % to make room for the central row. The addition of 2 will
        % create the central row, which is always full
t~      % Duplicate, logical negate. Gives an array of zeros of the
        % same length
v       % Concatenate vertically into a 2-row matrix
B       % Convert to binary. Gives a matrix, where each row is the
        % binary representation of one of the numbers of the input
        % matrix, read in column-major order
!P      % Transpose, flip vertically
'|'*    % Multiply by '|'. This transforms 1 into 124 (ASCII code of
        % '|') and leaves 0 as is
c       % Convert to char. Char 0 is shown as space. Implicitly display

Luis Mendo

Posted 2017-12-01T18:41:34.193

Reputation: 87 464

9

Python 3, 103 99 96 bytes

def f(i,r=[]):
 while i:r=[' | |||||  ||'[i%4::4]]+r;i//=4
 k='|| ',' | ';[*map(print,*k,*r,*k)]

Try it online!

ovs

Posted 2017-12-01T18:41:34.193

Reputation: 21 408

8

Jelly, 16 15 bytes

4;jƓb|ṃ⁾| ẎZṙ2G

Try it online!

How it works

4;jƓb|ṃ⁾| ẎZṙ2G  Main link. No arguments.

4                Set the argument and the return value to 4.
 ;               Concatenate the return value with the argument, yielding [4, 4].
   Ɠ             Read an integer n from STDIN.
  j              Join, yielding [4, n, 4].
    b            Convert 4, n, and 4 to base 4. Note that 4 is [1, 0] in base 4.
     |           Perform bitwise OR of each resulting quaternary digit and 4.
                 This pads the binary representation of a digit d to three digits: 
                 [1, d:2, d%2]
      ṃ⁾|        Convert the results to base " |", i.e., binary where ' '
                 represents 0 and '|' represents 1.
          Ẏ      Concatenate the resulting arrays that correspond to 4, n, and 4.
           Z     Zip; transpose rows and columns.
            ṙ2   Rotate 2 units yo the left, correcting the order of [1, d:2, d%2]
                 to [d%2, 1, d:2].
              G  Grid; separate columns by spaces, rows by linefeeds.

Dennis

Posted 2017-12-01T18:41:34.193

Reputation: 196 637

This string is 15 unicode characters long, how can it be 15 bytes? – jmster – 2017-12-02T12:12:47.650

2

@jmster Jelly has its own codepage

– Mr. Xcoder – 2017-12-02T12:16:52.853

@jmster These aren't the actual characters. The program is 15 specific bytes, which have these mnemonics. Compare it with Bubblegum, it mostly looks like ....... but each dot stands for a different byte. – FrownyFrog – 2017-12-02T12:18:37.180

Why bitwise OR instead of adding? – FrownyFrog – 2017-12-02T13:07:50.670

@FrownyFrog Both would work. Since the next step is conversion to binary, I went with a bitwise operator. – Dennis – 2017-12-02T13:36:07.797

7

JavaScript (ES6), 89 87 83 bytes

n=>`|  ${(g=(a,k=n)=>k?g(a,k>>2)+(k&a?'| ':'  '):' ')(1)}|
| |${g(~0)}| |
   `+g(2)

Test cases

let f =

n=>`|  ${(g=(a,k=n)=>k?g(a,k>>2)+(k&a?'| ':'  '):' ')(1)}|
| |${g(~0)}| |
   `+g(2)

console.log(f(4095))
console.log(f(4096))
console.log(f(7313145))

How?

NB: In the version below, template literals have been replaced with standard strings so that the code can be indented properly.

n =>                        // given the input n
  '|  ' +                   // append the top leading pattern
  (g = (a,                  // g is a recursive function taking a = mask
           k = n) =>        // and using k = value, initially set to n
    k ?                     //   if k is not zero:
      g(a, k >> 2) +        //     do a recursive call for the next group of 2 bits
      (k & a ? '| ' : '  ') //     append '| ' if the bit is set or '  ' otherwise
    :                       //   else:
      ' '                   //     append an extra leading space and stop the recursion
  )(1) +                    // invoke g() with mask = 0b01
  '|\n' +                   // append the top leading pattern and a linefeed
  '| |' +                   // append the middle leading pattern
  g(~0) +                   // invoke g() with all bits set in the mask
  '| |\n' +                 // append the middle trailing pattern and a linefeed
  '   ' +                   // append the bottom leading pattern
  g(2)                      // invoke g() with mask = 0b10

Arnauld

Posted 2017-12-01T18:41:34.193

Reputation: 111 334

i'd love to see this answer explained, there's some weird stuff going on :P – Brian H. – 2017-12-07T11:36:52.637

@BrianH. I've added an explanation. – Arnauld – 2017-12-07T11:55:34.523

7

Jelly, 19 bytes

;4⁺;b4Fd2Uj€1Zị⁾| G

Try it online!

-1 thanks to Mr. Xcoder.

Erik the Outgolfer

Posted 2017-12-01T18:41:34.193

Reputation: 38 134

7

Octave, 78 77 75 74 70 69 bytes

@(x)' |'(dec2bin([2 6 3 7;~(1:4)](:,[2 1 dec2base(x,4)-47 2 1]))-47)'

Try it online!

Unlike the original approach, this one uses a simple lookup table to map the base-4 values onto their binary equivalent. The lookup table also adds the spacing between each bar by adding a zero in between each number (which maps to a bar of all spaces).

The lookup table directly maps to bars as:

   base4:  0 1 2 3 -

  lookup:  2 6 3 7 0

  binary:  0 1 0 1 0
           1 1 1 1 0
           0 0 1 1 0

Conversion from binary to | and  is now done by indexing into a string of those two characters - basically the same principle as the lookup table for binary conversion.


*Saved 1 byte, thanks @LuisMendo


Original:

@(x)['' circshift(dec2bin([a=[5 4 dec2base(x,4)-44 5 4];a*0](:))'*92,1)-4384]

Try it online!

Anonymous function which returns the barcode as a string.

This is based on the fact that if we add 4 to the base4 digits, then we can represent bar/space by the number converted to binary with bits 1 and 2 swapped:

   base4:  0 1 2 3

    add4:  4 5 6 7

  binary:  0 1 0 1
           0 0 1 1
           1 1 1 1

swap 2/1:  0 1 0 1
           1 1 1 1
           0 0 1 1

The tricky bit from a golfing perspective is adding the spaces between the bars and converting from 0/1 to '|'/' '.

Tom Carpenter

Posted 2017-12-01T18:41:34.193

Reputation: 3 990

1@LuisMendo clever! Thanks. – Tom Carpenter – 2017-12-03T17:15:55.730

4

R, 154 109 bytes

function(n,d=c(1,0,n%/%4^floor(log(n,4):0)%%4,1,0),o=c(" ","|"))cat("",o[1+d%%2],"
",o[2+0*d],"
",o[1+(d>1)])

Try it online!

Saved a whole bunch of bytes by indexing and using cat rather than constructing a matrix and using write, as well as 6 from a slightly different conversion to base 4. Prints with a leading space in each row and no trailing newlines.

The indexing takes place using some modular arithmetic, not unlike some other answers, but since R uses 1-based indexing, the arithmetic is somewhat different.

Explanation:

function(n,
 d=c(1,0,                         # d contains the padding and 
   n%/%4^floor(log(n,4):0)%%4,   # the base 4 digits
   1,0),                         # 
 o=c("|"," ")                    # the vector to index into
 cat("",                         # cat separates things with spaces by default
                                 # so the empty string will print a leading space
  o[1+d%%2],"                    # odds have a | above
",                               # literal newline, a space will follow it (hence leading spaces)
 o[2+0*d],"                      # array of 2s since the middle is always |
",                               # another literal newline
 o[1+(d>1)])                     # digits greater than 1 have a | below

Giuseppe

Posted 2017-12-01T18:41:34.193

Reputation: 21 077

3

Japt, 32 31 bytes

A¤i2Us4)¬®n s|iS)ù2 w i|1ÃqR² y

Test it online!

Not really satisfied with this yet, but it's a start...

Explanation

A¤  i2Us4)¬ ®   n s |iS)ù2 w i |1Ã qR²  y
As2 i2Us4)q mZ{Zn s'|iS)ù2 w i'|1} qRp2 y
                                           Implicit: U = input, A = 10, R = newline, S = space
As2                                        Convert 10 to a binary string.
    i2   )                                 At index 2, insert
      Us4                                    the input converted to base 4.
          q                                Split into chars.
            mZ{                  }         Map each char Z to
               Zn                            Z converted to a number,
                  s'|iS)                     converted to base " |" (binary using ' ' as 0 and '|' as 1),
                        ù2                   left-padded to length 2 with spaces,
                           w                 reversed,
                             i'|1            with another pipe inserted at index 1.
                                   q       Join the resulting list on
                                    Rp2      a newline repeated twice (adds in blank columns).
                                        y  Transpose the entire string.
                                           Implicit: output result of last expression

ETHproductions

Posted 2017-12-01T18:41:34.193

Reputation: 47 880

Your 32 bytes makes me feel a little better about this complete mess! I really shouldn't try to golf while both serving and drinking pints!

– Shaggy – 2017-12-01T21:54:55.240

3

J, 57 49 47 bytes

10 bytes thanks to FrownyFrog!

[:,.2{."0[:|:' |'{~#:@2 6 3 7{~1 0,4&#.inv,1,0:

How it works:

1 0,4&#.inv,1,0: - converts the number to a list of base-4 digits, adds 1 0 to the beginning and the end of the list

((#:2 6 3 7){' |') - lookup table for the encryption, binary 0 corresponds to space, 1 to '|'

{~ - encrypts the base 4 digit by selecting a string from the lookup table above (argument reversed)

|: - transposes the resulting array from 3 columns to 3 rows

[: - caps the fork

,.2{."0 - puts spaces between the bars

Try it online!

Galen Ivanov

Posted 2017-12-01T18:41:34.193

Reputation: 13 815

@FrownyFrog Thank you! – Galen Ivanov – 2017-12-02T08:16:27.067

3

Haskell, 91 90 bytes

h s=[do a<-4%s++0%0;x!!a:" "|x<-[" | |","||||","  ||"]]
_%0=[1,0]
b%n=b%div n b++[mod n b]

Try it online! Returns a list of lines.


Same byte count alternative for first line:

h s=[do a<-4%s++0%0;" | |  ||||"!!(x+a):" "|x<-[0,6,4]]

Laikoni

Posted 2017-12-01T18:41:34.193

Reputation: 23 676

3

Charcoal, 50 bytes

NθF²⊞υι¿θWθ«⊞υ﹪θ⁴≧÷⁴θ»⊞υθF²⊞υιE⟦ |¦|¦  ||⟧⪫E⮌υ§ιλω

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

Nθ

Input a number.

F²⊞υι

Push the stop sequence to the predefined empty list.

¿θ

If the number is positive,

  Wθ«⊞υ﹪θ⁴≧÷⁴θ»

repeatedly apply divmod to convert it to reversed base 4,

  ⊞υθ

otherwise just push it.

F²⊞υι

Push the start sequence to the list.

E⟦ |¦|¦  ||⟧

Map over three strings. Each string represents barcode translation for the digits 0123 for each row.

⪫E⮌υ§ιλω

Map over the digits (reversed back into usual order), convert them to bars or spaces using the translation, then join the results into three strings which are then implicitly printed on separate lines.

Neil

Posted 2017-12-01T18:41:34.193

Reputation: 95 035

2

Python 2, 116 114 bytes

-2 bytes thanks to notjagan

c=a=0,1
n=input()
while n:c+=n%4,;n/=4
for l in zip(*[' |'[n%2]+'|'+' |'[n/2]for n in c+a][::-1]):print' '.join(l)

Try it online!

Rod

Posted 2017-12-01T18:41:34.193

Reputation: 17 588

114 bytes. – notjagan – 2017-12-01T19:55:00.747

2

APL+WIN, 63 bytes

(⍉5 3⍴' | ||  |||||   ')[;,⍉(2 1,(1+((⌈4⍟n)⍴4)⊤n←⎕),2 1),[.1]5]

Explanation:

(⍉5 3⍴' | ||  |||||   ') create a matrix where columns represent bars plus one for separator spaces

(1+((⌈4⍟n)⍴4)⊤n←⎕) prompt for screen input and convert to base 4 and add 1 to convert to index origin 1

2 1,...,2 1 concatenate start stop

,[.1]5 concatenate separator space indices

(.....)[.....] index into bar matrix to display

Graham

Posted 2017-12-01T18:41:34.193

Reputation: 3 184

2

J, 42 40 39 bytes

' |'{~[:#:4#.2|.0|:4#:@+1 0(,,[)4#.inv]

Shaved 2 bytes thanks to Dennis. 1 byte thanks to ngn.

Try it online!

How it works

                                4#.inv]      to base 4
                        1 0(,,[)             append (1 0) on both sides
                   4#:@+                     add 4 to each digit and convert to binary
                0|:                          transpose
             2|.                             rotate the rows
      [:#:4#.             from base 4 to base 2, it's supposed to separate the columns
' |'{~                                       to characters

FrownyFrog

Posted 2017-12-01T18:41:34.193

Reputation: 3 112

2

05AB1E, 19 bytes

4BT.øS4~bT„| ‡øÁ€S»

Try it online!

This is a half-port of Dennis' approach, which is just one byte shorter than the method I used before (which I am quite happy with):

05AB1E, 20 bytes

4BT.øS2‰í1ýøT„| ‡€S»

Try it online!

How it works?

4BT.øS2‰í1ýøT„| ‡€S»  | Full program. Takes input from STDIN, outputs to STDOUT.

4B                    | Convert to base 4.
  T                   | Push a 10 to the stack.
   .ø                 | Surrond (prepend and append the 10 to the base 4 representation).
     S                | Split into individual characters/digits.
                      +---------------------------------------------------------------
                      | This part used to be ¸4.ø4в˜ in the previous version, which
                      | means: surround with 4's, convert each to base 4 (4 -> [1, 0])
                      | and finally deep-flatten the list.
                      +---------------------------------------------------------------
      2‰              | Divmod 2 ([N // 2, N % 2]).
        í             | Reverse (element-wise).
         1ý           | Add a 1 in the middle (element-wise).
           ø          | Transpose.
            T„| ‡     | Translate (‡) from "10" (T) to "| " („| ).
                 €S»  | Format as a grid.
                 €S   | Push the characters of each.
                   »  | Join by newlines, while joining inner lists by spaces.

I have asked Adnan (the creator of 05AB1E) about the grid thingy in chat, and they helped me save 2 bytes, by pointing out a feature of 05AB1E: when joining multi-dimenisional lists by newlines, the inner lists are joined using spaces too, so ðý is unnecessary.

Mr. Xcoder

Posted 2017-12-01T18:41:34.193

Reputation: 39 774

2

APL (Dyalog Classic), 33 bytes

' |'[≠\2/2⊖1⍪2 2⊤1 0(,,⊣)4⊥⍣¯1⊢⎕]

Try it online!

ngn

Posted 2017-12-01T18:41:34.193

Reputation: 11 449

Oh, that’s how you’re supposed to surround with 1 0... – FrownyFrog – 2017-12-07T04:07:29.967

So 2⊥⍣¯1 is how you’d get a binary list? – FrownyFrog – 2017-12-07T06:15:00.793

@FrownyFrog There's no one true way to surround. Yes, 2⊥⍣¯1 is the inverse ("obverse"?) of "two-decode". It encodes into binary with as many bits as necessary. – ngn – 2017-12-07T10:08:53.173

2

JavaScript (ES6) 79 bytes

Uses .toString to convert the number to base 4, and then casework with each line and bitwise OR to build the output line by line. Outputs a list of lines.

n=>[2,3,1].map(d=>[...'10'+n.toString(4)+'10'].map(q=>(q|d)>2?"|":" ").join` `)

f = n=>[2,3,1].map(d=>[...'10'+n.toString(4)+'10'].map(q=>(q|d)>2?"|":" ").join` `)

console.log(f(19623))
console.log(f(4095))
console.log(f(4096))
console.log(f(7313145))

Kuilin Li

Posted 2017-12-01T18:41:34.193

Reputation: 421

1Cool approach with map and bitwise OR! You can save a whole byte by using \10${n.toString(4)}10`` :) – Chris M – 2017-12-19T16:51:40.817

2

Bash+coreutils, 71 67 bytes

dc -e4ddon?np|sed 's/./& /g;h;y/01/23/;G;y/12/21/;H;x;y/0123/ | |/'

Try it online!

Explanation

The dc bit converts to base 4, prepending and appending with a 4 (turns into 10 in the output) and using n to keep everything on one line.

The rest happens in sed:

s/./& /g;     Add a space after each digit
h;            Make a copy in hold space
y/01/23/;     Prepare up the second row (2/3 will turn to pipes)
G;y/12/21/;   Append what will be the third row and prep it (1/3 will turn to pipes)
H;x;          Prepend hold space
y/0123/ | |/  Make 1 and 3 pipes, 0 and 2 spaces

Sophia Lechner

Posted 2017-12-01T18:41:34.193

Reputation: 1 200

1

Converting the part after dc entirely to sed saves a few bytes, https://tio.run/##S0oszvj/PyVZQTfVJCUlP88@r6CmODVFQb1YX09fTUE/3TrDulLfwNDIWF@hRqFG39rdulg/ply/BijjDpdRqKnRV///39DSzMgYAA

– user41805 – 2018-08-28T15:16:36.530

Very nice! I tried something like that but I kept trying different ways of being clever with xing the hold/pattern spaces around to modify them and then do the s all at once, and nothing ended up shorter. – Sophia Lechner – 2018-08-28T17:15:29.020

@Cowsquack I even managed to get another two bytes down based on your idea! – Sophia Lechner – 2018-08-28T22:25:37.313

Nice idea of combining the transliterations, +1 – user41805 – 2018-08-29T11:40:07.717

1

Retina, 83 bytes

.+
$*
+`(1+)\1{3}
${1};
^
1;;
$
;1;;
1*;
$.&
.+
$&¶$&¶$&
T`13` `^.+
T`12` `.+$
\d
|

Try it online! Link includes the faster test cases. Explanation:

.+
$*

Convert to unary.

+`(1+)\1{3}
${1};

Convert to base 4 as unary numbers separated by ;s.

^
1;;

Prepend the start sequence.

$
;1;;

Append a ;, turning it into a digit terminator rather than a separator, and the stop sequence.

1*;
$.&

Convert to decimal, but adding 1 to each digit.

.+
$&¶$&¶$&

Triplicate it.

T`13` `^.+

On the first row, 1s and 3s (representing 0s and 2s) become spaces.

T`12` `.+$

On the last row, 1s and 2s (representing 0s and 1s) become spaces.

\d
|

All other digits become bars.

Neil

Posted 2017-12-01T18:41:34.193

Reputation: 95 035

1

Pip, 33 31 29 27 26 bytes

25 bytes of code, +1 for -S flag.

Y^aTB4WRt" |"@[y%2oMyy/2]

Try it online!

Explanation

We observe a pattern in the four bar types:

  • The first row is a space if the digit is even, pipe if odd.
  • The second row is always a pipe.
  • The third row is a space if the digit is 0 or 1, pipe if 2 or 3.

So:

                           a is cmdline arg; o is 1; t is 10 (implicit)
  aTB4                     Convert a to base 4
      WRt                  Wrap it before and after with 10
 ^                         Split into a list of digits
Y                          and yank into y
              [         ]  List of:
               y%2          0 if even, 1 if odd for each item in y
                  oMy       1 mapped to y, i.e. constant 1 for each item in y
                     y/2    Each item in y divided by 2 (0, 0.5, 1, or 1.5)
         " |"@             Use the elements of that list as indices into this string
                           Note that indices are truncated to integers!
                           Autoprint, separating rows with newline and elements of
                           each row with space (-S flag)

DLosc

Posted 2017-12-01T18:41:34.193

Reputation: 21 213

1

Zsh, 156 154 151 133 bytes

y(){for i (${(s//)$(echo 10$(([##4]x))10)});printf "$a[(i+1)] ";echo};a=(' ' '|' ' ' '|');y;a[1]='|';a[3]='|';y;a=(' ' ' ' '|' '|');y

Try it online!

Takes base-10 input from the var $x

Noskcaj

Posted 2017-12-01T18:41:34.193

Reputation: 421

1

SOGL V0.12, 28 bytes

4─10¹:h++{»1F2%¹{Ƨ| W}¹∑ā}⁰H

Try it Here!

dzaima

Posted 2017-12-01T18:41:34.193

Reputation: 19 048

1

PHP, 99+1 bytes

for($n=10 .base_convert($argn,10,4). 104;(~$c=$n[$i++])||3>$y+=$i=1;)echo" | ||  |||||

"[$c*3+$y];

requires PHP >= 5.5 for literal string indexing and < 7.1 for the indexing to not yield a warning.

Run as pipe with -nR or try it online.

Insert one more newline to get a trailing one.

Titus

Posted 2017-12-01T18:41:34.193

Reputation: 13 814

Warning: A non-numeric value encountered in [...][...] on line 7 – RedClover – 2017-12-02T15:26:56.697

@Soaku PHP version must be from 5.5 to 7.0 – Titus – 2017-12-03T11:09:29.637

1

C (gcc), 176 bytes

#include<stdio.h>
int n,m;f(n,r){if(n)f(n>>2);printf("%c%c",n?32:10,(n&r||!r)&&n?'|':32);}main(){scanf("%d",&n);m=(n+(4<<(32-__builtin_clz(n)/2*2)))*16+4;f(m,1);f(m,0);f(m,2);}

Try it online!

Slightly less terribly formatted (less golfed):

#include<stdio.h>
int n,m;
f(n,r) {
    if(n)
        f(n>>2);
    printf("%c%c",n?32:10,(n&r||!r)&&n?'|':32);
}

main() {
    scanf("%d",&n);
    m=(n+(4<<2*(16-__builtin_clz(n)/2)))*16+4;
    f(m,1);
    f(m,0);
    f(m,2);
}

Explanation

First, consider the following code to read an integer and output the base 4 version:

#include <stdio.h>
int n;
f(n) {if(n)printf("%d\n",n&3,f(n>>2));}
main(){scanf("%d",&n);f(n);}

This uses tail recursion to reverse the order of the output. Each recursive step bitshifts by 2 (lops off the last 2 bits and divides by 4). It outputs the result bitmasked with 3 (0b11), which only shows the last two bits, which is the last digit base 4.

The function call is included in the printf as a trailing argument (it isn't printed, but it is evaluated) to avoid needing to use {} (+2 bytes) to group the printf and the function call.

The solution here extends this base-4 code. First, m is defined as n, but such that in base 4 it will have 10 prepended and appended to it. We then print m.

In printing base 4 regularly, we used a bitmask of 3 to get the digit. In the mail code, the top line is that digit's low-order bit (a bitmask of 1) and the bottom line is the high order bit (a bitmask of 2). Accordingly, the r in f(n,r) is the bitmask - our main function calls f(m,1) for the first line and f(m,2) for the last line.

To make the middle line work (always print "|"), we add ||!r to the conditional - if r is 0, it will always evaluate to true and print a "|". Then we call f(m,0) for the middle line.

Finally, we want newlines to behave. Including an extra printf is expensive as far as bytes of source code goes, so instead we add another %c specifier to the existing printf. n?32:10 prints a newline if n is 0 (false), and a space otherwise. 32 and 10 are used instead of '\n' and ' ' to save bytes.

Billy Graydon

Posted 2017-12-01T18:41:34.193

Reputation: 11

1You can get it down to 146 if you don't mind warnings: f(n,r){n&&f(n>>2);printf("%c%c",n?32:10,(n&r|!r)&&n?'|':32);}main(n){scanf("%d",&n);f(n=(n+(4<<(32-__builtin_clz(n)/2*2)))*16+4,1);f(n,0);f(n,2);} – gastropner – 2017-12-02T13:32:28.800

1

Common Lisp, 191 bytes

(lambda(n &aux(k`(1 0,@((lambda(n &aux r f)(do()((= n 0)f)(setf(values n r)(floor n 4))(push r f)))n)1 0)))(format t"~3{~{~:[  ~;| ~]~}~%~}"`(,(mapcar'oddp k),k,(mapcar(lambda(c)(> c 1))k))))

Try it online!

Renzo

Posted 2017-12-01T18:41:34.193

Reputation: 2 260

1

Python 2, 142 126 bytes

B=lambda n:n<4and`n`or B(n/4)+`n%4`
def F(i):
 for r in 0,1,2:print' '.join(" |"[(int(x)%2,1,x>'1')[r]]for x in'10'+B(i)+'10') 

Big thanks to ovs!

I tried to not copy the other answers' methods and... yuck.

Daniel

Posted 2017-12-01T18:41:34.193

Reputation: 6 425

128 bytes – ovs – 2017-12-03T12:14:31.300

1

C# (.NET Core), 160 bytes

i=>{string s=$"10{B(i)}10",a="";for(int y=0;y<3;y++,a+="\n")foreach(var t in s)a+=t<51&y!=1&t-(y>>1)!=49?"  ":"| ";return a;string B(int n)=>n>0?B(n/4)+n%4:"";}

Try it online!

I'm sure I've missed some improvements.

DeGolfed

i=>{
    string s = $"10{B(i)}10", // prepend and append 10 to the base 4 number
           a="";

    for (int y=0; y<3; y++, a+="\n") // go through each row
        foreach (var t in s)         // go through each char digit
            a += t<51 & y != 1 & t-(y>>1) != 49 ? "  " : "| "; // check if bar or space occurs

    return a;

    string B(int n) => n>0? B(n/4) + n%4 : ""; // convert int to base 4
}

t<51 & y != 1 & t-(y>>1) != 49 checks that the char isn't '3', not the second row, and then some binary magic to see if the first or third row should contain the space.

Ayb4btu

Posted 2017-12-01T18:41:34.193

Reputation: 541

0

Japt, 42 bytes

s4 +A iA)d1"|| "0" | "2" ||"3'|³ ò3 miR ·y

Try it online!

Oliver

Posted 2017-12-01T18:41:34.193

Reputation: 7 160

there should be 1 space in between each upright bar – Erik the Outgolfer – 2017-12-01T20:27:34.957

0

Pyth, 32 bytes

jjL;CmXX1jk_d1`T"| ".DR2sj[4Q4)4

Try it here!

Mr. Xcoder

Posted 2017-12-01T18:41:34.193

Reputation: 39 774

0

C, 120 bytes

Sadly only works on Windows, since itoa is too much of a convenience to be standard.

char*p,s[21]="10";g(a){for(p=s;*p;)printf(!a|*p++&a?" |":"  ");puts(p);}f(n){strcat(itoa(n,s+2,4),"10");g(1);g(0);g(2);}

gastropner

Posted 2017-12-01T18:41:34.193

Reputation: 3 264

0

APL (Dyalog), 50 bytes

{↑,/(' |',¨' ')[⍉↑¯1⌽¨(3⍴2)∘⊤¨4+1 0,1 0,⍨4⊥⍣¯1⊢⍵]}

Try it online!

Uriel

Posted 2017-12-01T18:41:34.193

Reputation: 11 708

0

Python 2, 135 133 131 bytes

i=input();r=[1,0];c="  ","| ";a=b=""
while i:r=[i%4]+r;i=i/4
for x in[1,0]+r:a+=c[x%2];b+=c[x>1]
print a+"\n"+c[1]*len(r)+"| |\n"+b

Try it online!

Alternative Python approach.

ElPedro

Posted 2017-12-01T18:41:34.193

Reputation: 5 301

0

Perl 5, 102 + 1 (-n) = 103 bytes

do{unshift@a,($t=$_%4)%2?'|':$";unshift@b,$t>1?'|':$"}while$_>>=2;say"|   @a |
"."| "x(@a+4)."
    @b"

Try it online!

Xcali

Posted 2017-12-01T18:41:34.193

Reputation: 7 671