Thursday 31 July 2008

Good, simple games... that I can rip off

All this game-related programming's got me reminiscing about good, simple games from years gone by; games that I might one day fancy plagiarising. Heck, maybe I could use them as practice for getting into WPF, XNA, or such like.

Anyway, here are a few games from the early 90's that've sprung to mind:

XQuest
Arguably the inspiration for Geometry Wars, this was an incredibly addictive little game in which you controlled a spaceship with your mouse, collecting crystals littered about the screen while avoiding mines and shooting the baddies that chased after you. The genius of XQuest, however, was that your ship's movement had inertia. The developer must've tuned it to hell and back, because it felt perfect.
Apocalypse Cow
Described as "a fast-paced action game in which two players drive catapults hurling cows at each other. Cows colliding in mid-air resolve their differences in Cow Boxing mode." Yes, it was a little surreal. It was also brilliant fun. And it featured cows. And sometimes glass ones.
Cheesy Invaders
An incredibly simple space invaders clone that was elevated to the status of cult classic by its sense of humour, graphics and sound effects.

Pity I can't find decent links for those last two. Hell, I'd still be playing them now, if I could. And that, to my ears, sounds like enough of an endorsement to make them worth copying. I just need the source material to reference... and graphics to copy... and sound effects to steal... ;-)

Do you know any other forgotten gems I can add to the list?

Wednesday 30 July 2008

Logging results from Beaver

At the end of my last little spurt on Beaver (careful...), I decided it was time I logged the results of more extended play-tests. Well, that didn't take very long. After 45 minutes or so, I'd played my top strategy off against itself a total of 3000 times — each time on a randomised board — and crunched the results in an Excel spreadsheet.

And the results at the end of this WarGames-esque madness...?

Player 1 wins:46.2%
Player 2 wins:39.7%
Draw:14.1%

Hmmmm. Looks like a fair indicator of first-player bias. :-/

However! It could just be a consequence of that particular strategy's characteristics. To be sure, I'll need to run the same test with more strategies. But... to be honest, I'm not sure I can be bothered. The chances of seeing a different result seem slim and there are other tempting directions I can take this prototype e.g. making it human-playable...

I suspect we're in for a period of hot procrastination action. Hey ho.

TDD and bottom-up vs top-down design

Recently, I got to the point in my Beaver prototype of having developed the game components (tiles and board), the scoring algorithm, and the rules for valid tile placement. However, I was struggling with where to go next. Up to that point, I'd been neck-deep in TDD, but I couldn't see a natural next step that didn't require a lot of architectural planning.

I realised that all the TDD I'd been doing — and all that I'd ever done, for that matter — had been bottom-up design. And that this sometimes made me incredibly uncomfortable with it. Often, I simply can't predict what little class or method I'm going to need next. Certainly not when I'm sat staring at unit tests.

In this particular instance, I had to take a step back and code the following, much higher-level test:

[Test]
public void PlayGame()
{
  Player playerOne = new Player(TileSet.Side.Good, new IdiotStrategy());
  Player playerTwo = new Player(TileSet.Side.Evil, new IdiotStrategy());
  Player.PairOpponents(playerOne, playerTwo);

  Board board = new Board();

  Player nextPlayer = playerOne;
  while (playerOne.InTheGame || playerTwo.InTheGame)
  {
    nextPlayer.PlayTurn(board);
    if (nextPlayer.Opponent.InTheGame)
    {
      nextPlayer = nextPlayer.Opponent;
    }
  }

  ReportScore(board);
}

As you can see, it isn't much of a test at all. There's not a single assertion in there. However, it did clear up in my head how a game would play out and what new classes I was going to need. At the time of writing the test, neither the Player class nor the IdiotStrategy class existed. And so the code flowed from my fingertips once again.

By doing a small amount of top-down design and forgetting about the legitimacy of the test, I'd broken through a mental blockage. If I ever needed it, it was yet another reminder that no single approach should be adhered to dogmatically.

So say we all. :-)

Tuesday 29 July 2008

Self, self, self

One thing I want to do with this blog is stimulate some debate. It all feels a bit pointless without feedback; I want to learn stuff by sharing experiences. However, demanding comments is no good. Instead, I'll demand ideas on what you'd actually find interesting. Well? ;-)

Incidentally, I realise the most interesting thing would probably be to post prototypes of the things I'm working on. Sadly, the importance of preserving IP craps all over that. Well, at least until I decide the IP's worthless. ;-)

