To begin with, here is the updated progress sheet:
The changes with respect to the previous update are:
- Track Actions and Tech Cards are pretty much finished; I am just looking through lots of rule threads on BGG to see if I have missed any edge case, or if everything behaves as it should.
- Tapestry Cards are in a similar state, but due to the fact that the accompanying tests are a little more complex to set up, these are slightly behind.
- Substantial progress has been made with Civilization Actions, GameLog / Undo, Porting Logic to Lua, Localizations, and general Unit Testing, as well as some miscellaneous tasks.
And now to the most interesting part of this post — at least for most people:
As Jamey revealed in one of the latest livecasts, the current plan is to reach alpha stage during this fall. For anyone interested in joining the alpha, we will provide a Google Form for you to register your details in the near future. Furthermore, anyone who solved one of the puzzles will automatically be included in the alpha test.
Turn-based games and math, and what this has to do with an undo-system and the game log
In this update I would like to discuss the relationship between turn-based games and math, and why this has an impact on the undo-system and game log.
Any move you make in a game can be thought of as a function that changes the current gamestate. If Gamestate0 is the initial state at the start of the game, and Action0 is the “action” the first player took, then (in loosely mathematical terms):
Gamestate1 = Action0(Gamestate0)
GamestateN = ActionN-1(GamestateN-1)
GamestateN is thus the state after applying the (N-1)th action to GamestateN-1. This means that you can successively apply moves, such that:
GamestateN+1 = ActionN(…Action1(Action0(Gamestate0)
As an example, think of chess games that are often recorded in terms of their moves:
The above gamestate was reached by applying the actions:
- White pawn to e4
- Black pawn to e5
- White knight to f3
Successively applying these actions to the “start” chess gamestate created the new state seen in the picture above.
By looking at a game as simply an initial state and a series of moves, this gives us a few advantages:
First of all, the amount of data needed to transmit during multiplayer games is drastically reduced. As an analogy, just think of watching a sports game on TV and then of listening to the transmission of a sports game on the radio. TV shows everything — every moment — whereas on the radio, the commentator tells you only what is happening “right now”. Simply put, this is similar to what the “current function” changes.
Second, so long as a single game doesn’t consist of millions of moves, this makes creating an undo functionality comparatively easy. Often an undo function in a program is implemented by programming the inverse function for all existing functions. But provided that it is manageable to keep a log of what has happened, you can simply recalculate back from the start and just omit the last step.
Third, if a game is stored like this, this helps immensely with displaying a game log for a game.
Here you can see the very first version of the game log, within the first prototype:
It is to be read from top to bottom, left to right. Please ignore “TILE_1 TILE_2” and “TODO_EXACT_CARD”! 😉
The icons in the log reflect the applied actions (or functions) in the game. In this case:
- Player 1 advanced on the military track, conquered, and received a culture resource from that.
- Player 2 advanced on the exploration track and received two territory tiles, and
- Player 1 advanced on the technology track and invented a tech card.
In the final version, the log will display the exact territory tiles and tech card, and if you hover over the conquer icon it will show on the map which territory has been conquered.
There are probably some other advantages, however the ones mentioned above are the most important for us. In software engineering terms, this technique is called “Event Sourcing”/”Command Sourcing” (if interested, see https://www.martinfowler.com/eaaDev/EventSourcing.html).
Using this technique is also the reason why “GameLog / Undo” are in the same row in the progress sheet, because, as you saw, they are intrinsically linked.
The only situation in which I wouldn’t use this technique is if a game consisted of an extremely large number of actions. For nearly all turn-based games, this is probably not the case. And even if a game should fall into this category, one could always go with a hybrid model: keeping intermediate states around, and logging all the actions that happened after the last intermediate state.
RNGs – is my (digital) future predetermined?
Since we have already discussed game states and math in the previous section, we can now talk about random number generators (or RNGs) and how they work in software. A computer is generally deterministic, every function applied to the same input should lead to the same output. So, how do computers then generate random numbers? There are 2 possibilities:
- The computer uses a hardware random number generator that relies on some physical process that generates “random” data. You wouldn’t usually find this in a normal computer, mobile phone or game console.
- The computer uses an algorithm to generate numbers which behave as one would expect a random number sequence to behave.
2. is what nearly all software uses. The algorithm has an internal state, a so-called “seed”, to which a mathematical function is applied, that changes the seed to the next value in a very unpredictable way. Every time you want to calculate the next random number the same function is applied, resulting in a vastly different number and the current state is returned. So, by starting with the same seed you will get the same sequence of “random” numbers. But how do you determine a random starting seed? You don’t need a random starting seed, just a different starting seed, and for this there is a simple solution. We can use the current time — in most cases this is expressed as the number of milliseconds that have passed since 01/01/1970.
Even though you may think, “Wow, so it isn’t really random?”, it is extremely unlikely that you could distinguish a sequence of dice throws generated by a RNG, or one which has been rolled by a human being.
Furthermore, this provides us with two interesting properties:
- We only need to store the starting seed of the RNG in a game that is recorded as described in the previous section.
- It has an implicit protection against cheating players in a number of ways — they cannot wait for a moment in which the RNG would be in their favor, or “pretend” that they rolled a certain value.
Now you could argue that since the game has to know the current seed, one could precalculate which random value will be drawn next and, based on that, choose a different action. In multiplayer games where this is an issue, it can be solved by only transmitting the current seed from the server to the client after the client has locked in their choice of action (this is a simplification, there are a few more technical details surrounding how this would work).
So yes, in a way your digital future is — partially — predetermined. I say “partially” because who knows, maybe your opponent uses the next drawn random number instead of you, so there is still a human element to it. And, furthermore, can you really say that reality is random? If you knew the states of all atoms within a room that will have an influence on the dice throw, don’t you think you would be able to calculate what will be thrown?