2
$\begingroup$

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!

$\endgroup$
2

3 Answers 3

6
$\begingroup$

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 = [email protected], check,
                checkB, nextB},
  Dynamic[Refresh[
   Deploy@Framed@
     Grid[{
       {question, Dynamic@Row[pair, "\[Cross]"], "=", answer},
       {checkB, nextB, SpanFromLeft}

       }, optG],

   None]],

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

    new[] := (pair = RandomInteger[{1, 10}, 2]; col = [email protected]; ans =.);
    new[];

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

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

enter image description here

(minor edit: Refresh was forgotten by me earlier)

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

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}];
  layout = MapThread[Column[{
       #1,
       RadioButtonBar[Dynamic@#2, Thread[Range[Length@#3] -> #3], 
        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}];
  an = MapThread[
    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]

enter image description here

And just for fun a smaller example:

enter image description here

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}];
  layout = MapThread[Column[{
       #1,
       RadioButtonBar[Dynamic@#2, Thread[Range[Length@#3] -> #3], 
        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}], 
     Column[{Button["Answers", 
        If[! Or @@ MapThread[#1[#2] &, {enb, var}], 
         rr = Grid[Thread[{q, MapThread[#1[[#2]] &, {alt, a}]}]], 
         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}];
  an = MapThread[
    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]

enter image description here

$\endgroup$
2
$\begingroup$

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.

Please find the program below

(* 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: enter image description here

$\endgroup$
2
  • $\begingroup$ Coming from Germany I understand "richtig" and "falsch", but for interest what language is "wiefiel"? $\endgroup$
    – Phab
    Mar 31, 2014 at 8:09
  • $\begingroup$ @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 $\endgroup$ Mar 31, 2014 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.