OSDN Git Service

BattleInfoを大幅に書き直す
authorKazuhiro Fujieda <fujieda@users.osdn.me>
Sun, 2 Jun 2019 12:29:13 +0000 (21:29 +0900)
committerKazuhiro Fujieda <fujieda@users.osdn.me>
Sun, 2 Jun 2019 14:17:32 +0000 (23:17 +0900)
KancolleSniffer.Test/BattleTest.cs
KancolleSniffer/Log/Logger.cs
KancolleSniffer/Model/BattleInfo.cs

index bd2312f..7f81098 100644 (file)
@@ -317,5 +317,17 @@ namespace KancolleSniffer.Test
             SniffLogFile(sniffer, "enemy_combined_001");\r
             PAssert.That(() => sniffer.Battle.Result.Enemy.Guard[0].Slot[0].Spec.Id == 506);\r
         }\r
+\r
+        /// <summary>\r
+        /// 敵連合艦隊の制空値を計算する\r
+        /// </summary>\r
+        [TestMethod]\r
+        public void EnemyFighterPower()\r
+        {\r
+            var sniffer = new Sniffer();\r
+            SniffLogFile(sniffer, "enemy_combined_001");\r
+            var fp = sniffer.Battle.EnemyFighterPower;\r
+            PAssert.That(() => fp.AirCombat == 209 && fp.Interception == 212);\r
+        }\r
     }\r
 }
\ No newline at end of file
index e24303b..85492d6 100644 (file)
@@ -216,7 +216,7 @@ namespace KancolleSniffer.Log
                 else\r
                     dropName += "+" + itemName;\r
             }\r
