Entertaining the kid

I have a 15 month old daughter that simply loves to play on the laptop. Pressing random keys until I notice. Because of this I have gotten into the habit of closing my laptop while not using it. This has saved me from sending lots of random messages to random people.

Closing and opening my laptop all day is quite tedious. The best way to resolve this problem is to place a greater temptation in front of her. The only quesiton is what is that?

I found an old netbook laying around that I haven’t used in months. So I wrote a game.

I have been meaning to play with pygame for a while now and this gave me the perfect excuse to do so. The ‘requirements’ for the game were:

  • it must be simple enough to do in about an hour
  • it should do something on any input
    • the input is any keypress.
  • it should entertain a 15 month old toddler

Solution:

  • display an image
  • hide that image behind a grid of 4×4 black rectangles
  • on any key press remove a random rectangle
    • this causes a section of the hidden image to be displayed
  • if the entire image is shown, next keypress will cycle to the next image.
  • repeat

The above is extremely rudimentary. However, the audience has not acquired sophisticated tastes as of yet.

I wrote the pygame app last night and tested it out this afternoon. She loves it. She now has her very own laptop. She can bash the hell out of the keys and mommy and daddy do not stop her. Also, something happens on the screen too! Most exciting ;)

Usage is simple:

  1. mkdir -p ~/.phoebe/images
  2. put as many images as you want in the above dir
  3. python phoebe.py

Problems and Known limitiations:

  • I wrote this in an hour while learning pygame
  • I assume a lot and if you aren’t on the latest ubuntu w/ pygame it will probably throw exceptions and crash. Sorry
  • I did get it working on an old debian install with python2.5, see comment in World.__init__

If I do more with this I will throw it up on github.

Here is the python source:

#!/usr/bin/env python

import os
import sys
import glob
import itertools
import random
import datetime
import pygame
from pygame.locals import *

world = None

def init():
    pygame.init()

    global world
    world = World()

class World:
    def __init__(self):
        # this might not be available in older versions of python. If
        # that is the case, simply hard code the resolution to your
        # screen size.
        vid_info = pygame.display.Info()
        self.size = self.width,self.height = vid_info.current_w/2,vid_info.current_h/2

        self.screen = pygame.display.set_mode(self.size)

        images_files = glob.glob(os.environ["HOME"]+"/.phoebe/images/*.jpg")

        self.images = itertools.cycle(images_files)
        self.image = None

        self.grid_size = 4

        self.masks = self._get_masks()

        self._to_blit = []

        random.seed(datetime.datetime.now())

    def _get_masks(self):
        black_mask = pygame.Surface((self.width/self.grid_size, self.height/self.grid_size))
        black_mask.fill((0,0,0,0))

        rv = []
        for y in range(0,self.grid_size):
            ay = y * black_mask.get_height()
            for x in range(0,self.grid_size):
                ax = x * black_mask.get_width()
                rv.append((black_mask.copy(),(ax,ay)))
        return rv

    def _next_image(self):
        img = self.images.next()
        img_surface = pygame.image.load(img).convert()

        self.image = (pygame.transform.scale(img_surface,self.size),(0,0))

    def next(self):

        self._to_blit = []

        if not len(self.masks) or not self.image:
            #no more masks, setup the next image
            self._next_image()
            self.masks = self._get_masks()

        else:
            # we have masks remove on
            if len(self.masks) > 1:
                #if there is only one mask left, then range is 0 to 0, and randrange complains
                to_r = random.randrange(0,len(self.masks)-1)
                self.masks.pop(to_r)
            else:
                #this is the last mask,
                self.masks.pop()

        #put the image into the blit list
        self._to_blit.append(self.image)
        self._to_blit += self.masks

    def blit(self):
        screen = pygame.display.get_surface()
        for s,p in self._to_blit:
            screen.blit(s,p)

def main():
    global world

    init()

    world.next()

    clock = pygame.time.Clock()

    done = False
    while not done:

        clock.tick(30)

        world.blit()

        pygame.display.flip()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    done = True
                else:
                    world.next()

    pygame.quit()
    return 0

if __name__ == "__main__":
    sys.exit(main())

Comments (2)

  1. Cuong Le wrote::

    hey, looks this game is simple and very fast. Is that enough for your baby ? and How do you upgrade to the new version in the future ?

    Tuesday, February 22, 2011 at 12:57 am #
  2. Justin Kirby wrote::

    It is a quick hack to entertain a 15 month old :)

    I think the wireless stopped working so ‘upgrading’ is the modern version of sneaker net (sd card).

    Is it enough? well it entertains her for 5minutes here and there. So I call that a win.

    Tuesday, May 24, 2011 at 10:51 pm #