11
\$\begingroup\$

Today I stumbled upon CodinGame, a site with programming challenges. I'll describe one of them.

ASCII art allows you to represent forms by using characters. To be precise, in our case, these forms are words. For example, the word "MANHATTAN" could be displayed as follows in ASCII art:

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

​Your mission is to write a program that can display a line of text in ASCII art.

INPUT:
Line 1: the width L of a letter represented in ASCII art. All letters are the same width.
Line 2: the height H of a letter represented in ASCII art. All letters are the same height.
Line 3: The line of text T, composed of N ASCII characters. Following Lines: the string of characters ABCDEFGHIJKLMNOPQRSTUVWXYZ? Represented in ASCII art.

OUTPUT:
The text T in ASCII art.
The characters a to z are shown in ASCII art by their equivalent in upper case.
The characters which are not in the intervals [a-z] or [A-Z] will be shown as a question mark in ASCII art.

CONSTRAINTS :
0 < L < 30
0 < H < 30
0 < N < 200

Example input:

20
11
MANHATTAN
 .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .-----------------. .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------. 
| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |
| |      __      | || |   ______     | || |     ______   | || |  ________    | || |  _________   | || |  _________   | || |    ______    | || |  ____  ____  | || |     _____    | || |     _____    | || |  ___  ____   | || |   _____      | || | ____    ____ | || | ____  _____  | || |     ____     | || |   ______     | || |    ___       | || |  _______     | || |    _______   | || |  _________   | || | _____  _____ | || | ____   ____  | || | _____  _____ | || |  ____  ____  | || |  ____  ____  | || |   ________   | || |    ______    | |
| |     /  \     | || |  |_   _ \    | || |   .' ___  |  | || | |_   ___ `.  | || | |_   ___  |  | || | |_   ___  |  | || |  .' ___  |   | || | |_   ||   _| | || |    |_   _|   | || |    |_   _|   | || | |_  ||_  _|  | || |  |_   _|     | || ||_   \  /   _|| || ||_   \|_   _| | || |   .'    `.   | || |  |_   __ \   | || |  .'   '.     | || | |_   __ \    | || |   /  ___  |  | || | |  _   _  |  | || ||_   _||_   _|| || ||_  _| |_  _| | || ||_   _||_   _|| || | |_  _||_  _| | || | |_  _||_  _| | || |  |  __   _|  | || |   / _ __ `.  | |
| |    / /\ \    | || |    | |_) |   | || |  / .'   \_|  | || |   | |   `. \ | || |   | |_  \_|  | || |   | |_  \_|  | || | / .'   \_|   | || |   | |__| |   | || |      | |     | || |      | |     | || |   | |_/ /    | || |    | |       | || |  |   \/   |  | || |  |   \ | |   | || |  /  .--.  \  | || |    | |__) |  | || | /  .-.  \    | || |   | |__) |   | || |  |  (__ \_|  | || | |_/ | | \_|  | || |  | |    | |  | || |  \ \   / /   | || |  | | /\ | |  | || |   \ \  / /   | || |   \ \  / /   | || |  |_/  / /    | || |  |_/____) |  | |
| |   / ____ \   | || |    |  __'.   | || |  | |         | || |   | |    | | | || |   |  _|  _   | || |   |  _|      | || | | |    ____  | || |   |  __  |   | || |      | |     | || |   _  | |     | || |   |  __'.    | || |    | |   _   | || |  | |\  /| |  | || |  | |\ \| |   | || |  | |    | |  | || |    |  ___/   | || | | |   | |    | || |   |  __ /    | || |   '.___`-.   | || |     | |      | || |  | '    ' |  | || |   \ \ / /    | || |  | |/  \| |  | || |    > `' <    | || |    \ \/ /    | || |     .'.' _   | || |    /  ___.'  | |
| | _/ /    \ \_ | || |   _| |__) |  | || |  \ `.___.'\  | || |  _| |___.' / | || |  _| |___/ |  | || |  _| |_       | || | \ `.___]  _| | || |  _| |  | |_  | || |     _| |_    | || |  | |_' |     | || |  _| |  \ \_  | || |   _| |__/ |  | || | _| |_\/_| |_ | || | _| |_\   |_  | || |  \  `--'  /  | || |   _| |_      | || | \  `-'  \_   | || |  _| |  \ \_  | || |  |`\____) |  | || |    _| |_     | || |   \ `--' /   | || |    \ ' /     | || |  |   /\   |  | || |  _/ /'`\ \_  | || |    _|  |_    | || |   _/ /__/ |  | || |    |_|       | |
| ||____|  |____|| || |  |_______/   | || |   `._____.'  | || | |________.'  | || | |_________|  | || | |_____|      | || |  `._____.'   | || | |____||____| | || |    |_____|   | || |  `.___.'     | || | |____||____| | || |  |________|  | || ||_____||_____|| || ||_____|\____| | || |   `.____.'   | || |  |_____|     | || |  `.___.\__|  | || | |____| |___| | || |  |_______.'  | || |   |_____|    | || |    `.__.'    | || |     \_/      | || |  |__/  \__|  | || | |____||____| | || |   |______|   | || |  |________|  | || |    (_)       | |
| |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | |
| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |
 '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------' 