-            var fp = _shipInfo.Fleets[BattleInfo.DeckId(_battle)].FighterPower;\r
+            var fp = _shipInfo.Fleets[(int)_battle.api_deck_id - 1].FighterPower;\r
             var fPower = fp[0] == fp[1] ? fp[0].ToString() : fp[0] + "~" + fp[1];\r
             _writer("海戦・ドロップ報告書", string.Join(",", FormatDateTime(_nowFunc()),\r
                     result.api_quest_name,\r
@@ -243,7 +243,6 @@ namespace KancolleSniffer.Log
 \r
         private IEnumerable<string> GenerateFriendShipList()\r
         {\r
-            int deckId = BattleInfo.DeckId(_battle);\r
             if (_battle.api_f_nowhps_combined())\r
             {\r
                 var mainShips = _shipInfo.Fleets[0].Ships;\r
@@ -269,7 +268,7 @@ namespace KancolleSniffer.Log
                     return name + "," + hp;\r
                 }).ToList();\r
             }\r
-            var ships = _shipInfo.Fleets[deckId].Ships;\r
+            var ships = _shipInfo.Fleets[(int)_battle.api_deck_id - 1].Ships;\r
             if (ships.Count > 6)\r
             {\r
                 var result = new List<string>();\r
index 789624b..9f30d4d 100644 (file)
@@ -101,15 +101,14 @@ namespace KancolleSniffer.Model
 \r
         public void InspectBattle(string url, string request, dynamic json)\r
         {\r
-            if (json.api_formation())\r
-                Formation = ((dynamic[])json.api_formation).Select(f => f is string ? (int)int.Parse(f) : (int)f)\r
-                    .ToArray();\r
-            AirControlLevel = CheckAirControlLevel(json);\r
+            SetFormation(json);\r
+            SetAirControlLevel(json);\r
             SetSupportType(json);\r
-            ShowResult(false); // 昼戦の結果を夜戦のときに表示する\r
-            SetupResult(request, json, url.Contains("practice"));\r
-            FighterPower = CalcFighterPower();\r
-            EnemyFighterPower = CalcEnemyFighterPower(json);\r
+            ClearDamagedShipWarning();\r
+            ShowResult(); // 昼戦の結果を夜戦のときに表示する\r
+            SetupDamageRecord(request, json, url.Contains("practice"));\r
+            SetFighterPower();\r
+            SetEnemyFighterPower();\r
             BattleState = url.Contains("sp_midnight") ? BattleState.SpNight :\r
                 url.Contains("midnight") ? BattleState.Night : BattleState.Day;\r
             CalcDamage(json);\r
@@ -117,39 +116,70 @@ namespace KancolleSniffer.Model
             SetResult();\r
         }\r
 \r
-        public static int DeckId(dynamic json)\r
+        private void SetFormation(dynamic json)\r
+        {\r
+            if (json.api_formation())\r
+                Formation = (int[])json.api_formation;\r
+        }\r
+\r
+        private void SetAirControlLevel(dynamic json)\r
+        {\r
+            AirControlLevel = -1;\r
+            if (!json.api_kouku())\r
+                return;\r
+            var stage1 = json.api_kouku.api_stage1;\r
+            if (stage1 == null || stage1.api_f_count == 0 && stage1.api_e_count == 0)\r
+                return;\r
+            AirControlLevel = (int)stage1.api_disp_seiku;\r
+        }\r
+\r
+        private void SetSupportType(dynamic json)\r
         {\r
-            if (json.api_dock_id()) // 昼戦はtypoしている\r
-                return (int)json.api_dock_id - 1;\r
-            if (json.api_deck_id is string) // 通常の夜戦と連合艦隊(味方のみ)では文字列\r
-                return int.Parse(json.api_deck_id) - 1;\r
-            return (int)json.api_deck_id - 1;\r
+            SupportType = json.api_support_flag() ? (int)json.api_support_flag :\r
+                json.api_n_support_flag() ? (int)json.api_n_support_flag : 0;\r
         }\r
 \r
-        private void SetupResult(string request, dynamic json, bool practice)\r
+        private void SetupDamageRecord(string request, dynamic json, bool practice)\r
         {\r
             if (_friend != null)\r
                 return;\r
             _shipInfo.SaveBattleStartStatus();\r
-            var fleets = _shipInfo.Fleets;\r
-            _fleet = fleets[DeckId(json)];\r
+            SetupFriendDamageRecord(request, json, practice);\r
+            SetupEnemyDamageRecord(json, practice);\r
+        }\r
+\r
+        private void SetupFriendDamageRecord(string request, dynamic json, bool practice)\r
+        {\r
+            _fleet = _shipInfo.Fleets[(int)json.api_deck_id - 1];\r
             FlagshipRecovery(request, _fleet.ActualShips[0]);\r
             _friend = Record.Setup(_fleet.ActualShips, practice);\r
             _guard = json.api_f_nowhps_combined()\r
-                ? Record.Setup(fleets[1].ActualShips, practice)\r
+                ? Record.Setup(_shipInfo.Fleets[1].ActualShips, practice)\r
                 : new Record[0];\r
+        }\r
+\r
+        private void SetupEnemyDamageRecord(dynamic json, bool practice)\r
+        {\r
             _enemy = Record.Setup((int[])json.api_e_nowhps,\r
-                ((int[])json.api_ship_ke).Select(_shipInfo.GetSpec).ToArray(),\r
-                ((int[][])json.api_eSlot).Select(slot => slot.Select(_itemInfo.GetSpecByItemId).ToArray()).ToArray(),\r
-                practice);\r
+                EnemyShipSpecs(json.api_ship_ke),\r
+                EnemySlots(json.api_eSlot), practice);\r
             _enemyGuard = json.api_ship_ke_combined()\r
                 ? Record.Setup((int[])json.api_e_nowhps_combined,\r
-                    ((int[])json.api_ship_ke_combined).Select(_shipInfo.GetSpec).ToArray(),\r
-                    ((int[][])json.api_eSlot_combined).Select(slot => slot.Select(_itemInfo.GetSpecByItemId).ToArray())\r
-                    .ToArray(), practice)\r
+                    EnemyShipSpecs(json.api_ship_ke_combined),\r
+                    EnemySlots(json.api_eSlot_combined), practice)\r
                 : new Record[0];\r
         }\r
 \r
