Why learn to make games?

This year, at PyCon, I'll be joining Richard Jones to teach a tutorial on PyGame. I'm no expert at PyGame (he is), so I'll be covering game design and theory, since it's something I wish people put more thought into, rather than impulsively shoving bits of sucessful franchises into the empty husk of a big title, like Dr. Frankenstein trying to sort out what he's going to do with all these leftovers.

Ahem.

Why would someone want to learn to write a game? We've all heard about the hell that is working in the gaming industry, and I've met very few veterens of Python that have any interest in joining it. We all know that being the next Minecraft is a one-in-a-million shot, and that even Minecraft came to the table with decks stacked (Notch is a pretty smart dude, and had been doing this for a while). If you want a game, why shouldn't you just buy one?

It makes you a better coder

Games are finicky things. Unlike systems where all we're doing is moving around bits of text and the occassional image, games are practically nothing but images. Even the text is treated as an image. You have to be more conservative with your resources, watching how every function affects the load on your system. Sure, that ponderous animation you wrote up works fine when you just have one little dude on the screen, but what about when you have fifty? Or add particle effects, or explosions? You know you want explosions, so maybe you should tidy that up now.

It also forces you to organize earlier. You can't just dive in with a game. You need to have a plan, because at the very least, you need some graphics. If you need graphics, chances are you're going to mock something up, which will at least give you a vision of where you're going. I know many developers who never bother to do a mock-up when doing a web app, figuring they'll work that out 'later.' Writing for a game opens your eyes to how much easier having a vision makes development.

You will finally learn to make graphics

Every developer should know how to make their own graphics. Yes, there are designers in the world, but few of them will willingly give up their Saturday to make you an orc, gratis. Now is the time that you'll learn that making okay game art is not as hard as you've been lead to believe. It just takes time. 

It won't be fabuluous, but there was a time when everyone was taught how to draw. It wasn't seen as teaching art: it was a life skill. Okay, a life skill for the monied, but still. Making graphics should be one of your life skills. If your game does turn into the next Minecraft, you can have a visual artist overhaul the thing for you once your funding rolls in.

It's crazy awesome fun

Oh my goodness. It is such crazy awesome fun.

I didn't think it would be this much fun, honestly. I thought, meh, making websites is easier, I can do most of the prettifying with CSS and webfonts. Games are work.

Games do take a lot of work. I spent a lot more effort getting my little guy to move around than I did creating fake bank for my kids. It was addicting, though. Once I got him to move, well, I had to make some monsters, and move them around, too! And chests, oh, I needed chests! And the chests need things in them, what will I put in them...? It's layers of fun, because everything you make could have something even awesomer inside it!

I could have spent my mornings playing a game that was already done, but I found myself having more fun hacking at one that will never sell a million units, and that,hell, if I get 100 downloads, I'd be dancing. It made my kids laugh when I replaced the walls with silly tornados, or let them make me a new tileset. My daugher now understands that when I change the code, I change how my little man acts, or how many monsters there are, or what is in the chests. She thinks that's awesome.

And you know what? It totally is.

Pygame: Getting started

As I've mentioned before, I'll be helping Richard Jones with a tutorial on PyGame at PyCon this year. When I agreed to help him, I admit, I knew nothing about PyGame. I was coming on as someone who would talk about tropes and designs and pitfalls (and the crusher of a few dreams). I thought it would be wise to pick it up, though, so last month, I started playing around with it.

Getting started

System: Linux Mint 11

Katie, giving her laptop a dark look. Caption: Please make this code sentient, so when I kill it, it will feel pain.

The first thing I tried to do was to get PyGame installed into a virtualenv. This went... poorly. gcc kept throwing the dreaded "Status 1" error that makes me want to put my fist through something. I spent the better part of an hour Googling and trying to tweak the env, until I finally said Screw it, and tried a system-wide install.

Worked first time.

Fine. Be that way. Whatever.

System: Lion

Curious, I tried the virtualenv trick on Lion, and came up with errors there, as well. This time, though, doing a global install from source failed just as badly. I ended up having to use one of their pre-packaged distros. I hate doing that. I always come away feeling dirty.

Round zero - Decisions, decisions

I'm not starting with zero to be cute. I'm starting with zero because there's something very important that needs to be done, once you've established that your machine can, in fact, work with PyGame.

You have to figure out what your first project is.

Now, I have some dream games that I'd love to make. I'd love to make a sim, or a quirky RPG, or a sandbox game, but I don't even know if I'm up for making Pacman at this point.

Rather than spending weeks writing out requirements for a game before I know how to make one, I decided to just copy something that's already been documented to hell and back. I decided to make a roguelike.

A rogue character standing with a large red D behind him.

Roguelikes, one of the worst named genres ever, are nearly as old as the personal computer (if you discount the ones that weren't mass-produced). The basic premis is that dungeons are automatically generated and populated, and you descend levels until you get to some big baddie a the bottom. I wouldn't have to think about requirements when you start coding, as they're already written out for me.

Why not just make my dream game? Because there are subtle issues that crop up, and how you decide to solve these issues can impact design decisions down the road. You're already going to screw things up. Why screw the whole pooch?

Now that I knew what I was doing, I knew I would need some graphics. Sure, I could just make circles and squares and make do, but if I knew I was going to move to a graphic game, why not start out right? After struggling with Gimp, I was able to produce three things I knew I would need: a player, and a board.

I also had a list of things that I knew I wanted working for my first attempt: 

The list looks skimpy, but trust me, while learning you want a short, easy list. Impementation can be more subtle than expected.

Round one - Diving in

And here is where I made my first bad decision: I didn't start out with an object oriented design. My only excuse was that I was excited, and I was just trying to get the screen to pop up and look pretty.

# INTIALISATION
import pygame, math, sys
from pygame.locals import *

screen = pygame.display.set_mode((1024, 768))
player = pygame.image.load('dude.png')
clock = pygame.time.Clock()
k_up = k_down = k_left = k_right = 0
direction = 0
position = (300, 300)
BLACK = (0,0,0)
WHITE = (255, 255, 255)

def main():
	while 1:
		clock.tick(15)
		hor = 0
		vert = 0
		for event in pygame.event.get():
			if not hasattr(event, 'key'): continue
			if event.key == K_ESCAPE: sys.exit(0)
			if event.key == K_LEFT: hor = -25
			if event.key == K_RIGHT: hor = 25
			if event.key == K_UP: vert = -25
			if event.key == K_DOWN: vert = 25
		x, y = position
		x = x + hor
		y = y + vert
		global position
		position = (x, y)
		screen.fill(BLACK)
		screen.blit(player, position)
		pygame.display.flip()



if __name__ == "__main__":
	main()

The above certainly does that, as well as lets my little guy move around. There's a little bug in the above code that followed me around for a while, until I fixed it much, much later. No, I won't be telling you what it is... yet.

Round two - Doing it right

Happily, I remembered how to actually code properly, and tossed the above screwing-around code for some proper classes. Had I been thinking farther ahead I would have split things even more. As I've said before, making games is crazy fun, so it can be easy to get carried away.

# INTIALISATION
import pygame, math, sys
from pygame.locals import *


BLACK = (0,0,0)
WHITE = (255, 255, 255)
TILES_ACROSS = 20 - 1
TILES_DOWN = 15 - 1

class Map(object):
	def __init__(self):
		self.map = []
		for i in range(TILES_ACROSS+1):
			row = []
			for j in range(TILES_DOWN+1):
				row.append(0)
			self.map.append(row)

	def clear_block(self, position):
		x, y = position
		column = x/50
		row = y/50
		print "Column %s, Row %s" % (str(column), str(row))
		self.map[column][row] = 1

	def print_ascii_map(self):
		for row in self.map:
			print row

class Game(object):
	def __init__(self):
		self.screen = pygame.display.set_mode((1024, 768))
		self.player = pygame.image.load('dude.png')
		self.bg = pygame.image.load('rainbowbg.png')
		self.clock = pygame.time.Clock()
		self.direction = 0
		self.position = (300, 300)
		self.map = Map()
		self.map.clear_block(self.position)
		self.run()

	def draw_darkness(self):
		print self.map.map.__len__()
		for row in range(TILES_ACROSS+1):
			for col in range(TILES_DOWN+1):
				if self.map.map[row][col] == 0:
					pygame.draw.rect(self.screen, BLACK, (row*50, col*50, 50, 50)) 	

	def move(self, hor, vert):
		x, y = self.position
		x = x + hor
		y = y + vert
		if x > TILES_ACROSS * 50 or x < 0 or y > TILES_DOWN * 50 or y < 0:
			return
		self.position = (x, y)
		self.map.clear_block(self.position)
		self.screen.blit(self.bg, (0, 0))
		self.draw_darkness()
		self.screen.blit(self.player, self.position)
		pygame.display.flip()

	def run(self):
		while 1:
			self.clock.tick(30)
			hor = 0
			vert = 0
			for event in pygame.event.get():
                        	if not hasattr(event, 'key'): continue
                        	if event.key == K_ESCAPE: sys.exit(0)
                        	if event.key == K_LEFT: hor = -25
                        	if event.key == K_RIGHT: hor = 25
                        	if event.key == K_UP: vert = -25
                        	if event.key == K_DOWN: vert = 25
				self.move(hor, vert)
				self.map.print_ascii_map()

def main():
        while 1:
		game = Game()

if __name__ == "__main__":
        main()

Now, everything is snug in its own class. By accident, I made a good design decision above, making a master class called Game that would keep track of all of the pesky details within the game itself.

Explanations

At this iteration, I had a map, a player, a layer of darkness to obscure where the player hadn't been yet, and absolutely no documentation. Bad Katie. As I said, the fun can get in the way of being a good person, sometimes. Here are some explainations of what I have above:

Map

I needed something that would keep track of what blocks had been cleared. Though there was nothing to hide yet, I knew that I would eventually want to implement fog of war. I also tossed in a sanity checking function that prints out the ascii map on request. It's easy to screw up movement, even when it's just up, down, left, and right.

Game

The game itself keeps track of all the parts of the game with an init that's only going to grow. I also put it in charge of drawing everything, a move that I may regret later. For now, the player is a nebulous quality within the game itself. This is something that would have to change later.

Blits and flips

I admit, these confused me at first. A blit is just an image that is being drawn onto the screen on top of whatever else is already there. So, I draw the floor, then I draw the player. If I reversed this, the player would be under the floor, and the game would be a bit confusing.

Once an image is drawn, however, it isn't shown. PyGame draws everything, then reveals what it's drawn by 'flipping' the screen. It makes the graphics much more smooth, apparently. There's also an option to reveal the changes to just a part of the screen, but I wasn't rad enough at this point to play with that.

Event keys

Once the game is started, it starts listening for events. Any event that happens, such as a mouse click or a key stroke, goes into pygame.event. It's up to us to determine which of these events we want to actually use. I only care about movement and quitting, so I just watch for left, right, up, down, and escape. That's right. No WASD. I had enough to debug at this point. I didn't need to add optional movement choices.

Want to play with what I did?

Here's the commit!

Next

I get serious about splitting things up, and we find Treasure!

PyGame: Treasure!

Want to see where I am now? Here's the code! 

Setting some defaults

Now that I was hip deep in moving my little dude around, I realized that I was going to need some defaults. In games, nothing is a given. You might need to make the board bigger, smaller, change the size of graphics, or even swap out colors.

BLACK = (0,0,0)
WHITE = (255, 255, 255)
COLUMNS = 16
ROWS = 21
TREASURES = 10
TILE_SIZE = 48

Had I been less eager, I would have started off with defaults written out. Seeing as how this was only the second session, however, I think I can be forgiven.

Since a rogue-like is a fairly basic game, I didn't need too many defaults: a few colors, columns and rows, and the size of the tiles for movement.

Colors in PyGame

Colors in PyGame are set through triads of red, blue, and green. Colors are the first thing I recommend you go ahead and add to defaults, as they can be annoying to puzzle out later.

I don't have any specific tool that I use to calculate RGB. I tend to use what's at hand. When I'm using my Mint box, I use Gimp. When I'm on my Mac, I pull up any one of a million, billion websites that do color conversions. What I'm looking for is RGB values:

A screen shot of a Gimp color picker, with the RBG values highlighted

I can take those, and make some default colors that will make reading the rest of my code so much easier.

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

This way, if I decide later on that I want my whites to be off-white for usability reasons, I just have to change one value.

Why go binary?

I made a major change at this point, and it wasn't adding treasure: I moved to base 2.

Why do this? I'd never really thought about base 2 numbers, aside from the fact that they would crop up in computing. It was just one of those things that you learn to expect. I can't recall ever having it explained to me, and it wasn't quite interesting enough for me to look into.

I stumbled into the reasoning by accident, while reading, of all things, a crazy novel about religious extremists and MMO gold farmers.

Having everything in base two makes everything halve more easily, which is something you do all the time when making a game.

Let's say I have a tile width of 50px (which is what I started with). If I want something to be half on that tile, I can move it 25px. But what if I want it to be on a quarter of that tile? Now we're at 12.5... and there is no such thing as half a pixel.

As soon as I was out of the bath (yes, I read in the bath), I switched my tiles to be 48x48. Now, we were divisible all the way down, something that would become very important later.

Treasure!

Now that I had a little guy moving around a dungeon, I decided he needed something to find in the dungeon. Treasure! Finally, I had my Treasure class do something besides sit there and look sad.

ALL_TREASURES = {
     "hat": "Quite cunning",
     "sqord": "Knock-off sword. Probably from Ikea.",
     "book": "What the hell are you going to do with this?"
   }
LONG_STRING = "X" * 50

class Treasure(object):
	''' Not implemented yet. 
	'''
	def __init__(self):
		k = ALL_TREASURES.keys()
		r = random.randint(0, ALL_TREASURES.keys().__len__()-1)
		self.title = ALL_TREASURES.keys()[r]
		self.description = ALL_TREASURES[self.title]
		

The Treasure class, in its pupa stage, only has two things: a title, and a description. Why not go hog wild? Why not give it attributes and effects and all the awesomesauce I can?

Rogue discovering a chest. This delights him. Caption: Finally! Purpose!

Why? Because there's more things to consider before I go crazy. Being a little bit clever with the titles and descriptions was really all the complications I want at the moment. After all I have to get them on the board.

I decided to go with adding another layer to the Map. It worked for making the darkness, so it should work for treasures. Adding random treasures turned out to be quite easy, as was clearning them. Huzzah! This is going to be super easy to write!

Viewing area and fog

I realized, after placing treasures, that my rogue needed to be able to see a bit better. I paused on the treasure feature to give my guy a field of view.

I modified my move function to clear the blocks around the rogue, but only slightly. I still wanted some shadows, so my cleared layer got some subtle tweaks. Now, if the rogue hadn't been in a block, or by it twice, that block would be shadowy.

Screenshot, showing rogue's path through the darkness. There are shadows where he can see, but it's still kind of dark. Caption, with arrow pointing to shadows: Sort of dark here!

The 'walked by it twice' bit was an accident, but one that I ended up keeping. I imagined my little guy squinting into the darkness, looking really, really hard for treasures, worrying about the day I decide to add monsters.

One thing that bit me here: if I moved my rogue to the bottom of the screen, the top would light up. I was completely confused until I read up on some subtlties with python's arrays. They accept negative numbers as the index.

>>> a = [1,2,3,4,5]
>>> a[-1] = 0
>>> a
[1, 2, 3, 4, 0]
Lesson learned: test for your bounds.

Back to treasures!

Now that my guy could actually see treasures, I could get back to making them awesome! Placing and picking up treasure was a cinch, but I was pretty sure that the player would want to know what they had picked up. Time to add alerts and an inventory.

I already had some dead area, due to the size I'd made my tiles and board. Whenever my guy ran across a treasure, it would be added to an inventory, and its title and description would be displayed in an alert area at the bottom of the screen.

I discovered very quickly that text likes to persist in PyGame. My hacky solution was to write over the existing text with a bunch of dark letters before writing something new.

# In the Game class...
def draw_alert(self, alert, color=WHITE):
		''' Draws the alert box at the bottom 
		'''
		self.alert = self.font.render(LONG_STRING, True, BLACK, BLACK)
		self.screen.blit(self.alert, (0, 790))
		try:
			pygame.display.flip()
		except:
			pass
		self.alert = self.font.render(alert, True, color, BLACK)

This is a horrible, horrible solution, and it fills me with shame. Works, though.

Is that bug still there?

Oh yeah. Found it yet?

Next time

We add monsters! 

Pygame: Monsters and Walls!

Where am I in my code base? Here!

Walls

One feature I know I'm going to need in the future is rooms. A dungeon isn't very interesting if it's just one big featureless room. No, no, you need a lot of little featureless rooms to make your game sing!

I considered briefly coding for rooms, but decided to start with something even more granular: a wall. It ended up being the right decision. Several design decisions popped up almost immediately.

That last one seems like a no brainer, right? Of course it shouldn't! You'd be surprised how many modern games allow for the user to sense what's beyond a wall. Next time your game has a minimap, check out what happens when you belly up to a wall. Many will show a ghostly outline of what's just beyond your field of view.

A Diablo 2 screenshot of the mini map. The user has never been to a certain area, but they can see the outline of the room. Caption: How do I know about this?

Either all of these developers are lazy, or there's some advantage to making players mildly psychic.

I decided I would solve this in the time honored way that many developers have solved such conundrums before me: ignore it for now. Chances are, if I code the solution for this now, I'll just have to redo it when I add in rooms. Might as well save the effort.

As for the other issues, I decided that running into walls is a perfectly valid behavior for people and monsters to have. I do it all the time. Why should they be any different? When I make smarter monsters, I may have them see walls and not run into them. Maybe.

I'm not sure I want monsters that are more perceptive than me.

Monsters!

The rogue has encountered an ooze, and is terrified.

Now that we have something for my little guy to pick up, we now need something to smack him back down.

My first monster isn't really all that terrifying. He's not that bright. He can't do any damage. He can't chase after the hero. He's really quite dull.

Dull is good, though. Dull is a good place to start coding. I decided the only thing I wanted my monster to do was move. That should be easy enough, right? I made another layer in my Map class, and expended its init for adding monsters to random tiles.

Once they were technically on the board, all I had to do was render them. This, I added to the Game class, since that was doing all the heavy work of rendering. For once, things went as I wanted. The monsters were added, each its own distinct object, and if I took darkness away, I could see that they populated the full bounds of the board. Huzzah!

Then I decided to move them.

I had the monsters move every time the hero moved in a random direction. I'd already learned about testing the boundaries of the board, so they didn't try to move outside of the board. If they tried to move onto the same tile, however... they overwrote each other. Only one monster would emerge from the tile, the other doomed to the garbage collector. They also had the annoying habit of jumping over my walls, which is not something derpy monsters should be doing.

Dammit.

Checking tiles

One incredibly important function I hadn't realized I needed yet was something that would check to see if a tile was open. That got written in a hurry and added to the Map class.

def is_block_empty(self, row, col):
    if not self.treasure[row][col] \
    and not self.monsters[row][col] \
    and not self.walls[row][col]:
        return True
    else:
        return False        

It's not the most elegant bit of code, and it's highlighting where my method of 'just add another layer' may have issues down the road. For now, though, it works. I'm now starting to think about version 2.0, and how I might better deal with layers.

Why not just stop and fix it now? Because I'm not sure I know how I want to fix it. That's the beauty of prototypes. At the end of the day, you trash the project. The product of the prototype isn't something you use: it's all the lessons you learn.

Inventory

My guy has treasure, so now we need a way for the player to see what they've collected. After all, monsters imply fighting at some point, so the sqord he's been toting around will be for more than show.

Three kinds of inventories: slot based, volume based, and weight based

Rather than just have a list of items stored somewhere, I decide to make the Inventory a class of its own. Sure, it's just a list of things right now, but inventory management is one of those things that can get stunningly complex. Do we have a weight limit to the bag? A volume limit? Do items take up varying amounts of volume, like in Diablo, or is it a thing-per-slot system, like Torchlight? Are we going to completely screw with the player by making it a weight AND volume system? Or is this bag simply a Bag of Holding, that can hold an infinity of objects?

With all these unanswered design decisions (I don't know how much I dislike the player, yet), it's better just to create a simple class that I can build off of, right now.

class Inventory(object):
    ''' The inventory for the player.
    '''

    def __init__(self):
        ''' Sets up the initial blank inventory.
        '''
        self.inventory = {}

    def get_items(self):
        return self.inventory.keys()

    def add_to_inventory(self, item):
        ''' Adds an item to the inventory
        '''
        try:
            self.inventory[item] += 1
        except:
            self.inventory[item] = 1

Rogue guy, with a cunning hat, a rainbow, a sword, and a book. He is proud of his epic loots.

This set-up may not be perfect, but it will allow my little guy to carry more that one type of an item. I know I'll have to print out my inventory, so I might as well have a function that will return all the unique items in the bag.

Now that I have this nifty function for giving me all the items in the inventory, I suppose I should display them, huh? Displaying the inventory can be done in any number of ways: key stroke, icon, constant HUD, text listing, etc. I would like to say that I thought long and hard about how I would display my inventory, but that would be a dirty, filthy lie.

I had a big black space to the right side of my screen, and I decided to stick it there.

To the Game class, I added the following function:

def draw_inventory(self):
    self.screen.blit(self.inventory_screen, (1008, 0))
    items = self.inventory.get_items()
    for i in range(items.__len__()):
        line = self.small_font.render(LONG_STRING, \
            True, BLACK, BLACK)
        self.screen.blit(line, (1008, ((i+1)*15)))
        pygame.display.flip()
        for item in items:
            line = self.small_font.render(item, \
                True, WHITE, BLACK)
            self.screen.blit(line, (1008, \
                (items.index(item)+1)*15))
        pygame.display.flip()

It's simple, and likely going to change as my UI gets more complex. For now, though, having it on the side means I don't have to hit any keys or icons while I'm trying to debug treasure functionality.

The bug!

By this commit, I had found the movement bug! Basically, every keystroke is, in fact, two events: KEY_DOWN and KEY_UP. Adding a check for KEY_DOWN made it so that I wasn't moving twice for every keystroke:

for event in pygame.event.get():
    if not hasattr(event, 'key'): continue
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE: sys.exit(0)
            if event.key == K_LEFT: hor = -TILE_SIZE
            if event.key == K_RIGHT: hor = TILE_SIZE
            if event.key == K_UP: vert = -TILE_SIZE
            if event.key == K_DOWN: vert = TILE_SIZE
            self.map.move_monsters()
        self.move(hor, vert)

Feedback!

I've been getting super feedback in posts about making the game work better, or about some bugs that people are spotting. Awesome!

Since I code a bunch, then write the blog posts, some of these fixes aren't going to show up for a while. I'm not ignoring you! For instance, Joshua found the another issue I had been having with movement. Marius contributed a better way to add choices to the game.

I was also alerted to an upcoming seven day roguelike challenge by purplearcanist. Though I don't have time to get involved, contests like this can be fun to jump into, or at least watch.

Getting kind of long, isn't it?

Next time, we start actually organizing the code and media, so it's easier to manage and grow.

PyGame: Fog of War and Organization

Where am I? Here!

Organization

Up to this point, I had been diving into the code, head-first, without any consideration as to organizing it. Bad Katie. I took some time to break out my code into more manageable chunks, and finally put all of my images in their own directories. My file structure ended up looking like this:

- roguey
    __init__.py
    - classes
        constants.py
        game.py
        gamemap.py
        items.py
        monsters.py
        player.py
    + images
+ comps
main.py
README.txt

Looking into the contents of the files, it's not quite the Java-esque style of one class per file, but it's close. While I abhor Java, and only tolerate it until I can use Python and C to make Android applications, this is one pattern that can be okay to copy. I don't plan on keeping it to one class per file, though. The player is one thing that I expect to grow to include all sorts of classes, from Inventory to SpellList to CharacterStats.

Paths

One thing tripped me up briefly, so I'll go ahead and lay out it out here, even though it fills me with shame.

I have an image directory. I'd like to point to it. In fact, I have to point to it throughout the game, since I'm not doing an ASCII version of Rogue.

Python's os library has a great function that returns the current working directory. At first, I thought that it would return the current working directory of the file. It doesn't, because that would be stupid. It returns the cwd of the user. So, in order to make the full path of my images a constant, I had to add this code to constants.py:

import os
...
IMG_DIR = os.getcwd() + "/roguey/images/"

Since the game has to be run from the the same directory as main.py, this works. For now.

Also, now that I had all my class files in another directory, it became important to import them where needed. In the end, my imports ended up looking a bit like this:

A diagram showing imports in PyGame.

Darn you, Python (Maps)

If you plan on having a map class in your game, I highly recommend training yourself from the start to call it anything but 'Map'. Initially, I called my map 'Map', and this caused all sorts of strangeness when I separated things out. Python already has a Map class, and was a bit uppity that I had my own.

I renamed the file to 'gamemap.py', so Python and I are friends again.

Fog of War

Rogue guy, looking at a dying torch, surrounded by darkness. He looks very worried.

Fog of war is a nice way of making the player have memory, but be a bit less psychic. Essentially, the player can remember where they've been, but that doesn't give them the magical ability to know what's going on in those places. The simplest version of this is to not show monsters until they're in the player's field of vision.

There are some cool variations of this that I may play with in the future. For example, a listen check might reveal that a certain type of monster is in the next room. A spell might reveal creatures that are far away. Or, if I want to be really dastardly, the type of monster might not be revealed until they're in direct combat. Perhaps torches in the distance might show the player what's in its immediate radius, with blobby outlines further out.

Fog of war examples: Hard shadows, field of war with memory, and field of war with light sources.

It's a cool mechanic to play with.

For now, I'm keeping it simple. I added another layer, this time containing the view radius of the player. If a mob isn't in that radius, it isn't rendered. Everywhere else he's been on the board is now covered in a grey overlay.

# In gamemap.Map

def set_current_position(self, position):
    row = row/TILE_SIZE
    col = col/TILE_SIZE
    self.current[row][col] = 1
    for i in range(RADIUS):
        if row-i > 0:
            self.current[row-i-1][col] = 1
        if row+i < ROWS-1:
            self.current[row+i+1][col] = 1
        if col-i > 0:
            self.current[row][col-i-1] = 1
        if col+i < COLUMNS-1:
            self.current[row][col+i+1] = 1
    for i in range(RADIUS-1):
        if row-i > 0 and col-i > 0: self.current[row-i-1][col-i-1] = 1
        if row-i > 0 and col-i < COLUMNS-1: self.current[row-i-1][col+i+1] = 1
        if row+1 < ROWS-1 and col-i > 0: self.current[row+i+1][col-i-1] = 1
        if row+1 < ROWS-1 and col+i < COLUMNS-1: self.current[row+i+1][col+i+1] = 1

# In game.Game

def draw_darkness(self):
    ''' Draws the darkness and shadows on the board. 0 is dark, 1 is in shadows,
    '''
    for row in range(ROWS):
        for col in range(COLUMNS):
            if self.map.cleared[row][col] == 0:
                if not self.map.current[row][col]:
                    pygame.draw.rect(self.screen, BLACK, (row*TILE_SIZE, col*TILE_SIZE, TILE_SIZE, TILE_SIZE))  
            if self.map.cleared[row][col] == 1:
                if not self.map.current[row][col]:
                    shadow = pygame.Surface((TILE_SIZE, TILE_SIZE))
                    shadow.set_alpha(200)
                    shadow.fill(BLACK)
                    self.screen.blit(shadow, (row*TILE_SIZE, col*TILE_SIZE))

Next!

I explore "Active Gaming," and we talk animation!

PyCon 2012 - Thoughts on the Poster Session

Everyone is posting quick round-ups about PyCon today, but I thought I'd focus on one thing that really got nailed this year: the poster session.

At other conferences, the poster session was often the home to those whose proposals were not quite cool enough to get a talk, but not so awful that they should get outright rejected. I always visited them, but they seemed... sad. There were some great things there, but so few people bothered to go check it out that it looked like a ghost town. Even serving snacks there only ensured people would dash in and out to get cookies and coffee.

This year, we were able to submit something as a poster. This was awesome. The people who were in there with posters? They wanted to be there. They had a message that was uniquely suited to the poster session. I know that for my poster, I really wanted to be able to engage with people more. It was like a two hour questions session.

Also, there were no talks during this time. Everything was shut down, making posters the only game in town. I didn't take pictures (I was constantly talking to people), but the room was filled. Not only was it filled, but it was filled with people who were actively engaging the presenters. 

I was delighted to find out that we had supplies for our boards. I had brought thumb tacks and tape, but I'm a paranoid freak about things like this. Even for my crazy number of items, I had more than enough to pin my presentation up.

Recording the posters was genius. I now have another video I can show to people, when I get asked about my talks. 

I do wish I could have set up the night before, but I understand that people needed time to break down and set up. I was slightly pouty about missing Guido, but then again, I got to see the awesome robot dance without fighting fifty other people. 

Some thoughts I'd like to impart for future poster presenters:

I hope that future conferences look to PyCon and their treatment of poster sessions, and copies them.

PyGame: Animation and Active Gaming

Where am I? Here!

Animation

Now that I had my movement working correctly, I had another issue. The bug had the 'feature' of giving the rogue the illusion of movement through a little pause halfway through the movement. Now that the movement was gone, I found myself annoyed by his jumping from square to square.

Rogue dude sliding across the floor.

The first demo I ever used for PyGame involved animating a car and driving it around a box. Animation should be child's play, right? I decided to take my rogue, and have him move smoothly from square to square.

To move a something in PyGame, you have to step the blit through being drawn, flipping the screen, moving the bit a tiny bit, flipping again, and doing that until you get to the end point, if there even is an end point. If you've decided your blit is living in a frictionless world, then it could, technically, keep going forever.

I just want my guy to move to the next block, so I added this bit of code to game.py:

# In Game:
def animate_move(self, hor, vert, blit):
    if vert:
        if vert > 0:
            for i in range(TILE_SIZE/MOVEMENT_SIZE):
                self.draw_screen_layers()
                self.screen.blit(self.__getattribute__(blit),\
                    [self.old_row, self.old_col+i*MOVEMENT_SIZE])
                pygame.display.update()
        else:
            for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row, self.old_col-i*MOVEMENT_SIZE])
                    pygame.display.update()
        if hor:
            if hor > 0:
                for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row+i*MOVEMENT_SIZE, self.old_col])
                    pygame.display.update()
            else:
                for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row-i*MOVEMENT_SIZE, self.old_col])
                    pygame.display.update()

