Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analysis tabs #1167

Merged
merged 23 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
418 changes: 231 additions & 187 deletions lib/src/model/analysis/analysis_controller.dart

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions lib/src/model/analysis/analysis_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ class AnalysisPreferences extends _$AnalysisPreferences
return fetch();
}

Future<void> toggleEnableComputerAnalysis() {
return save(
state.copyWith(
enableComputerAnalysis: !state.enableComputerAnalysis,
),
);
}

Future<void> toggleEnableLocalEvaluation() {
return save(
state.copyWith(
Expand Down Expand Up @@ -90,6 +98,7 @@ class AnalysisPrefs with _$AnalysisPrefs implements Serializable {
const AnalysisPrefs._();

const factory AnalysisPrefs({
@JsonKey(defaultValue: true) required bool enableComputerAnalysis,
required bool enableLocalEvaluation,
required bool showEvaluationGauge,
required bool showBestMoveArrow,
Expand All @@ -101,6 +110,7 @@ class AnalysisPrefs with _$AnalysisPrefs implements Serializable {
}) = _AnalysisPrefs;

static const defaults = AnalysisPrefs(
enableComputerAnalysis: true,
enableLocalEvaluation: true,
showEvaluationGauge: true,
showBestMoveArrow: true,
Expand Down
6 changes: 2 additions & 4 deletions lib/src/model/analysis/server_analysis_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ class ServerAnalysisService {
///
/// This will return a future that completes when the server analysis is
/// launched (but not when it is finished).
Future<void> requestAnalysis(GameAnyId id, [Side? side]) async {
Future<void> requestAnalysis(GameId id, [Side? side]) async {
final socketPool = ref.read(socketPoolProvider);
final uri = id.isFullId
? Uri(path: '/play/$id/v6')
: Uri(path: '/watch/$id/${side?.name ?? Side.white}/v6');
final uri = Uri(path: '/watch/$id/${side?.name ?? Side.white}/v6');
final socketClient = socketPool.open(uri);

_socketSubscription?.$2.cancel();
Expand Down
19 changes: 13 additions & 6 deletions lib/src/model/common/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ abstract class Node {
return null;
}

/// Updates all nodes.
/// Recursively applies [update] to all nodes of the tree.
void updateAll(void Function(Node node) update) {
update(this);
for (final child in children) {
Expand Down Expand Up @@ -360,16 +360,20 @@ class Branch extends Node {
super.eval,
super.opening,
required this.sanMove,
this.isHidden = false,
this.isComputerVariation = false,
this.isCollapsed = false,
this.lichessAnalysisComments,
// below are fields from dartchess [PgnNodeData]
this.startingComments,
this.comments,
this.nags,
});

/// Whether this branch is from a variation generated by lichess computer analysis.
final bool isComputerVariation;

/// Whether the branch should be hidden in the tree view.
bool isHidden;
bool isCollapsed;

/// The id of the branch, using a concise notation of associated move.
UciCharPair get id => UciCharPair.fromMove(sanMove.move);
Expand Down Expand Up @@ -398,7 +402,8 @@ class Branch extends Node {
eval: eval,
opening: opening,
children: IList(children.map((child) => child.view)),
isHidden: isHidden,
isComputerVariation: isComputerVariation,
isCollapsed: isCollapsed,
lichessAnalysisComments: lichessAnalysisComments?.lock,
startingComments: startingComments?.lock,
comments: comments?.lock,
Expand Down Expand Up @@ -487,7 +492,8 @@ class Root extends Node {
final branch = Branch(
sanMove: SanMove(childFrom.data.san, move),
position: newPos,
isHidden: frame.nesting > 2 || hideVariations && childIdx > 0,
isCollapsed: frame.nesting > 2 || hideVariations && childIdx > 0,
isComputerVariation: isLichessAnalysis && childIdx > 0,
lichessAnalysisComments:
isLichessAnalysis ? comments?.toList() : null,
startingComments: isLichessAnalysis
Expand Down Expand Up @@ -587,7 +593,8 @@ class ViewBranch extends ViewNode with _$ViewBranch {
required Position position,
Opening? opening,
required IList<ViewBranch> children,
@Default(false) bool isHidden,
@Default(false) bool isCollapsed,
required bool isComputerVariation,
ClientEval? eval,
IList<PgnComment>? lichessAnalysisComments,
IList<PgnComment>? startingComments,
Expand Down
25 changes: 2 additions & 23 deletions lib/src/model/game/game_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/account/account_preferences.dart';
import 'package:lichess_mobile/src/model/account/account_repository.dart';
import 'package:lichess_mobile/src/model/analysis/analysis_controller.dart';
import 'package:lichess_mobile/src/model/analysis/server_analysis_service.dart';
import 'package:lichess_mobile/src/model/clock/chess_clock.dart';
import 'package:lichess_mobile/src/model/common/chess.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
Expand Down Expand Up @@ -424,21 +423,6 @@ class GameController extends _$GameController {
_socketClient.send('rematch-no', null);
}

Future<void> requestServerAnalysis() {
return state.mapOrNull(
data: (d) {
if (!d.value.game.finished) {
return Future<void>.error(
'Cannot request server analysis on a non finished game',
);
}
final service = ref.read(serverAnalysisServiceProvider);
return service.requestAnalysis(gameFullId);
},
) ??
Future<void>.value();
}

/// Gets the live game clock if available.
LiveGameClock? get _liveClock => _clock != null
? (
Expand Down Expand Up @@ -1183,13 +1167,8 @@ class GameState with _$GameState {
String get analysisPgn => game.makePgn();

AnalysisOptions get analysisOptions => AnalysisOptions(
isLocalEvaluationAllowed: true,
variant: game.meta.variant,
initialMoveCursor: stepCursor,
orientation: game.youAre ?? Side.white,
id: gameFullId,
opening: game.meta.opening,
serverAnalysis: game.serverAnalysis,
division: game.meta.division,
initialMoveCursor: stepCursor,
gameId: gameFullId.gameId,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:lichess_mobile/src/model/common/speed.dart';
import 'package:lichess_mobile/src/model/opening_explorer/opening_explorer.dart';
import 'package:lichess_mobile/src/model/opening_explorer/opening_explorer_preferences.dart';
import 'package:lichess_mobile/src/network/http.dart';
import 'package:lichess_mobile/src/utils/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'opening_explorer_repository.g.dart';
Expand All @@ -20,6 +21,7 @@ class OpeningExplorer extends _$OpeningExplorer {
Future<({OpeningExplorerEntry entry, bool isIndexing})?> build({
required String fen,
}) async {
await ref.debounce(const Duration(milliseconds: 300));
ref.onDispose(() {
_openingExplorerSubscription?.cancel();
});
Expand Down
18 changes: 9 additions & 9 deletions lib/src/model/study/study_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class StudyController extends _$StudyController implements PgnTreeNotifier {
currentNode: StudyCurrentNode.illegalPosition(),
pgnRootComments: rootComments,
pov: orientation,
isLocalEvaluationAllowed: false,
isComputerAnalysisAllowed: false,
isLocalEvaluationEnabled: false,
gamebookActive: false,
pgn: pgn,
Expand All @@ -121,7 +121,7 @@ class StudyController extends _$StudyController implements PgnTreeNotifier {
pgnRootComments: rootComments,
lastMove: lastMove,
pov: orientation,
isLocalEvaluationAllowed:
isComputerAnalysisAllowed:
study.chapter.features.computer && !study.chapter.gamebook,
isLocalEvaluationEnabled: prefs.enableLocalEvaluation,
gamebookActive: study.chapter.gamebook,
Expand Down Expand Up @@ -289,9 +289,9 @@ class StudyController extends _$StudyController implements PgnTreeNotifier {
_root.isOnMainline(path) ? node.children.skip(1) : node.children;

for (final child in childrenToShow) {
child.isHidden = false;
child.isCollapsed = false;
for (final grandChild in child.children) {
grandChild.isHidden = false;
grandChild.isCollapsed = false;
}
}
state = AsyncValue.data(state.requireValue.copyWith(root: _root.view));
Expand All @@ -304,7 +304,7 @@ class StudyController extends _$StudyController implements PgnTreeNotifier {
final node = _root.nodeAt(path);

for (final child in node.children) {
child.isHidden = true;
child.isCollapsed = true;
}

state = AsyncValue.data(state.requireValue.copyWith(root: _root.view));
Expand Down Expand Up @@ -417,9 +417,9 @@ class StudyController extends _$StudyController implements PgnTreeNotifier {
// always show variation if the user plays a move
if (shouldForceShowVariation &&
currentNode is Branch &&
currentNode.isHidden) {
currentNode.isCollapsed) {
_root.updateAt(path, (node) {
if (node is Branch) node.isHidden = false;
if (node is Branch) node.isCollapsed = false;
});
}

Expand Down Expand Up @@ -559,7 +559,7 @@ class StudyState with _$StudyState {
required Side pov,

/// Whether local evaluation is allowed for this study.
required bool isLocalEvaluationAllowed,
required bool isComputerAnalysisAllowed,

/// Whether we're currently in gamebook mode, where the user has to find the right moves.
required bool gamebookActive,
Expand All @@ -583,7 +583,7 @@ class StudyState with _$StudyState {

/// Whether the engine is available for evaluation
bool get isEngineAvailable =>
isLocalEvaluationAllowed &&
isComputerAnalysisAllowed &&
engineSupportedVariants.contains(variant) &&
isLocalEvaluationEnabled;

Expand Down
43 changes: 24 additions & 19 deletions lib/src/view/analysis/analysis_board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ import 'package:lichess_mobile/src/widgets/pgn.dart';

class AnalysisBoard extends ConsumerStatefulWidget {
const AnalysisBoard(
this.pgn,
this.options,
this.boardSize, {
this.borderRadius,
this.enableDrawingShapes = true,
this.shouldReplaceChildOnUserMove = false,
});

final String pgn;
final AnalysisOptions options;
final double boardSize;
final BorderRadiusGeometry? borderRadius;

final bool enableDrawingShapes;
final bool shouldReplaceChildOnUserMove;

@override
ConsumerState<AnalysisBoard> createState() => AnalysisBoardState();
Expand All @@ -38,26 +38,28 @@ class AnalysisBoardState extends ConsumerState<AnalysisBoard> {

@override
Widget build(BuildContext context) {
final ctrlProvider = analysisControllerProvider(widget.pgn, widget.options);
final analysisState = ref.watch(ctrlProvider);
final ctrlProvider = analysisControllerProvider(widget.options);
final analysisState = ref.watch(ctrlProvider).requireValue;
final boardPrefs = ref.watch(boardPreferencesProvider);
final showBestMoveArrow = ref.watch(
analysisPreferencesProvider.select(
(value) => value.showBestMoveArrow,
),
);
final showAnnotationsOnBoard = ref.watch(
analysisPreferencesProvider.select((value) => value.showAnnotations),
);

final evalBestMoves = ref.watch(
engineEvaluationProvider.select((s) => s.eval?.bestMoves),
);
final analysisPrefs = ref.watch(analysisPreferencesProvider);
final enableComputerAnalysis = analysisPrefs.enableComputerAnalysis;
final showBestMoveArrow =
enableComputerAnalysis && analysisPrefs.showBestMoveArrow;
final showAnnotationsOnBoard =
enableComputerAnalysis && analysisPrefs.showAnnotations;
final evalBestMoves = enableComputerAnalysis
? ref.watch(
engineEvaluationProvider.select((s) => s.eval?.bestMoves),
)
: null;

final currentNode = analysisState.currentNode;
final annotation = makeAnnotation(currentNode.nags);
final annotation =
showAnnotationsOnBoard ? makeAnnotation(currentNode.nags) : null;

final bestMoves = evalBestMoves ?? currentNode.eval?.bestMoves;
final bestMoves = enableComputerAnalysis
? evalBestMoves ?? currentNode.eval?.bestMoves
: null;

final sanMove = currentNode.sanMove;

Expand Down Expand Up @@ -87,7 +89,10 @@ class AnalysisBoardState extends ConsumerState<AnalysisBoard> {
validMoves: analysisState.validMoves,
promotionMove: analysisState.promotionMove,
onMove: (move, {isDrop, captured}) =>
ref.read(ctrlProvider.notifier).onUserMove(move),
ref.read(ctrlProvider.notifier).onUserMove(
move,
shouldReplace: widget.shouldReplaceChildOnUserMove,
),
onPromotionSelection: (role) =>
ref.read(ctrlProvider.notifier).onPromotionSelection(role),
),
Expand Down
Loading