Puzzle app step 3 - revving up the engine

Having finished the level analyser, work starts on actually finishing the game. Read up on the process and try out the playable prototype here!

Excusing the sound of silence

And suddenly a whole month went by! But we haven't just been idling around playing Carcassone all day (at least not just) - this past month has seen several wheels being set in motion;

  • An iPad/iOS4-friendly version of Golingo is waiting in the wings! Come on, little brother, type faster!
  • We're awaiting Apple's approval for our next AppStore application [edit: app is now live, read announcement here] - an inofficial app for Systembolaget (where swedes go buy their alcohol, since we're not trusted to do so without government supervision). There already is one on the market, but our not-so-objective opinion is that we blow that out of the water since Titanium > ObjectiveC (and also good design > bad design).
  • Communications are underway with the manufacturers behind our very favourite boardgame regarding taking it to the iPad/iPhone (inspired by the brilliant appification of Carcassone, which we're absolutely not playing all day all the time). Hopefully we can spill some more beans on that in the somewhat near future!
  • Continuing the trend of placing ourselves at risk of severe starstruckness, we've also begun development of the official app for our favourite metal band. Huge amounts of fun, and having an excuse to communicate with these long-time heroes of ours is simply awesome. Further beanspilling to follow here too [edit: check the related posts here] . 
  • Our biggest pastime, however, has been involvement with a very promising startup, spearheaded by a driven entrepeneur duo with a great idea and the know-how to make it happen. Seeing these guys in action has been a privilige; we might have some badass technical knowhow, but direction, ambition, organisation, efficiency and all those adjectives - not so much. So being able to piggyback on them has been a very educative ride. And, you guessed it; more beans later. 

All this action invariably meant less playtime for our less serious (read: turnover-potent) inhouse project, which demoted our puzzle game to the back burner. But before Carcassone took us over completely things got too busy, we did managed to make quite a bit of progress, which is what we wanted to share in this post!

Analyse this

First and foremost, the analyser module is finished. It turned out to be an almost AliceInWonderlandesque journey into some rather esotheric recursive gymnastics, so if you're into that sort of thing, do dive into the code and check it out (the code repository can be found here, if we haven't already said so).

The idea is detailed in the previous posts, but mainly the idea was to have all game logic in an analyser function that calculates all possible states in a level, including the transitions between these states. The actual player module (yet to be finished, but a scaffold is up) therefore doesn't need to contain any logic, but simply renders the different states and moves the player between them as he gives his commands. This backward design means...

  • Beautiful separation of concerns in the codebase
  • We can programmatically check the integrity of a level, to see if it's actually solvable
  • Better yet, we can also calculate the difficulty, by checking
    • the minimum amount of steps needed to reach a perfect solution
    • how many different branches there are
    • frequency of dead ends and deathtraps
  • This also means that we can include a level editor in the game, and give immediate feedback to the level designer.
  • With some work it should also be possible to randomise levels.

Prototyping

Ok, enough babbling - here's the game in action (if the iframe doesn't play nice, here's a link directly to the demo), using our scaffolded and not-that-lovingly crafted player module. The level isn't that interesting, but serves its purpose as a proof of concept rather nicely. Rule-wise it is very simple; whenever you make a move, all player marbles will fall as far as possible in the chosen direction. 

Here's a quick rundown on the different objects in the level:

  • The yellow marbles are your player objects, which you move with the arrow keys (or touches if you're on an iphone/ipad). The game keeps going as long as at least one marble is still in play. In this version, nothing at all happens when two marbles overlap, as we couldn't decide if they should block or kill each other.
  • The goal is to get them all to the red heart (the meaning of life is love?). 
  • For a perfect solution you must also pick up all the valuables, of which there're two kinds:
    • A small coin, which is simply picked up as you pass over it
    • A large star, which is picked up but makes the ball stop in the process. 
  • The blue rotating asterisks are teleports, which immediately moves the marble to another position (but doesn't interrupt the motion).
  • The blue square is a block of ice, which will gradually break as you collide with it, needing two hits to disappear completely.
  • We're not quite sure what the yellow sun symbol is supposed to represent, but it stops the marble upon entry.
  • The green triangle is a locked door, which is impassable until you've collected...
  • ...the green digit 1, which is a key.
  • The curved wall will of course change the direction of the marble accordingly.
  • The red arrow can only be passed in the direction it is pointing, all other paths are blocked. Not sure if it is intiutive for it to block just the opposite direction or all three non-pointing directions, the jury is still out on that.
  • The grey square does nothing except blocking, and is thus pretty uninteresting and useless. We'll most likely not use it further.

The data structure in action

None of these behaviours are hard-coded into the game logic. Instead they're all defined in the level data object, which is made possible due to the abstract design we laid out in the previous post. Now, whether or not that was a good idea I'm still not sure, since it made the engine tougher to code. It does however allow for a lot of agility now, as it would be easy to think up a new labyrinth denizen and define its behaviour with pure data, without having to touch the logic code.

The behaviours are in essence a description of what will happen upon the collision of two objects. For example, here's the definition of a collision between a player marble (plr) and a key square:

Collision with the gate then looks like this:

While not rocket science, this system allows us to easily accomodate these new ideas as they come along. The initial idea was to allow the level editor to define new objects here, but probably we'll stick with predefining lots of objects, and limiting the editor to choosing between these.

The missing pieces of the puzzle

Obviously, there's still a long way to go before we have anything close to a finished product:

  • Although a little slab of (CSS-only!) paint made a huge difference, we need to work on the graphics. We're not at all sure how to approach this, but it will definitely still be CSS3-based as far as possible, meaning a simple yet hopefully elegant design.
  • We still have to make the app infrastructure to contain the game.
  • The aforementioned level editor is probably a rather huge project in and of itself, so that will also take some time.
  • As is the randomiser, if it is at all possible.
  • We also need some more ideas regarding in-game objects. Having multiple player balls is a good one, but we need to freshen it all up some more. Balls travelling against gravity, pushable boxes, the list goes on and on.

And since all the excuses we made at the beginning of the post applies for the foreseeable future as well, completion of the labyrinth puzzle game is definitely far off. But, we're slowly but surely getting there!

Also, again: Carcassone is a brilliant game! The application rendition really does the real-life counterpart justice. Buy it, play it, send fan-mail to the developers! 

Posted by David Waller 

0 comments

Leave a comment...