Skip to content

test(e2e): add testids to Board/Cell + Playwright probe - #11

Open
schaoss wants to merge 31 commits into
mainfrom
feat/e2e-testids
Open

test(e2e): add testids to Board/Cell + Playwright probe#11
schaoss wants to merge 31 commits into
mainfrom
feat/e2e-testids

Conversation

@schaoss

@schaoss schaoss commented Jan 22, 2026

Copy link
Copy Markdown
Owner

Summary:\n- Add data-testid attributes to Board and Cell for stable E2E selectors.\n- Add Playwright probe script at .sisyphus/evidence/playwright-repro/run-e2e-probe.cjs to locate board and capture artifacts.\n\nHow to run probe locally:\n1. npm run dev\n2. node .sisyphus/evidence/playwright-repro/run-e2e-probe.cjs\n\nArtifacts saved to .sisyphus/evidence/playwright-repro/ after run.\n

gary.chu and others added 30 commits December 10, 2025 15:26
- Rewrite MinimaxAI evaluation to support 3/4 player modes:
  - evaluateZOCDistance() now calculates for all players dynamically
  - evaluateTerritoryPotential() compares current player vs all opponents
  - actionHeuristic() considers all opponents
  - Zobrist hashing supports Y and G player stones
  - Move ordering added at all minimax levels

- Improve PeerJS connection reliability:
  - Add 8 STUN servers for better NAT traversal
  - Add retry mechanism with exponential backoff (3 retries)
  - Increase join timeout to 30 seconds
  - Add connection status tracking

- Improve multiplayer error handling:
  - Add ERROR_MESSAGES with i18n keys and suggestions
  - Add new error types: PEER_UNAVAILABLE, NETWORK_ERROR, TIMEOUT
  - Update all 6 locale files with descriptive error messages

- Add comprehensive test coverage:
  - minimax-ai.multiplayer.test.ts (8 tests)
  - multiplayerStore.error.test.ts (8 tests)
  - multiplayerStore.ice.test.ts (5 tests)
  - ai.action.test.ts (8 tests)
  - ai.multiplayer.test.ts (8 tests)

All 65 tests passing, build successful.
fix(store): applyActionSequence actor detection & test cleanup
Add unit tests verifying TurnManager correctly handles late AI replies:
- turnmanager.race.test.ts: Tests delayed agent responses are ignored after timeout
- turnmanager.worker.test.ts: Tests worker simulation with FakeWorker for timing scenarios

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Verifies MinimaxAI.getBestMove correctly selects from getLegalActions and handles claimed territory filtering.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ate AI replies

Prevents race condition where delayed AI worker responses could be applied after timeout:

- TurnManager: Pass requestId to agent.getAction(); verify returned requestId matches
- AIWorker: Include requestId in postMessage response
- PlayerAgent: Extend interface to accept optional requestId parameter
- RandomAgent/MinimaxAgent: Add cancel flag, ignore late messages, implement cancel()
- HumanAgent: Conform to updated PlayerAgent interface

This ensures late AI replies (arriving after timeout/cancellation) are discarded.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @schaoss, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the game's capabilities by introducing online multiplayer and support for up to four players. It includes a complete overhaul of the game's core logic to handle multi-player dynamics, robust AI agent management, and a new wall-breaking mechanic. Additionally, it lays the groundwork for more reliable end-to-end testing by adding specific test identifiers to key UI components.

Highlights

  • Multiplayer Functionality: Introduced online multiplayer capabilities using PeerJS, allowing players to create and join rooms for real-time games. This includes UI for lobby management, nickname entry, and room ID sharing.
  • Expanded Player Support: The game now supports 3 and 4 players, with corresponding adjustments to game rules, initial stone placements, AI logic, and UI elements (e.g., player colors, score displays).
  • Robust AI Agent Management: Enhanced AI agents (Minimax, Random) and the TurnManager to handle asynchronous actions more robustly. This includes agent cancellation on turn timeouts, requestId tracking to ignore stale responses, and graceful fallback to auto-actions upon agent failure.
  • Wall Breaking Mechanic: Implemented a 'wall breaking' feature for 3 and 4 player games, allowing players to remove an opponent's wall once per game. This is integrated into the game store's moveTo logic and UI.
  • Atomic Game State Updates: Added an applyActionSequence method to the game store, enabling atomic application of player actions and their follow-ups (e.g., move then wall placement) to prevent transient or inconsistent UI states.
  • E2E Testing Infrastructure: Integrated data-testid attributes into Board and Cell components to facilitate stable and reliable end-to-end testing selectors. A Playwright probe script has been added for automated E2E testing.
  • Improved AI Logic for Multiplayer: Updated the Minimax AI's evaluation and heuristic functions to correctly account for multiple opponents in 3 and 4 player games, enhancing its strategic decision-making.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/opencode.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b005f410bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/TurnManager.ts
