Prevalence of #occupycentral in Hong Kong Instagrams

Many Hong Kong citizens are currently protesting for democratic reform in downtown Hong Kong. As is expected, the Chinese netizens have taken to social media to spread their message. Instagram in particular was in the press as there were reports of the Chinese government blocking access to the service. Nonetheless, many users in Hong Kong were still able to get Instagrams posted. Using the Instagram API, I gathered geocoded instagrams in Hong Kong tagged “#occupycentral”.

The tag had some rumblings late last week, but really exploded over the past few days.Line graph showing the rise of the #occupycentral tag
Although instagram users around the world used this tag, I wanted to get a visual of where in Hong Kong the tweets were coming from. By far, the majority are clustered around the downtown area, although there are some stragglers further away. Also note the few posts from Victoria Harbor.Map of Instagrams tagged #occupycentral
It is common for Instagram users to tag photos with multiple hashtags. This can increase visibility of posts since users often browse media by tag. Of the Instagrams that were tagged “#occupycentral”, these are the most common other tags. I was surprised to see the popularity of “#umbrellarevolution”, even surpassing the Chinese versions of “#occupycentral”.Bar chart of tags used alongside #occupycentral
If you’d like to see some of the actual photos being uploaded, check out this live map from Geofeedia.

One Year of My Workout Data

Penn Jillette would say that there are two kinds of people in the world: skinny fucks and fat fucks. While he places himself in the latter category, I am definitely part of team skinny fuck. Around this time last year I started casually lifting weights. In typical LTD fashion, I also started tracking my weight and workouts.

This chart shows my body weight gain, approximately 10% over the year.Body weight line graphAs for my workouts, I tracked the exercise, amount of weight, and number of reps. I don’t know what the standard is for recording free weight, but I made my recordings “per limb” so that a bench press of 30lbs means 30lbs per arm. Any days where I skipped a particular exercise were marked as 0lbs. (Mouseover to highlight.)
One thing this graph hides is the number of reps. For example, the transition from 10 reps of 20lbs to 5 reps of 25lbs. This is the same graph except with the y-axis showing the weight multiplied by the number of reps.
I’m still tracking my data and next year I’ll be able to do an update with double the data!

How to Write a Text Adventure in Python

People new to programming often ask for suggestions of what projects they should work on and a common reply is, “Write a text adventure game!” I think there are even some popular tutorials floating around that assign this as homework since I see it so much. This is a really good suggestion for a few reasons:

  • The concept is familiar and fun (everyone loves games!)
  • They can be written using core libraries
  • The UI is the console

But new programmers often struggle with knowing where to start. This tutorial is designed to walk you through the process from start to end of writing a very basic game. There are also plenty of points to expand the game and make it your own. It is written for people who are familiar with basic programming concepts (if-statements, loops, objects, etc.) but who are still new to writing full applications. This guide is geared towards Python learners and the code compiles in Python 3.x.

Just looking for the code? View this project on GitHub.

How to Write a Text Adventure in Python Part 4: The Game Loop

This is just one part of a series on writing your own text adventure in Python. Click here for the introduction.

The end is near, we’re almost ready to play the game! We’ll finish this series by implementing the game loop and receiving input from the human player.

The Game Loop

While some applications follow a discrete set of steps and terminate, a game typically just “keeps going”. The only way the program stops is if the player wins, loses, or quits. To handle this behavior, games usually run inside a loop. On each iteration, the game state is updated and input is received from the human player. In graphical games, the loop runs many times per second. Since we don’t need to continually refresh the player’s screen for a text game, our code will actually pause until the player provides input. Our game loop is going to reside in a new module game.py.

from adventuretutorial import world
from adventuretutorial.player import Player


def play():
    world.load_tiles()
    player = Player()
    while player.is_alive() and not player.victory:
        #Loop begins here

