October 18, 2005

Izabella, Icons, and Py2App

After I posted the first few articles in the "six-year-old Anthony learns how to program in python" series, a friend asked me, "does Anthony really understand the program he typed?" Honestly, I wasn't sure. I did have to help and teach him quite a bit to get "GuessANumber" working. Maybe I was actually doing all the programming, and Anthony was just a spectator.

It turns out we had a good chance to test this theory a couple weeks later. The original GuessANumber game was too hard for Anthony's little sister Piper. At 3, Piper could just barely count up to 20 and didn't have any way of knowing whether 75 should bigger than 57 or not. And so every time she would try to guess a number from 1 to 100 in five guesses, she would lose and be unhappy. The booming vampire audio echoing "I win!!!" did not help.

Soon Piper told us she wasn't really into vampires. She wanted a princess game.

So Anthony decided to make an "easyguessanumber.py" game. This time, as an experiment, I set up the text editor, typed in the #!/usr/bin/env python magic, and then left the room to see what Anthony would come up with.

Anthony did need answers to a couple questions, such as how to call "random.randint" - but I kept myself in the other room to see if he could get it working on his own. The results are pasted below, complete with spelling errors and a "highly secret" variable named "sx4". My conclusion: python is indeed a language a 6-year-old can learn.

 
#!/usr/bin/env python
import random
print "hello."
print"I am queen Izabella"
sx4=random.randint(1,20)
print"and I have a secret."
print"And what is it?"
print"A number 1-20"
print"Now you guess."
guesses_left = 5
while guesses_left != 0:
  guess=input()
  if guess == sx4:
    print"you got it!"
    break
  else:
    print"nope"
  if guess > sx4:
    print "smaller"
  else:
    print"biger"
  guesses_left -=1
  if guesses_left == 0:
    print"I won!"
    print sx4
 

With numbers limited to 20 and a new Princess character, Piper was far happier with this new version.

Cutting and Pasting

But Anthony was still not satisfied with his Vampire version. "I can't see the vampire!" And, he wanted to know, how come the icon that showed when we were running the app was a big black screen? Why couldn't it be a vampire?

He wanted me to draw a vampire for him. But, of course, a Daddy-drawn vampire wouldn't have been in the spirit of our project.

So we struck a deal. If he could make a vampire out of construction paper, I would figure out how to make it into the icon for his program.

A few days later, the following creation was dropped on my office desk.

Vampire, my nemisis. We finally meet.

Fit and Finish is Daddy's Job

Here is where daddy took over the last steps in the project. The last 10% of fit and finish in software requires 90% of the effort. It's fun to put together the algorithm for a program, but who wants to write the setup code? And handle all the error cases, and deal with the usability problems?

When you're six, Daddy will do it for you.

So I took out the digital camera and took a photo of Mr. Vampire; used a bit of photoshop magic to make it into a transparent png (hint: photograph your object on a piece of solid-color background paper, then use photoshop to select that color and delete it); this got converted into an OS X icon file.

To use an app icon, we needed to make our python script into an app. So I wrote the following py2app script to package the app up:

 
#!/usr/bin/env python
"""
setup.py
Usage:
    % python setup.py py2app
"""
from distutils.core import setup
import py2app
py2app_options = dict(
    iconfile='Vampire.icns',
)
setup(
    app=['GuessANumber.py'],
    data_files=[
      'big.wav',
      'guess.wav',
      'guess_again.wav',
      'i_win.wav',
      'intro.wav',
      'last_guess.wav',
      'number.wav',
      'small.wav',
      'you_win.wav',
      'Vampire.png',
    ],
    options=dict(
      py2app=py2app_options,
    )
)
 

The app also needed to be modified so that it wasn't a console program - using stdin and stdout would be no good, so input() and print had to be replaced with a pygame library that would take input and print to a graphics screen. There is a package called "pgterm" that does this job well. The details aren't that interesting, but you can take a look at the source code if you'd like (I have included it all below). There were some issues with getting fonts on the mac to work.

The results were great. Now Anthony has a little "vampire" icon in the tray on the Mac, and he can play it any time. The graphical input and output is just like the console interface, but the fonts are bigger and there is no problem with typeahead (a common source of confusion!)

We were ready to unleash the vampire on the world.

Packaging

We would release the source code, but it seems like the standard way of distributing OS X files is in a compressed .dmg file of the binary. No problem. The following command seemed to do the trick just fine.

 
hdiutil create -imagekey zlib-level=9 -srcfolder dist/GuessANumber.app dist/GuessANumber.dmg
 

So, now you can download it. We haven't actually tried running this program anywhere other than on my own computer, so your mileage may vary. We're including the source, so if you have problems you may be able to fix them yourself. Be sure to let us know :-).

Downloads (for OS X)

guessanumber-source.tar.gz source code.

GuessANumber.dmg compressed disk image of the binary.

Happy number guessing - Haaaarg!

For More

For more on this blog about learning to program:

Teaching Logo to Second-Graders

A Coding Puzzle about Kid Cryptography in Python

Learning to Program - Classroom notes for Third Grade Python Lessons

Posted by David at October 18, 2005 05:52 PM
Comments

I suppose if a 6 year old can learn Python, I could probably learn Java. It's still slow going...

Posted by: mapgirl at November 1, 2005 09:38 PM

Thanks for this post! Google led me to it when I was searching how to set an icon in a pygame app on OS X. I was delighted to find the 'hdiutil' command as well! I had been creating my .dmg with disk utility--what a pain.

I'm working on an implementation of checkers. Check it out if you want at http://stocksfam.com/checkers/

