bs_human_fire
Fire pirate cannons at specified grid coordinates to attack Navy ships. Returns hit, miss, or sunk status; reveals cargo of sunk ships.
Instructions
Fire the pirate cannons at the given coordinates on the Navy's grid. Call this when the human gives their shot. Returns hit/miss/sunk ā and if sunk, reveals what that Navy ship was carrying.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| row | Yes | Row index: 0 = top, 9 = bottom | |
| col | Yes | Column index: 0 = left, 9 = right |
Implementation Reference
- src/games/battleship.ts:241-262 (handler)The handler for the bs_human_fire tool. Validates game state (in_progress, pirate's turn, not already fired at coordinates), calls fireShot(), checks for victory via allSunk(), and updates the game state.
async ({ row, col }) => { if (bsGame.status !== "in_progress") { return { content: [{ type: "text", text: `The battle is over (${bsGame.status}). Call bs_new_game to sail again.` }], isError: true }; } if (bsGame.currentTurn !== "pirate") { return { content: [{ type: "text", text: `The Navy hasn't fired yet this round. Call bs_claude_fire first.` }], isError: true }; } if (bsGame.pirateAttacks[row][col] !== "?") { return { content: [{ type: "text", text: `You already fired at (row ${row}, col ${col}). Pick different waters.\n\n${renderState(bsGame)}` }], isError: true }; } const { summary } = fireShot(bsGame.pirateAttacks, bsGame.navyGrid, bsGame.navyShips, row, col); if (allSunk(bsGame.navyShips)) { bsGame.status = "pirates_win"; return { content: [{ type: "text", text: `${summary}\n\n${renderState(bsGame)}` }] }; } bsGame.currentTurn = "navy"; return { content: [{ type: "text", text: `${summary}\n\nThe Navy is lining up their cannons ā call bs_claude_fire to return fire.\n\n${renderState(bsGame)}` }] }; } ); - src/games/battleship.ts:237-240 (schema)Input schema for bs_human_fire: row and col are integers 0-9.
{ row: z.number().int().min(0).max(9).describe("Row index: 0 = top, 9 = bottom"), col: z.number().int().min(0).max(9).describe("Column index: 0 = left, 9 = right"), }, - src/games/battleship.ts:234-262 (registration)Registration of the bs_human_fire tool on the MCP server via server.tool(), within the registerBattleshipTools function.
server.tool( "bs_human_fire", "Fire the pirate cannons at the given coordinates on the Navy's grid. Call this when the human gives their shot. Returns hit/miss/sunk ā and if sunk, reveals what that Navy ship was carrying.", { row: z.number().int().min(0).max(9).describe("Row index: 0 = top, 9 = bottom"), col: z.number().int().min(0).max(9).describe("Column index: 0 = left, 9 = right"), }, async ({ row, col }) => { if (bsGame.status !== "in_progress") { return { content: [{ type: "text", text: `The battle is over (${bsGame.status}). Call bs_new_game to sail again.` }], isError: true }; } if (bsGame.currentTurn !== "pirate") { return { content: [{ type: "text", text: `The Navy hasn't fired yet this round. Call bs_claude_fire first.` }], isError: true }; } if (bsGame.pirateAttacks[row][col] !== "?") { return { content: [{ type: "text", text: `You already fired at (row ${row}, col ${col}). Pick different waters.\n\n${renderState(bsGame)}` }], isError: true }; } const { summary } = fireShot(bsGame.pirateAttacks, bsGame.navyGrid, bsGame.navyShips, row, col); if (allSunk(bsGame.navyShips)) { bsGame.status = "pirates_win"; return { content: [{ type: "text", text: `${summary}\n\n${renderState(bsGame)}` }] }; } bsGame.currentTurn = "navy"; return { content: [{ type: "text", text: `${summary}\n\nThe Navy is lining up their cannons ā call bs_claude_fire to return fire.\n\n${renderState(bsGame)}` }] }; } ); - src/games/battleship.ts:111-138 (helper)The fireShot() helper function called by the bs_human_fire handler. Checks if the shot hits a ship, marks hits, and returns the result (miss/hit/sunk) with a summary string.
function fireShot( attackGrid: AttackCell[][], targetGrid: boolean[][], targetShips: ShipState[], row: number, col: number ): { result: "miss" | "hit" | "sunk"; ship?: ShipState; summary: string } { if (!targetGrid[row][col]) { attackGrid[row][col] = "O"; return { result: "miss", summary: `Cannonball splashes into the water at (row ${row}, col ${col}). Miss!` }; } const ship = targetShips.find(s => s.positions.some(([r, c]) => r === row && c === col))!; ship.hitCount++; attackGrid[row][col] = "X"; if (ship.hitCount === ship.size) { ship.sunk = true; for (const [r, c] of ship.positions) attackGrid[r][c] = ship.letter; return { result: "sunk", ship, summary: `DIRECT HIT at (row ${row}, col ${col})! **${ship.name}** has been sent to the bottom!\nš Recovered from the wreck: *${ship.cargo}*`, }; } return { result: "hit", summary: `Hit! (row ${row}, col ${col}) ā ${ship.name} is taking on water!` }; }