Back in 2016 I built Coterie in about a week, mostly as an excuse to get comfortable with flexbox. It was a two player card game on a 4x4 board in the spirit of Triple Triad, and the one idea in it I still liked was the coterie: match an opponent’s notch exactly and the card you take fires off its own notches, which can take another card, which can fire again. Everything else aged badly. The cards were CSS boxes with dots on them, and the AI picked a random empty slot. It has been sitting on my GitHub profile looking exactly like a 2016 side project, which is what it was.

The 2016 version: CSS cards and an opponent that played at random

So I rebuilt it in Godot. It still runs in the browser, but now it’s a tabletop: glazed tiles on a slate board over a walnut table, with gold studs for the notches. The shot at the top is a coterie two links deep, with the taken tile mid-flip inside its ring of gold.

One real rules change

Notches used to be on or off. Now each one has a strength from 1 to 3, shown as that many studs. When your tile lands, every enemy neighbour it points a notch at is challenged against the notch facing back - a stronger notch converts it, a weaker one does nothing, and an equal one is a coterie, where the tile turns, fires its own notches at its neighbours as if it had just been played, and afterwards loses a stud from every notch. That last part is what keeps a cascade from being a free win. The tile that carried it is weaker for the rest of the game. Both hands are dealt from the same set of notch budgets, so a deal can’t give one side all the strong tiles.

An opponent worth beating

The old AI was Math.random() over the empty slots. There are three now, and all of them play with both hands face up. Coterie has no hidden information worth having, and hiding the opponent’s hand only turns strategy into guessing.

  • The Apprentice takes the biggest immediate capture half the time and plays anywhere the other half.
  • The Rival looks one move ahead, with a small penalty for leaving weak notches facing open cells.
  • The Curator looks two moves ahead, and once six or fewer cells are left it searches the rest of the game exactly.

In self-play the Rival beats the Apprentice 75% of the time and the Curator beats the Rival 70%. The Curator still decides in about a tenth of a second, which matters in a browser where there’s no second thread to think on.

The daily puzzle

The part I’m happiest with is the daily puzzle. Everyone gets the same deal each day against the Curator, and the Curator’s tie-breaks are seeded, so the same moves always get the same answers. That turns a hand of tiles into a puzzle. Your first finished game is the one that counts; replays are practice, which is where you learn how it answers.

Every deal is checked before it ships. A generator plays the Curator against itself on the day’s deal and only keeps it if the player’s side wins, otherwise it moves on to the next deal for that day. The margin of that winning line becomes the day’s par. Results copy out as a little grid of green and red squares, which is all the bragging a game like this needs.

Holding a tile over a cell marks every tile it would take

Hold a tile over a cell and the tiles it would take light up, gold for the ones taken by a coterie. It only shows the immediate result, never the reply, so it teaches the rules without playing the game for you.

No art files

There isn’t a model or texture file in the project. The tiles are a bevelled box built with SurfaceTool, the walnut and slate are noise shaders, and every sound is rendered by a small Python script, from the ceramic knock of a placed tile to the bell that climbs one step of a pentatonic scale for each link of a cascade. The sigil in the middle of each tile is the same mirrored 7x7 pattern the 2016 cards used for their backs, which felt like the right thing to carry over.

A finished board, nine to seven

It’s at coterie.twinspear.com. Play the daily before you look at par.