So, IP restrictions aside, what would make a debate-worthy topic for you? Agile methods? Forays into AI? Game ideas? Something else? Shoot.

Taking a step back

Last night, I finally sat down at the computer again with intent to program. I had all my minimax and alpha-beta pruning print-outs scattered around me, but I chose to postpone the development of any new strategies. I think common sense had returned instead.

As mentioned in the previous development post, I already had a strategy programmed that played the game better than me. I also had a few progressively dimmer strategies that I could pit it against. I'd already played them off against each other multiple times and it appeared that there was a second player advantage (contrary to expectations). What I really needed to do was validate this.

One thing I could do was test more board layouts. The game board for Project Beaver isn't necessarily the same for every game, so I set about randomising it (using RNGCryptoServiceProvider, if you're interested). A couple of hours later, I'd done that, changed the UI code a little to support it, wasted time making a few new icons, and then pitted strategy against strategy over and over again.

Although I didn't record the results anywhere, it did feel like any previous advantage mostly disappeared with this randomisation of the board layout. So, what next? I think I might write a little bit of code to run the tests continuously, pitting each strategy against each other for maybe 1000 games each. Oh, and I'll log the results this time. :)

Only after that might I start thinking about minimax again. For the moment, however, I'm trying to keep in mind my overall aim with this project: find out whether the game has a first player advantage. Something tells me I don't necessarily need fancy schmancy algorithms for that, even if a wider range of strategies would help confirm results.

EDGE: Artificial Idiocy

I don't think I've mentioned it yet, but another factor in me returning to the keyboard outside work hours has been (the rather excellent) Edge magazine. It's an excellent mag that, as well as running the regular game reviews and previews, reports on the more technical side of the industry. A couple of months back, they ran an article looking into artificial intelligence in games: Artificial Idiocy.

Rather than bemoaning the quality of AI in many titles, it took a fascinating look into how game programmers intentionally introduce flaws into game characters' behaviour, both in the pursuit of humanity and playability. It's a great read, so I figured you might be interested too. That is all. :)

Tuesday 15 July 2008

State of play

So... where were we?

As it stands, I've completed stages 1 and 2 for both Aardvark and Beaver. That is, I've coded the board and tiles for each, and implemented the tile placement rules and scoring. As mentioned in another post, I'm concentrating on Beaver right now and have started on stage 3: implementing tile placement strategies.

To keep the project's momentum, I started with an incredibly thick strategy. All it did was find the first tile it could place anywhere on the board. Even then, it would give up if it couldn't find a suitable tile in the first few available to it. A good player it was not.

Despite the strategy's inherent stupidity, it was a moment of great joy when I pitted it against itself in the first ever computer vs. computer game of Beaver. And for the record, player 2 won. Yay for player 2! ;)

Actually, such was the sense of accomplishment at this minor victory that I stopped for the night there and didn't return to programming for over a week. So much for my momentum, eh? ;) At least it gives you an idea of my lack of dedication. Maybe now you understand how my projects usually end up in the coding graveyard. Hehe.

A week or so later and I returned to Beaver, implementing another 3 variants on this strategy, culminating with one that found the best possible tile to play, but without any kind of look-ahead. I'm sure that, in the States, it would be called the scoringest tile. Whatever, watching these fellas fight it out was humbling. Immediately, I could see they were playing better than I could. Bastards.

Anyway, that's where things stand. None of the strategies currently has any unit tests around it. I simply didn't know how I could write a decent test, as any one case would exercise a tiny fraction of the game space (if that's the right term). Furthermore, with the strategies themselves evolving rapidly, I didn't see the value in cementing them in place with tests.

Instead, the approach I took was to just watch them play in the bare-bones UI (more on that in another post, maybe). It's a pity I can't include any screenshots or video here, as it was pretty cool to watch. Thing is, while it shows promise, it's valuable IP, so I've got to keep it secret. Just trust me: it's fun to program, at least.

Anyhoo... next up, it's decision time. Up-front design of a minimax architecture, or more exploratory coding? Hmmm... I'm not sure, but I suspect the latter may win, as I can feel Dr Procrastination creeping up to finger my colon...

Downtime

Right, after the initial splurge of posts, this place is going to look dead for a while. Call it a planned outage. I'm off on holiday tomorrow for a week and there's a fair chance my blogging enthusiasm will have dwindled by the time I get back. However, I've already got another 3 posts in draft form, so it shouldn't be too long before I start up again.

See you soon. :)

