Sunday, December 16, 2012

Developing a Developer: Weekly Report 6


I've finished the first book. It's been as good as I remembered. It's been a hard week. I'm having many assignments at college and I think I'll have to slower the pace. Anyway, let's get started.

Platforms



Last week I posted the source code of platforms, the adventures of a green square in a dungeon. I've finished the game and the old green square has evolved: now it's a raccoon.

If you look at the source code, you can see that the raccoon is composed of several images. Each image represents an action. One image represents standing, another for jumping and, finally, four for walking. I've experienced the following challenges:

  • Making the raccoon walk at the proper pace. At first, the raccoon images alternated to fast in comparison with the distance than it actually walked. This is because I made the images change every frame. I changed it to one image every four frames.
  • Making the raccoon “look” at the right direction. This is easily achieved through a Pygame built-in function: “pygame.transform.flip”. Although this is very easy, I had to be very careful storing directions in variables for the raccoon to remember where was looking at after walking.
  • Making the raccoon “fit” in the old square. Well, this is not the actual challenge, the thing is that my old green square was 20x20 pixels large. The raccoon looked so small in it... Well, I ended up changing the player square size to 50x50. This was tedious, because I had to change the entire level for the player to fit in the space between the first platform and the staircase... Lesson learnt the hard way.

I enjoyed the part of searching files for my game. It's amazing the amount of open resources available on the Internet.

Windows executables

This part took me a lot of time. The author of the book teaches us how to use a script to convert Python scripts into Windows executables. It hasn't work with my Pygame applications. In fact, it doesn't work with the author's scripts. In the past I used the book's method and I didn't encounter any of these problems. Something has changed either in py2exe or in Pygame. Apparently the problem is related with the font and mixer modules.

Gladly, someone else found this problem first and posted a solution. Check the link: http://www.pygame.org/wiki/Pygame2exe. I decided to use my own font file instead of the default. It works.

Honestly, I would like to hide every file that there's in the distribution but the exe file. This would keep things simple for the user. There must be a way to do this.

I got to understand a little about distributing applications in this pdf: http://cs.iupui.edu/~aharris/pygame/Appendix_C.pdf. However, I feel quite intimidated about the distutils Python module. My old C and Ada compilers output the executable with a single command. Having to write a setup script is quite strange for me. Perhaps I'll need to take a look at it in the future.

Monday, December 10, 2012

Developing a Developer: Weekly Report 5


This week I've corrected a few bugs there were in staircase.py. One of them – for example – allowed the player to jump while falling if he/she fell without jumping... Anyway, the current version includes further changes (I even changed the name of the project to “Platforms” and divided the program into files instead of keeping a single file). The program is organized in classes and since I read chapter 19 I've included a few things related with imported images. Let's begin.



Screenshot of Platforms

 

This game test is divided into several classes. Perhaps, the most important class is “Collisionable”. This class contains a Pygame “rect” object reference as an attribute. This “rect” object is the actual responsible of controlling collision between itself and other objects (including the player). Many of the remaining classes are children of “Collisionable” and they implement their own behaviour (platforms, mobile platforms, buttons, etc).

While chapter 19 taught me to load external images and place them into the game, it has only examples on putting a static picture and moving it around the screen. In this project, I've used texture images like the following:

Background texture
Platform's texture

 

If you pay attention to this texture images, you will see that the leftmost part fits with the rightmost part of the picture. The same happens with the top and the bottom of the picture. The only thing I have to do is to replicate the picture over and over again throughout the entire surface I want to have that texture. In order to fulfill that purpose, I created the Texture class. When a Texture object is created, the constructor copies the picture pixel by pixel into the Pygame “rect” object that requires that texture. In my opinion, this algorithm is quite slow and inefficient, but it allows me to store the calculated surface into an attribute and therefore needing to make the calculation only once in the execution of the game.

The pictures were downloaded from http://opengameart.org.

Monday, December 3, 2012

Developing a Developer: Weekly Report 4


I've read the second part of the 17th chapter (which covers animations) and the entire 18th chapter (which covers input). This week I've decided to change the Reverse Engineering phase I did in the previous weeks. Now, either I improve the program (in the way I did making a new AI for Reversi) or I made my own program. In fact, I've done many pygame tests this week and I think the best way to show my current skills is through a video.