What is MOVEMENT_SIZE? It's the number of pixels I want my rogue to move for every frame. I found out quickly that moving him just one or two pixels caused the animation to be extremely slow, but not any more smooth. I ended up with a movement size of sixteen (set in constants.py) before I was happy.

Well, kind of happy.

Complications from Animation

While I was delighted that my guy was animated now, I noticed that the interface now seemed... off. My monsters were still leaping to and fro, so that was jarring. The shadows were leaping, too, with only the area where the rogue was going to be getting highlighted. Movement still seemed very slow, even with reducing it to only a few frames. One small decision now made my interface feel very, very strange.

Katie looks disheartened. Caption: Well... hell.

I thought about making the monsters animated, but realized that still left the shadows leaping about. I could rewrite the shadow logic, but I knew that I wanted to rewrite it when I introduced rooms. Did I want to put up with jarring animations that slowed down my testing?

I decided that animation had become a time sink at this point. Combat still didn't exist, my player was nothing more than a bag of rainbows and sqords, I had no rooms, and I hadn't even gotten to adding multiple levels to my dungeon. I didn't even have a McGuffin yet.

I took it out, but left the functionality in there for another day.

Active Gaming

What the heck is active gaming? Am I making a Kinect version of my roguelike? Will this be on the Wii? Will the player have to sweat?

No. Active gaming allows a developer to think more deeply about gaming mechanics.

In high school, most people are taught 'active reading.' The name might differ across systems, but it comes down thinking about what you're reading while you're still reading it. You might underline text, or take notes, or reread every section before moving on. Whatever trick is used, it's meant to force the reader to engage with the material, rather than sitting back and letting it wash over him/her.

Active gaming is similar. Rather than just playing mindlessly, you take note of mechanics or plot devices or timing. You force yourself to become aware of how the game is manipulating you into playing the game a certain way. How are you brought to the next plot point? Did the developer motivate you with quest objectives, or did a big monster chase you there? How does combat work? If it's turn based, what starts a combat round? How does combat carry from round to round? Do the creatures stick it out the bitter end, or do they flee?

Let's Plays

One of the cool things about active gaming when compared to active reading is that there's more than one way to get input. This is great news for people like me who have hand issues, and can't do long gaming sessions. This is also huge for people who want to look at a game that's long out of print, or on a system they don't own.

One of my favorite ways to do an active gaming session is to wander over to YouTube and look for a 'Let's Play' for that game. Oh, you think there won't be one? You think your game is too rare for there to possibly be an LP? I found an LP of some Simpson's Driving game I'd never heard of. There are LP's of Flash games. You can find LP's for mods that add My Little Ponies to Skyrim, or for specific maps in Minecraft. People who make LP's are crazily dedicated.

Watching a Let's Play allows you to sit back and just take notes without getting obsessed about learning the keyboard commands, or what the left shoulder button does. You can pause and go back when you want to note how a certain mechanic works.

These people are also usually pretty good gamers, meaning they're not going to spend an hour locked in the bathroom because the texture of the handle is the same as the door and you just didn't notice it. Yes, I have done that. I'm not proud. Even if they're not great gamers, their death only sets you back seconds, since they usually cut out geting back to where they were before.

So, how do you do active gaming?

First, decide what you're going to be investigating. I like to go after game elements one at a time, so I can really focus on just those things. You might just focus on how movement is handled, or combat. You might spend a session tracing quests, and how you're being lead from objective to objective. I recently started working on combat in my roguelike, so I did a session just focusing on how you kill things in classic Rogue.

Note taking can be oenerous when you're trying to game, so I opt for video and audio capture. FRAPS does a great job at this, but Jing is quite good as well. I still haven't fully investigated any *nix options (sorry!). 

