OSDN Git Service

shared instanceを持つようにした
[kcd/KCD.git] / KCD / SakutekiCalculator.swift
1 //
2 //  SakutekiCalculator.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/06/18.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Foundation
10
11 protocol SakutekiCalculator {
12     
13     func calculate(_ ships: [Ship]) -> Double
14 }
15
16 extension SakutekiCalculator {
17     
18     fileprivate func alivedShips(ships: [Ship]) -> [Ship] {
19         
20         return ships.filter {
21             
22             TemporaryDataStore.default.ensuredGuardEscaped(byShipId: $0.id) == nil
23         }
24     }
25 }
26
27 final class SimpleCalculator: SakutekiCalculator {
28     
29     
30     func calculate(_ ships: [Ship]) -> Double {
31         
32         return Double(alivedShips(ships: ships).reduce(0) { $0 + $1.sakuteki_0 })
33     }
34 }
35
36 final class Formula33: SakutekiCalculator {
37     
38     let condition: Int
39     
40     init(_ condition: Int = 1) {
41         
42         self.condition = condition
43     }
44     
45     private func printShipData(_ ship: Ship) {
46         
47         let shipData = "\(ship.name)\t\(normalSakuteki(ship))"
48         let itemNames = ship
49             .equippedItem
50             .array
51             .compactMap { $0 as? SlotItem }
52             .reduce("") {
53                 
54                 let saku = $1.master_slotItem.saku ?? 0
55                 let ratio = typeRatio($1)
56                 let bounus = levelBounus($1)
57                 let culcSaku = ratio * (Double(truncating: saku) + bounus)
58                 
59                 return $0 + "\n\t\($1.name)\tLv.\($1.level)\t\t\(saku)\t\(ratio)\t\(bounus)\t\(culcSaku)"
60         }
61         
62         print("\(shipData)\(itemNames)\n")
63         
64     }
65     
66     func calculate(_ ships: [Ship]) -> Double {
67         
68         Debug.excute(level: .full) {
69             
70             ships.forEach(printShipData)
71         }
72         
73         let aliveShips = alivedShips(ships: ships)
74         
75         // 艦娘の索敵による索敵値
76         let saku1 = aliveShips
77             .map(normalSakuteki)
78             .map(sqrt)
79             .reduce(0, +)
80         
81         // 装備および分岐点係数による索敵値
82         let saku2 = aliveShips
83             .map(equipsSakuteki)
84             .reduce(0, +)
85         
86         // 艦隊司令部Lv.による影響
87         let saku3 = shireiSakuteki()
88         
89         // 艦隊の艦娘数による影響
90         let saku4 = 2 * (6 - aliveShips.count)
91         
92         return saku1 + Double(condition) * saku2 - saku3 + Double(saku4)
93     }
94     
95     private func normalSakuteki(_ ship: Ship) -> Double {
96         
97         let eqSakuteki = ship
98             .equippedItem
99             .array
100             .compactMap { $0 as? SlotItem }
101             .compactMap { $0.master_slotItem.saku as? Double }
102             .reduce(0, +)
103         
104         return Double(ship.sakuteki_0) - eqSakuteki
105     }
106     
107     private func equipsSakuteki(_ ship: Ship) -> Double {
108         
109         return ship
110             .equippedItem
111             .array
112             .compactMap { $0 as? SlotItem }
113             .map(equipSakuteki)
114             .reduce(0, +)
115     }
116     
117     private func equipSakuteki(_ item: SlotItem) -> Double {
118         
119         guard let saku = item.master_slotItem.saku as? Double else {
120             
121             return 0
122         }
123         
124         return typeRatio(item) * (saku + levelBounus(item))
125     }
126     
127     private func typeRatio(_ item: SlotItem) -> Double {
128         
129         guard let eqType = EquipmentType(rawValue: item.master_slotItem.type_2) else {
130             
131             return 1
132         }
133         
134         switch eqType {
135             
136         case .fighter: return 0.6
137             
138         case .bomber: return 0.6
139             
140         case .attacker: return 0.8
141             
142         case .searcher: return 1
143             
144         case .airplaneSearcher: return 1.2
145             
146         case .airplaneBomber: return 1.1
147             
148         case .smallRadar: return 0.6
149             
150         case .largeRadar: return 0.6
151             
152         case .antiSunmrinerSercher: return 0.6
153             
154         case .searchlight: return 0.6
155             
156         case .headquaters: return 0.6
157             
158         case .pilot: return 0.6
159             
160         case .shipPersonnel: return 0.6
161             
162         case .largeSonar: return 0.6
163             
164         case .largeAirplane: return 0.6
165             
166         case .largeSearchlight: return 0.6
167             
168         case .airplaneFighter: return 0.6
169             
170         case .searcherII: return 1
171             
172         case .jetBomber: return 0.6
173             
174         default: return 0
175             
176         }
177     }
178     
179     private func levelBounus(_ item: SlotItem) -> Double {
180         
181         return levelRatio(item) * sqrt(Double(item.level))
182     }
183     
184     private func levelRatio(_ item: SlotItem) -> Double {
185                 
186         guard let eqType = EquipmentType(rawValue: item.master_slotItem.type_2) else {
187             
188             return 1
189         }
190         
191         switch eqType {
192             
193         case .smallRadar: return 1.25
194             
195         case .largeRadar: return 1.4
196             
197         case .airplaneSearcher, .searcher: return 1.2
198             
199         default: return 0
200             
201         }
202     }
203     
204     private func shireiSakuteki() -> Double {
205         
206         guard let basic = ServerDataStore.default.basic() else {
207             
208             return 0
209         }
210         
211         return ceil(0.4 * Double(basic.level))
212     }
213     
214 }