Files
great-powers/README.md
T
jcoffey-dev c6c3b926d2 Both of those got built
The open list was still asking for the bots to be able to solo and for the
music, the battle sounds and the four endings. What is actually left is
everything to do with the game having a home, and the press being a
conversation the player can start rather than only answer.
2026-09-09 11:52:25 -07:00

498 lines
26 KiB
Markdown

# Great Powers
A browser rebuild of the classic seven-power negotiation game: one human
against six computer powers, in one sitting.
Not the name you expect, and [NOTICE.md](NOTICE.md) will make the argument at
length once there is more to notice. The short version is the one the
starship game already made on this site: the rules are free to rebuild, the
trademark and the published artwork are not. "Great Powers" is what those
seven were called at the time.
## What this is, and what it is not
**One human, six computer powers, one sitting.** The original is seven people
over weeks. A hub with no traffic cannot deliver that, and nobody is going to
wait months for six strangers — so the other six are played by the machine
and a game is finished in an evening.
**It still has to be a negotiation game.** A version where you simply write
orders is a good movement puzzle and not this game. So:
- **Both sides open.** Bots approach you with proposals; you approach them.
Neither waits for the other.
- **On real terms.** Offers are concrete and about this turn or the next few
— support this move, keep out of that province, no attack until 1903 — and
are accepted or refused on their merits, not by a die roll.
- **Betrayal is a move, not a bug.** A bot can agree to support you and then
not do it, and so can you. Bots remember who kept their word, tell each
other, and price you accordingly. That is the game.
The press is **structured rather than free text**: typed offers, deterministic
reasoning, and no model anywhere in it. That makes betrayal auditable — you
can read exactly why a power turned on you — and testable, which free text
would not be.
## The look and the sound
**The map is the standard board's geometry, under the same licence as this
project.** The outlines come from the diplomacy engine at
<https://github.com/diplomacy/diplomacy>, which is AGPL-3.0, extracted by
`tools/map.py` into `src/game/board.json`. Every colour and every mark drawn
on it is ours; see [NOTICE.md](NOTICE.md).
Three earlier attempts drew it from scratch and all three were worse.
Dividing the plane by nearest province gives a tidy board that looks nothing
like Europe. Drawing the adjacency graph gives something unimpeachable about
the rules and not a map at all -- circles joined by lines. Fifty-six polygons
placed by eye gives fifty-six islands. Drawing Europe accurately is real work,
somebody did it properly, and they published it under a licence that invites
exactly this.
**Clicking still answers with the rules, not the picture.** A shape cannot say
that a fleet on Spain's north coast may not enter the Gulf of Lyon while one
on the south coast may, because both coasts are the same shape. So selecting a
province lights up exactly where its unit may legally go, taken from the
adjacency graph.
**It gets the whole screen.** The other three games live in a four-by-three
cabinet because the machines they are rebuilding did. This one is not
rebuilding a machine, and seventy-five provinces plus somebody trying to talk
you out of Galicia do not fit in a letterbox. The map takes the viewport.
**The music is not cartoony, and that is the point.** It is 1914: serious,
and intense. The joke of this game is that something charming to look at is
the cruellest thing on the site, and the score is what says so.
**And the turn should be heard.** Orders resolve in silence in every online
version of this game, which is a waste of the one moment anybody cares
about. Armies grinding into each other, guns at sea, a convoy escorting
something across, a unit being built, a unit falling back -- each of those is
a different noise, generated by the synth like everything else on this site.
**The endings get their own.** This game does not end in a win or a loss, it
ends in one of four things, and they do not sound alike:
- **the solo** -- eighteen centres, and the only unambiguous victory there is;
- **elimination** -- your last centre gone, which can happen in 1903 and
leaves you watching;
- **the draw** -- nobody reaches eighteen and the survivors agree to stop.
This is what actually happens most of the time, and it is neither a win nor
a loss, so it cannot borrow either of their tunes;
- **somebody else's solo** -- you are still on the board and it is over
anyway, which is the bleakest of the four and should sound like it.
## The board
Seventy-five provinces, two adjacency graphs — an army walks the land, a
fleet follows the water — and three provinces with two coasts that do not
connect, where a fleet has to say which one it is on.
The topology is a fact about a published game and free to use. The artwork is
not, and there is none here: the map this game draws is generated from the
data, the way the cave game's dodecahedron is.
`npm test` checks the board against itself. The one that earns its keep is
symmetry: an adjacency list this size is typed by hand once and wrong in
three places unless something looks. It found the Skagerrak wired to the
Baltic on the first run — which would have quietly opened a back door into
the Baltic and made Denmark worthless.
## The published test cases
`tools/datc.py` turns the Diplomacy Adjudicator Test Cases into
`src/game/datc.json` -- the orders and their annotated outcomes, not the
document's prose, which is Lucas Kruijswijk's writing. Every one of the 176
published cases is parsed and run.
| Section | | Passing |
| --- | --- | --- |
| 6.A - 6.G | movement | 137 / 137 |
| 6.H | retreating | 18 / 18 |
| 6.I | building | 7 / 7 |
| 6.J | civil disorder | 13 / 13 |
All 176 pass.
They are the reason this adjudicator is worth trusting: they found that
orders were never validated, that the paradox rule did not terminate, that
civil disorder was measuring the wrong distance, and that five of my own
hand-written cases rested on a support Paris cannot give.
Three of the hundred and forty movement cases are excluded rather than
failed. 6.A.6, 6.B.10 and 6.B.11 state their setup in prose -- "Germany has
a fleet in London" -- and the fixture is built from order lines, so it
cannot carry a unit nobody ordered. The other hundred and thirty-seven all
pass.
There are no known deviations left.
### What the failing cases were worth
They found six real faults, which is the whole argument for running somebody
else's tests rather than only your own. Every one of them was invisible in
play: the engine had an answer, the answer looked reasonable, and it was
wrong.
**The illegal/invalid line (6.A.5, 6.D.8).** The document separates an order
that is impossible *in this position* from one this position allows that
merely did not come off. The first is ignored and the unit is holding like
any other; the second means the unit tried to leave and cannot be propped up
where it stands. An army sent across water with a fleet on the route and no
convoy ordered is the second (6.D.8); the identical order with nobody in the
sea at all is the first (6.D.32). Guessing at this once cost four passing
cases to buy one. Reading it properly cost nothing.
**A support that only looked like a hold (6.A.5).** "Supports A Yorkshire"
and "Supports A Yorkshire - Yorkshire" are the same shape once parsed, and
the case turns on the difference: the first holds a unit down, the second
supports a move nobody can legally make. The extractor now keeps the
distinction; it was losing it.
**The head of a cycle (6.F.22, 6.F.28).** Kruijswijk's resolver guesses that
an order fails, sees what follows, and guesses the other way. Only the
*outermost* order in a cycle may do that, because its answer is the one
everything else was computed against. This engine let any order that reached
the dependency list first declare the cycle its own -- and an inner one
takes both guesses with its callers' provisional answers held fixed, gets
the same result twice for that reason, and records it as settled. The cycle
then becomes invisible: the backup rule never runs, and the position
resolves to whichever of its two consistent readings the search walked into
first. It had been doing that on every paradox and getting away with it,
because on a first-order paradox both readings agree.
Fixing it took two passes. The first asked only whether the cycle had come
back round to this order, which fixed 6.F.22. 6.F.28 needed the second: an
order may not settle a cycle while *any* entry in the dependency list is
still waiting further down the stack, not merely the entries added since it
started. Six paradoxes in a ring, each a tidy four-order cycle in its own
right, and each was settling itself locally against a caller's provisional
answer. The ring they were links in was never seen at all.
**The answer has to agree with itself (6.F.29).** The resolver guesses, and
a guess is only ever tested against the guesses in force beside it. Where
several cycles are knotted together that is not enough: two readings can
each be locally consistent, and the search lands on whichever one its
starting order leads to. The document is explicit that there is no
straightforward way to fix this inside the recursion -- so it is fixed
outside it. The resolution is run again, starting each order from the answer
the last run gave it. A reading that is genuinely settled reproduces itself;
one that was an artefact of where the search began does not, and the next
run starts from somewhere better. Four passes, and a position still arguing
with itself after that is oscillating rather than converging, so the first
answer stands.
**A superfluous convoy order (6.G.19).** For a convoy order the fleet has to
be necessary to some route -- there must be a route, ordered or not, that
needs it. The path finder now considers only *proper* routes, which never
jump to a province an earlier fleet in the chain already reached: Tunis -
Ionian - Adriatic - Albania is not one, because the Ionian touches Albania
and the Adriatic is doing nothing. It decides whether two units swap places
or bounce off each other, and the document's own advice is that an
interactive system should never offer the order.
Sections H, I and J -- retreating and the winter -- have engine code but no
harness yet; their cases are shaped differently and need one.
## The bots
`src/game/evaluate.ts` prices a position and `src/game/bot.ts` plays it.
Everything a power does comes out of two numbers, and they are deliberately
few: when a power turns on you, you are entitled to know what it thought it
was getting and be furious about the price rather than confused by it.
`chooseOrders` returns its reasoning as a list of sentences for exactly that.
Centres are the only thing that counts. Units are how you get them and are
worth nothing in themselves -- a power with eight units and four centres is
losing, and will have four units by the winter.
Two things the tests had to teach it. It garrisons a threatened centre it is
already standing on, because scoring only *moves* had it defending Berlin by
marching the Munich garrison there -- abandoning one centre to save another
of identical value. And it never shoves at its own countryman, which has no
strength at all and is two units wasting a turn on each other.
### Making them able to win
For a long time they could not, and the reason was worse than being weak:
the board **froze**. By 1908 every centre count stopped changing, and a game
left running to 1960 finished on the distribution it had reached in 1939. A
game that cannot be won is not a hard game, it is a broken one. Thirty
seeded games now play themselves out in `solo.test.ts`; where the leader
used to finish on a mean of 7.8 centres and never once reach eighteen, it
now averages 13.7 and solos in six games out of thirty, which is about the
rate a table of decent human players manages.
Five things were wrong, and every one of them is a rule somebody could have
told you over a board.
**A threat is two units of one power, not two neighbours.** Support comes
from your own units or from a deal, so two enemies who have agreed nothing
cannot combine. Counting any two neighbours put half of every army on
garrison duty in a crowded position -- and the garrison was drawn from
exactly the units that would otherwise have attacked. France sat with three
units beside Kiel for five years and never took it, because all three were
defending against a threat that did not exist.
**Take what you can carry before what you merely want.** Sorting objectives
by worth and spending units as you go sounds right and is not: the most
valuable target is usually the one you cannot have, and it eats the units
that could have taken the second most valuable.
**March toward the guns.** Everything above asks what a unit can take *this*
turn, and a board settles the moment the answer everywhere is nothing. A
unit with nothing else to do now walks toward the best thing this power
wants and cannot yet have -- worth, less a penalty for every year of
walking, so an army does not set out across Europe for a centre somebody
nearer will have taken first. It is the only part of this that thinks past
the current turn.
**Gather.** Units choosing objectives one at a time never gather anywhere:
each picks the best thing it can see and sets off alone, and a defended
centre needs two. A goal somebody is already walking toward is now worth
more to the next unit than it was to the first. That single line was worth
two centres a game to the leader.
**And the one that mattered most: supporting somebody does not cost you the
province.** Support was priced at the full value of the target, which made
every request in the game refusable and every one of them refused -- a
centre is worth a hundred, a working relationship at most eighty, and the
arithmetic could not come out any other way. Two hundred and twenty-four
approaches in one game, ninety-five of them for support, and not a single
one accepted. The entire press had been reduced to agreeing where *not* to
go. But you only give up the province if you could have taken it yourself,
and usually you could not -- which is precisely why they are asking. The
rest of the time what it costs you is one unit's turn.
Powers also lean. Seven minds reasoning identically play the same game every
time, so each carries a seed that decides which way it goes when two things
are worth the same. It is not randomness: the same power asked twice in the
same game gives the same answer, and a seed replays a game exactly. It only
stops seven identical minds playing seven identical games.
The price of breaking a promise is **how far this power trusts the partner**,
not how far the partner trusts it. Pricing it the other way round gives a
power that has been lied to four times a high cost of retaliating, because it
has been scrupulous itself and is still well thought of, which is exactly
backwards. An ally who has already betrayed you is worth nothing to protect.
Bots open negotiations rather than only answering them. `propose()` works
from the plan the power has already made: it decides what it wants to do this
turn, notices where that will not work alone, and goes looking for somebody
who could make it work. Help taking something defended, first; then a quiet
border over ground neither of them owns; then peace outright, when a
neighbour is over your centres and your ambitions are elsewhere. Three
approaches a turn at most, because six powers all talking at once is a
mailstorm rather than a negotiation.
Each overture carries what the power actually says, so it can be put in front
of a human as a message rather than as a form.
**A power will offer a quiet border to somebody it is attacking somewhere
else**, and that is deliberate. Germany marching on French Belgium while
suggesting both sides leave Burgundy alone is not a lie and not incoherent: a
promise about Burgundy is a promise about Burgundy, and it will be kept to
the letter by somebody who is robbing you at the same moment. A bot that
refused to make that offer would be playing a politer game than this one. It
will not, however, ask the power it is attacking to *help*, or offer it peace
while the orders say otherwise.
## Giving orders
Clicks on the board, because that is where the question is. Select a unit,
pick a verb, and the provinces the next click will accept light up -- so
nobody writes a move the rules were never going to allow, and nobody has to
learn a notation to find that out.
Everything offered comes from the rules rather than from the map, which is
the same split the map itself is built on. A fleet on the north coast of
Spain is drawn identically to one on the south coast and can go to entirely
different places; no shape can express that, and the highlight has to.
Orders are drawn where they are given: an arrow to where a unit is going, a
dashed line to what a support is holding up, a ring round a unit that is
staying. The list beside the board says the same thing in the rules' own
words, because an arrow cannot tell you that the support you meant for Vienna
is being given to Budapest. Anything the rules will not take is struck
through in the list as it is written, rather than after the turn.
## The turn
`src/game/game.ts` is the loop, and it has no interface attached on purpose:
a whole game can be played out in a test from the opening to whatever end it
finds. That is the only way to answer the question that actually matters
about the bots -- not whether each order looks sensible, but whether seven
powers left alone reach a conclusion.
Spring orders, retreats, autumn orders, retreats, then the winter, which is
the only phase that changes who owns anything.
Before the orders, everybody talks. Computer powers approach each other,
answer on the merits, and what they agree binds that turn and is judged at
the end of it. Approaches to the human are handed back rather than answered
for them.
**The game is called at the end of 1920.** Diplomacy has no clock of its
own: it ends on eighteen centres, or when the players agree to stop. There
is a vote for stopping (see below) and a power in front will never take it,
which is correct and means somebody has to actually win.
Twenty years is set from measurement rather than taste. Solos land between
1915 and 1920 in these games; calling it at 1912, as this did, cut a fifth
of them off mid-conquest and turned a game somebody was winning into a draw.
Calling it much later adds years in which nothing new happens.
So: eighteen centres wins outright, and if nobody has them by the end of
1920 the survivors draw. **The draw is a proper ending here rather than a
failure to finish** -- it is still the commonest way this game ends, as it
is at a real table.
## The other two phases
A beaten unit that finds somewhere to stand is a nuisance for years; one that
does not is gone. And the winter is where a good spring turns into a bigger
army, or does not. Both are decisions, and both were being taken *for* the
player while the rest of this was built, which quietly removed half the
consequence of the turn they had just played.
They are offered as places rather than as a notation, for the same reason the
orders are: the rules already know what is allowed, so the interface only
ever offers what is. A beaten unit lists the provinces it may fall back to and
nothing else; the winter lists the home centres that are yours and empty, with
a fleet offered only where a fleet could sit.
The game steps past anything with no decision in it. A retreat phase where
none of your units was thrown out, or a winter where your centres and units
are level, is not a choice -- it is a screen asking you to press Done.
## Writing a support
Support is the order the whole game turns on, and it was the order this
interface was worst at taking.
The rule is that the supporting unit must be able to reach the province the
supported unit is *going to* -- not the province it is standing in. The board
was offering the wrong set: only units standing next door. So Vienna could
not support Venice into Tyrolia, which is a perfectly ordinary order --
Vienna and Tyrolia touch, Vienna and Venice do not -- and there was no way
to say it. It also offered support to hold for units too far away to prop
up, wrote the order anyway, and let the adjudicator quietly turn it into a
hold after the turn was submitted.
That question now lives in `src/game/targets.ts` with the rest of the rules,
where it is eight tests rather than a closure in a component.
The second half was that a stray click threw away a half-written order. You
picked Support, picked the unit to help, missed the destination by a
province, and the whole thing silently became a fresh move somewhere else.
A two-part order now stands its ground and says no; clicking the unit giving
the order takes you back to the start of it. And the unit you picked to
support is marked on the board in a different colour from the unit giving
the order, because otherwise the second half is written blind.
## Ordering a convoy
The rules have had convoys all along -- fifty of the DATC cases are about
them, including the paradoxes -- but for a while you could not order one.
The board offered an army the provinces it could walk to, and a crossing is
by definition not one of those, so the army's half of a convoy could not be
written. The fleet's half could, which made it worse: the verb was there and
did nothing anybody could use.
An army on a coast is now also offered the coasts a chain of crewed seas can
reach, drawn in blue and with long dashes so a crossing does not look like a
march. A sea counts if there is a fleet standing in it, whoever owns it. A
fleet that has not been ordered to convoy still counts: it is a thing that
could be arranged, which is what the negotiation is for, and the adjudicator
will bounce the crossing if it was not.
That is deliberately not the question the rules ask when they *judge* a
convoy, which is whether water could ever get there. That one says yes to
most of Europe. Offering thirty provinces because a chain of fleets is
conceivable would be worse than offering none.
Clicking a blue coast writes the move `viaConvoy`, which is not a formality:
the rules allow a unit to be convoyed to a province it could have walked to,
and the two orders resolve differently.
The two halves are written separately, as they always have been, and the
order list says so while only one of them exists -- `A Yorkshire → Norway`
sits there in red until `F North Sea convoys Yorkshire → Norway` joins it,
and then both go black. Nothing was added to make that happen. The validator
already knew.
### Half a convoy
Multi-fleet convoys work -- London to Spain across the Channel and the
Mid-Atlantic, with both fleets ordered to carry -- and the DATC's fifty
convoy cases say so. What did not work was finding that out.
A convoy takes as many orders as there are seas to cross, and the rules give
two different answers to writing fewer. No fleet ordered at all and the move
is *illegal*: legality is decided on the board alone, so the panel reddens
it. One fleet of a two-fleet chain and the move is perfectly *legal*. It
simply fails, silently, a turn later, with nothing on screen to distinguish
it from having been blocked.
From the chair where the orders are written those are the same complaint --
nobody is carrying this. And it is worse than it looks, because the board
deliberately offers crossings that run through other powers' fleets, which
cannot be ordered at all, only asked for. So the panel now answers the one
question that matters: given everything on the table, is there a chain that
gets this army there? If not it says which is missing -- *no fleet has been
ordered to carry it*, or *ordered to carry it, but not the whole way*.
## Before the map, and how to stop
A landing page, because Diplomacy has a reputation and about half of it is
wrong. The rules are short enough to state completely and strange enough
that stating them is a courtesy: there is no luck in this game at all, every
unit is exactly as strong as every other, and nothing happens on the board
that somebody did not agree to help with. It is also the honest place for
the licence and for where the map came from -- a page that says nothing
about that is a page quietly hoping nobody asks.
The dice that play when you start are the only joke in here. There are no
dice in Diplomacy; that is the sound of every other board game you have
played, and the last one you hear before finding out that none of this is
luck.
Giving up is two different things and `src/game/concede.ts` keeps them
apart.
**Resigning is unilateral.** Nobody is entitled to your attention, so nobody
votes on it. But your power does not leave the board: it goes into civil
disorder, holds everything, waives its builds and is paid off as it loses
centres, which is what the rules already say happens to an absent player.
That matters to everybody else. A resigned Austria is still eleven centres
somebody has to go and take, and letting them evaporate would hand the game
to whoever happened to be next door.
**A draw is not unilateral**, and that is the part that keeps it Diplomacy
rather than a menu. Every surviving power votes and one refusal is enough,
because being able to end the game by asking would be worth more than any
alliance in it. A power refuses while it leads or is within two centres of
leading, and it says so: *"England is 1 behind and thinks that is
catchable."* Asked in Spring 1901 every one of the other six refuses, which
is the right answer -- a game that could be drawn on the first turn would
not be worth playing.
**Conceding** -- handing the game to whoever is winning -- is harder to get
than a draw, and it should be. It only carries when the solo was coming
anyway: the leader is within four of eighteen and nobody left can close the
gap. Otherwise refusing costs nothing but time, and time is the only thing
the losing side has.
Elimination is the third way out and needs no button. When your last centre
goes you are told so and the rest plays out; you can watch it or close the
tab, and either way the board is still worth seeing.
## Still to build
- a home on the games hub: a repository, a container, a card and a location
block beside the other three
- a social preview card, which lives with the site rather than here
- the bots do not talk to the *player* about anything but support: no
proposal of a quiet border, no offer of peace, and no way to say "leave
Galicia alone this year" in the interface