Skip to content

Commit

Permalink
Merge branch '468-show-captured-pieces-instead-of-material-imbalance'…
Browse files Browse the repository at this point in the history
… of https://github.com/Jimima/lichess-mobile into Jimima-468-show-captured-pieces-instead-of-material-imbalance
  • Loading branch information
veloce committed Dec 9, 2024
2 parents 6773a74 + 442a4fd commit 48df046
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 53 deletions.
36 changes: 33 additions & 3 deletions lib/src/model/game/material_diff.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class MaterialDiffSide with _$MaterialDiffSide {
const factory MaterialDiffSide({
required IMap<Role, int> pieces,
required int score,
required IMap<Role, int> capturedPieces,
}) = _MaterialDiffSide;
}

Expand All @@ -30,11 +31,32 @@ class MaterialDiff with _$MaterialDiff {
required MaterialDiffSide white,
}) = _MaterialDiff;

factory MaterialDiff.fromBoard(Board board) {
factory MaterialDiff.fromBoard(Board board, {Board? startingPosition}) {
int score = 0;
final IMap<Role, int> blackCount = board.materialCount(Side.black);
final IMap<Role, int> whiteCount = board.materialCount(Side.white);

final IMap<Role, int> blackStartingCount =
startingPosition?.materialCount(Side.black) ??
Board.standard.materialCount(Side.black);
final IMap<Role, int> whiteStartingCount =
startingPosition?.materialCount(Side.white) ??
Board.standard.materialCount(Side.white);

IMap<Role, int> subtractPieceCounts(
IMap<Role, int> startingCount,
IMap<Role, int> subtractCount,
) {
return startingCount.map(
(role, count) => MapEntry(role, count - (subtractCount.get(role) ?? 0)),
);
}

final IMap<Role, int> blackCapturedPieces =
subtractPieceCounts(whiteStartingCount, whiteCount);
final IMap<Role, int> whiteCapturedPieces =
subtractPieceCounts(blackStartingCount, blackCount);

Map<Role, int> count;
Map<Role, int> black;
Map<Role, int> white;
Expand Down Expand Up @@ -80,8 +102,16 @@ class MaterialDiff with _$MaterialDiff {
});

return MaterialDiff(
black: MaterialDiffSide(pieces: black.toIMap(), score: -score),
white: MaterialDiffSide(pieces: white.toIMap(), score: score),
black: MaterialDiffSide(
pieces: black.toIMap(),
score: -score,
capturedPieces: blackCapturedPieces,
),
white: MaterialDiffSide(
pieces: white.toIMap(),
score: score,
capturedPieces: whiteCapturedPieces,
),
);
}

Expand Down
37 changes: 31 additions & 6 deletions lib/src/model/settings/board_preferences.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:chessground/chessground.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/l10n/l10n.dart';
import 'package:lichess_mobile/src/model/settings/preferences_storage.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/utils/color_palette.dart';
Expand Down Expand Up @@ -84,9 +86,11 @@ class BoardPreferences extends _$BoardPreferences
return save(state.copyWith(dragTargetKind: dragTargetKind));
}

