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.