20
\$\begingroup\$

There have of course been challenges to render ASCII art fonts here and there. This challenge doesn't add anything to those. In contrary, it subtracts from them for a good reason. If you allow all characters,

  1. it's a lot of boring work
  2. the task is mainly about clever compression of the font and short extraction

This one shall be different, because it is restricted to only five letters to be rendered: BDIOP

####   #   ###   ####   ####   
#   #  #  #   #  #   #  #   #  
####   #  #   #  ####   #   #  
#   #  #  #   #  #      #   #  
####   #   ###   #      ####   

Those are obviously chosen for a reason, they share some symmetry, some common elements, but not all of them:

  • They are vertically symmetrical, except for the P
  • The start with a vertical bar, except for the O
  • The second line is the same, except for the I

I think these subtile differences open the field for a lot of possible golfing improvements.

The input is a sequence of up to 13 uppercase characters out of those five (put as extended regex [BDIOP]{,13}) in a form that fits your language.

The output is the ASCII art output of the string with the given 5*x variable width font, using a whitespace for the background and a character of your choice for the letters, with two whitespaces between the letters. Trailing whitespaces on the lines are allowed, as well as one trailing newline.

The shortest code for each language wins.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ You say 5×4, but how should the I be rendered in that box, or is it really 5×1? \$\endgroup\$
    – Neil
    Nov 20, 2023 at 19:33
  • \$\begingroup\$ Suggested test case: BIPOD (found in /usr/share/dict/words on TIO). \$\endgroup\$
    – Neil
    Nov 20, 2023 at 19:36
  • \$\begingroup\$ @Neil Thank you! I clarified that I expect a proportional (variable width) font. And I change the example to (whatever that means) BIPOD. \$\endgroup\$
    – Philippos
    Nov 20, 2023 at 21:18
  • 1
    \$\begingroup\$ en.wikipedia.org/wiki/Bipod \$\endgroup\$
    – Glorfindel
    Nov 21, 2023 at 19:04

9 Answers 9

22
\$\begingroup\$

JavaScript (ES6), 110 bytes

Expects an array of ASCII codes.

a=>[759,4,55,5,757].map(p=>a.map(c=>"#   ##     ### #### ".substr((p>>c%31%9&3)*5,4>>c-9^5)).join`  `).join`
`

Try it online!

Or 102 bytes if we can output an array of strings.

Method

Only 4 distinct patterns are needed to build all letters. We store them into a single lookup string.

0: #...#      #...##.....###.####.
1: #....      \___/\___/\___/\___/
2: .###.        0    1    2    3
3: ####.

The letters are encoded per row, using 2 bits per pattern. By converting from base 4 to base 10, we eventually get the array [759, 4, 55, 5, 757].

2: .###. | 3: ####. | 3: ####. | 1: #.... | 3: ####. -> 23313 -> 759
0: #...# | 0: #...# | 0: #...# | 1: #.... | 0: #...# -> 00010 -> 4
0: #...# | 0: #...# | 3: ####. | 1: #.... | 3: ####. -> 00313 -> 55
0: #...# | 0: #...# | 0: #...# | 1: #.... | 1: #.... -> 00011 -> 5
2: .###. | 3: ####. | 3: ####. | 1: #.... | 1: #.... -> 23311 -> 757

By reducing each ASCII code modulo \$31\$ and modulo \$9\$, we get the shift amount that must be applied to extract the relevant bits from the above values. (This formula is the reason why the letters are encoded in the order ODBIP.)

Letter ASCII code modulo 31 modulo 9
O 79 17 8
D 68 6 6
B 66 4 4
I 73 11 2
P 80 18 0

Finally, we have to adjust to the correct character width. The expression 4 >> c - 9 ^ 5 used as the 2nd parameter of substr() gives \$1\$ for I and \$5\$ for all other letters. (The simpler c - 73 ? 5 : 1 is just as long ... but not as cool. ^^)

\$\endgroup\$
1
  • \$\begingroup\$ Yeah! That's what I was hoping for. \$\endgroup\$
    – Philippos
    Nov 21, 2023 at 8:25
