Skip to content

Commit

Permalink
Merge branch 'julien4215-game-phases-chart'
Browse files Browse the repository at this point in the history
  • Loading branch information
veloce committed Feb 6, 2024
2 parents 65bfaf3 + 3b8120f commit 7c2a78c
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/src/model/analysis/analysis_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class AnalysisOptions with _$AnalysisOptions {
required String pgn,
int? initialMoveCursor,
LightOpening? opening,
Division? division,

/// Optional server analysis to display player stats.
({PlayerAnalysis white, PlayerAnalysis black})? serverAnalysis,
Expand Down
11 changes: 11 additions & 0 deletions lib/src/model/common/chess.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ class LightOpening with _$LightOpening implements Opening {
_$LightOpeningFromJson(json);
}

@Freezed(fromJson: true, toJson: true)
class Division with _$Division {
const factory Division({
double? middlegame,
double? endgame,
}) = _Division;

factory Division.fromJson(Map<String, dynamic> json) =>
_$DivisionFromJson(json);
}

@freezed
class FullOpening with _$FullOpening implements Opening {
const FullOpening._();
Expand Down
9 changes: 9 additions & 0 deletions lib/src/model/game/archived_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ ArchivedGame _archivedGameFromPick(RequiredPick pick) {
final clocks = pick('clocks').asListOrNull<Duration>(
(p0) => Duration(milliseconds: p0.asIntOrThrow() * 10),
);
final division = pick('division').letOrNull(_divisionFromPick);

final initialFen = pick('initialFen').asStringOrNull();

Expand All @@ -147,6 +148,7 @@ ArchivedGame _archivedGameFromPick(RequiredPick pick) {
)
: null,
opening: data.opening,
division: division,
),
data: data,
status: data.status,
Expand Down Expand Up @@ -243,3 +245,10 @@ PlayerAnalysis _playerAnalysisFromPick(RequiredPick pick) {
accuracy: pick('accuracy').asIntOrNull(),
);
}

Division _divisionFromPick(RequiredPick pick) {
return Division(
middlegame: pick('middle').asDoubleOrNull(),
endgame: pick('end').asDoubleOrNull(),
);
}
3 changes: 3 additions & 0 deletions lib/src/model/game/game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@ class GameMeta with _$GameMeta {

/// Opening of the game, only available once finished
LightOpening? opening,

/// Game phases of the game, only avaible once finished
Division? division,
}) = _GameMeta;

factory GameMeta.fromJson(Map<String, dynamic> json) =>
Expand Down
2 changes: 2 additions & 0 deletions lib/src/model/game/game_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ class GameController extends _$GameController {
clocks: data.clocks,
meta: game.meta.copyWith(
opening: data.meta.opening,
division: data.meta.division,
),
white: game.white.copyWith(
analysis: data.white.analysis,
Expand Down Expand Up @@ -990,5 +991,6 @@ class GameState with _$GameState {
id: gameFullId,
opening: game.meta.opening,
serverAnalysis: game.serverAnalysis,
division: game.meta.division,
);
}
8 changes: 8 additions & 0 deletions lib/src/model/game/playable_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ GameMeta _playableGameMetaFromPick(RequiredPick pick) {
),
),
),
division: pick('division').letOrNull(_divisionFromPick),
);
}

Expand Down Expand Up @@ -273,3 +274,10 @@ Message _messageFromPick(RequiredPick pick) {
username: pick('u').asStringOrThrow(),
);
}

