# Writing a simple application: interactive quiz for multiplication table practice

I am trying to write a very simple application in Mathematica: a quiz that tests a multiplication table(let's use the 9x9 for this example). The app will display a question(e.g. 7x5), and the user has a textbox to type the answer(35 for the example). The user can simply press the enter or return key on a keyboard to proceed to another question (randomly selected).

I have been using Mathematica for years, but I have no experience with constructing an application with interactive user interface since I use it for data analysis and complex mathematical tasks (I have no experience in constructing user interface in Mathematica except some simple usages of the Manipulate function).

I would appreciate some guidance/help.

Thanks!

You can start from this, the answer is checked by Button or by pressing Enter. This may look like a lot of code. Well if your app is shor you do not need to play with Initialization and storing different elements in different variables but this is an approach that I'm using for more complicated cases, the code is readable and easy to extend.

With[{
optG = Sequence[Alignment -> {Center, Center},  BaseStyle -> {18, Bold}]},
DynamicModule[{new, pair, question, answer, ans, col = GrayLevel@.9, check,
checkB, nextB},
Dynamic[Refresh[
Deploy@Framed@
Grid[{
{checkB, nextB, SpanFromLeft}

}, optG],

None]],

Initialization :> (
question = "Calculate:";
InputField[Dynamic@ans, Number, ImageSize -> {45, 25}, Background -> Dynamic@col,
ContinuousAction -> True],
{"ReturnKeyDown" :> check[]}];

new[] := (pair = RandomInteger[{1, 10}, 2]; col = GrayLevel@.9; ans =.);
new[];

check[] := (col = If[ans == Times @@ pair, Green, Red]);

nextB = Button["Next question", new[]];
checkB = Button["Check", check[], Enabled -> Dynamic@NumberQ@ans]
)
]]


(minor edit: Refresh was forgotten by me earlier)

• what, no Manipulate? :) Mar 30, 2014 at 13:35
• @Nasser I don't like it for more complicated than one plot one parameter cases :)
– Kuba
Mar 30, 2014 at 13:37
• @user9490 works in V11, from the date I suppose it was working in V9. What version are you using?
– Kuba
Jan 11, 2017 at 21:44
• @user9490 what about Background -> Dynamic@First@List@col instead of ->Dynamic@col?
– Kuba
Jan 11, 2017 at 21:49

Here is a toy example:

