This repository is an ongoing project exploring how games built on Midnight can model real-world human interactions in which selective disclosure and zero-knowledge proofs play an important role. Midnight is described in its developer documentation as a privacy-first blockchain focused on selective disclosure, zero-knowledge proofs, and confidential data handling.
Our first project is Counterfeit Compass, described below.
Possible directions for future development include:
- hidden-alliance games
- anti-coercion governance games
- credential-and-bluff games
Counterfeit Compass is a two-player game of incomplete information in which bluffing is possible, but risky: a challenged claim must be backed by a ZK proof, and verification failure is fatal.
Both players start with zero charge, which increases or decreases as they take turns moving on a 5×5 board according to precise rules.
Objective: the first player to reach a charge of 5 wins.
There are currently two ways to run the game:
Uses a trusted Express referee.
- Install dependencies:
npm --prefix frontend installnpm --prefix backend install - In one terminal, rebuild the frontend on changes:
npm --prefix backend run dev:frontend - In another terminal, launch the backend:
npm --prefix backend run dev - Open
http://localhost:3000.
This serves the same React app, but expects the trust-critical game flow to come from a Midnight contract/client adapter instead of the backend referee.
- Install dependencies:
npm --prefix frontend installnpm --prefix backend install - Set the required frontend environment variables before building:
VITE_GAME_MODE=midnightVITE_MIDNIGHT_STAKE=<stake>VITE_MIDNIGHT_PAYOUT_ADDRESS=<payout-address> - Wire the generated Midnight bindings/adapter into
frontend/src/midnight/contract.ts. - In one terminal, rebuild the frontend on changes with the Midnight env vars set:
VITE_GAME_MODE=midnight VITE_MIDNIGHT_STAKE=<stake> VITE_MIDNIGHT_PAYOUT_ADDRESS=<payout-address> npm --prefix backend run dev:frontend - In another terminal, launch the backend as a static-file host:
COMPASS_GAME_MODE=midnight npm --prefix backend run dev - Open
http://localhost:3000.
This repository has a small Midnight client layer under frontend/src/midnight. It is responsible for:
- keeping each player's private board, salts, and local Merkle-tree mirror in browser-local storage;
- preparing witnesses for Compact circuits;
- mapping contract ledger state into the UI state used by the React app;
- replacing the "trusted" backend routes with Midnight transaction/query wrappers when Midnight mode is enabled.
Both players begin with the same initial field of directions (N, E, S, and W) on a 5×5 square grid.
A marker on each player's grid indicates that player's current position.
Setup:
- both players start at the center;
- both players start with charge 0;
- both players begin from the same initial grid and may modify up to 3 cells.
The game starts after both players confirm their modified cells. Each grid remains fixed for the duration of the game. Each player can see only their own grid.
Example: each player has chosen a grid of directions (modified directions shown in blue):
A player's position determines a dominant direction, defined as the most frequent direction among the arrows immediately above, to the right, below, and to the left of the player's current position.
If there is a tie among these four arrows, the dominant direction is the direction of the arrow at the player's current position.
The dominant direction of one player determines the movement of the other player, as explained below.
Charge changes as players move across the grid.
-
Players take turns moving, and each move consists of exactly one square up, right, down, or left; that is, one of
N,E,S, orW. -
The player whose turn it is to move, say Player A, requests a reading of the dominant direction associated with Player B's current position, as defined above. This reading determines the direction in which Player A must move.
Note that Player B is not required to tell the truth about the dominant direction at their current position.
-
After moving, Player A's charge may increase or decrease as follows. Let
Mdenote Player A's direction of movement, and letDdenote the direction of the arrow on the destination square.Player A's charge:
- increases by 2 if
Dcoincides withM; - increases by 1 if
Dis 90 degrees clockwise fromM; - decreases by 1 if
Dis opposite toM; - remains unchanged if
Dis 90 degrees counterclockwise fromM.
- increases by 2 if
As stated above, the first player to reach a charge of 5 wins.
-
Before Player A makes the move indicated by Player B's reading, Player A may challenge Player B's reading. In that case, Player B must submit a ZK proof attesting that the reading is correct. If verification of Player B's proof:
- fails, or no proof is submitted, then Player A wins the game;
- succeeds, then Player A loses 3 units of charge.
-
Movement at the board edges follows the Pac-Man (or toroidal) convention: if a player is at the top edge of the board, then moving up (
N) places the player at the bottom of the same column, and similarly for the other three edges.The same convention is used to determine the dominant direction. For example, if a player is at the top edge, then the arrow "above" is the arrow at the bottom of the same column.

