コード例 #1
0
ファイル: MajsoulClient.cs プロジェクト: sparrowhe/MahjongAI
        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);
        }
コード例 #2
0
ファイル: AIController.cs プロジェクト: winterdl/MahjongAI
        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);
        }
コード例 #3
0
        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);
        }
コード例 #4
0
ファイル: AIController.cs プロジェクト: winterdl/MahjongAI
 protected override void OnNaki(Player currentPlayer, FuuroGroup fuuro)
 {
     if (currentPlayer == player && (fuuro.type == FuuroType.pon || fuuro.type == FuuroType.chii))
     {
         decideMove(rightAfterNaki: true);
     }
 }
コード例 #5
0
ファイル: Monitor.cs プロジェクト: winterdl/MahjongAI
 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)));
 }
コード例 #6
0
        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());
            }
        }
コード例 #7
0
ファイル: MajsoulClient.cs プロジェクト: sparrowhe/MahjongAI
        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);
        }
コード例 #8
0
ファイル: MajsoulClient.cs プロジェクト: sparrowhe/MahjongAI
        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);
        }
コード例 #9
0
ファイル: MajsoulClient.cs プロジェクト: sparrowhe/MahjongAI
        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);
                }
            }
        }
コード例 #10
0
ファイル: TenhouClient.cs プロジェクト: cawa0505/MahjongAI
        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);
        }
コード例 #11
0
 protected void InvokeOnNaki(Player player, FuuroGroup fuuro)
 {
     OnNaki?.Invoke(player, fuuro);
 }
コード例 #12
0
 protected virtual void OnNaki(Player player, FuuroGroup fuuro)
 {
 }
コード例 #13
0
ファイル: Monitor.cs プロジェクト: tanshiheng/Tenhou-Client
 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)));
 }