OSDN Git Service

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