Division _divisionFromPick(RequiredPick pick) {
return Division(
middlegame: pick('middle').asDoubleOrNull(),
endgame: pick('end').asDoubleOrNull(),
);
}
24 changes: 24 additions & 0 deletions lib/src/view/analysis/analysis_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,8 @@ class ServerAnalysisSummary extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AcplChart(options),
// may be removed if game phases text is displayed vertically instead of horizontally
const SizedBox(height: 10.0),
Center(
child: SizedBox(
width: math.min(MediaQuery.sizeOf(context).width, 500),
Expand Down Expand Up @@ -1041,6 +1043,17 @@ class AcplChart extends ConsumerWidget {
final belowLineColor = Colors.white.withOpacity(0.7);
final aboveLineColor = Colors.grey.shade800.withOpacity(0.8);

VerticalLine phaseVerticalBar(double x, String label) => VerticalLine(
x: x,
color: const Color(0xFF707070),
strokeWidth: 0.5,
label: VerticalLineLabel(
style: const TextStyle(fontSize: 10),
labelResolver: (line) => label,
show: true,
),
);

final data = ref.watch(
analysisControllerProvider(options)
.select((value) => value.acplChartData),
Expand Down Expand Up @@ -1127,6 +1140,17 @@ class AcplChart extends ConsumerWidget {
color: mainLineColor,
strokeWidth: 1.0,
),
phaseVerticalBar(0.0, context.l10n.opening),
if (options.division?.endgame != null)
phaseVerticalBar(
options.division!.endgame!,
context.l10n.endgame,
),
if (options.division?.middlegame != null)
phaseVerticalBar(
options.division!.middlegame!,
context.l10n.middlegame,
),
],
),
gridData: const FlGridData(show: false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ class _BodyState extends ConsumerState<_Body> {
initialMoveCursor: stepCursor,
orientation: game.youAre,
id: game.id,
division: game.meta.division,
),
title: context.l10n.analysis,
),
Expand Down
1 change: 1 addition & 0 deletions lib/src/view/game/archived_game_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class _BottomBar extends ConsumerWidget {
id: gameData.id,
opening: gameData.opening,
serverAnalysis: game.serverAnalysis,
division: game.meta.division,
),
),
);
Expand Down
1 change: 1 addition & 0 deletions lib/src/view/game/game_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class _ContextMenu extends ConsumerWidget {
id: game.id,
opening: game.opening,
serverAnalysis: serverAnalysis,
division: archivedGame.meta.division,
),
),
);
Expand Down
4 changes: 2 additions & 2 deletions test/model/game/game_repository_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void main() {
group('GameRepository.getGame', () {
test('game with clocks', () async {
const testResponse = '''
{"id":"qVChCOTc","rated":false,"variant":"standard","speed":"blitz","perf":"blitz","createdAt":1673443822389,"lastMoveAt":1673444036416,"status":"mate","players":{"white":{"aiLevel":1},"black":{"user":{"name":"veloce","patron":true,"id":"veloce"},"rating":1435,"provisional":true}},"winner":"black","opening":{"eco":"C20","name":"King's Pawn Game: Wayward Queen Attack, Kiddie Countergambit","ply":4},"moves":"e4 e5 Qh5 Nf6 Qxe5+ Be7 b3 d6 Qb5+ Bd7 Qxb7 Nc6 Ba3 Rb8 Qa6 Nxe4 Bb2 O-O Nc3 Nb4 Nf3 Nxa6 Nd5 Nb4 Nxe7+ Qxe7 Nd4 Qf6 f4 Qe7 Ke2 Ng3+ Kd1 Nxh1 Bc4 Nf2+ Kc1 Qe1#","clocks":[18003,18003,17915,17627,17771,16691,17667,16243,17475,15459,17355,14779,17155,13795,16915,13267,14771,11955,14451,10995,14339,10203,13899,9099,12427,8379,12003,7547,11787,6691,11355,6091,11147,5763,10851,5099,10635,4657],"clock":{"initial":180,"increment":0,"totalTime":180}}
{"id":"qVChCOTc","rated":false,"variant":"standard","speed":"blitz","perf":"blitz","createdAt":1673443822389,"lastMoveAt":1673444036416,"status":"mate","players":{"white":{"aiLevel":1},"black":{"user":{"name":"veloce","patron":true,"id":"veloce"},"rating":1435,"provisional":true}},"winner":"black","opening":{"eco":"C20","name":"King's Pawn Game: Wayward Queen Attack, Kiddie Countergambit","ply":4},"moves":"e4 e5 Qh5 Nf6 Qxe5+ Be7 b3 d6 Qb5+ Bd7 Qxb7 Nc6 Ba3 Rb8 Qa6 Nxe4 Bb2 O-O Nc3 Nb4 Nf3 Nxa6 Nd5 Nb4 Nxe7+ Qxe7 Nd4 Qf6 f4 Qe7 Ke2 Ng3+ Kd1 Nxh1 Bc4 Nf2+ Kc1 Qe1#","clocks":[18003,18003,17915,17627,17771,16691,17667,16243,17475,15459,17355,14779,17155,13795,16915,13267,14771,11955,14451,10995,14339,10203,13899,9099,12427,8379,12003,7547,11787,6691,11355,6091,11147,5763,10851,5099,10635,4657],"clock":{"initial":180,"increment":0,"totalTime":180},"division":{"middle":18,"end":42}}
''';

when(
Expand All @@ -104,7 +104,7 @@ void main() {

test('threeCheck game', () async {
const testResponse = '''
{"id":"1vdsvmxp","rated":true,"variant":"threeCheck","speed":"bullet","perf":"threeCheck","createdAt":1604194310939,"lastMoveAt":1604194361831,"status":"variantEnd","players":{"white":{"user":{"name":"Zhigalko_Sergei","title":"GM","patron":true,"id":"zhigalko_sergei"},"rating":2448,"ratingDiff":6,"analysis":{"inaccuracy":1,"mistake":1,"blunder":1,"acpl":75}},"black":{"user":{"name":"catask","id":"catask"},"rating":2485,"ratingDiff":-6,"analysis":{"inaccuracy":1,"mistake":0,"blunder":2,"acpl":115}}},"winner":"white","opening":{"eco":"B02","name":"Alekhine Defense: Scandinavian Variation, Geschev Gambit","ply":6},"moves":"e4 c6 Nc3 d5 exd5 Nf6 Nf3 e5 Bc4 Bd6 O-O O-O h3 e4 Kh1 exf3 Qxf3 cxd5 Nxd5 Nxd5 Bxd5 Nc6 Re1 Be6 Rxe6 fxe6 Bxe6+ Kh8 Qh5 h6 Qg6 Qf6 Qh7+ Kxh7 Bf5+","analysis":[{"eval":340},{"eval":359},{"eval":231},{"eval":300,"best":"g8f6","variation":"Nf6 e5 d5 d4 Ne4 Bd3 Bf5 Nf3 e6 O-O","judgment":{"name":"Inaccuracy","comment":"Inaccuracy. Nf6 was best."}},{"eval":262},{"eval":286},{"eval":184,"best":"f1c4","variation":"Bc4 e6 dxe6 Bxe6 Bxe6 fxe6 Qe2 Qd7 Nf3 Bd6","judgment":{"name":"Inaccuracy","comment":"Inaccuracy. Bc4 was best."}},{"eval":235},{"eval":193},{"eval":243},{"eval":269},{"eval":219},{"eval":-358,"best":"d2d3","variation":"d3 Bg4 h3 e4 Nxe4 Bh2+ Kh1 Nxe4 dxe4 Qf6","judgment":{"name":"Blunder","comment":"Blunder. d3 was best."}},{"eval":-376},{"eval":-386},{"eval":-383},{"eval":-405},{"eval":-363},{"eval":-372},{"eval":-369},{"eval":-345},{"eval":-276},{"eval":-507,"best":"b2b3","variation":"b3 Be6","judgment":{"name":"Mistake","comment":"Mistake. b3 was best."}},{"eval":-49,"best":"c6e5","variation":"Ne5 Qh5","judgment":{"name":"Blunder","comment":"Blunder. Ne5 was best."}},{"eval":-170},{"mate":7,"best":"g8h8","variation":"Kh8 Rh6","judgment":{"name":"Blunder","comment":"Checkmate is now unavoidable. Kh8 was best."}},{"mate":6},{"mate":6},{"mate":5},{"mate":3},{"mate":2},{"mate":2},{"mate":1},{"mate":1}],"clock":{"initial":60,"increment":0,"totalTime":60}}
{"id":"1vdsvmxp","rated":true,"variant":"threeCheck","speed":"bullet","perf":"threeCheck","createdAt":1604194310939,"lastMoveAt":1604194361831,"status":"variantEnd","players":{"white":{"user":{"name":"Zhigalko_Sergei","title":"GM","patron":true,"id":"zhigalko_sergei"},"rating":2448,"ratingDiff":6,"analysis":{"inaccuracy":1,"mistake":1,"blunder":1,"acpl":75}},"black":{"user":{"name":"catask","id":"catask"},"rating":2485,"ratingDiff":-6,"analysis":{"inaccuracy":1,"mistake":0,"blunder":2,"acpl":115}}},"winner":"white","opening":{"eco":"B02","name":"Alekhine Defense: Scandinavian Variation, Geschev Gambit","ply":6},"moves":"e4 c6 Nc3 d5 exd5 Nf6 Nf3 e5 Bc4 Bd6 O-O O-O h3 e4 Kh1 exf3 Qxf3 cxd5 Nxd5 Nxd5 Bxd5 Nc6 Re1 Be6 Rxe6 fxe6 Bxe6+ Kh8 Qh5 h6 Qg6 Qf6 Qh7+ Kxh7 Bf5+","analysis":[{"eval":340},{"eval":359},{"eval":231},{"eval":300,"best":"g8f6","variation":"Nf6 e5 d5 d4 Ne4 Bd3 Bf5 Nf3 e6 O-O","judgment":{"name":"Inaccuracy","comment":"Inaccuracy. Nf6 was best."}},{"eval":262},{"eval":286},{"eval":184,"best":"f1c4","variation":"Bc4 e6 dxe6 Bxe6 Bxe6 fxe6 Qe2 Qd7 Nf3 Bd6","judgment":{"name":"Inaccuracy","comment":"Inaccuracy. Bc4 was best."}},{"eval":235},{"eval":193},{"eval":243},{"eval":269},{"eval":219},{"eval":-358,"best":"d2d3","variation":"d3 Bg4 h3 e4 Nxe4 Bh2+ Kh1 Nxe4 dxe4 Qf6","judgment":{"name":"Blunder","comment":"Blunder. d3 was best."}},{"eval":-376},{"eval":-386},{"eval":-383},{"eval":-405},{"eval":-363},{"eval":-372},{"eval":-369},{"eval":-345},{"eval":-276},{"eval":-507,"best":"b2b3","variation":"b3 Be6","judgment":{"name":"Mistake","comment":"Mistake. b3 was best."}},{"eval":-49,"best":"c6e5","variation":"Ne5 Qh5","judgment":{"name":"Blunder","comment":"Blunder. Ne5 was best."}},{"eval":-170},{"mate":7,"best":"g8h8","variation":"Kh8 Rh6","judgment":{"name":"Blunder","comment":"Checkmate is now unavoidable. Kh8 was best."}},{"mate":6},{"mate":6},{"mate":5},{"mate":3},{"mate":2},{"mate":2},{"mate":1},{"mate":1}],"clock":{"initial":60,"increment":0,"totalTime":60},"division":{"middle":18,"end":42}}
''';

when(
Expand Down

0 comments on commit 7c2a78c

Please sign in to comment.