Drawbacks of TDD?

As I get back into the swing of TDD, I'm rediscovering some of the things I didn't like about it first time round. While it's nice to have the rapid regression testing that it provides, I do worry about its effect on my code design.

When coding a lot of the rules for valid tile placement in Beaver, I started with simple test cases and worked my way up the complexity ladder. As I did so, the design of the production code became stretched to breaking point on more than one occasion. I ended up spending an inordinate amount of time refactoring and redesigning.

Sure, it was great that my encapsulation had shielded the tests from the changes I was making, but I felt frustrated at having led myself down so many dead ends. I wondered whether it wouldn't have been better to have done a bit more design up-front. As it was, my approach was killing momentum.

Still, I got through it. I'm left with code whose design still isn't brilliant, but it does the job reasonably efficiently. Next time, I'll not be such a slave to methodology. If I find myself coding things that probably won't stand the test of time, I'll just hack together some exploratory production code as quickly as possible and use it to decide on my direction. After which I can scrap/adapt it, and continue with TDD.

But what would you do? Has anyone out there reading this had similar experiences with evolving designs under TDD?

Project names

Before I go any further, I'm going to need names for these tile-based abstracts. So, without further ado, they are:

Project Aardvark
The one I started at university and later subjected to BDD.
Project Beaver
A more recent interest, subjected only to the rigours of TDD.

Currently, however, I'm concentrating my efforts on Beaver *cough*.

Automated playtesting

So, what's with the sudden interest in AI? Well, for the 2-player tile-based games, I want to see whether they have an inherent first-player bias. That is, does the person going first have a significantly better chance of being the winner than the player going second?

I could enlist a bunch of friends to play the game with me, over and over (like a monkey with a miniature cymbal), but I figure a computer's gonna be a lot faster and less resource-hungry. All I need now is some code that emulates the way human players play the game.

Now, I know this kind of intelligence isn't something I can rattle off in an hour or two. For the sake of my sanity, and to maintain my interest, I'm going to code progressively cleverer AI, starting with some incredibly simplistic stuff. That way, at least I'll get the positive feedback of seeing progress at regular intervals (a lack of which can consign projects like this to the graveyard in short measure).

These are the steps I figure I'll need to take:

  1. Develop code for the game components i.e. the tiles and the board
  2. Develop the rules for valid vs invalid tile placement, and for the scoring
  3. Develop strategies for placing the tiles
  4. Add some kind of human error and/or randomisation
  5. Set it off and log the results for different inputs (strategies, starting player, tile sets, board configurations, etc.)
  6. Crunch the numbers

Sounds simple enough. And I'm really looking forward to getting some hands-on practice with game trees, minimax and alpha-beta pruning, which I assume I'll need for the stategies. Wish I'd actually taken some notes at university now, instead of just trying to stay awake. Wikipedia to the rescue, no doubt.

Actually, it all sounds simple enough until we get to the stats at the end, but I'll worry about that later. :)

A brief history of my abandoned projects

First time round, I got all excited about making an online version of Carcassonne (a fabulous game, btw). I went about it all the wrong way and then found out that people had already done it, which kind of took the wind out of my sails.

Next time, I started to reimplement an old VB tile-placement game I'd half-written at university, this time using TDD and C#. In fact, I think I tried Behaviour Driven Development; something that drained my will to live entirely. Seriously, kids, don't go there. It blows.

Later, I gave the tile-placement game one last push, rescuing it from BDD hell, and finally got it playable. Pity, then, that it truly sucked. Hard. And that was the end of that bout of enthusiasm.

This time, I'm thinking of revisiting that last game, simplifying it and making it 2-player. I'm also playing around with another 2-player tile-based game. In fact, it's the 2-player aspect that's providing the real impetus this time. Back at university, I attended about 9 lectures on various aspects of artificial intelligence. And it BORED ME TO TEARS. Now, however, I actually want to use AI.

More on that in the next post...

Kicking off

Recently, I've spent a few weekends playing board games. Some of them have been Euro-games, some of them simple abstracts, but all of them have been pretty good fun. As a result, I've had my interest in hobby programming reinvigorated by the prospect of coding game logic.

Thing is, I've been here before. I get all excited for a week or so, before getting bored and disillusioned. I fully expect the cycle to see itself out again, but in the meantime I might as well enjoy it. And blog about it. Sporadically. :)

Let's go! And to hell with the site's appearance! :)