34
\$\begingroup\$

Inspired by Golf me an ASCII Alphabet, of which this challenge is (almost) a direct inverse.


Task:

Take a string of ASCII-art text and output the content of the text as regular ASCII text.


Input:

String of ASCII-art text.

Input will only contain instances of ASCII character #, spaces and 4 or 5 newlines (a trailing newline is optional). All lines have the same length. (That is, the last ASCII-art letter is padded with trailing spaces.) You can use some other printable ASCII character instead of # in the input if you wish.

The input will contain ASCII-art letters A-Z and ASCII-art spaces (a 5x5 block of whitespace). No punctuation. There is only one line of ASCII-art text (5 actual lines). There will be no trailing or leading ASCII-art spaces, nor will there be adjacent ASCII-art spaces.

Letter size is 5x5 characters. There is a 1x5 space between each letter. Space between words is a 5x5 block of whitespace (+ 1x5 space on each side, because it is just another letter). There will be no 1x5 space at the end or at the beginning, only between ASCII-art letters.


Output:

String containing the text as ASCII characters A-Z + spaces. The output can be in lowercase also, if that is somehow easier for your solution. Mixed case is also allowed.


The ASCII-art letters:

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

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

The space:

     |
     | A 5x5 square of spaces.
     | (Padded with |s to make it appear in this post.)
     |
     |

Examples:

Input:

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

Output: HELLO WORLD

Input:

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

Output: ASCII

Input:

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

Output: PPCG


This is , so the shortest answer in bytes wins.

\$\endgroup\$
  • 10
    \$\begingroup\$ I'm thinking the solution to this might involve some sort of hash function... \$\endgroup\$ – Neil Apr 7 '17 at 12:20
  • 6
    \$\begingroup\$ Bounty from me if you do this by converting the input to an image and solves this using image processing! (The solution must be golfed too of course) \$\endgroup\$ – Stewie Griffin Apr 7 '17 at 12:34
  • 3
    \$\begingroup\$ If it's of help to anyone: either the second row, the fourth row, or the middle column of the letters can be dropped without losing relevant information. \$\endgroup\$ – Martin Ender Apr 7 '17 at 12:57
  • 1
    \$\begingroup\$ @JungHwanMin Hmm. I think not, because that would not be very human-readable ASCII-art text. \$\endgroup\$ – Steadybox Apr 7 '17 at 17:04
  • 1
    \$\begingroup\$ @JonathanAllan I guess that would be ok, too. \$\endgroup\$ – Steadybox Apr 7 '17 at 21:00
13
\$\begingroup\$

Jelly,  50 44  42 bytes

ỴZ;6/UOḂḅ7‘ị“¥ŒƲVĊ⁾|W£⁼³ƭÇuʋụzḢĖ0ḢẆẠØsĠỌỊ»

Try it online! (note the argument does not require the leading newline, but since leading and trailing newlines have no effect I included one to make the multiline string more human-readable)

Results are mixed case (as allowed by the OP in a comment).

How?

Splits on new lines, transposes, and joins together sub-slices of (up to) six together to get the character representations and reverses each (equating the later base conversion for the final character of length 25 to all the others of length 30). Then maps '#' and ' ' to one and zero respectively using the fact that '#' has an odd ordinal while ' ' has an even one. Reads each as if it were a base seven number. Effectively takes modulo 81 of each (to yield 27 unique values for the 27 possible cases), and finally indexes into a "magic string" with the correct chars at the correct indexes (modulo indexing is used with a magic string of length 81 to save 2 bytes).

Here is the "magic string" I created along with a (case-insensitive) regex pattern it needed to match (I added "ed" to make it length 81):

 ' affectedly Azerbaijan rewaxed naganas jiujitsudankly pase UVB freqHaarlemcoacted'
'^ ..f...e.....z......a..r.w.x...n.g......iuj....d..kly.p.s...vb....qh.....m.o.ct.*'

