OSDN Git Service

先制対潜の判定を最新の検証結果に合わせる
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Model / Fleet.cs
1 // Copyright (C) 2018 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 using static System.Math;\r
19 \r
20 namespace KancolleSniffer.Model\r
21 {\r
22     public struct ChargeStatus\r
23     {\r
24         public int Fuel { get; set; }\r
25         public int Bull { get; set; }\r
26 \r
27         public ChargeStatus(ShipStatus status) : this()\r
28         {\r
29             Fuel = CalcChargeState(status.Fuel, status.Spec.FuelMax);\r
30             Bull = CalcChargeState(status.Bull, status.Spec.BullMax);\r
31         }\r
32 \r
33         public ChargeStatus(int fuel, int bull) : this()\r
34         {\r
35             Fuel = fuel;\r
36             Bull = bull;\r
37         }\r
38 \r
39         private int CalcChargeState(int now, int full)\r
40         {\r
41             if (full == 0 || now == full)\r
42                 return 0;\r
43             var ratio = (double)now / full;\r
44             if (ratio >= 7.0 / 9)\r
45                 return 1;\r
46             if (ratio >= 3.0 / 9)\r
47                 return 2;\r
48             if (ratio > 0)\r
49                 return 3;\r
50             return 4;\r
51         }\r
52     }\r
53 \r
54     public enum FleetState\r
55     {\r
56         Port,\r
57         Mission,\r
58         Sortie,\r
59         Practice\r
60     }\r
61 \r
62     public enum CombinedType\r
63     {\r
64         None,\r
65         Carrier, // 機動\r
66         Surface, // 水上\r
67         Transport // 輸送\r
68     }\r
69 \r
70     public class Fleet\r
71     {\r
72         private readonly ShipInventory _shipInventory;\r
73         private readonly Func<int> _getHqLevel;\r
74         private int[] _deck = Enumerable.Repeat(-1, ShipInfo.MemberCount).ToArray();\r
75         public int Number { get; }\r
76         public FleetState State { get; set; }\r
77         public CombinedType CombinedType { get; set; }\r
78         public IReadOnlyList<ShipStatus> Ships { get; private set; }\r
79         public IReadOnlyList<ShipStatus> ActualShips { get; private set; }\r
80 \r
81         public Fleet(ShipInventory shipInventory, int number, Func<int> getHqLevel)\r
82         {\r
83             _shipInventory = shipInventory;\r
84             Number = number;\r
85             _getHqLevel = getHqLevel;\r
86             Ships = _deck.Select(id => new ShipStatus()).ToArray();\r
87             ActualShips = new ShipStatus[0];\r
88         }\r
89 \r
90         public IReadOnlyList<int> Deck\r
91         {\r
92             get => _deck.ToArray();\r
93             set\r
94             {\r
95                 _deck = value.ToArray();\r
96                 SetDeck();\r
97             }\r
98         }\r
99 \r
100         public void SetDeck()\r
101         {\r
102             foreach (var ship in Ships)\r
103             {\r
104                 if (ship.Fleet != this) // 入れ替え操作で他の艦隊に移動しているときには触らない。\r
105                     continue;\r
106                 ship.Fleet = null;\r
107                 ship.DeckIndex = -1;\r
108             }\r
109             Ships = _deck.Select((id, num) =>\r
110             {\r
111                 var ship = _shipInventory[id];\r
112                 if (ship.Empty)\r
113                     return ship;\r
114                 ship.DeckIndex = num;\r
115                 ship.Fleet = this;\r
116                 return ship;\r
117             }).ToArray();\r
118             ActualShips = Ships.Where(ship => !ship.Empty).ToArray();\r
119         }\r
120 \r
121         public int SetShip(int index, int shipId)\r
122         {\r
123             var prev = _deck[index];\r
124             _deck[index] = shipId;\r
125             SetDeck();\r
126             return prev;\r
127         }\r
128 \r
129         public void WithdrawAccompanyingShips()\r
130         {\r
131             for (var i = 1; i < _deck.Length; i++)\r
132                 _deck[i] = -1;\r
133             SetDeck();\r
134         }\r
135 \r
136         public void WithdrawShip(int index)\r
137         {\r
138             var dst = index;\r
139             for (var src = index + 1; src < _deck.Length; src++)\r
140             {\r
141                 if (_deck[src] != -1)\r
142                     _deck[dst++] = _deck[src];\r
143             }\r
144             for (; dst < _deck.Length; dst++)\r
145                 _deck[dst] = -1;\r
146             SetDeck();\r
147         }\r
148 \r
149         public ChargeStatus ChargeStatus\r
150         {\r
151             get\r
152             {\r
153                 var fs = new ChargeStatus(Ships[0]);\r
154                 var others = (from ship in Ships.Skip(1) select new ChargeStatus(ship)).Aggregate(\r
155                     (result, next) => new ChargeStatus(Max(result.Fuel, next.Fuel), Max(result.Bull, next.Bull)));\r
156                 return new ChargeStatus(fs.Fuel != 0 ? fs.Fuel : others.Fuel + 5,\r
157                     fs.Bull != 0 ? fs.Bull : others.Bull + 5);\r
158             }\r
159         }\r
160 \r
161         public int[] FighterPower\r
162             => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>\r
163                     ship.Slot.Zip(ship.OnSlot, (slot, onSlot) => slot.CalcFighterPower(onSlot)))\r
164                 .Aggregate(new[] {0, 0}, (prev, cur) => new[] {prev[0] + cur[0], prev[1] + cur[1]});\r
165 \r
166         public double ContactTriggerRate\r
167             => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>\r
168                 ship.Slot.Zip(ship.OnSlot, (slot, onSlot) =>\r
169                     slot.Spec.ContactTriggerRate * slot.Spec.LoS * Sqrt(onSlot))).Sum();\r
170 \r
171         public double GetLineOfSights(int factor)\r
172         {\r
173             var result = 0.0;\r
174             var emptyBonus = 6;\r
175             foreach (var s in ActualShips.Where(s => !s.Escaped))\r
176             {\r
177                 emptyBonus--;\r
178                 var itemLoS = 0;\r
179                 foreach (var item in s.Slot)\r
180                 {\r
181                     var spec = item.Spec;\r
182                     itemLoS += spec.LoS;\r
183                     result += (spec.LoS + item.LoSLevelBonus) * spec.LoSScaleFactor * factor;\r
184                 }\r
185                 result += Sqrt(s.LoS - itemLoS);\r
186             }\r
187             return result > 0 ? result - Ceiling(_getHqLevel() * 0.4) + emptyBonus * 2 : 0.0;\r
188         }\r
189 \r
190         public double DaihatsuBonus\r
191         {\r
192             get\r
193             {\r
194                 // ReSharper disable IdentifierTypo\r
195                 var tokudaiBonus = new[,]\r
196                 {\r
197                     {0.00, 0.00, 0.00, 0.00, 0.00},\r
198                     {0.02, 0.02, 0.02, 0.02, 0.02},\r
199                     {0.04, 0.04, 0.04, 0.04, 0.04},\r
200                     {0.05, 0.05, 0.052, 0.054, 0.054},\r
201                     {0.054, 0.056, 0.058, 0.059, 0.06}\r
202                 };\r
203                 var daihatsu = 0;\r
204                 var tokudai = 0;\r
205                 var bonus = 0.0;\r
206                 var level = 0;\r
207                 var sum = 0;\r
208                 // ReSharper restore IdentifierTypo\r
209                 foreach (var ship in Ships)\r
210                 {\r
211                     if (ship.Name == "鬼怒改二")\r
212                         bonus += 0.05;\r
213                     foreach (var item in ship.Slot)\r
214                     {\r
215                         switch (item.Spec.Name)\r
216                         {\r
217                             case "大発動艇":\r
218                                 level += item.Level;\r
219                                 sum++;\r
220                                 daihatsu++;\r
221                                 bonus += 0.05;\r
222                                 break;\r
223                             case "特大発動艇":\r
224                                 level += item.Level;\r
225                                 sum++;\r
226                                 tokudai++;\r
227                                 bonus += 0.05;\r
228                                 break;\r
229                             case "大発動艇(八九式中戦車&陸戦隊)":\r
230                                 level += item.Level;\r
231                                 sum++;\r
232                                 bonus += 0.02;\r
233                                 break;\r
234                             case "特二式内火艇":\r
235                                 level += item.Level;\r
236                                 sum++;\r
237                                 bonus += 0.01;\r
238                                 break;\r
239                         }\r
240                     }\r
241                 }\r
242                 var levelAverage = sum == 0 ? 0.0 : (double)level / sum;\r
243                 bonus = Min(bonus, 0.2);\r
244                 return bonus + 0.01 * bonus * levelAverage + tokudaiBonus[Min(tokudai, 4), Min(daihatsu, 4)];\r
245             }\r
246         }\r
247 \r
248         public double TransportPoint => Ships.Where(ship => !ship.Escaped).Sum(ship => ship.TransportPoint);\r
249 \r
250         public int CombinedFirepowerBonus\r
251         {\r
252             get\r
253             {\r
254                 switch (CombinedType)\r
255                 {\r
256                     case CombinedType.None:\r
257                         return 0;\r
258                     case CombinedType.Carrier:\r
259                         return Number == 0 ? 2 : 10;\r
260                     case CombinedType.Surface:\r
261                         return Number == 0 ? 10 : -5;\r
262                     case CombinedType.Transport:\r
263                         return Number == 0 ? -5 : 10;\r
264                     default:\r
265                         return 0;\r
266                 }\r
267             }\r
268         }\r
269 \r
270         public int CombinedTorpedoPenalty => CombinedType != 0 && Number == 1 ? -5 : 0;\r
271     }\r
272 }