Add experimental 'Play vs Gemini' game mode - #58
Open
mterczynski wants to merge 2 commits into
Open
Conversation
- New GameMode.VS_GEMINI with a sparkles ModeButton (navy radial gradient) - Gemini settings panel: user-provided API key input, FIDE rating slider (1400-2800, default 1600) and model selector (flash-lite/flash/pro) - GeminiOpponent reads settings on each turn, sends board state, last player move and legal moves to the Gemini API as structured JSON and applies the returned move (structured output matching game-engine Move) - Missing API key or a failed request alerts the user and undoes their move (new GameEngineContext.undoLastMove) - Illegal/invalid Gemini moves are retried up to 3 times - Unit tests for prompt building, response parsing and the API client Closes #57 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
Verified against the live Gemini API with a real key (gemini-2.5-flash, FIDE 1600): played 1. e4 and 2. Ne2 in the browser - Gemini answered 1... e5 and 2... Nf6, both applied correctly to the board. No errors in the console and no invalid-move retries needed. |
mterczynski
commented
Jun 12, 2026
| <SettingsIcon>⚙️</SettingsIcon> | ||
| <GameScreenSelector /> | ||
| </> | ||
| <GeminiSettingsContextProvider> |
Owner
Author
There was a problem hiding this comment.
We do not need to provide it for every mode, maybe there is a smarter way to declare it
mterczynski
commented
Jun 12, 2026
| * the current settings, sends the board state and the player's last move to | ||
| * the Gemini API and applies the returned move. | ||
| */ | ||
| export const GeminiOpponent = () => { |
Owner
Author
There was a problem hiding this comment.
I am not a fan of combining UI with behavior. I would highly appreciate having a framework/ui-lib agnostic GeminiOpponent TS class and connecting it to UI via adapter design pattern, React Context or similar approach
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #57
What
Adds an experimental ✨ Play vs Gemini game mode where the player plays against the Gemini API.
How it works
GameMode.VS_GEMINI = "vs_gemini"and a newModeButtonon theModeSelectionScreenwith the ✨ sparkles emoji and a black background with a navy-blue radial gradient spreading from the middle of the button.Insert your own Gemini API Key). The key is kept in memory only and read on every request (committed on blur/Enter so partial keys aren't used mid-typing).gemini-2.5-flash-lite/gemini-2.5-flash/gemini-2.5-pro, defaultgemini-2.5-flash).GeminiOpponentcomponent sends the current board state, the player's last move and the list of legal moves tomodels/{model}:generateContent(no conversation history). Input is the game-engine'sBoard/MoveJSON; output uses Gemini structured output (responseMimeType: application/json+ aresponseSchemamatching the game-engineMoveinterface). The returned move is validated against the legal-move list and applied.window.alertexplains that the key is required and the move is undone (newGameEngineContext.undoLastMove, which replays the move history minus the last move). The same undo happens if the API call fails (e.g. invalid key), so the player can simply retry.Separation of concerns / tests
All Gemini code lives in
client/src/gemini/:buildGeminiRequest.tsparseGeminiResponse.tsMove(pure)requestGeminiMove.tsGeminiSettingsContext.tsx/GeminiSettingsPanel.tsxGeminiOpponent.tsx17 new unit tests cover prompt building, response parsing (incl. promotions defaulting to queen) and the API client (mocked
fetch, retry and error paths). They run as part of the rootjestsuite (jest.config.jsnow ignoresgame-engine/build/so the workspace package resolves without a haste collision).No
.envneeded — the API key is provided by the user at runtime and never persisted.Verification
npx jest --ci— 42 suites / 167 tests passnpm run build -w clientpassesfetchstubbed to a canned Gemini response the full move loop works (player plays e4, "Gemini" answers e5 and it's applied to the board).🤖 Generated with Claude Code