The Professional Steve

Tutorials, developer resources and inspiration.

Regular Expressions In Python Part 4: Character Classes

Regular_Expressions_In_Python_Part_4_Character_Classes

For your reading pleasure, here are parts 1, 2, and 3 of this series.

There’s lots more to learn about regular expressions, but I’d rather get back to game programming. :) However, before we leave this subject, I’ve got one more thing to show you. If I didn’t show you this thing, I’d feel I’ve neglected my duties to you as a big-time professional blogger.

It’s a little thing called character classes. But first, let’s revisit our date checker from the last post.

import re

number = raw_input("enter date: ")

date = re.search('(\d{1,2})/(\d{1,2})/(\d{4}|\d{2})', number)

if date and date.group(1) and date.group(2) and date.group(3):
    print "Month: " + date.group(1)
    print "Day: " + date.group(2)
    print "Year: " + date.group(3)
else:
    print "nope!"

Does it bother you that something like 99/99/9999 gets accepted by this code?

Let’s fix that:

import re

number = raw_input("enter date: ")

date = re.search('(1[0-2]|0\d|\d)/(3[01]|[0-2]\d|\d)/([1-9]\d{3}|\d{2})', number)

if date and date.group(1) and date.group(2) and date.group(3):
    print "Month: " + date.group(1)
    print "Day: " + date.group(2)
    print "Year: " + date.group(3)
else:
    print "nope!"

So what the heck is with the square brackets? [0-2] matches any number between 0 and 2 (inclusive), [01] matches either a 0 or a 1 but nothing else, and [1-9] matches any number between 1 and 9 (inclusive).

You can put any characters you want in a character class and any range.

For example, let’s say you were building a regex to check for hex values. [A-Fa-f\d] will match any hex digit (be it lower case or capitalized) and nothing else.

Well, I think that about covers it for regex. The next topic I need to cover with you is a simple one, function composition.

I’ll write at you later. :P

Disappearing into React

Sorry I’ve not been posting y’all. Making regular posts is hard. :)

There’s been personal stuff as there always is, also there’s been another distraction. I’ve been getting really really into the React JavaScript framework.

I’ve even started a new project, an Impossible Store (app, github), where you will be able to buy impossible things that will be delivered at Alpha Centauri (it’ll be up to you to pick them up there. :P ).

A real post will be here on Monday, and every Monday hereafter (or until I change my schedule or am hit by an asteroid or eaten by a giant bird).

Regular Expressions In Python Part 3

groupFor your reading pleasure, here are parts 1 and 2 of this series.

In our last post I showed you how to see if a bit of text matched your standard phone number pattern, stuff like (123) 456-7890. Now what if you wanted to grab that number and do something with it?

import re

number = raw_input("enter phone number: ")

phone_number = re.search('^\s*(\(\d{3}\) \d{3}-\d{4})\s*$', number)

if phone_number:
    print "The number you entered is " + phone_number.group(1)
else:
    print "nope!"

This is almost exactly the same as the regex we used in part 2, but there are extra parentheses. The new opening parenthesis is right before the first old escaped parenthesis and the new closing parenthesis is right after our last 4-digit sequence. The part of the pattern enclosed in parentheses like this is the character group we want to capture, in this case the phone number.

The next new thing we’re doing is assigning the result of calling re.search to a variable. That’s so we can get the captured group. The call to phone_number.group(1) is where we actually grab the phone number.

Let’s try another example:

import re

number = raw_input("enter date: ")

date = re.search('(\d{2})/(\d{2})/(\d{4})', number)

if date and date.group(1) and date.group(2) and date.group(3):
    print "Month: " + date.group(1)
    print "Day: " + date.group(2)
    print "Year: " + date.group(3)
else:
    print "nope!"

So we can do capture more than one group with a regex. Group 1 was the month (2 digits, represented by the pattern \d{2}), group 2 the day (same as the month), and group 3 was the year (4 digits, represented by \d{4}).

Let me show you another trick:

import re

number = raw_input("enter date: ")

date = re.search('(\d{1,2})/(\d{1,2})/(\d{4}|\d{2})', number)

if date and date.group(1) and date.group(2) and date.group(3):
    print "Month: " + date.group(1)
    print "Day: " + date.group(2)
    print "Year: " + date.group(3)
else:
    print "nope!"