The idea is to talk while playing, so you can later review your findings with pen in hand. I loaded up a Flash roguelike (don't judge), started Jing, and started playing.

After I was done, I had a rogue outline for what combat was like for a basic roguelike:

Just those simple things made me sit back and look at how I was doing rounds of combat, and ended up working as a list of starter requirements for the next round of coding.

Next week!

We get down to brass tacks and start fighting!

The Littlest Intern

This summer marks the end of my son's elementary school career. In this neck of the woods, sixth grade is the beginning of middle school, and the start of his world getting a little bigger.

Hannah, with red eyes and sharp, angry teeth, sneaks up on her brother.

Normally, his summers are a series of days in which he wrangles as much video game time as he can out of his grandmother, fights with his sister, and avoids going outside into the sweaty bumhole that is DC from June to September. We've done camps, with mixed results, and one bout of summer school, with tragic results.

What can I do with him for three months that keeps his brain from turning to mush?

The idea hit me when I was playing with Code Year. How cool would this be for him to dive into? There's no set-up, and the examples are incredibly clear. Unfortunately, school and homework leave him pretty drained. A tired kid is not a kid that's going to learn much.

So what about the summer? He has a laptop. I could send him to grandma's with it on his back and he could do it there. No, I realized, that wasn't likely to happen. He'd just turn on YouTube and watch NHL recaps and Let's Plays. It's not his grandmother's job to pester him to do a lesson, and she might not be able to help him if he got stuck. I'm sure he'd go with the intention of doing a lesson, but summer days have a way of getting away from you.

Then it hit me. He could be my intern.

I work from home, so I wouldn't have to annoy my office mates. And there are some little tasks around my home office that he could help with. He's shown interest in my work, but we really haven't had the time to sit down and go over what I do besides "Make computers go." Joining me in the office, one day a week, should give him the chance to learn what I do, help me with some of the more tedious things that must be done, and gain a skill or two.

And you know what? I'll even pay him and cover his lunch. I'm now 9000% better than most internships out there.

TaskSkill
Scan in notes and sketches that have piled up. Everyone should know how to use office equipment. There is nothing sadder than five people with advanced degrees panicking around a printer.
Perform office maintenence. It took me a long time to learn that desks are not cleaned by the magic cleaning fairy. There is also no fairy to restock the printer paper.
Test websites I am shocked at how many people who make websites can't test websites. Seriously? You didn't notice the header was gone?
Work through Code Year and Hello World Part of internships is learning, and these are two great resources for doing just that.
Update his own site It's been up forever, and he's been interested in adding to it. Now, he'll have dedicated time.

My mother suggested filing. I didn't have the heart to tell her that I haven't had to file anything in six years.

PyGame: FIGHT FIGHT FIGHT

Stats

Now that I had decided to add combat to the game, I realized I had to give the player and the monsters something to lose. Namely, hit points. Deciding how hard they would hit would be nice, too.

Now that the player was more than a bag of stuff, he needed his own class filled out a bit more:

class Player(object):
    """The player class. Contains level, HP, stats, and deals with combat."""
    def __init__(self):
        self.level = 1
        self.attack = 5
        self.defense = 5
        self.current_hp = 10
        self.strength = 1
        self.name = "Dudeguy McAwesomesauce"

    @property
    def max_hp(self):
        return 10 + (self.level-1)*5

    @property
    def get_defense(self):
        return 1

    def receive_damage(self, damage):
        self.current_hp -= damage

    def attempt_block(self, attack):
        pass

    def get_attack(self):
        return self.strength

The imporant bits here for combat came from my old stand-by, D&D 3.5. There might be simpler systems, but this one covers pretty much anything I might want my guy to do eventually. At the moment, I'm worrying only about good old hand to hand combat, so I have an attack value (affects likeliness of landing a hit), strength (how hard a landed hit is), and defense (likeliness of absorbing a hit).

The monster got a bit of a boost as well:

class Monster(object):
    def __init__(self):
        pass

    def get_attack(self):
        return self.strength

    def receive_damage(self, damage):
        self.current_hp -= damage


class Derpy(Monster):
    def __init__(self):
        self.title = "Derpy Slime"
        self.level = 1
        self.attack = 10
        self.defense = 1
        self.current_hp = 5
        self.max_hp = 5
        self.strength = 1

The monster is a better at hitting, but has a lower defense, and fewer hitpoints. Have to keep things interesting, after all!

Combat!

A flowchart outlining a possible combat scenario.

Oh, man. Combat.

If anything in your game has you breaking down things into the most basic of flow charts, it's combat.

At first, I tried to avoid dragging out the old flowchart. I'm not in CS 100 anymore! I can totally do this in my head! Besides, I've played lots of games. I know how combat works.

Oh, the hubris of the pre-alpha developer.

A few false starts later, it occurred to me that I was going to have to start writing some stuff down. My first go looked like this:

Player moves into monster square → Player hits monster → Monster hits back → Life goes on

Simple. I decided that, even if it looked simple, I would have a module to do the heavy lifting of combat.

from player import Player
from monsters import Derpy

from random import randint

class Combat(object):

    def __init__(self, player, monster):
        self.player = player
        self.monster = monster

    def fight(self):
        '''For now, we'll always start with the player.'''
	# Player, try to hit the monster!
	hit_attempt = randint(0, self.player.attack)
	if hit_attempt == 0:
	    pass
	if hit_attempt > 0 and hit_attempt <= self.monster.defense:
	    pass
	if hit_attempt > self.monster.defense:
	    damage = self.player.get_attack()
	    self.monster.receive_damage(damage)

	# Monster, try to hit back.
	if self.monster.current_hp > 0:
	    hit_attempt = randint(0, self.monster.attack)
	    if hit_attempt == 0:
	        pass
	    if hit_attempt > 0 and hit_attempt <= self.player.defense:
		pass
	    if hit_attempt > self.player.defense:
		damage = self.monster.get_attack()
		self.player.receive_damage(damage)

I decided, since these were stupid monsters, that they wouldn't chase down the Player, nor would they stay locked in combat. They'd get their hit and go on about their business. This will likely change as I make more complex monsters, so this module is going to completely morph over time.

Looking over the code, I noticed that I had started to make myself some setters and getters, but never ended up using them. When I expand combat, this will probably be the first thing I rectify.

Also, checking for empty squares had to get more complicated. Again. UGH.

#In Map:

def is_block_empty(self, row, col):
    if not self.treasure[row][col] and \
      not self.monsters[row][col] and \
      not self.walls[row][col] and \
      not (self.player[0]/TILE_SIZE, self.player[1]/TILE_SIZE) == (row, col):
        return True
    else:
        return False

Death

Rogue and derpy monster are messed up, with weapons sticking out of them everywhere. The rogue is still cheerful, giving the viewer a thumbs up.

One thing I forgot to add at first was death. That's right, I had things running around the board with negative health. D'oh.

One thing about death is that you have to catch it right away. Checking after combat (my first attempt) lead to dead creatures getting one last lick in. Poetic as that was, I decided I wanted them to die right off. So, before trying to hit someone, I had to make sure the creature was still alive. Once we'd returned back to the game, I could deal with all the bodies.

# In Game.move:

if self.has_monster(row, col):
    Combat(self.player_stats, self.map.monsters[row/TILE_SIZE][col/TILE_SIZE]).fight()
    if self.map.monsters[row/TILE_SIZE][col/TILE_SIZE].current_hp <= 0:
        pass #put death throes here
    if self.player_stats.current_hp <= 0:
        self.end_game()
    self.move(0,0)
    return

Now, why am I calling 'move' again, with no actual movement? Because I decided that, while the rogue is fighting something, life should go on. Creatures should keep moving around, possibly complicating his life when they get smart enough to chase after him.

Next time...

We think about seperating our our visual layers.

Fab for All

So, you know how you think you're going to get home from the conference and hop on your computer and do all sorts of awesome things like you said you would? And you know how that's complete and utter BS? I came home and collapsed for about a week. Or two.

At PyCon2012, I gave a talk about setting up your first site in the cloud. It was well received, and there were many requests for me to put up my fab files, in spite them being utter crap. Well, I've gone and done that.

The fab files are very rough. I tried to tidy them up a bit, but in the end, ended up hitting commit and hoping for the best. My hope for this repo is:

I'd really like, at the end of the day, to have a set of scripts that are not only functional, but easy to learn from, and easy to use. And I'd really welcome community support on this!

(Sorry about the cross-posts on Planet Django and Planet Python! I usually try to pick one!)

PyGame: View seperation and throwing away code

Where am I? Here!

View seperation

Katie looks distressed. Caption: How have I not been eaten by wolves?

I did a bad, bad thing.

We're taught very early on about model-view-controller. It's hammered into us constantly. Make your view seperate! It will make your life so much easier!

It's so true, too. MVC (or MVT) is wonderful, and it really does make things so much simpler. It's just easy to deviate from, when you're excited and caught up in making something awesome and new.

Well, no more. All my view stuff is going into it's own class! Game is now dedicated to all my game mechanics, and GameScreen is going to be for all my visual bits.

Splitting Game into two parts was a tiny bit tricky, but nothing that some wine, chocolate, and deep breaths couldn't take care of. Happily, most of my 'draw' functions were already seperate, so it became a game of move a function, update all calls to it, and run the game. If it breaks, fix it. If it doesn't break, move on to the next function.

One thing I had to consider now that everything wasn't in one bucket is what GameScreen had to know. I decided that all I should have to tell it is what I wanted to draw, where. All I'm passing back and forth is a map or some coordinates (where) and sometimes some text (what). This is as it should be.

GameScreen does not know what a monster is. It doesn't know anything about the player, or the inventory, or any other object in the game. Its only concern is what it needs to draw where, and in what order. If you have to import anything besides some constants and PyGame, you're starting to see MVC bleed.

As a counter, Game shouldn't know anything about how these things are being drawn. Maybe it's ASCII, maybe it's 3D. It should only be concerned about the mechanics of the game, like who hits first, and when we get to pick up some awesome stuff. If you see that, you have MVC bleed in that direction as well.

import pygame

from constants import *

class GameScreen(object):
    
    def __init__(self):
        ''' Does the initial drawing of the game screen.
        '''
        self.screen = pygame.display.set_mode((1280, 832))
        self.font = pygame.font.SysFont(None, 48)
        self.small_font = pygame.font.SysFont(None, 20)
        self.bg = pygame.image.load(IMG_DIR + 'rainbowbg.png')
        self.player_blit = pygame.image.load(IMG_DIR + 'dude.png')
        self.screen.blit(self.bg, (0,0))
        self.inventory_screen = self.small_font.render("Inventory", True, WHITE, BLACK)
        self.draw_alert("Welcome to Katie's Roguelike!")
        self.stats_screen = self.small_font.render("ARGH", True, WHITE, BLACK)
        pygame.display.flip()

    def draw_player(self, coord):
        ''' Draws the player at a specific coordinate
        '''
        self.screen.blit(self.player_blit, coord)

    def draw_stats(self, player_stats, color=WHITE):
        ''' Renders the stats for the player
        '''
        self.screen.blit(self.stats_screen, (1008, 0))
        self.stats_screen = self.small_font.render(player_stats.name, True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 0))
        self.stats_screen = self.small_font.render("Level: " + str(player_stats.level), True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 15))
        self.stats_screen = self.small_font.render("HP: %s/%s" % (str(player_stats.current_hp), str(player_stats.max_hp)), True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 30)) 

    def draw_alert(self, alert, color=WHITE):
        ''' Draws the alert box at the bottom 
        '''
        self.alert = self.font.render(LONG_STRING, True, BLACK, BLACK)
        self.screen.blit(self.alert, (0, 790))
        try:
            pygame.display.flip()
        except:
            pass
        self.alert = self.font.render(alert, True, color, BLACK)
        self.screen.blit(self.alert, (0, 790))
        pygame.display.flip()

    def draw_inventory(self, inventory):
        ''' Renders the inventory for the user
        '''
        self.screen.blit(self.inventory_screen, (1008, 100))
        items = inventory.get_items()
        for i in range(items.__len__()):
            line = self.small_font.render(LONG_STRING, True, BLACK, BLACK)
            self.screen.blit(line, (1008, ((i+1)*15)+100))
        pygame.display.flip()
        for item in items:
            line = self.small_font.render(item, True, WHITE, BLACK)
            self.screen.blit(line, (1008, (items.index(item)+1)*15+100))

    def draw_treasure(self, treasure_map):
        ''' Draws the treasure chests yet to be opened.
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if treasure_map[row][col] != 0:
                    treasure = pygame.image.load(IMG_DIR + 'chest.png')
                    self.screen.blit(treasure, (row*TILE_SIZE, col*TILE_SIZE))
    
    def draw_monsters(self, map):
        ''' Draws monsters that appear in the area that the rogue can see
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if map.monsters[row][col] != 0 and map.current[row][col] != 0:
                    monster = pygame.image.load(IMG_DIR + 'dumb_monster.png')
                    self.screen.blit(monster, (row*TILE_SIZE, col*TILE_SIZE))
    
    def draw_walls(self, walls):
        ''' Draws walls on the game map
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if walls[row][col] != 0:
                    wall = pygame.image.load(IMG_DIR + 'wall.png')
                    self.screen.blit(wall, (row*TILE_SIZE, col*TILE_SIZE))

    def draw_darkness(self, map):
        ''' Draws the darkness and shadows on the board. 0 is dark, 1 is in shadows,
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if map.cleared[row][col] == 0:
                    if not map.current[row][col]:
                        pygame.draw.rect(self.screen, BLACK, (row*TILE_SIZE, col*TILE_SIZE, TILE_SIZE, TILE_SIZE))  
                if map.cleared[row][col] == 1:
                    if not map.current[row][col]:
                        shadow = pygame.Surface((TILE_SIZE, TILE_SIZE))
                        shadow.set_alpha(200)
                        shadow.fill(BLACK)
                        self.screen.blit(shadow, (row*TILE_SIZE, col*TILE_SIZE))

    def draw_background(self):
        ''' Draws my glorious background.
        '''
        self.screen.blit(self.bg, (0,0))

    def draw_screen_layers(self, map, player_stats):
        ''' Draws the layers of the game screen
        '''
        self.draw_background()
        self.draw_treasure(map.treasure)
        self.draw_walls(map.walls)
        self.draw_monsters(map)
        self.draw_darkness(map)
        self.draw_stats(player_stats=player_stats)
        self.draw_player(coord=map.player)
        pygame.display.flip()

Throwing away code

Sometimes, I look at my code, and think, what horrible thing have I wrought? I know others do, too. It's tempting, to go in and try to get everything working perfectly. Sure, the combat works, but it could be more efficient! It could be more elegant! I could rid myself of the dread that Guido is going to jump out of a closet and ax-murder me!

The rogue and the derpy monster have been put out with the trash. The rogue doesn't look happy.

It's tempting, but I have to remind myself of something: I'm going to throw all this code away.

Keeping bad code is a habit that I've seen even the best developer succumb to. I spent forty hours on this! If I throw it away, I'll be throwing away all the time I spent on it! Look, just a few more hours, and it'll be better, okay? Maybe you just don't understand its innate genius.

No. It is bad code. Throw it away.

I've seen people treat code as if it's their child. Having two children, I say you should relish the things in your life that you can screw up and toss out without worry.

When I have a working roguelike, the most important thing I'll have won't be a game: it'll be the lessons I learned. At that point, I can spend a day looking over the monster I've brought into this world, and figure out where I could possibly be more efficient. Where am I duplicating effort? What did I never use? Where did I commit my cardinal sins because I was coding after my second glass of wine (again)?

Once I've bled the code dry of all its usefulness, into the wastebin it goes. With what I've learned under my belt, I can recreate the game, but this time without the stumbling about and 'Oh, Hell's of before. It won't take me nearly as long, and I can cut out the ugly bits from the start.

The only reason I decided to pull apart the screen code was because it was starting to make it harder to code, and I really wanted to talk about MVC. The time it took me to do that, however, was time I didn't spend making combat cooler, or adding more treasure, or adding rooms. In this case, I decided it was worth it, but for most things, I just choose to forge ahead.

Next time!

We're back to adding cool features! I think we need some rooms!

PyGame: Rooms!

Where am I? Here!

Algoritms

Before we dive in, I should point out that this world we're in is not short on room building algorithms. Google it, and you'll be awash in them. There's ones that do the basic rooms + hallways setup. Others generate walls in such a way that rooms naturally occur. Some add different kinds of rooms, like meeting halls and living quarters, and do it in a semi-reasonable fashion. Why don't I use one of those?

The rogue and the slime monster are in a very badly constructed room. The rogue looks worried. The monster is wearing a hard hat.

I might, eventually, use one of those (the one on Roguebasin looks especially promising). But before I do, I really want to understand the parts of the algorithm. I want to try making my own rooms and hallways before I let someone else do it for me. Will mine be awesome? Of course not! It'll be crap! It will have the minimum number of features I would want, and I bet its error checking won't be anywhere near as nice as one that's been tweaked for years, by dozens of hands, and tested by hundreds of users.

I believe in trying to do things on your own, at least once, though. I once built the world's most crap CMS ever from scratch. It not only gave me a deep appreciation for other frameworks, but made it easier for me to understand why they might have built theirs the way they did.

Making a room

I decided to start simple. All I wanted, for my first round, was to make a darn room. This is made much harder if you can't actually see the whole map, so I decided to turn off everything but the floor and the walls.

def draw_screen_layers(self, map, player_stats):
        ''' Draws the layers of the game screen
        '''
        self.draw_background()
        #self.draw_treasure(map.treasure)
        self.draw_walls(map.walls)
        #self.draw_monsters(map)
        #self.draw_darkness(map)
        #self.draw_stats(player_stats=player_stats)
        #self.draw_player(coord=map.player)
        pygame.display.flip()

Then, I created a class for my room:

class Room(object):

    def __init__(self, height=5, width=5, start=(0,0)):
        self.title = "Generic room"
        self.start = start
        self.width = width
        self.height = height
        self.end = (self.start[0]+self.width, self.start[1]+self.width)

Why? Why not just generate room like structures with walls? Because I know me. I'm not going to be able to resist tossing some flavor text in there. It also seems really useful to have a list of rooms. Maybe, at some point, I want to make sure I don't have too many monsters being generated in one room, or that no one room has all the treasure. Or, maybe I want to make a room that has ALL the treasure. We'll see.

As for adding the room, Gamemap does that, since a room is really just a bunch of walls:

def create_room(self, room):
        # make top and bottom walls
        for i in range(0, room.width):
            self.walls[room.start[0]+i][room.start[1]] = 1
            self.walls[room.start[0]+i][room.start[1]+room.height-1] = 1
        # make side walls
        for i in range(0, room.height):
            self.walls[room.start[0]][room.start[1]+i] = 1
            self.walls[room.start[0]+room.width-1][room.start[1]+i] = 1
        # fill in the floor
        for x in range (1, room.width-1):
            for y in range (1, room.height-1):
                self.rooms[room.start[0]+x][room.start[1]+y] = 1

I will not lie. Getting the walls to draw correctly took me an embarrassingly long time. I kept forgetting to subtract one, or I'd add my incremental variable to the wrong index.

At this point, though, I can at least draw a room. But I don't want just one room, do I? I want a number of random rooms! I don't want them overlapping each other, though, so I need to check all the squares that a room might contain first. Thus, check_room is born.

def check_room(self, coord, height, length):
        ''' Are all the spaces in a room free?
        '''
        for i in range(0, height):
            for j in range(0, length):
                if coord[1] + i > COLUMNS-1:
                    return False
                if coord[0] + j > ROWS-1:
                    return False
                if self.rooms[coord[0]+j][coord[1]+i]:
                    return False
        room = Room(start=coord, height=height, width=length)
        return room

I don't care if the rooms share walls, as that's often how rooms work. I don't want them to share floorspace. You might have noticed, I've added a new layer to my map for rooms. This is JUST for the open floor in a room, and is filled in when the room is created.

Now, I can get a bunch of random rooms (always generating one at 0,0, because I am boring).

def get_rooms(self):
        # Set initial room
        room = self.check_room(coord=(0,0), height=5, length=5)
        self.roomlist.append(room)
        rooms = 1
        keep_going = 50
        while rooms <= MAX_ROOMS and keep_going:
            height = randint(4,7)
            length = randint(4,7)
            x = randint(0, COLUMNS-1)
            y = randint(0, ROWS)
            room = self.check_room(coord=(x,y), height=height, length=length)
            if room:
                rooms += 1
                self.roomlist.append(room)
            else:
                keep_going -=1
        

What the heck is keep_going? It's possible for the board to be filled with rooms in such a way that no more rooms can be placed. Infinite loops are bad and embarrassing, so at some point, we just tell the game to give up and continue with loading the level.

Adding stuff back

A small room is almost completely filled by slimes and chests.

Another way having floors is useful: it makes it easier to tell where I can place something. I don't want my creatures outside of the room. That's just wasted XP, and is going to taunt the player. Happily, making things appear on floors is really easy. Just check to make sure your random square is actually a floor.

# in __init__()

for i in range(TREASURES):
    while 1:
        col = random.randint(0, COLUMNS-1)
        row = random.randint(0, ROWS-1)
        if not self.treasure[row][col] and self.rooms[row][col]:
            self.treasure[row][col] = Treasure()
            break