Example output:

 .----------------.  .----------------.  .-----------------. .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .-----------------.
| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |
| | ____    ____ | || |      __      | || | ____  _____  | || |  ____  ____  | || |      __      | || |  _________   | || |  _________   | || |      __      | || | ____  _____  | |
| ||_   \  /   _|| || |     /  \     | || ||_   \|_   _| | || | |_   ||   _| | || |     /  \     | || | |  _   _  |  | || | |  _   _  |  | || |     /  \     | || ||_   \|_   _| | |
| |  |   \/   |  | || |    / /\ \    | || |  |   \ | |   | || |   | |__| |   | || |    / /\ \    | || | |_/ | | \_|  | || | |_/ | | \_|  | || |    / /\ \    | || |  |   \ | |   | |
| |  | |\  /| |  | || |   / ____ \   | || |  | |\ \| |   | || |   |  __  |   | || |   / ____ \   | || |     | |      | || |     | |      | || |   / ____ \   | || |  | |\ \| |   | |
| | _| |_\/_| |_ | || | _/ /    \ \_ | || | _| |_\   |_  | || |  _| |  | |_  | || | _/ /    \ \_ | || |    _| |_     | || |    _| |_     | || | _/ /    \ \_ | || | _| |_\   |_  | |
| ||_____||_____|| || ||____|  |____|| || ||_____|\____| | || | |____||____| | || ||____|  |____|| || |   |_____|    | || |   |_____|    | || ||____|  |____|| || ||_____|\____| | |
| |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | |
| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |
 '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------' 

It's a , so the usual input validation, argument handling and over engineering was skipped. My code scores 100% (which only means it's bug free for all cases ran), but it feels like it could've been more succinct.

There's a lot of looping going around, which is probably a bad thing. And the naming probably could've been better. I didn't adhere strictly to the naming given by the assignment, since the program checking the code doesn't care anyway.

l = int(raw_input())
h = int(raw_input())
text_source = raw_input()
ascii_rows = []
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ?'
for d in xrange(h):
    ascii_row = raw_input()
    ascii_rows.append(ascii_row)

output = ['']

for i in xrange(h-1):
    output.append('')

for character in text_source:
    found = False
    for ascii in xrange(len(alphabet)):
        if character.upper() == alphabet[ascii]:
            found = True
            for it in xrange(h):
                output[it] += ascii_rows[it][l * ascii:l * (ascii + 1)]
    if not found:
        for it in xrange(h):
            output[it] += ascii_rows[it][l * ascii:l * (ascii + 1)]

for row in output:
    print row
\$\endgroup\$

5 Answers 5

7
\$\begingroup\$

Concise Looping

Whenever you find yourself needed the index of an element and the element, use enumerate(). That shortens us to:

for character in text_source:
    found = False
    for idx, letter in enumerate(alphabet):
        if character.upper() == letter:
            found = True
            for it in xrange(h):
                output[it] += ascii_rows[it][l * idx:l * (idx + 1)]
    if not found:
        for it in xrange(h):
            output[it] += ascii_rows[it][l * len(alphabet):]

Next, when you're looping until you find something, and having a check if you do, you can use a for...break...else construct. That lets you avoid the found:

for character in text_source:
    for idx, letter in enumerate(alphabet):
        if character.upper() == letter:
            for it in xrange(h):
                output[it] += ascii_rows[it][l * idx:l * (idx + 1)]
            break # no reason to keep checking anything
    else:
        # wasn't found

ascii_rows

Right now, each of your ascii_rows is the full row, and you grab the appropriate chunk as you go. However, instead of that, if you did all the chunking up front, that would make your algorithm simpler.

Let's add this chunker:

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

with which we can write:

ascii_rows = [list(chunks(raw_input(), l)) for _ in xrange(h)]

Once we chunk it up, we don't need to do any multiplication. Thus, our original loop can be shortened further to:

for character in text_source:
    for idx, letter in enumerate(alphabet):
        if character.upper() == letter:
            for it in xrange(h):
                output[it] += ascii_rows[it][idx]
            break
    else:
        for it in xrange(h):
            output[it] += ascii_rows[it][-1]

Better Searching Still

There is a find() function on strings. It will return the index, or -1 on failure. Appropriately enough, we want -1 on failure!

So we don't even need the loop at all:

for character in text_source:
    idx = alphabet.find(character.upper())
    for it in xrange(h):
        output[it] += ascii_rows[it][idx]

MOAR comprehensions?

We could just rewrite the whole business in a couple comprehensions:

