Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.23% covered (success)
94.23%
457 / 485
73.33% covered (warning)
73.33%
11 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
AchievementChecker
94.23% covered (success)
94.23%
457 / 485
73.33% covered (warning)
73.33%
11 / 15
242.36
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 gained
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 fillExistingStatuses
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 unlocked
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkProblemNumberAchievements
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
12
 checkDanSolveAchievements
100.00% covered (success)
100.00%
79 / 79
100.00% covered (success)
100.00%
1 / 1
36
 checkNoErrorAchievements
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
 checkTimeModeAchievements
100.00% covered (success)
100.00%
59 / 59
100.00% covered (success)
100.00%
1 / 1
39
 checkRatingAchievements
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
12
 checkLevelAchievements
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
11
 checkSetCompletedAchievements
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
8
 setAchievementSpecial
79.17% covered (warning)
79.17%
95 / 120
0.00% covered (danger)
0.00%
0 / 1
84.36
 checkSetAchievements
98.89% covered (success)
98.89%
89 / 90
0.00% covered (danger)
0.00%
0 / 1
39
 checkAll
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 finalize
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
4.01
1<?php
2
3class AchievementChecker
4{
5    public function __construct()
6    {
7        $this->fillExistingStatuses();
8    }
9
10    public function gained($achievementID): void
11    {
12        if ($this->existingStatuses[$achievementID])
13            return;
14        $achievementStatus = [];
15        $achievementStatus['achievement_id'] = $achievementID;
16        $achievementStatus['user_id'] = Auth::getUserID();
17        ClassRegistry::init('AchievementStatus')->create();
18        ClassRegistry::init('AchievementStatus')->save($achievementStatus);
19
20        $achievement = ClassRegistry::init('Achievement')->findById($achievementID);
21        $this->updated [] = $achievement['Achievement'];
22    }
23
24    private function fillExistingStatuses(): void
25    {
26        $achievementStatuses = ClassRegistry::init('AchievementStatus')->find('all', ['conditions' => ['user_id' => Auth::getUserID()]]) ?: [];
27        foreach ($achievementStatuses as $achievementStatus)
28            $this->existingStatuses[$achievementStatus['AchievementStatus']['achievement_id']] = true;
29    }
30
31    public function unlocked($achievementID): bool
32    {
33        return isset($this->existingStatuses[$achievementID]);
34    }
35
36    public function checkProblemNumberAchievements(): AchievementChecker
37    {
38        $solvedCount = Auth::getUser()['solved'];
39        if ($solvedCount >= 1000)
40            $this->gained(Achievement::PROBLEMS_1000);
41        if ($solvedCount >= 2000)
42            $this->gained(Achievement::PROBLEMS_2000);
43        if ($solvedCount >= 3000)
44            $this->gained(Achievement::PROBLEMS_3000);
45        if ($solvedCount >= 4000)
46            $this->gained(Achievement::PROBLEMS_4000);
47        if ($solvedCount >= 5000)
48            $this->gained(Achievement::PROBLEMS_5000);
49        if ($solvedCount >= 6000)
50            $this->gained(Achievement::PROBLEMS_6000);
51        if ($solvedCount >= 7000)
52            $this->gained(Achievement::PROBLEMS_7000);
53        if ($solvedCount >= 8000)
54            $this->gained(Achievement::PROBLEMS_8000);
55        if ($solvedCount >= 9000)
56            $this->gained(Achievement::PROBLEMS_9000);
57        if ($solvedCount >= 10000)
58            $this->gained(Achievement::PROBLEMS_10000);
59
60        if (ClassRegistry::init('AchievementCondition')->find('first', ['conditions' => ['user_id' => Auth::getUserID(), 'category' => 'uotd']]))
61            $this->gained(Achievement::USER_OF_THE_DAY);
62        return $this;
63    }
64
65    public function checkDanSolveAchievements(): void
66    {
67        $achievementConditions = ClassRegistry::init('AchievementCondition')->find('all', [
68            'order' => 'category ASC',
69            'conditions' => [
70                'user_id' => Auth::getUserID(),
71                'OR' => [
72                    ['category' => 'danSolve1d'],
73                    ['category' => 'danSolve2d'],
74                    ['category' => 'danSolve3d'],
75                    ['category' => 'danSolve4d'],
76                    ['category' => 'danSolve5d'],
77                    ['category' => 'emerald'],
78                    ['category' => 'sapphire'],
79                    ['category' => 'ruby'],
80                    ['category' => 'sprint'],
81                    ['category' => 'golden'],
82                    ['category' => 'potion'],
83                ],
84            ],
85        ]) ?: [];
86        $ac1 = [];
87        foreach ($achievementConditions as $achievementCondition)
88            if ($achievementCondition['AchievementCondition']['category'] == 'danSolve1d')
89                $ac1['1d'] = $achievementCondition['AchievementCondition']['value'];
90            elseif ($achievementCondition['AchievementCondition']['category'] == 'danSolve2d')
91                $ac1['2d'] = $achievementCondition['AchievementCondition']['value'];
92            elseif ($achievementCondition['AchievementCondition']['category'] == 'danSolve3d')
93                $ac1['3d'] = $achievementCondition['AchievementCondition']['value'];
94            elseif ($achievementCondition['AchievementCondition']['category'] == 'danSolve4d')
95                $ac1['4d'] = $achievementCondition['AchievementCondition']['value'];
96            elseif ($achievementCondition['AchievementCondition']['category'] == 'danSolve5d')
97                $ac1['5d'] = $achievementCondition['AchievementCondition']['value'];
98            elseif ($achievementCondition['AchievementCondition']['category'] == 'emerald')
99                $ac1['emerald'] = $achievementCondition['AchievementCondition']['value'];
100            elseif ($achievementCondition['AchievementCondition']['category'] == 'sapphire')
101                $ac1['sapphire'] = $achievementCondition['AchievementCondition']['value'];
102            elseif ($achievementCondition['AchievementCondition']['category'] == 'ruby')
103                $ac1['ruby'] = $achievementCondition['AchievementCondition']['value'];
104            elseif ($achievementCondition['AchievementCondition']['category'] == 'sprint')
105                $ac1['sprint'] = $achievementCondition['AchievementCondition']['value'];
106            elseif ($achievementCondition['AchievementCondition']['category'] == 'golden')
107                $ac1['golden'] = $achievementCondition['AchievementCondition']['value'];
108            elseif ($achievementCondition['AchievementCondition']['category'] == 'potion')
109                $ac1['potion'] = $achievementCondition['AchievementCondition']['value'];
110
111        if ($ac1['1d'] > 0)
112            $this->gained(Achievement::SOLVE_1D);
113        if ($ac1['2d'] > 0)
114            $this->gained(Achievement::SOLVE_2D);
115        if ($ac1['3d'] > 0)
116            $this->gained(Achievement::SOLVE_3D);
117        if ($ac1['4d'] > 0)
118            $this->gained(Achievement::SOLVE_4D);
119        if ($ac1['5d'] > 0)
120            $this->gained(Achievement::SOLVE_5D);
121        if ($ac1['1d'] >= 10)
122            $this->gained(Achievement::SOLVE_10_1D);
123        if ($ac1['2d'] >= 10)
124            $this->gained(Achievement::SOLVE_10_2D);
125        if ($ac1['3d'] >= 10)
126            $this->gained(Achievement::SOLVE_10_3D);
127        if ($ac1['4d'] >= 10)
128            $this->gained(Achievement::SOLVE_10_4D);
129        if ($ac1['5d'] >= 10)
130            $this->gained(Achievement::SOLVE_10_5D);
131        if (isset($ac1['emerald']) && $ac1['emerald'] == 1)
132            $this->gained(Achievement::EMERALD);
133        if (isset($ac1['sapphire']) && $ac1['sapphire'] == 1)
134            $this->gained(Achievement::SAPPHIRE);
135        if (isset($ac1['ruby']) && $ac1['ruby'] == 1)
136            $this->gained(Achievement::RUBY);
137
138        if ($this->unlocked(Achievement::EMERALD)
139            && $this->unlocked(Achievement::SAPPHIRE)
140            && $this->unlocked(Achievement::RUBY))
141                $this->gained(Achievement::DIAMOND);
142        if ($ac1['sprint'] >= 30)
143            $this->gained(Achievement::SPRINT);
144        if ($ac1['golden'] >= 10)
145            $this->gained(Achievement::GOLD_DIGGER);
146        if ($ac1['potion'] >= 1)
147            $this->gained(Achievement::BAD_POTION);
148    }
149
150    public function checkNoErrorAchievements(): void
151    {
152        $ac = ClassRegistry::init('AchievementCondition')->find('first', [
153            'order' => 'value DESC',
154            'conditions' => ['user_id' => Auth::getUserID(), 'category' => 'err']]);
155        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_I_STREAK_COUNT)
156            $this->gained(Achievement::NO_ERROR_STREAK_I);
157        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_II_STREAK_COUNT)
158            $this->gained(Achievement::NO_ERROR_STREAK_II);
159        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_III_STREAK_COUNT)
160            $this->gained(Achievement::NO_ERROR_STREAK_III);
161        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_IV_STREAK_COUNT)
162            $this->gained(Achievement::NO_ERROR_STREAK_IV);
163        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_V_STREAK_COUNT)
164            $this->gained(Achievement::NO_ERROR_STREAK_V);
165        if ($ac['AchievementCondition']['value'] >= Achievement::NO_ERROR_STREAK_VI_STREAK_COUNT)
166            $this->gained(Achievement::NO_ERROR_STREAK_VI);
167    }
168
169    public function checkTimeModeAchievements(): AchievementChecker
170    {
171        // select always just one session per each rank and category combination, and the best one
172        $timeModeSessions = Util::query("
173SELECT *
174FROM (
175    SELECT
176        time_mode_session.*,
177        ROW_NUMBER() OVER (
178            PARTITION BY time_mode_rank_id, time_mode_category_id
179            ORDER BY points DESC, created DESC
180        ) AS rn
181    FROM time_mode_session
182    WHERE time_mode_session.user_id = ?
183      AND time_mode_session.time_mode_session_status_id = ?
184) ranked
185WHERE rn = 1;", [Auth::getUserID(), TimeModeUtil::$SESSION_STATUS_SOLVED]);
186        foreach ($timeModeSessions as $timeModeSession)
187        {
188            // Compare IDs directly - no need for recursive loading
189            $statusId = isset($timeModeSession['time_mode_session_status_id']) ? $timeModeSession['time_mode_session_status_id'] : 0;
190            $rankId = isset($timeModeSession['time_mode_rank_id']) ? $timeModeSession['time_mode_rank_id'] : 0;
191            $categoryId = isset($timeModeSession['time_mode_category_id']) ? $timeModeSession['time_mode_category_id'] : 0;
192
193            if ($statusId == TimeModeSessionStatus::SOLVED)
194                if ($rankId == TimeModeRank::RANK_5K)
195                {
196                    if ($categoryId == TimeModeCategory::SLOW)
197                        $this->gained(Achievement::TIME_MODE_APPRENTICE_SLOW);
198                    elseif ($categoryId == TimeModeCategory::FAST)
199                        $this->gained(Achievement::TIME_MODE_APPRENTICE_FAST);
200                    elseif ($categoryId == TimeModeCategory::BLITZ)
201                        $this->gained(Achievement::TIME_MODE_APPRENTICE_BLITZ);
202                }
203                elseif ($rankId == TimeModeRank::RANK_4K)
204                {
205                    if ($categoryId == TimeModeCategory::SLOW)
206                        $this->gained(Achievement::TIME_MODE_SCHOLAR_SLOW);
207                    elseif ($categoryId == TimeModeCategory::FAST)
208                        $this->gained(Achievement::TIME_MODE_SCHOLAR_FAST);
209                    elseif ($categoryId == TimeModeCategory::BLITZ)
210                        $this->gained(Achievement::TIME_MODE_SCHOLAR_BLITZ);
211
212                }
213                elseif ($rankId == TimeModeRank::RANK_3K)
214                {
215                    if ($categoryId == TimeModeCategory::SLOW)
216                        $this->gained(Achievement::TIME_MODE_LABOURER_SLOW);
217                    elseif ($categoryId == TimeModeCategory::FAST)
218                        $this->gained(Achievement::TIME_MODE_LABOURER_FAST);
219                    elseif ($categoryId == TimeModeCategory::BLITZ)
220                        $this->gained(Achievement::TIME_MODE_LABOURER_BLITZ);
221                }
222                elseif ($rankId == TimeModeRank::RANK_2K)
223                {
224                    if ($categoryId == TimeModeCategory::SLOW)
225                        $this->gained(Achievement::TIME_MODE_ADEPT_SLOW);
226                    elseif ($categoryId == TimeModeCategory::FAST)
227                        $this->gained(Achievement::TIME_MODE_ADEPT_FAST);
228                    elseif ($categoryId == TimeModeCategory::BLITZ)
229                        $this->gained(Achievement::TIME_MODE_ADEPT_BLITZ);
230                }
231                elseif ($rankId == TimeModeRank::RANK_1K)
232                {
233                    if ($categoryId == TimeModeCategory::SLOW)
234                        $this->gained(Achievement::TIME_MODE_EXPERT_SLOW);
235                    elseif ($categoryId == TimeModeCategory::FAST)
236                        $this->gained(Achievement::TIME_MODE_EXPERT_FAST);
237                    elseif ($categoryId == TimeModeCategory::BLITZ)
238                        $this->gained(Achievement::TIME_MODE_EXPERT_BLITZ);
239                }
240                elseif ($rankId == TimeModeRank::RANK_1D)
241                    if ($categoryId == TimeModeCategory::SLOW)
242                        $this->gained(Achievement::TIME_MODE_MASTER_SLOW);
243                    elseif ($categoryId == TimeModeCategory::FAST)
244                        $this->gained(Achievement::TIME_MODE_MASTER_FAST);
245                    elseif ($categoryId == TimeModeCategory::BLITZ)
246                        $this->gained(Achievement::TIME_MODE_MASTER_BLITZ);
247
248            // Precision achievements based on points and rank
249            $points = isset($timeModeSession['points']) ? $timeModeSession['points'] : 0;
250            if ($points >= 850 && $rankId >= TimeModeRank::RANK_4K)
251                $this->gained(Achievement::TIME_MODE_PRECISION_IV);
252            if ($points >= 875 && $rankId >= TimeModeRank::RANK_6K)
253                $this->gained(Achievement::TIME_MODE_PRECISION_III);
254            if ($points >= 900 && $rankId >= TimeModeRank::RANK_8K)
255                $this->gained(Achievement::TIME_MODE_PRECISION_II);
256            if ($points >= 950 && $rankId >= TimeModeRank::RANK_10K)
257                $this->gained(Achievement::TIME_MODE_PRECISION_I);
258        }
259        return $this;
260    }
261
262    public function checkRatingAchievements(): AchievementChecker
263    {
264        $rating = Auth::getUser()['rating'];
265        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('6k'))
266            $this->gained(Achievement::RATING_6_KYU);
267        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('5k'))
268            $this->gained(Achievement::RATING_5_KYU);
269        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('4k'))
270            $this->gained(Achievement::RATING_4_KYU);
271        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('3k'))
272            $this->gained(Achievement::RATING_3_KYU);
273        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('2k'))
274            $this->gained(Achievement::RATING_2_KYU);
275        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('1k'))
276            $this->gained(Achievement::RATING_1_KYU);
277        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('1d'))
278            $this->gained(Achievement::RATING_1_DAN);
279        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('2d'))
280            $this->gained(Achievement::RATING_2_DAN);
281        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('3d'))
282            $this->gained(Achievement::RATING_3_DAN);
283        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('4d'))
284            $this->gained(Achievement::RATING_4_DAN);
285        if ($rating >= Rating::getRankMinimalRatingFromReadableRank('5d'))
286            $this->gained(Achievement::RATING_5_DAN);
287        return $this;
288    }
289
290    public function checkLevelAchievements(): AchievementChecker
291    {
292        $userLevel = Auth::getUser()['level'];
293        if ($userLevel >= 10)
294            $this->gained(Achievement::LEVEL_UP);
295        if ($userLevel >= 20)
296            $this->gained(Achievement::FIRST_HERO_POWER);
297        if ($userLevel >= 30)
298            $this->gained(Achievement::UPGRADED_INTUITION);
299        if ($userLevel >= 40)
300            $this->gained(Achievement::MORE_POWER);
301        if ($userLevel >= 50)
302            $this->gained(Achievement::HALF_WAY_TO_TOP);
303        if ($userLevel >= 60)
304            $this->gained(Achievement::CONGRATS_MORE_PROBLEMS);
305        if ($userLevel >= 70)
306            $this->gained(Achievement::NICE_LEVEL);
307        if ($userLevel >= 80)
308            $this->gained(Achievement::DID_LOT_OF_TSUMEGO);
309        if ($userLevel >= 90)
310            $this->gained(Achievement::STILL_DOING_TSUMEGO);
311        if ($userLevel >= 100)
312            $this->gained(Achievement::THE_TOP);
313        return $this;
314    }
315
316    public function checkSetCompletedAchievements(): AchievementChecker
317    {
318        $achievementCondition = ClassRegistry::init('AchievementCondition')->find('first', [
319            'order' => 'value DESC',
320            'conditions' => [
321                'user_id' => Auth::getUserID(),
322                'category' => 'set']]);
323
324        if (!$achievementCondition)
325            return $this;
326        $achievementCondition = $achievementCondition['AchievementCondition'];
327        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_I_SETS_COUNT)
328            $this->gained(Achievement::COMPLETE_SETS_I);
329        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_II_SETS_COUNT)
330            $this->gained(Achievement::COMPLETE_SETS_II);
331        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_III_SETS_COUNT)
332            $this->gained(Achievement::COMPLETE_SETS_III);
333        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_IV_SETS_COUNT)
334            $this->gained(Achievement::COMPLETE_SETS_IV);
335        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_V_SETS_COUNT)
336            $this->gained(Achievement::COMPLETE_SETS_V);
337        if ($achievementCondition['value'] >= Achievement::COMPLETE_SETS_VI_SETS_COUNT)
338            $this->gained(Achievement::COMPLETE_SETS_VI);
339        return $this;
340    }
341
342    public function setAchievementSpecial($s = null): AchievementChecker
343    {
344        $tsIds = [];
345        $completed = '';
346        if ($s == 'cc1')
347        {
348            $ts1 = TsumegoUtil::collectTsumegosFromSet(50);
349            $ts2 = TsumegoUtil::collectTsumegosFromSet(52);
350            $ts3 = TsumegoUtil::collectTsumegosFromSet(53);
351            $ts4 = TsumegoUtil::collectTsumegosFromSet(54);
352            $ts = array_merge($ts1, $ts2, $ts3, $ts4);
353            $tsCount = count($ts);
354            for ($i = 0; $i < $tsCount; $i++)
355                array_push($tsIds, $ts[$i]['Tsumego']['id']);
356            $uts = ClassRegistry::init('TsumegoStatus')->find('all', [
357                'conditions' => [
358                    'user_id' => Auth::getUserID(),
359                    'tsumego_id' => $tsIds]]) ?: [];
360            $counter = 0;
361            $utsCount = count($uts);
362            for ($j = 0; $j < $utsCount; $j++)
363                for ($k = 0; $k < $tsCount; $k++)
364                    if ($uts[$j]['TsumegoStatus']['tsumego_id'] == $ts[$k]['Tsumego']['id'] && ($uts[$j]['TsumegoStatus']['status'] == 'S'
365                    || $uts[$j]['TsumegoStatus']['status'] == 'W' || $uts[$j]['TsumegoStatus']['status'] == 'C'))
366                        $counter++;
367            if ($counter == count($ts))
368                $completed = $s;
369        }
370        elseif ($s == 'cc2')
371        {
372            $ts1 = TsumegoUtil::collectTsumegosFromSet(41);
373            $ts2 = TsumegoUtil::collectTsumegosFromSet(49);
374            $ts3 = TsumegoUtil::collectTsumegosFromSet(65);
375            $ts4 = TsumegoUtil::collectTsumegosFromSet(66);
376            $ts = array_merge($ts1, $ts2, $ts3, $ts4);
377            $tsCount = count($ts);
378            for ($i = 0; $i < $tsCount; $i++)
379                array_push($tsIds, $ts[$i]['Tsumego']['id']);
380            $uts = ClassRegistry::init('TsumegoStatus')->find('all', [
381                'conditions' => [
382                    'user_id' => Auth::getUserID(),
383                    'tsumego_id' => $tsIds]]) ?: [];
384            $counter = 0;
385            $utsCount = count($uts);
386            for ($j = 0; $j < $utsCount; $j++)
387                for ($k = 0; $k < $tsCount; $k++)
388                    if ($uts[$j]['TsumegoStatus']['tsumego_id'] == $ts[$k]['Tsumego']['id'] && ($uts[$j]['TsumegoStatus']['status'] == 'S'
389                    || $uts[$j]['TsumegoStatus']['status'] == 'W' || $uts[$j]['TsumegoStatus']['status'] == 'C'))
390                        $counter++;
391            if ($counter == count($ts))
392                $completed = $s;
393        }
394        elseif ($s == 'cc3')
395        {
396            $ts1 = TsumegoUtil::collectTsumegosFromSet(186);
397            $ts2 = TsumegoUtil::collectTsumegosFromSet(187);
398            $ts3 = TsumegoUtil::collectTsumegosFromSet(196);
399            $ts4 = TsumegoUtil::collectTsumegosFromSet(203);
400            $ts = array_merge($ts1, $ts2, $ts3, $ts4);
401            $tsCount = count($ts);
402            for ($i = 0; $i < $tsCount; $i++)
403                array_push($tsIds, $ts[$i]['Tsumego']['id']);
404            $uts = ClassRegistry::init('TsumegoStatus')->find('all', [
405                'conditions' => [
406                    'user_id' => Auth::getUserID(),
407                    'tsumego_id' => $tsIds,
408                ],
409            ]) ?: [];
410            $counter = 0;
411            $utsCount = count($uts);
412            for ($j = 0; $j < $utsCount; $j++)
413                for ($k = 0; $k < $tsCount; $k++)
414                    if ($uts[$j]['TsumegoStatus']['tsumego_id'] == $ts[$k]['Tsumego']['id'] && ($uts[$j]['TsumegoStatus']['status'] == 'S'
415                    || $uts[$j]['TsumegoStatus']['status'] == 'W' || $uts[$j]['TsumegoStatus']['status'] == 'C'))
416                        $counter++;
417            if ($counter == count($ts))
418                $completed = $s;
419        }
420        elseif ($s == '1000w1')
421        {
422            $ts1 = TsumegoUtil::collectTsumegosFromSet(190);
423            $ts2 = TsumegoUtil::collectTsumegosFromSet(193);
424            $ts3 = TsumegoUtil::collectTsumegosFromSet(198);
425            $ts = array_merge($ts1, $ts2, $ts3);
426            $tsCount = count($ts);
427            for ($i = 0; $i < $tsCount; $i++)
428                array_push($tsIds, $ts[$i]['Tsumego']['id']);
429            $uts = ClassRegistry::init('TsumegoStatus')->find('all', [
430                'conditions' => [
431                    'user_id' => Auth::getUserID(),
432                    'tsumego_id' => $tsIds]]) ?: [];
433            $counter = 0;
434            $utsCount = count($uts);
435            for ($j = 0; $j < $utsCount; $j++)
436                for ($k = 0; $k < $tsCount; $k++)
437                    if ($uts[$j]['TsumegoStatus']['tsumego_id'] == $ts[$k]['Tsumego']['id'] && ($uts[$j]['TsumegoStatus']['status'] == 'S'
438                    || $uts[$j]['TsumegoStatus']['status'] == 'W' || $uts[$j]['TsumegoStatus']['status'] == 'C'))
439                        $counter++;
440            if ($counter == count($ts))
441                $completed = $s;
442        }
443        elseif ($s == '1000w2')
444        {
445            $ts = TsumegoUtil::collectTsumegosFromSet(216);
446            $tsCount = count($ts);
447            for ($i = 0; $i < $tsCount; $i++)
448                array_push($tsIds, $ts[$i]['Tsumego']['id']);
449            $uts = ClassRegistry::init('TsumegoStatus')->find('all', [
450                'conditions' => [
451                    'user_id' => Auth::getUserID(),
452                    'tsumego_id' => $tsIds]]) ?: [];
453            $counter = 0;
454            $utsCount = count($uts);
455            for ($j = 0; $j < $utsCount; $j++)
456                for ($k = 0; $k < $tsCount; $k++)
457                    if ($uts[$j]['TsumegoStatus']['tsumego_id'] == $ts[$k]['Tsumego']['id'] && ($uts[$j]['TsumegoStatus']['status'] == 'S'
458                    || $uts[$j]['TsumegoStatus']['status'] == 'W' || $uts[$j]['TsumegoStatus']['status'] == 'C'))
459                        $counter++;
460            if ($counter == count($ts))
461                $completed = $s;
462        }
463
464        if ($completed == 'cc1')
465            $this->gained(Achievement::LIFE_DEATH_ELEMENTARY);
466        if ($completed == 'cc2')
467            $this->gained(Achievement::LIFE_DEATH_INTERMEDIATE);
468        if ($completed == 'cc3')
469            $this->gained(Achievement::LIFE_DEATH_ADVANCED);
470        if ($completed == '1000w1')
471            $this->gained(Achievement::WEIQI_1000_FIRST_HALF);
472        if ($completed == '1000w2')
473            $this->gained(Achievement::WEIQI_1000_SECOND_HALF);
474        return $this;
475    }
476
477    public function checkSetAchievements($sid = null, $setRating = 0): AchievementChecker
478    {
479        if ($sid == -1)
480        {
481            $this->gained(Achievement::FAVORITES);
482            return $this;
483        }
484
485        $tNum = count(TsumegoUtil::collectTsumegosFromSet($sid));
486        $acA = ClassRegistry::init('AchievementCondition')->find('first', [
487            'order' => 'value DESC',
488            'conditions' => [
489                'set_id' => $sid,
490                'user_id' => Auth::getUserID(),
491                'category' => '%']]);
492        if (!$acA)
493            return $this;
494        $acS = ClassRegistry::init('AchievementCondition')->find('first', [
495            'order' => 'value ASC',
496            'conditions' => [
497                'set_id' => $sid,
498                'user_id' => Auth::getUserID(),
499                'category' => 's']]);
500
501        if ($tNum < 100)
502            return $this;
503
504        if ($setRating < 1300)
505        {
506            if ($acA['AchievementCondition']['value'] >= 75)
507                $this->gained(Achievement::ACCURACY_I);
508            if  ($acA['AchievementCondition']['value'] >= 85)
509                $this->gained(Achievement::ACCURACY_II);
510            if ($acA['AchievementCondition']['value'] >= 95)
511                $this->gained(Achievement::ACCURACY_III);
512            if ($acS['AchievementCondition']['value'] < 15)
513                $this->gained(Achievement::SPEED_I);
514            if ($acS['AchievementCondition']['value'] < 10)
515                $this->gained(Achievement::SPEED_II);
516            if ($acS['AchievementCondition']['value'] < 5)
517                $this->gained(Achievement::SPEED_III);
518        }
519        elseif ($setRating >= 1300 && $setRating < 1500)
520        {
521            if ($acA['AchievementCondition']['value'] >= 75)
522                $this->gained(Achievement::ACCURACY_IV);
523            if ($acA['AchievementCondition']['value'] >= 85)
524                $this->gained(Achievement::ACCURACY_V);
525            if ($acA['AchievementCondition']['value'] >= 95)
526                $this->gained(Achievement::ACCURACY_VI);
527            if ($acS['AchievementCondition']['value'] < 18)
528                $this->gained(Achievement::SPEED_IV);
529            if ($acS['AchievementCondition']['value'] < 13)
530                $this->gained(Achievement::SPEED_V);
531            if ($acS['AchievementCondition']['value'] < 8)
532                $this->gained(Achievement::SPEED_VI);
533        }
534        elseif ($setRating >= 1500 && $setRating < 1700)
535        {
536            if ($acA['AchievementCondition']['value'] >= 75)
537                $this->gained(Achievement::ACCURACY_VII);
538            if ($acA['AchievementCondition']['value'] >= 85)
539                $this->gained(Achievement::ACCURACY_VIII);
540            if ($acA['AchievementCondition']['value'] >= 95)
541                $this->gained(Achievement::ACCURACY_IX);
542            if ($acS['AchievementCondition']['value'] < 30)
543                $this->gained(Achievement::SPEED_VII);
544            if ($acS['AchievementCondition']['value'] < 20)
545                $this->gained(Achievement::SPEED_VIII);
546            if ($acS['AchievementCondition']['value'] < 10)
547                $this->gained(Achievement::SPEED_IX);
548        }
549        else
550        {
551            if ($acA['AchievementCondition']['value'] >= 75)
552                $this->gained(Achievement::ACCURACY_X);
553            if ($acA['AchievementCondition']['value'] >= 85)
554                $this->gained(Achievement::ACCURACY_XI);
555            if ($acA['AchievementCondition']['value'] >= 95)
556                $this->gained(Achievement::ACCURACY_XII);
557            if ($acS['AchievementCondition']['value'] < 30)
558                $this->gained(Achievement::SPEED_X);
559            if ($acS['AchievementCondition']['value'] < 20)
560                $this->gained(Achievement::SPEED_XI);
561            if ($acS['AchievementCondition']['value'] < 10)
562                $this->gained(Achievement::SPEED_XII);
563        }
564
565        $achievementId = 46;
566        if ($acA['AchievementCondition']['value'] >= 100)
567        {
568            $ac100 = ClassRegistry::init('AchievementCondition')->find('all', ['conditions' => ['user_id' => Auth::getUserID(), 'category' => '%', 'value >=' => 100]]) ?: [];
569            $ac100counter = 0;
570            $ac100Count = count($ac100);
571            for ($j = 0; $j < $ac100Count; $j++)
572                if (count(TsumegoUtil::collectTsumegosFromSet($ac100[$j]['AchievementCondition']['set_id'])) >= 100)
573                    $ac100counter++;
574            $as100 = ClassRegistry::init('AchievementStatus')->find('first', ['conditions' => ['user_id' => Auth::getUserID(), 'achievement_id' => $achievementId]]);
575            if ($as100 == null)
576            {
577                $as100 = [];
578                $as100['AchievementStatus']['user_id'] = Auth::getUserID();
579                $as100['AchievementStatus']['achievement_id'] = $achievementId;
580                $as100['AchievementStatus']['value'] = 0;
581                ClassRegistry::init('AchievementStatus')->create();
582            }
583            if ($as100['AchievementStatus']['value'] != $ac100counter)
584            {
585                $as100['AchievementStatus']['value'] = $ac100counter;
586                ClassRegistry::init('AchievementStatus')->save($as100);
587            }
588        }
589        return $this;
590    }
591
592    public function checkAll(): void
593    {
594        $this->checkLevelAchievements();
595        $this->checkProblemNumberAchievements();
596        $this->checkRatingAchievements();
597        $this->checkDanSolveAchievements();
598        $this->checkNoErrorAchievements();
599        $this->checkTimeModeAchievements();
600    }
601
602    public function finalize(): AchievementChecker
603    {
604        if (empty($this->updated))
605            return $this;
606
607        $xpBonus = 0;
608        foreach ($this->updated as $achievement)
609            $xpBonus += $achievement['xp'];
610        if ($xpBonus == 0)
611            return $this;
612
613        $user = &Auth::getUser();
614        Level::addXP($user, $xpBonus);
615        Auth::saveUser();
616        return $this;
617    }
618
619    private array $existingStatuses = [];
620    public array $updated = [];
621}