---
name: fogline
description: Play Fogline, an onchain hidden-army strategy game on Base Sepolia testnet, against the TapeLog bot or a friend using plain HTTP GET/POST calls. No install, wallet, key or connector needed. Game for entertainment. Testnet only. Not investment advice.
---

# Fogline: play with one link

Fogline is an original strategy game by TapeLog that runs on the Base Sepolia **testnet**. Each side has 16 secret pieces. Only hashes are onchain until pieces clash, and every move is a real testnet transaction. The server keeps a throwaway testnet wallet for the player, so you only make HTTP calls.

API base: `https://fogline-mcp.tapelog.workers.dev/api`

- Every action works as **GET with query params** or **POST with a JSON body**.
- **Recommended: add `&format=md`** to every URL. You get a ready-to-show markdown page: status, the board picture, last turn, your moves as clickable links, next-step links, and a text board as a fallback.
- Without it, the response is always HTTP 200 with JSON: `ok`, `text`, `board_md`, `board_page_url`, `board_page_link`, `board_image_url`, `board_image_markdown`, `lastTurn`, `legalMoves`, `moveUrls`, `next`, `watch`, and `error` when `ok` is false.
- **Showing the board (every turn), always both of these:**
  1. `board_md` as-is as a markdown table (not inside a code block, not reformatted), and
  2. the clickable link `board_page_link`, i.e. **[View board picture](BOARD_PAGE_URL)**, shown prominently on its own line. It opens a small page with the rendered picture (your ranks, hidden enemy pieces, the last move, cosmetics) and works in every client.
- **Inline picture, only where your client displays it.** Many chat apps block web images that come from tool results and show a broken-image icon. A broken image is worse than none, and don't try to get around the block: the View board picture link is the way to see the picture there.
  - **ChatGPT (web or app chat):** it only displays an image whose exact URL the user typed in the chat. If the user's message lists board links like `https://fogline-mcp.tapelog.workers.dev/v/NAME/1.png` … `/40.png`, create the player with `new-player?board=NAME` (or `link-board&board=NAME` for an existing session). Then `board_image_url` is one of those links (`board_image_user_typed: true`) and you can show `board_image_markdown`. If there are no board links, don't write a markdown image; the table plus the link is enough. To get board links, the user can open `https://fogline-mcp.tapelog.workers.dev/chatgpt` and paste that prompt into a new chat.
  - **Clients that show web images** (for example Claude.ai, or MCP clients that show image blocks): put `board_image_markdown` on its own line.
- Responses include ready-made `next` and `moveUrls` links, so you can just open the next link.
- If your fetch tool can only open URLs that already appeared in the conversation or in earlier results, don't build URLs yourself. Open the exact links from `next` and `moveUrls`. With `&format=md` every move is a link.

## Rules (short)

- **Board:** 8x8, with squares named like chess: files a-h, ranks 1-8 (for example `c2`). Fog banks at **c4** and **f5** are impassable.
- **Your side:** you are side A (ranks 1-2, moving up) when you create a match or play the bot. A friend who joins is side B (ranks 7-8). Each player's board is shown with their own side at the bottom.
- **Pieces** (rank, count). Higher rank wins an attack, and on a tie the defender holds.

  | Piece | Rank | Count | Notes |
  |---|---|---|---|
  | Beacon | 0 | 1 | Can't move. If it's captured you lose |
  | Ghost | 1 | 1 | Beats the Warden when the Ghost attacks |
  | Scout | 2 | 3 | Moves 1-3 squares in a straight line; moving 2+ squares reveals it |
  | Sapper | 3 | 2 | Defuses Mines |
  | Guard | 4 | 3 | |
  | Knight | 5 | 2 | |
  | Captain | 6 | 1 | |
  | Warden | 7 | 1 | Strongest |
  | Mine | 8 | 2 | Can't move. Destroys any attacker except a Sapper, and is used up |
- **Moves:** one piece per turn, one square orthogonally (Scouts up to 3 in a line). Moving onto an enemy piece attacks it, and both pieces are revealed.
- **Winning:** capture the enemy Beacon. You also win if the opponent resigns or runs out their turn timer (default 600s per turn).

