private FuuroGroup HandleAnkan(Player currentPlayer, string tileName) { tileName = tileName.Replace('0', '5'); FuuroGroup fuuroGroup = new FuuroGroup(); fuuroGroup.type = FuuroType.ankan; if (currentPlayer == player) { IEnumerable <Tile> tiles = player.hand.Where(t => t.GeneralName == tileName).ToList(); fuuroGroup.AddRange(tiles); player.hand.RemoveRange(tiles); } else { if (tileName[0] == '5' && tileName[1] != 'z') // 暗杠中有红牌 { fuuroGroup.Add(new Tile(tileName)); fuuroGroup.Add(new Tile(tileName)); fuuroGroup.Add(new Tile(tileName)); fuuroGroup.Add(new Tile("0" + tileName[1])); } else { for (var i = 0; i < 4; i++) { fuuroGroup.Add(new Tile(tileName)); } } } currentPlayer.fuuro.Add(fuuroGroup); return(fuuroGroup); }
private bool shouldAnKan(Tile tile) { FuuroGroup group = tryGetFuuroGroup(FuuroType.ankan, new[] { Tuple.Create(tile.GeneralId, 4) }); if (group == null || gameData.remainingTile <= 0) { return(false); } int distance = calcDistance(); player.fuuro.Add(group); player.hand.RemoveRange(group); int newDistance = calcDistance(); bool res = false; if (newDistance == distance) { res = true; } player.fuuro.Remove(group); player.hand.AddRange(group); return(res); }
private FuuroGroup tryGetFuuroGroup(FuuroType type, IEnumerable <Tuple <int, int> > req, params Tile[] tiles) { var res = new FuuroGroup(); res.type = type; foreach (var reqItem in req) { var tmp = new List <Tile>(); tmp.AddRange(player.hand.Where(t => t.GenaralId == reqItem.Item1 && !tiles.Any(t2 => t == t2) && t.isRedDora)); tmp.AddRange(player.hand.Where(t => t.GenaralId == reqItem.Item1 && !tiles.Any(t2 => t == t2) && !t.isRedDora)); if (tmp.Count >= reqItem.Item2) { res.AddRange(tmp.Take(reqItem.Item2)); } else { return(null); } } res.AddRange(tiles); return(res); }
protected override void OnNaki(Player currentPlayer, FuuroGroup fuuro) { if (currentPlayer == player && (fuuro.type == FuuroType.pon || fuuro.type == FuuroType.chii)) { decideMove(rightAfterNaki: true); } }
protected override void OnNaki(Player player, FuuroGroup fuuro) { if (fuuro == null) { Trace.WriteLine(string.Format("player({0}) unknown naki", player.id)); return; } Trace.WriteLine(string.Format("player({0}) {1}: {2}", player.id, fuuro.type.ToString(), fuuro.ToString(" ", t => t.Name))); }
private string getFuuroType(FuuroGroup group) { var hasRedDora = group.Any(t => t.isRedDora); if (hasRedDora && group.type == FuuroType.chii) { return("aka_chii"); } else if (hasRedDora && group.type == FuuroType.pon) { return("aka_pon1"); } else { return(group.type.ToString()); } }
private FuuroGroup HandleKakan(Player currentPlayer, string tileName) { FuuroGroup fuuroGroup = currentPlayer.fuuro.First(g => g.type == FuuroType.pon && g.All(t => t.GeneralName == tileName.Replace('0', '5'))); fuuroGroup.type = FuuroType.kakan; if (currentPlayer == player) { Tile tile = player.hand.First(t => t.GeneralName == tileName.Replace('0', '5')); player.hand.Remove(tile); fuuroGroup.Add(tile); } else { fuuroGroup.Add(new Tile(tileName)); } return(fuuroGroup); }
private FuuroGroup HandleFuuro(Player currentPlayer, int type, IEnumerable <string> tiles, IEnumerable <int> froms) { FuuroGroup fuuroGroup = new FuuroGroup(); switch (type) { case 0: fuuroGroup.type = FuuroType.chii; break; case 1: fuuroGroup.type = FuuroType.pon; break; case 2: fuuroGroup.type = FuuroType.minkan; break; } foreach (var(tileName, from) in tiles.Zip(froms, Tuple.Create)) { if (NormalizedPlayerId(from) != currentPlayer.id) // 从别人那里拿来的牌 { fuuroGroup.Add(gameData.lastTile); gameData.lastTile.IsTakenAway = true; } else if (currentPlayer == player) // 自己的手牌 { Tile tile = player.hand.First(t => t.OfficialName == tileName); player.hand.Remove(tile); fuuroGroup.Add(tile); } else { fuuroGroup.Add(new Tile(tileName)); } } currentPlayer.fuuro.Add(fuuroGroup); return(fuuroGroup); }
private void HandleMessage(MajsoulMessage message, bool forSync = false) { if (syncing && !forSync && message.MethodName != ".lq.FastTest.fetchGamePlayerState") { pendingActions.Enqueue(message); return; } if (timers.ContainsKey(message.MethodName)) { timers[message.MethodName].Dispose(); } if (!message.Success && message.MethodName != ".lq.FastTest.authGame") { return; } if (message.MethodName == ".lq.Lobby.login" || message.MethodName == ".lq.Lobby.oauth2Login") { accountId = (int)message.Json["account_id"]; if (message.Json["error"] != null && message.Json["error"]["code"] != null) { InvokeOnLogin(resume: false, succeeded: false); } else if (message.Json["game_info"] != null) { continued = true; InvokeOnLogin(resume: true, succeeded: true); StartGame(message.Json["game_info"], true); } else { InvokeOnLogin(resume: false, succeeded: true); } } if (message.MethodName == ".lq.NotifyRoomGameStart" || message.MethodName == ".lq.NotifyMatchGameStart") { StartGame(message.Json, false); } else if (message.MethodName == ".lq.FastTest.authGame") { gameStarted = true; InvokeOnGameStart(continued); if (!continued) { Send(wsGame, ".lq.FastTest.enterGame", new { }).Wait(); } else { Send(wsGame, ".lq.FastTest.syncGame", new { round_id = "-1", step = 1000000 }).Wait(); continued = false; } } else if (message.MethodName == ".lq.NotifyPlayerLoadGameReady") { playerSeat = message.Json["ready_id_list"].Select(t => (int)t).ToList().IndexOf(accountId); } else if (message.MethodName == "ActionMJStart") { gameEnded = false; } else if (message.MethodName == ".lq.NotifyGameEndResult") { Bye(); gameEnded = true; InvokeOnGameEnd(); } else if (message.MethodName == "ActionHule") { int[] points = message.Json["scores"].Select(t => (int)t).ToArray(); int[] rawPointDeltas = message.Json["delta_scores"].Select(t => (int)t).ToArray(); int[] pointDeltas = new int[4]; for (var i = 0; i < 4; i++) { gameData.players[NormalizedPlayerId(i)].point = points[i]; pointDeltas[NormalizedPlayerId(i)] = rawPointDeltas[i]; } foreach (var agari in message.Json["hules"]) { Player who = gameData.players[NormalizedPlayerId((int)agari["seat"])]; Player fromWho = pointDeltas.Count(s => s < 0) == 1 ? gameData.players[Array.FindIndex(pointDeltas, s => s < 0)] : who; int point = !(bool)agari["zimo"] ? (int)agari["point_rong"] : (bool)agari["qinjia"] ? (int)agari["point_zimo_xian"] * 3 : (int)agari["point_zimo_xian"] * 2 + (int)agari["point_zimo_qin"]; if (gameData.lastTile != null) { gameData.lastTile.IsTakenAway = true; } if ((bool)agari["yiman"]) { SaveReplayTag("Yakuman"); } InvokeOnAgari(who, fromWho, point, pointDeltas, gameData.players); } DelayedNextReady(); } else if (message.MethodName == "ActionLiuJu") { InvokeOnAgari(null, null, 0, new[] { 0, 0, 0, 0 }, gameData.players); DelayedNextReady(); } else if (message.MethodName == "ActionNoTile") { var scoreObj = message.Json["scores"][0]; int[] rawPointDeltas = scoreObj["delta_scores"] != null ? scoreObj["delta_scores"].Select(t => (int)t).ToArray() : new[] { 0, 0, 0, 0 }; int[] pointDeltas = new int[4]; for (var i = 0; i < 4; i++) { gameData.players[NormalizedPlayerId(i)].point = (int)scoreObj["old_scores"][i] + rawPointDeltas[i]; pointDeltas[NormalizedPlayerId(i)] = rawPointDeltas[i]; } InvokeOnAgari(null, null, 0, pointDeltas, gameData.players); DelayedNextReady(); } else if (message.MethodName == "ActionNewRound") { Tile.Reset(); gameData = new GameData(); HandleInit(message.Json); if (!syncing) { InvokeOnInit(/* continued */ false, gameData.direction, gameData.seq, gameData.seq2, gameData.players); } if (player.hand.Count > 13) { operationList = message.Json["operation"]["operation_list"]; if (!syncing) { Thread.Sleep(2000); // 等待发牌动画结束 stopwatch.Restart(); InvokeOnDraw(player.hand.Last()); } } } else if (message.MethodName == ".lq.FastTest.syncGame") { syncing = true; continuedBetweenGames = (int)message.Json["step"] == 0; Send(wsGame, ".lq.FastTest.fetchGamePlayerState", new { }).Wait(); if (message.Json["game_restore"]["actions"] != null) { foreach (var action in message.Json["game_restore"]["actions"]) { var bytes = Convert.FromBase64String((string)action["data"]); var actionMessage = majsoulHelper.decodeActionPrototype((string)action["name"], bytes); pendingActions.Enqueue(actionMessage); } } } else if (message.MethodName == ".lq.FastTest.fetchGamePlayerState") { bool inited = false; playerSeat = message.Json["state_list"].ToList().IndexOf("SYNCING") - 2; while (pendingActions.Count > 1) { var actionMessage = pendingActions.Dequeue(); if (actionMessage.MethodName == "ActionNewRound") { inited = true; } HandleMessage(actionMessage, forSync: true); } Send(wsGame, ".lq.FastTest.finishSyncGame", new { }).Wait(); syncing = false; if (inited) { InvokeOnInit(/* continued */ true, gameData.direction, gameData.seq, gameData.seq2, gameData.players); } // Queue里的最后一个action需要响应 if (pendingActions.Count > 0) { HandleMessage(pendingActions.Dequeue()); } if (continuedBetweenGames) { NextReady(); } } else if (message.MethodName == "ActionDealTile") { gameData.remainingTile = (int)message.Json["left_tile_count"]; if (message.Json["doras"] != null) { var doras = message.Json["doras"].Select(t => (string)t); foreach (var dora in doras.Skip(gameData.dora.Count)) { gameData.dora.Add(new Tile(dora)); } } if (NormalizedPlayerId((int)message.Json["seat"]) == 0) { Tile tile = new Tile((string)message.Json["tile"]); player.hand.Add(tile); gameData.lastTile = tile; operationList = message.Json["operation"]["operation_list"]; if (!syncing) { stopwatch.Restart(); InvokeOnDraw(tile); } } } else if (message.MethodName == "ActionDiscardTile") { Player currentPlayer = gameData.players[NormalizedPlayerId((int)message.Json["seat"])]; if (!(bool)message.Json["moqie"]) { currentPlayer.safeTiles.Clear(); } var tileName = (string)message.Json["tile"]; if (currentPlayer == player) { if (lastDiscardedTile == null || lastDiscardedTile.OfficialName != tileName) { lastDiscardedTile = player.hand.First(t => t.OfficialName == tileName); } player.hand.Remove(lastDiscardedTile); } Tile tile = currentPlayer == player ? lastDiscardedTile : new Tile(tileName); lastDiscardedTile = null; currentPlayer.graveyard.Add(tile); gameData.lastTile = tile; foreach (var p in gameData.players) { p.safeTiles.Add(tile); } if ((bool)message.Json["is_liqi"] || (bool)message.Json["is_wliqi"]) { currentPlayer.reached = true; currentPlayer.safeTiles.Clear(); if (!syncing) { InvokeOnReach(currentPlayer); } } if (!syncing) { InvokeOnDiscard(currentPlayer, tile); } JToken keyValuePairs = message.Json; if (keyValuePairs["doras"] != null) { var doras = message.Json["doras"].Select(t => (string)t); foreach (var dora in doras.Skip(gameData.dora.Count)) { gameData.dora.Add(new Tile(dora)); } } if (keyValuePairs["operation"] != null) { operationList = message.Json["operation"]["operation_list"]; if (!syncing) { stopwatch.Restart(); InvokeOnWait(tile, currentPlayer); } } } else if (message.MethodName == "ActionChiPengGang") { Player currentPlayer = gameData.players[NormalizedPlayerId((int)message.Json["seat"])]; var fuuro = HandleFuuro(currentPlayer, (int)message.Json["type"], message.Json["tiles"].Select(t => (string)t), message.Json["froms"].Select(t => (int)t)); if (!syncing) { InvokeOnNaki(currentPlayer, fuuro); } } else if (message.MethodName == "ActionAnGangAddGang") { Player currentPlayer = gameData.players[NormalizedPlayerId((int)message.Json["seat"])]; FuuroGroup fuuro = null; if ((int)message.Json["type"] == 2) { fuuro = HandleKakan(currentPlayer, (string)message.Json["tiles"]); } else if ((int)message.Json["type"] == 3) { fuuro = HandleAnkan(currentPlayer, (string)message.Json["tiles"]); } if (!syncing) { InvokeOnNaki(currentPlayer, fuuro); } } }
private FuuroGroup HandleFuuro(Player currentPlayer, int m) { int type, kui; int[] hai = new int[4]; decodeMeld(m, out type, out kui, out hai[0], out hai[1], out hai[2], out hai[3]); FuuroGroup tiles = new FuuroGroup(); switch (type) { case 3: tiles.type = FuuroType.chii; break; case 1: tiles.type = FuuroType.pon; break; case 2: tiles.type = FuuroType.minkan; break; case 4: tiles.type = FuuroType.ankan; gameData.remainingTile--; break; case 5: tiles.type = FuuroType.kakan; gameData.remainingTile--; break; } foreach (int num in hai) { if (num != -1) { tiles.Add(new Tile(num)); if (gameData.lastTile != null && gameData.lastTile.Id == num) { gameData.lastTile.IsTakenAway = true; } } } if (kui != 0) // 从别人处拿到的牌 { var takenTile = new Tile(hai[3 - kui]); takenTile.IsTakenAway = true; var fromPlayer = gameData.players[(kui + currentPlayer.id) % 4]; if (!fromPlayer.graveyard.Exists(t => t.Id == takenTile.Id)) { fromPlayer.graveyard.Add(takenTile); gameData.remainingTile--; } } currentPlayer.fuuro.Add(tiles); // 如果加杠则移除原来的碰 if (tiles.type == FuuroType.kakan) { currentPlayer.fuuro.RemoveAll(g => g.type == FuuroType.pon && g.All(t => t.GeneralId == tiles[0].GeneralId)); } if (currentPlayer == player) { player.hand.RemoveWhere((tile) => tiles.Exists((_tile) => tile.Id == _tile.Id)); } return(tiles); }
protected void InvokeOnNaki(Player player, FuuroGroup fuuro) { OnNaki?.Invoke(player, fuuro); }
protected virtual void OnNaki(Player player, FuuroGroup fuuro) { }
protected override void OnNaki(Player player, FuuroGroup fuuro) { Trace.WriteLine(string.Format("player({0}) {1}: {2}", player.id, fuuro.type.ToString(), fuuro.ToString(" ", t => t.Name))); }