for i in range(MONSTERS):
    while 1:
        col = random.randint(0, COLUMNS-1)
        row = random.randint(0, COLUMNS-1)
        if not self.treasure[row][col] and self.rooms[row][col]:
            self.monsters[row][col] = Derpy()
            break

I could do something fancy, like get all the squares that are floors and pick from them, but doing it this way isn't eating up significant processing power. Now that they're back, we can start drawing them again, as well. The only thing I'm holding off on is the darkness, since it's very likely I'll be reimplementing that.

Next time!

Doors and hallways!

Pygame: Hallways

Where am I? Here!

Hallways!

In the last post, I made a reasonable number of rooms that would not overlap with each other. Rooms are nice and all, but what we really want are ways to get from room to room.

The plan

I ran through a ton of graph paper while brainstorming on how I wanted to do this. I didn't want an orphaned areas. I wanted some bends in the hallways and some reasonable variation. I wanted this plan to scale to larger spaces. I also wanted the simplest possible solution.

I finally settled on the simplest pattern I could find: give each room a door. Connect the doors, starting at the first generated room, and ending with the last room connecting to the first, using just one bend. Knock down all walls on the way. Simple, right? Super simple! I should barely be cursing at all!

Doors

Rogue guy is wielding a sledgehammer, looking determined.

The first thing I would need to make is doors. Right off the bat, I saw that this wasn't going to be as easy as knocking out a random wall. What use is a door that's in a corner, or at the edge of the map? What if the door is right next to another wall? Should I just smash the wall and connect the two rooms?

After lots and lots of sketching, I came up with the following rules:

A sketch showing where doors would be okay, and where they would not be possible.

The above lead to the following code:

def check_door(self, coord, check, next):
    ''' Can the current block be turned into a door?
    '''
    # Is it at the bounds?
    if check[0] < 0 or check[1] < 0:
        return False
    # Is it next to a wall?
    try:
        if self.walls[check[0]][check[1]]:
            # Is that wall next to another wall?
            if self.walls[next[0]][next[1]]:
                return False
            else:
                try:
                    self.walls[check[0]][check[1]] = 0
                except:
                    pass # Sometimes, we're one away from the border. That's okay.
    except:
        return False
    return True

With this, if the door is okay, I return True, with any extra blocks that needed destroying taken care of. If there are any issues, I return False, which will force the function that's trying to make the door to try again with a new random square.

def make_random_door(self, room):
    while True:
        wall = choice(DIRECTIONS)
        if wall in ['north', 'south']:
            block = randint(1, room.width-2)
        else:
            block = randint(1, room.height-2)
        if wall == 'north':
            coord = (room.start[0]+block,room.start[1])
            check = (coord[0], coord[1]-1)
            next = (coord[0], coord[1]-2)
        if wall == 'south':
            coord = (room.start[0]+block, room.start[1]+room.height-1)
            check = (coord[0], coord[1]+1)
            next = (coord[0], coord[1]+1)
        if wall == 'east':
            coord = (room.start[0],room.start[1]+block)
            check = (coord[0]-1, coord[1])
            next = (coord[0]-2, coord[1])
        if wall == 'west':
            coord = (room.start[0]+room.width-1, room.start[1]+block)
            check = (coord[0]+1, coord[1])
            next = (coord[0]+2, coord[1])
        door = self.check_door(coord, check, next)
        if door:
            self.walls[coord[0]][coord[1]] = 0
            self.floor[coord[0]][coord[1]] = 2
            room.door = (coord[0],coord[1])
            return

This way, every room has a proper door that we can use to make a hallway.

Hallways

I considered quite a few options when I thought of hallways. I played with the idea of having them bend a random number of times, or have random dead ends. I even considered making the passages twist and turn, in a more natural way. In the end, though, I went with what was simplest: start at a door, tunnel over until you're at the next door's column, tunnel up or down.

At this point, I had added a list of rooms to my map, and added the door coordinates to the rooms, so I was able to go go down the list, connecting one to the next as I went. I could have tried tunneling to the closest one, but I found I liked the branching more, connecting random rooms to each other.

def connect_rooms(self):
    for room in self.roomlist:
        i = self.roomlist.index(room)
        # If we're at the last room, connect the last to the first.
        try:
            next = self.roomlist[i+1]
        except:
            next = self.roomlist[0]

        #Determine where we're starting, and where we're ending.
        if room.door[0] < next.door[0]:
            start = room.door[0]
            end = next.door[0]
        else:
            start = next.door[0]
            end = room.door[0]

        # Tunnel over
        for x in range(start, end):
            self.walls[x][room.door[1]] = 0
            self.floor[x][room.door[1]] = 1

        # Determine where we're starting, and where we're ending, again
        if room.door[1] < next.door[1]:
            start = room.door[1]
            end = next.door[1]
        else:
            start = next.door[1]
            end = room.door[1]

        # Tunnel up/down
        for y in range(start, end):
            self.walls[next.door[0]][y] = 0
            self.floor[next.door[0]][y] = 1

Filling in

I had considered building walls around my hallways, but in a moment of fatigue, filled in the rest of the blank spaces on the board, and decided that I liked that better. Goodbye, forever, glorious rainbow background!

def fill_map(self):
    for i in range(ROWS):
        for j in range(COLUMNS):
                if not self.floor[i][j]:
                    self.walls[i][j] = 1

The Final Result

A screenshot of the Roguelike, with rooms and hallways implemented

Next

We make our items worth getting!

PyGame: Returning to Items

Where am I? Here!

Buffing up items

At the moment, Items are rather bland: they're just a title and a description. We want them to actually mean something, though, so it's time to give them a bit more shape.

class Treasure(object):
  ''' Things that can be picked up. 
  '''
  def __init__(self, title="Nada", description="", type="trash", armor=0, buff=0, attack=0):
    self.title = title
    self.description = description
    self.type = type
    self.armor = armor
    self.buff = buff
    self.attack = attack

Okay, not the most exciting upgrade ever, since we're only making armor and swords, but we're going with baby steps here. Though I've added three attributes, we'll only really be using two: armor and attack. Now that the item can have a bit more oomph, let's look at the Player again.

Getting Player ready

Right now, Dudeguy is pretty basic. I need to get him ready to deal with not only carrying things, but having those things matter.

class Player(object):
  """The player class. Contains level, HP, stats, and deals with combat."""
  def __init__(self):
    self.level = 1
    self.stats = {
      'strength': 1,
      'attack': 5,
      'defense': 5
    }
    self.current_hp = 10
    self.name = "Dudeguy McAwesomesauce"
    self.equipped = {}

    for treasure in EQUIPMENT_TYPES:
      self.equipped[treasure] = None

I decided to make stats a dictionary, since I might want to give some classes a stat that others didn't, and this seems like the easier way of doing this. Also, it's easier to iterate for my interface, once I get to that.

I've also added a dictionary for equipped items. He starts off with the standard equipment slots:

EQUIPMENT_TYPES = ('hat', 'shirt', 'pants', 'shoes', 'back', 'neck', 'hands', 'weapon')

but no equipment, of course. Naked adventuring, FTW!

Managing items

Now that I was buffing up items, I needed a better way to manage them. Looks like my game will need an admin tool. Enter admin.py:

import sys, pickle
import sys, pickle

sys.path.append("roguey/classes")

from items import Treasure
from constants import *

class Admin(object):
  def __init__(self):
    f = open("roguey/resources/items.pk")
    try:
      self.treasures = pickle.load(f)
    except:
      print "No treasures!"
      self.treasures = []
    f.close()
    self.main()

  def new_treasure(self):
    for treasure in TREASURE_TYPES:
      print "%s. %s" % \
              (TREASURE_TYPES.index(treasure)+1, treasure)
    choice = raw_input("Pick a type [1-9]: ")
    type = TREASURE_TYPES[int(choice)-1]
    title = raw_input("Give it a title: ")
    desc = raw_input("Give it a description: ")
    attack = 0
    armor = 0
    if type == 'weapon':
      attack = raw_input("How much damage will it add? [1-999]: ")
      attack = int(attack)
    else:
      armor = raw_input("How much armor will it add? [1-999]: ")
      armor = int(armor)
    tr = Treasure(title=title, \
               description=desc, \
               type=type, \
               armor=armor, \
               attack=attack)
    self.treasures.append(tr)

  def list_treasures(self):
    for treasure in self.treasures:
      print treasure.title

  def save(self):
    f = open("roguey/resources/items.pk", "w")
    pickle.dump(self.treasures, f)
    f.close()

  def main(self):
    while 1:
      print "1. Make a new treasure"
      print "2. List current treasures"
      print "0. Quit"
      c = raw_input("Make a choice [1-2, 0]: ")
      if c[0] == "1": self.new_treasure()
      if c[0] == "2": self.list_treasures()
      if c[0] == "0": 
        self.save()
        return

if __name__ == "__main__":
  a = Admin()

It's simple, but it made managing my items so much easier. Now, rather than maintaining a text file (or *gasp* a spreadsheet) of items, I could deal with them through this script. My items could be saved as a Pickle and checked into the repo.

Wait, a Pickle?

Pickles!

Rogue guy has found some pickles in a treasure chest. He doesn't look happy.

Now that we have all this data, we need to save it somehow. In this instance, I've chosen to use Pickles.

Pickles get a bad rap, mostly due to the fact that they're not very secure. It's almost a knee-jerk reaction at this point. Go ahead, test it: walk into any Python discussion, and suggest that they use Pickles. Watch them foam at the mouth.

Pickles are inherantly insecure. You should never accept a Pickle from a source you can't trust (this is still true if talking about lower-case pickles). In this case, we know who made the Pickle, so we're good. If you decide to fork my code and turn my roguelike into something where people would be swapping these piles of items... well, you'll have a bit more work cut out for you.

Giving the player items

Now that the items are worth having, and we have a selection of them, let's have the player pick them up.

For now, I only have one of each type of item, so when the player comes to an item and doesn't have anything in that slot, it's automatically equipped. If something is already there, into the backpack it goes, because as we know, all players are cleptomaniacs.

def add_to_inventory(self, item, player):
  ''' Adds an item to the inventory
  '''
  if item.type == "trash":
    return
  if player.equipped[item.type]:
    try:
      self.inventory[item] += 1
    except:
      self.inventory[item] = 1
  else:
    player.equip_item(item)

Now that the player can equip items, we need to update our status screen area to reflect that:

def draw_equipment(self, equipment=START_EQUIPMENT):
  ''' Renders the equipment. Expect it to be exchanged for something
    awesomer
  ''' 
  self.screen.blit(self.equipment_screen, (1008, 200))
  for i in range(equipment.keys().__len__()):
    line = self.small_font.render(LONG_STRING, True, BLACK, BLACK)
    self.screen.blit(line, (1008, ((i+1)*15)+200))
  pygame.display.flip()
  i = 1
  for slot in EQUIPMENT_TYPES:
    try:
      line_text = slot + ":  " + equipment[slot].title
    except:
      line_text = slot + ":  "
    line = self.small_font.render(line_text, True, WHITE, BLACK)
    self.screen.blit(line, (1008, i*15+200))
    i += 1
  pygame.display.flip()

Side note: I got a ten-minute lecture from my son about how 'awesomer' isn't a word. What are they teaching kids these days?!

Next time!

Combat needs to be updated, now that we have all these great items!

PyGame: Returning to Combat

Where am I? Here!

Revisiting admin.py

So, remember how I joked that Pythonistas froth at the mouth over Pickles? Yeah, my admin module (which was just a bit of trash code to be used while making the game) was easily the most contraversial bit of code I churned out. This is why working on open source is a good thing, though! It inspired someone to completely rewrite it! Thanks, Ian!

The new and improved admin.py:

import sys
import xml.etree.ElementTree as etree
from xml.dom.minidom import parseString

sys.path.append("roguey/classes")

from items import Treasure
from constants import *

def prettify(element):
  # Helper function to make XML look more prettier
    txt = etree.tostring(element)
    return parseString(txt).toprettyxml()

class Admin(object):
  def __init__(self):
    # Load the existing treasures
    f = open("roguey/resources/items.xml")
    self.treasures = etree.fromstring(f.read())
    f.close()
    # trim the annoying whitespace...
    self.treasures.text = ""
    for element in self.treasures.iter():
      element.text = element.text.strip()
      element.tail = ""
    # Load the list of treasure type templates
    f = open("roguey/resources/item_templates.xml")
    self.treasure_templates = etree.fromstring(f.read())
    f.close()
    # Enter main loop
    self.running = True
    self.main()   

  def new_treasure(self):
    item_attributes = {}  # This will hold optional stats only.

    template_options = [
      template.find("item_type").text for template in self.treasure_templates
    ]

    # Gather the mandatory attributes
    selection = self.prompt_for_selection(
      prompt="Choose an item type",
      options=template_options
    )
    item_type = template_options[selection]
    template = self.treasure_templates[selection]

    title = raw_input("Give it a title: ")
    description = raw_input("Give it a description: ")

    # Check if this template requires any additional attributes
    for attr in template:
      if attr.tag == "item_type":
        continue
      prompt = attr.attrib["prompt"]
      value_type = attr.attrib.get("type", "string")  # type defaults to "string" if not specified
      item_attributes[attr.tag] = (raw_input("%s (%s): " % (prompt, value_type)), value_type)

    # finally we can add this new item to the list
    new_item = etree.SubElement(self.treasures, "item")
    etree.SubElement(new_item, "item_type").text = item_type
    etree.SubElement(new_item, "title").text = title
    etree.SubElement(new_item, "description").text = description
    for attrib, value in item_attributes.iteritems():
      optional_stat = etree.SubElement(new_item, attrib)
      optional_stat.text, optional_stat.attrib["type"] = value

  def list_treasures(self):
    for treasure in self.treasures:
      print treasure.find('title').text.strip()

  def save_and_quit(self):
    f = open("roguey/resources/items.xml", "w")
    f.write(prettify(self.treasures))
    f.close()
    self.running = False

  def delete_treasure(self):
    pass

  def main(self):
    menu_options_with_actions = [
      ("Make a new treasure", self.new_treasure),
      ("List current treasures", self.list_treasures),
      ("Delete a treasure", self.delete_treasure),
      ("Quit", self.save_and_quit),
    ]
    menu_options = [x[0] for x in menu_options_with_actions]
    menu_prompt = "Make a choice"

    while self.running:
      selection = self.prompt_for_selection(menu_prompt, menu_options)
      # Call the appropriate action based on the user selection
      menu_options_with_actions[selection][1]()

  def prompt_for_selection(self, prompt, options):
    """Given a list of options and a prompt,
    get the users selection and return the index of the selected option
    """
    # Print out the numbered options
    for i, option in enumerate(options):
      print "%3s. %s" % (i+1, option)
    # Get the users selection
    selection = raw_input("%s: " % prompt)
    return int(selection)-1

if __name__ == "__main__":
  a = Admin()

It's been updated to use an XML file, which is semi-human-readable, and the code for generating the menu has been made cleaner. Woot!

Planning ahead

Katie sits before a desk with crumpled paper on it. She holds up another piece of paper, a joyous look on her face. Caption: A plan! Now, surely things won't go to shit!

At one point, I had this lovely roadmap of what I was going to do, when, for my roguelike. That list is about as relevant today as a midieval monk's advice on what smartphone I should get. I quickly found that a round-robin approach was working much better for me. I was able to crank out enough for a blog post each week, and I wasn't getting caught up on smaller details that relied on other modules being done.

A list isn't a bad thing to have, though. You just have to respect that the universe hates a tidy plan.

As the game has grown more complex, however, I've found I've had to stop and think about what bit I need to develop first more often. Modules are starting to affect each other more, and the changes I'm making are more subtle. For example, when do I want to add animation back in? After I've added all my monsters? Before I put darkness back? Hm, when do I want to add darkness back? How about levels?

It's tempting fate, but I put together a rough roadmap for the rest of game:

You may now take bets on how well I'll adhere to this.

Combat

Combat wasn't broken in the sense that it didn't work: it was broken in the sense that it worked in spite of the way it was written. In order to get it ready for new monsters, classes, stats, and levelling up, it needed some serious work. I needed to use proper settings and getters, because now my items had meaning. First, I had to update Player.

First, I decided that having stats as their own property was getting an little unweildy. It was much better to put them into a dictionary, for later spitting out onto the screen.

def __init__(self):
  self.level = 1
  self.stats = {
    'strength': 1,
    'attack': 1,
    'defense': 5
  }
  self.current_hp = 10
  self.name = "Dudeguy McAwesomesauce"
  self.equipped = {}

  for treasure in EQUIPMENT_TYPES:
    self.equipped[treasure] = None

Now, throughout my code, I'd referred to the 'strength' and 'defense' attributes. Rather than hunt them all down, I decided to name my new properties the same thing.

@property
  def defense(self):
      return self.stats['defense'] + self.get_armor()

  @property
  def strength(self):
      return self.stats['strength']

  def get_armor(self):
      armor = 0
      for slot in self.equipped.keys():
        if self.equipped[slot]:
          try:
            armor += self.equipped[slot].armor
          except AttributeError:
            # The Treasure in this slot doesn't have an 'armor' attribute
            pass
          except TypeError:
            # The Treasure in this slot has an armor stat, however it
            # appears to be a non numeric type. Make sure this Treasures armor stat
            # has the attribute type="int" in its XML definition.
            # This sort of error should probably get properly logged somewhere
            print (
              'Please make sure the armor stat for the "%s" weapon '
              'has the type="int" attribute in its XML definition' % self.equipped[slot].title
            )
      return armor

The properties can now be called like any other attribute:

>>> from player import Player
>>> p = Player()
>>> p.strength
1
>>> p.defense
5

Now that the Player is working well, I need to update my Monster class to work the same:

class Monster(object):
  def __init__(self):
    self.level = 0
    self.stats = {
      'attack': 0,
      'defense': 0,
      'strength': 0,
    }
    self.max_hp = 1
    self.current_hq = self.max_hp

  @property
  def attack(self):
    return self.stats['attack']

  def receive_damage(self, damage):
    self.current_hp -= damage

  @property
  def defense(self):
    return self.stats['defense']

  @property
  def strength(self):
    return self.stats['strength']

class Derpy(Monster):
  def __init__(self):
    self.title = "Derpy Slime"
    self.level = 1
    self.stats ={
      'attack': 5,
      'defense': 1,
      'strength': 1,
    }
    self.current_hp = 3
    self.max_hp = 3

Hm. I'm starting to see overlap here (well, to be honest, I saw overlap a while back). I'll make a note to keep an eye on how alike these two things stay, and if it would be worth it to make a new base class when the time comes to refactor.

Now, let's update combat!

*looks at combat module*