Rotating Ball


You can download the script here.

It's just a ball that bounces across the screen. The most interesting part about it is that I used polar coordinates for the mark that gives the impression of rotation and them I transform them into Cartesian coordinates in the following way:

x = r * cos(o)
y = r * sin(o)

x, y = Cartesian coordinates
r = distance from the centre of the ball to the centre of the small mark (which is a circle, by the way)
o = angle that constantly increments or decrements depending on the rotation direction



Spiral


You can download the script here.

OK, drawing a ball is easy, because Pygame has a built-in function called “pygame.draw.circle” which does the job for us. In order to draw a spiral, I've implemented a class which contains the formula of the spiral and remembers the value of the always incrementing angle in polar coordinates. Outside this class, in the game loop the angle is incremented and a straight line is drawn from the previous point to the new point calculated using the nextPoint method in the spiral class.

There are a few more sophistications that you can find in the source code, but I find particularly interesting the precision attribute in the spiral class. This attributes set the increment of the angle in polar coordinates and thus it will determine the smoothness of the line. It might be confusing, but since the precision attribute determines the increment of the angle, the slower it's value the higher the actual precision is going to be (counterintuitive, my mistake). See the pictures below.


Spiral: PRECISION = 1

Spiral: PRECISION = 0.005



Balls and Input



You can download the script here.

I basically take the rotating ball I've explained above and wrapped it into a class. Then, each time the player clicks on the screen, I call to the class constructor and create a new ball with new attributes. That way, I can have many rotating balls with different attributes such as speed, direction, rotation direction, etc. I confess I enjoy clicking and clicking insanely into the screen.

clickBall.py




Platforms


You can download the script here.

In this test I've had many headaches. It works with both input and collision detection in the context of a platforms game. The code is a little bit long to explain everything here, so I'll explain directly the most difficult function:


def controlFalling(self, rects):
    # control jumping
    if self.jumping:
        self.rect.top -= self.JUMPSPEED
        self.jumped += self.JUMPSPEED
        if self.jumped >= self.MAXJUMP:
            self.jumping = False
            self.falling = True

        # check if we hurt our head with some sort of ceiling
        collisionCeiling = 0
        for r in rects:
            if self.rect.colliderect(r):
                collisionCeiling = r.bottom

        # if this is the case, correct position
        if collisionCeiling > self.rect.top:
            self.rect.top = collisionCeiling
            self.jumping = False
            self.falling = True

    # fall
    else:
        self.rect.bottom += self.FALLINGSPEED

    # see if rects collide and detect current ground
    collisionGround = self.floorY
    for r in rects:
        if self.rect.colliderect(r):
            collisionGround = r.top

    # if the object is in the ground, correct it's position
    if collisionGround < self.rect.bottom:
        self.rect.bottom = collisionGround
        self.falling = False


 

There are two cases to consider.
a) The object is jumping
b) The object is falling

In both cases, I firstly move the object (upwards if it's jumping and backwards if it's falling) and then I see if it's colliding with something. If this is the case, I correct the position of the object, for I don't want to display it overlapping with anything. In the case of jumping, we also stop the jumping boolean attribute of the player, because the player must fall (although he could grab something in the ceiling and hang... Mmmm... Maybe for another test).

Sunday, November 25, 2012

Developing a Developer: Weekly Report 3

I'm on chapter 17. The Pygame part has started. This week has been interesting.


Cartesian Coordinates

If you know some basic maths, programming Cartesian coordinates changes a little bit your mindset. Usually, you put the X axis horizontally and the Y axis vertically. Going to the right means a greater value for the X coordinate and going to the left means a lower value; while going up means a higher value for the Y coordinate and going down means a lower value on that coordinate. When we program, it isn't always like that. Usually, the origin (0, 0) is at the top left corner on the screen, and while going to the right still means a greater value for X, going down now means a greater value for Y. It might sound confusing at first, but you get used to it very quickly.

However, this isn't a problem I struggle with when working with a Cartesian coordinate system. I do have experienced a little bit of confusion while I was reverse engineering the Reversi game. The board uses a Cartesian coordinate system, being the top left corner the origin. The problem comes when you have to assign values to the coordinates and then display them.

