OSDN Git Service

コメントを追加
[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 final class SimpleCalculator: SakutekiCalculator {
17     
18     func calculate(_ ships: [Ship]) -> Double {
19         
20         return Double(ships.reduce(0) { $0 + $1.sakuteki_0 })
21     }
22 }
23
24 final class Formula33: SakutekiCalculator {
25     
26     let condition: Int
27     
28     init(_ condition: Int = 1) {
29         
30         self.condition = condition
31     }
32     
33     private func printShipData(_ ship: Ship) {
34         
35         let shipData = "\(ship.name)\t\(normalSakuteki(ship))"
36         let itemNames = ship
37             .equippedItem
38             .array
39             .flatMap { $0 as? SlotItem }
40             .reduce("") {
41                 
42                 let saku = $1.master_slotItem.saku ?? 0
43                 let ratio = typeRatio($1)
44                 let bounus = levelBounus($1)
45                 let culcSaku = ratio * (Double(truncating: saku) + bounus)
46                 
47                 return $0 + "\n\t\($1.name)\tLv.\($1.level)\t\t\(saku)\t\(ratio)\t\(bounus)\t\(culcSaku)"
48         }
49         
50         print("\(shipData)\(itemNames)\n")
51         
52     }
53     
54     func calculate(_ ships: [Ship]) -> Double {
55         
56         Debug.excute(level: .full) {
57             ships.forEach(printShipData)
58         }
59         
60         let aliveShips = ships.filter(alive)
61         
62         // 艦娘の索敵による索敵値
63         let saku1 = aliveShips
64             .map(normalSakuteki)
65             .map(sqrt)
66             .reduce(0, +)
67         
68         // 装備および分岐点係数による索敵値
69         let saku2 = aliveShips
70             .map(equipsSakuteki)
71             .reduce(0, +)
72         
73         // 艦隊司令部Lv.による影響
74         let saku3 = shireiSakuteki()
75         
76         // 艦隊の艦娘数による影響
77         let saku4 = 2 * (6 - ships.count)
78         
79         return saku1 + saku2 - saku3 + Double(saku4)
80     }
81     
82     private func alive(_ ship: Ship) -> Bool {
83         
84         if let _ = TemporaryDataStore.default.ensuredGuardEscaped(byShipId: ship.id) {
85             
86             return false
87         }
88         
89         return true
90     }
91     
92     private func normalSakuteki(_ ship: Ship) -> Double {
93         
94         let eqSakuteki = ship
95             .equippedItem
96             .array
97             .flatMap { $0 as? SlotItem }
98             .flatMap { $0.master_slotItem.saku as? Double }
99             .reduce(0, +)
100         
101         return Double(ship.sakuteki_0) - eqSakuteki
102     }
103     
104     private func equipsSakuteki(_ ship: Ship) -> Double {
105         
106         let saku = ship
107             .equippedItem
108             .array
109             .flatMap { $0 as? SlotItem }
110             .map(equipSakuteki)
111             .reduce(0, +)
112         
113         return Double(condition) * saku
114     }
115     
116     private func equipSakuteki(_ item: SlotItem) -> Double {
117         
118         guard let saku = item.master_slotItem.saku as? Double else { return 0 }
119         
120         let lvBounus = levelBounus(item)
121         
122         let ratio = typeRatio(item)
123         
124         return Double(condition) * ratio * (saku + lvBounus)
125     }
126     
127     private func typeRatio(_ item: SlotItem) -> Double {
128         
129         guard let eqType = EquipmentType(rawValue: item.master_slotItem.type_2) else { return 1 }
130         
131         switch eqType {
132         case .fighter: return 0.6
133         case .bomber: return 0.6
134         case .attacker: return 0.8
135         case .searcher: return 1
136         case .airplaneSearcher: return 1.2
137         case .airplaneBomber: return 1.1
138         case .smallRadar: return 0.6
139         case .largeRadar: return 0.6
140         case .antiSunmrinerSercher: return 0.6
141         case .searchlight: return 0.6
142         case .headquaters: return 0.6
143         case .pilot: return 0.6
144         case .shipPersonnel: return 0.6
145         case .largeSonar: return 0.6
146         case .largeAirplane: return 0.6
147         case .largeSearchlight: return 0.6
148         case .airplaneFighter: return 0.6
149         case .searcherII: return 1
150         case .jetBomber: return 0.6
151         default: return 0
152         }
153     }
154     
155     private func levelBounus(_ item: SlotItem) -> Double {
156         
157         let level = item.level
158         
159         let ratio = levelRatio(item)
160         
161         return ratio * sqrt(Double(level))
162     }
163     
164     private func levelRatio(_ item: SlotItem) -> Double {
165                 
166         guard let eqType = EquipmentType(rawValue: item.master_slotItem.type_2) else { return 1 }
167         
168         switch eqType {
169         case .smallRadar: return 1.25
170         case .largeRadar: return 1.4
171         case .airplaneSearcher, .searcher: return 1.2
172         default: return 0
173         }
174     }
175     
176     private func shireiSakuteki() -> Double {
177         
178         guard let basic = ServerDataStore.default.basic() else { return 0 }
179         
180         return ceil(0.4 * Double(basic.level))
181     }
182     
183 }