ovos-skill-math-practice 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ovos_skill_math_practice/__init__.py +249 -0
- ovos_skill_math_practice/locale/da-dk/count_to.intent +1 -0
- ovos_skill_math_practice/locale/da-dk/count_too_high.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/counting.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/number_not_understood.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/operation_aliases.json +12 -0
- ovos_skill_math_practice/locale/da-dk/operation_not_understood.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_correct.dialog +2 -0
- ovos_skill_math_practice/locale/da-dk/quiz_finished.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_general.intent +3 -0
- ovos_skill_math_practice/locale/da-dk/quiz_incorrect.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_no_answer.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_operation.intent +2 -0
- ovos_skill_math_practice/locale/da-dk/quiz_question_add.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_question_divide.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_question_multiply.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_question_subtract.dialog +1 -0
- ovos_skill_math_practice/locale/da-dk/quiz_table.intent +2 -0
- ovos_skill_math_practice/locale/da-dk/recite_table.intent +2 -0
- ovos_skill_math_practice/locale/da-dk/skill.json +23 -0
- ovos_skill_math_practice/locale/da-dk/table_row.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/count_to.intent +1 -0
- ovos_skill_math_practice/locale/en-us/count_too_high.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/counting.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/number_not_understood.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/operation_aliases.json +9 -0
- ovos_skill_math_practice/locale/en-us/operation_not_understood.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_correct.dialog +2 -0
- ovos_skill_math_practice/locale/en-us/quiz_finished.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_general.intent +3 -0
- ovos_skill_math_practice/locale/en-us/quiz_incorrect.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_no_answer.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_operation.intent +2 -0
- ovos_skill_math_practice/locale/en-us/quiz_question_add.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_question_divide.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_question_multiply.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_question_subtract.dialog +1 -0
- ovos_skill_math_practice/locale/en-us/quiz_table.intent +2 -0
- ovos_skill_math_practice/locale/en-us/recite_table.intent +2 -0
- ovos_skill_math_practice/locale/en-us/skill.json +23 -0
- ovos_skill_math_practice/locale/en-us/table_row.dialog +1 -0
- ovos_skill_math_practice/version.py +5 -0
- ovos_skill_math_practice-0.0.1.dist-info/METADATA +104 -0
- ovos_skill_math_practice-0.0.1.dist-info/RECORD +48 -0
- ovos_skill_math_practice-0.0.1.dist-info/WHEEL +5 -0
- ovos_skill_math_practice-0.0.1.dist-info/entry_points.txt +2 -0
- ovos_skill_math_practice-0.0.1.dist-info/licenses/LICENSE +674 -0
- ovos_skill_math_practice-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""
|
|
2
|
+
skill OVOS Math Practice
|
|
3
|
+
Copyright (C) 2026 Andreas Lorensen
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Math practice for kids (and anyone else) - three distinct modes,
|
|
21
|
+
deliberately kept separate rather than folded into one "do math"
|
|
22
|
+
intent:
|
|
23
|
+
|
|
24
|
+
1. COUNTING - "count to ten" - pure recitation, no interaction.
|
|
25
|
+
2. TABLE RECITATION - "say the 5 times table" - pure recitation of
|
|
26
|
+
1x5 through 10x5, no interaction. Multiplication only - addition/
|
|
27
|
+
subtraction/division don't have a traditional "table" concept the
|
|
28
|
+
same way, so this mode is deliberately scoped to what "tabeller"
|
|
29
|
+
(the original request) actually means in everyday usage.
|
|
30
|
+
3. QUIZ - "quiz me on the 3 times table" / "quiz me on addition" -
|
|
31
|
+
genuinely interactive: asks NUM_QUIZ_QUESTIONS questions one at a
|
|
32
|
+
time via get_response(), checks each spoken answer, and reports a
|
|
33
|
+
final score. Covers all four basic operations, not just
|
|
34
|
+
multiplication - this was an explicit scope correction from the
|
|
35
|
+
original "times tables" framing.
|
|
36
|
+
|
|
37
|
+
ARCHITECTURE NOTE: get_response(), NOT A BACKGROUND THREAD
|
|
38
|
+
-----------------------------------------------------------------
|
|
39
|
+
Unlike ovos-skill-metronome/ovos-skill-rhythm-box/ovos-skill-white-
|
|
40
|
+
noise, quiz mode needs no background thread - it's a sequential,
|
|
41
|
+
blocking conversation (ask, wait for STT to transcribe an answer,
|
|
42
|
+
check it, ask the next one) using OVOSSkill.get_response(), which
|
|
43
|
+
already handles the listen-and-transcribe round-trip. This is
|
|
44
|
+
architecturally simpler than the audio-loop skills, but introduces a
|
|
45
|
+
different kind of failure mode: a timed-out or unparseable spoken
|
|
46
|
+
answer, handled explicitly per question rather than crashing the
|
|
47
|
+
whole quiz.
|
|
48
|
+
|
|
49
|
+
PROBLEM GENERATION DEFAULTS (state your assumptions, then build)
|
|
50
|
+
-----------------------------------------------------------------
|
|
51
|
+
- NUM_QUIZ_QUESTIONS = 5 per quiz round.
|
|
52
|
+
- Times tables: the traditional 1-12 range.
|
|
53
|
+
- Addition/subtraction: operands 1-20, subtraction always kept
|
|
54
|
+
non-negative (larger number first) - a negative answer isn't
|
|
55
|
+
wrong, but wasn't the kind of practice this was built for.
|
|
56
|
+
- Division: always divides evenly (dividend is constructed as
|
|
57
|
+
divisor x quotient, both 1-10) - no fractional answers, since
|
|
58
|
+
spoken fraction-checking is a much harder problem than this skill
|
|
59
|
+
attempts to solve.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
import json
|
|
63
|
+
import random
|
|
64
|
+
from pathlib import Path
|
|
65
|
+
|
|
66
|
+
from ovos_workshop.skills import OVOSSkill
|
|
67
|
+
from ovos_workshop.decorators import intent_handler
|
|
68
|
+
from ovos_number_parser import extract_number
|
|
69
|
+
|
|
70
|
+
NUM_QUIZ_QUESTIONS = 5
|
|
71
|
+
COUNT_MAX = 100 # sanity cap for "count to X" - not a hard product limit, just avoids absurdly long recitations
|
|
72
|
+
TABLE_MIN, TABLE_MAX = 1, 12
|
|
73
|
+
ADD_SUB_MIN, ADD_SUB_MAX = 1, 20
|
|
74
|
+
DIVIDE_FACTOR_MIN, DIVIDE_FACTOR_MAX = 1, 10
|
|
75
|
+
|
|
76
|
+
OPERATIONS = ["add", "subtract", "multiply", "divide"]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def generate_problem(operation, table=None):
|
|
80
|
+
"""Returns (a, b, correct_answer) for the given operation.
|
|
81
|
+
`table` restricts multiplication to a specific times table (the
|
|
82
|
+
fixed factor); ignored for other operations."""
|
|
83
|
+
if operation == "multiply":
|
|
84
|
+
a = table if table else random.randint(2, TABLE_MAX)
|
|
85
|
+
b = random.randint(1, 10)
|
|
86
|
+
return a, b, a * b
|
|
87
|
+
elif operation == "add":
|
|
88
|
+
a = random.randint(ADD_SUB_MIN, ADD_SUB_MAX)
|
|
89
|
+
b = random.randint(ADD_SUB_MIN, ADD_SUB_MAX)
|
|
90
|
+
return a, b, a + b
|
|
91
|
+
elif operation == "subtract":
|
|
92
|
+
a = random.randint(ADD_SUB_MIN, ADD_SUB_MAX)
|
|
93
|
+
b = random.randint(ADD_SUB_MIN, ADD_SUB_MAX)
|
|
94
|
+
if b > a:
|
|
95
|
+
a, b = b, a # keep the result non-negative, see module docstring
|
|
96
|
+
return a, b, a - b
|
|
97
|
+
elif operation == "divide":
|
|
98
|
+
divisor = random.randint(DIVIDE_FACTOR_MIN, DIVIDE_FACTOR_MAX)
|
|
99
|
+
quotient = random.randint(DIVIDE_FACTOR_MIN, DIVIDE_FACTOR_MAX)
|
|
100
|
+
dividend = divisor * quotient
|
|
101
|
+
return dividend, divisor, quotient
|
|
102
|
+
raise ValueError(f"unknown operation: {operation!r}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def multiplication_table(n, up_to=10):
|
|
106
|
+
"""Returns [(1, n, 1*n), (2, n, 2*n), ..., (up_to, n, up_to*n)] -
|
|
107
|
+
the actual (factor, table_number, product) rows of a times table,
|
|
108
|
+
for both reciting and quizzing."""
|
|
109
|
+
return [(i, n, i * n) for i in range(1, up_to + 1)]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
SKILL_ROOT = Path(__file__).resolve().parent
|
|
113
|
+
LOCALE_DIR = SKILL_ROOT / "locale"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_operation_aliases_from_disk():
|
|
117
|
+
"""locale/<lang>/operation_aliases.json - {spoken operation name:
|
|
118
|
+
key in OPERATIONS}. Same JSON-in-locale convention as the rest of
|
|
119
|
+
this project family."""
|
|
120
|
+
merged = {}
|
|
121
|
+
if not LOCALE_DIR.is_dir():
|
|
122
|
+
return merged
|
|
123
|
+
for lang_dir in sorted(LOCALE_DIR.iterdir()):
|
|
124
|
+
if not lang_dir.is_dir():
|
|
125
|
+
continue
|
|
126
|
+
alias_file = lang_dir / "operation_aliases.json"
|
|
127
|
+
if not alias_file.exists():
|
|
128
|
+
continue
|
|
129
|
+
with open(alias_file, encoding="utf-8") as f:
|
|
130
|
+
aliases = json.load(f)
|
|
131
|
+
lang = lang_dir.name.lower()
|
|
132
|
+
merged[lang] = {k: v for k, v in aliases.items() if not k.startswith("_")}
|
|
133
|
+
return merged
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
OPERATION_ALIASES = _load_operation_aliases_from_disk()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class MathPractice(OVOSSkill):
|
|
140
|
+
|
|
141
|
+
def _speak_number_not_understood(self):
|
|
142
|
+
self.speak_dialog("number_not_understood")
|
|
143
|
+
|
|
144
|
+
def _operation_aliases_for(self, lang):
|
|
145
|
+
lang = lang.lower()
|
|
146
|
+
return OPERATION_ALIASES.get(lang) or OPERATION_ALIASES.get("en-us", {})
|
|
147
|
+
|
|
148
|
+
def _resolve_operation(self, raw, lang):
|
|
149
|
+
"""Exact match only, no fuzzy matching - same reasoning as
|
|
150
|
+
every other alias resolver in this project family: quizzing
|
|
151
|
+
someone on the wrong operation is a more confusing wrong
|
|
152
|
+
answer than a slightly mis-parsed number."""
|
|
153
|
+
if not raw:
|
|
154
|
+
return None
|
|
155
|
+
return self._operation_aliases_for(lang).get(raw.strip().lower())
|
|
156
|
+
|
|
157
|
+
# ------------------------------------------------------------------
|
|
158
|
+
# Counting
|
|
159
|
+
# ------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
@intent_handler("count_to.intent")
|
|
162
|
+
def handle_count_to(self, message):
|
|
163
|
+
n_raw = message.data.get("number")
|
|
164
|
+
n = extract_number(n_raw, lang=self.lang) if n_raw else None
|
|
165
|
+
if n is False or n is None or n < 1:
|
|
166
|
+
self._speak_number_not_understood()
|
|
167
|
+
return
|
|
168
|
+
n = int(n)
|
|
169
|
+
if n > COUNT_MAX:
|
|
170
|
+
self.speak_dialog("count_too_high", {"max": COUNT_MAX})
|
|
171
|
+
return
|
|
172
|
+
numbers = ", ".join(str(i) for i in range(1, n + 1))
|
|
173
|
+
self.speak_dialog("counting", {"numbers": numbers})
|
|
174
|
+
|
|
175
|
+
# ------------------------------------------------------------------
|
|
176
|
+
# Table recitation
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
def _render_table_recitation(self, n):
|
|
180
|
+
"""Builds the full 'one times N is ..., two times N is ...'
|
|
181
|
+
recitation by rendering table_row.dialog once per row (with
|
|
182
|
+
that row's own data) rather than speaking each row as a
|
|
183
|
+
separate utterance - keeps the per-row phrasing localized in
|
|
184
|
+
table_row.dialog while still producing one continuous spoken
|
|
185
|
+
response."""
|
|
186
|
+
lines = []
|
|
187
|
+
for i, table_n, product in multiplication_table(n):
|
|
188
|
+
rendered = self.resources.load_dialog_file(
|
|
189
|
+
"table_row", {"i": i, "n": table_n, "product": product})
|
|
190
|
+
lines.append(rendered[0])
|
|
191
|
+
return ". ".join(lines)
|
|
192
|
+
|
|
193
|
+
@intent_handler("recite_table.intent")
|
|
194
|
+
def handle_recite_table(self, message):
|
|
195
|
+
n_raw = message.data.get("number")
|
|
196
|
+
n = extract_number(n_raw, lang=self.lang) if n_raw else None
|
|
197
|
+
if n is False or n is None or n < 1:
|
|
198
|
+
self._speak_number_not_understood()
|
|
199
|
+
return
|
|
200
|
+
n = int(n)
|
|
201
|
+
self.speak(self._render_table_recitation(n))
|
|
202
|
+
|
|
203
|
+
# ------------------------------------------------------------------
|
|
204
|
+
# Quiz
|
|
205
|
+
# ------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
def _ask_question(self, operation, a, b):
|
|
208
|
+
"""Speaks the question and listens for a spoken answer -
|
|
209
|
+
returns the transcribed text, or None on timeout/no response."""
|
|
210
|
+
return self.get_response(dialog=f"quiz_question_{operation}", data={"a": a, "b": b})
|
|
211
|
+
|
|
212
|
+
def _run_quiz(self, operation, table=None):
|
|
213
|
+
correct_count = 0
|
|
214
|
+
for _ in range(NUM_QUIZ_QUESTIONS):
|
|
215
|
+
a, b, answer = generate_problem(operation, table)
|
|
216
|
+
response_text = self._ask_question(operation, a, b)
|
|
217
|
+
if response_text is None:
|
|
218
|
+
self.speak_dialog("quiz_no_answer")
|
|
219
|
+
continue
|
|
220
|
+
user_value = extract_number(response_text, lang=self.lang)
|
|
221
|
+
if user_value is not False and user_value == answer:
|
|
222
|
+
correct_count += 1
|
|
223
|
+
self.speak_dialog("quiz_correct")
|
|
224
|
+
else:
|
|
225
|
+
self.speak_dialog("quiz_incorrect", {"answer": answer})
|
|
226
|
+
self.speak_dialog("quiz_finished", {"correct": correct_count, "total": NUM_QUIZ_QUESTIONS})
|
|
227
|
+
|
|
228
|
+
@intent_handler("quiz_table.intent")
|
|
229
|
+
def handle_quiz_table(self, message):
|
|
230
|
+
n_raw = message.data.get("number")
|
|
231
|
+
n = extract_number(n_raw, lang=self.lang) if n_raw else None
|
|
232
|
+
if n is False or n is None or n < 1:
|
|
233
|
+
self._speak_number_not_understood()
|
|
234
|
+
return
|
|
235
|
+
self._run_quiz("multiply", table=int(n))
|
|
236
|
+
|
|
237
|
+
@intent_handler("quiz_operation.intent")
|
|
238
|
+
def handle_quiz_operation(self, message):
|
|
239
|
+
operation_raw = message.data.get("operation")
|
|
240
|
+
operation = self._resolve_operation(operation_raw, self.lang) if operation_raw else None
|
|
241
|
+
if operation_raw and operation is None:
|
|
242
|
+
self.speak_dialog("operation_not_understood", {"operation": operation_raw})
|
|
243
|
+
return
|
|
244
|
+
operation = operation or random.choice(OPERATIONS)
|
|
245
|
+
self._run_quiz(operation)
|
|
246
|
+
|
|
247
|
+
@intent_handler("quiz_general.intent")
|
|
248
|
+
def handle_quiz_general(self, message):
|
|
249
|
+
self._run_quiz(random.choice(OPERATIONS))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tæl til {number}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
det er mange tal, lad os holde os til {max} eller derunder
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{numbers}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jeg fangede ikke det tal
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_notes": [
|
|
3
|
+
"Standard danske matematiske fagtermer, høj tillid - ikke specialiseret jargon der kan staves forkert på samme måde som rytmeboks' genre-navne."
|
|
4
|
+
],
|
|
5
|
+
"addition": "add",
|
|
6
|
+
"plus": "add",
|
|
7
|
+
"subtraktion": "subtract",
|
|
8
|
+
"minus": "subtract",
|
|
9
|
+
"multiplikation": "multiply",
|
|
10
|
+
"gange": "multiply",
|
|
11
|
+
"division": "divide"
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jeg har ikke en quiz til {operation}, jeg kender addition, subtraktion, multiplikation og division
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
du fik {correct} ud af {total} rigtige
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ikke helt, svaret er {answer}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jeg hørte ikke et svar, lad os gå videre
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hvad er {a} plus {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hvad er {a} divideret med {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hvad er {a} gange {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hvad er {a} minus {b}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "ovos-skill-math-practice.andlo",
|
|
3
|
+
"source": "https://github.com/andlo/ovos-skill-math-practice",
|
|
4
|
+
"package_name": "ovos-skill-math-practice",
|
|
5
|
+
"pip_spec": "ovos-skill-math-practice",
|
|
6
|
+
"license": "GPL-3.0-or-later",
|
|
7
|
+
"author": "andlo",
|
|
8
|
+
"name": "Regnetræning",
|
|
9
|
+
"description": "Regnetræning til børn - tælle, tabel-recitation, og interaktive quizzer i plus, minus, gange og division. Fuldstændig offline.",
|
|
10
|
+
"examples": [
|
|
11
|
+
"tæl til ti",
|
|
12
|
+
"sig 5 tabellen",
|
|
13
|
+
"quiz mig i 3 tabellen"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"matematik",
|
|
17
|
+
"undervisning",
|
|
18
|
+
"tabeller",
|
|
19
|
+
"quiz",
|
|
20
|
+
"børn"
|
|
21
|
+
],
|
|
22
|
+
"icon": "https://raw.githubusercontent.com/andlo/ovos-skill-math-practice/main/icon.png"
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{i} gange {n} er {product}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
count to {number}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
that's a lot of counting, let's keep it to {max} or under
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{numbers}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
I didn't catch that number
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
I don't have a quiz for {operation}, I know addition, subtraction, multiplication, and division
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
you got {correct} out of {total} right
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
not quite, the answer is {answer}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
I didn't hear an answer, let's move on
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
what is {a} plus {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
what is {a} divided by {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
what is {a} times {b}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
what is {a} minus {b}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "ovos-skill-math-practice.andlo",
|
|
3
|
+
"source": "https://github.com/andlo/ovos-skill-math-practice",
|
|
4
|
+
"package_name": "ovos-skill-math-practice",
|
|
5
|
+
"pip_spec": "ovos-skill-math-practice",
|
|
6
|
+
"license": "GPL-3.0-or-later",
|
|
7
|
+
"author": "andlo",
|
|
8
|
+
"name": "Math Practice",
|
|
9
|
+
"description": "Math practice for kids - counting, times table recitation, and interactive quizzes across addition, subtraction, multiplication, and division. Fully offline.",
|
|
10
|
+
"examples": [
|
|
11
|
+
"count to ten",
|
|
12
|
+
"say the 5 times table",
|
|
13
|
+
"quiz me on the 3 times table"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"math",
|
|
17
|
+
"education",
|
|
18
|
+
"times tables",
|
|
19
|
+
"quiz",
|
|
20
|
+
"kids"
|
|
21
|
+
],
|
|
22
|
+
"icon": "https://raw.githubusercontent.com/andlo/ovos-skill-math-practice/main/icon.png"
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{i} times {n} is {product}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ovos-skill-math-practice
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Math practice for OVOS - counting, times tables, and arithmetic quizzes across add/subtract/multiply/divide, fully offline
|
|
5
|
+
Home-page: https://github.com/andlo/ovos-skill-math-practice
|
|
6
|
+
Author: Andreas Lorensen
|
|
7
|
+
Author-email: andlo@outlook.dk
|
|
8
|
+
License: GPL-3.0-or-later
|
|
9
|
+
Project-URL: Source, https://github.com/andlo/ovos-skill-math-practice
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/andlo/ovos-skill-math-practice/issues
|
|
11
|
+
Keywords: ovos skill voice assistant math education times tables quiz kids
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: ovos-number-parser
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: keywords
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
Dynamic: project-url
|
|
30
|
+
Dynamic: requires-dist
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
Dynamic: summary
|
|
33
|
+
|
|
34
|
+
# <img src='icon.png' card_color='#DB4062' width='50' height='50' style='vertical-align:bottom'/> Math Practice
|
|
35
|
+
|
|
36
|
+
Math practice for kids (and anyone else) - counting, times table
|
|
37
|
+
recitation, and interactive arithmetic quizzes across all four basic
|
|
38
|
+
operations. Fully offline.
|
|
39
|
+
|
|
40
|
+
[](https://github.com/andlo/ovos-skill-math-practice/actions/workflows/test.yml)
|
|
41
|
+
[](https://pypi.org/project/ovos-skill-math-practice/)
|
|
42
|
+
|
|
43
|
+
## Three separate modes, deliberately not one "do math" intent
|
|
44
|
+
|
|
45
|
+
1. **Counting** - `"count to ten"` - pure recitation, no interaction.
|
|
46
|
+
2. **Table recitation** - `"say the 5 times table"` - pure recitation
|
|
47
|
+
of 1×5 through 10×5, no interaction. Multiplication only -
|
|
48
|
+
addition/subtraction/division don't have a traditional "table"
|
|
49
|
+
concept the same way, so this stays scoped to what "times table"
|
|
50
|
+
actually means.
|
|
51
|
+
3. **Quiz** - `"quiz me on the 3 times table"` / `"quiz me on
|
|
52
|
+
addition"` - genuinely interactive: asks 5 questions one at a
|
|
53
|
+
time, listens for a spoken answer, and reports a final score.
|
|
54
|
+
Covers all four basic operations, not just multiplication - this
|
|
55
|
+
was an explicit scope correction from the original "times tables"
|
|
56
|
+
framing during design.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
```
|
|
60
|
+
"count to ten"
|
|
61
|
+
"say the 5 times table"
|
|
62
|
+
"quiz me on the 3 times table"
|
|
63
|
+
"quiz me on addition"
|
|
64
|
+
"give me a math quiz"
|
|
65
|
+
"tæl til ti" (Danish)
|
|
66
|
+
"sig 5 tabellen" (Danish)
|
|
67
|
+
"quiz mig i 3 tabellen" (Danish)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## How the quiz works
|
|
71
|
+
|
|
72
|
+
Unlike [ovos-skill-metronome](https://github.com/andlo/ovos-skill-metronome)/
|
|
73
|
+
[ovos-skill-rhythm-box](https://github.com/andlo/ovos-skill-rhythm-box)/
|
|
74
|
+
[ovos-skill-white-noise](https://github.com/andlo/ovos-skill-white-noise),
|
|
75
|
+
quiz mode needs no background thread - it's a sequential, blocking
|
|
76
|
+
conversation using `OVOSSkill.get_response()`, which already handles
|
|
77
|
+
the listen-and-transcribe round-trip. Each of the 5 questions is
|
|
78
|
+
asked, answered (or timed out), and scored in turn; a timed-out or
|
|
79
|
+
unparseable answer counts as incorrect rather than crashing the quiz.
|
|
80
|
+
|
|
81
|
+
## Problem generation defaults
|
|
82
|
+
|
|
83
|
+
- 5 questions per quiz round.
|
|
84
|
+
- Times tables: the traditional 1-12 range.
|
|
85
|
+
- Addition/subtraction: operands 1-20, subtraction always kept
|
|
86
|
+
non-negative (larger number first).
|
|
87
|
+
- Division: always divides evenly (built as divisor × quotient, both
|
|
88
|
+
1-10) - no fractional answers, since spoken fraction-checking is a
|
|
89
|
+
much harder problem than this skill attempts to solve.
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
```bash
|
|
93
|
+
pip install ovos-skill-math-practice
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
See [DEVELOPMENT.md](DEVELOPMENT.md).
|
|
99
|
+
|
|
100
|
+
## Category
|
|
101
|
+
**Education**
|
|
102
|
+
|
|
103
|
+
## Tags
|
|
104
|
+
#math #education #times-tables #quiz #kids
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
ovos_skill_math_practice/__init__.py,sha256=zOI8mAfA7uhoyuPXhfKPTVAvNGaZ_w5rftS5XouNQd4,10483
|
|
2
|
+
ovos_skill_math_practice/version.py,sha256=M2FhGAK7lylNEcv-WWrBVqshZbkWwWxoIBOifaVfFYM,92
|
|
3
|
+
ovos_skill_math_practice/locale/da-dk/count_to.intent,sha256=lYVXNZ6L11rsuOsb-k6BwTwr2UUg3TgprF7g3V2mM0U,18
|
|
4
|
+
ovos_skill_math_practice/locale/da-dk/count_too_high.dialog,sha256=q2x0P2osG1-EHzepqvkid9PGrIGbmASEXTvXyvbNyJo,59
|
|
5
|
+
ovos_skill_math_practice/locale/da-dk/counting.dialog,sha256=gfLI4XGSLTQGplsJ_ojKeytdBccPWDMel6UdMtdfv5k,10
|
|
6
|
+
ovos_skill_math_practice/locale/da-dk/number_not_understood.dialog,sha256=hV9qlFmc9DF_REaiHtpEnTTT4yRyJkMmfzZsluaUE0M,25
|
|
7
|
+
ovos_skill_math_practice/locale/da-dk/operation_aliases.json,sha256=KX21WpB4ApXgKBjbzrI8CaKDGePfsV4boQCkaKfDdI8,343
|
|
8
|
+
ovos_skill_math_practice/locale/da-dk/operation_not_understood.dialog,sha256=q0-WAshgJcwR1Qlt4UDtp_nAsPdN1FmfQr79LZdzkh8,99
|
|
9
|
+
ovos_skill_math_practice/locale/da-dk/quiz_correct.dialog,sha256=Jg4vnhRmKhDXyYKLarojqRBUw5IE0JjNZ1PwAiZPHPU,23
|
|
10
|
+
ovos_skill_math_practice/locale/da-dk/quiz_finished.dialog,sha256=dklMETDlLv-NyXMvc7v27e8JTz7dl_IHhNvv-4ZgHG8,39
|
|
11
|
+
ovos_skill_math_practice/locale/da-dk/quiz_general.intent,sha256=LMqTvaO6dpaf3nodB-deNoGyHpTMIjuZLW47ssRkCX4,65
|
|
12
|
+
ovos_skill_math_practice/locale/da-dk/quiz_incorrect.dialog,sha256=D_4ru91Ct8jdAflbHaDwBMJQdr3p_ElvInckpRBVRow,30
|
|
13
|
+
ovos_skill_math_practice/locale/da-dk/quiz_no_answer.dialog,sha256=W9zOsqqWzXxs3yMcJ-lwFi7X-UAgNAS-6-YcPFIsUwA,43
|
|
14
|
+
ovos_skill_math_practice/locale/da-dk/quiz_operation.intent,sha256=pH6bUQhfgHLMyM6zxxaG6edTRj07wDE6dszlt4M-kxk,46
|
|
15
|
+
ovos_skill_math_practice/locale/da-dk/quiz_question_add.dialog,sha256=3w4JPwFfxCWXpyqMhM5lZVgc05JWNHYfGziHA5KtDUc,21
|
|
16
|
+
ovos_skill_math_practice/locale/da-dk/quiz_question_divide.dialog,sha256=KRfzbhHng3CLZVz2dVPh3XSS3xTeZCQ6h7hiVhZQYU4,30
|
|
17
|
+
ovos_skill_math_practice/locale/da-dk/quiz_question_multiply.dialog,sha256=A8sios0UeNwm7c7Hqczf0YuaRQigQd8H6H9udYYARng,22
|
|
18
|
+
ovos_skill_math_practice/locale/da-dk/quiz_question_subtract.dialog,sha256=8-n_M-6JrGJvmXX2w5Tw_3kHgevrEkXnVXTLjpJbAqA,22
|
|
19
|
+
ovos_skill_math_practice/locale/da-dk/quiz_table.intent,sha256=myeqogwCOLwwtLzvQFYgWrHYGVKCzhX3HKMqoLla20w,58
|
|
20
|
+
ovos_skill_math_practice/locale/da-dk/recite_table.intent,sha256=svOjHqNWpjdgEwbcPqWalwc28m4pUk30YrIFUceclrk,49
|
|
21
|
+
ovos_skill_math_practice/locale/da-dk/skill.json,sha256=QMH-6pYeRsGBG-xtqJ1FQoh9Fkq3mTBhwNGbkc1blec,712
|
|
22
|
+
ovos_skill_math_practice/locale/da-dk/table_row.dialog,sha256=Xj-vAbD2njlm4IOPDG7NcRNNVSYXYDNu-k5lRneD8v0,27
|
|
23
|
+
ovos_skill_math_practice/locale/en-us/count_to.intent,sha256=pqMZSTfUErRz60H0f7M0lCkhmglFDOgZ1aTwpZYG2yU,18
|
|
24
|
+
ovos_skill_math_practice/locale/en-us/count_too_high.dialog,sha256=JWfk0Uq3S_GxR9uUZFv5jy989OrtciGPHIOlaYtw-wg,58
|
|
25
|
+
ovos_skill_math_practice/locale/en-us/counting.dialog,sha256=gfLI4XGSLTQGplsJ_ojKeytdBccPWDMel6UdMtdfv5k,10
|
|
26
|
+
ovos_skill_math_practice/locale/en-us/number_not_understood.dialog,sha256=XakJMgZYy5uJPC_iTLAbSMGpzWIxVl_1hPPRUuoNv_g,27
|
|
27
|
+
ovos_skill_math_practice/locale/en-us/operation_aliases.json,sha256=x8pUdqdlb6IakeQzO_8eXimfs3dzy_-buQHot37Xp-Y,218
|
|
28
|
+
ovos_skill_math_practice/locale/en-us/operation_not_understood.dialog,sha256=dKhQriK27fhaGB0oFF9aaA5Vv5uW9-t0lJ9PnhHXcmc,96
|
|
29
|
+
ovos_skill_math_practice/locale/en-us/quiz_correct.dialog,sha256=DXo8P7llSivV0cC_KF43EFFOmuD_h85wedymaWFUwNs,21
|
|
30
|
+
ovos_skill_math_practice/locale/en-us/quiz_finished.dialog,sha256=07xE1N1wpJ8a2qMNecZjDPraNpu2m4a1YOqNae8gPaU,39
|
|
31
|
+
ovos_skill_math_practice/locale/en-us/quiz_general.intent,sha256=VT58otFMT0dZ4LwKvH8RMhloGeR_U7v9K6IDMV4cwvA,59
|
|
32
|
+
ovos_skill_math_practice/locale/en-us/quiz_incorrect.dialog,sha256=nirA2mWALuejTNEtZ-MS-e1VA3IYdnYsnkVzzLbPdDQ,34
|
|
33
|
+
ovos_skill_math_practice/locale/en-us/quiz_no_answer.dialog,sha256=zsQHQBkJ1fyI1wJ_F2WTDCaX2pRndWmPF714oec_5dU,39
|
|
34
|
+
ovos_skill_math_practice/locale/en-us/quiz_operation.intent,sha256=dDAfx2tqZUsCmlwD7OCMY-yLOqMOMZIrLQJa2U9Tkj0,46
|
|
35
|
+
ovos_skill_math_practice/locale/en-us/quiz_question_add.dialog,sha256=u9f2mn858sVdm4qsFx3sMviOd0hkp6_NSa9i1ONX4f8,21
|
|
36
|
+
ovos_skill_math_practice/locale/en-us/quiz_question_divide.dialog,sha256=PKwuAGo_Z_2zEqrBR5QK7a302tth_BIADtHfP7L354s,27
|
|
37
|
+
ovos_skill_math_practice/locale/en-us/quiz_question_multiply.dialog,sha256=-eZWX1Y60SQKTGQX2TovNrBgGmHzOb0mzy_7mcm8MBg,22
|
|
38
|
+
ovos_skill_math_practice/locale/en-us/quiz_question_subtract.dialog,sha256=7X9lpuiUU3Iu7buTRVZjFlmxnyKG9OJyX1PyMo8nGts,22
|
|
39
|
+
ovos_skill_math_practice/locale/en-us/quiz_table.intent,sha256=_Lsgq88JK7SinsoJx57vkoyMmXC9yXh5goMBH48Vjqo,72
|
|
40
|
+
ovos_skill_math_practice/locale/en-us/recite_table.intent,sha256=LA6NkN5Dva18jnkycfW3Vp46FesoocaNMLeySA8YxLQ,61
|
|
41
|
+
ovos_skill_math_practice/locale/en-us/skill.json,sha256=kkewsKJ3OsiKIPy4G8SzgSMQdrgAwMfMhtLCNX97Kqs,749
|
|
42
|
+
ovos_skill_math_practice/locale/en-us/table_row.dialog,sha256=d-sEoj-mLIoIY0-NzYNZSUtyP5iPdJZ39yNS2MwMlwQ,27
|
|
43
|
+
ovos_skill_math_practice-0.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
44
|
+
ovos_skill_math_practice-0.0.1.dist-info/METADATA,sha256=IyXodZHmYgl9DXfDo_jydoeMIgEqU2Dlx-5xsmxnt9g,3969
|
|
45
|
+
ovos_skill_math_practice-0.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
46
|
+
ovos_skill_math_practice-0.0.1.dist-info/entry_points.txt,sha256=3gEKvQCddojMhK91ibbJOwSdBAv2EyKHyjHEwYAuNOA,91
|
|
47
|
+
ovos_skill_math_practice-0.0.1.dist-info/top_level.txt,sha256=1wRuCqOnY3r4yst3hHZr3nxd5SB5na_0HmezfMbQ2wQ,25
|
|
48
|
+
ovos_skill_math_practice-0.0.1.dist-info/RECORD,,
|