Let's imagine we want to assign different values to each place on the board. The code could look something like this:

for x in range(row_size):
    for y in range(column_size):
        matrix[x][y] = value


Imagine that row_size = 2 and column_size = 2. The result would be something like:

matrix[0][0] = value for 0, 0
matrix[0][1] = value for 0, 1
matrix[1][0] = value for 1, 0
matrix[1][1] = value for 1, 1


It makes perfect sense. The first coordinate (x) comes first and then the second one (y). Nevertheless, if we tried to print the board following the same fashion:

for x in range(row_size):
    for y in range(column_size):
        print_on_board(matrix[x][y])


We end up getting something like this:


Value for 0,0
Value for 0, 1
Value for 1, 0
Value for 1, 1


If we use the previous algorithm, the computer will swap the X and Y axis. We don't want that happening. In order to avoid that we must use the following code:

for y in range(column_size): # Print each row
    for x in range(column_size): # Print each value on the same row
        print_on_board(matrix[x][y])


This way, we would get the desired result.


AI and Minimax

Chapter 16 of “Invent Your Own Computer Games with Python” by Al Sweigart deals with AI simulations. I've done many simulations in the past, in fact, in this very blog you can look at some I made about the Risk tabletop game. At university I had a few subjects about Artificial Intelligence and I thought it was a good idea to apply something about Game Theory in a game like Reversi. So, I decided to implement my own AI for Reversi. I've implemented the Minimax algorithm.

In very rough terms, Minimax is about choosing the option that gives us more profit taking into account that our adversary will choose the option that will give us the least. Let's imagine that in a certain game I could choose between two options: A and B. If I choose A, my opponent can choose between giving me 3 points or 2 points. If I choose B, the opponent can choose between giving me 20 points or 0. In this example, we minimize damage by choosing option A. Minimax considers that the opponent will choose 0, so it's better to take at 2 (in the worst scenario after going for A) than 0 (even if we know that by choosing B we have a chance of getting 20!). A human being might attempt to choose B hoping that his opponent will choose to give him 20 points by mistake, but that's not the way machines think.

In Reversi, we usually have more than 2 options (legal moves) and the game involves more than just one decision for the player to take (it easily lasts 40 moves). In other words, the depth of the search for the solution is greater. In the previous example, the depth is one. We analyze only one move forward for the player and one for the opponent. My AI implementation will analyze 4 moves forward. The benefit for a move will be the number of tiles turned by that move unless we're talking about a corner. My Minimax implementation will assign an infinite benefit to corner moves (in all simulations that I've made before it's clear that getting the corners is always in our benefit). Being this, implemented in Minimax means that the AI will also avoid the opponent claiming them!

You can download the source code for the simulation here, and if you just want to play against the computer (which will use Minimax) you can download the source code here. Remember that these codes are a variation of the ones in the book.