As such it may be compressed, looking up eleven sub-slices as words in Jelly's dictionary (most of which use the leading space default):

' affectedly Azerbaijan rewaxed naganas jiujitsudankly pase UVB freqHaarlemcoacted'
 ^          ^          ^       ^       ^        ^     ^    ^   ^    ^      ^

which results in the Jelly compressed string, “¥ŒƲVĊ⁾|W£⁼³ƭÇuʋụzḢĖ0ḢẆẠØsĠỌỊ»

The rest of the code works as follows:

ỴZ;6/UOḂḅ7‘ị“...» - Main link: multi-line string, s   e.g. HI as the #s and spaces
Ỵ                 - split on new lines                     ["#   # #####","#   #   #  ","#####   #  ","#   #   #  ","#   # #####"] (each is actually a list)
 Z                - transpose                              ["#####","  #  ","  #  ","  #  ","#####","     ","#   #","#   #","#####","#   #","#   #"] (each is actually a list)
   6/             - six-wise reduce by
  ;               -     concatenation                      ["#####  #    #    #  #####     ","#   ##   #######   ##   #"] (each is actually a list)
     U            - upend (reverse each)                   ["     #####  #    #    #  #####","#   ##   #######   ##   #"] (each is actually a list)
                  -     note: all except the last will be length 30 and like "     ...", which will become [0,0,0,0,0,...], while the last will be length 25 without those five leading zeros.
      O           - cast to ordinals ('#' -> 35, ' '-> 32) [[32,32,...],[35,32,...]]
       Ḃ          - modulo 2 ('#' -> 1, ' ' -> 0)          [000001111100100001000010011111, 1000110001111111000110001] (each is actually a list)
        ḅ7        - convert from base 7 (vectorises)       [223498370543967315553, 191672428080864454753] (these are now integers)
          ‘       - increment                              [223498370543967315554, 191672428080864454754]
                  -  (modulo 81 these would be [68, 41])
           ị      - index into (modulo & 1-indexed):                        
            “...» -     the "magic string" described above ' affectedly Azerbaijan rewaxed naganas jiujitsudankly pase UVB freqHaarlemcoacted'
                                                           "Hi"                                   41^                        68^
\$\endgroup\$
14
\$\begingroup\$

Python 2, 405 335 234 182 171 bytes

lambda s,j=''.join:j(' QPVXU_O__FBLK_JMD_CSYZWIENH_AG___TR'[int(j(`ord(y)%2`for y in j(s.split('\n')[x][i:i+5]for x in range(5))),2)%13836%37]for i in range(0,len(s)/5,6))

Try it online!


Finally shorter than JS

