Przeglądaj źródła

Fix Multiplication, Division and Rounding.

Gregory Huxley 1 tydzień temu
rodzic
commit
882b5a298c
3 zmienionych plików z 72 dodań i 17 usunięć
  1. 5 3
      README.md
  2. 3 2
      TK2.php
  3. 64 12
      questionGenerators/math/numeracy2.php

+ 5 - 3
README.md

@@ -66,13 +66,15 @@ generator in `questionGenerators/`. A subject unlocks at a difficulty threshold:
 | numbers (missing number), spelling | 0 |
 | numbers (missing number), spelling | 0 |
 | addition | 10,000 |
 | addition | 10,000 |
 | subtraction | 20,000 |
 | subtraction | 20,000 |
+| rounding (money to the nearest 5 cents) | 50,000 |
 | elements (periodic table) | 150,000 |
 | elements (periodic table) | 150,000 |
 | multiplication | 200,000 |
 | multiplication | 200,000 |
+| division | 300,000 |
 | anagrams | 4,000,000 |
 | anagrams | 4,000,000 |
 
 
-Division, rounding, worded problems and the geography/history/health subjects exist or are
-stubbed but are switched off (the `0` in `$subjectsList` in `TK2.php`). To switch one on, change
-that `0` to a `1`. Worded problems (`word_problems`) would unlock at 250,000 and use whole-number
+Worded problems and the geography/history/health subjects exist or are stubbed but are switched
+off (the `0` in `$subjectsList` in `TK2.php`). To switch one on, change that `0` to a `1`; to change
+when a subject appears, change its number. Worded problems (`word_problems`) would unlock at 250,000 and use whole-number
 answers only: pillow cases, sponges, an elephant's dinner, sandwiches, marbles, a day at the beach,
 answers only: pillow cases, sponges, an elephant's dinner, sandwiches, marbles, a day at the beach,
 cupcakes and sugar bags. The numbers get bigger as the student's accuracy and number of answers grow.
 cupcakes and sugar bags. The numbers get bigger as the student's accuracy and number of answers grow.
 
 

+ 3 - 2
TK2.php

@@ -25,6 +25,7 @@
  *  - showMessage(): user name escaped
  *  - showMessage(): user name escaped
  *  - someone with neither the student nor the teacher class now sees a
  *  - someone with neither the student nor the teacher class now sees a
  *    message instead of an empty page
  *    message instead of an empty page
+ *  - division and rounding are switched on (rounding at 50,000, division at 300,000)
  *  - new word_problems subject (switched off: see $subjectsList); the old
  *  - new word_problems subject (switched off: see $subjectsList); the old
  *    worded_* subjects were mapped to the spelling generator and are gone
  *    worded_* subjects were mapped to the spelling generator and are gone
  */
  */
@@ -39,8 +40,8 @@ class TK2 {
             ['addition', 1, 10000], // Numeracy adding
             ['addition', 1, 10000], // Numeracy adding
             ['subtraction', 1, 20000], // Numeracy minus'
             ['subtraction', 1, 20000], // Numeracy minus'
             ['multiplication', 1, 200000], // Numeracy times'
             ['multiplication', 1, 200000], // Numeracy times'
-            ['division', 0, 0], // Numeracy deviding
-            ['rounding', 0, 0], // Numeracy rounding
+            ['division', 1, 300000], // Numeracy deviding
+            ['rounding', 1, 50000], // Numeracy rounding (money to the nearest 5 cents)
             ['word_problems', 0, 250000], // Worded maths problems: change the 0 to a 1 to switch them on
             ['word_problems', 0, 250000], // Worded maths problems: change the 0 to a 1 to switch them on
         ],
         ],
         // English
         // English

+ 64 - 12
questionGenerators/math/numeracy2.php

@@ -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 [