Refusing to Code Golf

Posted on February 6, 2014 by Brian Jaress
Tags: code, python

I’ve found a new way to keep programming fun: refusing to code golf.

I’ve never cared for code golfing (writing a program in as few characters as you can). It seems similar to writing a novel without the letter “E.” It can be impressive, but I’d be more impressed if you had your priorities straight.

So I get a strange kick out of taking a problem that’s intended for golfing and writing readable, organized code instead. Instead of looking for interesting ways to save bytes, I’ll look for interesting ways of making the code easier to understand.

The first golf problem I found via Google was Saving Time on codegolf.com. The program has to take in a time, such as 08:49, and print out a clock face, such as:

        o
    o       o

 o             o

m               o

 h             o

    o       o
        o

The twelve characters are always in the same positions on the clock face and represent the places that are numbered on a regular analog clock. An m indicates that’s where the minute hand is pointing, while an h indicates that’s where the hour hand is pointing. The positions are “rounded down” so that the output above is correct for all times from 8:45 through 8:49, even though on a real clock both hands would be nearly on to the next number by 8:49.

The clock face arrangement is so irregular that I did it with a tiny template system that only works with a limited number of single character replacements:

# External references. (I like the new-style print.)
from __future__ import print_function
from collections import defaultdict
CLOCK_FACE = """
        0
    b       1

 a             2

9               3

 8             4

    7       5
        6
"""[1:]


def show_clock(mark):
    for char in CLOCK_FACE:
        try:
            position = int(char, 16)
            char = mark(position)
        except ValueError:
            pass #Leave as-is.
        print(char, end='')

All that does is take a prearranged clock face with hexadecimal digits in the right places, and use the hexadecimal values to look up the correct mark with a helper function.1 Things that aren’t hexadecimal digits, like the spaces, get printed as they are.

For all that to work, we need a different helper function depending on what time we’re trying to display. For example, to display 8:49 we need a helper that returns m when given 9, h when given 8, and o when given other numbers.

The simple way to do that is a function that takes the time we want to display and creates the right helper for displaying it:

def markings(hours, minutes):
    hour_position = hours % 12
    minute_position = minutes // 5

    def table(): return defaultdict(table)
    lookup = table()
    lookup[True ][False] = 'h'
    lookup[False][True ] = 'm'
    lookup[True ][True ] = 'x'
    lookup[False][False] = 'o'

    def mark(position):
        hour_match = (hour_position == position)
        minute_match = (minute_position == position)
        return lookup[hour_match][minute_match]
    return mark

The modulo operation and floor division convert the number of hours and number of minutes into the position numbers for the hour hand and the minute hand. Then we just build a helper function named mark that will look up2 the right mark according to whether the hour hand position, minute hand position, both, or neither match the position that show_clock asks about. (The problem description says to use x when both hands point at the same position.)

The final thing we need is to extract the number of hours and the number of minutes from the input. The problem description calls for a single line of input with a time like 08:49, so we can just split on the colon and convert the two parts into integers:

def main():
    hours, minutes = [int(part) for part in raw_input().split(':')]
    show_clock(markings(hours, minutes))
# Python boilerplate
if __name__ == "__main__":
    main()

We have to support 24-hour time inputs, like 20:49, but we’ve already done that with our use of zero-based positions and the modulo operation.

Here’s a full example of input and output:

$ python saving-time.py
17:27
        o
    o       o

 o             o

o               o

 o             o

    o       x
        o

For contrast, I also wrote a golf version. It’s not short enough to get on the Python leaderboard for the problem, but it’s cramped and incomprehensible enough to remind me why I don’t care for code golfing.

t,i=raw_input(),int
for s,n,o in zip("8461c0e1c468","102020202010","0b1a29384756"):print' '*i(s,16)+"ohmx"[(i(t[:2])%12==i(o,16))+(i(t[3:])//5==i(o,16))*2]+'\n'*i(n),

  1. If you don’t know python, that [1:] is just removing the first newline in the string, so the clock face can stand on it’s own in the code without an extra newline in the output.

  2. The markings function uses a trick to create nested dictionaries quickly and clearly.