public static void TestFenGeneration() { ThreeCheckChessGame game = new ThreeCheckChessGame("rnb1kb1Q/pppp1p1p/5np1/8/8/8/PPPPKPPP/RNB3NR w q - 1 7 +1+2"); game.ApplyMove(new Move("H8", "F8", Player.White), true); Assert.AreEqual("rnb1kQ2/pppp1p1p/5np1/8/8/8/PPPPKPPP/RNB3NR b q - 0 7 +2+2", game.GetFen()); }
public static void TestRecordedChecks() { ThreeCheckChessGame game = new ThreeCheckChessGame(); game.ApplyMove(new Move("E2", "E4", Player.White), true); game.ApplyMove(new Move("E7", "E5", Player.Black), true); game.ApplyMove(new Move("D1", "H5", Player.White), true); game.ApplyMove(new Move("G7", "G6", Player.Black), true); game.ApplyMove(new Move("H5", "E5", Player.White), true); Assert.AreEqual(1, game.ChecksByWhite); Assert.AreEqual(0, game.ChecksByBlack); game.ApplyMove(new Move("D8", "E7", Player.Black), true); game.ApplyMove(new Move("E5", "H8", Player.White), true); game.ApplyMove(new Move("E7", "E4", Player.Black), true); Assert.AreEqual(1, game.ChecksByWhite); Assert.AreEqual(1, game.ChecksByBlack); game.ApplyMove(new Move("F1", "E2", Player.White), true); game.ApplyMove(new Move("F8", "G7", Player.Black), true); game.ApplyMove(new Move("H8", "G8", Player.White), true); Assert.AreEqual(2, game.ChecksByWhite); game.ApplyMove(new Move("E8", "E7", Player.Black), true); game.ApplyMove(new Move("G8", "E8", Player.White), true); Assert.AreEqual(3, game.ChecksByWhite); Assert.True(game.IsWinner(Player.White)); }
public static void TestFenCheckCounter() { ThreeCheckChessGame game = new ThreeCheckChessGame("rnb1kbnQ/pppp1p1p/6p1/8/8/8/PPPPqPPP/RNB1K1NR w KQq - 0 6 +1+2"); Assert.AreEqual(1, game.ChecksByWhite); Assert.AreEqual(2, game.ChecksByBlack); }
public IActionResult SetupTraining(string id, string trainingSessionId = null) { int puzzleId; if (!int.TryParse(id, out puzzleId)) { return(Json(new { success = false, error = "Invalid puzzle ID." })); } Puzzle puzzle = puzzleRepository.Get(puzzleId); if (puzzle == null) { return(Json(new { success = false, error = "Puzzle not found." })); } puzzle.Game = gameConstructor.Construct(puzzle.Variant, puzzle.InitialFen); PuzzleTrainingSession session; if (trainingSessionId == null) { string g; do { g = Guid.NewGuid().ToString(); } while (puzzleTrainingSessionRepository.ContainsTrainingSessionId(g)); session = new PuzzleTrainingSession(g, gameConstructor); puzzleTrainingSessionRepository.Add(session); } else { session = puzzleTrainingSessionRepository.Get(trainingSessionId); if (session == null) { return(Json(new { success = false, error = "Puzzle training session ID not found." })); } } session.Setup(puzzle); string additionalInfo = null; if (puzzle.Variant == "ThreeCheck") { ThreeCheckChessGame tccg = puzzle.Game as ThreeCheckChessGame; additionalInfo = string.Format("At the puzzle's initial position, white delivered {0} checks and black delivered {1} checks.", tccg.ChecksByWhite, tccg.ChecksByBlack); } return(Json(new { success = true, trainingSessionId = session.SessionID, author = userRepository.FindById(session.Current.Author).Username, fen = session.Current.InitialFen, dests = moveCollectionTransformer.GetChessgroundDestsForMoveCollection(session.Current.Game.GetValidMoves(session.Current.Game.WhoseTurn)), whoseTurn = session.Current.Game.WhoseTurn.ToString().ToLowerInvariant(), variant = puzzle.Variant, additionalInfo = additionalInfo, authorUrl = Url.Action("Profile", "User", new { id = session.Current.Author }), pocket = session.Current.Game.GenerateJsonPocket(), check = session.Current.Game.IsInCheck(Player.White) ? "white" : (session.Current.Game.IsInCheck(Player.Black) ? "black" : null) })); }
void GenerateMateInOnePositions <TGame>(string pgn, Func <string, TGame> createFromFen, string variant, string initialFen) where TGame : ChessGame, new() { if (variant == "Horde" && initialFen.StartsWith("pppppppp")) { return; // Once upon a time on lichess, the Black player had the Pawns and the White player the Pieces. That has been changed now, resulting in several invalid PGNs. } PgnReader <TGame> pgnReader = new PgnReader <TGame>(); TGame copy = new TGame(); try { pgnReader.ReadPgnFromString(pgn); } catch (PgnException) { Console.WriteLine("[Warning] PGN exception for variant {0}; {1}", variant, pgn); return; // I once got a PGN with an atomic game where self-checks were allowed. So to handle this case and similar rule changes like this, I have to catch the PgnException. } string fen; ReadOnlyCollection <DetailedMove> moves = pgnReader.Game.Moves; if (moves.Count >= 3) // there will never be a mate-in-one position if white hasn't done two moves { for (int i = 0; i < moves.Count; i++) { copy.ApplyMove(moves[i], true); if (i < 2) { continue; // there will never be a mate-in-one position if white hasn't done two moves } if (copy is ThreeCheckChessGame) { ThreeCheckChessGame g = copy as ThreeCheckChessGame; if ((g.WhoseTurn == Player.White ? g.ChecksByWhite : g.ChecksByBlack) != 2) { continue; } } fen = copy.GetFen(); ReadOnlyCollection <Move> validMoves = copy.GetValidMoves(copy.WhoseTurn); DetailedMove lastMove = moves[i]; for (int j = 0; j < validMoves.Count; j++) { TGame copy2 = createFromFen(fen); copy2.ApplyMove(validMoves[j], true); if (copy2.IsWinner(ChessUtilities.GetOpponentOf(copy2.WhoseTurn))) { fenQueue.Add(new Tuple <string, string[], string, string>(fen, new string[] { lastMove.OriginalPosition.ToString().ToLowerInvariant(), lastMove.NewPosition.ToString().ToLowerInvariant() }, variant, "mateInOne")); break; } } } } }
public static void TestUndoChecks() { ThreeCheckChessGame game = new ThreeCheckChessGame(); string initial = game.GetFen(); game.MakeMove(new Move("E2", "E4", Player.White), true); game.MakeMove(new Move("D7", "D5", Player.Black), true); game.MakeMove(new Move("F1", "B5", Player.White), true); /* * Assert.AreEqual(1, game.ChecksByWhite); * Assert.True(game.Undo()); * Assert.AreEqual(0, game.ChecksByWhite); * game.Undo(2); * Assert.AreEqual(initial, game.GetFen());*/ Assert.Throws <NotImplementedException>(() => game.Undo()); }
async Task HandleReceived(string text) { GameSocketMessage preprocessed = new GameSocketMessage(text); if (!preprocessed.Okay) { await Send("{\"t\":\"error\",\"d\":\"invalid message\"}"); return; } switch (preprocessed.Type) { case "move": case "premove": if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is not ongoing.\"}"); return; } MoveSocketMessage moveMessage = new MoveSocketMessage(preprocessed); if (!moveMessage.Okay) { await Send("{\"t\":\"error\",\"d\":\"invalid message\"}"); return; } if ((Subject.ChessGame.WhoseTurn == Player.White && !Subject.White.Equals(client)) || (Subject.ChessGame.WhoseTurn == Player.Black && !Subject.Black.Equals(client))) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } bool flagged = await HandlePotentialFlag(Subject.ChessGame.WhoseTurn.ToString().ToLowerInvariant()); if (flagged) { return; } if (!Regex.IsMatch(moveMessage.Move, "([a-h][1-8]-[a-h][1-8](-[qrnbk])?|[PNBRQ]@[a-h][1-8])")) { await Send("{\"t\":\"error\",\"d\":\"invalid message format\"}"); return; } string[] moveParts; MoveType mt = MoveType.Move; if (!moveMessage.Move.Contains("@")) { moveParts = moveMessage.Move.Split('-'); Move move; if (moveParts.Length == 2) { move = new Move(moveParts[0], moveParts[1], Subject.ChessGame.WhoseTurn); } else { move = new Move(moveParts[0], moveParts[1], Subject.ChessGame.WhoseTurn, moveParts[2][0]); } if (Subject.ChessGame.IsValidMove(move)) { mt = await gameRepository.RegisterMoveAsync(Subject, move); } else if (moveMessage.Type == "move") { await Send("{\"t\":\"error\",\"d\":\"invalid move\"}"); return; } else { return; // for premoves, invalid moves can be silently ignored as mostly the problem is just a situation change on the board } } else { CrazyhouseChessGame zhGame = Subject.ChessGame as CrazyhouseChessGame; if (zhGame == null) { await Send("{\"t\":\"error\",\"d\":\"invalid move\"}"); return; } string[] typeAndPos = moveMessage.Move.Split('@'); Position pos = new Position(typeAndPos[1]); Piece piece = Subject.ChessGame.MapPgnCharToPiece(typeAndPos[0][0], Subject.ChessGame.WhoseTurn); Drop drop = new Drop(piece, pos, piece.Owner); if (zhGame.IsValidDrop(drop)) { await gameRepository.RegisterDropAsync(Subject, drop); } else { await Send("{\"t\":\"invalidDrop\",\"pos\":\"" + pos + "\"}"); } moveParts = new string[] { pos.ToString().ToLowerInvariant(), pos.ToString().ToLowerInvariant() }; } string check = null; if (Subject.ChessGame.IsInCheck(Subject.ChessGame.WhoseTurn)) { check = Subject.ChessGame.WhoseTurn.ToString().ToLowerInvariant(); } string outcome = null; if (Subject.ChessGame.IsWinner(Player.White)) { outcome = "1-0, white wins"; await gameRepository.RegisterGameResultAsync(Subject, Game.Results.WHITE_WINS, Game.Terminations.NORMAL); } else if (Subject.ChessGame.IsWinner(Player.Black)) { outcome = "0-1, black wins"; await gameRepository.RegisterGameResultAsync(Subject, Game.Results.BLACK_WINS, Game.Terminations.NORMAL); } else if (Subject.ChessGame.IsDraw() || Subject.ChessGame.DrawCanBeClaimed) { outcome = "½-½, draw"; await gameRepository.RegisterGameResultAsync(Subject, Game.Results.DRAW, Game.Terminations.NORMAL); } Dictionary <string, object> messageForPlayerWhoseTurnItIs = new Dictionary <string, object>(); Dictionary <string, object> messageForOthers = new Dictionary <string, object>(); messageForPlayerWhoseTurnItIs["t"] = messageForOthers["t"] = "moved"; messageForPlayerWhoseTurnItIs["fen"] = messageForOthers["fen"] = Subject.ChessGame.GetFen(); messageForPlayerWhoseTurnItIs["dests"] = moveCollectionTransformer.GetChessgroundDestsForMoveCollection(Subject.ChessGame.GetValidMoves(Subject.ChessGame.WhoseTurn)); messageForPlayerWhoseTurnItIs["lastMove"] = messageForOthers["lastMove"] = new string[] { moveParts[0], moveParts[1] }; messageForPlayerWhoseTurnItIs["turnColor"] = messageForOthers["turnColor"] = Subject.ChessGame.WhoseTurn.ToString().ToLowerInvariant(); messageForPlayerWhoseTurnItIs["plies"] = messageForOthers["plies"] = Subject.ChessGame.Moves.Count; Dictionary <string, double> clockDictionary = new Dictionary <string, double> { ["white"] = Subject.ClockWhite.GetSecondsLeft(), ["black"] = Subject.ClockBlack.GetSecondsLeft() }; messageForPlayerWhoseTurnItIs["clock"] = messageForOthers["clock"] = clockDictionary; messageForPlayerWhoseTurnItIs["check"] = messageForOthers["check"] = check; messageForPlayerWhoseTurnItIs["isCapture"] = messageForOthers["isCapture"] = mt.HasFlag(MoveType.Capture); if (outcome != null) { messageForPlayerWhoseTurnItIs["outcome"] = messageForOthers["outcome"] = outcome; messageForPlayerWhoseTurnItIs["termination"] = messageForOthers["termination"] = Game.Terminations.NORMAL; } if (Subject.ChessGame is ThreeCheckChessGame) { ThreeCheckChessGame tccg = Subject.ChessGame as ThreeCheckChessGame; messageForPlayerWhoseTurnItIs["additional"] = messageForOthers["additional"] = string.Format("White delivered {0} check(s), black delivered {1}.", tccg.ChecksByWhite, tccg.ChecksByBlack); } messageForPlayerWhoseTurnItIs["pocket"] = messageForOthers["pocket"] = Subject.ChessGame.GenerateJsonPocket(); messageForOthers["dests"] = new Dictionary <object, object>(); string jsonPlayersMove = JsonConvert.SerializeObject(messageForPlayerWhoseTurnItIs); string jsonSpectatorsMove = JsonConvert.SerializeObject(messageForOthers); await handlerRepository.SendAll(gameId, jsonPlayersMove, jsonSpectatorsMove, p => (Subject.White.Equals(p) && Subject.ChessGame.WhoseTurn == Player.White) || (Subject.Black.Equals(p) && Subject.ChessGame.WhoseTurn == Player.Black)); break; case "chat": ChatSocketMessage chatSocketMessage = new ChatSocketMessage(preprocessed); if (!chatSocketMessage.Okay) { await Send("{\"t\":\"error\",\"d\":\"invalid message\"}"); return; } int? senderUserId = null; string displayName = null; if (client is RegisteredPlayer) { senderUserId = (client as RegisteredPlayer).UserId; displayName = (await userRepository.FindByIdAsync(senderUserId.Value)).Username; } else { if (client.Equals(Subject.White)) { displayName = "[white]"; } else if (client.Equals(Subject.Black)) { displayName = "[black]"; } } if (displayName == null) { await Send("{\"t\":\"error\",\"d\":\"Anonymous users cannot use the Spectators' chat.\"}"); return; } ChatMessage chatMessage = new ChatMessage(senderUserId, displayName, chatSocketMessage.Content); Dictionary <string, string> forPlayers = null; Dictionary <string, string> forSpectators = null; string jsonPlayersChat = null; string jsonSpectatorsChat = null; if (chatSocketMessage.Channel == "player") { await gameRepository.RegisterPlayerChatMessageAsync(Subject, chatMessage); forPlayers = new Dictionary <string, string>() { { "t", "chat" }, { "channel", "player" }, { "msg", chatMessage.GetHtml() } }; jsonPlayersChat = JsonConvert.SerializeObject(forPlayers); } else if (chatSocketMessage.Channel == "spectator") { if (!(client is RegisteredPlayer)) { await Send("{\"t\":\"error\",\"d\":\"Anonymous users cannot use the Spectators' chat.\"}"); return; } await gameRepository.RegisterSpectatorChatMessageAsync(Subject, chatMessage); forSpectators = new Dictionary <string, string>() { { "t", "chat" }, { "channel", "spectator" }, { "msg", chatMessage.GetHtml() } }; jsonSpectatorsChat = JsonConvert.SerializeObject(forSpectators); if (Subject.Result != Game.Results.ONGOING) { jsonPlayersChat = jsonSpectatorsChat; } } await handlerRepository.SendAll(gameId, jsonPlayersChat, jsonSpectatorsChat, p => Subject.White.Equals(p) || Subject.Black.Equals(p)); break; case "syncClock": Dictionary <string, object> syncedClockDict = new Dictionary <string, object>() { { "t", "clock" }, { "white", Subject.ClockWhite.GetSecondsLeft() }, { "black", Subject.ClockBlack.GetSecondsLeft() }, { "run", Subject.ChessGame.Moves.Count > 1 && Subject.Result == Game.Results.ONGOING }, { "whoseTurn", Subject.ChessGame.WhoseTurn.ToString().ToLowerInvariant() } }; await Send(JsonConvert.SerializeObject(syncedClockDict)); break; case "flag": FlagSocketMessage flagMessage = new FlagSocketMessage(preprocessed); if (!flagMessage.Okay) { await Send("{\"t\":\"error\",\"d\":\"invalid message\"}"); return; } await HandlePotentialFlag(flagMessage.Player); break; case "syncChat": Dictionary <string, object> syncedChat = new Dictionary <string, object> { ["t"] = "chatSync" }; if (Subject.White.Equals(client) || Subject.Black.Equals(client)) { syncedChat["player"] = Subject.PlayerChats.Select(x => x.GetHtml()); } if (Subject.Result != Game.Results.ONGOING || !(Subject.White.Equals(client) || Subject.Black.Equals(client))) { syncedChat["spectator"] = Subject.SpectatorChats.Select(x => x.GetHtml()); } await Send(JsonConvert.SerializeObject(syncedChat)); break; case "rematch-offer": case "rematch-yes": if (Subject.Result == Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is still ongoing.\"}"); return; } bool isWhite = false; bool isBlack = false; if (!(isWhite = Subject.White.Equals(client)) && !(isBlack = Subject.Black.Equals(client))) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } bool createRematch = false; if (isWhite) { await gameRepository.RegisterWhiteRematchOfferAsync(Subject); if (Subject.BlackWantsRematch) { createRematch = true; } } else { await gameRepository.RegisterBlackRematchOfferAsync(Subject); if (Subject.WhiteWantsRematch) { createRematch = true; } } if (createRematch) { int posWhite; int posBlack; if (Subject.RematchLevel % 2 == 0) { posWhite = Subject.PositionWhite; posBlack = Subject.PositionBlack; } else { posWhite = randomProvider.RandomPositiveInt(Subject.ShortVariantName != "RacingKings" ? 960 : 1440); if (Subject.IsSymmetrical) { posBlack = posWhite; } else { posBlack = randomProvider.RandomPositiveInt(Subject.ShortVariantName != "RacingKings" ? 960 : 1440); } } Game newGame = new Game(await gameRepository.GenerateIdAsync(), Subject.Black, Subject.White, Subject.ShortVariantName, Subject.FullVariantName, posWhite, posBlack, Subject.IsSymmetrical, Subject.TimeControl, DateTime.UtcNow, Subject.RematchLevel + 1, gameConstructor); await gameRepository.AddAsync(newGame); await gameRepository.SetRematchIDAsync(Subject, newGame.ID); string rematchJson = "{\"t\":\"rematch\",\"d\":\"" + newGame.ID + "\"}"; await Send(rematchJson); await handlerRepository.SendAll(gameId, rematchJson, null, x => true); } else { string rematchOfferJson = "{\"t\":\"rematch-offer\"}"; await handlerRepository.SendAll(gameId, rematchOfferJson, null, x => x.Equals(isWhite ? Subject.Black : Subject.White)); } break; case "rematch-no": if (Subject.Result == Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is still ongoing.\"}"); return; } bool isWhite_ = false; bool isBlack_ = false; if (!(isWhite_ = Subject.White.Equals(client)) && !(isBlack_ = Subject.Black.Equals(client))) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } await gameRepository.ClearRematchOffersAsync(Subject); await handlerRepository.SendAll(gameId, "{\"t\":\"rematch-decline\"}", null, x => x.Equals(isWhite_ ? Subject.Black : Subject.White)); break; case "resign": if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is not ongoing.\"}"); return; } bool whiteResigns; if (!(whiteResigns = Subject.White.Equals(client)) && !Subject.Black.Equals(client)) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"Game is not ongoing.\"}"); return; } string outcomeAfterResign; if (!whiteResigns) { outcomeAfterResign = "1-0, white wins"; await gameRepository.RegisterGameResultAsync(Subject, Game.Results.WHITE_WINS, Game.Terminations.RESIGNATION); } else { outcomeAfterResign = "0-1, black wins"; await gameRepository.RegisterGameResultAsync(Subject, Game.Results.BLACK_WINS, Game.Terminations.RESIGNATION); } Dictionary <string, string> outcomeResponseDict = new Dictionary <string, string>() { { "t", "outcome" }, { "outcome", outcomeAfterResign }, { "termination", Game.Terminations.RESIGNATION } }; await handlerRepository.SendAll(gameId, JsonConvert.SerializeObject(outcomeResponseDict), null, x => true); break; case "abort": if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is not ongoing.\"}"); return; } if (!Subject.White.Equals(client) && !Subject.Black.Equals(client)) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } if (Subject.UciMoves.Count > 1) { await Send("{\"t\":\"error\",\"d\":\"It's too late to abort.\"}"); return; } await gameRepository.RegisterGameResultAsync(Subject, Game.Results.ABORTED, Game.Terminations.ABORTED); Dictionary <string, string> abortResultDict = new Dictionary <string, string>() { { "t", "outcome" }, { "outcome", Game.Results.ABORTED }, { "termination", Game.Terminations.ABORTED } }; await handlerRepository.SendAll(gameId, JsonConvert.SerializeObject(abortResultDict), null, x => true); break; case "draw-offer": case "draw-yes": if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is not ongoing.\"}"); return; } bool whiteOfferingDraw = false; bool blackOfferingDraw = false; if (!(whiteOfferingDraw = Subject.White.Equals(client)) && !(blackOfferingDraw = Subject.Black.Equals(client))) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } if (whiteOfferingDraw) { await gameRepository.RegisterWhiteDrawOfferAsync(Subject); } else { await gameRepository.RegisterBlackDrawOfferAsync(Subject); } if (Subject.WhiteWantsDraw && Subject.BlackWantsDraw) { await gameRepository.RegisterGameResultAsync(Subject, Game.Results.DRAW, Game.Terminations.NORMAL); Dictionary <string, string> drawResultDict = new Dictionary <string, string>() { { "t", "outcome" }, { "outcome", Game.Results.DRAW }, { "termination", Game.Terminations.NORMAL } }; await handlerRepository.SendAll(SubjectID, JsonConvert.SerializeObject(drawResultDict), null, x => true); } else { string rematchOfferJson = "{\"t\":\"draw-offer\"}"; await handlerRepository.SendAll(gameId, rematchOfferJson, null, x => x.Equals(whiteOfferingDraw ? Subject.Black : Subject.White)); } break; case "draw-no": if (Subject.Result != Game.Results.ONGOING) { await Send("{\"t\":\"error\",\"d\":\"The game is not ongoing.\"}"); return; } bool whiteDecliningDraw = false; bool blackDecliningDraw = false; if (!(whiteDecliningDraw = Subject.White.Equals(client)) && !(blackDecliningDraw = Subject.Black.Equals(client))) { await Send("{\"t\":\"error\",\"d\":\"no permission\"}"); return; } if (whiteDecliningDraw && !Subject.BlackWantsDraw) { await Send("{\"t\":\"error\",\"d\":\"You have no open draw offers.\"}"); return; } if (blackDecliningDraw && !Subject.WhiteWantsDraw) { await Send("{\"t\":\"error\",\"d\":\"You have no open draw offers.\"}"); return; } await gameRepository.ClearDrawOffersAsync(Subject); await handlerRepository.SendAll(gameId, "{\"t\":\"draw-decline\"}", null, x => x.Equals(whiteDecliningDraw ? Subject.Black : Subject.White)); break; case "keepAlive": await Send("{\"t\":\"keepAlive\"}"); break; } }