You should recognize the \d{1,2} business, that means we’ll match either 1 or 2 digits. But what is (\d{4}|\d{2}) all about? The | means or. This part of the pattern will match 4 digits or 2 digits, not three, not 1.

5 is right out.

Regular Expressions In Python Part 2

snake

In part 1 of the regular expressions series, I told you that this code:

import re

if re.search('a*', 'cucumber'):
    print "found it!"
else:
    print "didn't find it :("

prints out “found it!”

That’s because, in the world of regex, * means “match 0 to infinity of the last character”. a matches the pattern a*, so does aaaa, or an empty string. That might not sound useful now, but it will. Trust me, this stuff builds into something that you’ll wonder how you ever lived without.

The + character matches 1 or more of the previous character. batman matches a+ but cucumber doesn’t. ^ means the beginning of a string. joker matches the pattern ^j but banjo doesn’t. $ matches the end of a string. bangarang matches g$ but kangaroo misses the mark.

Time for another table:

character or set of characters what it matches
* 0 to infinity of the last character
+ 1 to infinity of the last character
^ the beginning of a string
$ the end of a string
? 0 or 1 of the previous character
\d The numbers 0 through 9
\D Any characters but the numbers 0 through 9
\w Any word character (alphanumeric mostly)
\W Any non-word character
\s Any white-space character, like spaces or tabs

Here’s a cool example: what if you had a program that wanted to see if a string matched a phone-number format, like (555) 555-5555? Here’s your regex:

import re

number = raw_input("enter phone number: ")

if re.search('^\(\d\d\d\) \d\d\d-\d\d\d\d$', number):
    print "it's valid!"
else:
    print "nope!"

Parentheses are a special regex character I’ll talk about in a later post, so I had to put a \ in front of them to escape them. Everything else should make sense. The first character ^ represents the beginning of the string and the last character $ represents the end of the string. That way we won’t say a number is valid if the user types in, say “blahblahblah(333) 333-3333eat my shorts”. The second character is an escaped parenthesis, then we put in 3 digit characters, another parenthesis, a space, 3 more digits, a dash, and then the final 4 digits.

Let’s do a few more tweaks:

import re

number = raw_input("enter phone number: ")

if re.search('^\s*\(\d{3}\) \d{3}-\d{4}\s*$', number):
    print "it's valid!"
else:
    print "nope!"

Here’s another cool regex feature. We can specify how many characters we want to match by putting that number after what we want to match. We just have to put it inside some curly braces. You can even include a range of numbers. For example \d{3,5} matches any strings with 3 to 5 digits in them.

I also added optional whitespace at the beginning and the end of the line with \s* just to make sure we ignore leading or trailing whitespace.

I hope you are starting to see the power of regular expressions for matching strings. In my next python regex post I’ll be talking about using regex to grab parts of strings for you. It’s called group capture.

Regular Expressions In Python Part 1

pieOk, first thing you need to know about regular expressions (or regex) is that they are an arcane magic you can use to do a lot with a little code. Second thing you need to know is that regular expressions are all about doing stuff with strings.

You get Python’s regex by importing the re module at the top of your python file, like so:

import re

Ok, let’s start with searching for patterns

import re

if re.search('a', 'a'):
    print "found it!"
else:
    print "didn't find it :("

If you run this code, you’ll print out “found it!”

Now replace

if re.search('a', 'a'):

with

if re.search('a', 'batman'):

You’ll still get a “found it!”

As you may have guessed, re.search takes the thing you give it in the first argument, and then searches for that thing in the second. We call that first thing a pattern.

Here are some more examples:

pattern string found it?
aa Banana didn’t find it :(
pie I like pie! found it!
hop Linus torvald is terrible at hopscotch found it!
a* cucumber found it!

Wait a minute, what’s up with that last one? How does ‘cucumber’ match the pattern ‘a*’? In regex patterns, the ‘*’ character has a special meaning. Are there more special regex characters? You bet your ass there are.

I’ll tell you more in part 2.

Making A Game In Python Part 5

I’m making a text-adventure in Python!

Here are the previous posts about this:
Making A Game In Python Part 1
Making A Game In Python Part 2
Making A Game In Python Part 3
Making A Game In Python Part 4

Here is the link to the code on github.

And here is the trello board I’m using to track my progress.

Ok, let’s get back to it!

Last time I said I wanted to have the game remember the name you give the penguin. I think I’ll create a separate data structure for this called “saved_data”. Later I’ll write some code to handle naming the penguin, but for now I’ll just add the player name to this structure:

player_name = raw_input("What's your name? ")

saved_data = {"player name" : player_name}

Next I’m going to change all instances where I typed something like this:

"Hello " + player_name + ", said the penguin"

To this:

"Hello {{player name}}, said the penguin"

Then I’ll write a function that takes “Hello {{player name}}, said the penguin”, looks at the saved data, and replaces “{{player name}}” with whatever’s in saved_data. If nothing is there, then it will use “player name” or whatever’s inside the double curly braces as the default. I’ll call the function add_saved_data.

So, if the player’s name is Reggie, then this code:

print add_saved_data("Hello {{player name}}, said the penguin")

should print out:
“Hello Reggie, said the penguin.”

The two places I want to call this function are when I print out a room’s description, and when I print out the player’s choices:

    print "\n" + add_saved_data(current_room["description"]) + "\n"

    for choice in current_room["choices"]:
        print choice["input"] + ") " + add_saved_data(choice["description"])
    print "q) quit game\n"

Ok, here’s the source code for add_saved_data:

def add_saved_data(string):
    matches = re.search("\{\{(.*?)\}\}", string)
    if(matches):
        default = matches.group(1)
        return add_saved_data(
                re.sub(
                    matches.group(0),
                    saved_data[default] if default in saved_data else default,
                    string))
    else:
        return string

Since this function is kind of complicated, I’m going to take a few posts to explain what it does.

In order to do that, I’ll first need to talk about regular expressions, function composition, and recursion.

Making A Game In Python Part 4

So, I’m making this game in python. Here are the previous posts about it:

Part 1
Part 2
Part 3

As always, the latest code for the game is available here on github.

Now, on with our adventure in making adventure…

In the last post we had developed a “real” game. Now I think it’s time to see how far we can get with what we’ve got. Before we add more code, let’s try and add some game content first: (again, you’ll have to scroll to the right to see it all)

game_data = {"one-room house" : {"description" : "You're name is " + player_name + ". You're in a darkly lit  one-room house. Its raining outside. You can hear the drops hit the ceiling and can see rain hit the window   when lightning strikes in the distance, which it often does. The window is above a sink full of dirty dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch facing a      television. It's turned to a channel that only gets static. Amazingly there's a penguin sitting on the couch. The penguin turns to face you when you look at it. Behind you is a pile of smelly blankets and an old set of  golf clubs.",
                                  "choices" : [{"input" : "t",
                                               "description" : "Talk to the penguin",
                                               "destination" : "penguin conversation"},
                                               {"input" : "l",
                                                "description" : "Look around the room again",
                                                "destination" : "one-room house"}]},
"penguin conversation" : {"description" : "You say, \"Hello penguin.\"\n\n\"Hello " + player_name + ",\"      replies the penguin.",
                          "choices" : [{"input" : "l",
                                        "description" : "look around the room again",
                                        "destination" : "one-room house"},
                                       {"input" : "a",
                                        "description" : "Ask the penguin what's going on.",
                                        "destination" : "penguin says what's up"},
                                       {"input" : "n",
                                        "description" : "Ask the penguin their name.",
                                        "destination" : "name the penguin"}]},
"penguin says what's up" : {"description" : "You say, \"What's going on?\"\n\n\"I heard a loud bang outside.  I think someone's out there.\" replies the penguin.\n\nJust then, you hear a loud noise. Some thing or some   one just hit the wall with a loud thud.",
                          "choices" : [{"input" : "l",
                                        "description" : "look around the room again",
                                        "destination" : "one-room house"},
                                       {"input" : "n",
                                        "description" : "Ask the penguin their name.",
                                        "destination" : "name the penguin"}]},
"name the penguin" : {"description" : "You say, \"What's your name?\"\n\nThe penguin says, \"You remember     it's...\"",
                          "choices" : [{"input" : "l",
                                        "description" : "look around the room again",
                                        "destination" : "one-room house"}]}}

I never did say in my tutorials what this \n or \” business is did I? \n means the program will output a new line. \” makes the program print the double quote character.

Already I can make a lot of game content with my little game program, but I can already see parts of the game that need expanding and improving.

I want to be able to name the penguin and have the game remember that name. If I name them, for example, Sparky, then I don’t want the game to call them “The Penguin” I want the game to call him Sparky.

Also, I don’t want to keep having to copy and paste the same choices over and over again. I pasted the “look around the room” choice a few times already. I should put them in another dictionary and then reference them in each scene.

I also think it’s high time I implemented the saving and loading of games. While I’m at it, I’d like the game to look for game files and ask if I want to make a new game with them.

There are a few other things I want to add, pretty much immediately.

I want an inventory, and I want to be able to have new choices when I have stuff in that inventory. For example, once I pick up a key, I should be able to open a lockbox I wasn’t able to open before. I should put a lockbox and key in that opening scene description to test this out.

I want to have some way to fight monsters, a battle engine.

And I should just build into the game that looking around is always a choice I can make. I don’t want to have to paste that choice into each scene. The game should remember my location and tell me what I should see when I look around.

Maybe I should tie certain choices to people, like the penguin, and locations? That might be getting ahead of myself.

Anyway, I’ll tackle the ‘name the penguin task’ next time. Also, I think it might be time to make a trello board. I’ll do that next post as well.

Making A Game In Python Part 3

So, the code’s gotten too big to keep copy and pasting so I’ll just need to give you links to the story so far:

Making A Game In Python Part 1

Making A Game In Python Part 2

I will show you this data structure (you’ll need to scroll to the right to see the whole thing):

game_data = {"one-room house" : {"description" : "You're name is " + player_name + ". You're darkly lit one-  room house. Its raining outside. You can hear the drops hit the ceiling and can see rain hit the window when  lightning strikes in the distance, which it often does. The window is above a sink which is full of dirty     dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch       facing a television. The power's out so it's not on. Amazingly there's a penguin sitting on the couch, but    they've turned to face you. Behind you is a pile of smelly blankets and an old set of golf clubs.",
                                  "choices" : [{"input" : "t",
                                               "description" : "talk to the penguin"}]},
"penguin conversation" : {"description" : "You say, \"Hello penguin.\"\n\n\"Hello " + player_name + ",\"      replies the penguin."}}

This represents the game data as it currently stands, but this is no good. I need to change this structure so that each entry is the same. Each one should have it’s own set of choices and each choice should lead us to another entry in the dictionary.

Let’s fix that now:

game_data = {"one-room house" : {"description" : "You're name is " + player_name + ". You're darkly lit one-  room house. Its raining outside. You can hear the drops hit the ceiling and can see rain hit the window when  lightning strikes in the distance, which it often does. The window is above a sink which is full of dirty     dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch       facing a television. The power's out so it's not on. Amazingly there's a penguin sitting on the couch, but    they've turned to face you. Behind you is a pile of smelly blankets and an old set of golf clubs.",
                                  "choices" : [{"input" : "t",
                                               "description" : "talk to the penguin",
                                               "destination" : "penguin conversation"},
                                               {"input" : "l",
                                                "description" : "look around the room again",
                                                "destination" : "one-room house"}]},
"penguin conversation" : {"description" : "You say, \"Hello penguin.\"\n\n\"Hello " + player_name + ",\"      replies the penguin.",
                          "choices" : [{"input" : "t",
                                        "description" : "talk to the penguin",
                                        "destination" : "penguin conversation"},
                                       {"input" : "l",
                                        "description" : "look around the room again",
                                        "destination" : "one-room house"}]}}

Now each “room” has it’s own description and set of choices. Each set of choices has an input (the character or string that the player types) a description (“look around the room again” or “talk to the penguin”) and a destination for that choice (one of the “rooms” in game data).

Now we need to rewrite the game loop to account for this new structure:

current_room = game_data["one-room house"]

player_input = ""
while player_input != "q":
    print "\n" + current_room["description"] + "\n"

    for choice in current_room["choices"]:
        print choice["input"] + ") " + choice["description"]
    print "q) quit game\n"

    player_input = raw_input("What will you do? ")

    if player_input == "q":
        print "Hope you had fun!"
    else:
        valid_choice = False

        for choice in current_room["choices"]:
            if player_input == choice["input"]:
                current_room = game_data[choice["destination"]]
                valid_choice = True

        if not valid_choice:
            print "I have no idea what you're talking about."

If you’ve gone through my python tutorials, there shouldn’t be anything new here. Basically we set the initial room, print its description, it’s choices and then check the player’s input against those available choices. If there’s a match, we go to that new destination.

Ok, that’s it for now! Again, you can see the latest version of the code at https://github.com/NerdcoreSteve/python-adventure

Learn You A Haskell

Learn You A Haskell is a great, free, online book for learning the purely-functional programming language, Haskell. In case you’ve been living under a rock, programming only in Java 7 and COBOL, you know functional programming is what all the cool kids are doing.

Personally, I’ve found Learn You A Haskell to go just the right pace (not dumbed down and slow, but not terse) for learning this style of programming.

Making A Game In Python Part 2

Here’s the game so far (scroll to the right to see the rest of the text.):

print "You're darkly lit one-room house. Its raining outside. You can hear the drops hit the ceiling and can  see rain hit the window when lightning strikes in the distance, which it often does. The window is above a    sink which is full of dirty dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch facing a television. The power's out so it's not on. Amazingly there's a penguin        sitting on the couch, but they've turned to face you. Behind you is a pile of smelly blankets and an old set  of golf clubs."


user_input = ''
while user_input != 'q':
    print ""

    print "t) talk to the penguin"
    print "q) quit game"

    print ""

    user_input = raw_input("What will you do? ")

    print ""

    if user_input == 't':
        print "You say, \"Hello penguin.\"\n\n\"Hello Dave,\" replies the penguin."
    elif user_input == 'q':
        print "Hope you had fun!"
    else:
        print "I have no idea what you're talking about."

You can also see the game at github.com/NerdcoreSteve/python-adventure.

Ok, let’s get back to work. One problem is that we never asked the user for their name! Let’s fix that right now.

player_name = ''

player_name = raw_input("What's your name? ")

print ""

print "You're name is " + player_name + ". You're darkly lit one-room house. Its raining outside. You can hear the drops hit the ceiling and can see rain hit the window when lightning strikes in the distance, which  it often does. The window is above a sink which is full of dirty dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch facing a television. The power's out so it's not   on. Amazingly there's a penguin sitting on the couch, but they've turned to face you. Behind you is a pile of smelly blankets and an old set of golf clubs."

And here’s the other change I made:

        print "You say, \"Hello penguin.\"\n\n\"Hello " + player_name + ",\" replies the penguin."

All we do is ask for the player name just the same way we ask for other input.

Well this is all fun and good, but I want new things to happen and new choices to come up when I do stuff. For that I’m gonna need to do some pre-work. I want the description and choices to be part of a data structure. Then when I make choices I can load a new part of the data structure with new descriptions and choices.

player_name = ''

player_name = raw_input("What's your name? ")

game_data = {"one-room house" : {"description" : "You're name is " + player_name + ". You're darkly lit one-  room house. Its raining outside. You can hear the drops hit the ceiling and can see rain hit the window when  lightning strikes in the distance, which it often does. The window is above a sink which is full of dirty     dishes. On the oven beside the sink there is a pot full of boiling water. To your left there is a couch       facing a television. The power's out so it's not on. Amazingly there's a penguin sitting on the couch, but    they've turned to face you. Behind you is a pile of smelly blankets and an old set of golf clubs.",
                                  "choices" : [{"input" : "t",
                                               "description" : "talk to the penguin"}]},
"penguin conversation" : {"description" : "You say, \"Hello penguin.\"\n\n\"Hello " + player_name + ",\"      replies the penguin."}}

print ""

current_room = game_data['one-room house']
choices = current_room['choices']

print current_room['description']

player_input = ''
while player_input != 'q':
    print ""

    choice = choices[0]['input']
    description = choices[0]['description']

    print choice + ") " + description
    print "q) quit game"

    print ""

    player_input = raw_input("What will you do? ")

    print ""

    if player_input == 't':
        print game_data['penguin conversation']['description']
    elif player_input == 'q':
        print "Hope you had fun!"
    else:
        print "I have no idea what you're talking about."

Ok, the content of the game is now in a data structure, we have two parts: one part describes the room, the other describes what happens when you talk to the penguin. But the data structure is incomplete! What are the choices you get after talking with the penguin?

Also, the while loop needs work. It should just be able to take the game data and use it without knowing about penguins or rooms or whatever. It should just read the data, output the room description and give you your choices. When you make a choice, the game should load up another part of the data structure and start again. It should do that until the end of the game.

But… um… it took me a bit to get to this point. I think we need to stop here…. At this ugly, ugly place in the code.

Write at you later! :)

Follow

Get every new post delivered to your Inbox.

Join 203 other followers