At a recent checkin, there were 486 tests defined for Ighalsk, which took 31.8 seconds to run (on my old laptop: 500 MHz, Win98). I'm happy with the number of tests, but would like to decrease the time somewhat (still too many file accesses).
A Google spreadsheet showing some numbers and times for different revisions is available here. I don't know why the time went down to 9 seconds and back up again almost immediately, but I think the dip from 23.3 seconds to 20.9 seconds was just after some test refactoring to speed up tests - probably replacing file I/O with StringIO (see previous post).
Also, I now have a Twitter account here - any updates to these numbers/this spreadsheet will probably be posted on Twitter first (and possibly only). I'm working on increasing the popularity of #roguelike ...
Monday, November 30, 2009
Thursday, November 26, 2009
Ighalsk and Motivation
Ighalsk is not the first software project that I've worked on at home, or even the first game. But it is easily the most mature and furthest along. With earlier projects, I've had a great idea, started working on it, got a few things done, and then my enthusiasm has slowly worn off and I've given up.
With all my projects, I've only had an hour or two to spend on them in a night, and some nights that dropped to zero. Often in this time I'd get started on a task, but wouldn't have time to finish it - I might find a bug, or the task might be more complex than I'd planned for. Then I'd need to wait a day or more before I could work on it again. This was frustrating, and it made me feel like I was going very slowly.
With Ighalsk, I've generally been able to get something finished each night. Part of the reason is that I'm using Test-Driven Development (TDD) almost all the time. This means that my work cycle is usually: write a new test, run it to check that it fails, write just enough code for it to pass, check the changes in. A lot of the comments that I make when I check in changes start with "Added test + implementation of ..." Similarly when I refactor, each refactoring is small and I test it before I go further, so that when I do make a mistake, I can go back and fix it easily. It helps that the full set of tests runs in less than a minute (though I still want to speed them up further over time).
TDD has meant less bugs - or more to the point, that it's quicker and easier to fix bugs - and frequent refactoring (not to mention using Python!) has meant simpler code. Both of these factors have also helped me finish tasks more quickly and feel like I'm making progress.
Motivation has been less of a problem lately; I've got to the point that I have a playable game, where it's easy to see how any new functionality can fit in and will be usable immediately.
Doubtless it's also helped that I don't have any new games that I want to play at the moment - though often ProgressQuest is running in the background while I work on Ighalsk, and I've started playing FreeCol years and years after I last played Colonization ...
With all my projects, I've only had an hour or two to spend on them in a night, and some nights that dropped to zero. Often in this time I'd get started on a task, but wouldn't have time to finish it - I might find a bug, or the task might be more complex than I'd planned for. Then I'd need to wait a day or more before I could work on it again. This was frustrating, and it made me feel like I was going very slowly.
With Ighalsk, I've generally been able to get something finished each night. Part of the reason is that I'm using Test-Driven Development (TDD) almost all the time. This means that my work cycle is usually: write a new test, run it to check that it fails, write just enough code for it to pass, check the changes in. A lot of the comments that I make when I check in changes start with "Added test + implementation of ..." Similarly when I refactor, each refactoring is small and I test it before I go further, so that when I do make a mistake, I can go back and fix it easily. It helps that the full set of tests runs in less than a minute (though I still want to speed them up further over time).
TDD has meant less bugs - or more to the point, that it's quicker and easier to fix bugs - and frequent refactoring (not to mention using Python!) has meant simpler code. Both of these factors have also helped me finish tasks more quickly and feel like I'm making progress.
Motivation has been less of a problem lately; I've got to the point that I have a playable game, where it's easy to see how any new functionality can fit in and will be usable immediately.
Doubtless it's also helped that I don't have any new games that I want to play at the moment - though often ProgressQuest is running in the background while I work on Ighalsk, and I've started playing FreeCol years and years after I last played Colonization ...
Friday, November 20, 2009
Ighalsk - releases 0.1.9 and 0.1.10
I've just released Ighalsk v0.1.10: you can download the source code here. This blog post also covers the changes in v0.1.9, since I didn't write a separate blog post about those.
Changes in v0.1.9:
* Weapons, attack powers and monsters now all do random damage (using a new Dice class). As discussed in the previous post, a Dice object can be created with a string such as "4d5+6". There's a little bit more to it than that; for example, when returning the string (which is used in the character screen or when examining a weapon), zeroes are dropped: "2d3" looks much nicer than "2d3+0".
* Savefiles now use compressed versions of the Hero and the current level. This reduces the size of the savefiles by a factor of about 8, and should improve savefile stability between releases (e.g. changes to methods in a class shouldn't affect the savefile). More savings are possible: currently (in v0.1.10, I should say) the walls in a level are saved as a list of booleans, which takes a number of characters per wall. They could instead be packed into 16-bit integers (just for saving and restoring - for speed, they'd still be a list of booleans during play).
Changes in v0.1.10:
* Refactored Level to use a list of booleans for walls. This speeds up level generation and also simplifies the code: there was a Square object for each square, doing nothing but holding and making available a boolean (whether or not that square contained a wall).
* Printing level to screen is now done in 2 passes for speed: the first for walls, the second for everything else. This is because everything else is sparse: that is, stored as a list of objects with positions, while the walls have one value per position. (Checking the list of monsters in a level for each square to see if it has a monster in it was taking some time.)
* Extracted resists of Beings, Families and Armour to a separate Resists class for ease of adding resists and returning the resist for a given damage type. This removed duplicate code between Being and Adventurer, and also simplified the size of both classes.
* Added BoostPowers which improve resistances temporarily, and Boosts to hold current improvements. Any Being can have boosts theoretically, though in practice only the Hero can, if she or he uses a boost power on herself or himself.
* Blessed and Mighty Heroes can now use a boost power: "Tenacity" and "Courage" respectively.
* Fixed bug 2889190 where up and down were reversed when using keys on the numeric keypad: thanks to Genosha for raising this bug.
* Added 7 new Monsters: Lesser Haunt, Paranoia, Zombie, Ivy, Flatworm, Ghost Spider, and Greater Huntsman. (The Zombie here is a common, stock-standard zombie; variants will be added later.)
* Added Monsters for level 3 of each quest.
* Characters saved with release v0.1.9 *can* be loaded using this release! (I've only tested this for one character, but I don't expect any problems for special cases.)
I've now implemented most major pieces of functionality that I was planning to add before releasing v0.2.0. The exceptions are adding a Level Editor and special levels (level 5 of each quest) - the plan is to use the Level Editor to generate the special levels :). Of course, there's a bunch of tidying up and refactoring to do, plenty of minor functionality that could be added, content to add (new armour and monsters are the most critical parts) and no doubt bugs to fix.
Changes in v0.1.9:
* Weapons, attack powers and monsters now all do random damage (using a new Dice class). As discussed in the previous post, a Dice object can be created with a string such as "4d5+6". There's a little bit more to it than that; for example, when returning the string (which is used in the character screen or when examining a weapon), zeroes are dropped: "2d3" looks much nicer than "2d3+0".
* Savefiles now use compressed versions of the Hero and the current level. This reduces the size of the savefiles by a factor of about 8, and should improve savefile stability between releases (e.g. changes to methods in a class shouldn't affect the savefile). More savings are possible: currently (in v0.1.10, I should say) the walls in a level are saved as a list of booleans, which takes a number of characters per wall. They could instead be packed into 16-bit integers (just for saving and restoring - for speed, they'd still be a list of booleans during play).
Changes in v0.1.10:
* Refactored Level to use a list of booleans for walls. This speeds up level generation and also simplifies the code: there was a Square object for each square, doing nothing but holding and making available a boolean (whether or not that square contained a wall).
* Printing level to screen is now done in 2 passes for speed: the first for walls, the second for everything else. This is because everything else is sparse: that is, stored as a list of objects with positions, while the walls have one value per position. (Checking the list of monsters in a level for each square to see if it has a monster in it was taking some time.)
* Extracted resists of Beings, Families and Armour to a separate Resists class for ease of adding resists and returning the resist for a given damage type. This removed duplicate code between Being and Adventurer, and also simplified the size of both classes.
* Added BoostPowers which improve resistances temporarily, and Boosts to hold current improvements. Any Being can have boosts theoretically, though in practice only the Hero can, if she or he uses a boost power on herself or himself.
* Blessed and Mighty Heroes can now use a boost power: "Tenacity" and "Courage" respectively.
* Fixed bug 2889190 where up and down were reversed when using keys on the numeric keypad: thanks to Genosha for raising this bug.
* Added 7 new Monsters: Lesser Haunt, Paranoia, Zombie, Ivy, Flatworm, Ghost Spider, and Greater Huntsman. (The Zombie here is a common, stock-standard zombie; variants will be added later.)
* Added Monsters for level 3 of each quest.
* Characters saved with release v0.1.9 *can* be loaded using this release! (I've only tested this for one character, but I don't expect any problems for special cases.)
I've now implemented most major pieces of functionality that I was planning to add before releasing v0.2.0. The exceptions are adding a Level Editor and special levels (level 5 of each quest) - the plan is to use the Level Editor to generate the special levels :). Of course, there's a bunch of tidying up and refactoring to do, plenty of minor functionality that could be added, content to add (new armour and monsters are the most critical parts) and no doubt bugs to fix.
Subscribe to:
Posts (Atom)