4
\$\begingroup\$

05AB1E, 37 bytes

•1b§…£U(γ°xāxK²•„# Åв5ô5ôIÇH<è€øJøð«»

Try it online.

Just uses # and spaces like the challenge description, and has a single trailing column of spaces. Using a different character (0) would be the same byte-count, by replacing „# Åв with b1ð:S:
try it online.

Explanation:

•1b§…£U(γ°xāxK²• # Push compressed integer 22122550664723003622970748254752
 „#              # Push string "# "
    Åв           # Convert the large integer to a base-2 list, and index it into "# "
5ô               # Split the list of characters into parts of size 5
  5ô             # Split the list of quintuplets into parts of (up to) size 5 as well
    I            # Push the input-string
     Ç           # Convert it to a list of codepoint-integers
      H          # Convert them from hexadecimal to base-10
       <         # Decrease each by 1
        è        # Modular 0-based index each into the earlier list of matrices
€                # Map over each matrix:
 ø               #  Zip/transpose; swapping rows/columns
  J              # Join each inner list together to a string
   ø             # Zip/transpose; swapping rows/columns
    ð«           # Append a space to each line
      »          # Join each inner list by a space, and then each string by newlines
                 # (after which the result is output implicitly)
letter Ç H < modular 0-based index
B 66 102 101 1
D 68 104 103 3
I 73 115 114 4
O 79 121 120 0
P 80 128 127 2

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •1b§…£U(γ°xāxK²• is 22122550664723003622970748254752.
This number has been generated by the program in this ASCII-art tip using the transposed flattened rendered letters as input-string.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I looked at your hexadecimal ordinals and I though, "I wonder whether I can do something like that in Charcoal?" I can... but it turns out it's what the Jelly code does, by taking the sum of the digits in the ordinal (although Jelly being 1-indexed avoids the decrement). \$\endgroup\$
    – Neil
    Nov 22, 2023 at 1:17
4
\$\begingroup\$

Jelly, 34 bytes

OD§ị“¡x%ẸȥpFẠẊẓ~ọØ¢’Bs5$⁺¤jØ1z1a⁶Y

Try it online!

A full program taking a string argument and printing the desired output, using 0 and space to make up the letters. In the end, a simple binary encoding of the five letters was most the shortest I could come up with.

Explanation

O                                   # Convert to codepoints
 D                                  # Convert to decimal digits
  §                                 # Sum inner lists
   ị                     ¤          # (Modular) index into the following:      
    “¡x%ẸȥpFẠẊẓ~ọØ¢’                # - Base-250 integer 22122550664723003622970748254752
                    B               # - ‎⁢Convert to binary
                     s5$⁺           # - ‎⁣Split into lists of length 5 twice (last one will only have length 1 - corresponds to I)
                          jØ0       # ‎⁢⁣Join with two ones between each list of lists
                              z1    # ‎⁢⁤Zip, filling with ones
                                a⁶  # ‎⁣⁡And with a space (replaces 1 with space)
                                  Y # ‎⁣⁢Join with newlines
💎

Jelly, previous version, 35 bytes

“¢ḄẸẏ߯¦ṠAi;$ƈƬ’Bs5s5⁽æ£,ḥⱮjØ0z0o⁶Y

Try it online!

A full program taking a string argument and printing the desired output, using 1 and space to make up the letters.

Explanation

“¢ḄẸẏ߯¦ṠAi;$ƈƬ’Bs5s5⁽æ£,ḥⱮjØ0z0o⁶Y­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢⁡⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁡⁢‏⁠‎⁡⁠⁢⁡⁣‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁢⁢‏⁠‎⁡⁠⁢⁢⁣‏⁠‎⁡⁠⁢⁢⁤‏⁠‎⁡⁠⁢⁣⁡‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁢⁣⁢‏⁠‎⁡⁠⁢⁣⁣‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁢⁣⁤‏⁠‎⁡⁠⁢⁤⁡‏⁠‎⁡⁠⁢⁤⁢‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁤⁣‏⁠‎⁡⁠⁢⁤⁤‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁣⁡⁡‏⁠‎⁡⁠⁣⁡⁢‏⁠‏​⁡⁠⁡‌⁣⁢​‎‎⁡⁠⁣⁡⁣‏‏​⁡⁠⁡‌­
“¢ḄẸẏ߯¦ṠAi;$ƈƬ’                     # ‎⁡Base-250 integer 40155886737228810123461877351903
                B                    # ‎⁢Convert to binary
                 s5                  # ‎⁣Split into lists of length 5
                   s5                # ‎⁤Split into lists of length 5 (last one will only have length 1 - corresponds to I)
                     ⁽æ£,            # ‎⁢⁡Pair 6503 with this
                         ḥⱮ          # ‎⁢⁢Use 6503 as the salt for the Jelly hash function for each of the input letters (will return  the first list of lists for B, the second for P, the third for O, the fourth for D and the fifth for I)
                           jØ0       # ‎⁢⁣Join with two zeros between each list of lists
                              z0     # ‎⁢⁤Zip, filling with zeros
                                o⁶   # ‎⁣⁡Or with a space (replaces 0 with space)
                                  Y  # ‎⁣⁢Join with newlines
💎

Created with the help of Luminespire

\$\endgroup\$
3
  • \$\begingroup\$ I guess, in a golfing language taking advantage of the redundancies (like in the JS solution) prevents using the powerful commands of such languages, so the general compressed bitmaps wins even for just five similar letters? Well, currently your Jelly solution is ahead of the field. \$\endgroup\$
    – Philippos
    Nov 21, 2023 at 19:03
  • 2
    \$\begingroup\$ @Philippos my solution that worked most similarly to the JS one was 40 bytes. This worked by having an integer for each letter that could be converted to five base 4 digits, and in turn those were indexed to a list of binary representations of the four possible horizontal pieces. If you’d gone with fixed width letters, my solution would have been similar in length to the one above, but trimming down the I added five bytes \$\endgroup\$ Nov 21, 2023 at 19:25
  • 1
    \$\begingroup\$ So it was a tight race! Sorry for the non-fixed width. (-; \$\endgroup\$
    – Philippos
    Nov 21, 2023 at 22:16
4
\$\begingroup\$

Charcoal, 41 39 36 35 33 bytes

↑ES⟦⪪§⪪”{∨'N4T≕⊞|"³üUr⁸mI”²⁴⊖Σ℅ι⁵

Try it online! Link is to verbose version of code. Explanation: Mainly clever compression of the font and short extraction.

  S                 Input string
 E                  Map over letters
       ”...”        Compressed rotated font
      ⪪     ²⁴      Split into letters
     §              Indexed by
                 ι  Current character
                ℅   Ordinal
               Σ    Digital sum
              ⊖     Decremented
    ⪪             ⁵ Split into columns
   ⟦                Doubly nest the list
↑                   Output rotated

The mapping of the characters to the font index is as follows (note that the modulo 5 is automatic due to the use of cyclic indexing):

Character Ordinal Digital Sum Decremented Modulo 5
O 79 16 15 0
B 66 12 11 1
P 80 8 7 2
D 68 14 13 3
I 73 10 9 4

The nested map would normally result in a list of lists so the extra list wrapper makes a doubly nested list which results in the desired output spacing.

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

GNU sed -E, 218 bytes

There are only four or five different lines to compose the characters, but seems expensive to use this knowledge in sed, so I a column-based approach. First, replace each character by a lower letter representing the type of column, from a for all set to z for zero set. The I build row after row, inheriting the type of column. Simple for a and z, slightly more complicated for every other row set and really long logical chains for others.

s/./a&zz/g
s/^/xy:kl:ghia:zzoe \
fglmnzaawxyzeo#\n_/
s/I//g
s/B/eeo/g
s/P/wwx/g
s/D/ffk/g
s/aO/kffk/g
G
:1
s/_([ #]*\n)(.*)/\1_\2\n/
s/((.)([a-z])\S*(.)\n.*_[ #]*)\2(.*)/\1\4\5\3/
#x;p;x
/.*#\n((.*\n){5})_.*/!b1
s//\1/

Who can beat that one with sed with a different approach?

Try it online!

And easily scalable

\$\endgroup\$
2
+200
\$\begingroup\$

Vyxal 3, 45 37 bytes

OṠv"ᵇVOᶠ4ZG%|∵₁ιf†“b5Ẇ5Ẇ$ið5Y2YjTð∧Ṅṅ

Try it Online!

This is a translation of my Jelly answer to Vyxal 3, mainly as an exercise in learning an alternative language. It’s 11 3 bytes longer, partly reflecting my inexperience with the language.

The lost bytes reflect two bytes to manually output as ASCII art rather than a representation of lists, 0-indexing (so one byte for decrementing the index).

Thanks to @lyxal for saving eight bytes, including by fixing some bugs in the language!

\$\endgroup\$
6
  • \$\begingroup\$ @lyxal any comments you might have on this would be much appreciated! You’ve developed a nice stack-based language. \$\endgroup\$ Nov 26, 2023 at 11:07
  • 1
    \$\begingroup\$ Ugh, that 2y after the compressed number should simply be b (built-in to-binary) but it seems like using a JVM language has forced the number into a weird integer size limit. I'll work on fixing that \$\endgroup\$
    – lyxal
    Nov 26, 2023 at 12:16
  • 1
    \$\begingroup\$ Yep, b for some strange reason casts to Int, forcing it to squeeze inside the range -2,147,483,648 to 2,147,483,647. That's a bug. Sorry about that. Makes me wonder how many other byte shaves are possible without bugs \$\endgroup\$
    – lyxal
    Nov 26, 2023 at 12:18
  • 1
    \$\begingroup\$ Also, O₀yṠ -> OṠ as numbers are automatically cast to list of digits in the context of \$\endgroup\$
    – lyxal
    Nov 26, 2023 at 12:59
  • 1
    \$\begingroup\$ j has been updated to behave like jelly \$\endgroup\$
    – lyxal
    Nov 26, 2023 at 21:36
1
\$\begingroup\$

APL+WIN, 80 bytes

Prompts for character string. Can be any length:

' #'[(5⍴2)⊤∊(¯9+(⊂⎕av)⍳¨('xnnnc' 'xjjjg' 'x' 'gjjjg' 'hmmma')['BDIOP'⍳⎕]),¨⊂0 0]

Try it online! Thanks to Dyalog Classic

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

Python, 118 bytes

s=input()
*z,=b"|b|b||bbb|\\bbb\``|b|"
for z[:0]in[[6]]*5:print(*(bin(z[ord(c)%9//2*5])[3:].replace(*'0 ')for c in s))

Attempt This Online!

Uses spaces and 1s for output.

Encodes the font in binary. Lookup uses %9//2 which maps the code points to 0...4, in particular, sending I to 0 which allows us to abuse the simple structure of I.

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

Uiua 0.3.1, 62 bytes

/⊐≡(⊂⊂:"  ")⊐≡(+@ ×3⋯)⊏:[±.⍜↘±3.⍜⊙⊡-.2.⊃+∘+14×3¬.⋯17]⊗:"IPBDO"

Explanation

Rndr ← (                        # Define a function
  ⊗:"IPBDO"                     # Convert letters to indices
  [±.⍜↘±3.⍜⊙⊡-.2.⊃+∘+14×3¬.⋯17] # Encode bit masks for letters as integer arrays
  ⊏:                            # Convert indices to masks
  ≡(□+@ ×3⋯)                    # Render each row as a letter,
                                #   box them to accommodate for different width
  /⊐≡(⊂⊂:"  ")                  # Join letters together, add spaces between them
  # ≡&p # Uncomment to print to stdout
)
Rndr "IDBIPOPD"                 # Call the function on a value

Try it out!

\$\endgroup\$

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.