Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
99.47% |
189 / 190 |
|
88.89% |
8 / 9 |
CRAP | |
0.00% |
0 / 1 |
| TimeModeController | |
99.46% |
185 / 186 |
|
88.89% |
8 / 9 |
51 | |
0.00% |
0 / 1 |
| start | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
| play | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
7 | |||
| skip | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| getRanksWithTsumegoCount | |
96.77% |
30 / 31 |
|
0.00% |
0 / 1 |
8 | |||
| overview | |
100.00% |
29 / 29 |
|
100.00% |
1 / 1 |
8 | |||
| exportSessionToShow | |
100.00% |
30 / 30 |
|
100.00% |
1 / 1 |
5 | |||
| deduceUnlock | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
5 | |||
| deduceFinishedSession | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
| result | |
100.00% |
37 / 37 |
|
100.00% |
1 / 1 |
10 | |||
| 1 | <?php |
| 2 | |
| 3 | App::uses('TimeModeUtil', 'Utility'); |
| 4 | App::uses('NotFoundException', 'Routing/Error'); |
| 5 | App::uses('BadRequestException', 'Routing/Error'); |
| 6 | App::uses('Play', 'Controller/Component'); |
| 7 | |
| 8 | class TimeModeController extends AppController |
| 9 | { |
| 10 | public function start(): mixed |
| 11 | { |
| 12 | $timeMode = new TimeMode(); |
| 13 | $categoryID = (int) $this->params['url']['categoryID']; |
| 14 | if (!$categoryID) |
| 15 | throw new BadRequestException('Time mode category not specified.'); |
| 16 | $rankID = (int) $this->params['url']['rankID']; |
| 17 | if (!$rankID) |
| 18 | throw new BadRequestException('Time mode rank not specified.'); |
| 19 | |
| 20 | $timeMode->startTimeMode($categoryID, $rankID); |
| 21 | return $this->redirect("/timeMode/play"); |
| 22 | } |
| 23 | |
| 24 | public function play(): mixed |
| 25 | { |
| 26 | if (!Auth::isLoggedIn()) |
| 27 | return $this->redirect('/users/login'); |
| 28 | |
| 29 | if (!Auth::isInTimeMode()) |
| 30 | { |
| 31 | Auth::getUser()['mode'] = Constants::$TIME_MODE; |
| 32 | Auth::saveUser(); |
| 33 | } |
| 34 | |
| 35 | $timeMode = new TimeMode(); |
| 36 | |
| 37 | if (!$timeMode->currentSession) |
| 38 | return $this->redirect('/timeMode/overview'); |
| 39 | |
| 40 | $tsumegoID = $timeMode->prepareNextToSolve(); |
| 41 | if ($timeModeSessionID = $timeMode->checkFinishSession()) |
| 42 | return $this->redirect("/timeMode/result/" . $timeModeSessionID); |
| 43 | assert($tsumegoID != null); |
| 44 | |
| 45 | $setConnection = ClassRegistry::init('SetConnection')->find('first', ['conditions' => ['tsumego_id' => $tsumegoID]]); |
| 46 | if (!$setConnection) |
| 47 | throw new Exception('Time mode session contains tsumego without a set connection.'); |
| 48 | |
| 49 | $this->set('timeMode', $timeMode); |
| 50 | $this->set('nextLink', '/timeMode/skip/'); |
| 51 | $this->set('noSkipNextLink', $timeMode->currentWillBeLast() ? '/timeMode/result/' . $timeMode->currentSession['TimeModeSession']['id'] : '/timeMode/play'); |
| 52 | $play = new Play(function ($name, $value) { $this->set($name, $value); }, function ($url) { return $this->redirect($url); }); |
| 53 | $play->play($setConnection['SetConnection']['id'], $this->params, $this->data); |
| 54 | $this->render('/Tsumegos/play'); |
| 55 | return null; |
| 56 | } |
| 57 | |
| 58 | public function skip(): mixed |
| 59 | { |
| 60 | $timeMode = new TimeMode(); |
| 61 | $timeMode->skip(); |
| 62 | return $this->play(); |
| 63 | } |
| 64 | |
| 65 | private function getRanksWithTsumegoCount() |
| 66 | { |
| 67 | $ranks = ClassRegistry::init('TimeModeRank')->find('all', ['order' => 'id']); |
| 68 | $rankPartOfQuery = ''; |
| 69 | $count = count($ranks); |
| 70 | if ($count == 0) |
| 71 | return null; |
| 72 | if ($count == 1) |
| 73 | { |
| 74 | $result = []; |
| 75 | $rank = $ranks[0]['TimeModeRank']; |
| 76 | |
| 77 | $count = Util::query(" |
| 78 | SELECT |
| 79 | COUNT(*) AS count |
| 80 | FROM |
| 81 | tsumego |
| 82 | JOIN set_connection ON set_connection.tsumego_id=tsumego.id |
| 83 | JOIN `set` ON set_connection.set_id=`set`.id |
| 84 | WHERE |
| 85 | `set`.`included_in_time_mode` = 1 AND |
| 86 | `set`.public = 1"); |
| 87 | |
| 88 | $rank['tsumego_count'] = $count[0]['count']; |
| 89 | $result[] = $rank; |
| 90 | return $result; |
| 91 | } |
| 92 | foreach ($ranks as $index => $rank) |
| 93 | if ($index + 1 < $count) |
| 94 | $rankPartOfQuery .= 'WHEN rating < ' . Rating::getRankMinimalRating(Rating::getRankFromReadableRank($rank['TimeModeRank']['name']) + 1) . ' THEN \'' . $rank['TimeModeRank']['id'] . '\' '; |
| 95 | else |
| 96 | $rankPartOfQuery .= 'ELSE \'' . $rank['TimeModeRank']['id'] . '\''; |
| 97 | $counts = ClassRegistry::init('Tsumego')->query(" |
| 98 | SELECT |
| 99 | CASE " . $rankPartOfQuery . " |
| 100 | END AS bucket, |
| 101 | COUNT(*) AS count |
| 102 | FROM |
| 103 | tsumego |
| 104 | JOIN set_connection ON set_connection.tsumego_id=tsumego.id |
| 105 | JOIN `set` ON set_connection.set_id=`set`.id |
| 106 | WHERE |
| 107 | `set`.`included_in_time_mode` = 1 AND |
| 108 | `set`.public = 1 |
| 109 | GROUP BY bucket |
| 110 | ORDER BY MIN(rating);"); |
| 111 | |
| 112 | $countsByRankID = []; |
| 113 | foreach ($counts as $count) |
| 114 | $countsByRankID[$count[0]['bucket']] = $count[0]['count']; |
| 115 | |
| 116 | $result = []; |
| 117 | foreach ($ranks as $rank) |
| 118 | { |
| 119 | $rank = $rank['TimeModeRank']; |
| 120 | if ($count = $countsByRankID[$rank['id']]) |
| 121 | $rank['tsumego_count'] = $count; |
| 122 | else |
| 123 | $rank['tsumego_count'] = 0; |
| 124 | $result [] = $rank; |
| 125 | } |
| 126 | return $result; |
| 127 | } |
| 128 | |
| 129 | public function overview(): mixed |
| 130 | { |
| 131 | if (!Auth::isLoggedIn()) |
| 132 | return $this->redirect('/users/login'); |
| 133 | $this->set('_title', 'Time Mode - Select'); |
| 134 | $this->set('_page', 'time mode'); |
| 135 | |
| 136 | $lastTimeModeCategoryID = Auth::getUser()['last_time_mode_category_id']; |
| 137 | if (!$lastTimeModeCategoryID) |
| 138 | $lastTimeModeCategoryID = ClassRegistry::init('TimeModeCategory')->find('first', ['order' => 'id DESC'])['TimeModeCategory']['id']; |
| 139 | assert($lastTimeModeCategoryID); |
| 140 | |
| 141 | $timeModeRankMap = Util::indexByID(ClassRegistry::init('TimeModeRank')->find('all', []) ?: [], 'TimeModeRank', 'name'); |
| 142 | |
| 143 | $timeModeStatuses = ClassRegistry::init('TimeModeSession')->find('all', [ |
| 144 | 'conditions' => [ |
| 145 | 'user_id' => Auth::getUserID(), |
| 146 | 'time_mode_session_status_id' => TimeModeUtil::$SESSION_STATUS_SOLVED]]) ?: []; |
| 147 | |
| 148 | $solvedMap = []; |
| 149 | foreach ($timeModeStatuses as $timeModeStatus) |
| 150 | { |
| 151 | $timeModeCategoryID = $timeModeStatus['TimeModeSession']['time_mode_category_id']; |
| 152 | $timeModeRankID = $timeModeStatus['TimeModeSession']['time_mode_rank_id']; |
| 153 | $category = &$solvedMap[$timeModeCategoryID]; |
| 154 | $category[$timeModeRankID] = $timeModeRankMap[$timeModeStatus['TimeModeSession']['time_mode_rank_id']]; |
| 155 | if (!isset($category['best-solved-rank']) || $category['best-solved-rank'] < $timeModeRankID) |
| 156 | $category['best-solved-rank'] = $timeModeRankID; |
| 157 | } |
| 158 | |
| 159 | $this->set('lastTimeModeCategoryID', $lastTimeModeCategoryID); |
| 160 | $this->set('timeModeCategories', ClassRegistry::init('TimeModeCategory')->find('all', ['order' => 'id'])); |
| 161 | $this->set('timeModeRanks', $this->getRanksWithTsumegoCount()); |
| 162 | $this->set('solvedMap', $solvedMap); |
| 163 | $finishedSessionCount = ClassRegistry::init('TimeModeSession')->find('count', ['conditions' => [ |
| 164 | 'user_id' => Auth::getUserID(), |
| 165 | 'time_mode_session_status_id !=' => TimeModeUtil::$SESSION_STATUS_IN_PROGRESS]]); |
| 166 | $this->set('hasFinishedSesssion', $finishedSessionCount > 0); |
| 167 | return null; |
| 168 | } |
| 169 | |
| 170 | public function exportSessionToShow($session, $timeModeCategory, $timeModeRank): array |
| 171 | { |
| 172 | $result = []; |
| 173 | $result['id'] = $session['TimeModeSession']['id']; |
| 174 | $result['status'] = $session['TimeModeSession']['time_mode_session_status_id'] == TimeModeUtil::$SESSION_STATUS_SOLVED ? 'passed' : 'failed'; |
| 175 | $result['category'] = $timeModeCategory['TimeModeCategory']['name']; |
| 176 | $result['points'] = $session['TimeModeSession']['points']; |
| 177 | $result['rank'] = $timeModeRank['TimeModeRank']['name']; |
| 178 | $date = new DateTime($session['TimeModeSession']['created']); |
| 179 | $result['created'] = $date->format('H:i d.m.Y'); |
| 180 | $timeModeAttempts = ClassRegistry::init('TimeModeAttempt')->find('all', ['conditions' => ['time_mode_session_id' => $session['TimeModeSession']['id']]]) ?: []; |
| 181 | $result['attempts'] = []; |
| 182 | $solvedCount = 0; |
| 183 | foreach ($timeModeAttempts as $timeModeAttempt) |
| 184 | { |
| 185 | if ($timeModeAttempt['TimeModeAttempt']['time_mode_attempt_status_id'] == TimeModeUtil::$ATTEMPT_RESULT_SOLVED) |
| 186 | $solvedCount++; |
| 187 | $attempt = []; |
| 188 | $setConnection = ClassRegistry::init('SetConnection')->find('first', ['conditions' => ['tsumego_id' => $timeModeAttempt['TimeModeAttempt']['tsumego_id']]]); |
| 189 | $set = ClassRegistry::init('Set')->findById($setConnection['SetConnection']['set_id']); |
| 190 | $attempt['tsumego_id'] = $timeModeAttempt['TimeModeAttempt']['tsumego_id']; |
| 191 | $attempt['set'] = $set['Set']['title'] . ' ' . $set['Set']['title2']; |
| 192 | $attempt['set_order'] = $setConnection['SetConnection']['num']; |
| 193 | $seconds = $timeModeAttempt['TimeModeAttempt']['seconds']; |
| 194 | $minutes = floor($seconds / 60); |
| 195 | $seconds -= $minutes * 60; |
| 196 | $attempt['seconds'] = $minutes . ' : ' . number_format($seconds, 2); |
| 197 | $attempt['points'] = $timeModeAttempt['TimeModeAttempt']['points']; |
| 198 | $attempt['order'] = $timeModeAttempt['TimeModeAttempt']['order']; |
| 199 | $attempt['status'] = TimeModeUtil::attemptStatusName($timeModeAttempt['TimeModeAttempt']['time_mode_attempt_status_id']); |
| 200 | $result['attempts'] [] = $attempt; |
| 201 | } |
| 202 | $result['solvedCount'] = $solvedCount; |
| 203 | return $result; |
| 204 | } |
| 205 | |
| 206 | public static function deduceUnlock(?array $finishedSession, array $timeModeRanks, array $timeModeCategories): ?array |
| 207 | { |
| 208 | if (!$finishedSession) |
| 209 | return null; |
| 210 | |
| 211 | // the current session wasn't a success, so it logically can't unlock shit |
| 212 | if ($finishedSession['TimeModeSession']['time_mode_session_status_id'] != TimeModeUtil::$SESSION_STATUS_SOLVED) |
| 213 | return null; |
| 214 | |
| 215 | // only one successful solve exists for this combination of rank and category, so it must be the one we just did |
| 216 | if (ClassRegistry::init('TimeModeSession')->find('count', [ |
| 217 | 'conditions' => [ |
| 218 | 'user_id' => Auth::getUserID(), |
| 219 | 'time_mode_category_id' => $finishedSession['TimeModeSession']['time_mode_category_id'], |
| 220 | 'time_mode_rank_id' => $finishedSession['TimeModeSession']['time_mode_rank_id'], |
| 221 | 'time_mode_session_status_id' => TimeModeUtil::$SESSION_STATUS_SOLVED]]) != 1) |
| 222 | return null; |
| 223 | |
| 224 | $rankIndex = array_find_key($timeModeRanks, function ($timeModeRank) use ($finishedSession) { return $timeModeRank['TimeModeRank']['id'] == $finishedSession['TimeModeSession']['time_mode_rank_id']; }); |
| 225 | |
| 226 | if ($rankIndex == 0) |
| 227 | { |
| 228 | return null; // there is no higher rank to unlock |
| 229 | } |
| 230 | |
| 231 | $unlock = []; |
| 232 | $unlock['rank'] = $timeModeRanks[$rankIndex - 1]['TimeModeRank']['name']; |
| 233 | |
| 234 | $categoryIndex = array_find_key($timeModeCategories, function ($timeModeCategory) use ($finishedSession) { return $timeModeCategory['TimeModeCategory']['id'] == $finishedSession['TimeModeSession']['time_mode_category_id']; }); |
| 235 | $unlock['category'] = $timeModeCategories[$categoryIndex]['TimeModeCategory']['name']; |
| 236 | return $unlock; |
| 237 | } |
| 238 | |
| 239 | private function deduceFinishedSession($passedSessionID, TimeMode $timeMode): ?array |
| 240 | { |
| 241 | if ($finishedSessionID = $timeMode->checkFinishSession()) |
| 242 | return ClassRegistry::init('TimeModeSession')->findById($finishedSessionID); |
| 243 | |
| 244 | if ($passedSessionID) |
| 245 | if ($result = ClassRegistry::init('TimeModeSession')->find('first', ['conditions' => ['id' => $passedSessionID]])) |
| 246 | return $result; |
| 247 | else |
| 248 | throw new NotFoundException('Time Mode Session not found.'); |
| 249 | return null; |
| 250 | } |
| 251 | |
| 252 | public function result($timeModeSessionID = null): mixed |
| 253 | { |
| 254 | if (!Auth::isLoggedIn()) |
| 255 | return $this->redirect("/users/login"); |
| 256 | |
| 257 | $timeMode = new TimeMode(); |
| 258 | $finishedSession = $this->deduceFinishedSession($timeModeSessionID, $timeMode); |
| 259 | |
| 260 | $this->loadModel('Tsumego'); |
| 261 | $this->loadModel('Set'); |
| 262 | $this->loadModel('TimeModeSession'); |
| 263 | $this->loadModel('SetConnection'); |
| 264 | $this->set('_title', 'Time Mode - Result'); |
| 265 | $this->set('_page', 'time mode'); |
| 266 | |
| 267 | $timeModeCategories = ClassRegistry::init('TimeModeCategory')->find('all', []); |
| 268 | $timeModeRanks = ClassRegistry::init('TimeModeRank')->find('all', ['order' => 'id DESC']); |
| 269 | |
| 270 | $sessionsToShow = []; |
| 271 | foreach ($timeModeCategories as $timeModeCategory) |
| 272 | foreach ($timeModeRanks as $timeModeRank) |
| 273 | { |
| 274 | $session = ClassRegistry::init('TimeModeSession')->find('first', [ |
| 275 | 'conditions' => [ |
| 276 | 'user_id' => Auth::getUserID(), |
| 277 | 'time_mode_category_id' => $timeModeCategory['TimeModeCategory']['id'], |
| 278 | 'time_mode_rank_id' => $timeModeRank['TimeModeRank']['id']], |
| 279 | 'order' => 'points DESC']); |
| 280 | $categoryID = $timeModeCategory['TimeModeCategory']['id']; |
| 281 | $rankID = $timeModeRank['TimeModeRank']['id']; |
| 282 | if (isset($finishedSession) |
| 283 | && $finishedSession['TimeModeSession']['time_mode_category_id'] == $categoryID |
| 284 | && $finishedSession['TimeModeSession']['time_mode_rank_id'] == $rankID) |
| 285 | $sessionsToShow[$categoryID][$rankID]['current'] = $this->exportSessionToShow($finishedSession, $timeModeCategory, $timeModeRank); |
| 286 | if (!$session || isset($finishedSession) && $session['TimeModeSession']['id'] == $finishedSession['TimeModeSession']['id']) |
| 287 | continue; |
| 288 | $sessionsToShow[$categoryID][$rankID]['best'] = $this->exportSessionToShow($session, $timeModeCategory, $timeModeRank); |
| 289 | } |
| 290 | |
| 291 | $this->set('sessionsToShow', $sessionsToShow); |
| 292 | $this->set('finishedSession', $finishedSession); |
| 293 | $this->set('rankArrowClosed', '/img/greyArrow1.png'); |
| 294 | $this->set('rankArrowOpened', '/img/greyArrow2.png'); |
| 295 | $this->set('unlock', self::deduceUnlock($finishedSession, $timeModeRanks, $timeModeCategories)); |
| 296 | $this->set('achievementUpdates', new AchievementChecker()->checkTimeModeAchievements()->finalize()->updated); |
| 297 | |
| 298 | return null; |
| 299 | } |
| 300 | } |