|
@@ -18,6 +18,14 @@
|
|
|
* - user/subject are escaped in SQL, and the hard-coded `e107.` database
|
|
* - user/subject are escaped in SQL, and the hard-coded `e107.` database
|
|
|
* name is replaced by MPREFIX
|
|
* name is replaced by MPREFIX
|
|
|
* - the previous tkLastQuestion row is removed before inserting the new one
|
|
* - the previous tkLastQuestion row is removed before inserting the new one
|
|
|
|
|
+ * - division has its own number ranges (times-table style, whole answers). It used
|
|
|
|
|
+ * getNumbers(), which gave a new student "1 / 1 = 1" every time, then jumped to
|
|
|
|
|
+ * four-digit dividends around 800 answers a month and fell back to 1 / 1 after 1200
|
|
|
|
|
+ * - rounding no longer asks about amounts that are already a multiple of 5 cents
|
|
|
|
|
+ * - multiplication uses the same ranges as division (the two stay in step). It used
|
|
|
|
|
+ * getNumbers() with its count/4 and accuracy/6 scaling, which started at 0 x 1, grew up
|
|
|
|
|
+ * to 100 x 100, jumped to about 250 x 250 at 800 answers a month and fell back to
|
|
|
|
|
+ * single digits after 1200
|
|
|
*/
|
|
*/
|
|
|
class numeracy2 {
|
|
class numeracy2 {
|
|
|
|
|
|
|
@@ -93,15 +101,10 @@ class numeracy2 {
|
|
|
* Two random whole numbers, larger first.
|
|
* Two random whole numbers, larger first.
|
|
|
*/
|
|
*/
|
|
|
private function getNumbers($difficulty, $sign) {
|
|
private function getNumbers($difficulty, $sign) {
|
|
|
- //TODO add finer grain control for multiplication here
|
|
|
|
|
|
|
+ // Used for addition and subtraction. Multiplication and division have their own ranges.
|
|
|
$count = (float) $difficulty['count'];
|
|
$count = (float) $difficulty['count'];
|
|
|
$percent = (float) $difficulty['percent'];
|
|
$percent = (float) $difficulty['percent'];
|
|
|
|
|
|
|
|
- if ($sign == 'x' || $sign == '/') {
|
|
|
|
|
- $count = floor($count / 4);
|
|
|
|
|
- $percent = $percent / 6;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
if ($count >= 300) {
|
|
if ($count >= 300) {
|
|
|
$max = (int) pow($count, ($percent / 100) * 2);
|
|
$max = (int) pow($count, ($percent / 100) * 2);
|
|
|
} elseif ($count >= 200) {
|
|
} elseif ($count >= 200) {
|
|
@@ -123,6 +126,10 @@ class numeracy2 {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private function basic($sign, $difficulty) {
|
|
private function basic($sign, $difficulty) {
|
|
|
|
|
+ if ($sign === 'x') {
|
|
|
|
|
+ return $this->Multiplication($difficulty);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$numbers = $this->getNumbers($difficulty, $sign);
|
|
$numbers = $this->getNumbers($difficulty, $sign);
|
|
|
$firstNumber = $numbers[0];
|
|
$firstNumber = $numbers[0];
|
|
|
$secondNumber = $numbers[1];
|
|
$secondNumber = $numbers[1];
|
|
@@ -130,19 +137,61 @@ class numeracy2 {
|
|
|
$solution = [
|
|
$solution = [
|
|
|
'+' => $firstNumber + $secondNumber,
|
|
'+' => $firstNumber + $secondNumber,
|
|
|
'-' => $firstNumber - $secondNumber,
|
|
'-' => $firstNumber - $secondNumber,
|
|
|
- 'x' => $firstNumber * $secondNumber,
|
|
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
return ['answer1' => $solution[$sign], 'question' => [$firstNumber, $sign, $secondNumber], 'type' => $this->subSubject];
|
|
return ['answer1' => $solution[$sign], 'question' => [$firstNumber, $sign, $secondNumber], 'type' => $this->subSubject];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Multiplication, times-table style: one factor goes up to 15 and the other up
|
|
|
|
|
+ * to 40 (answers up to 600) as the student's accuracy and number of answers
|
|
|
|
|
+ * grow. Both are at least 2, the bigger one first. Same ranges as Division.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function Multiplication($difficulty) {
|
|
|
|
|
+ $level = $this->level($difficulty);
|
|
|
|
|
+ $numbers = $this->flipNumbers($this->scaled(2, 15, $level), $this->scaled(2, 40, $level));
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'answer1' => $numbers[0] * $numbers[1],
|
|
|
|
|
+ 'question' => [$numbers[0], 'x', $numbers[1]],
|
|
|
|
|
+ 'type' => $this->subSubject,
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Division with whole-number answers, times-table style: the divisor goes up
|
|
|
|
|
+ * to 15 and the answer up to 40 (dividends up to 600) as the student's
|
|
|
|
|
+ * accuracy and number of answers grow. Both are at least 2.
|
|
|
|
|
+ */
|
|
|
private function Division($difficulty) {
|
|
private function Division($difficulty) {
|
|
|
- $numbers = $this->getNumbers($difficulty, '/');
|
|
|
|
|
- $quotient = max(1, $numbers[0]);
|
|
|
|
|
- $divisor = max(1, $numbers[1]);
|
|
|
|
|
- $dividend = $quotient * $divisor;
|
|
|
|
|
|
|
+ $level = $this->level($difficulty);
|
|
|
|
|
+ $divisor = $this->scaled(2, 15, $level);
|
|
|
|
|
+ $quotient = $this->scaled(2, 40, $level);
|
|
|
|
|
|
|
|
- return ['answer1' => $quotient, 'question' => [$dividend, '/', $divisor], 'type' => $this->subSubject];
|
|
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'answer1' => $quotient,
|
|
|
|
|
+ 'question' => [$divisor * $quotient, '/', $divisor],
|
|
|
|
|
+ 'type' => $this->subSubject,
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * How hard to make the numbers, 0-100: this month's accuracy, scaled up over
|
|
|
|
|
+ * the first 30 answers so one lucky answer doesn't jump to the hardest numbers.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function level($difficulty) {
|
|
|
|
|
+ $percent = max(0.0, min(100.0, (float) $difficulty['percent']));
|
|
|
|
|
+ return $percent * min(1.0, ((int) $difficulty['count']) / 30);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * A random whole number from $min up to $maxAtFull. At level 0 the top of the
|
|
|
|
|
+ * range is a quarter of the way there; at level 100 it is all of it.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function scaled($min, $maxAtFull, $level) {
|
|
|
|
|
+ $span = max(0, $maxAtFull - $min);
|
|
|
|
|
+ $max = $min + (int) ceil($span * (0.25 + 0.75 * $level / 100));
|
|
|
|
|
+ return mt_rand($min, max($min, $max));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -152,6 +201,9 @@ class numeracy2 {
|
|
|
private function Rounding($difficulty) {
|
|
private function Rounding($difficulty) {
|
|
|
$maxCents = (5 + (int) floor($difficulty['percent'] / 10) * 2) * 100;
|
|
$maxCents = (5 + (int) floor($difficulty['percent'] / 10) * 2) * 100;
|
|
|
$cents = mt_rand(100, $maxCents);
|
|
$cents = mt_rand(100, $maxCents);
|
|
|
|
|
+ if ($cents % 5 === 0) {
|
|
|
|
|
+ $cents += mt_rand(1, 4); // already a multiple of 5 cents: nothing to round
|
|
|
|
|
+ }
|
|
|
$roundedCents = (int) (round($cents / 5) * 5);
|
|
$roundedCents = (int) (round($cents / 5) * 5);
|
|
|
|
|
|
|
|
return [
|
|
return [
|