These are the result for the simulations (X is the book's AI and O is mine):

Welcome to Reversi!
Enter number of games to run: 350
Game #0: X scored 31 points. O scored 33 points.
Game #1: X scored 25 points. O scored 37 points.
Game #2: X scored 37 points. O scored 27 points.
Game #3: X scored 32 points. O scored 31 points.
Game #4: X scored 47 points. O scored 17 points.
Game #5: X scored 34 points. O scored 29 points.
Game #6: X scored 27 points. O scored 37 points.
Game #7: X scored 30 points. O scored 34 points.
Game #8: X scored 21 points. O scored 43 points.
Game #9: X scored 21 points. O scored 41 points.
[skipped for brevity]
Game #340: X scored 26 points. O scored 22 points.
Game #341: X scored 32 points. O scored 32 points.
Game #342: X scored 30 points. O scored 34 points.
Game #343: X scored 32 points. O scored 32 points.
Game #344: X scored 38 points. O scored 26 points.
Game #345: X scored 39 points. O scored 22 points.
Game #346: X scored 20 points. O scored 44 points.
Game #347: X scored 18 points. O scored 46 points.
Game #348: X scored 18 points. O scored 46 points.
Game #349: X scored 21 points. O scored 43 points.
X wins 94 games (26.86%), O wins 243 games (69.43%), ties for 13 games (3.71%) of 350.0 games total.


You can download the whole log here. I didn't run more than 350 games because I don't have that much computation time available and it's a fairly big number anyway. In any case, it seems unquestionable that my AI beats the one shown in the book (it wins almost 70% of the games!).


From ASCII Art to Pygame Art

This is my way of saying "Hello World!" with Pygame (see picture below).



Download the source code here.

Monday, November 19, 2012

Developing a Developer: Weekly Report 2


I'm finishing chapter 15 in “Invent Your Own Computer Games with Python” by Al Sweigart. Haven't I said it's a wonderful book yet? At the moment, I'm reversed engineering the Reversi game (it's ironic, isn't it?).

I haven't done anything out of the project programme (except my last post about calculating primes faster). I'm concerned about two things regarding to the project:
  • As far as I know Pygame doesn't work with Python 3 on Linux. This means that I have to continue the training offered in the book (which uses Python 3) in Windows. Perhaps I'll use pgreloaded in my games in the future.
  • How to make single EXE files. I've used tools like py2exe and cx_freeze in the past. It helped me to work my games in computers that didn't have Python installed, but I wasn't able to make a single EXE file nor an installer. I'll have to investigate about this.

That's been my week 2 working on this training project. By next week, I'll have finished the part of the book that deals with text games – mostly using ASCII art – and probably started programming with graphics using Pygame.

Sunday, November 11, 2012

Faster Primes

Do you remember my post about prime numbers? If you don't, you can catch up clicking on the link:
Prime Numbers: A Computational Approach

I've improved the function that checks if a number is a prime number. Here's the new code:

/*
 * Check if "number" is a prime number. If it is, the function will return 1.
 * If it isn't, it will return 0.
 *
 */
int isPrime(long long unsigned number)
{
    if(number == 2)
    {
        return 1;
    }
    else if(number % 2 == 0)
    {
        return 0;
    }
    long long unsigned aux;
    for(aux = 3; aux*aux <= number; aux+=2)
    {
        if(number % aux == 0)
        {
            return 0;
        }
    }
    return 1;
}


The key is the double increment in the for-loop (aux+=2). Apart from 2, all prime numbers are odd numbers. The first thing I do, is check if a prime number is 2. Secondly, I discard the possibility that the number is divisible by 2. Finally, I proceed with the algorithm the same way I did in the first program, but this time I start checking if the number can be divided by 3. And then, I can safely increment the number two units on each iteration (if 2 cannot divide a number, a number that can be divided by 2 won't be able to divide that number either).

This screencaptures (it's about the Unix time command again) show this improvement. I calculate primes up to 10000000 this time.

Old version
New version

You can download the source code with the improved function here.

Saturday, November 10, 2012

Developing a Developer: Weekly Report 1

First week. I've read the first 10 chapters of the first book (Invent Your Own Computer Games with Python). The process I've employed looks something like this:

1 - Copy the source code of the chapter's game.
2 - Read the chapter.
3 - Reverse Engineering (in this step I write a program that does the same, but I do not look into the book nor in it's source code).

This last step is my favourite. I've implemented the games "Guess", "Jokes", "Dragon Realm" and "Hangman". In my version of hangman I've made a few changes. For example, in my version the program reads the words from a file (instead of having them in the source code). You can download my version of the game here (remember that this game is heavily inspired in Al Sweigart's hangman).

I also set up Eclipse (the development environment) and installed the PyDev plugin that makes Eclipse able to work with Python. Setting up PyDev is a piece of cake. The only difficulty I found was configuring the Python interpreter in the environment. I used this tutorial for that purpose:
http://www.vogella.com/articles/Python/article.html

 Finally, I used this program for the diagrams:
https://live.gnome.org/Dia/

Actually, I didn't need to do diagrams, but the book spends a whole chapter on it. I thought it was interesting to get used to flowcharts. In the future, I'll surely need to do them.

Monday, November 5, 2012

Developing a Developer


Hello readers.

In this post I'm going to announce something special. I want to make a little game. It's going to be a small thing, but it's because I promised a friend to make one, and I have to fulfil my promises. It's also the perfect excuse to catch up with this beautiful hobby.

This is mainly a training project. I'll write posts in a journal entry style. That way you can track my progress.

I've divided this project in four phases:
1) The Basis
2) The Tools
3) The Experiments
4) The Games

PHASE 1: The Basis

I'll try to restore the knowledge about game programming I had in the past. It's basically reading this book:




“Invent Your Own Computer Games with Python ” is a free ebook. It's oriented to children that want to make computer games but don't know how to program. When I finished my first programming course (it lasted a year) I realized that I knew many algorithmic things. I knew how to search items in lists, storing integer in arrays, import libraries... It was a very good improvement taking into account that I started without knowing anything about programming. Nevertheless, I didn't know how to do something actually useful. And games... well, it's sort of what we all think of, don't we? So that summer, I found this book and read it. After that I made two simple games, but they were lost one sad day when I accidentally erased all the information in my hard-drive.

PHASE 2: The Tool

It's learning more about game programming but it will be mainly using the Pygame library. Actually, this phase is yet another excuse to read the sequel of the previous book: 


 

I have no idea what it's going to contribute to my knowledge, but I had so much fun reading the first one that I have to read it now.

PHASE 3: The Experiments

This phase it's probably going to be a little bit more chaotic. I'll make a kickoff game that doesn't take a lot of time. Let's see how it goes. I'll also read a few open source programs to take knew ideas. I also may do this kind of things after reading the first book.

PHASE 4: The Game

I'll program the game. I have no idea how long this phase is going to take me. Maybe two weeks? Three? A month and a half? I have an idea about how it's going to be, but I have no idea how I'm going to be posting about it without spoiling the argument. We'll see.

 

Monday, October 29, 2012

Setting up a Gmail account in Mozilla Thunderbird

I write this post in order to share my experience with my Gmail account in Thunderbird.

The first problem I faced involved contacts. When I wrote my first message, I didn't enjoy the comfort of predictive contact suggestion (because I hadn't any contacts stored on my local machine). I solved it through a plugin: Zindus. Zindus syncronizes your contacts between Thunderbird and Google Contacts. Its use is pretty straightforward. Once you've installed the plugin, you go to the “tools” menu and select “Zindus”. Then add your gmail account, select the convenient options and click on “Sync Now” before you finish. Voilà. Contacts loaded, suggestions enabled.