Before play begins, we load our world from the text file and create a new Player object. Next, we begin the loop. Note the two conditions we check: if the player is alive and if victory has not been achieved. For this game, the only way to lose is by dying. However, there isn’t any code yet that lets the player win. In my story, I want the player to escape the cave alive. If they do that, they win. To implement this behavior, we’re going to add a very simple room and place it into our world. Switch back to tiles.py and add this class:

class LeaveCaveRoom(MapTile):
    def intro_text(self):
        return """
        You see a bright light in the distance...
        ... it grows as you get closer! It's sunlight!


        Victory is yours!
        """

    def modify_player(self, player):
        player.victory = True

Don’t forget to include one of these rooms somewhere in your map.txt file. Now that the player can win, let’s finish the game loop.

def play():
    world.load_tiles()
    player = Player()
    while player.is_alive() and not player.victory:
        room = world.tile_exists(player.location_x, player.location_y)
        room.modify_player(player)
        # Check again since the room could have changed the player's state
        if player.is_alive() and not player.victory:
            print("Choose an action:\n")
            available_actions = room.available_actions()
            for action in available_actions:
                print(action)
            action_input = input('Action: ')
            for action in available_actions:
                if action_input == action.hotkey:
                    player.do_action(action, **action.kwargs)
                    break

The first thing the loop does is find out what room the player is in and then executes the behavior for that room. If the player is alive and they have not won after the behavior executes, we prompt the human player for input. This is done using the built-in input() function. If the human player provided a matching hotkey, then we execute the associated action using the do_action method.

The last thing we need to include is an instruction for Python to know that play() should run when running the file. Include these lines at the bottom of the game.py module:

if __name__ == "__main__":
    play()

To run the program, navigate to the folder containing the adventuretutorial package in your console and run python adventuretutorial/game.py. Have fun!

Where to go from here

Congratulations! You now have a working text adventure game. There are certainly other approaches that you could have taken, but I wanted to design the code for this tutorial to be easily expandable. With the information learned here, you should be able to quickly add your own custom items, enemies, and tiles. If you’re up for more of a challenge, you might even add new functionality or behavior to the game. Here are some ideas that you might want to implement:

  • Currently the game is the exact same every time it runs. Create a RandomEventTile that spawns an enemy, has an item, or does nothing based on a random number.
  • There’s a bug in the game where the player can get infinite items simply by re-entering an item room over and over. Fix this bug so that items can only be picked up once.
  • Add more items such as armor that reduces damage or quest items that advance a storyline. Do this by making a subclass hierarchy similar to the Item->Weapon->Dagger hierarchy.
  • Instead of providing hotkeys to the player, let them enter commands like “go east”, “attack”, “pick up dagger”, etc. An easy way to do this would be to store a list of acceptable commands with each Action instead of a single hotkey.
  • Create neutral NPCs who may help the player or provide quests.
  • Build an economy into the game. Instead of storing individual gold coins as items, store the total amount of gold the player has. Allow the player to buy and sell items.

I’d love to see the game you ended up with. If you want, fork this code on GitHub and link to your version in the comments!

How to Write a Text Adventure in Python Part 3: Player Action

This is just one part of a series on writing your own text adventure in Python. Click here for the introduction.

So far we’ve created a world and filled it with lots of interesting things. Now we’re going to create our player and provide ways for the player to interact with the world. This will probably be the most conceptually challenging part of the game, so you may want to re-read this section a few times.

The Player

Time for a new module! Create player.py and include this class:

from adventuretutorial import items

class Player:
    inventory = [items.Gold(15), items.Rock()]
    hp = 100
    location_x, location_y = (2, 4)
    victory = False

    def is_alive(self):
        return self.hp > 0

    def print_inventory(self):
        for item in self.inventory:
            print(item, '\n')

Now you can see some of the concepts that we previously templated have been made into reality. The player starts out with a few basic items and 100 hit points. We also provide a starting location and a victory flag that will notify us if the player has won the game. The methods is_alive and print_inventory should be self-explanatory.

Adding Actions

