Skip to content
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
2 changes: 1 addition & 1 deletion lib/src/model/analysis/analysis_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ class AnalysisCurrentNode with _$AnalysisCurrentNode {
required bool isRoot,
SanMove? sanMove,
Opening? opening,
ClientEval? eval,
LocalEval? eval,
IList<PgnComment>? lichessAnalysisComments,
IList<PgnComment>? startingComments,
IList<PgnComment>? comments,
Expand Down
46 changes: 36 additions & 10 deletions lib/src/model/common/eval.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,40 @@ part 'eval.g.dart';

sealed class Eval {
String get evalString;

/// The winning chances for the given [Side].
///
/// 1 = infinitely winning
/// -1 = infinitely losing
double winningChances(Side side);
}

/// The eval from an external engine, typically lichess server side stockfish.
sealed class ClientEval extends Eval {
Position get position;

IList<PvData> get pvs;
}

/// The cloud eval coming from Lichess server
@freezed
class CloudEval with _$CloudEval implements ClientEval {
CloudEval._();

factory CloudEval({required int depth, required Position position, required IList<PvData> pvs}) =
_CloudEval;

@override
String get evalString => _evalString(cp, mate);

@override
double winningChances(Side side) => _toPov(side, _toWhiteWinningChances(cp, mate));

int? get cp => pvs[0].cp;

int? get mate => pvs[0].mate;
}

/// The eval from an external engine, typically Lichess server side Stockfish.
@Freezed(fromJson: true, toJson: true)
class ExternalEval with _$ExternalEval implements Eval {
const ExternalEval._();
Expand Down Expand Up @@ -68,12 +98,12 @@ double _toWhiteWinningChances(int? cp, int? mate) {
}
}

/// The eval from the client's own engine, typically stockfish.
/// The eval from the Stockfish local engine
@freezed
class ClientEval with _$ClientEval implements Eval {
const ClientEval._();
class LocalEval with _$LocalEval implements ClientEval {
const LocalEval._();

const factory ClientEval({
const factory LocalEval({
required Position position,
required int depth,
required int nodes,
Expand All @@ -82,7 +112,7 @@ class ClientEval with _$ClientEval implements Eval {
required Duration searchTime,
int? cp,
int? mate,
}) = _ClientEval;
}) = _LocalEval;

double get knps => nodes / millis;

Expand All @@ -104,10 +134,6 @@ class ClientEval with _$ClientEval implements Eval {
@override
String get evalString => _evalString(cp, mate);

/// The winning chances for the given [Side].
///
/// 1 = infinitely winning
/// -1 = infinitely losing
@override
double winningChances(Side side) => _toPov(side, _whiteWinningChances);

Expand Down
10 changes: 5 additions & 5 deletions lib/src/model/common/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ abstract class Node {

final Position position;

/// The client evaluation of the position.
ClientEval? eval;
/// The local evaluation of the position.
LocalEval? eval;

/// The opening associated with this node.
Opening? opening;
Expand Down Expand Up @@ -569,7 +569,7 @@ abstract class ViewNode {
SanMove? get sanMove;
Position get position;
IList<ViewBranch> get children;
ClientEval? get eval;
LocalEval? get eval;
Opening? get opening;
IList<PgnComment>? get startingComments;
IList<PgnComment>? get comments;
Expand All @@ -593,7 +593,7 @@ class ViewRoot extends ViewNode with _$ViewRoot {
const factory ViewRoot({
required Position position,
required IList<ViewBranch> children,
ClientEval? eval,
LocalEval? eval,
}) = _ViewRoot;

@override
Expand Down Expand Up @@ -630,7 +630,7 @@ class ViewBranch extends ViewNode with _$ViewBranch {
required IList<ViewBranch> children,
@Default(false) bool isCollapsed,
required bool isComputerVariation,
ClientEval? eval,
LocalEval? eval,
IList<PgnComment>? lichessAnalysisComments,
IList<PgnComment>? startingComments,
IList<PgnComment>? comments,
Expand Down
4 changes: 2 additions & 2 deletions lib/src/model/engine/evaluation_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class EvaluationService {
Stream<EvalResult>? start(
UciPath path,
Iterable<Step> steps, {
ClientEval? initialPositionEval,
LocalEval? initialPositionEval,

/// A function that returns true if the evaluation should be emitted by the
/// [EngineEvaluation] provider.
Expand Down Expand Up @@ -186,7 +186,7 @@ EvaluationService evaluationService(Ref ref) {
return service;
}

typedef EngineEvaluationState = ({String engineName, EngineState state, ClientEval? eval});
typedef EngineEvaluationState = ({String engineName, EngineState state, LocalEval? eval});

/// A provider that holds the state of the engine and the current evaluation.
@riverpod
Expand Down
4 changes: 2 additions & 2 deletions lib/src/model/engine/uci_protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class UCIProtocol {
Work? _nextWork;
bool _stopRequested = false;
void Function(String command)? _send;
ClientEval? _currentEval;
LocalEval? _currentEval;
int _expectedPvs = 1;

ValueListenable<bool> get isComputing => _isComputing;
Expand Down Expand Up @@ -136,7 +136,7 @@ class UCIProtocol {
final pvData = PvData(moves: IList(moves), cp: isMate ? null : ev, mate: isMate ? ev : null);

if (multiPv == 1) {
_currentEval = ClientEval(
_currentEval = LocalEval(
position: _work!.position,
searchTime: Duration(milliseconds: elapsedMs),
depth: depth,
Expand Down
6 changes: 3 additions & 3 deletions lib/src/model/engine/work.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:lichess_mobile/src/model/common/uci.dart';

part 'work.freezed.dart';

typedef EvalResult = (Work, ClientEval);
typedef EvalResult = (Work, LocalEval);

/// A work item for the engine.
@freezed
Expand All @@ -33,14 +33,14 @@ class Work with _$Work {
int get ply => steps.lastOrNull?.position.ply ?? initialPosition.ply;

/// Cached eval for the work position.
ClientEval? get evalCache => steps.lastOrNull?.eval;
LocalEval? get evalCache => steps.lastOrNull?.eval;
}

@freezed
class Step with _$Step {
const Step._();

const factory Step({required Position position, required SanMove sanMove, ClientEval? eval}) =
const factory Step({required Position position, required SanMove sanMove, LocalEval? eval}) =
_Step;

factory Step.fromNode(Branch node) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/study/study_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ class StudyCurrentNode with _$StudyCurrentNode {
IList<PgnComment>? startingComments,
IList<PgnComment>? comments,
IList<int>? nags,
ClientEval? eval,
LocalEval? eval,
}) = _StudyCurrentNode;

factory StudyCurrentNode.illegalPosition() {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/analysis/analysis_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class _Body extends ConsumerWidget {
isEngineAvailable && numEvalLines > 0
? EngineLines(
onTapMove: ref.read(ctrlProvider.notifier).onUserMove,
clientEval: currentNode.eval,
localEval: currentNode.eval,
isGameOver: currentNode.position.isGameOver,
)
: null,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/broadcast/broadcast_game_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class _Body extends ConsumerWidget {
engineLines:
isLocalEvaluationEnabled && numEvalLines > 0
? EngineLines(
clientEval: currentNode.eval,
localEval: currentNode.eval,
isGameOver: currentNode.position.isGameOver,
onTapMove:
ref
Expand Down
4 changes: 2 additions & 2 deletions lib/src/view/engine/engine_depth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'package:popover/popover.dart';
class EngineDepth extends ConsumerWidget {
const EngineDepth({this.defaultEval});

final ClientEval? defaultEval;
final LocalEval? defaultEval;

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down Expand Up @@ -73,7 +73,7 @@ class EngineDepth extends ConsumerWidget {
class _StockfishInfo extends ConsumerWidget {
const _StockfishInfo(this.defaultEval);

final ClientEval? defaultEval;
final LocalEval? defaultEval;

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down
6 changes: 3 additions & 3 deletions lib/src/view/engine/engine_lines.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import 'package:lichess_mobile/src/view/engine/engine_gauge.dart';
import 'package:lichess_mobile/src/widgets/buttons.dart';

class EngineLines extends ConsumerWidget {
const EngineLines({required this.onTapMove, required this.clientEval, required this.isGameOver});
const EngineLines({required this.onTapMove, required this.localEval, required this.isGameOver});
final void Function(NormalMove move) onTapMove;
final ClientEval? clientEval;
final LocalEval? localEval;
final bool isGameOver;

@override
Widget build(BuildContext context, WidgetRef ref) {
final numEvalLines = ref.watch(analysisPreferencesProvider.select((p) => p.numEvalLines));
final engineEval = ref.watch(engineEvaluationProvider).eval;
final eval = engineEval ?? clientEval;
final eval = engineEval ?? localEval;

final emptyLines = List.filled(numEvalLines, const Engineline.empty());

Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/study/study_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ class _Body extends ConsumerWidget {
engineLines:
isComputerAnalysisAllowed && isLocalEvaluationEnabled && numEvalLines > 0
? EngineLines(
clientEval: currentNode.eval,
localEval: currentNode.eval,
isGameOver: currentNode.position?.isGameOver ?? false,
onTapMove: ref.read(studyControllerProvider(id).notifier).onUserMove,
)
Expand Down
12 changes: 6 additions & 6 deletions test/model/common/node_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void main() {

expect(root.branchesOn(nodePath!), equals([root.children.first, branch]));

final eval = ClientEval(
final eval = LocalEval(
position: branch.position,
searchTime: const Duration(seconds: 10),
cp: 100,
Expand All @@ -180,7 +180,7 @@ void main() {

expect(root.mainline.map((n) => n.eval), equals([null, null, null]));

final eval = ClientEval(
final eval = LocalEval(
position: root.position,
searchTime: const Duration(seconds: 10),
cp: 100,
Expand Down Expand Up @@ -399,7 +399,7 @@ void main() {

final root = Root.fromPgnGame(PgnGame.parsePgn(pgn));
expect(root.mainline.length, equals(59));
final clientEval = ClientEval(
final localEval = LocalEval(
position: Chess.initial,
depth: 22,
nodes: 100000,
Expand All @@ -408,7 +408,7 @@ void main() {
searchTime: const Duration(milliseconds: 1230900),
cp: 23,
);
root.mainline.last.eval = clientEval;
root.mainline.last.eval = localEval;

const pgn2 = '''
1. d4 { [%clk 1:00:00] } Nf6 { [%clk 1:00:00] } 2. c4 { [%clk 1:00:00] } g6 { [%clk 1:00:00] } 3. Nc3 { [%clk 1:00:00] } Bg7 { [%clk 1:00:00] } 4. e4 { [%clk 1:00:00] } d6 { [%clk 1:00:00] } 5. f3 { [%clk 1:00:00] } O-O { [%clk 1:00:00] } 6. Be3 { [%clk 1:00:00] } e5 { [%clk 1:00:00] } 7. d5 { [%clk 1:00:00] } Nh5 { [%clk 1:00:00] } 8. Qd2 { [%clk 1:00:00] } Qh4+ { [%clk 1:00:00] } 9. g3 { [%clk 1:00:00] } Qe7 { [%clk 1:00:00] } 10. Nh3 { [%clk 1:00:00] } f5 { [%clk 0:56:44] } 11. exf5 { [%clk 0:58:18] } gxf5 { [%clk 0:55:20] } 12. O-O-O { [%clk 0:57:22] } Na6 { [%clk 0:52:30] } 13. Re1 { [%clk 0:52:22] } Nf6 { [%clk 0:48:20] } 14. Ng5 { [%clk 0:50:43] } c6 { [%clk 0:47:38] } 15. h4 { [%clk 0:50:01] } h6 { [%clk 0:46:10] } 16. Nh3 { [%clk 0:49:18] } cxd5 { [%clk 0:45:06] } 17. Bxh6 { [%clk 0:47:13] } Bxh6 { [%clk 0:44:17] } 18. Qxh6 { [%clk 0:45:59] } Bd7 { [%clk 0:43:34] } 19. cxd5 { [%clk 0:45:15] } Nc5 { [%clk 0:42:50] } 20. Kb1 { [%clk 0:44:14] } Qg7 { [%clk 0:41:29] } 21. Qd2 { [%clk 0:42:39] } e4 { [%clk 0:40:55] } 22. b4 { [%clk 0:40:31] } Na4 { [%clk 0:39:58] } 23. Nxa4 { [%clk 0:39:13] } Bxa4 { [%clk 0:38:39] } 24. Ng5 { [%clk 0:37:47] } Rfc8 { [%clk 0:37:14] } 25. Ne6 { [%clk 0:36:01] } Rc2 { [%clk 0:36:38] } 26. Qe3 { [%clk 0:34:49] } Nxd5 { [%clk 0:34:34] } 27. Nxg7 { [%clk 0:34:17] } Nxe3 { [%clk 0:34:04] } 28. Rxe3 { [%clk 0:33:12] } Kxg7 { [%clk 0:33:33] } 29. Ra3 { [%clk 0:31:18] } Rac8 { [%clk 0:32:46] } 30. Bh3 { [%clk 0:30:15] } Bd7 { [%clk 0:32:05] } 31. fxe4 { [%clk 0:29:38] } R8c4 { [%clk 0:31:11] } 32. Rxa7 { [%clk 0:27:46] } Bc6 { [%clk 0:30:37] } 33. Bxf5 { [%clk 0:27:20] } Re2 { [%clk 0:29:32] } 34. b5 { [%clk 0:26:56] } Rb4+ { [%clk 0:29:02] } 35. Ka1 { [%clk 0:25:59] } Rxb5 { [%clk 0:28:13] } 36. Rb1 { [%clk 0:25:17] } Rc5 { [%clk 0:27:47] } 37. h5 { [%clk 0:23:42] } Rh2 { [%clk 0:27:22] } 38. g4 { [%clk 0:22:55] } Kf6 { [%clk 0:26:59] } 39. Ra3 { [%clk 0:22:10] } Rc4 { [%clk 0:26:36] } 40. Re1 { [%eval 1.17,33] [%clk 0:19:30] } *
Expand All @@ -428,11 +428,11 @@ void main() {
}
// one new external eval
expect(root2.mainline.where((n) => n.externalEval != null).length, equals(1));
// one old client eval preseved
// one old local eval preseved
expect(root2.mainline.where((node) => node.eval != null).length, equals(1));
expect(
root2.mainline.firstWhereOrNull((node) => node.eval != null)?.eval,
equals(clientEval),
equals(localEval),
);
});
});
Expand Down