+        private ShipSpec[] EnemyShipSpecs(dynamic ships)\r
+        {\r
+            return ((int[])ships).Select(_shipInfo.GetSpec).ToArray();\r
+        }\r
+\r
+        private ItemSpec[][] EnemySlots(dynamic slots)\r
+        {\r
+            return ((int[][])slots).Select(slot => slot.Select(_itemInfo.GetSpecByItemId).ToArray()).ToArray();\r
+        }\r
+\r
         private void SetResult()\r
         {\r
             Result = new BattleResult\r
@@ -210,60 +240,34 @@ namespace KancolleSniffer.Model
             _lastCell = false;\r
         }\r
 \r
-        private int CheckAirControlLevel(dynamic json)\r
-        {\r
-            if (!json.api_kouku())\r
-                return -1;\r
-            var stage1 = json.api_kouku.api_stage1;\r
-            if (stage1 == null)\r
-                return -1;\r
-            if (stage1.api_f_count == 0 && stage1.api_e_count == 0)\r
-                return -1;\r
-            return (int)stage1.api_disp_seiku;\r
-        }\r
-\r
-        private void SetSupportType(dynamic json)\r
-        {\r
-            SupportType = json.api_support_flag() ? (int)json.api_support_flag :\r
-                json.api_n_support_flag() ? (int)json.api_n_support_flag : 0;\r
-        }\r
-\r
-        private int[] CalcFighterPower()\r
+        private void SetFighterPower()\r
         {\r
             var fleets = _shipInfo.Fleets;\r
-            if (_guard.Length > 0 && _enemyGuard.Length > 0)\r
-                return fleets[0].FighterPower.Zip(fleets[1].FighterPower, (a, b) => a + b).ToArray();\r
-            return _fleet.FighterPower;\r
+            FighterPower = _guard.Length > 0 && _enemyGuard.Length > 0\r
+                ? fleets[0].FighterPower.Zip(fleets[1].FighterPower, (a, b) => a + b).ToArray()\r
+                : _fleet.FighterPower;\r
         }\r
 \r
-        private EnemyFighterPower CalcEnemyFighterPower(dynamic json)\r
+        private void SetEnemyFighterPower()\r
         {\r
-            var result = new EnemyFighterPower();\r
-            var ships = (int[])json.api_ship_ke;\r
-            if (json.api_ship_ke_combined() && _guard.Length > 0)\r
-                ships = ships.Concat((int[])json.api_ship_ke_combined).ToArray();\r
-            var maxEq = ships.SelectMany(id =>\r
-            {\r
-                var r = _shipInfo.GetSpec(id).MaxEq;\r
-                if (r != null)\r
-                    return r;\r
-                result.HasUnknown = true;\r
-                return new int[5];\r
-            });\r
-            var equips = ((int[][])json.api_eSlot).SelectMany(x => x);\r
-            if (json.api_eSlot_combined() && _guard.Length > 0)\r
-                equips = equips.Concat(((int[][])json.api_eSlot_combined).SelectMany(x => x));\r
-            foreach (var entry in from slot in equips.Zip(maxEq, (id, max) => new {id, max})\r
-                let spec = _itemInfo.GetSpecByItemId(slot.id)\r
-                let perSlot = (int)Floor(spec.AntiAir * Sqrt(slot.max))\r
-                select new {spec, perSlot})\r
+            EnemyFighterPower = new EnemyFighterPower();\r
+            foreach (var record in _guard.Length == 0 ? _enemy : _enemy.Concat(_enemyGuard))\r
             {\r
-                if (entry.spec.CanAirCombat)\r
-                    result.AirCombat += entry.perSlot;\r
-                if (entry.spec.IsAircraft)\r
-                    result.Interception += entry.perSlot;\r
+                var ship = record.SnapShot;\r
+                if (ship.Spec.MaxEq == null)\r
+                {\r
+                    EnemyFighterPower.HasUnknown = true;\r
+                    continue;\r
+                }\r
+                foreach (var entry in ship.Slot.Zip(ship.Spec.MaxEq, (item, maxEq) => new {item.Spec, maxEq}))\r
+                {\r
+                    var perSlot = (int)Floor(entry.Spec.AntiAir * Sqrt(entry.maxEq));\r
+                    if (entry.Spec.CanAirCombat)\r
+                        EnemyFighterPower.AirCombat += perSlot;\r
+                    if (entry.Spec.IsAircraft)\r
+                        EnemyFighterPower.Interception += perSlot;\r
+                }\r
             }\r
-            return result;\r
         }\r
 \r
         private void CalcDamage(dynamic json)\r
