import 'dart:convert'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:mchess/api/websocket_message.dart'; import 'package:mchess/chess_bloc/chess_events.dart'; import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/connection/ws_connection.dart'; import 'package:mchess/utils/chess_utils.dart'; import 'dart:developer'; class ChessBloc extends Bloc { static final ChessBloc _instance = ChessBloc._internal(); static ChessColor turnColor = ChessColor.white; static ChessColor? myColor = ChessColor.white; static ChessColor? getSidesColor() { return myColor; } ChessBloc._internal() : super(ChessBoardState.init()) { on(initBoard); on(flipBoard); on(moveHandler); on(promotionHandler); on(ownMoveHandler); on(ownPromotionHandler); on(invalidMoveHandler); } factory ChessBloc.getInstance() { return ChessBloc(); } factory ChessBloc() { return _instance; } void initBoard(InitBoard event, Emitter emit) { ChessPosition.getInstance().resetToStartingPosition(); emit(ChessBoardState(ChessColor.white, ChessColor.white, ChessPosition.getInstance().currentPosition)); } void flipBoard(ColorDetermined event, Emitter emit) { log("My Color is $myColor"); myColor = event.myColor; emit(ChessBoardState(event.myColor, state.newTurnColor, state.position)); } void moveHandler(ReceivedMove event, Emitter emit) { 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); 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 ? ChessColor.black : ChessColor.white; emit( ChessBoardState( state.bottomColor, turnColor, newPosition, ), ); } void promotionHandler( ReceivedPromotion event, Emitter 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); emit(ChessBoardState( state.bottomColor, turnColor, newPosition, )); } void ownMoveHandler(OwnPieceMoved event, Emitter emit) { log('ownMoveHandler()'); var apiMove = ChessMove(from: event.startSquare, to: event.endSquare).toApiMove(); var apiMessage = ApiWebsocketMessage( type: MessageType.move, move: apiMove, color: null, reason: null); ServerConnection.getInstance().send(jsonEncode(apiMessage)); //Temporary chess position until server responds with acknowledgement var move = ChessMove.fromApiMove(apiMove); var tempPosition = ChessPosition.getInstance().copyOfCurrentPosition; tempPosition[move.to] = tempPosition[move.from] ?? const ChessPiece.none(); tempPosition[move.from] = const ChessPiece.none(); emit( ChessBoardState( state.bottomColor, turnColor, tempPosition, ), ); } void ownPromotionHandler( OwnPromotionPlayed event, Emitter 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( InvalidMovePlayed event, Emitter emit) { emit( ChessBoardState( state.bottomColor, turnColor, ChessPosition.getInstance().currentPosition, ), ); } } class ChessBoardState { late ChessColor bottomColor; final ChessColor newTurnColor; final Map position; ChessBoardState._(this.bottomColor, this.newTurnColor, this.position); factory ChessBoardState( ChessColor bottomColor, ChessColor turnColor, Map position, ) { return ChessBoardState._(bottomColor, turnColor, position); } factory ChessBoardState.init() { ChessColor bottomColor = ChessColor.white; ChessColor turnColor = ChessColor.white; ChessPosition.getInstance().resetToStartingPosition(); return ChessBoardState( bottomColor, turnColor, ChessPosition.getInstance().currentPosition); } void logPosition(Map pos) { // for (int i = 0; i < 7; i++) } }