And nothing there needs to be changed.

class Combat(object):

  def __init__(self, player, monster):
    self.player = player
    self.monster = monster
    self.fight()

  def fight(self):
    '''For now, we always start with the player.
    '''
    # Player, try to hit the monster!
    hit_attempt = randint(0, self.player.attack)
    if hit_attempt > self.monster.defense:
      damage = self.player.strength
      self.monster.receive_damage(damage)
    else:
      pass

    # Monster, try to hit back.
    if self.monster.current_hp > 0:
      hit_attempt = randint(0, self.monster.attack)
      if hit_attempt > self.player.defense:
        damage = self.monster.strength
        self.player.receive_damage(damage)
      else:
        pass

The only thing I re-jiggered was having combat call fight itself, since there's really no other reason for me to call Combat, and removed some unnecessary checks. Everything is set up, now, for our next addition...

Next time!

We add smart mobs!

Getting more women to your meet-up

I get this question a lot. Like, once a week.

I've puzzled over it, because whenever I start to answer it, I sound a bit rambly. There's a reason for that, though: women take up half of this planet. A population that big isn't easy to target (in spite what advertisers would have you believe).

See, there's so many reasons why women might not be coming to your meet-up or hacker space. Maybe it's a bit guy heavy (I know, catch-22, right?). Maybe they're already booked. Maybe they have obligations at home. Maybe they just don't know about your event. Maybe they think they won't get anything from it. Whatever the reason, one thing is clear:

You can't use just one strategy if you want more women to show up at your tech event.

So, what are some strategies you can use?

Invite them

No, don't just put 'Ladies welcome' on your flyer. Invite specific women to your event. A word of warning, though: there's a right way to do this, and a wrong way to do this. The wrong way:

Hey! We're trying to encourage more women to show up at our tech event, and I was wondering if you'd like to come?

Sounds pretty tame, right? It's polite, it's sleaze-free... and it makes it sound like the only reason you're inviting this person is because she's a woman. This does not make me want to come to your event, which is what you're aiming for, right? What if you drop the 'encourage women attendance' bit?

Hey! I run a meet-up on Thursdays! You should come!

That's fine, but there's lots of meet-ups this person could be going to. Also, if this worked well, you wouldn't be in the situation you're in right now, would you? What do you do? Well, here's an example that worked on me:

Hey! I read your roguelike posts, and loved them! Do you think you could do a talk about them at my meet-up once you're done?

I couldn't clear my calendar fast enough. I was flattered (and I felt the invite was genuine), and I felt like I had something I could add to this group. Even if he'd added that they were trying to attract more women (which came up later), I was still happy to attend.

But what if you don't know any women in your area who are active bloggers, or who actively commit to open source projects that you can use as conversation starters?

Do an alternate night now and then

Childcare isn't an issue for all women, but man, once it's on the table, it's a big issue. It's different for every parent, but often, it's easier to get alternative child care on one night rather than an other. For me, weekday events are almost impossible for me to get to, but weekend ones are a snap. Other women I know can get away during the week more easily than they can on the weekend.

So... should you get day care of your own at your meet-up? No, probably not. Legal and money issues aside, there's a simpler solution: move the date once a month.

I bet your meet-up is on the same day of the week every week, and at the same time. If the women in your area happen to be in the group that can't attend on that night, moving it once a month would enable them to attend. As a side benefit, anyone who can't attend due to work hours or other obligations can suddenly show up.

Advertise somewhere new

As everyone should be aware by now, the tech industry is having a hard time recruiting and holding on to women. This means that the women you would traditionally consider inviting to your meet-up are already pretty rare. What do you do?

At a recent PyLadies gathering, we had women from the following industries:

Counting heads, I realized that almost half of the women present did not call themselves a 'developer,' and yet, they were interested in learning more about Python. So... why are you still advertising only on Planet Python?

Reach out to non-traditional venues that can take advantage of whatever your meet-up is about. Teachers benefit from pretty much anything that's free and can be used in the classroom, or to make their lives easier outside of it. Journalists are being asked to know more about the platforms their careers depend upon. If someone is working in an office that's cash-strapped, it benefits everyone if even the non-developers know about how to code, even if it's only to work with templates, and be able to read the code the devs checked in.

So, come up with a pitch about how your meet-up could benefit someone that isn't a developer, and see if you can push it somewhere new. Does someone in the group have kids? See if they can talk to the teachers at the kid's school.

Have a clear harassment policy, and enforce it

Yeah, this one has been an issue in the past for me.

Back in college, I went to the *nix meet-up at our school. There was a guy there that was, well, less than pleasant to anyone female that walked in the door. He would talk over us, deride anything we had to say, and if we had a question, he'd made snide comments. He was sometimes a jerk to the guys in the group, but he was batting 1.000 for the women.

The three women in the meet-up brought it up to the head of the group.

"Oh, just ignore him."

There was no harassment policy in place, and even if there had been, I wonder if anyone would have been willing to do anything.

The thing is, we had a choice as to what we could do with our Thursday nights. We didn't have to go to this group. There were other tech meet-ups in the area. We left.

Have a policy. Enforce it. It'll ensure the women that do show up actually stay.

More ideas?

This is far from an exhaustive list. They're simply the handful ideas I had when examining why I didn't go to more meet-ups (or issues that other women had shared with me). If you have more ideas, please, leave them in the comments.

DjangoCon - DC Tips

Mad props to Jackie Kazil, who helped me write this post!

So, coming to DjangoCon in September? Let's have a chat about DC.

The location

DjangoCon DC is actually in Northern Virginia, not in the heart of the Capitol. Specifically, it's being held in Crystal City at the Hyatt Regency, which is close to the Crystal City Metro Station (the yellow and blue metro lines) and one stop away from Reagan National Airport (DCA).

I worked in the area for many years, and while it doesn't have as much of the DC grit and history, it makes up for in being a great nexus between convenience and cost. As someone who's been to several conferences in DC, I can tell you this is a good thing. It is a safe, clean, affordable, and transportation-accessible part of the DC metro area that is littered with Django developers.

If, for some reason, you aren't going to be staying at the conference's hotel, you're in luck. DC has a LOT of hotels. In general, the closer they are to a metro stop, the more expensive they are, but it's still possible to find no-frills hotels very close to a metro stop. If you want to remain close to the main action, I would get a hotel at L'Enfant (which is close to the Mall), Crystal City, or National Airport. Franconia/Springfield may look convenient, but it's actually not close to anything.

Getting to DC

Flying


View DC airports in a larger map

There are three airports in the D.C. region. Travel sites like Hipmunk and Kayak try to make all our local airports equivalent, as if it's just a short jaunt from each to DC. This is a dirty, nasty lie.

The airport visible from the hotel is Reagan National. It is only one metro stop from the hotel (or just under a mile walk if you are crazy). The other airports, Dulles and BWI, are inconvenient but usually cheaper if you are on a budget and willing to spend an extra hour to two hours using a combination of shuttle services, Amtrak, buses, metro. If you choose to stomach one of the other options, then add about $15 to your trip. If you take a taxi, you could end up spending more than the cost of your savings.

If you're coming in on an international flight, it may be worth looking for a flight that lands at some other international airport like Atlanta or New York, then catch a flight into Reagan. Trust me, you will not regret this.

Train

The train station is located at Union Station. It can be nice way to travel if you are on the Eastern Seaboard. The commute from Union Station to the conference isn't bad, since Union Station is also a metro stop.

Chinatown buses and other bus services

For those on a budget or looking for adventure and located on the Eastern Seaboard you might check look into the Chinatown buses. Various companies in Chinatown shuttle to Baltimore, Philideplia, New York, and Boston. D.C. to New York is between three and five hours (depending on your driver aherance to the speed limit) and costs around $20 each way. They will drop you off in Chinatown near the metro, which is the same metro line that you would need to get to the conference. Sometimes you can find a service that offers wifi and plugs and sometimes there are live animals and sketchy late night stops. Adventure!

Other buses services will drop you off in other parts, but they are usually close to a metro stop. Bolt Bus is probably one of the nicest.

Driving

Don't. It's not worth it unless you are car pooling. You won't need your car while visiting. If you do though, you can park you car at the Hotel (expect to pay a pretty hefty amount per night, though). If you want to bring your car, bring a GPS and some antacids. I've been told we're pretty bad drivers around here.

Transportation

DC's metro is stupidly simple and is probably going to all the places you want to go. DC metro area cabs are priced pretty well as a back up. I wouldn't bother to rent a car.

However, if you want to rent a bike, I have good news! There's a Revolution Cycles in Crystal City that rents bikes by the week. I spoke with the owners, and they have about 100 bikes available. Given how many people are going to be in town, they recommended reserving one ahead of time.

I'm no bike enthusiast (I still think my green Huffy was pretty rad), but I have been informed by bike geeks that these are good bikes.

There is also BikeShare, which is like Zipcars for bikes, through DC and in the conference hotel neighbor. Bring your own helmet, though. Don't be one of those guys. But there are only 10 spots at the hotel, so don't count on easily finding a bike at the conference. This is a better option used to tool around D.C. for a couple hours.