@@ -370,43 +374,40 @@ namespace KancolleSniffer.Model
             {\r
                 PhaseName = phaseName,\r
                 AirControlLevel = json.api_stage1.api_disp_seiku() ? (int)json.api_stage1.api_disp_seiku : 0,\r
-                Stage1 = new AirBattleResult.StageResult\r
-                {\r
-                    FriendCount = (int)json.api_stage1.api_f_count,\r
-                    FriendLost = (int)json.api_stage1.api_f_lostcount,\r
-                    EnemyCount = (int)json.api_stage1.api_e_count,\r
-                    EnemyLost = (int)json.api_stage1.api_e_lostcount\r
-                },\r
+                Stage1 = CreateStageResult(json.api_stage1),\r
                 Stage2 = json.api_stage2 == null\r
-                    ? new AirBattleResult.StageResult\r
-                    {\r
-                        FriendCount = 0,\r
-                        FriendLost = 0,\r
-                        EnemyCount = 0,\r
-                        EnemyLost = 0\r
-                    }\r
-                    : new AirBattleResult.StageResult\r
-                    {\r
-                        FriendCount = (int)json.api_stage2.api_f_count,\r
-                        FriendLost = (int)json.api_stage2.api_f_lostcount,\r
-                        EnemyCount = (int)json.api_stage2.api_e_count,\r
-                        EnemyLost = (int)json.api_stage2.api_e_lostcount\r
-                    }\r
+                    ? new AirBattleResult.StageResult()\r
+                    : CreateStageResult(json.api_stage2),\r
+                AirFire = CreateAirFireResult(json)\r
             };\r
-            if (json.api_stage2 != null && json.api_stage2.api_air_fire())\r
-            {\r
-                var airFire = json.api_stage2.api_air_fire;\r
-                var idx = (int)airFire.api_idx;\r
-                result.AirFire = new AirBattleResult.AirFireResult\r
-                {\r
-                    ShipName = idx < _friend.Length ? _friend[idx].Name : _guard[idx - 6].Name,\r
-                    Kind = (int)airFire.api_kind,\r
-                    Items = ((int[])airFire.api_use_items).Select(id => _itemInfo.GetSpecByItemId(id).Name).ToArray()\r
-                };\r
-            }\r
             AirBattleResults.Add(result);\r
         }\r
 \r
