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$
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 = GrayLevel@.9, 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 = 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]
    )
  ]]

enter image description here

(minor edit: Refresh was forgotten by me earlier)

$\endgroup$
  • $\begingroup$ what, no Manipulate? :) $\endgroup$ – Nasser Mar 30 '14 at 13:35
  • 3
    $\begingroup$ @Nasser I don't like it for more complicated than one plot one parameter cases :) $\endgroup$ – Kuba Mar 30 '14 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 '17 at 21:44
  • $\begingroup$ @user9490 what about Background -> Dynamic@First@List@col instead of ->Dynamic@col? $\endgroup$ – Kuba Jan 11 '17 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$
  • $\begingroup$ Coming from Germany I understand "richtig" and "falsch", but for interest what language is "wiefiel"? $\endgroup$ – Phab Mar 31 '14 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$ – Alexei Boulbitch Mar 31 '14 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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