The second thing I missed was the conversation thread-style that Gmail provides. I installed another plugin again: ThreadVis. It works like a charm. In fact, it has a fun feature that Gmail doesn't have: a timeline of the conversation. The following picture is an example of what I'm talking about:


In the picture, the empty circles represent my messages. The filled ones refer to other participants in the conversation (one color per participant). The separation between each circle represents the time it has passed between each message.

I also changed the relative position of the reply to the quote. The settings by default put the reply bellow the quote. This is not the way Gmail behaves and I found it a little bit confusing and counterintuitive. In order to change this go to “Edit > Account Settings... > Compositing and Addressing”. On the window you shoud see a checkbox that says “Automatically quote the original message when replying”. Check it if it isn't. Below that checkbox, you can select the following option from the drop-down list: “start my reply above the quote” and, if you use a signature, you can also select where to place it. It works, but you have to place an enter between the reply and the quote, otherwise Gmail won't hide the quote.

Finally, I set up my Google Talk account. Everything works fine. However, there are a few things that I haven't been able to fix and that I miss:
  • Sync Gmail labels with Thunderbird. Maybe there's a way, but I haven't been able to do so. Here, people say it's impossible:
    http://superuser.com/questions/48591/how-do-i-synchronize-thunderbird-tags-with-gmail-labels
    Nevertheless, the message is old. Perhaps, this issue might be already solvable with a plugin.
  • An Android icon next to the contacts in Google Talk. Actually, Thunderbird provides you information about the devices your contact is connected to. If you put the mouse pointer on the contact, Thunderbird retrieves this information. Nevertheless, I'd rather prefer the icon. I find useful to know if my contacts are chatting from an Android device, because that way I know I'll probably have to wait more for replies, links are going to be visualized on a phone and that sort of things.
  • I have to delete conversations message by message. Gmail allows you to remove an entire conversation easily. The good news is that deleting messages is synchronized between Gmail and Thunderbird.
