Tuesday 15 June 2010

Tools in Python

There's been some time since my last update, so I've decided to write a new post to prove I am still alive and kicking.

However, the second post about Wall Chaos will have to wait a bit more, as I haven't finished writing it yet. Instead, I will show you some tools developed in Python that I have coded to assist me in the development of TINC (the first two scripts), and while I was playing Harvest Moon: Island of Happiness on my DS (the third one). The link to get the source code for the three of them is at the end of this post.

The reasons why I chose Python were that I wanted to get better at it, and mainly because it is a language I feel comfortable programming in. Besides, it is widely used in the games industry for tools or scripting, which is always a plus.
The first one is a simple level editor. If you remember TINC from previous episodes, it is just a clone of the Columns puzzle game. While I was designing test cases for it (yeah, I know...a bit over-engineered for a puzzle game) I got bored of editing text files by hand, so I came up with a GUI application. It may seem a little counter-intuitive at first sight, but it is quite easy to use in fact:
Select the checkboxes under the cell(s) you want to set and then pick a colour on the right combo box(you can choose "EMPTY" to clear the cells). The left-most checkboxes will toggle selection on a full row. Likewise, the top-most checkboxes will select/deselect full columns. Last, the checkbox at the top left corner will toggle selection on the full board.

I used wxPython as the library of choice for the graphical user interface. The main frame consists of two separate panels, the left one displaying the board and a grid of checkboxes to select cells, rows, columns or the entire board, and the second one displaying further functionality, such as colour selection, board clearing and exporting the board to a text file. 

Aside from the usual tasks related to GUI programming (layout, event binding, etc.), the most challenging part of the application, to name any, was the test for valid boards, since the program won't allow the user to export an invalid board.
A board is considered valid when there are no gaps left below a coloured block. For instance, the first screenshot corresponded to a valid state.

...whereas this one does not:
If an attempt is done to export the layout when the board in in an invalid state, the application will complain:
The validity check is quite straightforward. The board, initially assumed empty, is considered valid. After that, we'll start going through the columns as long as the current column is valid. To ensure that, the algorithm will start on the top-most row, and then it'll go down until the first coloured block has been found. If the column is empty, it'll be OK. If we find a coloured block, then every other block located in the rows below it must be coloured as well, otherwise the column, and therefore, the whole board, is considered to be invalid.
On the application, this check is performed when clicking on the "Export" button.
You may find the full code  under the "editor" subdirectory in the linked archive at the end of the post.
The second script that I coded while developing TINC is a bit more complex despite the fact that it does not come with a fancy user interface, as it is a command line tool.
You may find the full code  under the "editor" subdirectory in the linked archive at the end of the post.
Sometimes, when you're using enums in C++ (and other languages) you may need a text translation of the value. For instance, when logging messages, a string is more human-friendly than the enum's integer value, hence the need for this script (again, I didn't feel like browsing through all the enums and then writing the string arrays by hand, so I did some coding to automate the process).
It parses a specific header file where I keep type redefinitions, constants and others, searching for enumerated type definitions. Then, it creates a second header file, now containing several arrays of strings (one array for each enumerated type), and a set of matching inline functions, to translate a value of a certain enum to its matching string value. This screenshot depicts on the left side the source file, and on the right a portion of the generated translations file:



The script source code is now a bit more complex, specially on the parser side, relying on regular expressions to find enum declarations and such. The code relies on two classes: Context, which is actually the class in charge for parsing the source header file, and Generator, responsible for creating the enum translations header file.
Check the code under "enum_names.py".

My last tool is actually a lookup application I did to avoid checking the WWW when I was playing Harvest Moon: Island of Happiness, a game by Natsume and Marvelous for the DS. 
In case you don't know the HM series, these games put you in the place of a boy (or girl, depending on the game: the most recent instalments let you choose) that accepts a position as a farmer on a certain town. You'll get to develop the farm by growing crops, tending cattle, chopping trees, mining, fishing, cooking and flirting among several other tasks (if you've ever played Facebook's Farmville, it is a bit similar, but WAY MORE complex and a lot funnier).
It turns out that there are lots of recipes you can make by using several ingredients: the crops you've grown, the fish you've caught, and many others. At some point during my play-through I decided to fill my 'discovered/delivered recipes' (you can get a grasp of your progression throughout the game by checking the list of delivered crops, animal raw materials, gems, recipes, etc.), and I needed some quick reference about the ingredients I might need. As I said before, I wasn't in the mood for keeping an Opera tab constantly open to check every now and then, so I decided to develop a query application.
The GUI lists the set of recipes matching the criterion filter (you may search by the ingredient you need to deliver to unlock the recipe, or by one of the ingredients the recipe consists of), together with some additional information (name, description, stamina recovery, etcetera). The rows on the grid may be selected, and the text area below will display a list containing all the ingredients the recipe needs.
Again, I used Python and wxPython for the GUI. In addition, the application uses an sqlite embedded database where the recipes, ingredients and related data are stored. One of the python modules of the application contains the SQL code to create and fill the tables. I suggest using sqliteadmin, too, to manage the database. 
I intended to make the application multilingual, hence the initial language selection dialog, but it isn't functional, so at the moment Spanish is the only available language, sorry.
And, finally, here is the Dropbox link to the *.7z file containing the code for the three tools. Feel free to modify them at will. Credit will be nice, of course ;P
http://db.tt/YwdUywWD