Finally scoring some points

Here we are at Day 26 of this year’s Devember (Boxing Day, if you’re in a part of the world that celebrates that) and after “boxing” a couple more bugs, we have a playable prototype of our little game; scoring is now in. This completes the tasks for having a human and computer player, alternating turns (where possible), scoring points to see who wins. This is still crude in that there is no game over screen just yet, and there should be three rounds and not one (to mention just a couple), but nevertheless, progress!

Here’s a twitter post where I showcase all of the new functionality (which up until today hasn’t been shown completely due to various small bugs that I had to leave dangling at the end of the day):

The flow of the game so far is that the the two players alternate turns, pushing balls into the maze. If a player has no valid move, their turn is skipped. If both players have no valid move, we move into the end game phase. Here we first find and remove all balls that have no chance of further movement, then remove all of the gray bricks, and then finally move all of the balls that remain through the maze one at a time, ending at a final score.

Points are scored as follows:

  • 10 points for collecting a bonus brick; this is applied to your score immediately
  • 60 points for getting a ball all the way to the goal (the bottom of the maze); this is applied to your score immediately
  • 2 points/row for any ball that ends its travels in the maze not at the goal line; this is calculated from the final resting position and is not cumulative. Due to when this is scored, these score values are not added until the final phase of the round when the balls are moved one last time.

Before I could get this fully working, I had to tweak the code that I implemented yesterday for removing the blocked balls just prior to the final ball move. There was a bug in this code that I didn’t detect until I was trying to record the animation to show, so I didn’t bother for the night.

The change required that we distinguish between a hidden ball removed from the maze because it had just finished moving and a hidden ball being removed because it is blocked and can no longer function (as there is a different event for that situation). I used the wrong flag in my check, which had the effect of stopping the null pointer exception it was meant to, but which stopped the actual event from firing.

I also did a small bit of refactoring:

  • The Ball entity now has a player property that tells you what player owns it
  • The ballDropComplete event no longer takes a boolean flag that indicates if the ball reached the goal and instead the boolean is an indication as to whether this ball drop was a final ball drop (at end of round) or not

With these changes in place, I implemented some simple scoring routines. These are currently global functions (although still namespace’d to stop any true global pollution; I’m a heathen, not an animal) and invoking them modifies the appropriate score value by the appropriate amount. This allows for us to create functions to score something and have all of the score generation code in one place for easy modification.

Using the events that were created along this long, arduous process, we can score points when needed. The bonus tile collection is easy since there is an event for that. When a ball drop completes, we score if the ball reached the goal or using a multiplier if the ball did not but this is a final drop. This partial scoring also applies when a blocked ball is vanished out.

Now that this is done I want to go back and tweak the level generation a little bit before continuing forward. Having played around with the code as a whole more since it was originally created I realize that it was a mistake to generate the maze content row by row; it should instead be generated column by column.

The current method leaves clearly unobstructed paths, since we can’t guarantee that every column in every row has something generated into it. If we instead generated column by column, we would be more assured that there is no simple path for the ball to take through the maze.

There are also a couple of outstanding bugs; ball that stop on a black hole get “lost” in that they’re no longer stored in the maze, but are still visually there. I’m currently pondering a couple of ways to repair that situation, but I want to look into exactly what sequence of events is happening there first. Also, sometimes it’s possible for the human player to try and push a ball that’s blocked; visually nothing happens, but the internal state gets out of sync. I’m pretty sure this is just a doofus mistake of not checking the return value before assuming that the push worked, but I haven’t checked yet.