|
|
@@ -19,6 +19,9 @@
|
|
|
* - getDifficulty(): MPREFIX instead of `e107.`, user escaped, no null result
|
|
|
* - removed the broken, unused shortCode() and findInList()
|
|
|
* - a wrong answer takes a minute off but never below zero
|
|
|
+ * - a correct answer gets a friendlier, randomised message: a cheer, the answer
|
|
|
+ * worked out, the play minutes earned, and a streak of correct answers (the
|
|
|
+ * old "Congratulations" strings at messages[...][1] are no longer used)
|
|
|
*/
|
|
|
class answerChecker2 {
|
|
|
|
|
|
@@ -136,18 +139,20 @@ class answerChecker2 {
|
|
|
$word = (string) $answer['word'];
|
|
|
$correct = $this->isCorrect($word, $usersAnswer);
|
|
|
|
|
|
- $safeAnswer = htmlspecialchars($usersAnswer);
|
|
|
- $safeWord = htmlspecialchars($word);
|
|
|
+ $minutes = $this->updateScore($correct);
|
|
|
+ $this->addToReport($word, $usersAnswer, 'spelling', "Spell the word " . $word, $correct);
|
|
|
|
|
|
if ($correct) {
|
|
|
- $text = "Congratulations '" . $safeAnswer . "' was correct for 'spell the word " . $safeWord . "'<br/>";
|
|
|
+ $text = $this->correctText('spelling', $word, $usersAnswer, $word, $minutes);
|
|
|
+ $title = 'Correct!';
|
|
|
} else {
|
|
|
+ $safeAnswer = htmlspecialchars($usersAnswer);
|
|
|
+ $safeWord = htmlspecialchars($word);
|
|
|
$text = "Sorry but '" . $safeAnswer . "' was incorrect it should have been '" . $safeWord . "' for 'spell the word " . $safeWord . "'<br/>";
|
|
|
+ $title = 'Spelling answer check';
|
|
|
}
|
|
|
- $this->updateScore($correct);
|
|
|
|
|
|
- $this->addToReport($word, $usersAnswer, 'spelling', "Spell the word " . $word, $correct);
|
|
|
- e107::getRender()->tablerender('Spelling answer check', $text);
|
|
|
+ e107::getRender()->tablerender($title, $text);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -159,19 +164,118 @@ class answerChecker2 {
|
|
|
$expected = (string) $expected;
|
|
|
$messages = $this->messages[$subject];
|
|
|
|
|
|
- $safe = [
|
|
|
- '{ANSWER}' => htmlspecialchars($expected),
|
|
|
- '{USER_ANSWER}' => htmlspecialchars($usersAnswer),
|
|
|
- '{QUESTION}' => htmlspecialchars($question),
|
|
|
- ];
|
|
|
-
|
|
|
- $text = strtr($correct ? $messages[1] : $messages[2], $safe) . "<br/>";
|
|
|
- $this->updateScore($correct);
|
|
|
+ $minutes = $this->updateScore($correct);
|
|
|
|
|
|
$questionString = strtr($messages[0], ['{ANSWER}' => $expected, '{QUESTION}' => $question]);
|
|
|
$this->addToReport($expected, $usersAnswer, $subject, $questionString, $correct);
|
|
|
|
|
|
- e107::getRender()->tablerender(ucfirst($subject) . ' answer check', $text);
|
|
|
+ if ($correct) {
|
|
|
+ $text = $this->correctText($subject, $expected, $usersAnswer, $question, $minutes);
|
|
|
+ $title = 'Correct!';
|
|
|
+ } else {
|
|
|
+ $safe = [
|
|
|
+ '{ANSWER}' => htmlspecialchars($expected),
|
|
|
+ '{USER_ANSWER}' => htmlspecialchars($usersAnswer),
|
|
|
+ '{QUESTION}' => htmlspecialchars($question),
|
|
|
+ ];
|
|
|
+ $text = strtr($messages[2], $safe) . "<br/>";
|
|
|
+ $title = ucfirst($subject) . ' answer check';
|
|
|
+ }
|
|
|
+
|
|
|
+ e107::getRender()->tablerender($title, $text);
|
|
|
+ }
|
|
|
+
|
|
|
+ private $cheers = [
|
|
|
+ "🎉 Woohoo! You got it!",
|
|
|
+ "⭐ Brilliant!",
|
|
|
+ "🚀 Boom! Nailed it!",
|
|
|
+ "💪 Spot on!",
|
|
|
+ "🙌 High five! That's right!",
|
|
|
+ "🧠 Big brain move!",
|
|
|
+ "🌟 You're a star!",
|
|
|
+ "😎 Too easy for you!",
|
|
|
+ "🏆 Champion answer!",
|
|
|
+ "✨ Yes! Exactly right!",
|
|
|
+ "🎯 Bullseye!",
|
|
|
+ "🦄 Magical! Correct!",
|
|
|
+ ];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The message for a correct answer: a cheer, the answer, minutes earned,
|
|
|
+ * and a streak when there is one. Everything typed by the student is escaped.
|
|
|
+ */
|
|
|
+ private function correctText($subject, $expected, $usersAnswer, $question, $minutes) {
|
|
|
+ $text = '<div style="font-size:160%;font-weight:bold;">' . $this->cheers[array_rand($this->cheers)] . '</div>';
|
|
|
+ $text .= '<p>' . $this->correctDetail($subject, $expected, $usersAnswer, $question) . '</p>';
|
|
|
+
|
|
|
+ if ($minutes > 0) {
|
|
|
+ $text .= '<p>🎮 <b>+' . (int) $minutes . ((int) $minutes === 1 ? ' minute' : ' minutes') . '</b> of play time earned!</p>';
|
|
|
+ }
|
|
|
+
|
|
|
+ $streak = $this->correctStreak();
|
|
|
+ if ($streak >= 25) {
|
|
|
+ $text .= '<p>🚀 25 or more correct in a row. Unstoppable!</p>';
|
|
|
+ } elseif ($streak >= 10) {
|
|
|
+ $text .= '<p>🚀 ' . $streak . ' correct in a row. Unstoppable!</p>';
|
|
|
+ } elseif ($streak >= 5) {
|
|
|
+ $text .= '<p>🔥 ' . $streak . ' correct in a row. You\'re on fire!</p>';
|
|
|
+ } elseif ($streak >= 3) {
|
|
|
+ $text .= '<p>🔥 ' . $streak . ' correct in a row!</p>';
|
|
|
+ }
|
|
|
+
|
|
|
+ return $text;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The line that says what the right answer was, for this kind of question.
|
|
|
+ */
|
|
|
+ private function correctDetail($subject, $expected, $usersAnswer, $question) {
|
|
|
+ $e = htmlspecialchars($expected);
|
|
|
+
|
|
|
+ switch ($subject) {
|
|
|
+ case 'numbers':
|
|
|
+ return 'The missing number was <b>' . $e . '</b>.';
|
|
|
+ case 'addition':
|
|
|
+ case 'subtraction':
|
|
|
+ case 'multiplication':
|
|
|
+ case 'division':
|
|
|
+ $sum = strtr($question, ['x' => ' × ', '/' => ' ÷ ', '+' => ' + ', '-' => ' − ']);
|
|
|
+ return '<b>' . htmlspecialchars($sum) . ' = ' . $e . '</b>';
|
|
|
+ case 'rounding':
|
|
|
+ return '<b>' . htmlspecialchars($question) . '</b> rounds to <b>' . $e . '</b> (the nearest 5 cents).';
|
|
|
+ case 'anagrams':
|
|
|
+ if (mb_strtolower(trim($usersAnswer)) !== mb_strtolower($expected)) {
|
|
|
+ return 'You found <b>' . htmlspecialchars($usersAnswer) . '</b>. I was thinking of <b>' . $e . '</b>, but that is a real word too!';
|
|
|
+ }
|
|
|
+ return 'You unjumbled it: <b>' . $e . '</b>!';
|
|
|
+ case 'spelling':
|
|
|
+ return 'You spelled <b>' . $e . '</b> perfectly!';
|
|
|
+ default:
|
|
|
+ return '<b>' . $e . '</b> was exactly right.';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * How many of the student's latest answers in a row were correct (up to 25).
|
|
|
+ * Called after the current answer has been written to the report.
|
|
|
+ */
|
|
|
+ private function correctStreak() {
|
|
|
+ $db = e107::getDb();
|
|
|
+ $db->gen("SELECT `correct` FROM `" . MPREFIX . "tkReport` WHERE `username` = '" . $db->escape($this->user) . "' ORDER BY `id` DESC LIMIT 25");
|
|
|
+
|
|
|
+ $recent = [];
|
|
|
+ while ($row = $db->fetch()) {
|
|
|
+ $recent[] = (int) $row['correct'];
|
|
|
+ }
|
|
|
+
|
|
|
+ $streak = 0;
|
|
|
+ foreach ($recent as $correct) {
|
|
|
+ if ($correct !== 1) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ $streak++;
|
|
|
+ }
|
|
|
+ return $streak;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -228,7 +332,9 @@ class answerChecker2 {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Add or minus the score
|
|
|
+ * Add or minus the score.
|
|
|
+ *
|
|
|
+ * @return int The play minutes awarded for a correct answer (0 otherwise).
|
|
|
*/
|
|
|
private function updateScore($scorePoint) {
|
|
|
$db = e107::getDb();
|
|
|
@@ -241,9 +347,11 @@ class answerChecker2 {
|
|
|
$currentScore = ['timekeeper_lifetimecorrect' => 0, 'timekeeper_lifetimequestions' => 0, 'timekeeper_minutes' => 0];
|
|
|
}
|
|
|
|
|
|
+ $awarded = 0;
|
|
|
if ($scorePoint) {
|
|
|
$currentScore['timekeeper_lifetimecorrect']++;
|
|
|
- $currentScore['timekeeper_minutes'] = $currentScore['timekeeper_minutes'] + $this->scaleTimeAddition();
|
|
|
+ $awarded = (int) $this->scaleTimeAddition();
|
|
|
+ $currentScore['timekeeper_minutes'] = $currentScore['timekeeper_minutes'] + $awarded;
|
|
|
} else {
|
|
|
// Never below zero: the column is signed now, and a negative balance would
|
|
|
// otherwise have to be earned back before any play time counts.
|
|
|
@@ -260,6 +368,8 @@ class answerChecker2 {
|
|
|
$currentScore['WHERE'] = 'timekeeper_username="' . $user . '"';
|
|
|
$db->update('timekeeperScore', $currentScore);
|
|
|
}
|
|
|
+
|
|
|
+ return $awarded;
|
|
|
}
|
|
|
|
|
|
/**
|