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

7
$\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$
7
  • $\begingroup$ what, no Manipulate? :) $\endgroup$
    – Nasser
    Commented 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
    Commented 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
    Commented Jan 11, 2017 at 21:44
  • 1
    $\begingroup$ @Alan it seems that at least from 13.3 InputField does not respect the Background options. I will report it. In a meanwhile you can replace Background with BaseStyle and make the font colored. $\endgroup$
    – Kuba
    Commented Mar 14 at 18:54
  • 1
    $\begingroup$ @Alan hmm, it seems that it will work with Background if you set Appearance->Framed for example. $\endgroup$
    – Kuba
    Commented Mar 14 at 18:56
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
    Commented 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$ Commented Mar 31, 2014 at 9:55

Your Answer

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

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