Happy !

shakedown / Galclone

Project infos

License Public domain
Tags
Creation date 2013-02-18
Website

Monticello registration

About Galclone

Galclone

A Galcon clone in Smalltalk by Nate Wolfe.

Conquer the enemy in a planetary Risk-like game of speed and strategy.

Galclone is a single-player graphical top-down game where you take control of enemy Planets by attacking them with Fleets of Ships that your Planets have produced. The game is over when either the player or computer no longer control any Planets - last man standing.

The game starts with several Planets randomly positioned on the screen. The player and computer each own a single Planet with all others being neutral. Each Planet has a number on it, which denotes the number of Ships available on the Planet - this is effectively the Planet's "life". Ships are produced at varying rates on each Planet during the game tick. Each Planet also has a color which represents its owner - the player, computer, or neutral. A neutral Planet starts with a random number of Ships, but has no owner and no production rate, and is nothing more than a Planet to be captured by a player.

The primary action is to send Fleets of Ships between Planets. The player does this by selecting the Planets to send Ships from, then selecting the Planet to send them to. When the Ships arrive at their destination, combat will occur, which simply results in subtracting the Fleet size from the Planet's number of available Ships. If the attacking Fleet has more Ships than the defending Planet, ownership of the Planet will transfer to the attacking player, and the number of Ships available on the Planet will become the difference in Ship count between the attacking Fleet and the defending Planet. That is, one attacking Ship will destroy one defending Ship, and once all of the defending Ships have been destroyed, any remaining attacking Ships will "land" on the Planet and ownership will transfer to the attacking player.

Since there are no turns, the player must constantly be attacking other Planets (or reinforcing ones they already own) in an effort to capture all the enemy Planets faster than the enemy can conquer new ones. When deciding which moves to make, the player must take into account the distance (and thus travel time) between the Planets, as well as the production rate of those Planets.

Use Cases or User Stories

I have a hard time coming up with what would be the Use Cases for something like a game, but as far as interactions with the application go, there are really only two conceptual moves that a user can make.

A Player can perform the following actions:

  • Attack enemy Planet
  • Reinforce ally Planet

Both of these actions are initiated the same way by the user: the player first clicks on the Planet(s) to send Ships from, then clicks on the Planet to send them to. When the Ships arrive, if the destination Planet is an enemy or neutral Planet, combat will occur. Otherwise, the Ships will reinforce the ally Planet.

User Interface

As there aren't many interactions with the game, the user interface is minimal and only the mouse is needed to play. When the user starts the game, a window will open up with the game in a "ready and waiting" state. Planets will be randomly positioned within the window, and once the user clicks the screen to begin playing, the game starts ticking. Planets are represented simply as colored circles of varying sizes, and Ships are smaller colored circles with constant size. There are no buttons or other UI elements on the screen - only the window with Planets and Ships within it.

Development Phases

The goals of each phase are described below.

Phase 1: Prepare for Game - At the end of this phase, the player should be able to start the game, causing a window to appear on the screen with several Planets positioned randomly inside. The Planets should tick varying intervals, causing the number on them to increment.

Phase 2: Travel between Planets - At the end of this phase, the player should be able to send Ships between Planets by clicking on the source Planet(s) and then clicking on the destination Planet. A Fleet of Ships should appear next to each source Planet and immediately begin moving toward the destination. When each Ship arrives at the destination, it is deleted and nothing happens.

Phase 3: Combat! - At the end of this phase, the player should be able to attack or reinforce other Planets. If a Fleet arrives at an enemy or neutral Planet, they should attack it by reducing the population. Otherwise, they should reinforce it by increasing the population.

Phase 4: Artificial Intelligence - At the end of this phase, the computer player should be incorporated in and be able to play the game along with the human player.

Phase 5: End Game - At the end of this phase, either player should be able to win the game by capturing all of the opponents' Planets. When the game ends, the winner is declared and the game restarts.

Architecture

The entire underlying game system was created iteratively using Test Driven Development.
As such, the game can exist and be played without any UI on top of it. Nearly all of the encapsulated state is configurable to allow for proper testing, though many of the resulting messages are not used during the game.