\$\endgroup\$
  • \$\begingroup\$ Clever use of the modulus, but I can't help thinking there must be a way to do: [0,2,3,7,...] and ' UBGOTA... split it, and use some sort of mapping. 0:' ',2:'U',3:'V'... looks so long,,, there are so many :'',. (I know you had something similar in the original post, but with very long numbers. \$\endgroup\$ – Stewie Griffin Apr 7 '17 at 13:45
  • 1
    \$\begingroup\$ @StewieGriffin it is better now \$\endgroup\$ – ovs Apr 7 '17 at 14:14
11
\$\begingroup\$

JavaScript (ES6), 204 186 184 182 bytes

Saved 18 bytes thanks to Neil
Saved 2 bytes thanks to ETHproductions
Saved 2 bytes thanks to YairRand

Breakdown:

  • 42-byte lookup table
  • 162 144 142 140 bytes of code
s=>(a=s.split`
`)[0].replace(/.{6}/g,(_,n)=>' H_JM__WDCSORLU___QKG_P_AFT_N_EI_XBV____YZ'[[0,1,2,4].reduce((p,r,i)=>p+='0b'+a[r].substr(n,5).replace(/./g,c=>1^1-c)<<i*6,0)%178%69%43])

Demo

let f =

s=>(a=s.split`
`)[0].replace(/.{6}/g,(_,n)=>' H_JM__WDCSORLU___QKG_P_AFT_N_EI_XBV____YZ'[[0,1,2,4].reduce((p,r,i)=>p+='0b'+a[r].substr(n,5).replace(/./g,c=>1^1-c)<<i*6,0)%178%69%43])

console.log(f(
  ' ###   ###   ###  ##### ##### \n'+
  '#   # #     #   #   #     #   \n'+
  '#####  ###  #       #     #   \n'+
  '#   #     # #   #   #     #   \n'+
  '#   #  ###   ###  ##### ##### \n'
));

\$\endgroup\$
  • 1
    \$\begingroup\$ You can save a whole bunch of bytes using (a=s.split`\n`)[0].replace(/......?/g, with substr(n,5) and without the join of course. \$\endgroup\$ – Neil Apr 7 '17 at 18:21
  • \$\begingroup\$ I think you can save a byte with c=>0|c>' ', and another with p+='0b'+... \$\endgroup\$ – ETHproductions Apr 7 '17 at 18:57
  • \$\begingroup\$ As mentioned by @Steadybox in the Comment section of the Challenge, taking \n in input is invalid. However, I think you can use template literal with actual newline as input, just like you are doing in the split method. \$\endgroup\$ – Arjun Apr 8 '17 at 0:55
  • 1
    \$\begingroup\$ @DobbyTheFree-Elf I could indeed use a template literal -- which incidentally would impair the readability because of the leading quote. But adding a constraint on the way the input data is formatted before being passed to the function is out of topic, IMHO (as long as the actual content of the input is valid). \$\endgroup\$ – Arnauld Apr 8 '17 at 12:48
  • 1
    \$\begingroup\$ @DobbyTheFree-Elf I'm afraid my comment was a bit ambiguous, and I may have slightly misunderstood the question I was answering. Using \n in a string literal at the call site in the code is okay, because the actual string that is passed to the function includes only the newline character, not both \` and n. Passing a string that contains \` and n as separate adjacent characters would not be okay. \$\endgroup\$ – Steadybox Apr 8 '17 at 21:27
9
+50
\$\begingroup\$

Bash + ImageMagick + Tesseract, 161 bytes

I wanted to try the approach suggested by @stewie-griffin and went for bash + ImageMagick (to convert a string to an image) and Tesseract (to do the OCR). Here's my code, which works for the 'HELLO WORLD' testcase, but fails the other ones. Maybe some tweaking to the parameters (font, font size, kerning, spacing) helps.

convert -font Courier-Bold -pointsize 8 -interline-spacing -3 -kerning -3 label:"$(</dev/stdin)" -bordercolor White -border 5%x20% png:- | tesseract stdin stdout

Just copy-paste the ascii art into the commandline after running the command. Finish your input by pressing ^d.

Current output for the test cases:

  • HELLO WORLD: HELLO WORLD
  • ASCII: H5511
  • PPCG: PPOG
\$\endgroup\$
6
\$\begingroup\$

Scala, 184 181 bytes

A magic string + modulo solution based on hashCode :)

(a:String)=>a.split("\n").map(_.grouped(6)map(_.take(5))toArray).transpose.map(l=>"Q__WLUY_XOI_ZN_GEFR_P__JKBMV_S__ __C__D__H_T__A"(Math.abs(l.mkString.hashCode)%106%79%47))mkString

Try online (Scalafiddle)

More readable :

(a:String) => a.split("\n")
                .map(_.grouped(6)map(_.take(5))toArray)
                .transpose
                .map ( l => 
                    "Q__WLUY_XOI_ZN_GEFR_P__JKBMV_S__ __C__D__H_T__A"(
                        Math.abs(l.mkString.hashCode)%106%79%47
                    )
                )mkString

Explanations

  • The initial String (ASCII art) is split into 5 lines (height of ASCII character)
  • Each line is split in 6-characters elements (width of ASCII character + 1 space)
  • Only the first 5 characters are kept (the space at the end is useless)
  • The lines are transposed (each ASCII character is represented as a sequence of 25 characters (5x5), containing either '#' or ' ')
  • Each ASCII character representation (sequence) is converted to String and an absolute hashcode is computed for that String (absolute needed because of next modulus)
  • 3 consecutive modulus (% 106 % 79 % 47) are applied to associate a number ∈ [0; 47[ for each ASCII character (see explanations below)
  • This number is used as an index of the magic string

How to get the magic string ?

First, I represented all letters like this :

case class Letter(letter: Char, ascii: Seq[Char])

Then, I created an alphabet containing ASCII representations of all characters :

Example :

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

becomes

Letter('A', " ### #   #######   ##   #") // 25 characters from top-left to bottom-right

For each letter, an absolute hashcode was computed (all hashcodes are distinct) :

val codes = alphabet.map { case Letter(l, a) => (l, Math.abs(a.mkString.hashCode)) }
// codes: Seq[(Char, Int)] = List(( ,253243360), (A,380997542), (B,1221679148), (C,1573119535), (D,307929190), (E,858088672), (F,857996320), (G,750155628), (H,897290147), (I,1518088099), (J,928547488), (K,1184149391), (L,519601059), (M,741735953), (N,2139154001), (O,1625960980), (P,1307658950), (Q,92382816), (R,1221771494), (S,1689301359), (T,1515228067), (U,1390718627), (V,386730851), (W,733134481), (X,628338619), (Y,23919695), (Z,2081560145))

Then I tried to decrease each code, but always respecting the fact that each code must be unique (the list grouped by the code must have 27 elements, 1 for each letter). So I tried the first 200 modulus :

val mod = (1 to 200).find(modulo => codes.map { case (a,b) => (a, b % modulo) }.groupBy(_._2).size==27).get

I found 106 as the first modulo to be applied :

val codes2 = codes.map { case (l, c) => (l, c%mod) }
val codes = codes2
// codes: Seq[(Char, Int)] = List(( ,32), (A,46), (B,104), (C,35), (D,38), (E,16), (F,96), (G,94), (H,41), (I,89), (J,102), (K,71), (L,83), (M,105), (N,13), (O,56), (P,20), (Q,0), (R,18), (S,29), (T,43), (U,5), (V,27), (W,3), (X,87), (Y,53), (Z,91))

I repeated the previous steps until the smallest modulo. I found :

  • 79
  • 47
  • 44
  • 42

Note : The last modulo I chose (47) is not the smallest here :

  • I found 44, but if I had chosen 44, the magic string would have size 44 (instead of 47) but I would have to write %106%79%47%44 (13 characters instead of %106%79%47 = 10 characters). So in bytes, the code should have had the same size as the one I got
  • There is also 42, but then the code should have had 1 byte more than the one I got

Next, I applied the consecutive modulus (% 79 % 47) to the last codes, to get the definitive codes associated to each letter :

codes: Seq[(Char, Int)] = List(( ,32), (A,46), (B,25), (C,35), (D,38), (E,16), (F,17), (G,15), (H,41), (I,10), (J,23), (K,24), (L,4), (M,26), (N,13), (O,9), (P,20), (Q,0), (R,18), (S,29), (T,43), (U,5), (V,27), (W,3), (X,8), (Y,6), (Z,12))

Finally, to construct the magic string :

val initialMap = (0 until 47).map(i => (i, '_')).toMap
val codesMap = codes.map(i => (i._2, i._1)).toMap

val magicString = (initialMap ++ codesMap).toSeq.sortBy(_._1).map(_._2).mkString
// magicString: String "Q__WLUY_XOI_ZN_GEFR_P__JKBMV_S__ __C__D__H_T__A"

Example : The letter A above is associated to 46 (380997542 % 106 % 79 % 47), and the 46th element of the magic string is A :)

Test cases

// assign function to f
val f = (a:String)=>a.split("\n").map(_.grouped(6)map(_.take(5))toArray).transpose.map(l=>"Q__WLUY_XOI_ZN_GEFR_P__JKBMV_S__ __C__D__H_T__A"(Math.abs(l.mkString.hashCode)%106%79%47))mkString

HELLO WORLD :

val asciiArt = """|#   # ##### #     #      ###        #   #  ###  ####  #     #### 
                  |#   # #     #     #     #   #       #   # #   # #   # #     #   #
                  |##### ####  #     #     #   #       # # # #   # ####  #     #   #
                  |#   # #     #     #     #   #       ## ## #   # #   # #     #   #
                  |#   # ##### ##### #####  ###        #   #  ###  #   # ##### #### """.stripMargin

f(asciiArt)    // HELLO WORLD

ASCII :

val asciiArt = """| ###   ###   ###  ##### #####
                  |#   # #     #   #   #     #  
                  |#####  ###  #       #     #  
                  |#   #     # #   #   #     #  
                  |#   #  ###   ###  ##### #####""".stripMargin

f(asciiArt)    // ASCII

PPCG :

val asciiArt = """|####  ####   ###   ### 
                  |#   # #   # #   # #    
                  |####  ####  #     #  ##
                  |#     #     #   # #   #
                  |#     #      ###   ### """.stripMargin

f(asciiArt)    // PPCG

Edits

  • Saved 3 bytes by removing . before map, toArray and mkString
\$\endgroup\$
3
\$\begingroup\$

PHP, 294 Bytes

<?$l=" 00000YE00G0000R000A0Q0000C0BW000K00000000000LT00000J00000000MU0000Z0000DI000000V0000000P00H0000ONF000S00X";preg_match_all("#(.{5})\s#s","$_GET[0] ",$t);for($i=0;$i<$c=count($a=$t[1])/5;$i++)$s.=$l[bindec(strtr($a[$i].$a[$i+$c].$a[$i+2*$c].$a[$i+3*$c].$a[$i+4*$c]," #","01"))%106];echo$s;

Try it online!

Expanded

$l=" 00000YE00G0000R000A0Q0000C0BW000K00000000000LT00000J00000000MU0000Z0000DI000000V0000000P00H0000ONF000S00X"; # search string mod 106
preg_match_all("#(.{5})\s#s","$_GET[0] ",$t); # Regex take each group of five chars followed by a whitespace
for($i=0;$i<$c=count($a=$t[1])/5;$i++)
  $s.=$l[bindec(strtr($a[$i].$a[$i+$c].$a[$i+2*$c].$a[$i+3*$c].$a[$i+4*$c]," #","01"))%106]; # join each groups make a binaray make a decimal mod 106  
echo$s; # Output

Converting the Input to an image format

@Stevie Griffin search a solution to get this from an image. I think he want not really the image format I have use.

echo'<svg xmlns="http://www.w3.org/2000/svg" width="100%"><switch><foreignObject x="0" y="0" width="100%" height="300"><body xmlns="http://www.w3.org/1999/xhtml"><pre>'.$_GET[0].'</pre></body></foreignObject></switch></svg>';

SVG can contains HTML parts if then included in a foreignObject. So I put a pre Element in a SVG.

Image Output

<svg xmlns="http://www.w3.org/2000/svg" width="100%"><switch><foreignObject x="0" y="0" width="100%" height="300"><body xmlns="http://www.w3.org/1999/xhtml"><pre>#   # ##### #     #      ###        #   #  ###  ####  #     #### 
#   # #     #     #     #   #       #   # #   # #   # #     #   #
##### ####  #     #     #   #       # # # #   # ####  #     #   #
#   # #     #     #     #   #       ## ## #   # #   # #     #   #
#   # ##### ##### #####  ###        #   #  ###  #   # ##### #### </pre></body></foreignObject></switch></svg>

Solving from Image Changes

SVG is machine readable so after saving the SVG as "i.svg" you need only replace $_GET[0] with preg_replace("#(^.*e>)(.*)(</p.*$)#s","$2",join(file("i.svg"))) in the way with normal input + 55 Bytes

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

Powershell, 152 146 bytes

-join$(for($t=$args-split'
';$c-lt$t[0].Length;$c+=6){$s=0;$t|% s*g $c,5|% t*y|%{$s+=$s+$_}
'_ISRJ_BK_HFQPL_MYNCE _TXDAO_VWUZ__G'[$s%578%174%36]})

Test script:

$f = {

-join$(for($t=$args-split'
';$c-lt$t[0].Length;$c+=6){$s=0;$t|% s*g $c,5|% t*y|%{$s+=$s+$_}
'_ISRJ_BK_HFQPL_MYNCE _TXDAO_VWUZ__G'[$s%578%174%36]})

}

&$f @"
#   # ##### #     #      ###        #   #  ###  ####  #     #### 
#   # #     #     #     #   #       #   # #   # #   # #     #   #
##### ####  #     #     #   #       # # # #   # ####  #     #   #
#   # #     #     #     #   #       ## ## #   # #   # #     #   #
#   # ##### ##### #####  ###        #   #  ###  #   # ##### #### 
"@

&$f @"
 ###   ###   ###  ##### #####
#   # #     #   #   #     #  
#####  ###  #       #     #  
#   #     # #   #   #     #  
#   #  ###   ###  ##### #####
"@

&$f @"
####  ####   ###   ### 
#   # #   # #   # #    
####  ####  #     #  ##
#     #     #   # #   #
#     #      ###   ### 
"@

&$f @"
       ###  ####   ###  ####  ##### #####  ###  #   # ##### ##### #   # #     #   # #   #  ###  ####   ###  ####   ###  ##### #   # #   # #   # #   # #   # #####
      #   # #   # #   # #   # #     #     #     #   #   #     #   #  #  #     ## ## ##  # #   # #   # #   # #   # #       #   #   # #   # #   #  # #   # #     # 
      ##### ####  #     #   # ####  ####  #  ## #####   #     #   ###   #     # # # # # # #   # ####  #   # ####   ###    #   #   #  # #  # # #   #     #     #  
      #   # #   # #   # #   # #     #     #   # #   #   #   # #   #  #  #     #   # #  ## #   # #     #  #  #   #     #   #   #   #  # #  ## ##  # #    #    #   
      #   # ####   ###  ####  ##### #      ###  #   # ##### ###   #   # ##### #   # #   #  ###  #      ## # #   #  ###    #    ###    #   #   # #   #   #   #####
"@

Output:

HELLO WORLD
ASCII
PPCG
 ABCDEFGHIJKLMNOPQRSTUVWXYZ

Note:

  1. $t|% s*g $c,5|% t*y|%{$s+=$s+$_} is shortcut for $t.substring($c,5).toCharArray()|%{$s+=$s+$_}
  2. ("abcd","efgh").substring(1,2) returns the array ("bc","de")
  3. ("bc","de").toCharArray() returns the array ('b','c','d','e')
\$\endgroup\$
2
\$\begingroup\$

C,  225  209 bytes

Thanks to @ceilingcat for saving 16 bytes!

i,j,k,l,n,m;f(char*s){l=index(s,10)-s+1;for(i=0;i<l/6;++i){for(m=j=0;j<5;m+=n*(exp10(j++)+.1))for(n=k=0;k<5;)n+=(s[i*6+j+k*l]==35)<<k++;for(j=0;"Qi Y$>W);Xa%d^F4K-]7jcMAG="[j++]-32-m%77;);putchar(n?64+j:32);}}

Try it online!

\$\endgroup\$

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.