Now that we have a player, we can start to give them actions. We’ll start with moving around first.

    def move(self, dx, dy):
        self.location_x += dx
        self.location_y += dy
        print(world.tile_exists(self.location_x, self.location_y).intro_text())

    def move_north(self):
        self.move(dx=0, dy=-1)

    def move_south(self):
        self.move(dx=0, dy=1)

    def move_east(self):
        self.move(dx=1, dy=0)

    def move_west(self):
        self.move(dx=-1, dy=0)

The player can move in four directions: north, south, east, and west. To avoid repeating ourselves, we have a basic move method that takes care of actually changing the player’s position and then we have four convenience methods that use the common move method. Now we can simply refer to move_south without specifically trying to remember if y should be positive or negative, for example.

The next action the player should have is attack.

    def attack(self, enemy):
        best_weapon = None
        max_dmg = 0
        for i in self.inventory:
            if isinstance(i, items.Weapon):
                if i.damage > max_dmg:
                    max_dmg = i.damage
                    best_weapon = i

        print("You use {} against {}!".format(best_weapon.name, enemy.name))
        enemy.hp -= best_weapon.damage
        if not enemy.is_alive():
            print("You killed {}!".format(enemy.name))
        else:
            print("{} HP is {}.".format(enemy.name, enemy.hp))

In order to find the most powerful weapon in the player’s inventory, we loop through all the items and use isinstance (a built-in function) to see if the item is a Weapon. This is another feature we gain by having all of our weapons share a common class. If we didn’t do this, we would need to do something messy like if item.name=="dagger" or item.name=="rock" or item.name=="sword".... The rest of the method actually attacks the enemy and reports the result back to the user.

We now have behavior defined for certain actions. But within the game, we need some additional information. First, we need to bind keyboard keys to these actions. It would also be nice if we had a “pretty” name for each action that could be displayed to the player. Because of this additional “meta” information, we are going to wrap these behavior methods inside of classes. It’s time for a new module called actions.py

from adventuretutorial.player import Player


class Action():
    def __init__(self, method, name, hotkey, **kwargs):
        self.method = method
        self.hotkey = hotkey
        self.name = name
        self.kwargs = kwargs

    def __str__(self):
        return "{}: {}".format(self.hotkey, self.name)

We’re going to use the now-comfortable design of a base class with specific subclasses. For starters, the Action class will have a method assigned to it. This method will correspond directly to one of the action methods in the player class, which you will see shortly. Additionally, each Action will have a hotkey, the “pretty” name, and a slot for additional parameters. These additional parameters are specified by the special ** operator and are named kwargs by convention. Using **kwargs allows us to make the Action class extremely flexible. We know all actions will require certain parameters, but there may be additional parameters that are different for certain actions. For example, we’ve already seen the attack method that requires an enemy parameter.

The following classes are our first wrappers:

class MoveNorth(Action):
    def __init__(self):
        super().__init__(method=Player.move_north, name='Move north', hotkey='n')


class MoveSouth(Action):
    def __init__(self):
        super().__init__(method=Player.move_south, name='Move south', hotkey='s')


class MoveEast(Action):
    def __init__(self):
        super().__init__(method=Player.move_east, name='Move east', hotkey='e')


class MoveWest(Action):
    def __init__(self):
        super().__init__(method=Player.move_west, name='Move west', hotkey='w')


class ViewInventory(Action):
    """Prints the player's inventory"""
    def __init__(self):
        super().__init__(method=Player.print_inventory, name='View inventory', hotkey='i')

Notice how the method parameter actually points to a specific method in the Player class. Referring to methods as objects in a feature in Python and other languages with “first class” methods. Be sure that you do not include () after the method name. The code Player.some_method() will execute the method whereas Player.some_method is just a reference to the method as an object.

The attack method wrapper is very similar with one small difference:

class Attack(Action):
    def __init__(self, enemy):
        super().__init__(method=Player.attack, name="Attack", hotkey='a', enemy=enemy)

