Diplomacy in Declarative Swift
I want to introduce a fabulous game. It requires strategic thinking, courage, psychology, negotiation skills — and the occasional backstabbing. It is aptly named «Diplomacy».
It is set in Europe of 1901 in the prelude to World War I.
The game’s objective is to occupy at least half of the continent’s territories. But where other games introduce random choice like rolling the dice, in Diplomacy the outcome of any move is solely determine by your negotiation skills, as at a certain point you will have to forge alliances — and there will be points where those alliances will be broken in betrayal. I guess you won’t be surprised that some describe it as a «Friendship Killer».
For this article I want to implement the board and the units’ movements on it. We assume that the validity of those moves have been checked before and they can be executed — this is true if there aren't any conflicting moves, as one speciality of Diplomacy is, that all players moves are executed at the same time for a given in-game season.
We will implement the movement of armies over land and fleets plowing the sea.
We will use a declarative coding style in which the state and all models are 100% immutable — reducing the room for possible bugs by magnitudes. In fact our code will only feature one reassignment to a variable that had been used before — writing the newly generated state.
Declarative here means that we tell the computer what we want it to do, rather how to do it — and repeat that over and over again.
The programming interface will resemble spoken english quite much.
At the end we will move an army from Ruhr in Germany to Holland and Belgium (that sound sounds like history repeating) and one from Berlin all the way to Constantinople (Istanbul since 1930), a tour I would like to make one day — not with an army but an old Volvo 240, though. And we will see a fleet steaming its way to the Aegean Sea — to pick me up.
Diplomacy knows seven players for seven empires, Territories can be of kind
sea and may be occupied by either an army or a fleet.
Borders can be between lands, seas, land and sea (coastal) or a channel, between Denmark and Kiel or the Bosphorus in Constantinople.
Board holds the territories and borders.
Board.Change encodes the possible commands. We will have a look on
change value is evaluated and accordingly processed by applying the necessary changes to the territories and writing those back. The only point where mutability is actually needed. Moving is only possible if two territories are directly connected.
Now we create the territories. There are a whole bunch, just some examples:
Now we need to connect the territories according to the map by establishing borders:
Now let’s create a board and make some moves! Doesn’t
.move( .army(of:.germany,from:bulgaria, to:constantinople) )
read like natural english — provided you can look past the funky punctation?
print(board:) before moving will output
That’s it: Germany has moved its army through Belgium to Holland, conquering both in the process. All moves from Berlin to Constantinople went through and the fleet arrived in the Agean Sea.
During my time at university I and some friends played Diplomacy a few times. Both as a classical in-person board game and online. In the years that have passed since then I have coded parts of a game as a kind of Coding Kata from time to time — never it has been such a delightful and simple experience than with this declarative style. Also it took just roughy 100 lines of code to express the model and board. The code is readable even to non-coders. My mother can understand what is going on — really, I tested her. It is as immutable as possible, reducing the possibility for bugs and crashes.
I have uploaded the storyboard at gitlab. Read more about Khipu — a general application architecture using the same language elements and establishing the same constraints.
A great and fun introduction into Diplomacy:
In one of my next articles — it is nearly finished — I want to introduce you to «Capability-based Design» in Swift, again fully immutable and declarative.