14
\$\begingroup\$

Input

    _  _     _  _  _  _  _  _ 
  | _| _||_||_ |_   ||_||_|| |
  ||_  _|  | _||_|  ||_| _||_|

output

1234567890

Rules

Code golf - shortest code wins.

Input will be given as a 3-line string with each line separated by a newline, or as 3 separate strings. Each line is guaranteed to be a multiple of 3 characters long with no additional whitespace, and all lines will be the same length.

Each digit is represented as a 3x3 block of characters as above. Note that the 1 digit has two leading spaces in order to fill the 3x3 block.

\$\endgroup\$
17
  • 3
    \$\begingroup\$ Reverse challenge. Additionally, all challenges on this site require a winning criterion, for example code-golf \$\endgroup\$
    – Jo King
    Oct 19, 2019 at 11:17
  • 3
    \$\begingroup\$ Welcome to PPCG. This has potential to be a very good challenge, however there are a number of issues. As others have mentioned, it needs a winning criterion (I would suggest codegolf meaning shortest code wins. Can we assume all digits will be 3 characters wide? At the current time the 1 is narrower than the others. I've checked and I see that in your original source you had 2 leading spaces on the 1, so I'll go ahead and edit these back in. You also haven't specified the output for 0 so I will edit that in also. Feel free to roll back my edits if you disagree. \$\endgroup\$ Oct 19, 2019 at 11:46
  • 2
    \$\begingroup\$ Closely related. \$\endgroup\$
    – Arnauld
    Oct 19, 2019 at 12:09
  • 2
    \$\begingroup\$ It is way too early to accept an answer. Unlike other sites of the SE network, it is recommended here to wait at least a week or 2. Accepting no answer at all is also perfectly fine. \$\endgroup\$
    – Arnauld
    Oct 19, 2019 at 22:54
  • 4
    \$\begingroup\$ That's far, far too soon to be accepting a solution. Moreso seeing as it's the only solution, so far. \$\endgroup\$
    – Shaggy
    Oct 19, 2019 at 22:55

8 Answers 8

8
\$\begingroup\$

JavaScript (ES6),  105  104 bytes

Takes input as an array of 3 strings. Returns an array of digit characters.

a=>a.map(s=>s.match(/.../g).map(([a,b,c],n)=>'3789465021'[(o[n]=~~o[n]+[++a|7*++b^44*++c])%13]),o=[])[2]

Try it online!

How?

Finding a concise way to process the input

A common trick to turn an input string into an identifier is to use parseInt(). But it only works with alphanumeric strings and is therefore pointless here. Other than that, JS is not very good at processing ASCII codes in few bytes, so we'd better find another trick.

We can notice that each digit in this font can still be uniquely identified if we replace each character with a binary value: \$1\$ for space, \$0\$ for not space:

  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
  _  |     |  _  |  _  |     |  _  |  _  |  _  |  _  |  _  
 | | |   | |  _| |  _| | |_| | |_  | |_  |   | | |_| | |_| 
 |_| |   | | |_  |  _| |   | |  _| | |_| |   | | |_| |  _| 
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 101 | 111 | 101 | 101 | 111 | 101 | 101 | 101 | 101 | 101 
 010 | 110 | 100 | 100 | 000 | 001 | 001 | 110 | 000 | 000 
 000 | 110 | 001 | 100 | 110 | 100 | 000 | 110 | 000 | 100 

That looks like a good golfing start: it means that we can abuse the fact that a space is coerced to \$0\$ and can be turned into a \$1\$ by applying the pre-increment operator to it, while it will result in NaN for the other characters.

Hence the idea to implement something along these lines:

s.match(/.../g)        // split each line into groups of 3 characters
.map(([a, b, c], n) => // for each group (a, b, c) of characters at position n:
  ???                  //   do some bitwise magic with ++a, ++b and ++c
                       //   and use it to update an identifier for the n-th digit
)                      // end of map()

Bitwise magic

After some experiments and brute-forcing, it turned out that a pretty short and efficient formula is:

o[n] = ~~o[n] + [++a | 7 * ++b ^ 44 * ++c]

Step-by-step example:

  • The 1st line of a "0" is " _ ". Therefore, we have ++a -> 1, ++b -> NaN and ++c -> 1.

    So, ++a | 7 * ++b ^ 44 * ++c evaluates to 1 | 7 * NaN ^ 44 * 1, which is \$45\$.

    Because o[n] is initially undefined, ~~o[n] evaluates to \$0\$. And because \$45\$ is coerced to a string, o[n] is updated to "045".

  • The 2nd line of a "0" is "| |". This time, we have ++a -> NaN, ++b -> 1 and ++c -> NaN. The bitwise expression evaluates to \$7\$ and o[n] is updated to "457". (Note that the leading 0 is thrown away by ~~o[n].)

  • The 3rd line of a "0" is "|_|", leading to ++a -> NaN, ++b -> NaN and ++c -> NaN. The bitwise expression evaluates to \$0\$ and the final value of o[n] is "4570".

Minimal perfect hash function

Of course, the parameters of the bitwise formula were not chosen at random (well ... at least no completely): by coercing the final identifier of a digit back to an integer and applying a modulo \$13\$, we get a unique value in \$[0..9]\$.

The following table summarizes the results for all digits:

  digit |   0   |   1   |   2   |   3   |   4   |   5   |   6   |   7   |   8   |   9
--------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
     ID |  4570 |  4377 | 45144 |  4511 |  4307 | 45441 | 45440 |  4577 |  4500 |  4501
--------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
 mod 13 |   7   |   9   |   8   |   0   |   4   |   6   |   5   |   1   |   2   |   3

Hence the 10-byte lookup table: '3789465021'.

\$\endgroup\$
4
\$\begingroup\$

J, 55 bytes

(0 3,:3 3)(6 7 8 11 4 2 13 14 5 9 i.15|[:#.@,' '&=);.3]

Try it online!

J has a primitive ;.3 called Subarrays that let's you process a multi-dimensional "sliding window". Here we have a 3x3 window moving 3 steps to the right each time, which grabs exactly 1 digit.

We convert each 3x3 matrix to a boolean mask ' '&=, flatten it ,, and convert that binary number to decimal #.. After a little experimenting, I found that modding it by 15 15| returned a unique list of small numbers, and we just find the index within that.

More brute forcing could likely find an even more efficient encoding.

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

05AB1E, 25 bytes

€S3δôøðQε˜JC15%•#/ι®ˆ¼•sè

Input as a list of lines, output as a list of digits. If we could take the input as a 2D list of characters, the first 2 bytes can be removed.

Try it online.

Explanation:

€S                        # Convert each line in the (implicit) input-list of strings to
                          # an inner list of characters
   δ                      # Convert each inner list of characters to:
  3 ô                     #  Split it into blocks of size 3
     ø                    # Zip/transpose; swapping rows/columns
      ðQ                  # Check which characters are spaces (1 if truthy; 0 if falsey)
        ε                 # Map each 3x3 block to:
         ˜                #  Flatten it to a single list
          J               #  Join it together to a single string
           C              #  Convert it from binary to an integer
            15%           #  Take modulo-15
               •#/ι®ˆ¼•   #  Push compressed integer 105048012903067
                       s  #  Swap to take the earlier integer
                        è #  And (0-based) index it into this to get the resulting digit
                          # (after the map, the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •#/ι®ˆ¼• is 105048012903067 (NOTE: the leading 1 and all the 0s except for the 3rd (7th digit) are fillers.

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

Python 3, 105 bytes

f=lambda p,q,r:p and'5498136207'[int(''.join(map(str,p[:2]+q[:3]+r[:2])))%49%11]+f(p[3:],q[3:],r[3:])or''

Try it online!

Takes input as three bytestrings. It considers the following sections in a digit, which gives a unique combination:

 _     12
|_  => 345
 _|    67

In this order, the base-10 ascii values for each character are converted into a string, concatenated, and converted back into an integer. In the example above this would be '32'+'95'+'124'+'95'+'32'+'32'+'95' => 329512495323295. This integer is reduced with a double modulo %49%11. This results in a unique value from 0 to 9 for each digit, hence the string map '5498136207'.

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

Charcoal, 57 bytes

E³SF÷L賫J⊕׳ι¹⊞υ÷⌕”{“→✂⌕Q¤αA?=¹⧴Zb⁻e⁵ηπ⪫Q¡”⁺KK⪫KMω⁹»⎚⪫υω

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

E³S

Copy the three lines of input to the canvas.

F÷L賫

Loop over the digits...

J⊕׳ι¹

... jump to the middle of the digit...

⊞υ÷⌕”{“→✂⌕Q¤αA?=¹⧴Zb⁻e⁵ηπ⪫Q¡”⁺KK⪫KMω⁹

... and concatenate the nine characters of the digit and look it up in a compressed string, dividing the index by 9.

»⎚⪫υω

Clear the canvas and output the detected digits.

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

PHP, 104 102 bytes

-2 bytes thanks to Arnauld

for(;!$i||$d&&print'6930274581'[crc32($d)%63%10];$i++)for($d=0;$a=$argv[++$$i];$d.=substr($a,$i*3,3));

Try it online!

Takes input as 3 separate strings.

How?

I create a single line string for each digit with a leading "0" (10 in total for each digit), this is basically 3 lines of 3 characters for each digit concatenated to each other without new lines and a "0" at start of them. For string of each digit I get the cyclic redundancy checksum polynomial of 32-bit lengths (crc32) and then do a mod 63 and mod 10 on it to get unique values for each digit. Then using the unique values and a mapping I simply output the actual digit. Look at the table below for a visual demonstration:

| Digit | Single line string | CRC32      | %63 | %10 |
|-------|--------------------|------------|-----|-----|
| 0     | "0 _ | ||_|"       | 4018184249 |  23 |   3 |
| 1     | "0     |  |"       |  750606692 |  59 |   9 |
| 2     | "0 _  _||_ "       | 2375221876 |  34 |   4 |
| 3     | "0 _  _| _|"       | 2330511487 |  52 |   2 |
| 4     | "0   |_|  |"       |  745793270 |  26 |   6 |
| 5     | "0 _ |_  _|"       | 1887416553 |  57 |   7 |
| 6     | "0 _ |_ |_|"       |  366569277 |  60 |   0 |
| 7     | "0 _   |  |"       | 1506663000 |  45 |   5 |
| 8     | "0 _ |_||_|"       | 2483214119 |   8 |   8 |
| 9     | "0 _ |_| _|"       | 4054057203 |  21 |   1 |

Commented

for(;                     // outer loop, iterates on digits
  !$i||                   // this allows the first iteration of loop to happen
  $d&&                    // $d is the single line string of each digit
                          //   continue the loop as long as $d is not empty
  print                   // and if $d is not empty, print
    '6930274581'          //   a character from mapping string
    [crc32($d)%63%10];    //   at index of CRC32 of the digit's single line string % 63 % 10
  $i++                    // increment $i by one, $i indicates which digit we are reading now
)
  for(                    // inner loop to get single line string of each digit
                          //   this loop iterates 3 times as we have 3 separate inputs
    $d=0;                 // set $d to "0" on start of each loop
    $a=$argv[++$$i];      // set $a to appropriate input (input 1 or 2 or 3)
    $d.=substr($a,$i*3,3) // concatenate a sub string of current digit from $a to $d
  );
\$\endgroup\$
3
  • 1
    \$\begingroup\$ 102 bytes by initializing $d to 0 and using another hash function. \$\endgroup\$
    – Arnauld
    Oct 20, 2019 at 1:18
  • \$\begingroup\$ @Arnauld, thanks, I have to create a brute force tool later as your idea opens up so many possibilities. I can set $d to any character (a-z, A-Z, 0-9, _) and then if I can find some mods that return 1-10 (without 0), I can save another byte by using _0123456789 instead of '0123456789'. \$\endgroup\$
    – Night2
    Oct 20, 2019 at 1:49
  • \$\begingroup\$ Oh, never mind!!! I have to add a space before _ and $d must be equal to falsy, so no bytes will be saved. Idiot me! \$\endgroup\$
    – Night2
    Oct 20, 2019 at 3:08
1
\$\begingroup\$

Jelly, 26 bytes

s€3=⁶ZF€Ḅ%15“pŻḣṾ:’b⁴¤iⱮ’Ḍ

Try it online!

A monadic link taking a list of three Jelly strings and returning an integer. Converts each input into a binary list where 1 is space and 0 anything else, converts back from binary, takes mod 15 (lowest divisor that yields unique output for each digit) and then looks these up in a list of 10 values to extract the relevant digit. Finally converts from a list of decimal digits to an integer.

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

Jelly, 21 bytes

20 if a list of digits is acceptable -- remove trailing
...or if we may print with any leading zeros -- remove trailing and replace the D with a

ZOḅ3/ḅ⁹%129ị“ȯṂṾ;’D¤Ḍ

A monadic Link accepting a list of the three lines which yields an integer.

Try it online!

How?

Magic...

ZOḅ3/ḅ⁹%129ị“ȯṂṾ;’D¤Ḍ - Link: list of lists of characters
Z                     - transpose
 O                    - to ordinals
   3/                 - three-wise reduce using:
  ḅ                   -   (left list) from base (right) (vectorises)
      ⁹               - literal 256
     ḅ                - (left list) from base (256)
        129           - literal 129
       %              - (left) modulo (129) (vectorises)
                   ¤  - nilad followed by link(s) as a nilad:
            “ȯṂṾ;’    -   base 250 integer = 3792546810
                  D   -   to digits = [3,7,9,2,5,4,6,8,1,0]
           ị          - (left) index into (right) 1-indexed & modular (vectorises)

Huh?...

After ZO we have a list of columns of 32s, 95s, and 124s where there were spaces, underscores, and pipes, respectively. For example:

 _  _ 
 _||_   -> [[32,32,32],[95,95,95],[32,124,124],[32,124,124],[95,95,95],[32,32,124]]
 _||_|

The three-wise reduce using base conversion with vectorisation effectively takes each group of three lists (i.e. an input-digit), first converting the left-most to each base defined by the middle list and then converts that resulting list to each base defined by the right-most list:

[32,32,32]ḅ95 = 32×95²+32×95+32 = 291872
->
[32,32,32]ḅ[95,95,95] = [291872,291872,291872]

[291872,291872,291872]ḅ32 = 291872×32²+291872×32+291872 = 308508704
and
[291872,291872,291872]ḅ124 = 291872×124²+291872×124+291872 = 4524307872
->
[291872,291872,291872]ḅ[32,124,124] = [308508704,4524307872,4524307872]

similarly
([32,124,124]ḅ[95,95,95])ḅ[32,32,124] = [317844128,317844128,4661212704]

so  applying ZOḅ3/ we have:
 _  _ 
 _||_   -> [[308508704,4524307872,4524307872],[317844128,317844128,4661212704]]
 _||_|

This gives a unique triples for each input-digit which we convert to unique integers by conversion from base 256 (ḅ⁹) and modulo by 129 (%129) to find unique integers which remain unique if modulo-ed by ten.

   ZOḅ3/                              ḅ⁹              %129  (implicit %10 of ị)
0  [309401120,4628496048,4628496048]  21466435284656  110   0
1  [ 35751968, 524305824, 524305824]   2477787571616  119   9
2  [308605948,4525733964, 308605948]  21383695908860  104   4
3  [308508704,4524307872,4524307872]  21381173548448   41   1
4  [ 47306784, 602657424, 602657424]   3255180354192    6   6
5  [317746884, 317746884,4659786612]  20909862778740   15   5
6  [317844128, 317844128,4661212704]  20916262082080   97   7
7  [299993120,4492051872,4492051872]  20814806443424   92   2
8  [317844128,4661212704,4661212704]  22028164437536    8   8
9  [317746884,4659786612,4659786612]  22021424949108   93   3

(256 was chosen because it is a short literal in Jelly, ḅ14%67 is the same length and also works but the lookup integer, 5987203416, takes an extra base 250 digit - “¡⁴,>ʠ’.)

 _  _      ZOḅ3/ḅ⁹                            %129       (implicit %10 of ị)
 _||_   -> [21381173548448,20916262082080] -> [41,97] -> [1,7]
 _||_|

and the 1st and seventh elements of “ȯṂṾ;’D = [3,7,9,2,5,4,6,8,1,0]
...are three and six -> [3,6]
\$\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.