Compare commits
10 Commits
52540ec96c
...
105b6e7565
Author | SHA1 | Date | |
---|---|---|---|
105b6e7565 | |||
da986c8d9b | |||
4b8624f82b | |||
95fba78d0c | |||
fea24c8274 | |||
c3d747a60e | |||
9ce188ae32 | |||
a5befed62c | |||
0f27fc6b4e | |||
3bec7a84d8 |
@ -1,21 +1,30 @@
|
|||||||
class ApiMove {
|
class ApiMove {
|
||||||
final ApiCoordinate startSquare;
|
final ApiCoordinate startSquare;
|
||||||
final ApiCoordinate endSquare;
|
final ApiCoordinate endSquare;
|
||||||
|
String? promotionToPiece;
|
||||||
|
|
||||||
const ApiMove({
|
ApiMove({
|
||||||
required this.startSquare,
|
required this.startSquare,
|
||||||
required this.endSquare,
|
required this.endSquare,
|
||||||
|
this.promotionToPiece,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ApiMove.fromJson(Map<String, dynamic> json) {
|
factory ApiMove.fromJson(Map<String, dynamic> json) {
|
||||||
final startSquare = ApiCoordinate.fromJson(json['startSquare']);
|
final startSquare = ApiCoordinate.fromJson(json['startSquare']);
|
||||||
final endSquare = ApiCoordinate.fromJson(json['endSquare']);
|
final endSquare = ApiCoordinate.fromJson(json['endSquare']);
|
||||||
|
final promotionToPiece = json['promotionToPiece'];
|
||||||
|
|
||||||
return ApiMove(startSquare: startSquare, endSquare: endSquare);
|
return ApiMove(
|
||||||
|
startSquare: startSquare,
|
||||||
|
endSquare: endSquare,
|
||||||
|
promotionToPiece: promotionToPiece);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() =>
|
Map<String, dynamic> toJson() => {
|
||||||
{'startSquare': startSquare, 'endSquare': endSquare};
|
'startSquare': startSquare,
|
||||||
|
'endSquare': endSquare,
|
||||||
|
'promotionToPiece': promotionToPiece
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiCoordinate {
|
class ApiCoordinate {
|
||||||
|
@ -58,6 +58,9 @@ class ApiWebsocketMessage {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() =>
|
Map<String, dynamic> toJson() => {
|
||||||
{'messageType': type, 'move': move, 'color': color};
|
'messageType': type,
|
||||||
|
'move': move,
|
||||||
|
'color': color,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||||
|
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||||
import 'package:mchess/utils/chess_router.dart';
|
import 'package:mchess/utils/chess_router.dart';
|
||||||
|
|
||||||
@ -9,17 +10,24 @@ class ChessApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return MultiBlocProvider(
|
||||||
create: (_) => ConnectionCubit.getInstance(),
|
providers: [
|
||||||
child: BlocProvider(
|
BlocProvider(
|
||||||
create: (_) => ChessBloc.getInstance(),
|
create: (_) => ConnectionCubit.getInstance(),
|
||||||
child: MaterialApp.router(
|
|
||||||
theme: ThemeData.dark(
|
|
||||||
useMaterial3: true,
|
|
||||||
),
|
|
||||||
routerConfig: ChessAppRouter.getInstance().router,
|
|
||||||
title: 'mChess',
|
|
||||||
),
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => ChessBloc.getInstance(),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => PromotionBloc.getInstance(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
child: MaterialApp.router(
|
||||||
|
theme: ThemeData.dark(
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
routerConfig: ChessAppRouter.getInstance().router,
|
||||||
|
title: 'mChess',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||||
|
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||||
|
|
||||||
import '../chess_bloc/chess_events.dart';
|
import '../chess_bloc/chess_events.dart';
|
||||||
import '../utils/chess_utils.dart';
|
import '../utils/chess_utils.dart';
|
||||||
@ -58,30 +59,44 @@ class ChessSquare extends StatelessWidget {
|
|||||||
draggableFdbSize = 0.15 * windowHeight;
|
draggableFdbSize = 0.15 * windowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DragTarget<PieceMovedFrom>(
|
return DragTarget<PieceDragged>(
|
||||||
onWillAccept: (move) {
|
onWillAccept: (move) {
|
||||||
if (move?.fromSquare == coordinate) {
|
if (move?.fromSquare == coordinate) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onAccept: (move) {
|
onAccept: (pieceDragged) {
|
||||||
if (coordinate != move.fromSquare) {
|
// Replace the dummy value with the actual target of the move.
|
||||||
|
pieceDragged.toSquare = coordinate;
|
||||||
|
|
||||||
|
if (isPromotionMove(pieceDragged)) {
|
||||||
|
var move = ChessMove(
|
||||||
|
from: pieceDragged.fromSquare, to: pieceDragged.toSquare);
|
||||||
|
PromotionBloc.getInstance().add(PawnMovedToPromotionField(
|
||||||
|
move: move, colorMoved: ChessBloc.myColor!));
|
||||||
|
} else if (coordinate != pieceDragged.fromSquare) {
|
||||||
ChessBloc.getInstance().add(OwnPieceMoved(
|
ChessBloc.getInstance().add(OwnPieceMoved(
|
||||||
startSquare: move.fromSquare, endSquare: coordinate));
|
startSquare: pieceDragged.fromSquare,
|
||||||
|
endSquare: pieceDragged.toSquare,
|
||||||
|
piece: pieceDragged.movedPiece!));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, candidateData, rejectedData) {
|
builder: (context, candidateData, rejectedData) {
|
||||||
var maxDrags =
|
var maxDrags = kDebugMode
|
||||||
kDebugMode ? 1 : (ChessBloc.turnColor == ChessBloc.myColor ? 1 : 0);
|
? 1
|
||||||
|
: ((ChessBloc.turnColor == ChessBloc.myColor) &&
|
||||||
|
(containedPiece?.color == ChessBloc.turnColor)
|
||||||
|
? 1
|
||||||
|
: 0);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: color,
|
color: color,
|
||||||
width: ChessSquare.pieceWidth,
|
width: ChessSquare.pieceWidth,
|
||||||
height: ChessSquare.pieceWidth,
|
height: ChessSquare.pieceWidth,
|
||||||
child: Draggable<PieceMovedFrom>(
|
child: Draggable<PieceDragged>(
|
||||||
/* We create the move with the startSquare == endSquare. The receiving widget will give the correct value to end square. */
|
/* We create the move with the startSquare == endSquare. The receiving widget will give the correct value to end square. */
|
||||||
data: PieceMovedFrom(coordinate, containedPiece),
|
data: PieceDragged(coordinate, coordinate, containedPiece),
|
||||||
maxSimultaneousDrags: maxDrags,
|
maxSimultaneousDrags: maxDrags,
|
||||||
feedback: FractionalTranslation(
|
feedback: FractionalTranslation(
|
||||||
translation: const Offset(-0.5, -0.75),
|
translation: const Offset(-0.5, -0.75),
|
||||||
@ -100,4 +115,28 @@ class ChessSquare extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPromotionMove(PieceDragged move) {
|
||||||
|
bool isPromotion = false;
|
||||||
|
if (move.movedPiece!.pieceClass != ChessPieceClass.pawn) {
|
||||||
|
return isPromotion;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ChessBloc.myColor) {
|
||||||
|
case ChessColor.black:
|
||||||
|
if (move.toSquare.row == 1) {
|
||||||
|
isPromotion = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ChessColor.white:
|
||||||
|
if (move.toSquare.row == 8) {
|
||||||
|
isPromotion = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPromotion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:mchess/api/move.dart';
|
|
||||||
import 'package:mchess/api/websocket_message.dart';
|
import 'package:mchess/api/websocket_message.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_position.dart';
|
import 'package:mchess/chess_bloc/chess_position.dart';
|
||||||
@ -12,7 +11,7 @@ import 'dart:developer';
|
|||||||
class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||||
static final ChessBloc _instance = ChessBloc._internal();
|
static final ChessBloc _instance = ChessBloc._internal();
|
||||||
static ChessColor turnColor = ChessColor.white;
|
static ChessColor turnColor = ChessColor.white;
|
||||||
static ChessColor? myColor;
|
static ChessColor? myColor = ChessColor.white;
|
||||||
|
|
||||||
static ChessColor? getSidesColor() {
|
static ChessColor? getSidesColor() {
|
||||||
return myColor;
|
return myColor;
|
||||||
@ -22,7 +21,9 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
on<InitBoard>(initBoard);
|
on<InitBoard>(initBoard);
|
||||||
on<ColorDetermined>(flipBoard);
|
on<ColorDetermined>(flipBoard);
|
||||||
on<ReceivedMove>(moveHandler);
|
on<ReceivedMove>(moveHandler);
|
||||||
|
on<ReceivedPromotion>(promotionHandler);
|
||||||
on<OwnPieceMoved>(ownMoveHandler);
|
on<OwnPieceMoved>(ownMoveHandler);
|
||||||
|
on<OwnPromotionPlayed>(ownPromotionHandler);
|
||||||
on<InvalidMovePlayed>(invalidMoveHandler);
|
on<InvalidMovePlayed>(invalidMoveHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initBoard(InitBoard event, Emitter<ChessBoardState> emit) {
|
void initBoard(InitBoard event, Emitter<ChessBoardState> emit) {
|
||||||
|
turnColor = ChessColor.white;
|
||||||
ChessPosition.getInstance().resetToStartingPosition();
|
ChessPosition.getInstance().resetToStartingPosition();
|
||||||
emit(ChessBoardState(ChessColor.white, ChessColor.white,
|
emit(ChessBoardState(ChessColor.white, ChessColor.white,
|
||||||
ChessPosition.getInstance().currentPosition));
|
ChessPosition.getInstance().currentPosition));
|
||||||
@ -48,9 +50,48 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
|
|
||||||
void moveHandler(ReceivedMove event, Emitter<ChessBoardState> emit) {
|
void moveHandler(ReceivedMove event, Emitter<ChessBoardState> emit) {
|
||||||
log('opponentMoveHandler()');
|
log('opponentMoveHandler()');
|
||||||
|
|
||||||
|
var move = ChessMove(from: event.startSquare, to: event.endSquare);
|
||||||
|
bool wasEnPassant = move.wasEnPassant();
|
||||||
|
bool wasCastling = move.wasCastling();
|
||||||
|
|
||||||
|
var oldPosition = ChessPosition.getInstance().copyOfCurrentPosition;
|
||||||
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
||||||
var newPosition = ChessPosition.getInstance().currentPosition;
|
var newPosition = ChessPosition.getInstance().currentPosition;
|
||||||
|
|
||||||
|
if (wasEnPassant) {
|
||||||
|
if (turnColor == ChessColor.white) {
|
||||||
|
newPosition[ChessCoordinate(
|
||||||
|
event.endSquare.column, event.endSquare.row - 1)] =
|
||||||
|
const ChessPiece.none();
|
||||||
|
} else {
|
||||||
|
newPosition[ChessCoordinate(
|
||||||
|
event.endSquare.column, event.endSquare.row + 1)] =
|
||||||
|
const ChessPiece.none();
|
||||||
|
}
|
||||||
|
} else if (wasCastling) {
|
||||||
|
ChessPiece rookToMove;
|
||||||
|
ChessPiece kingToMove;
|
||||||
|
if (move.to.column == 7) {
|
||||||
|
rookToMove = oldPosition[ChessCoordinate(8, move.to.row)]!;
|
||||||
|
newPosition[ChessCoordinate(6, move.to.row)] = rookToMove;
|
||||||
|
newPosition[ChessCoordinate(8, move.to.row)] = const ChessPiece.none();
|
||||||
|
|
||||||
|
kingToMove = oldPosition[ChessCoordinate(5, move.to.row)]!;
|
||||||
|
newPosition[ChessCoordinate(7, move.to.row)] = kingToMove;
|
||||||
|
newPosition[ChessCoordinate(5, move.to.row)] = const ChessPiece.none();
|
||||||
|
}
|
||||||
|
if (move.to.column == 3) {
|
||||||
|
rookToMove = oldPosition[ChessCoordinate(1, move.to.row)]!;
|
||||||
|
newPosition[ChessCoordinate(4, move.to.row)] = rookToMove;
|
||||||
|
newPosition[ChessCoordinate(1, move.to.row)] = const ChessPiece.none();
|
||||||
|
|
||||||
|
kingToMove = oldPosition[ChessCoordinate(5, move.to.row)]!;
|
||||||
|
newPosition[ChessCoordinate(3, move.to.row)] = kingToMove;
|
||||||
|
newPosition[ChessCoordinate(5, move.to.row)] = const ChessPiece.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
turnColor = state.newTurnColor == ChessColor.white
|
turnColor = state.newTurnColor == ChessColor.white
|
||||||
? ChessColor.black
|
? ChessColor.black
|
||||||
: ChessColor.white;
|
: ChessColor.white;
|
||||||
@ -64,14 +105,46 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void promotionHandler(
|
||||||
|
ReceivedPromotion event,
|
||||||
|
Emitter<ChessBoardState> emit,
|
||||||
|
) {
|
||||||
|
var pieceAtStartSquare = ChessPosition.getInstance().getPieceAt(
|
||||||
|
ChessCoordinate(event.startSquare.column, event.startSquare.row));
|
||||||
|
if (pieceAtStartSquare == null) {
|
||||||
|
log('received a promotion but piece on start square was empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChessPieceClass pieceClass = ChessPieceClass.none;
|
||||||
|
for (var piece in chessPiecesShortName.entries) {
|
||||||
|
if (piece.value.toLowerCase() == event.piece) {
|
||||||
|
pieceClass = piece.key.pieceClass;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newPosition = ChessPosition.getInstance().currentPosition;
|
||||||
|
newPosition[
|
||||||
|
ChessCoordinate(event.startSquare.column, event.startSquare.row)] =
|
||||||
|
const ChessPiece.none();
|
||||||
|
newPosition[ChessCoordinate(event.endSquare.column, event.endSquare.row)] =
|
||||||
|
ChessPiece(pieceClass, pieceAtStartSquare.color);
|
||||||
|
|
||||||
|
turnColor = state.newTurnColor == ChessColor.white
|
||||||
|
? ChessColor.black
|
||||||
|
: ChessColor.white;
|
||||||
|
|
||||||
|
emit(ChessBoardState(
|
||||||
|
state.bottomColor,
|
||||||
|
turnColor,
|
||||||
|
newPosition,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
|
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
|
||||||
log('ownMoveHandler()');
|
log('ownMoveHandler()');
|
||||||
|
var apiMove =
|
||||||
var start = ApiCoordinate(
|
ChessMove(from: event.startSquare, to: event.endSquare).toApiMove();
|
||||||
col: event.startSquare.column, row: event.startSquare.row);
|
|
||||||
var end =
|
|
||||||
ApiCoordinate(col: event.endSquare.column, row: event.endSquare.row);
|
|
||||||
var apiMove = ApiMove(startSquare: start, endSquare: end);
|
|
||||||
var apiMessage = ApiWebsocketMessage(
|
var apiMessage = ApiWebsocketMessage(
|
||||||
type: MessageType.move, move: apiMove, color: null, reason: null);
|
type: MessageType.move, move: apiMove, color: null, reason: null);
|
||||||
|
|
||||||
@ -92,10 +165,32 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ownPromotionHandler(
|
||||||
|
OwnPromotionPlayed event, Emitter<ChessBoardState> emit) {
|
||||||
|
var apiMove = event.move.toApiMove();
|
||||||
|
var shorNameForPiece = chessPiecesShortName[
|
||||||
|
ChessPieceAssetKey(pieceClass: event.pieceClass, color: myColor!)]!
|
||||||
|
.toLowerCase();
|
||||||
|
apiMove.promotionToPiece = shorNameForPiece;
|
||||||
|
var message = ApiWebsocketMessage(
|
||||||
|
type: MessageType.move,
|
||||||
|
move: apiMove,
|
||||||
|
color: null,
|
||||||
|
reason: null,
|
||||||
|
);
|
||||||
|
log(jsonEncode(message));
|
||||||
|
ServerConnection.getInstance().send(jsonEncode(message));
|
||||||
|
}
|
||||||
|
|
||||||
void invalidMoveHandler(
|
void invalidMoveHandler(
|
||||||
InvalidMovePlayed event, Emitter<ChessBoardState> emit) {
|
InvalidMovePlayed event, Emitter<ChessBoardState> emit) {
|
||||||
emit(ChessBoardState(state.bottomColor, turnColor,
|
emit(
|
||||||
ChessPosition.getInstance().currentPosition));
|
ChessBoardState(
|
||||||
|
state.bottomColor,
|
||||||
|
turnColor,
|
||||||
|
ChessPosition.getInstance().currentPosition,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,33 @@ class ReceivedMove extends ChessEvent {
|
|||||||
ReceivedMove({required this.startSquare, required this.endSquare});
|
ReceivedMove({required this.startSquare, required this.endSquare});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReceivedPromotion extends ChessEvent {
|
||||||
|
final ChessCoordinate startSquare;
|
||||||
|
final ChessCoordinate endSquare;
|
||||||
|
final String piece;
|
||||||
|
|
||||||
|
ReceivedPromotion(
|
||||||
|
{required this.startSquare,
|
||||||
|
required this.endSquare,
|
||||||
|
required this.piece});
|
||||||
|
}
|
||||||
|
|
||||||
class OwnPieceMoved extends ChessEvent {
|
class OwnPieceMoved extends ChessEvent {
|
||||||
final ChessCoordinate startSquare;
|
final ChessCoordinate startSquare;
|
||||||
final ChessCoordinate endSquare;
|
final ChessCoordinate endSquare;
|
||||||
|
final ChessPiece piece;
|
||||||
|
|
||||||
OwnPieceMoved({required this.startSquare, required this.endSquare});
|
OwnPieceMoved(
|
||||||
|
{required this.startSquare,
|
||||||
|
required this.endSquare,
|
||||||
|
required this.piece});
|
||||||
|
}
|
||||||
|
|
||||||
|
class OwnPromotionPlayed extends ChessEvent {
|
||||||
|
final ChessPieceClass pieceClass;
|
||||||
|
final ChessMove move;
|
||||||
|
|
||||||
|
OwnPromotionPlayed({required this.pieceClass, required this.move});
|
||||||
}
|
}
|
||||||
|
|
||||||
class InitBoard extends ChessEvent {
|
class InitBoard extends ChessEvent {
|
||||||
|
@ -31,48 +31,52 @@ class ChessPosition {
|
|||||||
|
|
||||||
for (int i = 1; i <= 8; i++) {
|
for (int i = 1; i <= 8; i++) {
|
||||||
pos[ChessCoordinate(i, 7)] =
|
pos[ChessCoordinate(i, 7)] =
|
||||||
ChessPiece(ChessPieceName.blackPawn, ChessColor.black);
|
ChessPiece(ChessPieceClass.pawn, ChessColor.black);
|
||||||
pos[ChessCoordinate(i, 2)] =
|
pos[ChessCoordinate(i, 2)] =
|
||||||
ChessPiece(ChessPieceName.whitePawn, ChessColor.white);
|
ChessPiece(ChessPieceClass.pawn, ChessColor.white);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos[ChessCoordinate(1, 8)] =
|
pos[ChessCoordinate(1, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
ChessPiece(ChessPieceClass.rook, ChessColor.black);
|
||||||
pos[ChessCoordinate(2, 8)] =
|
pos[ChessCoordinate(2, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
ChessPiece(ChessPieceClass.knight, ChessColor.black);
|
||||||
pos[ChessCoordinate(3, 8)] =
|
pos[ChessCoordinate(3, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
ChessPiece(ChessPieceClass.bishop, ChessColor.black);
|
||||||
pos[ChessCoordinate(4, 8)] =
|
pos[ChessCoordinate(4, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackQueen, ChessColor.black);
|
ChessPiece(ChessPieceClass.queen, ChessColor.black);
|
||||||
pos[ChessCoordinate(5, 8)] =
|
pos[ChessCoordinate(5, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackKing, ChessColor.black);
|
ChessPiece(ChessPieceClass.king, ChessColor.black);
|
||||||
pos[ChessCoordinate(6, 8)] =
|
pos[ChessCoordinate(6, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
ChessPiece(ChessPieceClass.bishop, ChessColor.black);
|
||||||
pos[ChessCoordinate(7, 8)] =
|
pos[ChessCoordinate(7, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
ChessPiece(ChessPieceClass.knight, ChessColor.black);
|
||||||
pos[ChessCoordinate(8, 8)] =
|
pos[ChessCoordinate(8, 8)] =
|
||||||
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
ChessPiece(ChessPieceClass.rook, ChessColor.black);
|
||||||
|
|
||||||
pos[ChessCoordinate(1, 1)] =
|
pos[ChessCoordinate(1, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
ChessPiece(ChessPieceClass.rook, ChessColor.white);
|
||||||
pos[ChessCoordinate(2, 1)] =
|
pos[ChessCoordinate(2, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
ChessPiece(ChessPieceClass.knight, ChessColor.white);
|
||||||
pos[ChessCoordinate(3, 1)] =
|
pos[ChessCoordinate(3, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
ChessPiece(ChessPieceClass.bishop, ChessColor.white);
|
||||||
pos[ChessCoordinate(4, 1)] =
|
pos[ChessCoordinate(4, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteQueen, ChessColor.white);
|
ChessPiece(ChessPieceClass.queen, ChessColor.white);
|
||||||
pos[ChessCoordinate(5, 1)] =
|
pos[ChessCoordinate(5, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteKing, ChessColor.white);
|
ChessPiece(ChessPieceClass.king, ChessColor.white);
|
||||||
pos[ChessCoordinate(6, 1)] =
|
pos[ChessCoordinate(6, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
ChessPiece(ChessPieceClass.bishop, ChessColor.white);
|
||||||
pos[ChessCoordinate(7, 1)] =
|
pos[ChessCoordinate(7, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
ChessPiece(ChessPieceClass.knight, ChessColor.white);
|
||||||
pos[ChessCoordinate(8, 1)] =
|
pos[ChessCoordinate(8, 1)] =
|
||||||
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
ChessPiece(ChessPieceClass.rook, ChessColor.white);
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChessPiece? getPieceAt(ChessCoordinate coordinate) {
|
||||||
|
return position[ChessCoordinate(coordinate.column, coordinate.row)];
|
||||||
|
}
|
||||||
|
|
||||||
void resetToStartingPosition() {
|
void resetToStartingPosition() {
|
||||||
history = ChessMoveHistory.empty(growable: true);
|
history = ChessMoveHistory.empty(growable: true);
|
||||||
_instance = ChessPosition(position: _getStartingPosition());
|
_instance = ChessPosition(position: _getStartingPosition());
|
||||||
|
76
lib/chess_bloc/promotion_bloc.dart
Normal file
76
lib/chess_bloc/promotion_bloc.dart
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||||
|
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||||
|
import 'package:mchess/utils/chess_utils.dart';
|
||||||
|
|
||||||
|
class PromotionBloc extends Bloc<PromotionEvent, PromotionState> {
|
||||||
|
static final PromotionBloc _instance = PromotionBloc._internal();
|
||||||
|
static late ChessMove move;
|
||||||
|
|
||||||
|
PromotionBloc._internal() : super(PromotionState.init()) {
|
||||||
|
on<PawnMovedToPromotionField>(promotionMoveHandler);
|
||||||
|
on<PieceChosen>(pieceChosenHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void promotionMoveHandler(
|
||||||
|
PawnMovedToPromotionField event,
|
||||||
|
Emitter<PromotionState> emit,
|
||||||
|
) {
|
||||||
|
switch (event.colorMoved) {
|
||||||
|
case ChessColor.white:
|
||||||
|
if (event.move.from.row != 7) return;
|
||||||
|
break;
|
||||||
|
case ChessColor.black:
|
||||||
|
if (event.move.from.row != 2) return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
move = event.move;
|
||||||
|
emit(PromotionState(
|
||||||
|
showPromotionDialog: true, colorMoved: event.colorMoved));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pieceChosenHandler(
|
||||||
|
PieceChosen event,
|
||||||
|
Emitter<PromotionState> emit,
|
||||||
|
) {
|
||||||
|
ChessBloc.getInstance()
|
||||||
|
.add(OwnPromotionPlayed(pieceClass: event.pieceClass, move: move));
|
||||||
|
emit(PromotionState(showPromotionDialog: false, colorMoved: event.color));
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PromotionBloc.getInstance() {
|
||||||
|
return PromotionBloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PromotionBloc() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class PromotionEvent {}
|
||||||
|
|
||||||
|
class PawnMovedToPromotionField extends PromotionEvent {
|
||||||
|
final ChessMove move;
|
||||||
|
final ChessColor colorMoved;
|
||||||
|
|
||||||
|
PawnMovedToPromotionField({required this.move, required this.colorMoved});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PieceChosen extends PromotionEvent {
|
||||||
|
final ChessPieceClass pieceClass;
|
||||||
|
final ChessColor color;
|
||||||
|
|
||||||
|
PieceChosen({required this.pieceClass, required this.color});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PromotionState {
|
||||||
|
final bool showPromotionDialog;
|
||||||
|
final ChessColor colorMoved;
|
||||||
|
|
||||||
|
PromotionState({required this.showPromotionDialog, required this.colorMoved});
|
||||||
|
|
||||||
|
factory PromotionState.init() {
|
||||||
|
return PromotionState(
|
||||||
|
showPromotionDialog: false, colorMoved: ChessColor.white);
|
||||||
|
}
|
||||||
|
}
|
@ -77,15 +77,24 @@ class ServerConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleIncomingColorDeterminedMessage(ApiWebsocketMessage apiMessage) {
|
void handleIncomingColorDeterminedMessage(ApiWebsocketMessage apiMessage) {
|
||||||
|
ConnectionCubit.getInstance().opponentConnected();
|
||||||
|
ChessBloc.getInstance().add(InitBoard());
|
||||||
ChessBloc.getInstance().add(
|
ChessBloc.getInstance().add(
|
||||||
ColorDetermined(myColor: ChessColor.fromApiColor(apiMessage.color!)));
|
ColorDetermined(myColor: ChessColor.fromApiColor(apiMessage.color!)));
|
||||||
ConnectionCubit.getInstance().opponentConnected();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
|
void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||||
var move = ChessMove.fromApiMove(apiMessage.move!);
|
var move = ChessMove.fromApiMove(apiMessage.move!);
|
||||||
ChessBloc.getInstance()
|
|
||||||
.add(ReceivedMove(startSquare: move.from, endSquare: move.to));
|
if (apiMessage.move?.promotionToPiece?.isNotEmpty ?? false) {
|
||||||
|
ChessBloc.getInstance().add(ReceivedPromotion(
|
||||||
|
startSquare: move.from,
|
||||||
|
endSquare: move.to,
|
||||||
|
piece: apiMessage.move!.promotionToPiece!));
|
||||||
|
} else {
|
||||||
|
ChessBloc.getInstance()
|
||||||
|
.add(ReceivedMove(startSquare: move.from, endSquare: move.to));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleInvalidMoveMessage(ApiWebsocketMessage apiMessage) {
|
void handleInvalidMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||||
|
@ -6,6 +6,9 @@ import 'package:mchess/chess_bloc/chess_bloc.dart';
|
|||||||
|
|
||||||
import 'package:mchess/chess/chess_board.dart';
|
import 'package:mchess/chess/chess_board.dart';
|
||||||
import 'package:mchess/chess/turn_indicator_widget.dart';
|
import 'package:mchess/chess/turn_indicator_widget.dart';
|
||||||
|
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||||
|
import 'package:mchess/utils/chess_utils.dart';
|
||||||
|
import 'package:mchess/utils/widgets/promotion_dialog.dart';
|
||||||
import 'package:mchess/utils/widgets/server_log_widget.dart';
|
import 'package:mchess/utils/widgets/server_log_widget.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
@ -32,23 +35,32 @@ class _ChessGameState extends State<ChessGame> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: FittedBox(
|
body: Center(
|
||||||
fit: BoxFit.contain,
|
child: FittedBox(
|
||||||
child: Column(
|
fit: BoxFit.contain,
|
||||||
children: [
|
child: Column(
|
||||||
if (kDebugMode) const ServerLogWidget(textColor: Colors.white),
|
children: [
|
||||||
Container(
|
if (kDebugMode) const ServerLogWidget(textColor: Colors.white),
|
||||||
margin: const EdgeInsets.all(20),
|
Container(
|
||||||
child: BlocBuilder<ChessBloc, ChessBoardState>(
|
margin: const EdgeInsets.all(20),
|
||||||
builder: (context, state) {
|
child: BlocListener<PromotionBloc, PromotionState>(
|
||||||
return ChessBoard(
|
listener: (context, state) {
|
||||||
bState: state,
|
if (state.showPromotionDialog) {
|
||||||
);
|
promotionDialogBuilder(context, state.colorMoved);
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
child: BlocBuilder<ChessBloc, ChessBoardState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return ChessBoard(
|
||||||
|
bState: state,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (kDebugMode) const TurnIndicator(),
|
||||||
if (kDebugMode) const TurnIndicator(),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@ -59,6 +71,18 @@ class _ChessGameState extends State<ChessGame> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> promotionDialogBuilder(BuildContext context, ChessColor color) {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return PromotionDialog(
|
||||||
|
sideColor: color,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChessGameArguments {
|
class ChessGameArguments {
|
||||||
|
@ -6,8 +6,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:mchess/api/register.dart';
|
import 'package:mchess/api/register.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
|
||||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
|
||||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
@ -27,8 +25,6 @@ class _HostGameWidgetState extends State<HostGameWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
ChessBloc.getInstance().add(InitBoard());
|
|
||||||
|
|
||||||
registerResponse = hostPrivateGame();
|
registerResponse = hostPrivateGame();
|
||||||
connectToWebsocket(registerResponse);
|
connectToWebsocket(registerResponse);
|
||||||
|
|
||||||
@ -83,7 +79,7 @@ class _HostGameWidgetState extends State<HostGameWidget> {
|
|||||||
color: Theme.of(context).colorScheme.primary),
|
color: Theme.of(context).colorScheme.primary),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
Text(
|
SelectableText(
|
||||||
passphrase,
|
passphrase,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
@ -130,7 +126,7 @@ class _HostGameWidgetState extends State<HostGameWidget> {
|
|||||||
Future.delayed(const Duration(seconds: 2), () {
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
ScaffoldMessenger.of(context).clearSnackBars();
|
ScaffoldMessenger.of(context).clearSnackBars();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
context.pop();
|
context.push('/'); // We go back to the lobby selector
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ class _JoinGameWidgetState extends State<JoinGameWidget> {
|
|||||||
Future.delayed(const Duration(seconds: 2), () {
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
ScaffoldMessenger.of(context).clearSnackBars();
|
ScaffoldMessenger.of(context).clearSnackBars();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
context.pop();
|
context.push('/'); // We go back to lobby selector
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -13,24 +13,25 @@ class LobbySelector extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ElevatedButton(
|
//We deactivate random lobbies for now.
|
||||||
onPressed: () {
|
// ElevatedButton(
|
||||||
context.push('/prepareRandom');
|
// onPressed: () {
|
||||||
},
|
// context.push('/prepareRandom');
|
||||||
child: const Row(
|
// },
|
||||||
mainAxisSize: MainAxisSize.min,
|
// child: const Row(
|
||||||
children: [
|
// mainAxisSize: MainAxisSize.min,
|
||||||
Icon(Icons.question_mark),
|
// children: [
|
||||||
SizedBox(
|
// Icon(Icons.question_mark),
|
||||||
width: 10,
|
// SizedBox(
|
||||||
),
|
// width: 10,
|
||||||
Text('Random')
|
// ),
|
||||||
],
|
// Text('Random')
|
||||||
),
|
// ],
|
||||||
),
|
// ),
|
||||||
const SizedBox(
|
// ),
|
||||||
height: 25,
|
// const SizedBox(
|
||||||
),
|
// height: 25,
|
||||||
|
// ),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_dialogBuilder(context);
|
_dialogBuilder(context);
|
||||||
|
@ -5,8 +5,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:mchess/api/register.dart';
|
import 'package:mchess/api/register.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
|
||||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
|
||||||
import 'package:mchess/pages/chess_game.dart';
|
import 'package:mchess/pages/chess_game.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
@ -24,12 +22,8 @@ class _PrepareRandomGameWidgetState extends State<PrepareRandomGameWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
ChessBloc.getInstance().add(InitBoard());
|
|
||||||
|
|
||||||
randomGameResponse = registerForRandomGame();
|
randomGameResponse = registerForRandomGame();
|
||||||
|
|
||||||
goToGameWhenResponseIsHere(randomGameResponse as Future<PlayerInfo?>);
|
goToGameWhenResponseIsHere(randomGameResponse as Future<PlayerInfo?>);
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,20 +4,35 @@ import 'package:mchess/api/move.dart';
|
|||||||
import 'package:mchess/api/websocket_message.dart';
|
import 'package:mchess/api/websocket_message.dart';
|
||||||
import 'package:quiver/core.dart';
|
import 'package:quiver/core.dart';
|
||||||
|
|
||||||
enum ChessPieceName {
|
import '../chess_bloc/chess_position.dart';
|
||||||
|
|
||||||
|
enum ChessPieceClass {
|
||||||
none,
|
none,
|
||||||
whitePawn,
|
pawn,
|
||||||
whiteBishop,
|
bishop,
|
||||||
whiteKnight,
|
knight,
|
||||||
whiteRook,
|
rook,
|
||||||
whiteQueen,
|
queen,
|
||||||
whiteKing,
|
king,
|
||||||
blackPawn,
|
}
|
||||||
blackBishop,
|
|
||||||
blackKnight,
|
class ChessPieceAssetKey {
|
||||||
blackRook,
|
final ChessPieceClass pieceClass;
|
||||||
blackQueen,
|
final ChessColor color;
|
||||||
blackKing,
|
|
||||||
|
ChessPieceAssetKey({required this.pieceClass, required this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return (other is ChessPieceAssetKey &&
|
||||||
|
(pieceClass == other.pieceClass) &&
|
||||||
|
(color == other.color));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return hash2(pieceClass, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChessColor {
|
enum ChessColor {
|
||||||
@ -31,38 +46,124 @@ enum ChessColor {
|
|||||||
return white;
|
return white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChessColor getOpposite() {
|
||||||
|
if (name == 'black') {
|
||||||
|
return white;
|
||||||
|
} else {
|
||||||
|
return black;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<ChessPieceName, String> chessPiecesAssets = {
|
Map<ChessPieceAssetKey, String> chessPiecesAssets = {
|
||||||
ChessPieceName.whitePawn: 'assets/pieces/white/pawn.svg',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.whiteBishop: 'assets/pieces/white/bishop.svg',
|
pieceClass: ChessPieceClass.pawn,
|
||||||
ChessPieceName.whiteKnight: 'assets/pieces/white/knight.svg',
|
color: ChessColor.white,
|
||||||
ChessPieceName.whiteRook: 'assets/pieces/white/rook.svg',
|
): 'assets/pieces/white/pawn.svg',
|
||||||
ChessPieceName.whiteQueen: 'assets/pieces/white/queen.svg',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.whiteKing: 'assets/pieces/white/king.svg',
|
pieceClass: ChessPieceClass.bishop,
|
||||||
ChessPieceName.blackPawn: 'assets/pieces/black/pawn.svg',
|
color: ChessColor.white,
|
||||||
ChessPieceName.blackBishop: 'assets/pieces/black/bishop.svg',
|
): 'assets/pieces/white/bishop.svg',
|
||||||
ChessPieceName.blackKnight: 'assets/pieces/black/knight.svg',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.blackRook: 'assets/pieces/black/rook.svg',
|
pieceClass: ChessPieceClass.knight,
|
||||||
ChessPieceName.blackQueen: 'assets/pieces/black/queen.svg',
|
color: ChessColor.white,
|
||||||
ChessPieceName.blackKing: 'assets/pieces/black/king.svg',
|
): 'assets/pieces/white/knight.svg',
|
||||||
ChessPieceName.none: 'assets/empty.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<ChessPieceName, String> chessPiecesShortName = {
|
Map<ChessPieceAssetKey, String> chessPiecesShortName = {
|
||||||
ChessPieceName.whitePawn: 'P',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.whiteBishop: 'B',
|
pieceClass: ChessPieceClass.pawn,
|
||||||
ChessPieceName.whiteKnight: 'N',
|
color: ChessColor.white,
|
||||||
ChessPieceName.whiteRook: 'R',
|
): 'P',
|
||||||
ChessPieceName.whiteQueen: 'Q',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.whiteKing: 'K',
|
pieceClass: ChessPieceClass.bishop,
|
||||||
ChessPieceName.blackPawn: 'p',
|
color: ChessColor.white,
|
||||||
ChessPieceName.blackBishop: 'b',
|
): 'B',
|
||||||
ChessPieceName.blackKnight: 'n',
|
ChessPieceAssetKey(
|
||||||
ChessPieceName.blackRook: 'r',
|
pieceClass: ChessPieceClass.knight,
|
||||||
ChessPieceName.blackQueen: 'q',
|
color: ChessColor.white,
|
||||||
ChessPieceName.blackKing: 'k',
|
): 'N',
|
||||||
ChessPieceName.none: 'X',
|
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,
|
||||||
|
): '-',
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChessCoordinate {
|
class ChessCoordinate {
|
||||||
@ -82,6 +183,10 @@ class ChessCoordinate {
|
|||||||
return ChessCoordinate(apiCoordinate.col, apiCoordinate.row);
|
return ChessCoordinate(apiCoordinate.col, apiCoordinate.row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiCoordinate toApiCoordinate() {
|
||||||
|
return ApiCoordinate(col: column, row: row);
|
||||||
|
}
|
||||||
|
|
||||||
ChessCoordinate.copyFrom(ChessCoordinate original)
|
ChessCoordinate.copyFrom(ChessCoordinate original)
|
||||||
: column = original.column,
|
: column = original.column,
|
||||||
row = original.row;
|
row = original.row;
|
||||||
@ -140,24 +245,26 @@ class ChessCoordinate {
|
|||||||
|
|
||||||
class ChessPiece extends StatelessWidget {
|
class ChessPiece extends StatelessWidget {
|
||||||
final ChessColor color;
|
final ChessColor color;
|
||||||
final ChessPieceName pieceName;
|
final ChessPieceClass pieceClass;
|
||||||
final String shortName;
|
final String shortName;
|
||||||
final Widget? pieceImage;
|
final Widget? pieceImage;
|
||||||
|
|
||||||
const ChessPiece._(
|
const ChessPiece._(
|
||||||
this.pieceName, this.color, this.pieceImage, this.shortName);
|
this.pieceClass, this.color, this.pieceImage, this.shortName);
|
||||||
|
|
||||||
factory ChessPiece(ChessPieceName name, ChessColor color) {
|
factory ChessPiece(ChessPieceClass pieceClass, ChessColor color) {
|
||||||
Widget? pieceImage;
|
Widget? pieceImage;
|
||||||
String pieceAssetUrl = chessPiecesAssets[name]!;
|
String pieceAssetUrl = chessPiecesAssets[
|
||||||
String shortName = chessPiecesShortName[name]!;
|
ChessPieceAssetKey(pieceClass: pieceClass, color: color)]!;
|
||||||
|
String shortName = chessPiecesShortName[
|
||||||
|
ChessPieceAssetKey(pieceClass: pieceClass, color: color)]!;
|
||||||
|
|
||||||
pieceImage = SvgPicture.asset(pieceAssetUrl);
|
pieceImage = SvgPicture.asset(pieceAssetUrl);
|
||||||
return ChessPiece._(name, color, pieceImage, shortName);
|
return ChessPiece._(pieceClass, color, pieceImage, shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChessPiece.none({super.key})
|
const ChessPiece.none({super.key})
|
||||||
: pieceName = ChessPieceName.none,
|
: pieceClass = ChessPieceClass.none,
|
||||||
color = ChessColor.white,
|
color = ChessColor.white,
|
||||||
pieceImage = null,
|
pieceImage = null,
|
||||||
shortName = "-";
|
shortName = "-";
|
||||||
@ -183,6 +290,13 @@ class ChessMove {
|
|||||||
return ChessMove(from: start, to: end);
|
return ChessMove(from: start, to: end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiMove toApiMove() {
|
||||||
|
var toSquare = to.toApiCoordinate();
|
||||||
|
var fromSquare = from.toApiCoordinate();
|
||||||
|
return ApiMove(
|
||||||
|
startSquare: fromSquare, endSquare: toSquare, promotionToPiece: null);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
operator ==(other) {
|
operator ==(other) {
|
||||||
return other is ChessMove && other.from == from && other.to == to;
|
return other is ChessMove && other.from == from && other.to == to;
|
||||||
@ -192,11 +306,35 @@ class ChessMove {
|
|||||||
int get hashCode {
|
int get hashCode {
|
||||||
return hash2(from, to);
|
return hash2(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wasEnPassant() {
|
||||||
|
var pieceMoved = ChessPosition.getInstance().getPieceAt(from);
|
||||||
|
var pieceAtEndSquare = ChessPosition.getInstance().getPieceAt(to);
|
||||||
|
if (pieceMoved != null &&
|
||||||
|
pieceMoved.pieceClass == ChessPieceClass.pawn &&
|
||||||
|
pieceAtEndSquare == null &&
|
||||||
|
from.column != to.column) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasCastling() {
|
||||||
|
var pieceMoved = ChessPosition.getInstance().getPieceAt(from);
|
||||||
|
if (pieceMoved != null && pieceMoved.pieceClass == ChessPieceClass.king) {
|
||||||
|
var colDiff = (from.column - to.column).abs();
|
||||||
|
if (colDiff == 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PieceMovedFrom {
|
class PieceDragged {
|
||||||
ChessCoordinate fromSquare;
|
ChessCoordinate fromSquare;
|
||||||
|
ChessCoordinate toSquare;
|
||||||
ChessPiece? movedPiece;
|
ChessPiece? movedPiece;
|
||||||
|
|
||||||
PieceMovedFrom(this.fromSquare, this.movedPiece);
|
PieceDragged(this.fromSquare, this.toSquare, this.movedPiece);
|
||||||
}
|
}
|
||||||
|
72
lib/utils/widgets/promotion_dialog.dart
Normal file
72
lib/utils/widgets/promotion_dialog.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||||
|
import 'package:mchess/utils/chess_utils.dart';
|
||||||
|
|
||||||
|
class PromotionDialog extends StatelessWidget {
|
||||||
|
final ChessColor sideColor;
|
||||||
|
const PromotionDialog({required this.sideColor, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
double windowWidth = MediaQuery.of(context).size.width;
|
||||||
|
double windowHeight = MediaQuery.of(context).size.height;
|
||||||
|
double iconSize;
|
||||||
|
|
||||||
|
if (windowWidth < windowHeight) {
|
||||||
|
iconSize = 0.15 * windowWidth;
|
||||||
|
} else {
|
||||||
|
iconSize = 0.15 * windowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dialog(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
pieceChosen(ChessPieceClass.queen);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(chessPiecesAssets[ChessPieceAssetKey(
|
||||||
|
pieceClass: ChessPieceClass.queen, color: sideColor)]!),
|
||||||
|
iconSize: 200,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
pieceChosen(ChessPieceClass.rook);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(chessPiecesAssets[ChessPieceAssetKey(
|
||||||
|
pieceClass: ChessPieceClass.rook, color: sideColor)]!),
|
||||||
|
iconSize: 100,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
pieceChosen(ChessPieceClass.knight);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(chessPiecesAssets[ChessPieceAssetKey(
|
||||||
|
pieceClass: ChessPieceClass.knight, color: sideColor)]!),
|
||||||
|
iconSize: 10,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
pieceChosen(ChessPieceClass.bishop);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(chessPiecesAssets[ChessPieceAssetKey(
|
||||||
|
pieceClass: ChessPieceClass.bishop, color: sideColor)]!),
|
||||||
|
iconSize: iconSize,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pieceChosen(ChessPieceClass pieceClass) {
|
||||||
|
PromotionBloc.getInstance()
|
||||||
|
.add(PieceChosen(pieceClass: pieceClass, color: sideColor));
|
||||||
|
}
|
||||||
|
}
|
38
pubspec.lock
38
pubspec.lock
@ -53,10 +53,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.17.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -144,6 +144,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -164,18 +172,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.16"
|
version: "0.12.15"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.2.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -241,10 +249,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.9.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -281,10 +289,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.5.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -333,14 +341,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: web
|
|
||||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.4-beta"
|
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -358,5 +358,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0-185.0.dev <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.7.0-0"
|
flutter: ">=3.7.0-0"
|
||||||
|
Loading…
Reference in New Issue
Block a user