Skip to content

Comments

Offline computer#2585

Merged
veloce merged 24 commits intomainfrom
computer_play
Jan 30, 2026
Merged

Offline computer#2585
veloce merged 24 commits intomainfrom
computer_play

Conversation

@veloce
Copy link
Contributor

@veloce veloce commented Jan 29, 2026

This pull request introduces the offline "Play vs Computer" mode, allowing users to play chess games against a local Stockfish engine.

@veloce veloce marked this pull request as ready for review January 30, 2026 15:05
@veloce veloce requested a review from Copilot January 30, 2026 15:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request introduces an offline "Play vs Computer" mode that allows users to play chess games against a local Stockfish engine without requiring an internet connection. The implementation includes comprehensive game state management, hint system, takeback functionality, and game persistence.

Changes:

  • Implements offline computer game screen with UI controls for starting games, configuring Stockfish level (1-12), choosing sides, and managing casual/rated modes
  • Adds game controller with engine integration for move generation, hints computation using multi-PV evaluation, and game state management
  • Introduces game storage layer for persisting ongoing games and user preferences
  • Refactors MaterialDifferenceDisplay widget into a reusable component
  • Enhances engine work system with Elo-based multiPV and search time scaling for realistic difficulty levels

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/view/offline_computer/offline_computer_game_screen_test.dart Comprehensive widget tests covering game flow, hints, casual mode, and saved game restoration
test/model/engine/fake_stockfish.dart Adds LegalMoveFakeStockfish and MultiPvFakeStockfish for testing game progression and hints
test/model/engine/evaluation_service_test.dart Updates tests to reflect new Elo-based multiPV and search time scaling
lib/src/view/offline_computer/offline_computer_game_screen.dart Main game screen with board, player info, controls, and dialogs for game setup and results
lib/src/model/offline_computer/offline_computer_game_controller.dart Game state controller managing moves, engine responses, hints computation, and takebacks
lib/src/model/offline_computer/offline_computer_game_storage.dart JSON-based persistence for saving and loading ongoing games
lib/src/model/offline_computer/offline_computer_game_preferences.dart User preferences for Stockfish level, side choice, and casual mode
lib/src/model/game/offline_computer_game.dart Game model with 12 Stockfish levels mapping to Elo ratings (1320-3190)
lib/src/widgets/material_diff.dart Extracted reusable widget for displaying material difference and captured pieces
lib/src/view/game/game_player.dart Refactored to use extracted MaterialDifferenceDisplay widget
lib/src/model/engine/work.dart Enhanced MoveWork with Elo-scaled multiPV (2-10) and search times (150-5000ms) for realistic strength levels
lib/src/model/engine/evaluation_service.dart Removed redundant _isDisposed checks for cleaner code
lib/src/model/engine/evaluation_mixin.dart Made EvalWork.path nullable to support contexts without position trees
lib/src/view/play/play_menu.dart Adds "Play against computer" menu item
lib/src/styles/lichess_icons.dart Minor whitespace cleanup
lib/src/model/settings/preferences_storage.dart Adds offlineComputerGame preference category
CLAUDE.md Documents Dart 3.10+ dot shorthand syntax conventions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

context: context,
builder: (context) => YesNoDialog(
title: Text(context.l10n.mobileAreYouSure),
content: const Text('No worries, your game will be saved.'),
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The text 'No worries, your game will be saved.' is hardcoded and should use a localized string from context.l10n for internationalization consistency.

Suggested change
content: const Text('No worries, your game will be saved.'),
content: Text(context.l10n.mobileGameWillBeSaved),

Copilot uses AI. Check for mistakes.
const SizedBox(height: 16),
SwitchListTile.adaptive(
title: Text(context.l10n.casual),
subtitle: const Text('Allow takebacks and hints'),
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The text 'Allow takebacks and hints' is hardcoded and should use a localized string from context.l10n for internationalization consistency.

Suggested change
subtitle: const Text('Allow takebacks and hints'),
subtitle: Text(context.l10n.allowTakebacksAndHints),

Copilot uses AI. Check for mistakes.
@override
void runBuild() {
_evaluationService = ref.read(evaluationServiceProvider);
_evaluationService = ref.watch(evaluationServiceProvider);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

Changing from ref.read to ref.watch means the mixin will now rebuild when the evaluationServiceProvider changes. This could cause unnecessary rebuilds if the service is mutated, as watching a provider typically means you want to rebuild when it changes. The original ref.read was likely intentional to avoid this. Please verify this change is necessary and doesn't cause performance issues or unexpected rebuilds.

Suggested change
_evaluationService = ref.watch(evaluationServiceProvider);
_evaluationService = ref.read(evaluationServiceProvider);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

intended

Comment on lines +364 to +365
mainAxisSize: .max,
mainAxisAlignment: .start,
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The dot shorthand syntax (.max and .start) is not used elsewhere in the codebase. For consistency, use the full form MainAxisSize.max and MainAxisAlignment.start instead. While the CLAUDE.md document describes this syntax, the existing codebase consistently uses the full form throughout (see lib/src/view/game/game_player.dart:83, lib/src/view/analysis/analysis_layout.dart:208, and many other files).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we'd rather update the code little by little to use this new syntax

// Verify side selection options (label is "Side")
expect(find.text('Side'), findsOneWidget);
expect(find.text('White'), findsOneWidget);
expect(find.text('Random side'), findsOneWidget);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The test expects the text 'Random side' but the UI code uses context.l10n.randomColor which, based on existing usage in lib/src/model/common/game.dart:11, would return a different localized string. The test should use the actual localized string that will be rendered, not a hardcoded value.

Suggested change
expect(find.text('Random side'), findsOneWidget);
expect(find.text('Random color'), findsOneWidget);

Copilot uses AI. Check for mistakes.
ref.read(_isBoardFlippedProvider.notifier).toggle();
},
),
BottomSheetAction(makeLabel: (context) => const Text('New game'), onPressed: onNewGame),
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The text 'New game' is hardcoded and should use a localized string from context.l10n. This is inconsistent with other menu items which properly use localization (e.g., flipBoard, analysis, resign).

Suggested change
BottomSheetAction(makeLabel: (context) => const Text('New game'), onPressed: onNewGame),
BottomSheetAction(
makeLabel: (context) => Text(context.l10n.newGame),
onPressed: onNewGame,
),

Copilot uses AI. Check for mistakes.
content: Text(subtitle),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: Text(context.l10n.close)),
TextButton(onPressed: onNewGame, child: const Text('New game')),
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The text 'New game' is hardcoded and should use a localized string from context.l10n. This is inconsistent with other UI text which properly uses localization (e.g., close, victory, draw).

Suggested change
TextButton(onPressed: onNewGame, child: const Text('New game')),
TextButton(onPressed: onNewGame, child: Text(context.l10n.newGame)),

Copilot uses AI. Check for mistakes.
@veloce veloce merged commit 0f8d893 into main Jan 30, 2026
1 check passed
@veloce veloce deleted the computer_play branch January 30, 2026 15:50
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