feat: génération pondérée des questions (les grands facteurs sortent plus souvent)
This commit is contained in:
3 files changed
+52
-3
No files matched your search
@@ -84,7 +84,7 @@ data class GameResult(
|
|||||||
Événements : `AnswerSubmitted(n)`, `Timeout`, `Tick`, `FeedbackElapsed`.
|
Événements : `AnswerSubmitted(n)`, `Timeout`, `Tick`, `FeedbackElapsed`.
|
||||||
|
|
||||||
Règles :
|
Règles :
|
||||||
- Génération : `a`, `b` tirés uniformément dans 1..`config.maxTable` (le joueur limite la difficulté en réduisant la table max) ; forme `Direct` ou `MissingFactor` au hasard (~50/50) ; interdiction de répéter exactement la même question deux fois de suite.
|
- Génération : `a`, `b` tirés dans 1..`config.maxTable` avec poids ∝ facteur (amendé le 2026-08-31 : cf. spec « Génération pondérée des questions ») ; forme `Direct` ou `MissingFactor` au hasard (~50/50) ; interdiction de répéter exactement la même question deux fois de suite.
|
||||||
- Pour `MissingFactor(a, b)` : la question s'affiche `a × ? = a*b`, la réponse attendue est `b`.
|
- Pour `MissingFactor(a, b)` : la question s'affiche `a × ? = a*b`, la réponse attendue est `b`.
|
||||||
- Réponse correcte → `Feedback` vert 1,5 s → question suivante.
|
- Réponse correcte → `Feedback` vert 1,5 s → question suivante.
|
||||||
- Réponse fausse → `Feedback` rouge 1,5 s (bonne réponse affichée) → question suivante.
|
- Réponse fausse → `Feedback` rouge 1,5 s (bonne réponse affichée) → question suivante.
|
||||||
|
|||||||
@@ -9,11 +9,21 @@ fun interface QuestionSource {
|
|||||||
class QuestionGenerator(private val random: Random = Random.Default) : QuestionSource {
|
class QuestionGenerator(private val random: Random = Random.Default) : QuestionSource {
|
||||||
override fun next(previous: Question?, maxTable: Int): Question {
|
override fun next(previous: Question?, maxTable: Int): Question {
|
||||||
while (true) {
|
while (true) {
|
||||||
val a = random.nextInt(1, maxTable + 1)
|
val a = nextFactor(maxTable)
|
||||||
val b = random.nextInt(1, maxTable + 1)
|
val b = nextFactor(maxTable)
|
||||||
val question =
|
val question =
|
||||||
if (random.nextBoolean()) Question.Direct(a, b) else Question.MissingFactor(a, b)
|
if (random.nextBoolean()) Question.Direct(a, b) else Question.MissingFactor(a, b)
|
||||||
if (question != previous) return question
|
if (question != previous) return question
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun nextFactor(maxTable: Int): Int {
|
||||||
|
val totalWeight = maxTable * (maxTable + 1) / 2
|
||||||
|
var roll = random.nextDouble() * totalWeight
|
||||||
|
for (k in 1..maxTable) {
|
||||||
|
roll -= k
|
||||||
|
if (roll < 0) return k
|
||||||
|
}
|
||||||
|
return maxTable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -58,4 +58,43 @@ class QuestionGeneratorTest {
|
|||||||
assertEquals("7 × 8 = ?", Question.Direct(7, 8).toString())
|
assertEquals("7 × 8 = ?", Question.Direct(7, 8).toString())
|
||||||
assertEquals("7 × ? = 56", Question.MissingFactor(7, 8).toString())
|
assertEquals("7 × ? = 56", Question.MissingFactor(7, 8).toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun factors(q: Question): Pair<Int, Int> = when (q) {
|
||||||
|
is Question.Direct -> q.a to q.b
|
||||||
|
is Question.MissingFactor -> q.a to q.b
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `biais progressif - les facteurs eleves reviennent plus souvent`() {
|
||||||
|
val generator = QuestionGenerator(Random(42))
|
||||||
|
var with1 = 0
|
||||||
|
var with10 = 0
|
||||||
|
repeat(5000) {
|
||||||
|
val (a, b) = factors(generator.next(previous = null, maxTable = 10))
|
||||||
|
if (a == 1 || b == 1) with1++
|
||||||
|
if (a == 10 || b == 10) with10++
|
||||||
|
}
|
||||||
|
assertTrue(with1 < with10 / 5, "with1=$with1, with10=$with10")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `les petits facteurs restent possibles`() {
|
||||||
|
val generator = QuestionGenerator(Random(42))
|
||||||
|
var with1 = 0
|
||||||
|
repeat(5000) {
|
||||||
|
val (a, b) = factors(generator.next(previous = null, maxTable = 10))
|
||||||
|
if (a == 1 || b == 1) with1++
|
||||||
|
}
|
||||||
|
assertTrue(with1 > 0, "with1=$with1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `maxTable egale a 1 - seule question possible sans boucle infinie`() {
|
||||||
|
val generator = QuestionGenerator(Random(42))
|
||||||
|
repeat(200) {
|
||||||
|
val (a, b) = factors(generator.next(previous = null, maxTable = 1))
|
||||||
|
assertEquals(1, a)
|
||||||
|
assertEquals(1, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user