indices = [alphabet.find(letter.upper()) for letter in text_source]
output = (''.join(ascii_rows[it][idx] for idx in indices)
          for it in xrange(h))
print '\n'.join(output)
\$\endgroup\$
2
  • \$\begingroup\$ I knew I was forgetting something simple, it was the enumerate. All very good points! However, the last part seems to cluttered for my taste. But it's the Python way, so I'll take it into consideration anyway. \$\endgroup\$
    – Mast
    Oct 8, 2015 at 1:36
  • \$\begingroup\$ @Mast YMMV. Just throwing it out there as an option. I'm not sure I would do it either. \$\endgroup\$
    – Barry
    Oct 8, 2015 at 12:50
10
\$\begingroup\$

Generator expressions

for loops like this that are simply appending items to lists are a little ugly:

for i in xrange(h-1):
    output.append('')

This is not ideal. Rather, you can directly set the value of output like this:

output = ['' for _ in xrange(h - 1)]

The generator expression pattern can also be applied to this block of code as well:

for d in xrange(h):
    ascii_row = raw_input()
    ascii_rows.append(ascii_row)

The above code would the become this:

ascii_rows = [raw_input() for _ in xrange(h)]

Using str.join() to print lists

Rather than iterating over a list and printing each individual element like this:

for row in output:
    print row

You can just use str.join() and print the entire list with a certain separator character, like this:

print '\n'.join(output)

This eliminates the need for a loop at all.


Input validation

I know you're probably aware of input validation, and even though this is a trivial program, it's still good to have. This means that this block of code:

l = int(raw_input())
h = int(raw_input())

Should be wrapped in a try/except block that's catching a ValueError:

try:
    l = int(raw_input())
    h = int(raw_input())
except ValueError:
    ...

Nitpicks

There's not much to nitpick, but here we go:

  • If you want cross-compatibility with Python 3, you should use parentheses with print, like this:

    print( ... )
    
  • l and h aren't very descriptive variable names. I'm assuming that they're synonymous with "length" and "height". I'd suggest changing them.

  • If you don't use a variable in a loop, then it should just name it _ rather than i, and such.
\$\endgroup\$
6
\$\begingroup\$

Your ascii_rows almost constitute a font definition. I think there should be a font = something somewhere in the program, and I would prefer that the something involve a list comprehension.

I say almost because you're storing ascii_rows in a rather raw form. As a result, your for character in text_source: … loop is rather complex. What you are missing is a better data structure. In the solution below, I've split up the work of defining the font (part of the font = … statement) and the work of composing the output (part of the output = … statement).

Actually, ascii is a bit of a misnomer. In alphabet[ascii], ascii looks like it should be an ASCII code value, but it's not.

Flag variables like found are inelegant. Inevitably, there is a need to (re-)initialize the flag, maybe set the flag, and test the flag. In this case, the entire need can be avoided with a dict.get() lookup.

from string import ascii_uppercase

# Read input
l = int(raw_input())
h = int(raw_input())
text_source = raw_input().upper()
font_source = [raw_input() for _ in range(h)]

# Define the font
repertoire = ascii_uppercase + '?'
font = [
    { char: row[l*i : l*(i+1)] for i, char in enumerate(repertoire) }
    for row in font_source
]

# Generate output
output = "\n".join(
    ''.join(row.get(char) or row['?'] for char in text_source)
    for row in font
)
print(output)
\$\endgroup\$
3
\$\begingroup\$

Instead of manually entering the alphabet, you can just use the string module. It has a constant of the uppercase ascii letters.

from string import ascii_uppercase as alphabet

Though you need to add the ? manually afterwards.

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

I solved it with a nested list comprehension. Not sure you can get more succinct if that is your goal.

l = int(raw_input())
h = int(raw_input())
text_source = raw_input().upper() # put the upper() here to make the code below easier to read
ascii_rows = []
for d in xrange(h):
    ascii_row = raw_input()
    ascii_rows.append(ascii_row)

def ascii_index(c):
    # '[' comes after 'Z' in the ascii chart.
    # ord('A') == 65, so this function returns a number
    # that can be used as an index into the ascii_row strings.
    return (ord('[') if c < 'A' or c > 'Z' else ord(c)) - 65

# No need for xrange(), just loop the data structures.
print("\n".join(["".join([ row[l*ascii_index(c):l*(ascii_index(c)+1)] for c in text_source]) for row in ascii_rows ]))
\$\endgroup\$
2
  • \$\begingroup\$ I just registered, so I can't comment on answers that are not my own. But one of the above solutions has output = ['' for _ in xrange(h - 1)] but recognize that this can be done without a loop or a list comprehension as simply output = ['']*(h-1) \$\endgroup\$
    – RobertB
    Oct 8, 2015 at 23:35
  • 2
    \$\begingroup\$ Please edit your comments inside your answer, it will be much better that way. \$\endgroup\$
    – janos
    Oct 9, 2015 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.