Ejemplo n.º 1
0
        static Kuikae()
        {
            KuikaeTileTypesByCalledTileTypeId = new List <List <TileType> >();
            for (var i = 0; i < 34; i++)
            {
                var list = new List <TileType> {
                    TileType.FromTileTypeId(i)
                };
                if (i < 27 && i % 9 > 3)
                {
                    list.Add(TileType.FromTileId(i - 3));
                }
                if (i < 27 && i % 9 < 6)
                {
                    list.Add(TileType.FromTileId(i + 3));
                }

                KuikaeTileTypesByCalledTileTypeId.Add(list);
            }
        }
Ejemplo n.º 2
0
        public static void Parse(string path, IReplayVisitor visitor)
        {
            var haipai       = new Tile[13];
            var scores       = new int[4];
            var scoreChanges = new int[4];

            var playerCount    = 4;
            var activePlayerId = 0;

            var indexInBlock = 0;
            var block        = File.ReadAllBytes(path);
            var maxIndex     = block.Length;

#if DEBUG
            var log = new StringBuilder();
#endif

            while (maxIndex > indexInBlock)
            {
                var action = block[indexInBlock++];
                if (action == 255)
                {
                    visitor.End();
#if DEBUG
                    log.AppendLine("end bundle");
#endif
                    return;
                }
                if (action == 127)
                {
                    visitor.EndMatch();
#if DEBUG
                    log.AppendLine("end match");
#endif
                    continue;
                }

                switch (action)
                {
                case 0: // GO flags: 1 byte
                    var flags = (GameTypeFlag)block[indexInBlock++];
                    if (flags.HasFlag(GameTypeFlag.Sanma))
                    {
                        playerCount = 3;
                    }

                    visitor.GameType(flags);
#if DEBUG
                    log.AppendLine("go");
#endif
                    break;

                case 1: // INIT seed: 6 bytes, ten: playerCount*4 bytes, oya: 1 byte
                {
                    var roundWind     = TileType.FromTileTypeId(27 + block[indexInBlock++] / 4);
                    var honba         = block[indexInBlock++];
                    var riichiSticks  = block[indexInBlock++];
                    var dice0         = block[indexInBlock++];
                    var dice1         = block[indexInBlock++];
                    var doraIndicator = Tile.FromTileId(block[indexInBlock++]);

                    for (var i = 0; i < playerCount; i++)
                    {
                        scores[i]     = BitConverter.ToInt32(block, indexInBlock) * 100;
                        indexInBlock += 4;
                    }

                    activePlayerId = block[indexInBlock++];

                    visitor.Seed(roundWind, honba, riichiSticks, dice0, dice1, doraIndicator);
                    visitor.Scores(scores);
                    visitor.Oya(activePlayerId);
#if DEBUG
                    log.AppendLine("init");
#endif
                    break;
                }

                case 2: // INIT haipai 1 byte playerId, 13 bytes tileIds
                {
                    var playerId = block[indexInBlock++];
                    for (var i = 0; i < 13; i++)
                    {
                        haipai[i] = Tile.FromTileId(block[indexInBlock++]);
                    }

                    visitor.Haipai(playerId, haipai);
#if DEBUG
                    log.AppendLine("haipai");
#endif
                    break;
                }

                case 3: // Draw: 1 byte playerId, 1 byte tileId
                {
                    var tileId = block[indexInBlock++];
                    var tile   = Tile.FromTileId(tileId);
                    visitor.Draw(activePlayerId, tile);
#if DEBUG
                    log.AppendLine("draw");
#endif
                    break;
                }

                case 4: // Discard: 1 byte playerId, 1 byte tileId
                {
                    var tileId = block[indexInBlock++];
                    var tile   = Tile.FromTileId(tileId);
                    visitor.Discard(activePlayerId, tile);

                    activePlayerId = (activePlayerId + 1) % 4;
#if DEBUG
                    log.AppendLine("discard");
#endif
                    break;
                }

                case 5: // Tsumogiri: 1 byte playerId, 1 byte tileId
                {
                    var tileId = block[indexInBlock++];
                    var tile   = Tile.FromTileId(tileId);
                    visitor.Draw(activePlayerId, tile);
                    visitor.Discard(activePlayerId, tile);

                    activePlayerId = (activePlayerId + 1) % 4;

#if DEBUG
                    log.AppendLine("tsumogiri");
#endif
                    break;
                }

                case 6: //Chii: 1 byte who, 1 byte fromWho, 1 byte called tileId, 2 bytes tileIds from hand, 1 byte 0 (padding)
                {
                    var who        = block[indexInBlock++];
                    var fromWho    = block[indexInBlock++];
                    var calledTile = Tile.FromTileId(block[indexInBlock++]);
                    var handTile0  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile1  = Tile.FromTileId(block[indexInBlock++]);
                    indexInBlock++;

                    visitor.Chii(who, fromWho, calledTile, handTile0, handTile1);

#if DEBUG
                    Debug.Assert(activePlayerId == who);
                    log.AppendLine("chii");
#endif
                    break;
                }

                case 7: //Pon: 1 byte who, 1 byte fromWho, 1 byte called tileId, 2 bytes tileIds from hand, 1 byte 0 (padding)
                {
                    var who        = block[indexInBlock++];
                    var fromWho    = block[indexInBlock++];
                    var calledTile = Tile.FromTileId(block[indexInBlock++]);
                    var handTile0  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile1  = Tile.FromTileId(block[indexInBlock++]);
                    indexInBlock++;

                    visitor.Pon(who, fromWho, calledTile, handTile0, handTile1);
#if DEBUG
                    Debug.Assert(activePlayerId != who || (fromWho + 1) % 4 == activePlayerId);
                    log.AppendLine("pon");
#endif
                    activePlayerId = who;
                    break;
                }

                case 8: //Daiminkan: 1 byte who, 1 byte fromWho, 1 byte called tileId, 3 bytes tileIds from hand
                {
                    var who        = block[indexInBlock++];
                    var fromWho    = block[indexInBlock++];
                    var calledTile = Tile.FromTileId(block[indexInBlock++]);
                    var handTile0  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile1  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile2  = Tile.FromTileId(block[indexInBlock++]);

                    visitor.Daiminkan(who, fromWho, calledTile, handTile0, handTile1, handTile2);
#if DEBUG
                    Debug.Assert(activePlayerId != who || (fromWho + 1) % 4 == activePlayerId);
                    log.AppendLine("daiminkan");
#endif
                    activePlayerId = who;
                    break;
                }

                case 9: //Shouminkan: 1 byte who, 1 byte fromWho, 1 byte called tileId, 1 byte added tileId, 2 bytes tileIds from hand
                {
                    var who        = block[indexInBlock++];
                    var fromWho    = block[indexInBlock++];
                    var calledTile = Tile.FromTileId(block[indexInBlock++]);
                    var addedTile  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile0  = Tile.FromTileId(block[indexInBlock++]);
                    var handTile1  = Tile.FromTileId(block[indexInBlock++]);

                    visitor.Shouminkan(who, fromWho, calledTile, addedTile, handTile0, handTile1);

#if DEBUG
                    Debug.Assert(activePlayerId == who);
                    log.AppendLine("shouminkan");
#endif
                    break;
                }

                case 10: //Ankan: 1 byte who, 1 byte who (padding), 4 bytes tileIds from hand
                {
                    var who = block[indexInBlock++];
                    indexInBlock++;
                    var tileType = TileType.FromTileId(block[indexInBlock++]);
                    indexInBlock++;
                    indexInBlock++;
                    indexInBlock++;

                    visitor.Ankan(who, tileType);

#if DEBUG
                    Debug.Assert(activePlayerId == who);
                    log.AppendLine("ankan");
#endif
                    break;
                }

                case 11: //Nuki: 1 byte who, 1 byte who (padding), 1 byte tileId, 3 bytes 0 (padding)
                {
                    var who = block[indexInBlock++];
                    indexInBlock++;
                    var tile = Tile.FromTileId(block[indexInBlock++]);
                    indexInBlock++;
                    indexInBlock++;
                    indexInBlock++;
                    visitor.Nuki(who, tile);

#if DEBUG
                    Debug.Assert(activePlayerId == who);
                    log.AppendLine("nuki");
#endif
                    break;
                }

                case 12: //Ron
                case 13: //Tsumo
                {
                    var honba        = block[indexInBlock++];
                    var riichiSticks = block[indexInBlock++];

                    var haiLength = block[indexInBlock++];
                    indexInBlock += haiLength;

                    var meldCount = block[indexInBlock++];
                    var isOpen    = HasOpenMeld(block, indexInBlock, meldCount);
                    indexInBlock += meldCount * 7;

                    var machi = Tile.FromTileId(block[indexInBlock++]); // machi

                    var fu        = BitConverter.ToInt32(block, indexInBlock);
                    var score     = BitConverter.ToInt32(block, indexInBlock + 4);
                    var limitKind = BitConverter.ToInt32(block, indexInBlock + 8); // limit kind: 1 for mangan, ... 5 for yakuman
                    indexInBlock += 12;

                    var yakuLength = block[indexInBlock++];
                    var yakuOffset = indexInBlock;
                    indexInBlock += yakuLength;

                    var yakumanLength = block[indexInBlock++];
                    var yakumanOffset = indexInBlock;
                    indexInBlock += yakumanLength;

                    var yaku = ToYakuEnum(block, yakuOffset, yakuLength, yakumanOffset, yakumanLength, isOpen);

                    var doraHaiLength = block[indexInBlock++];
                    indexInBlock += doraHaiLength;

                    var doraHaiUraLength = block[indexInBlock++];
                    indexInBlock += doraHaiUraLength;

                    var who     = block[indexInBlock++];
                    var fromWho = block[indexInBlock++];
                    var paoWho  = block[indexInBlock++];

                    for (var i = 0; i < playerCount; i++)
                    {
                        scores[i]       = BitConverter.ToInt32(block, indexInBlock);
                        indexInBlock   += 4;
                        scoreChanges[i] = BitConverter.ToInt32(block, indexInBlock);
                        indexInBlock   += 4;
                    }

                    var payment = new PaymentInformation(fu, score, scoreChanges, yaku);

                    if (action == 12)
                    {
                        visitor.Ron(who, fromWho, payment);
#if DEBUG
                        Debug.Assert(activePlayerId != who || (fromWho + 1) % 4 == activePlayerId);
                        log.AppendLine("ron");
#endif
                    }
                    else
                    {
                        visitor.Tsumo(who, payment);
#if DEBUG
                        Debug.Assert(activePlayerId == who);
                        log.AppendLine("tsumo");
#endif
                    }

                    break;
                }

                case 14: //Ryuukyoku: 2 byte ba, 2*4*playerCount byte score, 1 byte ryuukyokuType, 4 byte tenpaiState
                {
                    var honba        = block[indexInBlock++];
                    var riichiSticks = block[indexInBlock++];

                    for (var i = 0; i < playerCount; i++)
                    {
                        scores[i]       = BitConverter.ToInt32(block, indexInBlock);
                        indexInBlock   += 4;
                        scoreChanges[i] = BitConverter.ToInt32(block, indexInBlock);
                        indexInBlock   += 4;
                    }

                    var ryuukyokuType = (RyuukyokuType)block[indexInBlock++];

                    indexInBlock += 4; // tenpai states

                    visitor.Ryuukyoku(ryuukyokuType, honba, riichiSticks, scores, scoreChanges);
#if DEBUG
                    log.AppendLine("ryuukyoku");
#endif
                    break;
                }

                case 15: //Dora: 1 byte tileId
                {
                    var tileId = block[indexInBlock++];
                    visitor.Dora(Tile.FromTileId(tileId));
#if DEBUG
                    log.AppendLine("dora");
#endif
                    break;
                }

                case 16: //CallRiichi: 1 byte who
                {
                    var who = block[indexInBlock++];
                    visitor.DeclareRiichi(who);

#if DEBUG
                    Debug.Assert(activePlayerId == who);
                    log.AppendLine("riichi");
#endif
                    break;
                }

                case 17: //PayRiichi: 1 byte who
                {
                    var who = block[indexInBlock++];
                    visitor.PayRiichi(who);

#if DEBUG
                    Debug.Assert(activePlayerId == (who + 1) % 4);
                    log.AppendLine("pay");
#endif
                    break;
                }

                default:
                {
                    throw new InvalidDataException("Have to handle each value to read away the data");
                }
                }
            }
        }