Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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!

share|improve this question
1  
this section in the demonstration site has lots of similar apps demonstrations.wolfram.com/… You can see all topics here demonstrations.wolfram.com/topics.html here are some on use of multiplication table demonstrations.wolfram.com/… –  Nasser Mar 30 at 0:57
    
You may also want to look at the answers to this: Automatically check student answers in tests. In my answer there, I focused on making free-form input "secure" while still allowing symbolic input. This isn't what you need, but it's related. In my answer, the Enter key is used only to submit answers. The Tab key is used to go to the next answer. –  Jens Mar 31 at 15:53

3 Answers 3

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)

share|improve this answer
    
what, no Manipulate? :) –  Nasser Mar 30 at 13:35
3  
@Nasser I don't like it for more complicated than one plot one parameter cases :) –  Kuba Mar 30 at 13:37

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

share|improve this answer

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

share|improve this answer
    
Coming from Germany I understand "richtig" and "falsch", but for interest what language is "wiefiel"? –  Phab Mar 31 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 –  Alexei Boulbitch Mar 31 at 9:55

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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