The game state and logic is implemented in the Planet, Fleet, and Game classes, and all "gameplay" interactions are done through the Game. To support testing and keep the code simple, these classes do not enforce any game behavior; they are merely containers for state and related state transformations. For example, there's nothing that would stop you from modifying the state of a Planet in a way that doesn't make sense in the context of this game, like dispatching 100 ships from a Planet that only has 10. The Game will manage the Planets, Fleets, and Players in the game, but it does not advance the game by itself, that must happen externally.

Unlike most games, the UI is event-driven and is updated as the game state updates. The graphical representation of the underlying model is implemented in the PlanetMorph, FleetMorph, and GalcloneWindow classes. These follow the standard model-view separation, and as such there is a one-to-one correspondence between a Planet/Fleet/Game and PlanetMorph/FleetMorph/GalcloneWindow, respectively. For example, when a new Fleet is created, the UI will create a FleetMorph to be a dependent of the new Fleet. Whenever the Fleet changes state, it notifies its dependents (a FleetMorph) so they can update themselves to reflect the change. Similarly, the Game will notify its GameListeners whenever a Fleet is added or removed from the game.

The Artificial Intelligence, along with support for the human player, are all handled by the various Player classes. A Player is responsible for taking a turn when asked by the Game, and must manipulate the state of the game by sending Ships between Planets. A Player is provided with the Game instance when asked to take their turn, and it is the responsibility of the Player implementation to behave appropriately. There is nothing preventing a Player from maliciously reaching into the internals of the Game and its Planets and Fleets.

All of the components are wired together by the top level "main" class, Galclone. This class will set up the game window, create the Players, create the Planets, and begin the game. When the game is over and a player has won, the winner is presented and the window should be destroyed. As mentioned above, the Game will not "play itself", and thus the game loop exists in this class. In order for the game to be interactive and not freeze the UI, the game loop is performed in a separate process, managed by this class.

Post-Mortem

Development did not occur in the proposed phases, which were intended to introduce a vertical slice through the entire system, one at a time. As it was, the first few phases would involve fleshing out most of the system, and it became obvious that they did not divide development into meaningful steps. I ended up restarting the project a couple times, although each time I learned something new as I focused on areas that seemed to have high risk for problems.

I decided to cut development into horizontal slices rather than vertical, and the development phases ended up being:

  1. Create underlying game model
  2. Create UI on top of game model
  3. Implement AI to play against the human

Using TDD to develop the game was by far the most interesting, challenging, and beneficial thing about the whole process. This was my first time developing something from scratch using TDD. I experienced all the benefits of a TDD approach, and the more complicated the game and code became, the happier I was that I had an entire suite of tests to verify my changes. I was very good at doing things the TDD way: red, green, refactor. Writing unit tests first really forced me to think through what exactly I needed without thinking about how I was going to implement it. The resulting code and design was, in some areas, much different than what I would've done had I jumped straight into the "how" without first realizing the "what".
I had a little bit of TDD experience prior to this, but not for something this big and green. I knew that I would very much like TDD, but after experiencing it first-hand and witnessing how much more productive I can be with its safety net, I'm even more convinced that it should become the industry standard.

Understanding and implementing the mechanics of a game was a bit challenging. Even though some games are not turn based from the point of view of the player, the underlying engine must operate in a determined way, and thus must be "turn based" at the core. Figuring out how to represent such a high-speed real-time game in a turn-based fashion was a mental exercise, but the result is fairly obvious. I didn't get the chance to further separate the updating of the UI from the updating of underlying game engine, and as a result the game advances VERY FAST, which is evident in the speed at which the Planets produce ships. Future improvements could take explicit control of time and update the UI at a different rate than the game model, rather than just let the UI update only as a response to the game model changing.

The UI is very minimal, and could use a lot of improvement, but the necessary features are all there, and the underlying game system is ready to support any front-end improvements. For example, I would've ideally represented a Fleet of 10 ships using ten little Morphs all grouped together, but in the interest of time I settled for a simple rectangle with "10" on it. Some of the UI code isn't as clean as I would like it to be, but it is still small and understandable.

I was fortunately surprised at how easy it was to handle background processes/threads in Smalltalk, although the game window doesn't close correctly, which I'm assuming has something to do with it being called from the background.