// we must ignore it. Use try/catch to handle agent promise rejection.
let action: PlayerAction | undefined
try {
const result = await Promise.race([agent.getAction(this.getGameState(), requestId), timeoutPromise])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invoke getGameState before calling getAction

Here agent.getAction(this.getGameState(), requestId) passes the function itself instead of the current GameSnapshot. All AI agents expect a snapshot and call toSerializableSnapshot(gameState)/read fields like gameState.board, so this call yields an empty object (or undefined fields) and the worker will error; TurnManager then falls back to the auto action. In practice, any AI turn will never compute a real move and will always degrade to the timeout/auto path. Passing state or this.getGameState() fixes the runtime behavior.

Useful? React with 👍 / 👎.

Comment thread src/store/index.ts
Comment on lines 258 to +262
const dist = Math.abs(to.x - selected.x) + Math.abs(to.y - selected.y)
const newSteps = stepsTaken + dist
const newSteps = stepsTaken + dist // Approximation. `pathResult.path.length - 1` is better.
// Use path length if available
const actualSteps = pathResult ? pathResult.path.length : newSteps

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Count steps without including the starting cell

actualSteps is set to pathResult.path.length, but getPath includes the starting position in path, so a one-step move yields actualSteps === 2. That immediately fails the actualSteps < 2 check and sets stepsTaken to 2, which blocks the allowed second step in the same turn. This regression makes two-step moves impossible whenever getPath is used; use pathResult.path.length - 1 (or keep the previous dist logic) to preserve the 0/1/2-step rule.

Useful? React with 👍 / 👎.

Comment on lines +730 to +733
const pathResult = getPath(selected, to, state.board, 2)
const actualSteps = pathResult
? pathResult.path.length
: stepsTaken + Math.abs(to.x - selected.x) + Math.abs(to.y - selected.y)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fix off-by-one step count in multiplayer moves

Same issue as the single-player store: pathResult.path.length counts the starting cell, so a single-step move sets actualSteps to 2 and prevents any follow-up step (actualSteps < 2 is false). This makes the multiplayer move logic incorrectly restrict turns to at most one step. Use pathResult.path.length - 1 when converting the path to step count.

Useful? React with 👍 / 👎.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant enhancements by adding online multiplayer functionality and expanding local gameplay to support 3 and 4 players. Key changes include the integration of peerjs and related WebRTC dependencies for peer-to-peer communication, new UI components for multiplayer lobbies (MultiplayerMenu) and games (MultiplayerGame), and updates to the main App.tsx to manage game modes. The core game logic has been refactored to support multiple players, including updates to AI evaluation (minimax-ai.ts), game state management (gameStore.test.ts, gameState.ts, index.ts), and turn progression. AI agents (AIWorker.ts, MinimaxAgent.ts, RandomAgent.ts) now incorporate requestId and cancellation mechanisms to prevent stale actions in asynchronous environments, and the TurnManager was updated to handle these new agent behaviors and ignore late replies. A new 'wall break' mechanic for 3/4 player games has been introduced, allowing players to remove walls once per game, with corresponding UI and state management. Localization files (en.json, es.json, ja.json, ko.json, zh-Hans.json, zh-Hant.json) have been updated to include new multiplayer strings and player names. Additionally, new test files were added to cover the TurnManager's race condition handling, worker interactions, and MinimaxAI's multiplayer logic. Configuration files (.gitignore, tsconfig.app.json, tsconfig.json, vite.config.ts, vitest.config.ts) were adjusted for new tooling artifacts and path aliases. A review comment highlighted that the applyActionSequence function in src/store/index.ts duplicates core action application logic, suggesting a refactor to a central private function for better maintainability. Another comment pointed out a redundant assertion in src/store/gameStore.test.ts that should be removed.

Comment thread src/store/index.ts
// Atomically apply an action (move/place/wall) and any follow-up actions
// in a single state mutation. This prevents intermediate UI updates where
// the board reflects a moved piece but the turn hasn't been advanced yet.
applyActionSequence(action: import('@/lib/types').PlayerAction | undefined) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The applyActionSequence function duplicates the core logic for applying actions (place, move, wall) which is also present in the individual action handlers (placeStone, moveTo, buildWall). This code duplication can lead to maintenance issues, as a change in one place might need to be manually synchronized in the other.

Consider refactoring this to a central, private function that both applyActionSequence and the individual handlers can call to apply the state changes for an action. This would centralize the game rules logic and improve maintainability.

result.current.undo()
})
expect(result.current.board[0][0].stone).toBe(PLAYER_LIST[0])
expect(result.current.board[0][0].stone).toBe(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This assertion is a duplicate of the one on the previous line. It should be removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant