366 lines
9.5 KiB
Dart
366 lines
9.5 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<ChessPieceAssetKey, String> pieceCharacter = {
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.pawn, color: ChessColor.white):
|
|
"♙",
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.rook, color: ChessColor.white):
|
|
"♖",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.knight, color: ChessColor.white): "♘",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.bishop, color: ChessColor.white): "♗",
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.king, color: ChessColor.white):
|
|
"♔",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.queen, color: ChessColor.white): "♕",
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.pawn, color: ChessColor.black):
|
|
"♟︎",
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.rook, color: ChessColor.black):
|
|
"♜",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.knight, color: ChessColor.black): "♞",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.bishop, color: ChessColor.black): "♝",
|
|
ChessPieceAssetKey(pieceClass: ChessPieceClass.king, color: ChessColor.black):
|
|
"♚",
|
|
ChessPieceAssetKey(
|
|
pieceClass: ChessPieceClass.queen, 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);
|
|
}
|
|
|
|
factory ChessCoordinate.none() {
|
|
return ChessCoordinate(0, 0);
|
|
}
|
|
|
|
@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);
|
|
}
|