Thursday, April 18, 2013

Code Kata: text game from 1982

The goal is to implement a text-based game in which the player walks through dungeons in order to accomplish a task. The basic characteristics of an implementation are as follows:
  • the game generates random dungeons as a group of ROOMS and PATHS that join the ROOMS
  • the generation does not leave unreachable ROOMS
  • the user is presented with the information about the ROOM  they are in and the ROOMS reachable directly from the current ROOM
  • the user can move from the current ROOM to the directly reachable ROOMS by entering a command
This may seem very boring at first, but let's add some delighters:
  1. We do all of the implementation using "TDD, as if you meant it", of course
  2. Initially, the goal may be simply to get to a given ROOM
  3. Then, we may start adding attributes to ROOMS; a possible attribute is something that the user can collect, such as diamonds; N diamonds are placed in the dungeon and the game is finished when the user collects all of them; additional attributes can be used to describe the look a ROOM
  4. When that's done, we can refactor our implementation so that we do not use IF statements
  5. When that's done, we can refactor our implementation so that we do not use for/while loops
  6. We can work on the implementation a little bit more to get rid of excessive tabs (let's allow only up to two tabs in the body of a function, per Robert Martin's suggestion)
  7. We can get better by limiting the number of lines of a function (how about < 5 lines per function?)
  8. I'm guessing you did all of this with classes and objects; how would you do it with pure functions and no objects?

Tuesday, April 9, 2013

Python profiling for beginners

def bar(i):
    return i*i

def foo():
    for i in range(0,1000000):
        bar(i)


Let's try to use these two silly functions to do a profiling exercise.

First of all, we need to import cProfile. And then call: cProfile.run('foo()')

Output (from my laptop):
         1000004 function calls in 3.564 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.926    1.926    3.564    3.564 <pyshell#11>:1(foo)
  1000000    1.638    0.000    1.638    0.000 <pyshell#9>:1(bar)
        1    0.000    0.000    3.564    3.564 <string>:1(<module>)
        1    0.000    0.000    3.564    3.564 {built-in method exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


At the first glance, it may look criptic, but: the total time is the time spent in a function excluding the time spent in callees. The cummulative time is the time spent in a function including the time spent in callees. Therefore, these two times are equal for bar function. ncalls and percall are self-explanatory.

But we can go further. I highly recommend trying out a GUI application that can read the cProfile output, such as RunSnakeRun from http://www.vrplumber.com/programming/runsnakerun For this simple case it'd be useless, but for much more complex call tree, it is of great help.

In order to use it, we need to run cProfile giving it the dump file name as the second argument:


cProfile.run('foo()', 'dump.txt')

See also