18
\$\begingroup\$

I think the ASCII art questions on PPCG are fun to do, but personally I think it can be pretty hard, especially when the question is tagged as .

I wonder if anyone here might have some tips that will be useful for producing ASCII art.

Simply arranging characters is easy, but with (short) algorithms, things will become more complicated.

I'm talking about ASCII art like:

  • Text to ASCII art (characters)
  • Images (logos or icons)

I'm just looking for general tips, but language-specific is allowed since most of them can be translated anyhow.

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

Compression algorithms

You can apply LZMA compression to the string.
Many languages support it.

Run-length encoding

You can use processing instructions such as [char][number] (e.g. b12).
This compression algorithm is used here: https://codegolf.stackexchange.com/a/20589/10920

Further reading: http://en.wikipedia.org/wiki/Run-length_encoding

Integer packing

You can use arrays of integers to store small shapes such as:

// This is an invader!
// (SE line height makes it looks awful)
// ~158 characters

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

Each space will be translated into a 0.
Each sharp will be translated into a 1.

// ~58 characters
// Saved ~100 bytes!
[
  196656,  49344,   262128,  999228,
  4194303, 3407859, 3342387, 62400
]

Each bit is then read using bitwise operator &.

The above algorithm could be improved by using a bigger integer base:

// ~44 characters
// Integers are in base 36.
// Use `_` as a separator (or a line break).
"47qo_122o_5m9c_lf0c_2hwcf_211ir_1zn03_1c5c"
\$\endgroup\$
  • 3
    \$\begingroup\$ Your processing instructions is commonly known as run-length encoding, FYI. \$\endgroup\$ – FireFly Feb 12 '14 at 12:50
  • \$\begingroup\$ @FireFly Thanks! I didn't know there was a name for that. \$\endgroup\$ – Florent Feb 12 '14 at 12:53
  • \$\begingroup\$ In par (my language) this can be extended further as it supports integers encoded up to base 62: [0-9A-Za-z] \$\endgroup\$ – Cyoce Dec 15 '15 at 0:46
5
\$\begingroup\$

Look for symmetry

Sometimes the required ASCII art is symmetric at some point. For example, Argyle ASCII Art requires an output similar to this:

    /\        /\
   /  \  /\  /  \
/\/    \/  \/    \/\
\/\    /\  /\    /\/
   \  /  \/  \  /
    \/        \/

One could just print this normally, but depending on the language, the code required can be shortened by only generating the top half of the result, reversing it and swapping / and \.

Try transposing

In ASCII Art Archery Arrows the result to print is this, scaled to a given n:

     /\
    /  \
   /    \
  /      \
  \      /
   \____/
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
   /|  |\
  / |  | \
 /  |  |  \
/   |  |   \
/   |  |   \
/   |__|   \
/  /    \  \
/ /      \ \
//        \\
/          \

If we take a look at the arrow, we can see there's 8 kinds of lines:

/ \
\ /
\_/
| |
/ | | \
/ |_| \
/ / \ \
/ \

Let's try the same for its transpose.

                         ///////
                        /     / 
   /\                  /     /  
  /  \                /     /   
 /   _||||||||||||||||||||||    
/    _                     _    
\    _                     _    
 \   _||||||||||||||||||||||    
  \  /                \     \   
   \/                  \     \  
                        \     \ 
                         \\\\\\\

Here, there are 10 kinds of lines.

/
/ /
/ \ / /
/ _|
/ _ _
\ _ _
\ _|
\ / \ \
\ \
\

But here's the catch: the bottom 5 are identical to the top 5, except for swapping / and \. As per the previous rule, you can first generate the first 5, copy, do the swap, and finally transpose to get the arrow. This can save a lot of code.

\$\endgroup\$
5
\$\begingroup\$

Control characters, escape sequences and console codes

Unless the question forbids them, the current consensus on Meta is that ASCII art challenges do not require a specific byte stream, but output that looks correct.

This means that we can use ASCII control characters, ANSI escape sequences and Linux console codes in our answers, assuming a supporting terminal.

Unless specified otherwise, the rest of this answer will explain the behavior of Linux terminals, which is what I have available for testing right now.

ASCII control characters

Support/interpretation varies from terminal to terminal and character to character. The most portable one ought to be the linefeed (\n, \x0a), which moves the character to the beginning of the next line.

Other useful characters include:

  • The vertical tab (\v, \x0b) moves the cursor one position to the right, then one position down.

    $ echo -e 'a\vb'
    a
     b
    
  • The carriage return (\r, \x0d) moves the cursor to the beginning of the current line. Any subsequent printable character will overwrite the first character of the current line.

    $ echo -e 'ab\rc'
    cb
    
  • The backspace (\b, \x08) moves the cursor one position to the left. Any subsequent printable character will overwrite the character before the backspace.

    $ echo -e 'ab\bc'
    ac
    
  • The escape (\e, \x1b) does nothing on its own, but forms part of ANSI escape sequences (optionally) and Linux console codes.

Many languages permit actual control characters in the source code.

ANSI escape sequences

(yet to come)

Linux console codes

While there are many more, the most useful console codes for ASCII art are probably these:

  • The sequence \ec will reset the terminal. This clears the screen, moes the cursor the upper left corner and sets fore- and background color, cursor blinking rate, etc. to their default values.

  • The sequence \eM causes a reverse linefeed, i.e., the cursor will move one position up.

    $ echo -e '\na\eMb\n'
     b
    a
    
  • The sequence \eH sets the tab stop at the current column.

    $ echo -e '   \eHa\n\tb'
       a
       b
    
\$\endgroup\$
2
\$\begingroup\$

Look for patterns

This one might be a bit obvious, but... look for patterns, similarities and repetitions in the output. For instance, when I saw the Transform number into 7-segment display pattern task I started thinking about how one could golf it, and started looking for similarities. Due to the way the horizontal segments go between the vertical ones in the character matrix it'd probably be easiest to deal with three segments at a time, grouped together as such (adding two "always empty" segments for the first, topmost one):

segments groping

Thus, you could do something like lc + " "*N + rc + "\n" N-1 times and then lc + bc*N + rc once, for every three segments (lc, bc, rc being the left-, bottom-, and right-segment characters, i.e. one of |, _ or  ).

\$\endgroup\$
2
+400
\$\begingroup\$

Use base conversion

This answer was to a question which wanted ASCII art which consisted of the characters + |- and newlines. Since there are only 5 possible characters, these can be treated as a base 5 number and converted to bytes, packing 3.45 characters per byte.

Exploit regularities

Often, the data will have some regularities, even if those regularities aren't strong enough to employ specific tools such as mirroring. For instance, in the above question, the desired output had newlines roughly evenly spaced throughout the test, since the text was roughly rectangular. I exploited this to shorten my code, by using Pyth's split into n pieces function, then joining on newlines.

Know your tools, and choose the right one for the job.

The most powerful and efficient text processing tools I know are:

Regex engines: ///, Retina, Perl, in order of power/conciseness tradeoff.

Use if the thing you want to do can be conscisely described in regex substitutions, such as this answer

Obscure text-processing tools: gema, etc. (I'm sure there are others, but they're too obscure)

Use if they have a feature that is exactly what you need, that nothing else has. Such as in this question, with gema's recursive matching.

General code golfing languages: CJam, Pyth, etc.

Use if you're exploiting some subtlety complex enough that no other tool will do the job or it just does the job shorter.

Try many approaches

This applies in every code-golf question, but especially here. You're not going to know whether a regularity is exploitable until you try it out. Possibly in multiple languages.

\$\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.