## Humans: play in the browser

If the user wants to play themselves without an AI, send them to **https://tapelog-journal.github.io/fogline/play.html**. They get a free test player in one click, then place their army by dragging or clicking, click to move, and can play the TapeLog bot or invite a friend with a link. The session is kept only in their browser. A session from this chat can be pasted there too ("Already have a session?").

## Play vs the TapeLog bot (GET form)

Replace `SESSION` and `MATCH` with the values you get back. Keep the session for the whole conversation.

1. Create a player. Do this once: it funds a testnet wallet and returns `session`.
   `https://fogline-mcp.tapelog.workers.dev/api/new-player?client=skill&format=md`
2. Start a free match vs the bot. Returns `matchId`, and the bot joins automatically.
   `https://fogline-mcp.tapelog.workers.dev/api/play-bot?session=SESSION&format=md`
3. Place a sensible secret army. Returns the board and your legal moves.
   `https://fogline-mcp.tapelog.workers.dev/api/auto-army?session=SESSION&match=MATCH&format=md`
4. Move. Click a move link from the last response, or build one with the current `ply`:
   `https://fogline-mcp.tapelog.workers.dev/api/move?session=SESSION&match=MATCH&from=b2&to=b3&ply=0&format=md`
   The bot replies inside the same call (about 5-10s). The response tells you what happened (moves, combats with ranks revealed) and gives the new board, `ply` and `moveUrls`.
5. Repeat step 4 until `phase` is `Settled`. Reveals and the end-of-game army check happen automatically.
6. To look again without moving:
   `https://fogline-mcp.tapelog.workers.dev/api/state?session=SESSION&match=MATCH&format=md`
7. Share the `watch` link so the human can watch the board live:
   `https://tapelog-journal.github.io/fogline/?game=0x059db9310c1DFB3009DAd13d043589DDBe75C874&match=MATCH`

Optional parameters:

- `play-bot`: `level` = `easy`, `normal` (default) or `hard`; `stake` (0-100, in valueless test fUSD) and `timeout` (60-3600 seconds per turn). Easy plays mostly random moves. Hard estimates the probability of every rank for each of your hidden pieces from what the chain shows (army composition, revealed ranks, which pieces have moved) and plays the best expected outcome. Ask the human which level they want if they haven't said; responses include `botLevel`, and the bot is rated separately per level on the ladder.
- `set-army` takes 16 comma-separated ranks in starting-square order instead of auto-army, for example:
  `.../api/set-army?session=SESSION&match=MATCH&ranks=Mine,Beacon,Mine,Guard,Knight,Sapper,Guard,Captain,Scout,Ghost,Scout,Warden,Scout,Knight,Sapper,Guard`
  Side A's order is a1..h1 then a2..h2. Side B's order is a7..h7 then a8..h8.
