WordPress gives blog owners a lot of neat-o stats regarding their blog’s traffic. One of the reports lists the search engine terms people entered before clicking on your blog’s link when it came up. The site hasn’t received very many hits (only a few hundred total, most due to curious visitor’s from the Pygame website project I created for Python Chess), but there were some search engine terms like “pygame chess” or “python chess code” that led people here.
I thought, “Gee, that’s cool, you can get to my blog from Google!” So, naturally, I wanted to check it out for myself. I entered “python chess code” in Google, and, yep, there’s my blog down at spot #5! But, wait, what’s down there a few spots below that…looks awfully familiar:
Ha! I guess Softpedia must troll the pygame website periodically and copy some (all?) of the projects. I guess I don’t really mind too much, after all I did “release” the game under the GPL – not planning on selling it or anything, and I am acknowledged as the developer on the Softpedia page for Python Chess. But I am somewhat miffed that I wasn’t notified or asked about it before they went ahead and put my program up on their page. Softpedia’s ripping-off process doesn’t seem to be completely automated; there are subtle differences between the description of Python Chess I wrote on the pygame website and the description given on Softpedia’s page. So there’s some human in the loop somewhere along the way, I presume…would have been nice to get some notification or something from said human. But, I guess that would take too much of their time away – time that they could be spending ripping off other people’s programs!
Anyhow, the Softpedia page says there are 120 (!) downloads of Python Chess since they listed it (April 29th). That’s cool, hope ya’ll liked it! There are a handful of generally positive comments on the pygame Python Chess page; so I know some have downloaded it via that route as well. I guess I’m a published developer now! Woo hoo! 😉
Someone brought to my attention that Python Chess was using up a lot of CPU cycles, even when (actually, especially when) it was just sitting waiting for user input. I checked on my system and found it was using 30-40% of the CPU time…way too much for such a simple game!
Some googling revealed that I was using pygame’s event loop in a reckless manner. I was using pygame.event.get() to get user mouse clicks or other input — evidently, this will return a list of events that have queued up since the last time the get() function was called (not sure if there is a limit to the queue size….) Since the call to get() is basically a while(1) loop, it was being called just as fast as it could, which is pretty unecessary for a game like chess. Actually, probably for any game this is overkill. Luckily, there are a few easy solutions.
The option which I used for Python Chess is to use pygame.event.wait() rather than pygame.event.get(). The wait() function will cause the program to go into an idle state (ie. not hogging the processor) until it gets a single event. This is much more suited for the game of chess, where the user needs time to ponder their move, anyway. Replacing my event.get() calls to event.wait() makes the CPU usage of Python Chess go down to 0-1% on my system, with occassional spikes during the AI’s turn of up to 10% (these are just rough numbers seen via Windows Task Manager). Quite an improvement!
Another option for limiting CPU usage is to create a pygame.time.Clock() object, then calling the object’s tick(fps) function every time through your while(1) loop. This will limit the loop’s execution to fps (frames per second) times per second, allowing it to go idle in between loop processing. (Note that on slower systems, the desired fps may not be achievable, so it will still use up a healthy chunk of processing time if the value is too high.) Using time.Clock seems to be more suited to “action” style games, where there is screen movement going on every time through the loop, for instance.
No big changes in this version of Python Chess, just a few compatibility fixes. I decided version 0.5 was ready to be released to the world, so I posted it on the Pygame website: http://www.pygame.org/project/1099/?release_id=1960. And, at least one person actually downloaded and played! He pointed out some issues/fixes with running on Linux (I had just been developing with Windows).
So here’s the really, truly, cross-platform, Python Chess: PythonChess_v0.6.zip
I’ve added in some better AI functionality. In addition to version 0.4’s random AI, which just picks any random legal move, there is now a defense AI and an offense AI.
Defense AI’s GetMove() Priority
- Only make a “protected move” — don’t move a piece to a space where the enemy can capture it on the next turn.
- Put enemy king in check if possible.
- If a piece is in danger of being captured by the enemy next turn, move it to a safe place. There is a priority mechanism here — the AI will move its queen to safety before moving a pawn to safety, for instance.
- Pick a protected move that captures another piece. The same priority mechanism applies — capture the queen or other high-value piece before capturing a pawn.
- If no moves from prior steps, pick any remaining protected move.
- If no protected moves, revert to random AI and pick any legal move.
The offense AI is the same as the defense AI, except steps 3 and 4 are reversed. In other words, it will ignore its own pieces’ safety if there is an opportunity to harm the enemy. Maybe it should be called “berserk” mode. 🙂
In expanding the ChessAI module, I took the opportunity to play around with inheritance in Python. The ChessAI_defense class extends the ChessAI_random class, while the ChessAI_offense class extends ChessAI_defense. They each have a GetMove() functions, so I don’t have to worry which class my AI object is from when I need to get a move from it in the main game loop.
I also created a sideline program called PythonChessAIStats.py. Since I can pit one AI vs. a different AI, I thought it would be cool to see how well they do versus each other. This could be used in the future to rank new AI approaches. The AIs all rely on random numbers to some extent, so in order to make the tests repeatable, the script reads in numbers from a file and uses them to seed Python’s random number generator. (I used RANDOM.ORG to generate 100 random seeds.) Sometimes the AI vs. AI won’t end (they just end up moving the same pieces back and forth), so I end the game after 200 turns and call it a tie if no one is in checkmate yet.
Here’s a summary of some trials I did (X = no trial):
|Win – Loss – Tie||AI Opponent (black)|
|AI Player (white)||Random AI||Defense AI||Offense AI|
|Random AI||X||0 – 78 – 22||0 – 81 – 19|
|Defense AI||X||32 – 44 – 24||21 – 47 – 32|
|Offense AI||X||32 – 44 – 24||21 – 47 – 32|
The new AIs are clearly better than just randomly moving about, so that’s good. Interesting that numbers for Defense vs. Defense are the same as for Offense vs. Defense….
This version of Python Chess also has better command line parsing. I found out about Python’s OptionParser class and used it. Both PythonChessMain.py and PythonChessAIStats.py can be run from the command line with a number of options — the -h option should display all of the other available options and usage.
I tried to make a standalone windows executable using py2exe (there are a couple of scripts in the .zip file that try to do that), which is supposed to wrap up the code along with all of the required libraries and the python interpreter into an executable, so it can be run on a Windows machine without the user having to have everything pre-installed. Would be useful for producing a game for the masses, but it wouldn’t work for me. The script runs and builds an executable, but it crashes when I try to run it. I’ll have to investigate further.
The latest version of Python Chess is looking pretty slick!
I found the public domain chess graphics at Wikimedia Commons: http://commons.wikimedia.org/wiki/File:Chess_tile_pd.png. Adding them into Python Chess just required a resize, which Pygame handles very easily with the pygame.transform.scale function. I also added the labels around the board so it is easier to interpret the “move reports” in the scrolling text box on the right. Oh, and I also cleaned up those messages, too.
The other big addition is that I incorporated a little Tkinter, as promised. Before the main pygame window displays, a Tkinter dialog box pops up to get the game parameters — player names and types (human or AI).
In my previous post, I said something about perhaps changing my scrolling text box to something slicker and already implemented. Well, it seems that it is not possible (or at least tricky and not recommended) to use pygame in conjunction with a windows manager because of their conflicting event loops. I googled around and found some ways people can get it to work, but the methods all were dubious looking hacks. There is a window manager project created within pygame called PGU GUI which might be useful for future projects. For now, I’ll content myself with the Tkinter window at the beginning of my program and not worry about changing the scrolling text box.
Incidentally, while looking into Tkinter, I discovered wxPython. There is a comprehensive demo available when you download wxPython which goes through a lot of the possible gui functionality. Looks pretty amazing! There is a lot you can do there. In fact, I think that Python Chess could have been easily done in just wxPython (or Tkinter for that matter) and look just as good as it does using Pygame (Python Chess is not exactly a graphical powerhouse). I guess Tkinter is nice because it is a standard Python library. But Guido van Rossum (the guy who created Python), is quoted on the wxPython website as saying:
wxPython is the best and most mature cross-platform GUI toolkit, given a number of constraints. The only reason wxPython isn’t the standard Python GUI toolkit is that Tkinter was there first.
From what I can tell from the wxPython demo, I think agree! Now I want to use wxPython in a future project….
Here’s Python Chess, version 0.4: PythonChess_v0.4.zip
The only thing remaining to do is add in better AI!
Moving steadily along with Python Chess. Although it now looks completely different, the underlying game mechanics are the same. I used Pygame to do the graphics and user input via the mouse. I’m pleased with Pygame; it made importing and displaying images pretty easy. Most of the questions I had while coding were answered through the documentation on the Pygame website. Also on the Pygame website are a ton of examples of games (etc) people have made that you can look at to get an idea of how to do things. (I may put Python Chess up there when it’s finished.)
I spent about as much time getting the text on the right side of the chess board (“ScrollingTextBox”) working as I did getting the chess board to display right. The ScrollingTextBox uses Pygame to render fonts. It keeps track of how much info is in the window and bumps everything up when needed. It was more trouble to code than it’s worth…about half-way through, I figured there is probably an easier solution out there, but I kept on going and got it to work anyway.
A few things remain to do:
- Replace the current, rather simple, chess piece graphics with better looking images. (From the screenshot, it looks like the piece graphics might be rendered text, but they are actually imported .png files. I used Paint.NET to create 50×50 pixel transparent squares and added the appropriate text in the right color, then saved the files. Pygame imports the files and displays them as images.)
- Replace/add more options to the AI. The current AI is dumb. Really dumb. It just picks a random piece of its own color and moves it to a random (legal) space. (If you can’t beat it, then you probably don’t know anything about chess!) I think it would be interesting to implement a few different chess AI routines, then pit AI vs. AI over a set number of randomized trials (“Monte Carlo simulation”) and see some results – win %, number of turns taken, etc.
- Tkinter! I don’t have anything in the “GetGameSetupParams” function in the ChessGUI_pygame class. I’m thinking of branching out even further with this project and trying to use Tkinter to create a window for the user(s) to enter their name and select black/white, human/AI. I’m sure there is something like my scrolling text box already in Tkinter, too. I’ll probably try to turn this into a Tkinter project next.
I cleaned up the structure of Python Chess a bit. Before, everything was kind of haphazardly placed in a single file. Not very maintainable. Now everything is broken up into separate files. I tried to set up the program in such a way that it will be easier to make future modifications. Next up will be to replace the lovely ascii graphics with something a little nicer. After that, I’ll try to add in some AI.
So, gentle reader, you probably think I’ve been quite lazy. Not true – here’s a working game in Python! As you might guess from the title, I’ve made a little chess game. It is pretty simple and has no graphics. Ah, well – you have to start somewhere! Right now Python Chess supports two players; no AI opponent yet. Input is via keyboard entry.
It was pretty easy to code – 458 lines, took me about 5-10 hours (an hour here and there over a few days) from start to finish. Before starting, I had read through the tutorial on the Python website and consulted it when I needed to get specific syntax for something.
I didn’t have any large design errors, largely thanks to a solid understanding of the (relatively simple) game mechanics. The game of chess is pretty well defined in my head; for future development of other, more complex games, I think it will be more important to spend some serious time up front generating a design document. I didn’t do that for Python Chess because it’s pretty simple…just kind of added functionality as I decided it was needed. I know, I know…terrible programming practice!
Download: Python Chess version 0.1 (Note: for some reason, if you try to Right-click – Save As and download this file, Google Sites will scramble the file name. If you would like, you I put a zip file up as well…it’s name will get scrambled, too, when you save but when you unzip it the names of the files inside are intact.)
You’ll need the Python interpreter to run this file. If you don’t have it, you can get it for free from http://www.python.org/download/.