Feel free to post a comment if you've found this post useful or have any suggestions to solve any of the problems I still experience.

Wednesday, October 3, 2012

Making Magic Number Cards


The following video has inspired me to write this post:


I've automatized the number card generation.
Screen shot for “2 to the power of n” option:
10-3-2012_1

Screen shot for “Fibonacci” option:
10-3-2012_2

Screen shot for “primes” option:
10-3-2012_3

You can download the Python scripts by clicking here. (They're compressed in a zip folder).

Monday, August 20, 2012

The Hunger Games... Probabilities?


SPOILER ALERT. If you've not read The Hunger Games by Suzanne Collins or you haven't managed to get through the first two paragraphs of chapter 2, this post might spoil some important plot twists you might want to read yourself.

I'm reading The Hunger Games by Suzanne Collins. Although I've read only half of it, I'm enjoying it. However, I can't help it. I need to post about it... with the usual probabilistic approach. I'm going to write about the odds of being elected as a tribute in the day of the reaping in District 12. The trigger? The following fragment in chapter 2:

There must have been some mistake. This can’t be happening. Prim was one slip of paper in thousands! Her chances of being chosen so remote that I’d not even bothered to worry about her.

INTRODUCTION


Just as a reminder, lets see what the rules are:
    • Each member of any district between 12 and 18 (both included) participate in the game.
    • Every year, the participants have an entry for the game.
    • Entries are cumulative. So, your name is in the pool once at 12, twice at 13, three times at 14, …, and seven times at 18.
    • You can add more entries (cumulative, remember) in exchange for tesserae: "Each tessera is worth a meager year’s supply of grain and oil for one person". It is convenient for people who are starving because they get food, and for rich people because it gives them statistical coverage. For example: Gale, being 18, participates with 42 entries, for every year he's traded 5 additional entries for tesserae in order to sustain his family.

ESTIMATING DISTRICT 12 DATA


The book doesn't provide the actual number of people living in the district. It doesn't provide the entries signed for the reaping day either. It just says that the population of District 12 is about 8000. Knowing that District 12 is quite a poor place, I've decided to transform the population distribution of a poor country to simulate District 12's population pyramid. I've chosen Burundi for it's poverty levels (no evil purpose, neither any other similitude with District 12).

This is Burundi's 2005 population pyramid for male population (according to Wikipedia).


Not having the exact data shown in the pyramid, we have to extract it manually. I've measured the length of the bars of each age group using the Measure Tool in Gimp. I approximate the result using only 2 decimal digits. I've measured only the left side of the pyramid and I assume it is perfectly symmetric. The extracted data for one sex is found in the following table.
 
Age
Population
[0, 4]
0.72 millions
[5, 9]
0.6 millions
[10, 14]
0.51 millions
[15, 19]
0.44 millions
[20, 24]
0.37 millions
[25, 29]
0.29 millions
[30, 34]
0.23 millions
[35, 39]
0.19 millions
[40, 44]
0.15 millions
[45, 49]
0.13 millions
[50, 54]
0.1 millions
[55, 59]
0.07 millions
[60, 64]
0.05 millions
[65, 69]
0.04 millions
[70, 74]
0.03 millions
[75, 79]
0.01 millions
80+
0.01 millions


The total population for one sex: 3.94 millions.


Now, I assume that the population pyramid of District 12 is also symmetric (i.e. 4000 for one sex). The transformed table would be like this.

Age
Population
[0, 4]
731
[5, 9]
609
[10, 14]
518
[15, 19]
447
[20, 24]
376
[25, 29]
294
[30, 34]
233
[35, 39]
193
[40, 44]
152
[45, 49]
132
[50, 54]
102
[55, 59]
71
[60, 64]
51
[65, 69]
41
[70, 74]
30
[75, 79]
10
80+
10

I need to get the specific population for 12 year old, 13 year old, …, and 18 year old people. In order to do that, I'll express the previous table with it's accumulated values.

Age
Population
[0, 4]
731
[0, 9]
1340
[0, 14]
1858
[0, 19]
2305
[0, 24]
2681
[0, 29]
2975
[0, 34]
3208
[0, 39]
3401
[0, 44]
3553
[0, 49]
3685
[0, 54]
3787
[0, 59]
3858
[0, 64]
3909
[0, 69]
3950
[0, 74]
3980
[0, 79]
3990
TOTAL
4000


And now, I need a function that describes this behaviour. If I had such a function, I would be able to extract the values for a single age. We know the following points of the function:

[0, 0], [5, 731], [10, 1340], [15, 1858], [20, 2305], [25, 2681], [30, 2975], [35, 3208], [40, 3401], [45, 3553], [50, 3685], [55, 3787], [60, 3858], [65, 3909], [70, 3950], [75, 3980], [80, 3990], [83, 4000]

As you can see, I've forced the last point a little bit (the oldest person is 83 years old). I don't think there are so many old people in District 12.

So, I need to interpolate. I'm going to use the implementation of Lagrange Interpolation I've found in this web page. However, since the web page itself doesn't allow me to use all the points, I'm going to use only up to x=30 (included) so the function will be more manageable. The result is:

f(x) = (3x⁶-270x⁵+7250x⁴-5000x³-5320625x²+300106250x)/1875000

In order to extract the ages of interest, I've made a Python script. You can download it by pressing here. The results I've obtained are the following:


661 kids participate in the day of the reaping.

Mmmm... I bet there's only one school in District 12... It makes sense, the mayor's daughter and Katniss went to the same school... But let's keep focused!


PARTICIPANTS AND ENTRIES


Each year every participant makes a new entry. You can find another Python script to calculate the mandatory entries. Screen capture with the results:


2566 entries! That counts as one in thousands... Either Katniss was exactly right or she was pessimistic (with pessimistic I mean the probabilities were actually lower, don't forget there are people that put more entries in exchange for tesserae).


TESSERAE


Taking tesserae into account might be a bit tricky... Nevertheless, this is my approach.

Returning to Burundi's case, Wikipedia states that 80% of the population lives in poverty. I'll extrapolate it to District 12. So, those 80% would need tesserae. However, it's not told in the book, but it suggests that usually the older brothers who can participate in the Hunger Games are the ones who asks for tesserae for the rest of the family in order to prevent the young ones of having higher probabilities of being elected (Katniss and Prim both live in poverty, but Katniss risks in exchange for both Katniss and Prim's tesserae, instead of distributing the risk). So, I'll say that only 60% are going to ask for more tesserae. I'm going to consider Gale's case extreme. This is what I think it could be a reliable distribution:

  • 40% asks for no tesserae.
  • 25% asks for one tessera.
  • 15% asks for two tesserae.
  • 10% asks for three tesserae. (Katniss belongs to this group).
  • 7% asks for four tesserae.
  • 3% asks for five tesserae. (Gale belongs to this group).

That being said, the entries are corrected (another Python script) and the new results are:


Katniss was right! Incredible! I envy her math skills! Nevertheless, probabilities speak about the uncertain. Therefore, until we know the results, anything can happen! Prim could have been elected as well as Katniss, for her probabilities are higher than 0. I bet the author put the words in Katniss' mouth just to reflect the adolescent indignation with the world, which in my opinion it's very well portrayed in the book. Beating around the bush again, sorry.


PROBABILITY OF BEING ELECTED IN A LIFETIME


I'm going to do one final calculation: the probabilities of Prim being elected at some point in her life. The result will be the same for any kid who doesn't ask for any tesserae. For this, I assume the number of participants remains constant in time (i.e. each year there are exactly 661 participants and 5664 entries). I don't know if this assumption is correct, because I don't know the birth rate, and the mortality rate of District 12 (poverty could also vary from year to year); but I'll assume the age distribution remains constant in time.

The probability is:

1 – [(1–(1/5664)) x (1–(2/5664)) x (1–(3/5664)) x (1–(4/5664)) x (1–(5/5664)) x (1–(6/5664)) x (1–(7/5664))] = 0.004933476478782839

0.4933% is the probability of being elected as a tribute if you don't ask for more tesserae.

I hope you enjoyed this post. It's a little bit longer than usual...