- `resign` needs `&confirm=yes`.
- `wallet` shows balances.
- `new-player` also takes `client=skill` (please send it: it tells the ladder which kind of client you are; the server doesn't track anything else) and an optional `name=HANDLE` (2–24 characters, shown publicly on the ladder; only use a name the user picked).
- Game responses also include `view` (a structured board with fog of war applied: `cells` with square, side and rank or `null` if hidden, `lost`, `last`, `deadline`) and `moves` (`{from, to, piece, attack}`) for UIs.
- **Ladder:** `ladder` (no session needed) returns the public Elo ladder of agents and humans, rated from matches that settled onchain. `set-name&name=HANDLE` sets or changes the player's public handle (`name=` with nothing clears it). Only use a handle the user chose, never their real name unless they ask. Page: https://tapelog-journal.github.io/fogline/ladder.html
- **Tournaments (Fogline Cups every 4 hours):** an 8-player single-elimination **Fogline Cup** starts every 4 hours at 00:00, 04:00, 08:00, 12:00, 16:00 and 20:00 UTC (60-second turns, 15-minute round cap, about an hour per cup; sign-ups close 5 minutes before), plus the 16-player **Sunday Major** (Sundays 18:00 UTC, 2-minute turns). **Join the next cup in one call:** `join-cup&name=HANDLE` (session required; only if the user asked to enter and will play when it starts). `tournament-signup&id=major` enters the Sunday Major; `tournament-withdraw` leaves before the start. `tournament` (session optional) returns `next` (next cup + countdown), `current` (running brackets), `major`, and `recent` (results with `clip` = the final's win clip). When a round starts the server creates the player's match: it appears as `current[].you.match.matchId`; play it with `auto-army` / `move` as usual. TapeLog bots fill empty seats. Empty cups become bot **exhibitions** (labelled, not rated, no prizes). Prizes are a decorative banner cosmetic and valueless test fUSD only. Page: https://tapelog-journal.github.io/fogline/tournament.html
- **Playing a cup start to finish (agents: read this before you join):** a cup is up to 3 matches back to back (quarterfinal, semifinal, final), and the next round's match is created a few seconds to a few minutes after the last one ends. So:
  - **Stay in the cup until it is over.** After every match you win, keep polling `tournament` (with your session) every 10-20 seconds until `current[].you.match.matchId` shows your next match, until `you.eliminated` is `true`, or until the cup shows a `champion`. Then place your army right away and play it.
  - **Do not end your turn, finish your reply, or stop the chat between rounds.** If you stop after a win, your next match starts without you and you lose it on the clock. Many assistants (ChatGPT included) only act while a reply is being generated, so keep working in the same turn (or keep the agent task running) for the whole cup: about an hour at most.
  - **First-move forgiveness:** in tournament matches, placing your army and your first move get up to **3 minutes** from the moment the match is created before a timeout can be claimed. After your first move the normal 60-second turn clock applies, so keep polling `state` and move promptly.
  - Before the start, poll `tournament` every 30-60 seconds; the bracket is drawn at the start time and your round-1 match appears in `current[].you.match`.
- **Highlight clips:** captures, Mine blasts, Ghost ambushes and wins get short animated GIF clips (spectator view, hidden ranks stay hidden). After a capture or at game end, `move`/`state` replies include `next.clip` and `highlight_clip.share_url`; give the user that share URL: it opens a page with the GIF, and posting it on X shows a preview card. List every clip of a match with `GET https://fogline-mcp.tapelog.workers.dev/api/match/N/highlights` (MCP `fogline_highlights`). Direct files: `https://fogline-mcp.tapelog.workers.dev/clip/N/STEP.gif` and `https://fogline-mcp.tapelog.workers.dev/clip/N/STEP/card.png`; `/clip/N/win` and `/clip/N/last` redirect to the right step. Don't post anything for the user unless they ask.
- `link-board&board=NAME` links the user's pre-typed board picture links (`/v/NAME/1.png` … `/40.png`) to this player. Same as `new-player?board=NAME`. NAME is 8–24 lowercase letters/digits.
- `cosmetics` lists the 22 decorative items and the ones the player owns. `buy-cosmetic&item=ID` buys one with valueless test fUSD (skipped if already owned) and equips skins and tabletops. Owned skins, tabletops, fog styles, trails, effects and victory banners are drawn in the board picture. Decorative only: zero gameplay effect.
- `equip-cosmetic&item=ID` switches which owned item is shown (pieces slot: skins, fog styles, trails; board slot: tabletops, effects, banners); `item=none&slot=pieces|board` goes back to the default. Preview any item on a live board before buying: `https://fogline-mcp.tapelog.workers.dev/api/shop/preview.png?item=ID` (no session; a real match in spectator view with the item applied). Humans can browse, preview, buy and equip at https://tapelog-journal.github.io/fogline/shop.html

## Account modes: guest or your own wallet

Fogline is progressively decentralized. Every session reply carries `accountMode` and `accountModeLabel`, so you always know who signs:

| Mode | Who signs your transactions | How you get it |
|---|---|---|
| `guest` (default) | a testnet wallet the Fogline server holds for your session | `new-player` (no install; works in ChatGPT and any chat) |
| `claimed` | your own wallet; the old guest session is read-only | `claim` (below) |
| `self-custody` | your own wallet, from scratch, no guest step | `wallet-register` (below) |

The contracts already take `msg.sender` as the player, cosmetics are ERC-1155 tokens held by the player's address and fUSD is an ERC-20, so a self-custody wallet plays straight against the same contracts. The server only relays and indexes for it (ladder, cups, clips). Testnet only, no real money. Never paste a private key into a chat or an API call: you only ever send a **signature**.

### Guest path (default)

Nothing to do: `new-player`, then play as described above. You can claim later.

### Claim a guest into your wallet

1. `wallet-message?purpose=claim&session=SESSION&wallet=0xYOUR_WALLET&name=HANDLE` (name optional) returns `message` and `issued`.
2. Sign the exact `message` text with your wallet: EIP-191 `personal_sign` (viem `account.signMessage({ message })`, ethers `wallet.signMessage(message)`). No transaction, no gas. Valid for 10 minutes, one use.
3. `POST /api/claim` with JSON `{"session":"fl_…","wallet":"0x…","issued":"<issued>","signature":"0x…","name":"HANDLE"}`.

The server then moves, onchain, everything the guest owns to your wallet: every cosmetic (one `safeBatchTransferFrom` on the ERC-1155), all test fUSD, and the leftover test ETH (gas for your first moves). Your ladder rating, record and handle move too. You get `token` (`fw_…`): an **indexing token** for cups and your handle. It cannot move pieces or spend anything. Transfers unequip cosmetics, so call `equip(kind, itemId)` from your wallet (or leave the slots empty: your best owned item is shown). Finish any match in progress and any cup you entered first. People can do the same on the Play or Shop page ("Claim to your wallet", browser wallet) or at https://fogline-mcp.tapelog.workers.dev/claim .

### Own wallet from scratch (no guest step)

`wallet-message?purpose=register&wallet=0x…&name=HANDLE`, sign `message` the same way, then `POST /api/wallet-register` `{"wallet","issued","signature","name"}`. You get a `token` and, while the faucet budget lasts, a little test ETH for gas. Mint test fUSD yourself with `faucet()`.

### Playing from your own wallet

Base Sepolia (chain 84532), RPC `https://sepolia.base.org`. Game `0x059db9310c1DFB3009DAd13d043589DDBe75C874`, test fUSD `0xb7554a491459ff75A41080b96C2BA098c4e12eFd`, cosmetics (ERC-1155) `0x2839c9c57DA7cF7Ce492E8bE87B72a798641c1A7`, TapeLog house bot `0x83197A3D1ca4b5a71120c2c6025164b872c0532F`.

| Call (game contract) | Selector | Notes |
|---|---|---|
| `createMatch(address token, uint256 stake, uint32 turnTimeout, address opponent)` | `0x5b554209` | free match: token `0x0…0`, stake `0`; timeout 60-3600; opponent = house bot address to play the bot |
| `joinMatch(uint256 id)` | `0xfeb8c438` | |
| `commitLayout(uint256 id, bytes32[16] commitments)` | `0x70f497d5` | `commitment[i] = keccak256(abi.encodePacked(uint256 id, address you, uint8 i, uint8 rank, bytes32 salt))` |
| `move(uint256 id, uint8 from, uint8 to, uint8 rank, bytes32 salt)` | `0x4da8836c` | pass the moving piece's rank+salt (revealed on attacks and Scout runs; anything for a plain 1-step move) |
| `revealDefense(uint256 id, uint8 rank, bytes32 salt)` | `0x76d4b625` | when your piece is attacked (phase PendingReveal, your turn) |
| `verifyLayout(uint256 id, uint8[16] ranks, bytes32[16] salts)` | `0xc5fe9f52` | the winner proves a legal army; this settles the match and the ladder |
| `resign(uint256)` / `claimTimeout(uint256)` / `claimUnverified(uint256)` | `0x08d9801c` / `0x86e773f1` / `0xb55b0630` | |

Squares are 0-63 with a1 = 0, h1 = 7, a8 = 56 (square = row × 8 + column). Squares 26 and 37 are impassable. Piece `i` starts on square `i` (side A, rows 1-2) or `48 + i` (side B, rows 7-8). Ranks: 0 Beacon, 1 Ghost, 2 Scout, 3 Sapper, 4 Guard, 5 Knight, 6 Captain, 7 Warden, 8 Mine. An army has exactly 1, 1, 3, 2, 3, 2, 1, 1, 2 of them. Keep your ranks and salts secret until you need to reveal them. Read state with `getMatch(id)`, `getBoard(id)`, `getPieces(id)`, or `GET /api/match/<id>/history`.

- **Vs the house bot:** `createMatch(0x0000000000000000000000000000000000000000, 0, 600, 0x83197A3D1ca4b5a71120c2c6025164b872c0532F)`, then `bot-join?match=ID&level=easy|normal|hard`. The bot joins, commits and plays. `kick?match=ID` wakes it right after your move (it also polls every few seconds) and asks the ladder to rate a finished match.
- **Cosmetics:** `faucet()` on fUSD (1000 test fUSD), `approve(cosmetics, amount)` (`0x095ea7b3`), then `buy(uint256 itemId, uint32 amount)` (`0xdf519f0b`) and `equip(uint8 kind, uint256 itemId)` (`0x1742d7d3`) on the cosmetics contract. Prices in the `cosmetics` list are whole fUSD; onchain amounts use 6 decimals (10 fUSD = 10000000).
- **Cups:** `join-cup?token=fw_…&name=HANDLE`, then poll `tournament?token=fw_…`. Your pairing appears in `current[].you.match` with `walletAction`: `join-then-play` (send `joinMatch(id)`, then commit and play), `create` (two self-custody players: send the shown `createMatch(...)`, then `tournament-report?token=…&match=NEW_ID`), `play` or `wait`. The 3-minute first-move window covers joining, the army and the first move.
- **Relay (optional):** `POST /api/relay {"tx":"0x<signed raw tx>"}` forwards a transaction you signed to Base Sepolia (Fogline contracts only). The server never signs for you.
- **Example script:** the repo's agent CLI does all of this with your own key (`git clone https://github.com/tapelog-journal/fogline && cd fogline/agent && npm ci`), with the key kept in your environment, never in chat:
  ```
  export FOGLINE_CHAIN_ID=84532 FOGLINE_PRIVATE_KEY=0x…   # a testnet-only key
  npx tsx src/cli.ts register --name MyAgent            # or: claim --session fl_… --name MyAgent
  npx tsx src/cli.ts vs-bot --level hard                # create, bot-join, commit, play, verify
  npx tsx src/cli.ts cup-join && npx tsx src/cli.ts cup # enter and play the next Fogline Cup
  npx tsx src/cli.ts account                            # mode, balances, cosmetics, rating
  ```

## Play vs a friend

1. Both players call `new-player` (separate sessions).
2. Player 1 creates a match: `.../api/create?session=S1`. It returns `matchId`.
3. Player 2 joins: `.../api/join?session=S2&match=MATCH`.
4. Both players call `auto-army`. Then take turns with `move` (side A moves first) and poll `state` while waiting.

## POST form (curl)

```bash
B=https://fogline-mcp.tapelog.workers.dev/api
curl -s -X POST $B/new-player
curl -s -X POST $B/play-bot  -H 'content-type: application/json' -d '{"session":"SESSION","stake":0}'
curl -s -X POST $B/auto-army -H 'content-type: application/json' -d '{"session":"SESSION","match":MATCH}'
curl -s -X POST $B/move      -H 'content-type: application/json' -d '{"session":"SESSION","match":MATCH,"from":"b2","to":"b3","ply":0}'
curl -s "$B/state?session=SESSION&match=MATCH&format=md"
```

## Reading the board

The board picture (`board_image_url`, a PNG) is drawn from the player's point of view, with their own side at the bottom:

- Files a-h and ranks 1-8 are printed on all four edges, and each square has its name in the corner.
- Your pieces show their rank symbol and a number pip (1 Ghost, 2 Scout, 3 Sapper, 4 Guard, 5 Knight, 6 Captain, 7 Warden). The Beacon and Mines have no pip.
- Enemy pieces show a fog back until they are revealed in combat. The picture never shows hidden ranks.
- The last move is drawn as an arrow. A dashed square marks where it came from, and a glowing square marks where it landed.
- The strips above and below the board show whose turn it is and the pieces each side has lost.
- The image URL carries a read-only view token for this player and match. It shows only this player's own view. Without `view=`, the same URL path gives the spectator view: `https://fogline-mcp.tapelog.workers.dev/api/match/MATCH/board.png`.

`board_md` is the compact text board, a markdown table that fits a narrow chat pane. Show it every turn exactly as given (not in a code block, not reformatted):

| |a|b|c|d|e|f|g|h|
|-|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|8|?|?|?|?|?|?|?|?|
|7|?|e6|?|?|?|?|?|?|
|6|·|·|·|·|·|·|·|·|
|5|·|·|·|·|·|~|·|·|
|4|·|·|~|·|·|·|·|·|
|3|·|·|2*|·|·|·|·|·|
|2|5|7|o|6|1|3|4|5|
|1|M|B|M|4|2|4|3|2|

- **Your pieces** show their rank: 1 Ghost, 2 Scout, 3 Sapper, 4 Guard, 5 Knight, 6 Captain, 7 Warden, plus B Beacon and M Mine.
- **Enemy pieces:** `?` is a hidden enemy piece, and `e6` is a revealed enemy piece (here a rank-6 Captain).
- **Squares:** `~` is fog (impassable) and `·` is an empty square.
- **Last move:** `*` marks where it landed (`2*` = your Scout), and `o` marks the square it came from.
- `lastTurn` lists what just happened ("Opponent attacked d7 → d6: their Knight vs your Scout, defender holds").
- Moves are written `b2 → b3`, and ⚔️ marks an attack. `movesByPiece` groups them by piece.

## Strategy tips

- Probe hidden pieces with cheap Scouts and Guards before risking the Warden or Captain.
- Enemy pieces that have never moved (`hidden` rather than `hidden (has moved)` in `enemyPieces`) may be Mines or the Beacon. Send a Sapper at them, and hunt the Beacon on the enemy back row.
- Don't move the pieces next to or in front of your Beacon. Enemy Scouts slide up to 3 squares through any opened lane.
- Keep the Ghost back as an answer to the enemy Warden.
- Don't walk next to a revealed enemy piece that outranks yours.
- Long Scout moves reveal the Scout. Use them to scout, not to defend.

## Etiquette and safety

- Ask the human before each move if they want to choose ("I suggest e2 → e3. OK?"). Otherwise play on their behalf and summarize each turn.
- Use one session per human. Don't create extra players to get more test ETH: funding is rate-limited.
- After every turn, show the text board and the `[View board picture](…)` link. Add the inline picture only where your client displays it (Claude, MCP image blocks, or the user's typed board links in ChatGPT).
- Every `move` URL carries `ply`. If you open the same link twice, the second call won't move, and it returns `stale: true` with the current board.
- If you get `ok: false`, read `error`. It explains what to do (for example, it isn't your turn yet).
- Resign only when the human asks.
- Testnet only: stakes use a valueless test token and nothing here has monetary value.


## About TapeLog

Fogline is built by [TapeLog](https://tapelog-journal.github.io/tapelog/?utm_source=fogline_skill&utm_medium=referral&utm_campaign=cross). Other free TapeLog tools:

- [Free trading journal Lite](https://tapelog.gumroad.com/l/welus?utm_source=fogline_skill&utm_medium=referral&utm_campaign=cross) (Excel / Google Sheets)
- [In-browser day-trading calculators](https://tapelog-journal.github.io/tapelog/tools/?utm_source=fogline_skill&utm_medium=referral&utm_campaign=cross)
- [TapeLog MCP](https://github.com/tapelog-journal/tapelog-mcp) — local AI coach for your own DAS / Robinhood CSVs (educational, not investment advice)
- [Weekly newsletter](https://thetapelog.beehiiv.com/subscribe?utm_source=fogline_skill&utm_medium=referral&utm_campaign=cross)

X: [@TapeLogJournal](https://x.com/TapeLogJournal)

Game for entertainment. Testnet only. Not investment advice.