Future<void> toggleShowMaterialDifference() {
Future<void> setMaterialDifferenceFormat(
MaterialDifferenceFormat materialDifferenceFormat,
) {
return save(
state.copyWith(showMaterialDifference: !state.showMaterialDifference),
state.copyWith(materialDifferenceFormat: materialDifferenceFormat),
);
}

Expand Down Expand Up @@ -120,11 +124,11 @@ class BoardPrefs with _$BoardPrefs implements Serializable {
required bool boardHighlights,
required bool coordinates,
required bool pieceAnimation,
required bool showMaterialDifference,
@JsonKey(
defaultValue: ClockPosition.right,
unknownEnumValue: ClockPosition.right,
defaultValue: MaterialDifferenceFormat.materialDifference,
unknownEnumValue: MaterialDifferenceFormat.materialDifference,
)
required MaterialDifferenceFormat materialDifferenceFormat,
required ClockPosition clockPosition,
@JsonKey(
defaultValue: PieceShiftMethod.either,
Expand Down Expand Up @@ -157,7 +161,7 @@ class BoardPrefs with _$BoardPrefs implements Serializable {
boardHighlights: true,
coordinates: true,
pieceAnimation: true,
showMaterialDifference: true,
materialDifferenceFormat: MaterialDifferenceFormat.materialDifference,
clockPosition: ClockPosition.right,
pieceShiftMethod: PieceShiftMethod.either,
enableShapeDrawings: true,
Expand Down Expand Up @@ -324,6 +328,27 @@ enum BoardTheme {
);
}

enum MaterialDifferenceFormat {
materialDifference(label: 'Material difference'),
capturedPieces(label: 'Captured pieces'),
hidden(label: 'Hidden');

const MaterialDifferenceFormat({
required this.label,
});

final String label;

bool get visible => this != MaterialDifferenceFormat.hidden;

String l10n(AppLocalizations l10n) => switch (this) {
//TODO: Add l10n
MaterialDifferenceFormat.materialDifference => materialDifference.label,
MaterialDifferenceFormat.capturedPieces => capturedPieces.label,
MaterialDifferenceFormat.hidden => hidden.label,
};
}

enum ClockPosition {
left,
right;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ class _BodyState extends ConsumerState<_Body> {

@override
Widget build(BuildContext context) {
final shouldShowMaterialDiff = ref.watch(
final materialDifference = ref.watch(
boardPreferencesProvider.select(
(prefs) => prefs.showMaterialDifference,
(prefs) => prefs.materialDifferenceFormat,
),
);

Expand All @@ -157,9 +157,10 @@ class _BodyState extends ConsumerState<_Body> {

final black = GamePlayer(
player: game.black,
materialDiff: shouldShowMaterialDiff
materialDiff: materialDifference.visible
? game.materialDiffAt(stepCursor, Side.black)
: null,
materialDifferenceFormat: materialDifference,
shouldLinkToUserProfile: false,
mePlaying: youAre == Side.black,
confirmMoveCallbacks: youAre == Side.black && moveToConfirm != null
Expand All @@ -179,9 +180,10 @@ class _BodyState extends ConsumerState<_Body> {
);
final white = GamePlayer(
player: game.white,
materialDiff: shouldShowMaterialDiff
materialDiff: materialDifference.visible
? game.materialDiffAt(stepCursor, Side.white)
: null,
materialDifferenceFormat: materialDifference,
shouldLinkToUserProfile: false,
mePlaying: youAre == Side.white,
confirmMoveCallbacks: youAre == Side.white && moveToConfirm != null
Expand Down
6 changes: 4 additions & 2 deletions lib/src/view/game/game_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ class GameBody extends ConsumerWidget {

final black = GamePlayer(
player: gameState.game.black,
materialDiff: boardPreferences.showMaterialDifference
materialDiff: boardPreferences.materialDifferenceFormat.visible
? gameState.game.materialDiffAt(gameState.stepCursor, Side.black)
: null,
materialDifferenceFormat: boardPreferences.materialDifferenceFormat,
timeToMove: gameState.game.sideToMove == Side.black
? gameState.timeToMove
: null,
Expand Down Expand Up @@ -174,9 +175,10 @@ class GameBody extends ConsumerWidget {
);
final white = GamePlayer(
player: gameState.game.white,
materialDiff: boardPreferences.showMaterialDifference
materialDiff: boardPreferences.materialDifferenceFormat.visible
? gameState.game.materialDiffAt(gameState.stepCursor, Side.white)
: null,
materialDifferenceFormat: boardPreferences.materialDifferenceFormat,
timeToMove: gameState.game.sideToMove == Side.white
? gameState.timeToMove
: null,
Expand Down
76 changes: 49 additions & 27 deletions lib/src/view/game/game_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:dartchess/dartchess.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand All @@ -28,6 +29,7 @@ class GamePlayer extends StatelessWidget {
required this.player,
this.clock,
this.materialDiff,
this.materialDifferenceFormat,
this.confirmMoveCallbacks,
this.timeToMove,
this.shouldLinkToUserProfile = true,
Expand All @@ -40,6 +42,7 @@ class GamePlayer extends StatelessWidget {
final Player player;
final Widget? clock;
final MaterialDiffSide? materialDiff;
final MaterialDifferenceFormat? materialDifferenceFormat;

/// if confirm move preference is enabled, used to display confirmation buttons
final ({VoidCallback confirm, VoidCallback cancel})? confirmMoveCallbacks;
Expand Down Expand Up @@ -148,33 +151,12 @@ class GamePlayer extends StatelessWidget {
if (timeToMove != null)
MoveExpiration(timeToMove: timeToMove!, mePlaying: mePlaying)
else if (materialDiff != null)
Row(
mainAxisAlignment: clockPosition == ClockPosition.right
? MainAxisAlignment.start
: MainAxisAlignment.end,
children: [
for (final role in Role.values)
for (int i = 0; i < materialDiff!.pieces[role]!; i++)
Icon(
_iconByRole[role],
size: 13,
color: Colors.grey,
),
const SizedBox(width: 3),
Text(
style: const TextStyle(
fontSize: 13,
color: Colors.grey,
),
materialDiff != null && materialDiff!.score > 0
? '+${materialDiff!.score}'
: '',
),
],
)
else
// to avoid shifts use an empty text widget
const Text('', style: TextStyle(fontSize: 13)),
MaterialDifferenceDisplay(
materialDiff: materialDiff!,
materialDifferenceFormat: materialDifferenceFormat,
),
// to avoid shifts use an empty text widget
const Text('', style: TextStyle(fontSize: 13)),
],
);

Expand Down Expand Up @@ -347,6 +329,46 @@ class _MoveExpirationState extends ConsumerState<MoveExpiration> {
}
}

class MaterialDifferenceDisplay extends StatelessWidget {
const MaterialDifferenceDisplay({
required this.materialDiff,
this.materialDifferenceFormat = MaterialDifferenceFormat.materialDifference,
});

final MaterialDiffSide materialDiff;
final MaterialDifferenceFormat? materialDifferenceFormat;

@override
Widget build(BuildContext context) {
final IMap<Role, int> piecesToRender =
(materialDifferenceFormat == MaterialDifferenceFormat.capturedPieces
? materialDiff.capturedPieces
: materialDiff.pieces);

return materialDifferenceFormat?.visible ?? true
? Row(
children: [
for (final role in Role.values)
for (int i = 0; i < piecesToRender[role]!; i++)
Icon(
_iconByRole[role],
size: 13,
color: Colors.grey,
),
const SizedBox(width: 3),
Text(
style: const TextStyle(
fontSize: 13,
color: Colors.grey,
),
materialDiff.score > 0 ? '+${materialDiff.score}' : '',
),
],
)
: const SizedBox.shrink();
}
}

const Map<Role, IconData> _iconByRole = {
Role.king: LichessIcons.chess_king,
Role.queen: LichessIcons.chess_queen,
Expand Down
1 change: 0 additions & 1 deletion lib/src/view/game/game_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'package:lichess_mobile/src/view/settings/board_settings_screen.dart';
import 'package:lichess_mobile/src/widgets/adaptive_bottom_sheet.dart';
import 'package:lichess_mobile/src/widgets/list.dart';
import 'package:lichess_mobile/src/widgets/settings.dart';

import 'game_screen_providers.dart';

class GameSettings extends ConsumerWidget {
Expand Down
3 changes: 2 additions & 1 deletion lib/src/view/over_the_board/over_the_board_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,10 @@ class _Player extends ConsumerWidget {
name: side.name.capitalize(),
),
),
materialDiff: boardPreferences.showMaterialDifference
materialDiff: boardPreferences.materialDifferenceFormat.visible
? gameState.currentMaterialDiff(side)
: null,
materialDifferenceFormat: boardPreferences.materialDifferenceFormat,
shouldLinkToUserProfile: false,
clock: clock.timeIncrement.isInfinite
? null
Expand Down
Loading

0 comments on commit 48df046

Please sign in to comment.