mirror of
https://github.com/lichess-org/mobile.git
synced 2026-05-26 13:50:52 +00:00
Variant material diff (#2655)
This commit is contained in:
@@ -221,7 +221,7 @@ ExportedGame _archivedGameFromPick(RequiredPick pick, {bool withBookmarked = fal
|
||||
GameStep(
|
||||
sanMove: SanMove(san, move),
|
||||
position: position,
|
||||
diff: MaterialDiff.fromBoard(position.board),
|
||||
diff: MaterialDiff.fromPosition(position),
|
||||
archivedWhiteClock: index.isOdd ? stepClock : clock,
|
||||
archivedBlackClock: index.isEven ? stepClock : clock,
|
||||
),
|
||||
|
||||
@@ -412,7 +412,7 @@ IList<GameStep> stepsFromJson(String json) {
|
||||
GameStep(
|
||||
position: position,
|
||||
sanMove: SanMove(san, move),
|
||||
diff: MaterialDiff.fromBoard(position.board),
|
||||
diff: MaterialDiff.fromPosition(position),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class GameController extends AsyncNotifier<GameState> {
|
||||
final newStep = GameStep(
|
||||
position: newPos,
|
||||
sanMove: sanMove,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
state = AsyncValue.data(
|
||||
@@ -286,7 +286,7 @@ class GameController extends AsyncNotifier<GameState> {
|
||||
final newStep = GameStep(
|
||||
position: newPos,
|
||||
sanMove: sanMove,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
state = AsyncValue.data(
|
||||
@@ -671,7 +671,7 @@ class GameController extends AsyncNotifier<GameState> {
|
||||
final newStep = GameStep(
|
||||
sanMove: sanMove,
|
||||
position: newPos,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
newState = newState.copyWith(
|
||||
|
||||
@@ -10,7 +10,13 @@ sealed class MaterialDiffSide with _$MaterialDiffSide {
|
||||
required IMap<Role, int> pieces,
|
||||
required int score,
|
||||
required IMap<Role, int> capturedPieces,
|
||||
|
||||
/// Number of checks given by this side. Only relevant in the 3-check variant, null otherwise.
|
||||
required int? checksGiven,
|
||||
}) = _MaterialDiffSide;
|
||||
|
||||
factory MaterialDiffSide.empty() =>
|
||||
MaterialDiffSide(pieces: IMap(), score: 0, capturedPieces: IMap(), checksGiven: null);
|
||||
}
|
||||
|
||||
const IMap<Role, int> pieceScores = IMapConst({
|
||||
@@ -29,15 +35,19 @@ sealed class MaterialDiff with _$MaterialDiff {
|
||||
const factory MaterialDiff({required MaterialDiffSide black, required MaterialDiffSide white}) =
|
||||
_MaterialDiff;
|
||||
|
||||
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);
|
||||
factory MaterialDiff.fromPosition(Position position) {
|
||||
if (position.rule == Rule.crazyhouse || position.rule == Rule.horde) {
|
||||
return MaterialDiff(black: MaterialDiffSide.empty(), white: MaterialDiffSide.empty());
|
||||
}
|
||||
|
||||
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);
|
||||
int score = 0;
|
||||
final IMap<Role, int> blackCount = position.board.materialCount(Side.black);
|
||||
final IMap<Role, int> whiteCount = position.board.materialCount(Side.white);
|
||||
|
||||
final startingPosition = Position.initialPosition(position.rule);
|
||||
|
||||
final IMap<Role, int> blackStartingCount = startingPosition.board.materialCount(Side.black);
|
||||
final IMap<Role, int> whiteStartingCount = startingPosition.board.materialCount(Side.white);
|
||||
|
||||
IMap<Role, int> subtractPieceCounts(
|
||||
IMap<Role, int> startingCount,
|
||||
@@ -95,16 +105,24 @@ sealed class MaterialDiff with _$MaterialDiff {
|
||||
}
|
||||
});
|
||||
|
||||
int? checksGiven(Side side) => switch (position) {
|
||||
ThreeCheck(remainingChecks: (final white, final black)) =>
|
||||
3 - (side == Side.white ? white : black),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return MaterialDiff(
|
||||
black: MaterialDiffSide(
|
||||
pieces: black.toIMap(),
|
||||
score: -score,
|
||||
capturedPieces: blackCapturedPieces,
|
||||
checksGiven: checksGiven(Side.black),
|
||||
),
|
||||
white: MaterialDiffSide(
|
||||
pieces: white.toIMap(),
|
||||
score: score,
|
||||
capturedPieces: whiteCapturedPieces,
|
||||
checksGiven: checksGiven(Side.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ PlayableGame _playableGameFromPick(RequiredPick pick) {
|
||||
GameStep(
|
||||
sanMove: SanMove(san, move),
|
||||
position: position,
|
||||
diff: MaterialDiff.fromBoard(position.board),
|
||||
diff: MaterialDiff.fromPosition(position),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ class OfflineComputerGameController extends Notifier<OfflineComputerGameState> {
|
||||
final newStep = GameStep(
|
||||
position: newPos,
|
||||
sanMove: sanMove,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
_clearHints();
|
||||
|
||||
@@ -61,7 +61,7 @@ class OverTheBoardGameController extends Notifier<OverTheBoardGameState> {
|
||||
final newStep = GameStep(
|
||||
position: newPos,
|
||||
sanMove: sanMove,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
// In an over-the-board game, we support "implicit takebacks":
|
||||
|
||||
@@ -213,7 +213,7 @@ class TvController extends AsyncNotifier<TvState> {
|
||||
final newStep = GameStep(
|
||||
sanMove: sanMove,
|
||||
position: newPos,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
TvState newState = curState.copyWith(
|
||||
|
||||
@@ -332,7 +332,7 @@ class _BodyState extends ConsumerState<_Body> {
|
||||
final newStep = GameStep(
|
||||
position: newPos,
|
||||
sanMove: sanMove,
|
||||
diff: MaterialDiff.fromBoard(newPos.board),
|
||||
diff: MaterialDiff.fromPosition(newPos),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
|
||||
@@ -32,13 +32,15 @@ class MaterialDifferenceDisplay extends StatelessWidget {
|
||||
: materialDiff!.pieces)
|
||||
: IMap();
|
||||
|
||||
Icon roleIcon(Role role) => Icon(_iconByRole[role], size: 13, color: textShade(context, 0.5));
|
||||
|
||||
return materialDifferenceFormat?.visible ?? true
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final role in Role.values)
|
||||
for (int i = 0; i < (piecesToRender.get(role) ?? 0); i++)
|
||||
Icon(_iconByRole[role], size: 13, color: textShade(context, 0.5)),
|
||||
for (int i = 0; i < (piecesToRender.get(role) ?? 0); i++) roleIcon(role),
|
||||
...Iterable.generate(materialDiff?.checksGiven ?? 0, (_) => roleIcon(Role.king)),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
// a text font size of 14 is used to ensure that the text will take more vertical space
|
||||
|
||||
@@ -77,7 +77,7 @@ IList<GameStep> makeSteps(String pgn) {
|
||||
GameStep(
|
||||
position: position,
|
||||
sanMove: SanMove(san, move),
|
||||
diff: MaterialDiff.fromBoard(position.board),
|
||||
diff: MaterialDiff.fromPosition(position),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ IList<GameStep> _makeSteps(String pgn) {
|
||||
GameStep(
|
||||
position: position,
|
||||
sanMove: SanMove(san, move),
|
||||
diff: MaterialDiff.fromBoard(position.board),
|
||||
diff: MaterialDiff.fromPosition(position),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@ import 'package:lichess_mobile/src/model/game/material_diff.dart';
|
||||
|
||||
void main() {
|
||||
group('GameMaterialDiff', () {
|
||||
test('generation from board', () {
|
||||
final Board board = Board.parseFen('r5k1/3Q1pp1/2p4p/4P1b1/p3R3/3P4/6PP/R5K1');
|
||||
final MaterialDiff diff = MaterialDiff.fromBoard(board);
|
||||
test('generation from position', () {
|
||||
final Position position = Position.setupPosition(
|
||||
Rule.chess,
|
||||
Setup.parseFen('r5k1/3Q1pp1/2p4p/4P1b1/p3R3/3P4/6PP/R5K1'),
|
||||
);
|
||||
final MaterialDiff diff = MaterialDiff.fromPosition(position);
|
||||
|
||||
expect(diff.bySide(Side.black).score, equals(-10));
|
||||
expect(diff.bySide(Side.white).score, equals(10));
|
||||
@@ -63,6 +66,51 @@ void main() {
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(diff.bySide(Side.white).checksGiven, null);
|
||||
expect(diff.bySide(Side.black).checksGiven, null);
|
||||
});
|
||||
test('three-check', () {
|
||||
final Position position = Position.setupPosition(
|
||||
Rule.threecheck,
|
||||
Setup.parseFen('rnbqkbnr/ppp1pppp/3p4/1B6/4P3/8/PPPP1PPP/RNBQK1NR b KQkq - 2+3 1 2'),
|
||||
);
|
||||
final MaterialDiff diff = MaterialDiff.fromPosition(position);
|
||||
|
||||
expect(diff.bySide(Side.white).checksGiven, 1);
|
||||
expect(diff.bySide(Side.black).checksGiven, 0);
|
||||
});
|
||||
|
||||
test('horde returns empty material diff', () {
|
||||
final Position position = Position.setupPosition(
|
||||
Rule.horde,
|
||||
Setup.parseFen('rnbqkbnr/pppppppp/8/8/pppppppp/pppppppp/PPPPPPPP/PPPPPPPP w kq - 0 1'),
|
||||
);
|
||||
final MaterialDiff diff = MaterialDiff.fromPosition(position);
|
||||
|
||||
for (final side in Side.values) {
|
||||
final sideDiff = diff.bySide(side);
|
||||
expect(sideDiff.score, 0);
|
||||
expect(sideDiff.pieces.isEmpty, isTrue);
|
||||
expect(sideDiff.capturedPieces.isEmpty, isTrue);
|
||||
expect(sideDiff.checksGiven, null);
|
||||
}
|
||||
});
|
||||
|
||||
test('crazyhouse returns empty material diff', () {
|
||||
final Position position = Position.setupPosition(
|
||||
Rule.crazyhouse,
|
||||
Setup.parseFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'),
|
||||
);
|
||||
final MaterialDiff diff = MaterialDiff.fromPosition(position);
|
||||
|
||||
for (final side in Side.values) {
|
||||
final sideDiff = diff.bySide(side);
|
||||
expect(sideDiff.score, 0);
|
||||
expect(sideDiff.pieces.isEmpty, isTrue);
|
||||
expect(sideDiff.capturedPieces.isEmpty, isTrue);
|
||||
expect(sideDiff.checksGiven, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user