@@ -156,6 +156,7 @@ type TelemetryStats struct {
156156 HiddenFoundByName map [string ]int `json:"hidden_found_by_name,omitempty"`
157157 UpsetStatsByDiff map [int ]* UpsetStat `json:"upset_stats_by_diff,omitempty"`
158158 SwordSaleStats map [string ]* SwordSaleStat `json:"sword_sale_stats,omitempty"`
159+ SwordEnhanceStats map [string ]* SwordEnhanceStat `json:"sword_enhance_stats,omitempty"`
159160 ItemFarmingStats map [string ]* ItemFarmingStat `json:"item_farming_stats,omitempty"`
160161}
161162
@@ -182,6 +183,14 @@ type SwordSaleStat struct {
182183 Count int `json:"count"`
183184}
184185
186+ // SwordEnhanceStat 검 종류별 강화 통계
187+ type SwordEnhanceStat struct {
188+ Attempts int `json:"attempts"`
189+ Success int `json:"success"`
190+ Fail int `json:"fail"`
191+ Destroy int `json:"destroy"`
192+ }
193+
185194// ItemFarmingStat 아이템별 파밍 통계
186195type ItemFarmingStat struct {
187196 TotalCount int `json:"total_count"`
@@ -224,6 +233,7 @@ type StatsStore struct {
224233 hiddenFoundByName map [string ]int
225234 upsetStatsByDiff map [int ]* UpsetStat
226235 swordSaleStats map [string ]* SwordSaleStat
236+ swordEnhanceStats map [string ]* SwordEnhanceStat
227237 itemFarmingStats map [string ]* ItemFarmingStat
228238}
229239
@@ -233,6 +243,7 @@ var stats = &StatsStore{
233243 hiddenFoundByName : make (map [string ]int ),
234244 upsetStatsByDiff : make (map [int ]* UpsetStat ),
235245 swordSaleStats : make (map [string ]* SwordSaleStat ),
246+ swordEnhanceStats : make (map [string ]* SwordEnhanceStat ),
236247 itemFarmingStats : make (map [string ]* ItemFarmingStat ),
237248}
238249
@@ -278,9 +289,27 @@ func getGameData() GameData {
278289 {Level : 15 , MinPrice : 30000000 , MaxPrice : 40000000 , AvgPrice : 35000000 },
279290 },
280291 BattleRewards : []BattleReward {
292+ // 레벨 차이가 클수록 승률↓ 보상↑
281293 {LevelDiff : 1 , WinRate : 35.0 , MinReward : 500 , MaxReward : 1500 , AvgReward : 1000 },
282294 {LevelDiff : 2 , WinRate : 20.0 , MinReward : 1500 , MaxReward : 4000 , AvgReward : 2750 },
283295 {LevelDiff : 3 , WinRate : 10.0 , MinReward : 4000 , MaxReward : 10000 , AvgReward : 7000 },
296+ {LevelDiff : 4 , WinRate : 5.0 , MinReward : 10000 , MaxReward : 25000 , AvgReward : 17500 },
297+ {LevelDiff : 5 , WinRate : 3.0 , MinReward : 25000 , MaxReward : 60000 , AvgReward : 42500 },
298+ {LevelDiff : 6 , WinRate : 2.0 , MinReward : 60000 , MaxReward : 140000 , AvgReward : 100000 },
299+ {LevelDiff : 7 , WinRate : 1.5 , MinReward : 140000 , MaxReward : 300000 , AvgReward : 220000 },
300+ {LevelDiff : 8 , WinRate : 1.0 , MinReward : 300000 , MaxReward : 600000 , AvgReward : 450000 },
301+ {LevelDiff : 9 , WinRate : 0.7 , MinReward : 600000 , MaxReward : 1200000 , AvgReward : 900000 },
302+ {LevelDiff : 10 , WinRate : 0.5 , MinReward : 1200000 , MaxReward : 2500000 , AvgReward : 1850000 },
303+ {LevelDiff : 11 , WinRate : 0.35 , MinReward : 2500000 , MaxReward : 5000000 , AvgReward : 3750000 },
304+ {LevelDiff : 12 , WinRate : 0.25 , MinReward : 5000000 , MaxReward : 10000000 , AvgReward : 7500000 },
305+ {LevelDiff : 13 , WinRate : 0.18 , MinReward : 10000000 , MaxReward : 20000000 , AvgReward : 15000000 },
306+ {LevelDiff : 14 , WinRate : 0.12 , MinReward : 20000000 , MaxReward : 40000000 , AvgReward : 30000000 },
307+ {LevelDiff : 15 , WinRate : 0.08 , MinReward : 40000000 , MaxReward : 80000000 , AvgReward : 60000000 },
308+ {LevelDiff : 16 , WinRate : 0.05 , MinReward : 80000000 , MaxReward : 150000000 , AvgReward : 115000000 },
309+ {LevelDiff : 17 , WinRate : 0.03 , MinReward : 150000000 , MaxReward : 300000000 , AvgReward : 225000000 },
310+ {LevelDiff : 18 , WinRate : 0.02 , MinReward : 300000000 , MaxReward : 500000000 , AvgReward : 400000000 },
311+ {LevelDiff : 19 , WinRate : 0.01 , MinReward : 500000000 , MaxReward : 800000000 , AvgReward : 650000000 },
312+ {LevelDiff : 20 , WinRate : 0.005 , MinReward : 800000000 , MaxReward : 1000000000 , AvgReward : 900000000 },
284313 },
285314 UpdatedAt : time .Now ().Format (time .RFC3339 ),
286315 }
@@ -404,6 +433,17 @@ func handleTelemetry(w http.ResponseWriter, r *http.Request) {
404433 stats .swordSaleStats [key ].Count += stat .Count
405434 }
406435
436+ // 검 강화 통계
437+ for name , stat := range payload .Stats .SwordEnhanceStats {
438+ if stats .swordEnhanceStats [name ] == nil {
439+ stats .swordEnhanceStats [name ] = & SwordEnhanceStat {}
440+ }
441+ stats .swordEnhanceStats [name ].Attempts += stat .Attempts
442+ stats .swordEnhanceStats [name ].Success += stat .Success
443+ stats .swordEnhanceStats [name ].Fail += stat .Fail
444+ stats .swordEnhanceStats [name ].Destroy += stat .Destroy
445+ }
446+
407447 // 아이템 파밍 통계
408448 for name , stat := range payload .Stats .ItemFarmingStats {
409449 if stats .itemFarmingStats [name ] == nil {
@@ -583,11 +623,12 @@ func handleUpsetStats(w http.ResponseWriter, r *http.Request) {
583623 stats .mu .RLock ()
584624 defer stats .mu .RUnlock ()
585625
586- // 이론 승률
626+ // 이론 승률 (레벨 차이 1-20)
587627 theoryRates := map [int ]float64 {
588- 1 : 35.0 ,
589- 2 : 20.0 ,
590- 3 : 10.0 ,
628+ 1 : 35.0 , 2 : 20.0 , 3 : 10.0 , 4 : 5.0 , 5 : 3.0 ,
629+ 6 : 2.0 , 7 : 1.5 , 8 : 1.0 , 9 : 0.7 , 10 : 0.5 ,
630+ 11 : 0.35 , 12 : 0.25 , 13 : 0.18 , 14 : 0.12 , 15 : 0.08 ,
631+ 16 : 0.05 , 17 : 0.03 , 18 : 0.02 , 19 : 0.01 , 20 : 0.005 ,
591632 }
592633
593634 type DiffStat struct {
@@ -599,7 +640,7 @@ func handleUpsetStats(w http.ResponseWriter, r *http.Request) {
599640 }
600641
601642 byDiff := make (map [string ]DiffStat )
602- for diff := 1 ; diff <= 3 ; diff ++ {
643+ for diff := 1 ; diff <= 20 ; diff ++ {
603644 stat := stats .upsetStatsByDiff [diff ]
604645 winRate := 0.0
605646 attempts := 0
@@ -664,6 +705,51 @@ func handleItemStats(w http.ResponseWriter, r *http.Request) {
664705 })
665706}
666707
708+ // 검 종류별 강화 성공률
709+ func handleEnhanceStats (w http.ResponseWriter , r * http.Request ) {
710+ w .Header ().Set ("Content-Type" , "application/json" )
711+ w .Header ().Set ("Access-Control-Allow-Origin" , "*" )
712+
713+ stats .mu .RLock ()
714+ defer stats .mu .RUnlock ()
715+
716+ type EnhanceEntry struct {
717+ Name string `json:"name"`
718+ Attempts int `json:"attempts"`
719+ Success int `json:"success"`
720+ Fail int `json:"fail"`
721+ Destroy int `json:"destroy"`
722+ SuccessRate float64 `json:"success_rate"`
723+ DestroyRate float64 `json:"destroy_rate"`
724+ }
725+
726+ var swords []EnhanceEntry
727+ for name , stat := range stats .swordEnhanceStats {
728+ successRate := 0.0
729+ destroyRate := 0.0
730+ if stat .Attempts > 0 {
731+ successRate = float64 (stat .Success ) / float64 (stat .Attempts ) * 100
732+ destroyRate = float64 (stat .Destroy ) / float64 (stat .Attempts ) * 100
733+ }
734+ swords = append (swords , EnhanceEntry {
735+ Name : name ,
736+ Attempts : stat .Attempts ,
737+ Success : stat .Success ,
738+ Fail : stat .Fail ,
739+ Destroy : stat .Destroy ,
740+ SuccessRate : successRate ,
741+ DestroyRate : destroyRate ,
742+ })
743+ }
744+
745+ json .NewEncoder (w ).Encode (map [string ]interface {}{
746+ "total_attempts" : stats .enhanceAttempts ,
747+ "total_success" : stats .enhanceSuccess ,
748+ "total_destroy" : stats .enhanceDestroy ,
749+ "swords" : swords ,
750+ })
751+ }
752+
667753func generateSignature (sessionID , period string ) string {
668754 h := sha256 .Sum256 ([]byte (sessionID + period + getAppSecret ()))
669755 return hex .EncodeToString (h [:])[:16 ]
@@ -714,6 +800,9 @@ func validateTelemetryPayload(p *TelemetryPayload) error {
714800 if len (p .Stats .SwordSaleStats ) > maxMapEntries {
715801 return fmt .Errorf ("sword_sale_stats too many entries" )
716802 }
803+ if len (p .Stats .SwordEnhanceStats ) > maxMapEntries {
804+ return fmt .Errorf ("sword_enhance_stats too many entries" )
805+ }
717806 if len (p .Stats .ItemFarmingStats ) > maxMapEntries {
718807 return fmt .Errorf ("item_farming_stats too many entries" )
719808 }
@@ -734,6 +823,11 @@ func validateTelemetryPayload(p *TelemetryPayload) error {
734823 return fmt .Errorf ("item name too long: %s" , name )
735824 }
736825 }
826+ for name := range p .Stats .SwordEnhanceStats {
827+ if len (name ) > maxSwordNameLen {
828+ return fmt .Errorf ("enhance sword name too long: %s" , name )
829+ }
830+ }
737831
738832 return nil
739833}
@@ -769,9 +863,9 @@ func validateStatValues(s *TelemetryStats) error {
769863 }
770864 }
771865
772- // 역배 레벨차 검증
866+ // 역배 레벨차 검증 (1-20 허용)
773867 for diff , stat := range s .UpsetStatsByDiff {
774- if diff < 1 || diff > 10 {
868+ if diff < 1 || diff > 20 {
775869 return fmt .Errorf ("invalid upset level diff: %d" , diff )
776870 }
777871 if stat != nil && (stat .Attempts < 0 || stat .Wins < 0 || stat .GoldEarned < 0 ) {
@@ -799,6 +893,7 @@ func main() {
799893 http .HandleFunc ("/api/stats/hidden" , handleHiddenStats )
800894 http .HandleFunc ("/api/stats/upset" , handleUpsetStats )
801895 http .HandleFunc ("/api/stats/items" , handleItemStats )
896+ http .HandleFunc ("/api/stats/enhance" , handleEnhanceStats )
802897
803898 log .Printf ("🚀 Sword API 서버 시작 (포트: %s)" , port )
804899 log .Printf (" /api/game-data - 게임 데이터 조회" )
@@ -808,6 +903,7 @@ func main() {
808903 log .Printf (" /api/stats/hidden - 히든 검 출현 확률 (v2)" )
809904 log .Printf (" /api/stats/upset - 역배 실측 승률 (v2)" )
810905 log .Printf (" /api/stats/items - 아이템 파밍 통계 (v2)" )
906+ log .Printf (" /api/stats/enhance - 검 종류별 강화 성공률 (v2)" )
811907
812908 if err := http .ListenAndServe (":" + port , nil ); err != nil {
813909 log .Fatal (err )
0 commit comments