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