Here we have included the “enemy” parameter as previously discussed. Since enemy is not a named parameter in the base Action class constructor, it will get bundled up into the **kwargs parameter.

Now that we have some actions defined, we need to consider how they will be used in the game. For example, the player should not be able to attack when no enemy is present. Conversely, they shouldn’t be able to calmly leave a room that has an enemy! Actions should be available or unavailable based on the context of the situation. To handle this, we need to flip back to our tiles module.

Change your import statement to include the actions and world modules:

from adventuretutorial import items, enemies, actions, world

Next add the following methods to MapTile:

    def adjacent_moves(self):
        """Returns all move actions for adjacent tiles."""
        moves = []
        if world.tile_exists(self.x + 1, self.y):
            moves.append(actions.MoveEast())
        if world.tile_exists(self.x - 1, self.y):
            moves.append(actions.MoveWest())
        if world.tile_exists(self.x, self.y - 1):
            moves.append(actions.MoveNorth())
        if world.tile_exists(self.x, self.y + 1):
            moves.append(actions.MoveSouth())
        return moves

    def available_actions(self):
        """Returns all of the available actions in this room."""
        moves = self.adjacent_moves()
        moves.append(actions.ViewInventory())

        return moves

These methods provide some default behavior for a tile. The default actions that a player should have are: move to any adjacent tile and view inventory. The method adjacent_moves determines which moves are possible in the map. For each available action, we append an instance of one of our wrapper classes to the list. Since we used the wrapper classes, we will later have easy access to the names and hotkeys of the actions.

Now we need to allow the Player class to take an Action and run the action’s internally-bound method. Add this method to the Player class:

    def do_action(self, action, **kwargs):
        action_method = getattr(self, action.method.__name__)
        if action_method:
            action_method(**kwargs)

That getattr rears its head again! We have a similar concept to what we did to create tiles, but this time instead of looking for a class in a module, we’re looking for a method in a class. For example, if action is a MoveNorth action, then we know that its internal method is Player.move_north. The __name__ of that method is “move_north”. Then getattr finds the move_north method inside the Player class and stores that method as the object action_method. If getattr was successful, we execute the found method and we include the **kwargs in case that method needs additional objects (like the attack method).

At this point, I decided to add one more action: flee. As an alternative to battle, the player can flee which causes them to a random adjacent tile. Here’s the behavior for the Player in the players module:

import random #Note the new import!
from adventuretutorial import items, world

class Player:
    # Existing code omitted for brevity

    def flee(self, tile):
        """Moves the player randomly to an adjacent tile"""
        available_moves = tile.adjacent_moves()
        r = random.randint(0, len(available_moves) - 1)
        self.do_action(available_moves[r])

And here’s our wrapper in the actions module:

class Flee(Action):
    def __init__(self, tile):
        super().__init__(method=Player.flee, name="Flee", hotkey='f', tile=tile)

Similar to the attack action, the flee action requires an additional parameter. This time, it’s the tile from which the player needs to flee.

Most of the tiles we have created so far can use the default available moves. However, the enemy tiles need to provide the attack and flee actions. To do this, we will override the default behavior of the MapTile class with our own version of the method in the EnemyRoom class.

class EnemyRoom(MapTile):
    def __init__(self, x, y, enemy):
        self.enemy = enemy
        super().__init__(x, y)

    def modify_player(self, the_player):
        if self.enemy.is_alive():
            the_player.hp = the_player.hp - self.enemy.damage
            print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, the_player.hp))

    def available_actions(self):
        if self.enemy.is_alive():
            return [actions.Flee(tile=self), actions.Attack(enemy=self.enemy)]
        else:
            return self.adjacent_moves()

If the enemy is still alive then the player’s only options are attack or flee. If the enemy is dead, then this room works like all other rooms.

Whew! That was a lot of new code. Be sure to check out the project GitHub if you want to see the complete code. We’re almost finished! All we need to do to wrap up is create and interface for the human player.

Click here for part 4