At the moment, I don't have much information regarding bike parking at the hotel. Hotels in the region are starting to have bike parking sections available, but if you're renting a bike for the week, keep in mind that you might be keeping it in your room (I hope I'm wrong).

More soon!

I'm going to post something about our tourist scene, if anyone is interested in seeing the sites while you're here.

DjangoCon 2012 - Life in DC

As long as you're in town, you might as well see some of the sights, right?

Smithsonian

When people come into town, I always point them at the Smithsonian. It's an awesome set of museums that are completely free. You can spend a week going through them and still not see all of the exhibits. They're also only closed two days a year, and neither of those days are during the conference.

If you're a gamer, make sure to check out The Art of Video Games at the American Art Museum. If gaming isn't your thing, there's still plenty to see.

Other museums

I adore the International Spy Museum and the Museum of Crime and Punishment. They film America's Most Wanted in the latter! You can get deals for both museums off of Groupon on a regular basis, so keep an eye out for those.

Nearby, there's also a Madame Tussauds if you want to go look at creepy wax people. Also, the National Academy of Sciences has a museum that's super cheap and has some awesomely detailed exhibits. It's not a huge museum, but it's packed with information. They're closed on Tuesdays, so plan accordingly.

And there's so many more! We have no shortage of museums.

Monuments!

If you want to see monuments, buy a step-on-step-off tour. Seriously. The monuments are NOT close to each other, and some of them are surrounded by highway. The buses come by every thirty minutes, which is about how long a monument is likely to entertain most people.

I wanna run!

I confess: I'm not a runner. I've toyed with Couch-to-5k a few times, but the summer always beats me. I did, however, find this trail right by the hotel. amk also recommended Bike Washington for some multi-purpose trails.

I need to go shopping

You forgot to pack socks. We're hit by a freak snow storm. They lost your luggage. Your kids will not let you back in the house without souvenirs. The vendors ran out of your t-shirt size. It's cool. We've got you covered. We're right by the Crystal City Shops. They're easy to miss because the shops are hidden inside the buildings at Crystal City, behind the restaurants.

If your needs aren't covered by those stores, you can ride one metro stop over to Pentagon City Mall and pick up almost everything you need, from clothes to Apple connectors. They even have a Costco, if you need a palate of danishes or something.

Next: FOOD

Yes, you want to eat and possibly hit up a bar while you're here. I'm still compiling that list! Locals, shout out your favorites so I can add them.

DjangoCon 2012 - Eating in DC

A caveat: When I'm eating in DC, it's usually because there's a Capitals game going on. I have very deep knowledge of the food options near the Verizon center and that's about it. Happily, DC people are heavy users of Yelp and such, so finding the good places isn't too difficult.

I highly recommend making reservations if you can. DC is unpredictable. I've walked into a restaurant on Friday night and found it dead. I've gone back a few weeks later on a Tuesday, and it's jammed. You don't know when conferences will be in town, so just save yourself some heartache and use Open Table.

That said, here's my list of places to eat and drink in DC!

Near the hotel

The hotel is near quite a bit of food. Some of the restaurants are chains (though higher-quality chains) and some are family owned.

Let's go to the city!

The city is a short metro ride away. I highly recommend making the trek at least once.

Chinatown

Um, this is awkward. See, we have a Chinatown... and we put a stadium in the middle of it. As a result, our Chinatown isn't what most people expect. We have a Ruby Tuesdays with Chinese calligraphy all over it. It's a bit weird.

It's still a great place to go grab something to eat. There's a ton of restaurants down there, and they range from the extremely pricey to the fairly cheap. If I'm with a group of mixed gastrointestinal needs, I'll head to Chinatown.

Downtown

If you're visiting the Mall, do yourself a favor and DO NOT eat on the Mall. It's expensive. Walk a few blocks away.

Also recommended, for those wanting to put on fancy clothes, is The Capital Grille.

Old Town Alexandria

If, for some reason, you don't want to go to the city, take the metro down to King Street. There, you'll be in Old Town Alexandria. Lots of shopping, bars, and privately owned restaurants. This is a quieter option for those that don't want to be in the city (DC really isn't that busy, though, unless you're driving).

Food trucks

We have food trucks! Though the conference provides lunch, it can be fun to go track one of these down instead. Also, many are open for breakfast as well.

They move around from day to day, so check out Food Truck Fiestafor what's going on by us that day.

I'm ________

Paleo: Many restaurants will have some sort of meat 'n' veggies option. Retro Rays seems to be popular with the paleo crowd, though, and the buffalo at Ted's is grass-fed. And really, you can't go wrong with Fago de Chao.

Vegan: I rarely dine with vegans in DC. Most of my friends in the area are vegetarian, and that's completely different when looking for a restaurant. Happily, Urban Spoonhas a list for you! I can highly recommend 2Amys, Clydes, and Busboys and Poets.

Gluten-free: Many places around here offer gluten-free options. Urban Spoononce again comes through with a list of GF places.

Triple D likes...

These places!

I need to do this on the cheap

Every year, The Washingtonian Magazine puts together a list of Cheap Eatsfor the DC area. It's seriously worth checking out. I recommend hitting Five Guys if you can. This goes double for you West Coasters, so you can compare it to In-N-Out.

Next time!

How to Metro!

DjangoCon 2012 - Metro

The DC area has a commuter rail called the Metro. It's pretty easy to use in my opinion. Still, I've had to give enough impromptu lessons to tourists to know it's not completely intuitive.

The Map

A map of the Metro rail system

Do yourself a favor and get a copy of the Metro's rail map now, whether you store it on your phone or in your backpack. They have one of these maps on every car and in every station, but sometimes people stand in front of them.

Lines are indicated by color (that part should be intuitive). Stops are indicated by circles. If a stop appears on more than one line, then you can transfer there.

A close up of the Metro map, showing which stations are transfer points, and which are not. In spite being next to an orange line, one station is red line only because the station dot is not on the orange line.

Why are some of the stops double circles? Because they're the first or last place you can transfer to a new line. They're not the only places you can transfer. Any circle on any color (even those that run under two other lines of color) are transfer stations. For example, at L'Enfant Plaza you can transfer to orange, blue, green, and yellow lines.

One metro stop where four lines stop.

Something to keep in mind: The map is not to scale. Some stops, you can get off and walk to the next stop with no issues. Some will land you miles away from where you wanted to be. Unless you know for certain that another stop is a good alternative, do not get off of the Metro at a stop that seems to be 'close'.

Catching a train

Trains are labeled by two things: color and end destination. When you're in a station, look for the platform that matches the color of the train you're trying to get and the end destination of that line. So, if you're at the Crystal City station and you want to go to Gallery Place / Chinatown, get on the platform for the Yellow Line, on the side that ends in Fort Totten (or Greenbelt, if you happen to see a yellow train going to Greenbelt. Either works).

To find your platform, follow the signs and pillars for your line's color AND final destination. In some stations, one line has trains in both direction serviced by one track. In others, you're on completely different levels.

Once you're at your platform, there should be a sign announcing when the next train is, what color it is, what it's final destination is, and how many cars it has. Trains stop at the end of the platform (consider the opening where the train is coming from the beginning).

The stations

On the escalators: Stand on the right, walk on the left. For the love of god, please do this so you don't give some poor Washingtonian a coronary.

The stations in the city are all underground, while the ones outside of the city are a mixed bag (though mostly above ground).

Many of the stations have cell service in the stations. This drops in the tunnels for some carriers. Get your stuff done before the train moves.

There's often more than one exit for a station. Some people look at one of the posted maps to figure out which one would be best for them. Me? I take the closest one, then turn on my GPS to figure out how to get where I'm going.

Tickets and fare

Fare is determined by three things: where you are, your destination, and what time it is. Either use the trip planner tool or look at the fair list in your beginning station (the fare list is almost always right above the rail map). Fares are one-way, and you need to add a dollar each way if you're using a paper ticket.

You can get either a FastPass or a paper ticket. If you're going to do any more Metroing besides going to and from the Regean National Airport, get a FastPass. It costs five dollars (you pay ten, but it comes with five bucks on it). Also, if your FastPass doesn't have enough money to get out of the station, it'll let you go negative. With a paper pass, you simply won't get out until you up the balance.

FastPasses are magnetic cards, so just touch the FastPass card to the FastPass shield, and the gates will open. A screen should show you how much you have on your card.

Transfers are free. Screwing up and going too far is free. Metro doesn't charge you until you leave the station.

We do not run 24 hours

We are not a cool train system that runs at all hours. It was a fight to get them to stay open past eleven, trust me.

Each station closes at a different time, so they each post a sign as you go in, stating when the last train will leave that station. There might be separate times for each line.

Rush+

UGH. Why did you do this to me, Metro? Rush+ was added recently to make trips a bit shorter for people working in the city who work off of one line, but live / park on another.

Anyway, some trains only run on certain tracks and certain times. These are indicated by the dashed lines (for example, a Yellow Rush+ train runs from King Street to Franconia-Springfield).

Rush+ trains only run Monday through Friday in the morning (6:30am - 9:00am) and in the evening (3:30pm - 6:00pm).

Mobility challenged

Every station has escalators and elevators. That said, they go out of service. It's rare that I walk into a Metro station and don't see an escalator / elevator outage warning. If you can't use the stairs, keep an eye on the outages, either on WMATA's site or through one of the Metro apps.

If there is an outage, they'll usually pick another stop and offer a shuttle from that one to the stop with the outage.

Next

I think that's it for now! If you have any questions, hit me up in the comments!

Accessibility: Look at What's Out!

Overnight, my book on making accessible websites was released. It's my first book, and I'm completely over the moon. Not only do I get my own animal, but I get to put a book out on something I'm passionate about. I get to help break the paradigm that accessibility is too expensive, just about the blind, or only benefits a small portion of the population.

Even better is the timing: I'm teaching a tutorial at DjangoCon on making accessible websites. I've been super jazzed about being able to do more than drive-by conversations with people about accessibility. It's one of those topics that many people approach with skeptisism, but leave with a budding interest. This time, I'll be able to take that interest and mold it into a deeper understanding in what it means to be accessible.

We also get to improve a really bad restaurant site, which I think will be cathartic for most of us.

There's space at the tutorial, so if you're at DjangoCon or in the DC area, you can still sign up! And heck, you can say you took a class on accessibility from the woman who wrote a book on it.

Career Day

Note: I half-wrote this way back, but then had to put the blog on hiatus due to commitments that make me money. I've decided to post this now that schools are back in session, hopefully encouraging others to speak at their kids' schools this year.

I did my first career day at my son's school, and it was a blast.

I'd never done one before, because either the timing was poor, or the stinker would hide the flyer from me. This year, I intercepted it and was able to sign up, much to his horror.

The lead-up

Since I signed up for a whole day, I thought I'd be speaking for the whole day. Imagine my level of peevishness when I found out I was only signed up for three talking slots. Was I not cool enough or something? I pouted with another first-time parent (also in IT), as we waited for our turns.

The cool factor of the other parents was also a bit intimidating. We had:

Seriously? I'm going to compete with that? I comforted myself that I was at least a step above the nutritionist.

Talking to the kids

Finally, it was my turn.

We were handed a sheet of things to cover. This sheet was very important, because the kids had to write down certain things about you, like how much you can make in your career, what character traits are important, and how much education you need. You know, the boring stuff. After that, you could ad lib all you wanted.

I was tempted to ad lib all the way through, but the kids stopped me cold. "You're not reading the sheet."

"Uh..."

A girl held up her worksheet. It looked like the sheet I'd been handed, except hers was mostly blank. Fine, I read off the sheet, they filled out their paper, and they were happy. Less stressed, they actually paid closer attention to what I had to say after that.

Oh, and be good at writing and communication, I added. I figured I might as well indoctrinate some of them while I had them, because we totally need more developers who can write coherently.

I also brought up that I was way overdressed for my job, seeing as how I was in jeans and a nice sweater set. I usually wore my pajamas.

My spiel done, I brought up the code for a fairly basic number guessing game.

import random

high = 10
secret_num = random.randint(1, high)
while 1:
    guess = input("Give me a number between 1 and %s: " % high)
    if guess == secret_num:
        print "You got it!"
        break
    elif guess > secret_num:
        print "That's too high. Try again!"
    else:
        print "That's too low. Try again!"

I walked them through it, and surprisingly, the kids got it right off the bat. We messed around with it, raising the upper limit, changing the inputs, adding how many times the class had guessed. They really, really understood it. They knew what to change, and they knew what the outcome would be.

This should be a lesson to educators: You don't need to start kids off on some fake programming language like Scratch. We have something they can understand and actually use later.

I then brought up my roguelike, toyed around with it, and showed them how much more code was involved. They began to understand that you can do a lot with a little code, but sometimes you need a bunch of code to do something bigger.

Lastly, I made my Mac speak. This, hands-down, was the best trick, and it was one I didn't add until a few minutes before I started my talks. You'd think a bunch of kids who had consoles and computers at home that spouted voice acting all the time wouldn't be into this, but they were amazed. I got requests to do everyone's names, get the computer to sass me, and get it to say bits of songs that I wasn't cool enough to recognize.

Question time!

There was a time set aside for questions, and I got hammered.

What surprised me

The kids repeat questions. Over and over and over again. Not variations on a question: the same exact question. I would just repeat myself until the teacher intervened.

Every kid has a smart phone. I guess my son wasn't being dramatic when he said he was the only one without one.

I should have reviewed what kids call numbers before I went in. They don't call them integers and floats. They call them whole numbers and decimal numbers. I'd recommend asking the teacher before you go in, since this could vary by district.

One kid asked me if I knew anyone who worked on Grand Theft Auto. "That's rated M. What do you know about that series?" "Um..."

The kids were shocked that me, an old person, played as many video games as I do. They were also shocked that I knew what all the systems were, and that I would actually choose not to buy one.

They were really, really good at the number guessing game. Like, almost perfect. I've known adults who couldn't do the guessing game in the minimum amount of moves.

Not everyone presenting was a parent of a child there. Some had children who graduated long ago, others were friends of a teacher. Some had approached the school and had a standing agreement. I had always assumed that the adults at career day were parents to a child there, but most weren't.

So, should you do a career day?

You absolutely should. I was up against a SWAT team and a vet, and I ended up being one of the more popular presentations. It takes barely any prep: I wrote the number guessing game while I was waiting. They already had the format they needed, and it wasn't anything I had to think terribly hard about.

Getting kids excited about programming today means that one day, you're more likely to have competent juniors. If you plan on being in the workforce eleven years from now, the fifth grader you talk to today is your new hire in the future.

Katie Writes Again!

A few months ago, I signed a contract to write a book in the Teach Yourself X in 24 Hours series. Pearson was looking to do a complete re-write of the one they already had on Python, and my name had ended up on the list.

When I tell people I'm writing a Teach Yourself book, I get one of two reactions:

Two sketches of Katie. One is delighted, captioned "Omigosh yay!" The other looks slightly pained, and is captioned "Um... Sorry?"

Let's talk about that.

Why I'm writing a book for beginners

Personally, I think Python is an excellent language for people who want to learn programming. Why?

Why I'm writing a Teach Yourself book

For starters, they asked.

Secondly, I don't have a huge beef with beginners series. I'm one of those rare developers that actually likes Head First books, and yes, I have owned several Dummies guides (one for photography, and the other for writing romance novels [1]). Hell, I own Manga guides! I think it's important for any field to have something that helps put the novice on track without talking down to them or scaring them off.

Yes, I know there's several free books out there for learning Python. One problem: they're on-line. Even the novice who knows how to google properly may not have the vocabulary to find these books. Many beginners, if they don't have access to someone who already codes, are going to go to the bookstore or library and look for a book.

Many 'Beginning' books are deceptive: They're not always for the beginner in programming. They're often for the person who's decided to learn that language who already has some programming under their belt. With tech books not being cheap, it can be an expensive gamble to see where this book is aiming. Say what you will about beginner series: they leave no doubt as to where they're going to start you: the absolute bottom rung.

It's impossible to know which beginner series a beginner is going to gravitate towards. Some like the Dummies guides, while others like the Head First books. I think we should have an excellent Python book in each of them, and the one in the 24 hours series needed to be redone. So, I'm doing it.

What was this proposal like?

This proposal was completely different than the Accessibility Handbook's proposal. That one, I had no predefined format, so it was completely up to me how I organized my material. If anything, my novel organization helped sell the book to higher-ups.

With the Teach Yourself book, I still had some freedom, but there were definitely some constraints: There had to be 24 chapters. Each chapter had to have certain sections, like quizzes or a FAQ. Each chapter had to be doable in an hour. I had to open and close with a list of what the reader was going to learn / what had been learned. There were a few optional sections, but if I included that section for one chapter, it had to be in all the chapters.

Seriously, their style guide is thirty-five pages long. It's crazy.

Also, they had someone tech-read my outline, and I'm glad they did. It ended up a ton better for the second pair of eyes (Thanks, Doug!).

So, where am I?

I've gathered my tech and test readers, and I've submitted chapters one through ten. Eleven through thirteen are written, and chapter fourteen is being a big ol' pain in my ass. It will either be the chapter that everyone loves, or the chapter that no one actually reads.

When's it coming out?

Uh, next year? Sometime?

I plan on handing in the manuscript in January or February, but that doesn't mean I'll be completely done. I'll still have edits to do, and books take a long time to get from the author's desk to the public. They take a lot of time, making sure you don't look like a complete moron, re-doing your images, double checking your resources, and basically making you sweat bullets for weeks and weeks and weeks.


[1] Note, I have no actual interest in writing romance novels. But Writing a Romance Novel For Dummies was one of the most realistic books ever I've ever read about being an author. While other books tried to nurture your delicate soul, that one flat out told you to network and market yourself, and to do what your editor says.

PyLadies Workshop Wrap-Up

A chilly weekend in October, PyLadies descended upon the American University campus to teach a class for women on programming and Python.

Workshop numbers

We had thirty-five slots (five were for AU students, and the rest were for us). Every one of the Meet-up slots was filled, and we had a small waiting list.

A few days before the workshop, we asked people to drop if they realized they couldn't make it. Some did, so everyone on the wait-list ended up getting in.

We ended up having twenty-four students, and this was a very good thing. Any more, and we would have had students without desks. As it was, our volunteers had to sit against the wall.

A panoramic of the PyLadies workshop, from the teacher's perspective.

Materials

With our first class, we used the MIT materials given to us by other PyLadies chapter. This time, though, we wanted to try doing something on our own. I had some different ideas about teaching order, so we scrapped those materials and started fresh.

Well, 'fresh.' To be honest, I was using the order from a book I'm writing. I wanted to give it a dry run on some actual humans before sending the book off for publication. My theory, when putting together the table of contents for the book, was that going back and forth between data types and functionality offered more chances for reinforcement than diving into data types first, then functionality.

I also decided to use IDLE, since getting paths set up is a pain in the ass, when you're talking 20 windows machines. While I still think IDLE is a great teaching tool, having some people on older versions proved an issue. I may turn set-up idle time into 'learn how to get around your computer' time, since many students don't know how to open a terminal or change directories.

Sign-up

This time, we did sign-up through the DC PyLadies meet-up rather than through the much larger DC Python group. This meant our class was filled within days, rather than hours, and our wait-list was much smaller (last time, if memory serves, our wait-list was three times the number of spots).

We kept the number of slots the same (30), but added a new rule: The class was for women and their friends. Basically, men could attend as long as they came as the guest of a woman.

Why did we do this? Because last time, the waiting list was almost completely male. This made me wonder if the fact that our class was mostly women was only due to chance. Had we posted at a slightly different time, would the class have been mostly guys?

This isn't the case for every PyLadies chapter (or group that's set up for women and programming). Some have never run into this issue. Some have had huge issues with it, and have had to put this rule in place for even their casual meet-ups. We added this clause because I'm not sure yet where we fall on the spectrum.

In our case, only two men signed up, so we didn't have to have any awkward conversations. Huzzah!

Also, we didn't charge for the class. I don't believe in nominal fees. No, not because I'm completely selfless; I don't believe in them because they tend to have the opposite of the intended effect. When people shell out a few bucks for a seat, they feel more justified in not attending. You've basically given them a way to pay off their guilt. If you don't let them do this, then they're more likely to get out of bed early and get to class.

Volunteer meeting

A few days before the class, we had a meeting with the volunteers, which I highly recommend to anyone running an Intro to Python class. We covered the syllabus, schedule, and some ground rules:

Day One

Setting-up

I don't know what the hell happened in DC on Saturday, but traffic was horrific. Like, sit in one spot for 30 minutes horrific. I'm a hardened native who thinks anyone who whines about an hour long commute is a wuss, but even I was yelling 'Oh, come ON!' over my steering wheel. I'd planned on being there an hour early, and was there an hour late.

Happily, everyone else where there on time. The first hour was dedicated to setting everyone up, so all I missed was a bunch of cursing at Windows machines.

I recommend setting up in person or over a screen share, because instructions can easily go off the rails. One click installers often aren't. Sometimes students grab the wrong version, or installed a version that was current a while back. Sometimes, they grab Python 3 no matter how many times you say "Get 2.7" because they've been trained to get the latest version of whatever they need.

You also do not want a poor Windows user screwing up their path while they try to get Python to run on the command line.

Wifi was problematic due to some duplicate logins. Some people stayed on fine. Others kept knocking each other off. We weren't doing much with the Internet, but I still recommend bringing in installers on flash drives. I also recommend making sure that the students have wifi before anyone else.

Let's get started!

Once everyone was set-up, I realized we still had a while before lunch, so I went ahead and started covering some introductions and basic concepts.

I talked a bit about who we are (most of the attendees had never been to a PyLadies meeting), gave an overview of the class, and talked about all the stuff that Python can do, and is currently doing. We had a mix of students, from a high school student to people into scientific computing, as well as a hardened Java and PHP developer. I tried to hit on as wide a range of applications I could. I wanted everyone to recognize at least one thing I was tossing out there.

At that point, I pulled up IDLE and started going over some basics.

At this point, food was ready, so we ate and helped anyone who was still having technical issues.

After lunch, we moved into our next topics.

By this time, it was three o'clock, so we introduced the idea of student projects. We asked them to go home and think about what in their life could be automated, or what they were curious about. Sunday afternoon, we'd break into groups and help them realize at least a part of their project.

I then went home and drank.

Day Two

There was a marathon scheduled in DC, so I emailed students, warning them to check their route. I struck out early and managed to get there insanely early.

Note: Nothing is open on the AU campus on Sunday.

I didn't notice any attrition on our second day, which delighted me. Everyone showed up, we cleared up a few technical issues, did a quick review of what we'd gone over the day before, then launched into more Python.

More Python!

We were only teaching that morning, but we crammed quite a bit in.

Brains full, we had lunch, then moved on to student projects.

Student projects

Previously, we had the entire class do a Twitter project. This time, though, we decided to let the students think of their own projects. We broke them up into groups that had roughly the same idea. There was a web group (including a woman who got Django running the night of the first class!), a PyGame group, some OS people, and... other groups. I'm kicking myself that I didn't make a note of all the groups that we had.

Volunteers and teachers moved amongst the groups, helping students focus a big idea into something more manageable. It was amazing watching students work through the logic of a program, then light up when their code started doing what they expected.

The next level?

One request we got from many students was a second class, covering the next level of coding. We didn't get to object oriented programming (because come on, people! We only had two days!), and we were barely got to cover imports or installing other libraries.

I'm putting together materials for a 'Intro to Python II' class now, and trying to figure out a way to screen for people that may be better off in the Intro I class. Ask them to write a bit of code? Quick test? Ask that they finish the Codecademy Python section before they come in? I don't want to put people off, but our time and the seats available are highly limited. I'd rather not have a class full of people who assume that they'll catch up quickly.

Releasing materials

I was asked during the class whether I would be releasing the materials for others to use, and the answer is 'yes.' I'm just considering licensing. Creative commons makes the most sense, so I've been reading over all the variations. After talking with some people who've used CC licensing, I think I'm going to go with CC-NC-SA. So, people are free to use the materials (with attribution), and alter them (as long as they share alike), but any commercial uses will require a waiver.

I may strip off the NC part, but it was pointed out that, once I do that, I can't undo it. I'm not against people charging for a class, but I want err on the side of caution for now.

Last but not least...

A huge thanks to my co-organizer, Jackie Kazil, who made the food and the space happen, DC Python for lending us their non-profit status, all of our wonderful volunteers who made it possible for me to teach without having to debug someone's computer every two minutes, Social Code for the sponsorship, and American University for the space.

Looking back, looking forward

Around this time of year, I like to look back at the past year, and look ahead to what I have planned for the next. It's less of a 'resolutions' post (I suck at keeping them), and more of a way to take stock of where I am and where I'm going.

2012 was crazy busy. I'm actually having trouble remembering all of the things that I did during 2012. It was like 2012 was the year of me finding ways to drive myself crazy.

I decided that it was a super idea to submit a poster, a talk, and a tutorial to PyCon. It was not a super idea. I looked like I was about to fall over by the end of the conference.

I got married. It was a small wedding. I thought this would make it effortless. Ha! Hahahah. Totally worth it, though. Renting a limo to barhop around DC rather than renting a hall and feeding 150 people crappy chicken? Best idea ever!

The book I signed a contract for in 2010 was released, and I officially became a published author. Right before this, though, was a month or so of mental anguish where I convinced myself I was a hack, and had no business anywhere near a book contract.

Not having learned my lesson the first time, I signed a contract for another book. This one is due out in 2013.

I taught classes. I taught so many classes. Two PyLadies intro classes. Part of a tutorial on PyGame. Then a class at DjangoCon, which, hilariously enough, I discovered the day of my class that my time-slot was an hour shorter than I thought it would be. Amazingly, I still made it all fit.

For the first time, one of my blog posts went viral. Then another one did.

I got my first consulting gig, and because of that, had to make a mad dash to create an LLC so it's harder to sue the pants off of me. Many thanks to #django-social for hand-holding me through that.

The boy started middle school. He's fully mainstreamed, now, so this has been our first year of walking the tightrope without a net. It's been... interesting.

The girl started preschool. She's very nearly civilized.

So, what's up for next year?

My second book will be published! My editor will personally strangle me if it isn't, so consider me highly motivated!

I have two secret projects that don't have any signed paper yet, so I can't do any more than drop less-than-subtle clues. Hint: it may involve more books!

The girl will start Kindergarten. If I don't sell the boy as punishment for not turning his classwork in, he will start seventh grade.

I want to get better with my jQuery and Javascript skills, so I'm thinking of projects that involve them. I'm seriously considering an interactive fiction engine.

I really want to finish my roguelike. When I get a chance to chill for a few hours, I'll pick it back up, probably starting with writing some tests for it. I already have some ideas for the next game I want to write (see above).

Okay, I have to go prep for a party in which I alienate most of my friends via Cards Against Humanity. Here's to an excellent 2013!

PyCon: Mani Party!

I've been talking about this on Twitter for a while, but I figure I'd make some sort of official announcement: I'm hosting a mani/pedi BOF at PyCon! What started as a cute joke snowballed into an actual thing, as many of the best things often do!

This will be a DIY mani/pedi party. I do not have the head-space / time to find a manicurist willing to come set up shop at the conference. Maybe in Montreal!

When and where

Friday, March 15th, from 5-6 (and possibly rolling later, but I wanted to start before dinner time)
Room 202

Do I need to sign up?

Nope! Feel free to come on by! Just a warning though... You come into the room, you are leaving with painted nails. It may be clear coat, but there will be nail painting.

What is Katie bringing?

A poker case, filled with nail polsihes.

Source: Uploaded by user via Katie on Pinterest

I'm a bit of a nail polish hoarder, so I'll be bringing a chunk of my collection with me. I may do a few last minute substitutions, so I won't be posting a list until right before I leave. I have a few polishes making their way to me that may end up getting traded in.

Rest assured, though, I'll have a nice spread of colors, from office friendly to freak flag, and a selection of top and base coats. I'll also have nail polish remover, files, nail clippers, orange sticks, and cotton balls.

