mchess-client/lib/utils/chess_utils.dart
Marco 7c5439a635 Display last played move.
We now color the squares of the last move. This makes the state of the
board clearer and shows whose turn it is.
2023-08-19 03:45:03 +02:00

335 lines
8.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mchess/api/move.dart';
import 'package:mchess/api/websocket_message.dart';
import 'package:quiver/core.dart';
Map<ChessPieceAssetKey, String> chessPiecesAssets = {
ChessPieceAssetKey(
pieceClass: ChessPieceClass.pawn,
color: ChessColor.white,
): 'assets/pieces/white/pawn.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.bishop,
color: ChessColor.white,
): 'assets/pieces/white/bishop.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.knight,
color: ChessColor.white,
): 'assets/pieces/white/knight.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.rook,
color: ChessColor.white,
): 'assets/pieces/white/rook.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.queen,
color: ChessColor.white,
): 'assets/pieces/white/queen.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.king,
color: ChessColor.white,
): 'assets/pieces/white/king.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.pawn,
color: ChessColor.black,
): 'assets/pieces/black/pawn.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.bishop,
color: ChessColor.black,
): 'assets/pieces/black/bishop.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.knight,
color: ChessColor.black,
): 'assets/pieces/black/knight.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.rook,
color: ChessColor.black,
): 'assets/pieces/black/rook.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.queen,
color: ChessColor.black,
): 'assets/pieces/black/queen.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.king,
color: ChessColor.black,
): 'assets/pieces/black/king.svg',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.none,
color: ChessColor.black,
): 'assets/empty.svg',
};
Map<ChessPieceAssetKey, String> chessPiecesShortName = {
ChessPieceAssetKey(
pieceClass: ChessPieceClass.pawn,
color: ChessColor.white,
): 'P',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.bishop,
color: ChessColor.white,
): 'B',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.knight,
color: ChessColor.white,
): 'N',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.rook,
color: ChessColor.white,
): 'R',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.queen,
color: ChessColor.white,
): 'Q',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.king,
color: ChessColor.white,
): 'K',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.pawn,
color: ChessColor.black,
): 'p',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.bishop,
color: ChessColor.black,
): 'b',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.knight,
color: ChessColor.black,
): 'n',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.rook,
color: ChessColor.black,
): 'r',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.queen,
color: ChessColor.black,
): 'q',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.king,
color: ChessColor.black,
): 'k',
ChessPieceAssetKey(
pieceClass: ChessPieceClass.none,
color: ChessColor.black,
): '-',
};
Map<String, ChessPiece> pieceFromShortname = {
'P': ChessPiece(ChessPieceClass.pawn, ChessColor.white),
'R': ChessPiece(ChessPieceClass.rook, ChessColor.white),
'N': ChessPiece(ChessPieceClass.knight, ChessColor.white),
'B': ChessPiece(ChessPieceClass.bishop, ChessColor.white),
'K': ChessPiece(ChessPieceClass.king, ChessColor.white),
'Q': ChessPiece(ChessPieceClass.queen, ChessColor.white),
'p': ChessPiece(ChessPieceClass.pawn, ChessColor.black),
'r': ChessPiece(ChessPieceClass.rook, ChessColor.black),
'n': ChessPiece(ChessPieceClass.knight, ChessColor.black),
'b': ChessPiece(ChessPieceClass.bishop, ChessColor.black),
'k': ChessPiece(ChessPieceClass.king, ChessColor.black),
'q': ChessPiece(ChessPieceClass.queen, ChessColor.black),
};
enum ChessColor {
black,
white;
ChessColor getOpposite() {
if (name == 'black') {
return white;
} else {
return black;
}
}
static ChessColor fromApiColor(ApiColor color) {
if (color == ApiColor.black) {
return black;
} else {
return white;
}
}
}
class ChessCoordinate {
final int column;
final int row;
ChessCoordinate(this.column, this.row);
ChessCoordinate.copyFrom(ChessCoordinate original)
: column = original.column,
row = original.row;
factory ChessCoordinate.fromApiCoordinate(ApiCoordinate apiCoordinate) {
return ChessCoordinate(apiCoordinate.col, apiCoordinate.row);
}
factory ChessCoordinate.fromString(String coordString) {
var column = int.parse(coordString[0]);
var row = int.parse(coordString[1]);
return ChessCoordinate(column, row);
}
@override
int get hashCode {
return hash2(column, row);
}
@override
operator ==(other) {
return other is ChessCoordinate &&
other.column == column &&
other.row == row;
}
String toAlphabetical() {
Map<int, String> columnMap = {
1: "a",
2: "b",
3: "c",
4: "d",
5: "e",
6: "f",
7: "g",
8: "h"
};
String rowStr = row.toString();
String colStr = columnMap[column]!;
return '$colStr$rowStr';
}
ApiCoordinate toApiCoordinate() {
return ApiCoordinate(col: column, row: row);
}
@override
String toString() {
String rowStr = row.toString();
String colStr = column.toString();
return '$colStr$rowStr';
}
static String columnIntToColumnString(int col) {
String colStr;
colStr = String.fromCharCode(col + 96);
return colStr;
}
static int columnStringToColumnInt(String col) {
int colInt;
colInt = col.codeUnitAt(0) - 96;
return colInt;
}
}
class ChessMove {
ChessCoordinate from;
ChessCoordinate to;
ChessMove({required this.from, required this.to});
factory ChessMove.fromApiMove(ApiMove apiMove) {
final start = ChessCoordinate.fromApiCoordinate(apiMove.startSquare);
final end = ChessCoordinate.fromApiCoordinate(apiMove.endSquare);
return ChessMove(from: start, to: end);
}
factory ChessMove.none() {
return ChessMove(from: ChessCoordinate(0, 0), to: ChessCoordinate(0, 0));
}
@override
int get hashCode {
return hash2(from, to);
}
@override
operator ==(other) {
return other is ChessMove && other.from == from && other.to == to;
}
ApiMove toApiMove() {
var toSquare = to.toApiCoordinate();
var fromSquare = from.toApiCoordinate();
return ApiMove(
startSquare: fromSquare, endSquare: toSquare, promotionToPiece: null);
}
}
class ChessPiece extends StatelessWidget {
final ChessColor color;
final ChessPieceClass pieceClass;
final String shortName;
final Widget? pieceImage;
factory ChessPiece(ChessPieceClass pieceClass, ChessColor color) {
Widget? pieceImage;
String pieceAssetUrl = chessPiecesAssets[
ChessPieceAssetKey(pieceClass: pieceClass, color: color)]!;
String shortName = chessPiecesShortName[
ChessPieceAssetKey(pieceClass: pieceClass, color: color)]!;
pieceImage = SvgPicture.asset(pieceAssetUrl);
return ChessPiece._(pieceClass, color, pieceImage, shortName);
}
const ChessPiece.none({super.key})
: pieceClass = ChessPieceClass.none,
color = ChessColor.white,
pieceImage = null,
shortName = "-";
const ChessPiece._(
this.pieceClass, this.color, this.pieceImage, this.shortName);
@override
Widget build(BuildContext context) {
return SizedBox(
child: pieceImage,
);
}
}
class ChessPieceAssetKey {
final ChessPieceClass pieceClass;
final ChessColor color;
ChessPieceAssetKey({required this.pieceClass, required this.color});
@override
int get hashCode {
return hash2(pieceClass, color);
}
@override
bool operator ==(Object other) {
return (other is ChessPieceAssetKey &&
(pieceClass == other.pieceClass) &&
(color == other.color));
}
}
enum ChessPieceClass {
none,
pawn,
bishop,
knight,
rook,
queen,
king,
}
class PieceDragged {
ChessCoordinate fromSquare;
ChessCoordinate toSquare;
ChessPiece? movedPiece;
PieceDragged(this.fromSquare, this.toSquare, this.movedPiece);
}