+        private AirBattleResult.StageResult CreateStageResult(dynamic stage)\r
+        {\r
+            return new AirBattleResult.StageResult\r
+            {\r
+                FriendCount = (int)stage.api_f_count,\r
+                FriendLost = (int)stage.api_f_lostcount,\r
+                EnemyCount = (int)stage.api_e_count,\r
+                EnemyLost = (int)stage.api_e_lostcount\r
+            };\r
+        }\r
+\r
+        private AirBattleResult.AirFireResult CreateAirFireResult(dynamic json)\r
+        {\r
+            if (json.api_stage2 == null || !json.api_stage2.api_air_fire())\r
+                return null;\r
+            var airFire = json.api_stage2.api_air_fire;\r
+            var idx = (int)airFire.api_idx;\r
+            return new AirBattleResult.AirFireResult\r
+            {\r
+                ShipName = idx < _friend.Length ? _friend[idx].Name : _guard[idx - 6].Name,\r
+                Kind = (int)airFire.api_kind,\r
+                Items = ((int[])airFire.api_use_items).Select(id => _itemInfo.GetSpecByItemId(id).Name).ToArray()\r
+            };\r
+        }\r
+\r
         private void CalcKoukuDamage(dynamic json)\r
         {\r
             if (json.api_stage3() && json.api_stage3 != null)\r
@@ -498,7 +499,7 @@ namespace KancolleSniffer.Model
 \r
             public BothRecord(Record[] friend, Record[] guard, Record[] enemy, Record[] enemyGuard)\r
             {\r
-                _records = new[] { new Record[12], new Record[12] };\r
+                _records = new[] {new Record[12], new Record[12]};\r
                 Array.Copy(friend, _records[1], friend.Length);\r
                 Array.Copy(guard, 0, _records[1], 6, guard.Length);\r
                 Array.Copy(enemy, _records[0], enemy.Length);\r
@@ -537,7 +538,9 @@ namespace KancolleSniffer.Model
             BattleState = BattleState.Result;\r
             if (_friend == null)\r
                 return;\r
-            ShowResult(!_lastCell);\r
+            ShowResult();\r
+            if (!_lastCell)\r
+                SetDamagedShipWarning();\r
             _shipInfo.SaveBattleResult();\r
             _shipInfo.DropShipId = json.api_get_ship() ? (int)json.api_get_ship.api_ship_id : -1;\r
             VerifyResultRank(json);\r
@@ -550,12 +553,12 @@ namespace KancolleSniffer.Model
             BattleState = BattleState.Result;\r
             if (_friend == null)\r
                 return;\r
-            ShowResult(false);\r
+            ShowResult();\r
             VerifyResultRank(json);\r
             CleanupResult();\r
         }\r
 \r
-        private void ShowResult(bool warnDamagedShip = true)\r
+        private void ShowResult()\r
         {\r
             if (_friend == null)\r
                 return;\r
@@ -565,10 +568,16 @@ namespace KancolleSniffer.Model
                 : _fleet.ActualShips;\r
             foreach (var entry in ships.Zip(_friend.Concat(_guard), (ship, now) => new {ship, now}))\r
                 entry.now.UpdateShipStatus(entry.ship);\r
-            if (warnDamagedShip)\r
-                _shipInfo.SetBadlyDamagedShips();\r
-            else\r
-                _shipInfo.ClearBadlyDamagedShips();\r
+        }\r
+\r
+        private void SetDamagedShipWarning()\r
+        {\r
+            _shipInfo.SetBadlyDamagedShips();\r
+        }\r
+\r
+        private void ClearDamagedShipWarning()\r
+        {\r
+            _shipInfo.ClearBadlyDamagedShips();\r
         }\r
 \r
         private void VerifyResultRank(dynamic json)\r
@@ -619,8 +628,9 @@ namespace KancolleSniffer.Model
             public int StartHp { get; private set; }\r
 \r
             public static Record[] Setup(IEnumerable<ShipStatus> ships, bool practice) =>\r
-            (from s in ships\r
-                select new Record {_status = (ShipStatus)s.Clone(), _practice = practice, StartHp = s.NowHp}).ToArray();\r
+                (from s in ships\r
+                    select new Record {_status = (ShipStatus)s.Clone(), _practice = practice, StartHp = s.NowHp})\r
+                .ToArray();\r
 \r
             public static Record[] Setup(int[] nowHps, ShipSpec[] specs, ItemSpec[][] slots, bool practice)\r
             {\r
@@ -685,70 +695,63 @@ namespace KancolleSniffer.Model
 \r
         private BattleResultRank CalcLdResultRank()\r
         {\r
-            var combined = _friend.Concat(_guard).Where(r => !r.Escaped).ToArray();\r
-            var friendGauge = combined.Sum(r => r.StartHp - r.NowHp);\r
-            var friendGaugeRate = Floor((double)friendGauge / combined.Sum(r => r.StartHp) * 100);\r
+            var friend = new ResultRankParams(_friend.Concat(_guard).ToArray());\r
 \r
-            if (friendGauge <= 0)\r
+            if (friend.Gauge <= 0)\r
                 return BattleResultRank.P;\r
-            if (friendGaugeRate < 10)\r
+            if (friend.GaugeRate < 10)\r
                 return BattleResultRank.A;\r
-            if (friendGaugeRate < 20)\r
+            if (friend.GaugeRate < 20)\r
                 return BattleResultRank.B;\r
-            if (friendGaugeRate < 50)\r
+            if (friend.GaugeRate < 50)\r
                 return BattleResultRank.C;\r
-            if (friendGaugeRate < 80)\r
+            if (friend.GaugeRate < 80)\r
                 return BattleResultRank.D;\r
             return BattleResultRank.E;\r
         }\r
 \r
         private BattleResultRank CalcResultRank()\r
         {\r
-            var friend = _friend.Concat(_guard).ToArray();\r
-            var enemy = _enemy.Concat(_enemyGuard).ToArray();\r
-\r
-            var friendCount = friend.Length;\r
-            var friendStartHpTotal = 0;\r
-            var friendNowHpTotal = 0;\r
-            var friendSunk = 0;\r
-            foreach (var ship in friend)\r
+            var friend = new ResultRankParams(_friend.Concat(_guard).ToArray());\r
+            var enemy = new ResultRankParams(_enemy.Concat(_enemyGuard).ToArray());\r
+            if (friend.Sunk == 0 && enemy.Sunk == enemy.Count)\r
             {\r
-                if (ship.Escaped)\r
-                    continue;\r
-                friendStartHpTotal += ship.StartHp;\r
-                friendNowHpTotal += ship.NowHp;\r
-                if (ship.NowHp == 0)\r
-                    friendSunk++;\r
-            }\r
-            var friendGaugeRate = (int)((double)(friendStartHpTotal - friendNowHpTotal) / friendStartHpTotal * 100);\r
-\r
-            var enemyCount = enemy.Length;\r
-            var enemyStartHpTotal = enemy.Sum(r => r.StartHp);\r
-            var enemyNowHpTotal = enemy.Sum(r => r.NowHp);\r
-            var enemySunk = enemy.Count(r => r.NowHp == 0);\r
-            var enemyGaugeRate = (int)((double)(enemyStartHpTotal - enemyNowHpTotal) / enemyStartHpTotal * 100);\r
-\r
-            if (friendSunk == 0 && enemySunk == enemyCount)\r
-            {\r
-                if (friendNowHpTotal >= friendStartHpTotal)\r
+                if (friend.Gauge <= 0)\r
                     return BattleResultRank.P;\r
                 return BattleResultRank.S;\r
             }\r
-            if (friendSunk == 0 && enemySunk >= (int)(enemyCount * 0.7) && enemyCount > 1)\r
+            if (friend.Sunk == 0 && enemy.Sunk >= (int)(enemy.Count * 0.7) && enemy.Count > 1)\r
                 return BattleResultRank.A;\r
-            if (friendSunk < enemySunk && enemy[0].NowHp == 0)\r
+            if (friend.Sunk < enemy.Sunk && _enemy[0].NowHp == 0)\r
                 return BattleResultRank.B;\r
-            if (friendCount == 1 && friend[0].DamageLevel == ShipStatus.Damage.Badly)\r
+            if (friend.Count == 1 && _friend[0].DamageLevel == ShipStatus.Damage.Badly)\r
                 return BattleResultRank.D;\r
-            if (enemyGaugeRate > friendGaugeRate * 2.5)\r
+            if (enemy.GaugeRate > friend.GaugeRate * 2.5)\r
                 return BattleResultRank.B;\r
-            if (enemyGaugeRate > friendGaugeRate * 0.9)\r
+            if (enemy.GaugeRate > friend.GaugeRate * 0.9)\r
                 return BattleResultRank.C;\r
-            if (friendCount > 1 && friendCount - 1 == friendSunk)\r
+            if (friend.Count > 1 && friend.Count - 1 == friend.Sunk)\r
                 return BattleResultRank.E;\r
             return BattleResultRank.D;\r
         }\r
 \r
+        private class ResultRankParams\r
+        {\r
+            public readonly int Count;\r
+            public readonly int Sunk;\r
+            public readonly int Gauge;\r
+            public readonly int GaugeRate;\r
+\r
+            public ResultRankParams(Record[] records)\r
+            {\r
+                var staying = records.Where(r => !r.Escaped).ToArray();\r
+                Count = records.Length;\r
+                Sunk = staying.Count(r => r.NowHp == 0);\r
+                Gauge = staying.Sum(r => r.StartHp - r.NowHp);\r
+                GaugeRate = (int)((double)Gauge / records.Sum(r => r.StartHp) * 100);\r
+            }\r
+        }\r
+\r
         /// <summary>\r
         /// テスト専用\r
         /// </summary>\r