Posted by: Nathan at February 13, 2006 12:37 AM

That is a very cool game. Just downloaded it and played it. Creating games like that at only 6 imagen what he will be able to do when he grows up.

Posted by: Eddie Sowden at May 26, 2006 06:52 AM

wow. its the best game ever! fantastic sound effects :)
played for an half hour, but only once i have i guessed the number...hehe.

Posted by: ole at May 29, 2006 05:43 AM

Hi, David-

This is great! It really shows that kids can be active creators, not just spectators, on the computer.

Your son has a very good intuition about what makes a good game. The voice, the timing... all great touches. It took me many tries to finally win the game.

You might be interested in showing SVG to Anthony... it's an open Web graphics standard, it has animation, it works in the browser, you can create graphical content in text or in free apps like Inkscape, and it supports JavaScript, which would probably be easy to learn for a bright guy like him. It doesn't yet have built-in audio support, but that's coming.

Posted by: Doug Schepers at June 3, 2006 08:28 PM

My son turns 5 in another few months. He already knows more about computers than his grandparents... he regularly teaches them new things. :P

I am sure in the next year or so we will be writing some simple games. I think he will be combining them with some robots too...

Posted by: Eric D. Burdo at October 12, 2006 09:57 AM

Wow... Thank you for this wonderfull story. I'm 36 and just started learning Python 2 weeks ago. Right now I feel a little like Anthony :-)

Posted by: Robert at November 30, 2006 06:02 PM

That sounds great. I wish my son was old enough, I'm just longing to start doing stuff like this with him.

If you want to try a more ambitious game, the XNA libraries are a pretty good starting framework.

Posted by: Laurie Cheers at August 20, 2007 06:50 AM

Stumbled across this story by chance while searching for 'old games' in Google.

I found it sort of inspiring. I started learning to program games when I was 9. I'm just about 19 now, and actually haven't progressed that much in game development, unfortunately - I tend to do applications programming now instead.

I really need to get back into it!

All the best.

Posted by: Cameron at December 17, 2007 11:11 PM

Hi! I'm a 50+ grandmother who spent a few hours today showing my 8 year old grandson the "Learning Python" lessons you wrote. I wish I had done Vampire first but I have to tell you - he is getting it and he loves it! My biggest hurdle was figuring how to run the script on Windows XP - after googling I finally figured out I didn't need to do anything other than save the program to the Python folder. Thank you for some great material and for helping me to live up to the name "Grandma Zelda" that he gave me several years ago! Now ... how to convince Grandpa that Grandma needs a Mac ... hey! those are my initials!
P.S. I think Hangman will be a great reinforcement for those weekly spelling lists ... thanks again!

Posted by: Mary at April 5, 2008 12:44 AM

Great Game!!! Here the python-Script that is running on Ubuntu. You just have to install python-pygame. But unfortunately it lacks graphics.

#!/usr/bin/python
import random

# Anthony says it is important for the game to pause at the right time
# and to make vampire sounds
import time
import pygame, sys

#import macfont
pygame.init()
pygame.display.set_icon(pygame.image.load("Vampire.png"))
pygame.display.set_caption("Guess A Number")

# Anthony recorded all these sounds with Audacity
intro_sound = pygame.mixer.Sound("intro.wav")
guess_sound = pygame.mixer.Sound("guess.wav")
you_win_sound = pygame.mixer.Sound("you_win.wav")
big_sound = pygame.mixer.Sound("big.wav")
small_sound = pygame.mixer.Sound("small.wav")
i_win_sound = pygame.mixer.Sound("i_win.wav")
last_guess_sound = pygame.mixer.Sound("last_guess.wav")
guess_again_sound = pygame.mixer.Sound("guess_again.wav")
number_sound = pygame.mixer.Sound("number.wav")

# This is the game, written by Anthony.
def play():
secret_number = random.randint(1, 100)
intro_sound.play()
sys.stdout.write("HARRRG! I've got the secret")
sys.stdout.write("it's a number. 1-100")
guesses_left = 5
time.sleep(7)
guess_sound.play()
sys.stdout.write("GUESS!")

while guesses_left != 0:
letters = raw_input()
try:
guess = int(letters)

if guess == secret_number:
you_win_sound.play()
sys.stdout.write("you win!")
time.sleep(5)
break

if guess > secret_number:
big_sound.play()
sys.stdout.write("(TOO BIG!)")
else :
small_sound.play()
sys.stdout.write("(too small!)")

except:
sys.stdout.write("(it's a number!)")
number_sound.play()

time.sleep(2)

guesses_left -= 1

if guesses_left == 0:
i_win_sound.play()
sys.stdout.write("I WIN!")
sys.stdout.write(secret_number)
time.sleep(5)
elif guesses_left == 1:
last_guess_sound.play()
sys.stdout.write("LAST GUESS!")
else:
guess_again_sound.play()
sys.stdout.write("GUESS AGAIN!")

# Daddy added this part to catch errors
try:
play()
except:
pass

Posted by: Marekuana at October 24, 2008 05:13 PM

Hi there,

I found this series when I searched information to start my son to do programming. Thanks for sharing!

Posted by: Alice at July 11, 2009 03:37 PM

I did a google search for how to add an icon using py2app, and sure enough, this was one of the top hits. I loved the game. Could you add a while True: loop around play() so I can play over and over, lol! I still can't guess that pesky number.

Anyway, here's a little math game I was working on, hoping the next version will have an icon!

http://bitbucket.org/jgrigonis/mathfacts

Posted by: Joshua Grigonis at December 18, 2010 03:38 AM
Post a comment









Remember personal info?