qnam[q_, a_, alt_, nr_] :=
DynamicModule[
{ln, funs, var, enb, layout, cusum, vis = "", perc},
ln = Length[q];
enb = Table[Unique["e"], {ln}];
Map[(#[_] := True) &, enb];
funs = Table[Unique["f"], {ln}];
var = Table[Unique["v"], {ln}];
MapThread[(#1[u_] := If[u == #2, 1, 0]) &, {funs, a}];
#1,
Enabled -> Dynamic@#5[#2], Appearance -> "Vertical"],
Button["Lock in answer", #5[#2] := False]
}] &, {q, var, alt, funs, enb}];
cusum = Dynamic[Total@MapThread[#1[#2] &, {funs, var}]];
perc = NumberForm[
Dynamic[N@100 Total@MapThread[#1[#2] &, {funs, var}]/ln, 3], 2];
Quiet@Column[{
Grid[Partition[layout, nr]],
Row[{Button["Cumulative score",
If[! Or @@ MapThread[#1[#2] &, {enb, var}],
vis = Style[Row[{cusum/Length@q, "(", perc, "%)"}], 20, Red],
vis = ""]
], Dynamic@vis}]}, Frame -> True,
BaseStyle -> {FontFamily -> "Kartika", Blue, 20}]
]
tt[n_, na_, nr_] := Module[{r, q, a, or, an, alt, ans, res},
r = RandomInteger[{2, 12}, {n, 2}];
q = StringForm["1\[Times]2=", #[[1]], #[[2]]] & /@ r;
a = Times @@@ r;
or = Table[RandomSample[Range[na]], {n}];
Join[#1, {#2}] &, {RandomSample[DeleteCases[Range[4, 144], #],
na - 1] & /@ a, a}];
alt = MapThread[#1[[#2]] &, {an, or}];
ans = Flatten@MapThread[Position[#1, #2] &, {alt, a}];
qnam[q, ans, alt, nr]]
full[n_, na_, nr_] := DynamicModule[{res},
res = tt[n, na, nr];
Dynamic[Column[{res, Button["Reset", res = tt[n, na, nr]]}]]]


Now say you want to create a set of 12 questions: 3 x 4 (rxc) layout with 5 alternatives:

full[12, 5, 4]


And just for fun a smaller example:

I appreciate: (i) it does not tell you what the right answer is (ii)let you try again. This was just for 'fun' and if I get a chance may be I will improve.

UPDATE The following modification deal with some of the caveats. However, once you show the answers you can redo...but is there really any point?

qnamod[q_, a_, alt_, nr_] :=
DynamicModule[
{ln, funs, var, enb, layout, cusum, vis = "", perc, rr = ""},
ln = Length[q];
enb = Table[Unique["e"], {ln}];
Map[(#[_] := True) &, enb];
funs = Table[Unique["f"], {ln}];
var = Table[Unique["v"], {ln}];
MapThread[(#1[u_] := If[u == #2, 1, 0]) &, {funs, a}];
#1,
Enabled -> Dynamic@#5[#2], Appearance -> "Vertical"],
Button["Lock in answer", #5[#2] := False]
}] &, {q, var, alt, funs, enb}];
cusum = Dynamic[Total@MapThread[#1[#2] &, {funs, var}]];
perc = NumberForm[
Dynamic[N@100 Total@MapThread[#1[#2] &, {funs, var}]/ln, 3], 2];
Quiet@Column[{
Grid[Partition[layout, nr]],
Row[{Button["Cumulative score",
If[! Or @@ MapThread[#1[#2] &, {enb, var}],
vis = Style[Row[{cusum/Length@q, "(", perc, "%)"}], 20, Red],
vis = ""]
], Dynamic@vis}],
If[! Or @@ MapThread[#1[#2] &, {enb, var}],
rr = ""]], Dynamic@rr}],
Button["Try again",
Dynamic@MapThread[(#1[#2] := True) &, {enb, var}]]},
Frame -> True, BaseStyle -> {FontFamily -> "Kartika", Blue, 20}]
]
ttm[n_, na_, nr_] := Module[{r, q, a, or, an, alt, ans, res},
r = RandomInteger[{2, 12}, {n, 2}];
q = StringForm["1\[Times]2=", #[[1]], #[[2]]] & /@ r;
a = Times @@@ r;
or = Table[RandomSample[Range[na]], {n}];
Join[#1, {#2}] &, {RandomSample[DeleteCases[Range[4, 144], #],
na - 1] & /@ a, a}];
alt = MapThread[#1[[#2]] &, {an, or}];
ans = Flatten@MapThread[Position[#1, #2] &, {alt, a}];
qnamod[q, ans, alt, nr]]
fullm[n_, na_, nr_] := DynamicModule[{res},
res = ttm[n, na, nr];
Dynamic[Column[{res, Button["Reset", res = ttm[n, na, nr]]}]]]


Example:

fullm[4, 4, 2]


I did analogous, when teaching my son to calculate. It is about addition, but is easily transformable for the multiplication. Below argue01.gif and cataround.gif are two movies that I took from a site with animated icons. They were stored in the same directory and loaded before the first evaluation of the game. If you will play the code without these movies you will find a line of text in the place of the movie. The effect is that with a right answer one of these two movies has been shown and a simple melody has been played. In the case of a wrong answer it was another movie and melody.

Both the cell loading the movies and the one with the game were saved as the Initialization cells.

(* This is the first cell *)
right := 0;
wrong := 0;
Clear[cat, dummy];
cat = Import[NotebookDirectory[] <> "cataround.gif"];
dummy = Import[NotebookDirectory[] <> "argue01.gif"];
DarkRed = RGBColor[0.9, 0, 0];


This is the main code body:

g1[] := EmitSound[
Sound[{SoundNote["E", 0.5], SoundNote["C", 0.5],
SoundNote["E", 0.5], SoundNote["C", 0.5], SoundNote["F", 0.5],
SoundNote["E", 0.5], SoundNote["D", 1]}]];
g2[] := EmitSound[
Sound[{SoundNote["G", 0.2], SoundNote["G", 0.2],
SoundNote["G", 0.2], SoundNote["Eb", 7]}]];
sign = RandomChoice[{"+", "-"}];
a = RandomInteger[{0, 100}];
b = If[sign == "+", RandomInteger[{0, 100 - a}],
FromDigits[{RandomInteger[{0, IntegerDigits[a][[1]]}],
RandomInteger[{0, IntegerDigits[a][[2]]}]}]];
countOK = 0;
lst = {};
c = "??";

Panel@Column[{
Row[{

Dynamic@
Button[Style[1, 30], lst = Append[lst, 1]; c = FromDigits[lst]],
Dynamic@
Button[Style[2, 30], lst = Append[lst, 2]; c = FromDigits[lst]],
Dynamic@
Button[Style[3, 30], lst = Append[lst, 3]; c = FromDigits[lst]],

Dynamic@Button[Style[4, 30], lst = Append[lst, 4];
c = FromDigits[lst]],

Dynamic@Button[Style[5, 30], lst = Append[lst, 5];
c = FromDigits[lst]],
Dynamic@
Button[Style[6, 30], lst = Append[lst, 6]; c = FromDigits[lst]],
Dynamic@
Button[Style[7, 30], lst = Append[lst, 7]; c = FromDigits[lst]],
Dynamic@
Button[Style[8, 30], lst = Append[lst, 8]; c = FromDigits[lst]],

Dynamic@Button[Style[9, 30], lst = Append[lst, 9];
c = FromDigits[lst]],

Dynamic@Button[Style[0, 30], lst = Append[lst, 0];
c = FromDigits[lst]]

}], "",
Panel@Row[{Style[a, 28, Bold, DarkRed],
Style[sign, 28, Bold, DarkRed], Style[b, 28, Bold, DarkRed],
Style[" = ", 28, Bold, DarkRed],
Style[Dynamic[c], 28, Bold, DarkRed]}], Spacer[{10, 20}],

Button["OK", countOK++;

If[countOK > 1, wrong++;
mov = ListAnimate[dummy, AnimationRepetitions -> 1,
AnimatorElements -> None]; g2[],

If[sign == "+",

If[c == a + b, right++;
mov = ListAnimate[cat, AnimationRepetitions -> 1,
AnimatorElements -> None]; g1[], wrong++;
mov = ListAnimate[dummy, AnimationRepetitions -> 1,
AnimatorElements -> None]; g2[]],
If[c == a - b, right++;
mov = ListAnimate[cat, AnimationRepetitions -> 1,
AnimatorElements -> None]; g1[], wrong++;
mov = ListAnimate[dummy, AnimationRepetitions -> 1,
AnimatorElements -> None]; g2[]]
]
]
],
"",

Dynamic[mov], "",

Column[{
Row[{Style["Richtig:", 20], Spacer[10],
Style[Dynamic[right], 20, Bold, RGBColor[0, 0.8, 0]]}], "",
Row[{Style["Falsch:", 20], Spacer[15],
Style[Dynamic[wrong], 20, Bold, RGBColor[0.8, 0, 0]]}], "",
Row[{Style["Wiefiel?", 20], Spacer[10],
Style[Dynamic[right + wrong], 20]}]
}, Alignment -> Center],
"",

Row[{
Button[Style["Weiter", 16],
SelectionMove[EvaluationNotebook[], All, EvaluationCell];
SelectionMove[EvaluationNotebook[], Previous,
Cell] SelectionEvaluate[EvaluationNotebook[]],
ImageSize -> {100, 20}],
Spacer[10],
Button[Style["Aufhören", 16], NotebookClose[],
ImageSize -> {100, 20}]
}]
}, Alignment -> Center]


It should look as follows:

• Coming from Germany I understand "richtig" and "falsch", but for interest what language is "wiefiel"?
– Phab
Mar 31, 2014 at 8:09
• @Phab you are right, it is my mistake. It was intended to be "wieviel". That is, my son speaks mostly German, while I - mostly Russian and English Mar 31, 2014 at 9:55