Blog do projektu Open Source JavaHotel

niedziela, 27 sierpnia 2017

Civilization The Board Game

For some time, I became a fan of Civilization The Board Game. I found it more engaging, dynamic and enthralling than the computer game. It is like comparing real melee combat with the bureaucratic war waged behind the office desk.
And an idea stirred me up to move the game to the computer screen. Avoid the stuff piling up on the table, train and test ideas without spreading the board game and allow players in remote locations to fight.
For the time being, my idea ended up in two projects.
CivilizationEngine here
Civilization UI here
Demo version on Heroku:  (wait a moment until dyno is activated, it is a free quota).
Every project comes with its own build.xml file allowing creation of target artifact.
General design principles
The solution consists of two separate projects: Civilization Engine and Civilization UI. I decided that all game logic and state is managed by back end engine. The UI, as the name suggests, is focused only on displaying the board game and allowing the user to execute a command. The command is sent to the server, server changes the game state and UI is receiving the current game state and updates the screen.
The game is nothing more like moving from one game state to another. Every change is triggered by the command. At every moment, it is possible to restore the current game state by setting the initial board and replaying all commands up to the point.
Data is transmitted between engine and UI in JSON format.
Civilization Engine
Civilization Engine is created as IntelliJ IDEA Scala project, can be imported directly from GitHub.
Why Scala? I found it very appropriate here. Most of the operations are related to list walking through, list looking up, filtering, mapping and Scala is an excellent tool for that. If I decided to use Java probably the code would pump up twice even with Java8 streaming features.
I'm very fond of this command (full source) :
  def itemizeForSetSity(b: GameBoard, civ: Civilization.T): Seq[P] =
    getFigures(b, civ).filter(_.s.figures.numberofScouts > 0).map(_.p).filter(p => SetCityAction.verifySetCity(b, civ, p, Command.SETCITY).isEmpty)
It yields all points where a new city can be set.
  • Find all figures on the board belonging to a civilization
  • Single out squares with at least one scout
  • Map squares to points
  • Verify if the point is eligible for city setting using SetCityAction.verifySetCity
All stuff in a single line.
A general outline of the project
  • resources, game objects (JSON format) used in the game: tiles, squares, objects (now TECHNOLOGIES only)
  • gameboard , class definitions
  • objects , enumerations and classes related to game artifacts
  • helper, game logic, I found more convenient to put them as helper, object class then as methods in Gameboard class.
  • io, methods regarding reading and writing data in JSON format. I'm using a dependency PlayJSON package.
  • I, external interface
Brief interface description
  • getData(LISTOFCIV), list of civilizations available
  • getData(REGISTEOWNER), generates new game and returns unique token to be used in further communication
  • getData(GETBOARDGAME), returns current game state
  • executeCommand,  executes next command
  • itemizeCommand, provides all possible parameters for a particular command. For instance: for StartOfMove it brings all points where figure movement are allowed to commence.
So far, only a few commands are implemented
  • SetCapital
  • SetArmy
  • SetScout
  • EnfOfPhase
  • BuyScout
  • BytArmy
  • MoveFigure
  • RevealTile
  • SetCity
User Interface
For the time being, so ugly that only a mother or father could love it. More details: look here.

Next steps
Implementation of game persistence. Because of Heroku limitation, I cannot use disk file system as a mean. I'm planning to use Redis, there is a free quota for this service in Heroku. Redis will be used to store the games and also as a cache. This way, the server part will be completely stateless. Every step will consist of restoring the game from Redis, executing a command and storing the updated game to Redis again.