Finally, for those that worry about this sort of stuff, all but one polish that I'm brining is at least 3-Free, and all are cruelty-free.

Can I bring my own stuff?

Totally! Please do! You might want to mark it in some way. I'll bring a few Sharpies and a thing of tape to help with that.

I don't have anything to bring...

Did you see my case of colors? You don't need to bring anything but your hands.

Lastly

This is a dry event. Lacquer and liquor, people. It doesn't mix.

PyCon 2013: Young Coders

This year at PyCon, I had the honor of co-teaching the very first workshop just for children at PyCon. Barbara Shaurette and I taught approximately 35 kids from eight to eighteen the basics of programming Python.

The class was offered for free to all children, and all kids got to take home a Raspberry Pi at the end of the day. The students came from attendees and the local community. Some parents were Python developers, and some had no idea what programming was. They just knew this was a chance to enrich their children's lives.

What did we teach?

Barbara took her materials that she uses for her class and, with minimal tweaks, converted them to a kids' class (I'm lazy, and have no slides for my classes). We tweaked the lesson plan for the second day when we realized that some things were confusing to the kids (or to the teacher who couldn't remember how slices work).

This is roughly what we taught:

With the first class, we had to take lots of breaks. Every other module, we'd let the kids get up and move around or run to the bathroom. The second class, where the kids were older, we didn't need as many breaks (in fact, they often refused breaks), so we got through way more material. In fact, we ran out, so I ended up live coding a few more examples and running through some code Barbara had written for another class.

Barbara and I swapped back and forth as needed. Sometimes, one teacher needed a break. Other times, the other teacher knew more about that module so wanted to take point.

Volunteers

I'll confess, I was worried when I saw how many volunteers were signed up to help us. Would it be too confusing? Would we have too many chefs in the kitchen? I didn't want to tell anyone that they couldn't come, though, just in case some people couldn't make it (or forgot that they had signed up).

I asked the volunteers to stand or sit at the back of the room and watch the kids' monitors rather than the slides. My reasoning? If they're focusing on what Barbara or I were saying, they were missing what was going on with the students. The one thing we couldn't see was the kids' monitors, so we had no idea if they were lost, if their Pi was throwing errors, or if they were done typing an example. I also needed them to grab the 'off' teacher to tell us if we needed to go over something again, or if they'd noticed something particularly interesting that a student had done.

One thing I made sure to ask was that they not interrupt the class, even if they noticed an error. Having two teachers, though, meant that there was always someone they could grab and talk to quietly.

We ended up having around a 1:3 ratio, and it was perfect. Volunteers were able to jump in if a student was floundering, and many developed a relationship with a few select kids over the course of eight hours. The volunteers were also an invaluable asset to me, letting me know with nods and hand signals if I could move on, or if I needed to chill out for a bit.

Tools

We used IDLE because it's already on Raspian's desktop. Personally, I like IDLE as a teaching tool. It's included in the standard library, it does tab completion and color coding, and it even has a text editor included so you don't have to start your class off by teaching everyone about paths.

Too bad it's broke as hell.

I believe my first contribution to the Python Standard Library will be fixes to IDLE. I really do like it that much. Happily, the kids were flexible. If they needed to do a workaround, or ignore something on our slides (they were written with the standard shell in mind), they did so. They were total champs. My adult students would have been much more upset.

So... what was it like?

It was amazing. It was exhausting. I wanted to do it again. I wanted a drink.

The kids were fabulous (I only had to use Mom Voice once). They stayed on task in a way I've never seen. Hell, at work, if we've been coding for a whole hour we start whining that we need a Starbucks run. They had a network connection and never once did I see Facebook open. They had a link to Python games as well, and only opened them during breaks.

People kept stopping by and peeking in, then giving us little cheers at the sight of rows of kids with their 'coder faces' on. I tried to impress on the kids that they were the stars of PyCon. I'm not quite sure they bought it, but it's true. Everyone was so excited about the classes. I got stopped at least once an hour by someone who wanted to know about it, or how they could do it themselves.

We even had a group of programmers from Mexico approach Jesse and offer to translate our slides so that they could teach the classes in their home country. I believe we're even finding a way to send them Pis so they can copy our set-up exactly.

Lessons learned

How are my son's teachers not alcoholics?

The biggest lesson learned? Two days of teaching, even shared between two teachers, is probably too much. Barbara and I were about to collapse by the end of it. When you teach an eight-hour class, you have to be 'on' for the entire time. Even when we weren't actively teaching, we were looking at the next module, walking the room, talking to a volunteer, or helping a student. Even our break times were spent going over what we needed to change in the slides.

Next time? Two different teachers every day. The optimal pair would be two people who know each other well and can pass the ball back and forth with a minimum amount of effort. Barbara and I have known each other for years, so this was was easy for us. It would have been harder if it was someone I didn't know as well.

Mavis Beacon they aren't

With the exception of a few of the teenagers, the kids were not fast typists. When I was live-coding, some tried to follow along, word for word. As I raced ahead, they became discouraged. We also had long variable names in the examples, which took forever for the kids to type in (and they wanted to type in every code sample).

Oh man, objects...

We were able to get to classes, but it turned out to be something that's a bit difficult to explain. I'm still coming up with a story that will make objects easy for kids to grasp. I don't want to ignore them because many of the kids want to make a game, and that requires understanding what an object is.

Wait, they need to eat?!

We forgot to feed the kids snacks at snack-time. Barbara and I both face-palmed, especially since snacks were provided for tutorial attendees.

The future

I want this class to happen again. I want to see it done at PyCons, big and small. I'd love to see them done in smaller communities, and I even have a few leads back home. People offering to translate our slides has me jumping with joy.

I have ideas for adding to the class, and I'd love to see what other people might add to the material.

I will totally be doing this again and again.

PyCon 2013: Mani Party Wrap-up!

It all started off as a joke.

I've decided I'm bringing all my nail polishes to PyCon so we can have a mani party.

— Katie Cunningham (@kcunning) November 24, 2012

Flush with my new obsession, nail polish, I told everyone on Twitter that I was totally going to bring some polish along so we could all do our nails at PyCon. I wasn't really going to do it. Who would come? It's not like PyCon was a fashion show. We had to give people explicit instructions to take showers. This is not a mani/pedi crowd.

People responded, though. Yes! Please! Bring some polishes! And yes, people. About half of the people on Twitter asking me to go ahead and do it were men. Apparently, more of my guy friends like nail polish than I thought. Hell, I was surprised most of the women liked nail polish. At conferences, I almost always saw bare, stubby nails.

So... I started planning. I took my ever-patient husband's poker set and turned it into a travel case. I picked out colors. I put them back and picked out completely new colors. I got more nail polish in and had to swap again. I got a BOF room reserved. I blogged about it.

I was sure that no one would show up.

I was wrong.

The BOF

Approximately 20 people seated around a long table, all doing their nails and talking.

I didn't count how many people showed up. My guess is that it was somewhere over twenty, but I was spending most of my time finding bottles and being very, very excited.

A manicurist setting up her station

At the last minute, I got contacted by a sponsor. A sponsor! StyleSeat wanted to bring a professional nail artist so this didn't have to be a completely DIY affair.

Doug Napoleone supplied us with the cutest monkey nail blow dryers I've ever seen. We had a few of these left, so gave them away at the PyLadies booth. A Survey Monkey booth guy was enamored with the last one, so I'm wondering if we'll have those sponsored next year as well.

A woman drying her nails with a tiny monkey nail dryer.

I had planned for the event to last an hour, but I forgot that not everyone is a twitchy freak who does theirs every day (like me). I can do my nails in ten minutes, but most people are going to take way longer. We also had a fairly steady stream of people coming in and out of the room.

We also had a good number of men show up. I had posted a warning, that coming in meant you were getting your nails done, but they all seemed to come in with an idea of exactly what color they were looking for. I don't think I saw a single clear coat leave the room.

A thumb with the Python logo on it.

In fact, I am now insanely jealous of the infamous Gregory P. Smith. He came in and declared that he was going to do the Python logo on his nails. Pfft, I thought, I've been trying to do that for months. Good luck, dude. What did he do? A perfect Python logo. Okay, fine. I'm going to assume skills like that are why he works at Google.

Next year

I would love to do this again next year. Heck, I might already have a sponsor lined up. I will do a few things differently:

Other than that, I was shocked at how well it turned out. I'm looking forward to doing this again next year!

Teach Yourself Python: Last chapter submitted

Yesterday, I submitted the final hour to Teach Yourself Python in 24 Hours.

I could not wipe the smile off of my face.

This high is why I write. This is why I punish myself for months at a time.

...

Is there a rehab for writers?

Young Coders at PyOhio

This past July, I got a chance to not only do my first smaller regional conference, but also to do Young Coders again!

The numbers

We had 25 kids, from 12 to 17. From what I heard, the class list filled up within half-hour, and the waitlist was three times as long as the available slots. Some students were children of conference attendees, but several came from outside of the Python community altogether.

Sign-up didn’t open until two weeks before the class (I know this was probably killing PyOhio organizers. I know how persistent people can be!). This helped keep the no-show rate down to one student. There were a half-dozen kids outside the door vying for that extra seat, so I’m glad that we had the waitlist on hand to tell who had signed up first!

The setup

We went with the Raspberry Pi set-up again, due to a generous grant. This required getting and setting up 25 Raspberry Pis, which included lugging around monitors, keyboards, mice, and power supplies. If you want to do this yourself, make sure you have a few dollies and some strong backs to help!

We got into the room the day before. It took several hours, and honestly, when they booted us out, I would have gladly taken one more hour to get everything tidied up.

Because of the limited amount of time, we stuck to checking the important stuff:

I decided to skip networking. One, we didn’t have the Wifi adapters for the RPis, two, we didn’t have cable for the hard connection, and three, I didn’t have Noah on hand.

We had a bunch of people on hand to help, and we needed every single one of them. We barely got everything checked before the University told us we had to get out (in the nicest way possible, though).

The class

The class itself was amazing. The kids were completely attentive, loved to participate, and picked up concepts quickly. I knew that we were going to zoom through slides when they immediately knew start with “Open the bag of bread, then remove to slices” during the PB+J demo.

One amusing hiccup occurred when we realized that the food court in the building was closed for renovations. I called the building help desk in a panic.

“Oh, the tavern is open!”

“The... tavern?”

“Yeah! They can eat there.”

Awesome. Come on kids! Let’s go to the bar!

Happily, the tavern was just a slightly swankier place to get food, and not really a bar. The students could get food, and I wasn’t breaking any state laws by bringing minors into a dive.

Because we finished the slides early, and we raced through the game demos I had, we ended up hacking on the games included on the RPi. The kids had been playing them during breaks, so they already had an idea of what they wanted to do.

We started off simple, changing text, then colors. We then played around with making the games harder or easier by increasing enemies, making the board smaller or larger, or making things move faster or slower. Best part? I suck at these games, so the kids got to watch me die over and over and over.

At the end of the class, I made sure that everyone knew how to disassemble and reassemble their Pis, and that they knew how to log back in. My fabulous volunteers had made a cheat sheet (now in the repo!) and got it printed, so we handed that out as well.

Topics covered

We covered (in the order I’m remembering, not the order taught):

A shadow!

Our goal with Young Coders is not to run me and Barbara into the ground, running around to conferences. We’d prefer it if these could be run by locals, not only to cut down on costs, but so that the students have a local resource to go to when they have questions.

I got such a shadow at PyOhio! Bill Triest, who works at the University, took the time to go over the slides and chat with me about what goes into running a YC class. He also watched the class, so he got to see how much of what Barbara and I talk about looks like in practice.

If you decide you want to do a Young Coder’s class in your area, you should most certainly look into getting a shadow if you have to import a teacher.

Next year

Will I be doing this again? I certainly hope so! Not only did I love doing the class, but the rest of PyOhio was awesome as well. The talks (which, admittedly, I’ve been watching mostly on YouTube) have been superb. There’s a class on Kivy that I’m eying for my next free Sunday, where people were watching from the hallway.

Beginners: What now?

Recently, Pearson/InformIT reached out to its authors and asked us what advice we give to beginning developers. Here's my contribution!


Several times a year, I teach groups of people how to program in Python. These are absolute beginners. Many times, the first time they opened up their terminal or command prompt was that morning.

By the end of the class, they’ve learned enough to make a fairly robust program. They can save data, use files, make calculations, loop, and test for conditions. They know how to read error messages, and they’ve played with imports and libraries. I get one question, every time:

“Now what?”

At first, I told them to think of a cool project. Surely, they must know of one! No, everything neat they could think of had been done already. Or, they had an idea, but it was too big. They’d have to learn 3D animation, or advanced calculus, or a tricky API. Finally, I remembered the first project I had really undertaken on my own. It was a loot tracker for a game. My friends and I wasted a ton of time keeping track of who got what spoils when we played together in an online game. I ended up making a simple app to keep track of possible loot, who could use it, and who had gotten the last cool item.

It didn’t make me millions. No one ever used it outside of our little group. But it taught me more than most classes could. I learned how to use databases and secure them. I learned about linking up forms and organizing my code. I learned how to maintain my server, and I learned how to upgrade my system without breaking my app.

The annoyances in our lives, as much as they make us grit our teeth, are also the best places to start. The fact that they exist means there is a void in the existing technology. Perhaps the bit of code hasn’t been written, or it’s prohibitively expensive. Perhaps it’s too clunky, or terribly behind the times. Whatever the case, it represents a place the student can start their journey, and create something that makes their life, and perhaps the lives of others, better.


Here are the rest of the responses, from all over the various tech communities! They should be adding to the list all month.

Teaching: The OS Divide

I spend much of my time in the OS community teaching. I teach kids, I teach beginners, I introduce experts to new concepts. At nearly every conference, I will at least attempt to teach a class. I love teaching, and I also try to talk others into teaching as well.

There’s one problem I’ve noticed with those that are interested in teaching, and it’s something that’s not really discussed openly. Normally, people make a single snarky comment, then walk away. The topic?

Windows.

Why is this a problem?

When I’ve taught classes, I’ve almost always taught to a mixed crowd of Macs, Windows laptops, and the odd Linux machine. The more novice the class, the more likely it is that I’m going to be dealing with Windows people.

People in beginner classes expect you, the teacher, to know how to deal with their hardware. Yet, when I bring up the Windows problem, people scoff. The most common answer?

“They should get a better laptop.”

I’ve endured tirades about how they should just install Ubuntu, or how a Mac is really just as cheap as a Windows machine. I get to hear about how many years it’s been since they touched a Windows machine. I’ve heard so many stories about how people moved their loved ones onto Ubuntu or Mint or a Mac and that they loved it and everyone else should just do the same.

It’s a problem because it’s snobbery, and it doesn’t belong in education. Why do people buy Windows? There are many reasons why people might own a Windows machine. Maybe they like gaming. I know that’s why my husband and I keep Windows laptops around. They may not have a choice, since that’s what their work gave them (not all of us can demand MacBook Pros). Maybe the suite they work with only works on Windows. They might be like my parents, and scared to learn a new OS.

Or, maybe the decision is economic.

The cheapest Mac starts at a grand. I can easily find laptops for less than that. Sure, they may not have the screen or build or battery life, but that’s not the deciding factor for a large part of the population.

When money is unstable, it can be hard to save money. Savings are pecked away at by daily life and a host of things that cannot be cut. When you’re in this situation, you tend to get chunks of cash in on an irregular basis. You get a Christmas bonus, someone dies and leaves you a bit of cash, or your tax return clears.

Everyone I know who lives at the lower end of the middle-class spectrum or lower has a death or taxes laptop.

If you only get these bits of income in every now and then, you try to make them count. You get things replaced. You pay off a large debt. You buy big ticket items. A laptop is a common choice, but you want the most for your money. You won’t be upgrading in two years. You may be using this laptop for the next eight. So you go for the most memory, space, and video that you can get for that little pile of cash you have.

And if you want that, you’re going to get a Windows machine.

We can shout all we want about stability, or how you still have a MacBook from 2002 that works fine, or how you know of an indie seller that has cheap Linux laptops. At the end of the day, the Windows line is going to win. They are not the enemy At this point, the people I talk to often get mad at the users. They’re blind! Sheep! Technophobes!

The users are not the enemy.

Sure, we’ve all had our infuriating experiences with Windows. Some of us have watched the political wars, both from afar and in the trenches, and we have bad tastes in our mouths. We’ve been held hostage at parties to an infected install of XP that we’ve been asked to clean up, while everyone else takes in holiday cheer. We’ve realized that our children’s school system has been locked into a contract that only exposes the kids to Windows. I get it. But you’re getting mad at the wrong people.

These people did not buy the OS they did in order to make you mad. Really. They did it because it was the cheapest option, or the one they were the most familiar with, or they had specific needs with regards to their OS, and they weren’t confident enough to research alternatives on MacOS or Linux. So stop being mean to them by being snarky or ignoring them.

But if they would just try…

There’s probably something in your life that you use, but you’re not 100% knowledgeable about. How would you feel if someone ran in and, without explaining themselves or gaining your trust, told you to get something wildly different and foreign? For me, I felt this way the first time I walked into a bike shop. I was interested in getting a bike to tool around town. I had grown up with this sort of bike:

A simple Huffy bike. No fills!

It’s basic. One gear, break by pedaling backwards. The bars were at a certain angle, one you could grab while leaning over or sitting up straight. Sitting up was common, because who doesn’t want to pretend to be Julie Andrews now and then? You got one kind of tire. The one choice you got was color.

Julie Andrews on a bike

Then, I went into a bike shop. None of the bikes looked like that. They pointed me towards something like this thing:

A complex looking bike. Wires, tiny seat, weird angles.

Wait, there’s wires everywhere! And the handlebars are so tiny… And what’s with the seat and handle angle? My arms would need to be four feet long to sit up! Wait, the brakes are where? Why can’t I just pedal backwards? And that seat! That’s not comfortable looking at all. Wait, why are you asking me about tire choices WHY IS THIS EVEN A THING?

Whenever I feel the desire to rush a Windows person to a new OS, I remember the feeling of being slightly panicked and wondering if this salesperson was just trying to con me into a bike that wouldn’t work for me, and I back off. I earn their trust by showing that I do know how to deal with Windows, and that I can get Python working on their system. Then, once I have their trust, I start to explore whether they might be interested in checking out another OS.

And note that I say might. They might never move over. That’s okay, because that’s their choice.

What do you need to know how to do?

If you haven’t run away at this point, good! Let’s talk about what you need to know in order to work on a Windows machine.

You’ll need to know how to get the command prompt up. In general, all you need to do is hit the Windows key and type ‘CMD’ + Enter.

You need to know how to figure out what version of Windows you’re on. Don’t just ask the student. This is a chance for you to prove that you’re not going to break their machine. Pull up the command prompt and type in ‘ver’ or ‘systeminfo’. At least one of those should work.

You need to know how to install Python on Windows, both two and three. This is not the time to be Ultimate Nerd. Use the installer. You might even want to trust the defaults, though I know you’ll probably look, anyway. If you stick with defaults, the student will have an easier time debugging down the road.

There’s a good chance you’ll need to update the system path. If you pull up the command prompt after installing Python and get an error when you type ‘python’, your path needs help. This is slightly different for each system, so some googling might be in order.

If you’re teaching something other than just Python, you need to make sure that you know how to install THAT package as well. Oh, and while you’re at it? It’s a good idea to get pip and easy_install working as well.

Finally, you should get familiar with some sort of editing program for Windows. I generally show off IDLE first (since it’s great for beginners), but it can fall short when you’re trying to work in a project that has items other than Python files. I like Notepad++. It’s free. It’s updated pretty often. It looks fine. It’s not crazypants like Eclipse.

And that’s it. That’s not a huge list, all told. But just knowing how to do those things can open you up to a much wider community of learners, and give you a chance, after you get friendly, to maybe move them over to your OS of choice.

Your wiki is a dump

I freaking hate wikis.

Note that I’m not talking about things like Wikipedia or Bulbapedia (my two favorite wikis). Those are maintained by fanatics and are generally up to date. As long as you’re not on a controversial article, you’re probably safe to show it to someone to prove that they’re wrong about something.

But wikis need fanatics. They need people that will get het up over proper format for an article, or point out where you need a citation. They need people who are passionate about a certain topic who will haunt their pages and immediately check any change.

You know where we have wikis without fanatics? Our workplaces.

These commons are tragic

I’ve yet to hear of a tech company that operates without some sort of wiki. Maybe they’ve called it something fancier, like an enterprise dynamic document management system, but it’s still a wiki. Everyone can run around editing stuff. It doesn’t matter if you wanted to call it something more business-like. At the end of the day, you have yourself a wiki.

But corporate wikis are like the company fridge. Eventually, they get nasty. You get to the point where you know when someone opened the fridge, because you can smell it down the hall. There are terse emails and passive-aggressive notes, but none of that cleans the fridge. Why?

Because it is everyone’s responsibility, and therefore no one’s responsibility.

And that’s one of the features of wikis that drive me insane. Everyone can edit and maintain pages, so therefore, no one does. The health of the wiki is rent asunder because everyone assumes someone else will take care of the accumulating garbage.

What does a healthy wiki look like?

There are those that insist that wikis will always be a dump, as if this were a feature of wikis themselves. That’s not true. Wikis with fanatics tend to be fairly healthy. They’re more like a fridge with a few suspicious delivery boxes than a full-on disaster area.

So, what are the features of a healthy wiki?

Bad habits

So how do we get into this mess where our wikis are completely useless? Well, we have some bad habits.

You don’t need to keep everything forever.

You really, really don’t. I swear. No one will care, two years from now, how we had search set up on those test environments that we ended up taking down and never using again.

I know that one of the features of wikis is keeping tons of information in one place, but you know what another feature of wikis is? Page history. If someone really needs to know about that (and you’re following better practices), they can find it.

No one makes sure that what’s there is actually accurate.

I’ve run into this so many times. I find the instructions on how to do something on the wiki. I try them out. They don’t work. I have never had instructions on a corporate wiki work the first time. I usually have to grab someone to figure out what in our crazy set-up is breaking. That’s when I find out that the instructions would never have worked. They either skipped steps, made assumptions, or were completely out of date.

Why was it out of date? Who knows! People either had their own instructions for setting up this feature, had gotten used to the missing steps, or had figured out how to do their job without it. No one passing by ever fixed the instructions, probably because we were so relieved just to have the feature working finally.

You put everything in the wiki. EVERYTHING.

Nothing drives me more mad than looking for the documentation on how Widget X works, only to find nothing but meeting notes about Widget X. Maybe, if I’m lucky, I find the actual documentation about Widget X on the third page of results.

There are so many things that do not belong on the wiki. They clutter search results. They make sections difficult to navigate. They give you more crap to maintain. Your wiki is a dump because you treat it like one!

You let too many people into the wiki.

Many companies have only one wiki. So useful! Everyone can search everyone else’s stuff and find answers across divisions! Oh, and we only have to buy one copy / keep one installation updated.

But this leads to organizational issues. One group may use the wiki as a dumping ground for meeting notes. Another may use it to document how their system works. Yet another might use it to discuss what they might want to include in their architecture. And then there’s that one group that puts cookie recipies up.

Also, the more people you have, the more diffuse the blame. If there are five wiki users (and no fanatics), there’s not that much blame to go around. Everyone has to pitch in. If there’s a hundred? No one is at fault because everyone is microscopically at fault!

You only have one wiki

People. You’ll buy everyone at your company MacBook Pros and Cinema Display monitors, and you won’t stand up one more wiki? Really?

There are free ones and they can be run off a thumb drive. I’m sure you can find the money and hardware somewhere.

Good habits

So what are the good habits of wiki maintenance? Sure, it would be nice to get some fanatics in here to take care of this for us, but let’s be real. It’s a corporate wiki. We’re going to care more about who’s turn it is to bring in donuts, and what they’re stocking in the soda machine.

Break off into groups

The most effective wiki set-up I ever was immersed in was one where we broke off into groups. Design had their group. The Python people had theirs. Java had their own as well.

We could all check out each others wikis, but for the most part, we worked in our own. This kept the numbers down on each wiki. I believe each one had fewer than a dozen people who were active participants. The one I worked on daily had five people. This meant that there was tons of blame to go around if things went wrong, so we tended to hold our edits to a higher standard.

Decide what goes into the wiki

Maybe a group really does need to keep track of meeting notes forever. That’s fine! That’s a use case for that group, and they can feel free to put those notes in their wiki. Maybe they even have a wiki just for meeting notes.

My group tended to want things such as install instructions and notes on our architecture. We had notes about dealing with production issues, and important contact information. We kept our discussions to our mailing list, our meeting notes on a shared drive, and used our wiki as a snapshot of how stuff works today.

The front page is important

On most wikis, the front page is the most neglected page. Before my old group buckled down, we had:

My first act in the great razing was to remove all of that and post something more sensible. We had links to important pages within the wiki, but very little information on the page itself that would change. Why would we post that in multiple places and invite entropy in?

Decide on a structure

Structure is highly important. New pages should be made with caution, because every page you create makes the wiki slightly harder to maintain.

If it’s a technical wiki (and not a CYA wiki, which is what I call meeting note wikis), you probably want to have one page for getting your development environment up and running. That page should forever be the go-to for figuring out how to get everything working on your local. Another page might be dedicated to troubleshooting search (note, not SOLR. Search. You may change what you use for search one day).

Basically, the idea is to have every page be the one place to update information about a certain topic. I should be able to bookmark that page and go back to it, and get the most recent information about that topic. That’s how wikis work. How would you feel if you bookmarked the page on, say, Voyager, only to discover that someone had made a page called ‘Voyager - New page’ and started putting updates only on that. There would be a Wikipedia revolt.

History is your friend

You know how we’re just updating that one page with relevant information? That means we have to remove the information that’s no longer relevant. But fear not, wiki citizen. That information is not gone. It’s merely resting in the page history.

Should anyone care how we were doing things a year ago, they can look it up. If they want to see who decided to move remote the database setup from the install instructions, you can check that out. It’s like having the world’s most boring time machine.

No one is fixing that page when they have a deadline

Remember that no one is ever going to stop in the middle of a crisis and start fixing the install instructions. There are fires to be put out, deadlines to meet, and metrics to massage.

So, accept that this is a part of human nature and find a way to work around it. One way you might do this is dedicate time to cleaning out the wiki. Just like you probably ended up assigning someone to clean out the fridge, you’ll probably need someone to run through the wiki to make sure it’s up to snuff. They should verify:

You probably want to have this duty rolled around various groups. I strongly recommend a bounty so that it doesn’t get a vague nod before it’s passed on. My office has the ability to pipe in sound. I may ask that our bounty be the ability to pipe in one song of our choice at lunch time.

Note that I’m a remote employee. This makes the option that much more fun, though.

Looking back, looking forward

I don’t do resolutions. I don’t have anything against them, but they do feel like daring the universe to throw distractions and impediments your way. The few years I’ve made them, I’ve had some ridiculous results.

Plan to lose weight? Get pregnant.

Decide to spend more time on my hobbies? Get a promotion and become crazy busy.

Get the house in order? Extended family medical emergency.

So, instead, I like to look back and think about what I got done this past twelve months.

2013

2013 was insane. I’ll sometimes talk about how I felt like I spent my early twenties idling. These days, I feel like all the action that should have happened then is being crammed into my early thirties.

Barbara Shaurette and I ran the first Young Coders workshop. Maybe someone else saw how much this was going to explode, but I was caught completely off guard. I ended up running another workshop at PyOhio, and I have several more lined up for 2014.

For the first time, I spoke at a technical conference that didn’t revolve around Python.

I published my second book. It ended up being three times as long as my first, but out in the same amount of time. I’m getting better at this!

I got my first one star review. I survived it with a bit of wine and a lot of comfort from other author-friends.

My daughter started kindergarten. All of the children are now in school!

I got the urge to mother something. Rather than have a baby, I got a dog. Dog > baby, 100%.

My husband and I celebrated our first anniversary. I actually got around to planning our honeymoon. I explained to the kids that no, they were not going with us. I threatened to go into detail as to why.

I taped a series of videos for my book. I thought this would be easy, since I do talks all the time, but the experience was both surreal and educational. I learned that I talk way too fast. I also learned that being slightly prepared makes you something of a treasure in the tech world. I think I got thanked for having my slides ready the day of the shoot about forty times.

I took up knitting again. I don’t know what’s changed, but I’m suddenly knitting much faster. Projects that were taking me months to finish are now taking me weeks. Look at all these things I made this year! Most of these were done in the last quarter!

All of my finished objects for 2013. There are eleven items: One baby sweater, four sets of gloves, two scarves, two washcloths, and one bunny.

I won NaNoBlogMo. I even sold some of the posts!

I convinced two of my friends to join me at Cox. I realized that I no longer word my sentences so Cox doesn’t sound dirty. When someone makes a joke about it, I stare at them blankly. 

I brought some nail polish to a conference, and it became a thing.

2013 was so packed that I’m staring at the items above, certain I’ve missed something.

2014

I don’t have a proposal in for a third book yet, but I have some thoughts. I’ll probably spend January banging out some outlines and seeing what sticks.

2014 actually has some planned vacations! This is a major shortcoming of mine. I tend to not plan in time to just get away and drink fancy drinks and read. I travelled a ton in 2013, but all of it was either business or to visit family (which, while nice, is totally not a vacation).

I have a few conferences lined up. Some, it will be my first time (PyTenn!), others, it’ll be like going home (PyCon!). At least this year I’ve actually planned well in advance.

The boy will start his last year of middle school. We will start preparing for high school.

The girl will start getting actual homework. I’m girding my loins for that battle.

Here’s to 2014 being as awesome, though maybe a bit less hectic, than 2013!

Katie writes: Kids Code!

This holiday weekend, I turned in half the chapters on my new book. Guess that means I can start talking about it, huh?

My new book was inspired by a conversation I’ve had with several parents while running Young Coders. This exchange happened every time I ran a class, and sometimes, has happened several times during one session.

“I tried to teach my kid how to code before, but it didn’t seem to stick. I hope this class gets them interested!”

“How did you try to teach them?”

The parents seemed to fall into two groups: Those that tossed a book at the kids (“That’s how I learned!”) and those that tried to show the kid how to code (“I taught them to use the toilet and ride a bike, and I train people at work. How different could this be?’). Neither approach seemed to work very well unless the child was already deeply interested in coding.

Some might say that the only kid you can get interested in coding is one that already seems predestined to be a hacker, but my experience teaching says otherwise. One of my most disinterested students was a teen girl who was only there to make sure her brother behaved. At the end of the class, she was one of my best new coders.

So, what was missing? Why were books and parents falling flat, but classes doing so well?

The answer was surprisingly simple: The parents didn’t know how to teach.

How do you teach?

Teaching is not a simple act. Your mind is in constant motion, thinking about what you need to teach, what the student knows, and what you need to lay out to bridge those two things. You also have to keep an eye on how the student is doing. Are they tired? Overloaded? Distracted? Confused? Stuck? Do you need to go back and review a concept, or do they just need to get up and walk around a bit? Oh, wait, have we fed them? Could they just be hungry?

You also have to take the knowledge in your head and rip it apart, breaking it into chunks that can be easily taken in and reassembled. You have to figure out which chunks rely on other chunks, and try to find a good place to start. If you think this is a skill we all have, go ask a co-worker to explain the most complex part of your stack to a new hire. How many times is the new hire confused because the old hand left out something vital?

So what was wrong with just handing the kid a book? When I asked the kids (my son included) it usually came down to getting stuck. They’d come to a place where they got a minor bug, or where a piece of jargon wasn’t explained, and they’d shut down.

I’ve seen this happen in classrooms, but with a difference: There, we had someone at the ready to jump in and fix the mistake. Once the mistake was fixed (or the concept explained again), the student would dive back in.

So, how could I help? I’m teaching as many classes as I can. Could I write a book that could help?

Teaching the teacher and the student

I realized that we needed a book written for two audiences: students and mentors. The students needed to be taught how to code in such a way that they never got overwhelmed and had ample opportunities to practice the skills they gained. The mentors needed to be taught how to teach, helping them strike a balance between jumping in and leaving the student to fend for themselves.

After pitching it to my editor at O’Reilly, we also decided to add videos to help reinforce concepts and to help students review when needed. Lessons about Python are broken up by projects that use all the skills the student has acquired by that point. Some are, of course, games, while others are more practical (like a to-do list manager).

The ages I’m writing this for are somewhat fluid. It’s written at a 6th grade level (which is where most tech books should be written, in my opinion). A middle schooler would probably be able to make it through most of the chapters with the mentor off to the side, providing occasional encouragement. A precocious third-grader could probably make it through most of the book with a mentor providing support during the coding bits.

Hey, I know someone who would enjoy this!

Good! I’ll need tech readers soon! The final chapters are due at the end of November, with the tech review starting a bit before that. This might make an interesting winter break project!

If you would like to be a tech reader (or just want to know more about this book), contact me either through Twitter (@kcunning) or my email.

Oh, and I’m totally looking for a better title for this thing. If you have any ideas on that front, ping me as well.

Flask + git: Easiest workshop ever

A few weeks back, I got a chance to teach a Flask class to my local PyLadies chapter. Normally, when I run a class, I spend a lot of time typing into the shell and into an IDE, then waiting for the students to do the same. My co-teachers and I then run around trying to correct typos, indentation errors, and syntax. The end result is aching backs and feet, and quite a bit of lost time.

I began to wonder if there was a way to get that time back. Could I take typing out of the equation while still giving the student the power to play around with the material? I realized the solution was in something I touched every day: Git.

Rather than have students type examples in, why not have them check out a specific commit from the class materials and run that? They could mess around with the code, reverting once they were ready to move on. `git diff` would tell them what had changed between commits, so they could see how the new functionality had been added.

After a few weeks of outlines and careful commits, I finally had something I could use to teach a class. I decided to test it on DC PyLadies.

The Set-Up

Teaching a class this way does require a bit more set-up than usual. We couldn’t just install Python and go. We had to install:

Because we only had four hours, I skipped over virtual environments, promising that we would teach that next month.

I did cheat a bit by bringing in the Flask chapter from Python in 24 Hours, since that goes through installation of almost everything above. Still, that didn’t save us from having to put fingers on keyboards. On Windows machines, we had to fix paths. Some of the Macs were finicky. I would say, all told, we spent an hour getting everyone rolling.

Starting the class

I had thought about making slides, but ran out of time. This ended up not being too much of a problem. I simply spend most of the time live coding in front of the students.

The process was simple.

The first few commits were the hardest. There was a ton to explain, such as what a decorator was, what “starting a server” meant, what __init__.py was doing, where the heck the HTML was coming from (we hadn’t created any templates yet), etc. Even with almost minimal code, we probably spent the first hour just going over the first three commits.

Once we were over that hump, however, commits moved quickly. I’d check one out, go over the diff, and almost immediately, everyone got it. In fact, in later commits, I almost felt like I was moving too slowly.

Even with the speed-up, I still wasn’t able to cover all of the material. I had expected this, though, since the class was really meant for a longer workshop. Still, everyone left with working knowledge of Flask and a nice introduction to git.

What will I change for next time?

First off, I will most certainly factor in more time for setting up. I tried to get students to set up their own environment beforehand, but there’s no way to make sure that everyone does that. Some get stuck on an install. Others install the wrong thing.

What I’ll probably do is have a dedicated set-up time before the class starts. So, if the class starts at 1, then the set-up party starts at 12.

I’d also have someone dedicated to setting up stragglers, and make sure they do it outside of the classroom. This, of course, means I have to get a volunteer who knows Windows as well as Mac OS and Linux.

I’m also investigating doing all of the installs off of a thumb drive. We were lucky that the library’s wifi held up, but there have been some meetings where no one could get more than a trickle of bandwidth. Being able to install Flask (and all of its dependencies) without needing wifi could save a bunch of time in a pinch.

One of the things I skipped was hacking on the code together between commits. The original plan was to propose a small change, then let the students do it. Then, I’d check out the next commit to show how I did it. The room we had and the time we had were both too small, though, for this much interaction.

Future plans

The code I have isn’t finished, yet. I’d like to get Flask talking to a database. I’ll probably add another project so that I can more naturally add in things like text search, pagination, and reusing data in several views (maybe a blog app? Everyone needs to write a crap blog app, so we might as well get that out of the way).

I’d also like to cover using Python Anywhere to get your site out in the wild, and perhaps even deploying to your own server (something I think everyone should know how to do).

Finally, I should add a section on tests, since this would be a good place to teach when you should write a test, and when you shouldn’t.

Want to help?

My repo is here! I’m more than happy to accept suggestions through issues and code through pull requests. All I ask is that you comment clearly in your commits so that it’s easy for anyone to know what exactly they’re going to be demoing next.

Also, try moving in baby steps. If someone wants to run the class at a faster pace, they can always skip commits. And if you want to run the class? Please do! If you can, let me know how it goes, and what would make the materials work better for you!