Programming Home Projects with Microsoft Small Basic: Chapter 6: Flash Card Math Quiz Project
Small Basic > Small Basic Books > **Programming Home Projects with Small Basic **> 6: Flash Card Math Quiz Project
This chapter is adapted from the book Programming Home Projects with Microsoft Small Basic by Philip Conrod and Lou Tylee.
To purchase this book in its entirety, please see the Computer Science For Kids web site.
6. Flash Card Math Quiz Project
In this chapter, we build a project that lets kids (or adults) practice their basic addition, subtraction, multiplication and division skills. The Flash Card Math Quiz Project allows you to select problem type, what numbers you want to use and has three timing options. We also look at using random numbers and accepting keyboard input.
Flash Card Math Quiz Project Preview
In this chapter, we will build a flash card math program. Random math problems (selectable from addition, subtraction, multiplication, and/or division) using the numbers from 0 to 9 are presented. Timing options are available to help build both accuracy and speed.
The finished project is saved as FlashCard in the HomeSB\HomeSB Projects\FlashCard folder. Start Small Basic and open the finished project. Run the project (click Run in the toolbar or press <F5>). The flash card math program will appear as:
Many options are available. First, choose problem type from the Type box. Choose from Addition, Subtraction, Multiplication, and/or Division problems (click on your choice; you may choose more than one problem type). Choose your Factor (use the Change button), any number from 0 to 9, or choose Random for random factors. These options may be changed at any time. To practice math facts, click on the Start Practice button.
When I click Start Practice (using the default choices), I see:
You can now see there is a large display in the middle where the problem (8 + 3 =) is displayed. The program is waiting for an answer to this problem. Type your answer. If it is correct, the number next to Correct: is incremented. Whether correct or not, another problem is presented.
A few notes on entering your answer. The primary goal of the program is to build speed in solving simple problems. As such, you have one chance to enter an answer - there is no erasing. If the answer has more than two digits (the number of digits in the answer is shown using question marks), type your answer from left to right. For example, if the answer is 10, type a 1 then a 0. Try several addition problems to see how answers are entered. You can stop practicing math problems, at any time, by clicking the Stop Practice button.
Other problem types can be selected and a new factor chosen at any time. Each problem is generated randomly, based on problem type and factor value. For Addition, you are given problems using your factor as the second addend. If you choose 6 as your factor (click Change until 6 appears as the Factor), an example problem would be:
For Subtraction (click Subtraction under Type – note the added check mark) you are given problems using your factor as the subtrahend (the number being subtracted). Selecting a factor of 5, an example subtraction problem is:
For Multiplication, you are given problems using your factor as the multiplier (the number you’re multiplying by). If a factor of 9 is selected, an example multiplication problem is:
Lastly, for Division, you are given problems using your factor as the divisor (the number you are dividing by). If the selected factor is 4, a typical division problem would be:
As mentioned, you do not have to choose a specific factor – Random factors can be chosen. Try all kinds of factors with all kinds of problem types.
There is another option to consider when using the flash card math project – the corresponding option choices are in the Timer box. These options can only be selected when not solving problems. There are three choices here. If you select Off, you solve problems until you click Stop Practice. If you select On-Count Up, a timer will appear and the computer will keep track of how long you were solving problems (a maximum of 30 minutes is allowed). If you select On-Count Down, a timer will appear, along with +/- buttons. The buttons are used to set how long you want to solve problems (a maximum of 30 minutes is allowed). The timer will then count down, allowing you to solve problems until the allotted time expires.
Try the timer options if you’d like. Here’s the beginning of a run I made using the On-Count Down option (starting at 1 minute):
Once you are done practicing math problems (either you clicked Stop Practice or time ran out with the On-Count Down option), a message box appears giving you the results of your little quiz. This box tells you how many problems you solved and how many you got correct (including a percentage score). If the timer was on, you are also told how long you were solving problems and how much time (on average) you spent on each problem. Here’s the message box I saw when I finished the quiz I started above:
Click OK and you can try again. Click the Exit button in Flash Card Math when you are done solving problems.
You will now build this project in several stages. We first address window design. We discuss the controls used to build the window and establish initial properties. And, we address code design in detail. We cover random generation of problems, selection of the various program options, and how to use timing.
Flash Card Math Window Design
Here is the sketch for the window layout of the Flash Card Math program:
There’s a lot here. Two labeled text shapes are used for scoring. There’s a large text shape in the middle of the window used to display the math problem. A dividing rectangle separates the problem display from option choices and buttons. Two button controls (at the bottom of the window) are used to start and stop the problems and to exit the project. There are three rectangles used to group option choices. The first holds four “check boxes” used to select problem type. The second holds a button and display used to select numbers used in the problems. The third box holds three little circles used to select the timing option. Two small button controls (marked with – and +) are used to adjust the amount of time used in the flash card drills.
We will begin writing code for the application. We will write the code in several steps. We start with the code needed to add all the above elements to the program window. The program will be built in its default configuration: Addition problems, Random factor and timer Off. Later, we add code to change these default values.
Start a new program in Small Basic. Once started, we suggest you immediately save the program with a name you choose. This sets up the folder and file structure needed for your program.
Window Design – Scoring and Problem Display
We begin by setting up the graphics window and adding the text shapes that display the number of problems tried (TriedDisplay) and the number answered correctly (CorrectDisplay). We also add an empty text shape (ProblemDisplay, with a very large font) to display the problem to answer and draw a red, dividing rectangle. Add this code to your program:
' Flash Card Math
InitializeProgram()
Sub InitializeProgram
'graphics window
GraphicsWindow.Width = 430
GraphicsWindow.Height = 360
GraphicsWindow.Title = "Flash Card Math"
GraphicsWindow.BackgroundColor = "LightYellow"
* 'labels/scores*
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontBold = "false"
GraphicsWindow.FontSize = 24
GraphicsWindow.DrawText(40, 10, "Tried:")
GraphicsWindow.DrawText(200, 10, "Correct:")
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(110, 10, 60, 30)
GraphicsWindow.FillRectangle(300, 10, 60, 30)
GraphicsWindow.BrushColor = "Yellow"
TriedDisplay = Shapes.AddText("0")
Shapes.Move(TriedDisplay, 125, 10)
CorrectDisplay = Shapes.AddText("0")
Shapes.Move(CorrectDisplay, 315, 10)
* 'problem display*
GraphicsWindow.BrushColor = "Blue"
GraphicsWindow.FontSize = 72
ProblemDisplay = Shapes.AddText("")
Shapes.Move(ProblemDisplay, 50, 50)
* 'divider*
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(10, 160, 410, 10)
EndSub
The first line of code calls a subroutine InitializeProgram where we will put all code needed to set up the program for use. All remaining code here goes in that subroutine.
As written, you won’t see a problem since ProblemDisplay is blank. To get an idea of what a problem will look like, temporarily change the line setting the text to:
ProblemDisplay = Shapes.AddText("8 + 7 = 15")
Now, Save and Run the program. You should see:
Notice how we put the text shapes (TriedDisplay and CorrectDisplay) on red rectangles to give them some background. You may wonder how we knew to use FontSize of 72 in the problem display. There was no magic - we just tried different values until we got a nice display.
Remember to change the line setting the problem display back to:
ProblemDisplay = Shapes.AddText("")
Next, we start writing the code to choose the program options. Before doing this, add this code (in InitializeProgram) to draw and label the three blue rectangles to hold each option choice:
'problem types/factor/timer
GraphicsWindow.BrushColor = GraphicsWindow.GetColorFromRGB(192, 192, 255)
GraphicsWindow.FillRectangle(10, 180, 130, 130)
GraphicsWindow.FillRectangle(150, 180, 130, 130)
GraphicsWindow.FillRectangle(290, 180, 130, 130)
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
GraphicsWindow.FontBold = "true"
GraphicsWindow.DrawText(15, 185, "Type(s)")
GraphicsWindow.DrawText(155, 185, "Factor")
GraphicsWindow.DrawText(295, 185, "Timer")
Save and Run to see the added rectangles:
Window Design – Choosing Problem Types
In the Flash Card Math program, you can practice any of the four basic arithmetic operations: addition, subtraction, multiplication and/or division. We want to provide the user with a convenient way to choose among the problem types. Here’s a display of the method chosen:
White squares are drawn next to the choices. When a user clicks a square, if there is no check mark there, one appears (selecting that problem type). If a check mark is already there, it disappears (deselecting that problem type).
The code to implement the selection process is a bit complicated and will be discussed later. For now, we draw the display assuming only Addition problems (default choice) are available. Add this code to InitializeProgram:
'problem types
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
GraphicsWindow.FontBold = "false"
GraphicsWindow.DrawText(35, 210, "Addition (+)")
GraphicsWindow.DrawText(35, 230, "Subtraction (-)")
GraphicsWindow.DrawText(35, 250, "Multiplication (x)")
GraphicsWindow.DrawText(35, 270, "Division (/)")
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(15, 210, 15, 15)
GraphicsWindow.FillRectangle(15, 230, 15, 15)
GraphicsWindow.FillRectangle(15, 250, 15, 15)
GraphicsWindow.FillRectangle(15, 270, 15, 15)
ProblemSelected[1] = "true"
ProblemSelected[2] = "false"
ProblemSelected[3] = "false"
ProblemSelected[4] = "false"
This code draws four white squares with the appropriate labels. An array ProblemSelected is used to specify which problem types are selected. For now, only element 1 (Addition problems) is “true”.
Save and Run the program. The selection squares appear:
No check mark appears in the square indicating that Addition problems are selected. We will correct that when we write code for the selection process.
**
**
Window Design - Factor Selection
A user can choose a particular number (factor) from 0 to 9 to practice math problems or can choose a random factor. This choice is made in the Factor selection box. Like we did for the problem type selection, we simply set up the window for the default choice (Random factor), then later address the code for changing the factor.
Add this code to InitializeProgram:
'factor
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(160, 210, 110, 30)
GraphicsWindow.BrushColor = "DarkRed"
GraphicsWindow.FontSize = 20
FactorDisplay = Shapes.AddText("Random")
Shapes.Move(FactorDisplay, 180, 212)
FactorChoice = -1
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
FactorButton = Controls.AddButton("Change", 170, 245)
Controls.SetSize(FactorButton, 90, 30)
This places a text shape (FactorDisplay) on top of a white rectangle to show which factor is selected (shows Random for now). A button control (FactorButton) will be used to change this display. The variable FactorChoice keeps track of the selected factor. If it is positive, its value is the selected factor (0 to 9). When its value is -1 (the default value here), a random factor is used.
Save and Run the program to see the factor selection box:
Later, we write code to change the displayed factor when the Change button is clicked.
**
**
Window Design - Timer Options
In the Flash Card Math program, you can time your practice if you choose. There are three choices: timer Off, timer On – Count Up, timer On – Count Down. Like the problem type selection, we want to provide the user with a convenient way to choose the timer option. The difference between this selection and that of problem type is that only one choice can be made. Here’s a display of the method chosen:
White circles are drawn next to the choices. When a user clicks a circle, that circle is marked (selecting that timer option). All other choices become ‘unmarked’. When one of the timer On options is selected, timing information and buttons to change times will be displayed under the circles.
Also, like the problem type selection, the code behind selecting timing options is involved and will be discussed later. Here, we write code to build the display and use the default option of timer Off. Add this code to InitializeProgram:
'timer choices
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
GraphicsWindow.FontBold = "false"
GraphicsWindow.DrawText(315, 210, "Off")
GraphicsWindow.DrawText(315, 230, "On-Count Up")
GraphicsWindow.DrawText(315, 250, "On-Count Down")
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(295, 210, 15, 15)
GraphicsWindow.FillEllipse(295, 230, 15, 15)
GraphicsWindow.FillEllipse(295, 250, 15, 15)
TimerChoice = 0 ' 0-off, 1 -on/up, 2- on/down
GraphicsWindow.BrushColor = "DarkBlue"
GraphicsWindow.FontSize = 20
TimeDisplay = Shapes.AddText("0:30")
Shapes.Move(TimeDisplay, 335, 277)
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
TimerPlusButton = Controls.AddButton("+", 390, 275)
Controls.SetSize(TimerPlusButton, 20, 30)
TimerMinusButton = Controls.AddButton("-", 300, 275)
Controls.SetSize(TimerMinusButton, 20, 30)
This code draws the circles (ellipses) next to the labeled choices. A variable TimerChoice stores which option is selected (0-off, 1-on, count up, 2-on, count down). It is set to zero initially (timer is off). A text shape (TimeDisplay) is used to display time when the timer is used and two buttons (TimerPlusButton, TimerMinusButton) are used to adjust the timer in the count down situation.
Save and Run the program to see the new additions:
No selection mark appears in the circle indicating the Timer is Off. We will correct that when we write code for the selection process. TimeDisplay and the two adjustment buttons should not appear in the default configuration (timer Off), so add these three lines in InitializeProgram (after the code just added to create them) to hide these controls:
Shapes.HideShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
Rerun the program to make sure the controls are hidden.
**
**
Window Design - Add Buttons
The final element needed in the window are the two buttons to control the program operation. One button (StartStopButton) is used to start and stop practice of math problems. One button (ExitButton) is used to stop the program. Add this code to InitializeProgram:
'buttons
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
StartStopButton = Controls.AddButton("Start Practice", 60, 320)
Controls.SetSize(StartStopButton, 130, 30)
ExitButton = Controls.AddButton("Exit", 230, 320)
Controls.SetSize(ExitButton, 130, 30)
Save and Run the program to see the final window design:
We now start writing the code behind choosing all the options and generating and solving problems. As a first step, we write the code that generates a random problem and gets the answer from the user, updating the score.
**
**
Code Design – Start Practice
The idea of the flash card math project is to display a problem, receive an answer from the user and check for correctness. Problems can be of four different types with different factor choices and different timer options. For now, we work with Addition problems with Random factors. And, we will ignore the timer options. Once this initial code is working satisfactorily, other problem types and factors and timing will be considered. Again, this step-by-step approach to building a project is far simpler than trying to build everything at once.
Things begin by clicking the Start Practice button (StartStopButton). When this happens, the following steps are taken:
- Change caption of StartStopButton to Stop Practice.
- Hide ExitButton.
- Set number of problems tried (NumberTried) and number correct (NumberCorrect) to zero.
- Generate and display a problem in ProblemDisplay.
- Obtain answer from user.
- Check answer and update score.
Once each generated problem is answered, subsequent problems are generated and answered.
The user answers problems until he/she clicks Stop Practice (or time elapses in timed drills). The steps followed at this point are:
- Change caption of StartStopButton to Start Practice
- Show ExitButton.
- Clear ProblemDisplay.
- Present results.
The code behind the listed steps is fairly straightforward. First, add this line at the end of InitializeProgram to allow detection of button clicks:
Controls.ButtonClicked = ButtonClickedSub
And the corresponding subroutine (ButtonClickedSub) called when a button is clicked:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (B = StartStopButton) Then
StartStopButtonClicked()
ElseIf (B = ExitButton) Then
Program.End()
** EndIf**
EndSub
We have added possibilities for clicking on either StartStopButton or ExitButton. As seen, if a user clicks the ExitButton, the program simply ends.
Now, use this code in the StartStopButtonClicked subroutine (implements the steps above, except for presenting results):
Sub StartStopButtonClicked
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
Controls.SetButtonCaption(StartStopButton, "Stop Practice")
Controls.HideControl(ExitButton)
NumberTried = 0
NumberCorrect = 0
Shapes.SetText(TriedDisplay, "0")
Shapes.SetText(CorrectDisplay, "0")
GetProblem()
Else
Controls.SetButtonCaption(StartStopButton, "Start Practice")
Controls.ShowControl(ExitButton)
Shapes.SetText(ProblemDisplay, "")
EndIf
EndSub
This code uses a subroutine GetProblem to generate the random problem and display it in ProblemDisplay. Add this nearly empty procedure (we’ll fill it in soon).
Sub GetProblem
Shapes.SetText(ProblemDisplay, "Problem!!")
EndSub
Save and Run the project. Click Start Practice to make sure buttons change as planned. You will also see the generated “problem”:
Now, click Stop Practice. Make sure Exit works.
This framework seems acceptable. We continue code design by discussing problem generation and obtaining an answer (including scoring) from the user. Then, later we discuss timing and presenting the results.
Code Design – Problem Generation
To generate a problem, we examine the current options selected by the user and produce a random problem based on these selections. All code will be in the GetProblem subroutine currently in the framework code. And, even though we are only using Addition problems with Random factors in this initial design, we will write code for all possibilities.
The steps involved in generating a random flash card problem are:
- Select problem type (random selection based on checked choices in Type box)
- Generate factor (based on selection in Factor box)
- Formulate problem and determine correct answer.
- Display problem in ProblemDisplay text shape, replacing correct answer with question marks (?) in place of digits. An example of the desired form of the returned value is:
8 + 6 = ??
where question marks tell the user how many digits are in the correct answer.
- Initialize YourAnswer to blank and DigitNumber (used to check if you have typed in all the digits to the answer).
Let’s look at each step of the problem generation process. The first step is to choose a random problem type from the maximum of four possibilities. We will use a simple approach, first generating a random number from 1 to 4 (1 representing addition, 2 representing subtraction, 3 representing multiplication and 4 representing division). If the corresponding element of the ProblemSelected array is “true” (meaning that check box is checked), that will be the problem type. It that element of ProblemSelected is “false”, we choose another random number. We continue this process until a problem type is selected. Notice this approach assumes at least one check box is always selected. We will make sure this is the case when developing code for the problem type option. There are more efficient ways to choose problem type which don’t involve loops, but, for this simple problem, this works quite well.
A snippet of code that performs the choice of problem type (ProblemType) is:
ProblemType = 0
While (ProblemType = 0)
P = Math.GetRandomNumber(4)
If (P = 1 And ProblemSelected[1]) Then
'Addition
ProblemType = P
ElseIf (P = 2 And ProblemSelected[2]) Then
'Subtraction
ProblemType = P
ElseIf (P = 3 And ProblemSelected[3]) Then
'Multiplication
ProblemType = P
ElseIf (P = 4 And ProblemSelected[4]) Then
'Division
ProblemType = P
EndIf
EndWhile
Once a problem type is selected, we determine the factor used to generate a problem. It can be a selected value from 0 to 9, or a random value from 0 to 9, based on the value selected in the Factor box. For now, we assume that value is provided by a subroutine GetFactor that determines a Factor value, based on problem type ProblemType.
Each problem has four variables associated with it: Factor, representing the value returned by GetFactor, Number, the other number used in the math problem, CorrectAnswer, the problem answer, and Problem, a string representation of the unsolved problem. Once a problem type and factor have been determined, we find values for each of these variables. Each problem type has unique considerations for problem generation. Let’s look at each type.
For Addition problems (ProblemType = 1), the selected factor is the second addend in the problem. The string form of addition problems (Problem) will be:
Number + Factor =
where Number is a random value from 0 to 9, while recall Factor is the selected factor. A snippet of code to generate an addition problem and determine the CorrectAnswer is:
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number + Factor
Problem = Number + " + " + Factor + " = "
For Subtraction problems (ProblemType = 2), the factor is the subtrahend (the number being subtracted). The string form of subtraction problems (Problem) will be:
Number - Factor =
We want all the possible answers to be positive numbers between 0 and 9. Because of this, we formulate the problem in a backwards sense, generating a random answer (CorrectAnswer), then computing Number based on that answer and the known factor (Factor). The code that does this is:
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer + Factor
Problem = Number + " - " + Factor + " = "
For Multiplication problems (ProblemType = 3), the selected factor is the multiplier (the number you’re multiplying by) in the problem. The string form of multiplication problems (Problem) will be:
Number x Factor =
where Number is a random value from 0 to 9, and Factor is the factor. A snippet of code to generate a multiplication problem and the CorrectAnswer is:
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number * Factor
Problem = Number + " x " + Factor + " = "
For Division problems (ProblemType = 4), the factor is the divisor (the number doing the dividing). The string form of division problems (Problem) will be:
Number / Factor =
Like in subtraction, we want all the possible answers to be positive numbers between 0 and 9. So, we again formulate the problem in a backwards sense, generating a random answer (CorrectAnswer), then computing Number based on that answer and the known factor (Factor). The code that does this is:
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer * Factor
Problem = Number + " / " + Factor + " = "
Note with division, we must make sure the factor is never zero (can’t divide by zero).
The GetFactor routine provides the factor based on the selected value (FactorChoice) in the Factor box and problem type ProblemType. For random factors, it will make sure a zero is not returned if a division problem is being generated. The GetFactor subroutine is thus:
Sub GetFactor
If (FactorChoice = -1) Then
If (ProblemType = 4) Then
Factor = Math.GetRandomNumber(9)
Else
Factor = Math.GetRandomNumber(10) - 1
EndIf
Else
Factor = FactorChoice
EndIf
EndSub
If the Random (FactorChoice = -1) option is selected, 0 to 9 is returned for addition, subtraction and multiplication problems; 1 to 9 is returned for division problems (ProblemType = 4). In other cases, the selected factor is returned (we will have to make sure zero is not a choice when doing division).
The GetProblem subroutine is nearly complete. We want to return the Problem variable with appended question marks that represent the number of digits (NumberDigits) in the correct answer and we need to initialize YourAnswer and DigitNumber. The code snippet that does this is:
If (CorrectAnswer < 10) Then
NumberDigits = 1
Shapes.SetText(ProblemDisplay, Problem + "?")
Else
NumberDigits = 2
Shapes.SetText(ProblemDisplay, Problem + "??")
EndIf
YourAnswer = ""
DigitNumber = 1
We can now assemble all the little code snippets into a final form for the GetProblem subroutine. To build the GetProblem subroutine, first eliminate the single line of code that displays “Problem!!”. Then, start with the snippet that selects problem type. Add each problem generation segment (one for each of the four mathematical operations) in its corresponding location. Finally, add the question mark appending code. The finished function is:
Sub GetProblem
ProblemType = 0
While (ProblemType = 0)
P = Math.GetRandomNumber(4)
If (P = 1 And ProblemSelected[1]) Then
'Addition
ProblemType = P
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number + Factor
Problem = Number + " + " + Factor + " = "
ElseIf (P = 2 And ProblemSelected[2]) Then
'Subtraction
ProblemType = P
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer + Factor
Problem = Number + " - " + Factor + " = "
ElseIf (P = 3 And ProblemSelected[3]) Then
'Multiplication
ProblemType = P
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number * Factor
Problem = Number + " x " + Factor + " = "
ElseIf (P = 4 And ProblemSelected[4]) Then
'Division
ProblemType = P
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer * Factor
Problem = Number + " / " + Factor + " = "
EndIf
EndWhile
If (CorrectAnswer < 10) Then
NumberDigits = 1
Shapes.SetText(ProblemDisplay, Problem + "?")
Else
NumberDigits = 2
Shapes.SetText(ProblemDisplay, Problem + "??")
EndIf
EndSub
Add this to the project along with the code for GetFactor.
Save and Run the project. Click Start Practice and you should see a random addition problem:
The two question marks tell us there are two digits in the correct answer. We’ll see how to get that answer next. At this point, all you can do is click Stop Practice. You can then click Start Practice to see another addition problem if you’d like. View as many random addition problems as you want.
Code Design – Obtaining Answer
Once a problem is displayed, the user can enter the digits in the answer. These digits will be entered using the keyboard. The keystrokes will be handled by the graphics window KeyDown event.
The steps for obtaining and checking an answer are:
- Make sure keystroke is a number (0 to 9).
- If number, keep keystroke as part of your answer and replace question mark with number.
- If a question mark remains, exit waiting for another keystroke.
- If all question marks are gone, compare your answer (YourAnswer) with correct answer (CorrectAnswer).
- Increment the number of problems tried.
- If your answer is correct, increment the number of correct problems.
- Update scoring label controls.
- Generate another problem.
Add this line of code at the end of InitializeProgram to assign the KeyDownSub subroutine to the KeyDown event:
GraphicsWindow.KeyDown = KeyDownSub
The KeyDownSub subroutine that incorporates the steps listed above is then:
Sub KeyDownSub
If (Controls.GetButtonCaption(StartStopButton) = "Stop Practice") Then
'can only check keystrokes when practicing-only allow number keys
'number is last character in keypressed
KeyPressed = Text.GetSubTextToEnd(GraphicsWindow.LastKey, Text.GetLength(GraphicsWindow.LastKey))
If (Text.GetCharacterCode(KeyPressed) >= Text.GetCharacterCode("0") And Text.GetCharacterCode(KeyPressed) <= Text.GetCharacterCode("9")) Then
YourAnswer = Text.Append(YourAnswer, KeyPressed)
If DigitNumber <> NumberDigits Then
DigitNumber = DigitNumber + 1
Shapes.SetText(ProblemDisplay, Problem + YourAnswer + "?")
Else
NumberTried = NumberTried + 1
'check answer
If (YourAnswer = CorrectAnswer) Then
NumberCorrect = NumberCorrect + 1
EndIf
Shapes.SetText(TriedDisplay, NumberTried)
Shapes.SetText(CorrectDisplay, NumberCorrect)
GetProblem()
EndIf
EndIf
EndIf
EndSub
In the first few lines of code, we make sure we are solving problems before allowing any keystrokes. Notice how all digits in your answer (represented by the typed character in KeyPressed) are saved and concatenated into YourAnswer. Also, notice how the displayed problem is updated, overwriting a question mark, with each keystroke. As mentioned earlier, the program only gives you one chance to enter an answer - there is no erasing.
**
**
Save and Run the project. You should now be able to answer as many random addition problems as you’d like. Try it. Make sure the score is updating properly. Here’s my window after trying a few problems:
You can stop practicing problems, at any time, by clicking the Stop Practice button. Random addition problems will get boring after a while. Let’s add the logic for other problem types and other factors.
Code Design – Choosing Problem Type
The selection of problem type seems simple. Choose the check box or check boxes you want and the correct problem will be generated. But there are a couple of problems we’ve alluded to. We need to keep a user from “unchecking” all the boxes, leaving no problem type to select. We must make sure at least one box is always selected. And, if Division problems are selected, we cannot allow zero (0) to be used as a factor.
Here, we write code to mark and unmark check boxes and to address the first problem (always having one problem selected). The problem of a zero factor in division problems is addressed when we discuss factors in the next section. Let’s review the location of each check box (all squares with 15 pixel sides). This will define the ‘clickable’ areas.
Addition – Check box is at (15, 210)
Subtraction – Check box is at (15, 230)
Multiplication – Check box is at (15, 250)
Division – Check box is at (15, 270)
To allow detection of mouse clicks, add this single line of code at the end of InitializeProgram:
GraphicsWindow.MouseDown = MouseDownSub
Now, in the MouseDownSub subroutine, we do the following:
- Determine which check box was clicked (ProblemTypeClicked).
- If clicked box is checked (ProblemSelected is “true”), uncheck the box unless the number of boxes checked (Selections) is one. Decrement Selections if possible.
- If selected box is unchecked (ProblemSelected is “false”), check the box and increment Selections.
The initial selection is Addition problems. Add this initialization code to InitializeProgram after the code setting the initial values of the ProblemSelected array.
Selections = 1
ProblemTypeClicked = 1
MarkProblemType()
Note the initialization code calls a subroutine (MarkProblemType) to place an initial check mark next to Addition. That routine is:
Sub MarkProblemType
If (ProblemTypeClicked < 0) Then
ProblemTypeClicked = Math.Abs(ProblemTypeClicked)
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(15, 190 + ProblemTypeClicked * 20, 15, 15)
Else
GraphicsWindow.PenColor = "Black"
GraphicsWindow.PenWidth = 1
GraphicsWindow.DrawLine(18, 199 + ProblemTypeClicked * 20, 22, 203 + ProblemTypeClicked * 20)
GraphicsWindow.DrawLine(22, 203 + ProblemTypeClicked * 20, 28, 191 + ProblemTypeClicked * 20)
EndIf
EndSub
This subroutine will place a check mark (drawn using two DrawLine methods within the selected check box) when ProblemTypeClicked is positive. When ProblemTypeClicked is negative, the check mark is removed (the box is cleared).
With this information, the MouseDownSub procedure that implements the needed steps is:
Sub MouseDownSub
X = GraphicsWindow.MouseX
Y = GraphicsWindow.MouseY
'problem selections - must always have at least one selected
If (X > 15 And X < 30 And Y > 210 And Y < 285) Then
If (Y > 210 And Y < 225) Then
'clicked addition
ProblemTypeClicked = 1
ElseIf (Y > 230 And Y < 245) Then
'clicked subtraction
ProblemTypeClicked = 2
ElseIf (Y > 250 And Y < 265) Then
'clicked multiplication
ProblemTypeClicked = 3
ElseIf (Y > 270 And Y < 285) Then
'clicked division
ProblemTypeClicked = 4
EndIf
If (ProblemSelected[ProblemTypeClicked] And Selections <> 1) Then
'clear choice if not last one selected
Selections = Selections - 1
ProblemSelected[ProblemTypeClicked] = "false"
ProblemTypeClicked = -ProblemTypeClicked
MarkProblemType()
ElseIf (ProblemSelected[ProblemTypeClicked] = "false") Then
'mark choice
Selections = Selections + 1
ProblemSelected[ProblemTypeClicked] = "true"
MarkProblemType()
EndIf
EndIf
EndSub
You should be able to see how the various steps are implemented. In particular, note when a box is to be cleared, the ProblemTypeClicked is converted to a negative number before calling MarkProblemType. Enter this code into your project.
Save and Run the project. Make sure all the newly installed code is doing its job. Try to “uncheck” all the problem type boxes – one box will always remain. You can now solve any problem type. If you change options while solving problems, the changes will be seen once you finish solving the current problem. Try solving problems, changing problem type. Here’s my window while solving a division problem (note the check marks):
Next, we remove the random factor restriction.
Code Design – Changing Factor
Thus far, we have only used a random factor in problems. To add the capability of other factors, we need code for clicking the FactorButton. With each click of that button, we cycle through values of FactorChoice (0 through 9, -1 for random factors) and display that choice in the text shape (FactorDisplay).
Add the shaded code to ButtonClickSub to detect clicks of FactorButton:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (B = StartStopButton) Then
StartStopButtonClicked()
ElseIf (B = ExitButton) Then
Program.End()
ElseIf (B = FactorButton) Then
FactorButtonClicked()
EndIf
EndSub
Add the FactorButtonClicked subroutine to cycle through and display factor values:
Sub FactorButtonClicked
'change factor choice
FactorChoice = FactorChoice + 1
If (FactorChoice > 9) Then
FactorChoice = -1
Shapes.SetText(FactorDisplay, "Random")
Else
Shapes.SetText(FactorDisplay, Text.Append(" ", FactorChoice))
EndIf
EndSub
Save and Run the program. Click Start Practice. Click Change and watch the values change. Here is my window showing an Addition problem with a selected Factor of 6:
We need two changes related to not allowing a zero factor for Division problems. First, make the shaded change to the FactorButtonClicked subroutine:
Sub FactorButtonClicked
'change factor choice
FactorChoice = FactorChoice + 1
If (FactorChoice > 9) Then
FactorChoice = -1
Shapes.SetText(FactorDisplay, "Random")
Else
If (ProblemSelected[4] And FactorChoice = 0) Then
'no zero if doing division
FactorChoice = 1
EndIf
Shapes.SetText(FactorDisplay, Text.Append(" ", FactorChoice))
EndIf
EndSub
Here, if Division is a selected problem type and a zero factor is selected, the zero factor will be skipped over.
Second, make the shaded change to the MouseDownSub subroutine:
Sub MouseDownSub
X = GraphicsWindow.MouseX
Y = GraphicsWindow.MouseY
'problem selections - must always have at least one selected
If (X > 15 And X < 30 And Y > 210 And Y < 285) Then
If (Y > 210 And Y < 225) Then
'clicked addition
ProblemTypeClicked = 1
ElseIf (Y > 230 And Y < 245) Then
'clicked subtraction
ProblemTypeClicked = 2
ElseIf (Y > 250 And Y < 265) Then
'clicked multiplication
ProblemTypeClicked = 3
ElseIf (Y > 270 And Y < 285) Then
'clicked division
ProblemTypeClicked = 4
EndIf
If (ProblemSelected[ProblemTypeClicked] And Selections <> 1) Then
'clear choice if not last one selected
Selections = Selections - 1
ProblemSelected[ProblemTypeClicked] = "false"
ProblemTypeClicked = -ProblemTypeClicked
MarkProblemType()
ElseIf (ProblemSelected[ProblemTypeClicked] = "false") Then
'mark choice
Selections = Selections + 1
ProblemSelected[ProblemTypeClicked] = "true"
MarkProblemType()
'make sure zero not selected factor if division selected
If (ProblemTypeClicked = 4 And FactorChoice = 0) Then
FactorButtonClicked()
EndIf
EndIf
EndIf
EndSub
In this added code, if a user selects Division and zero is the factor, we simulate a click on FactorButton to increment the factor to one.
Save and Run the program again. Check Division problems. Click on the Change button and notice the zero factor never appears. Uncheck Division problems. Choose 0 as a factor using the Change button. Now, check Division again. Notice the factor is changed to 1 and the 0 option does not appear with subsequent clicks on Change. You can now solve any problem type with any factor.
Code Design – Timing Options
Having coded problem generation and answer checking, we can now address the use of timing in the flash card math project. Up to now, we’ve assumed the timer is off (TimerChoice = 0). We have two possibilities for a timer: (1) one where the timer counts up, keeping track how long you are solving problems (TimerChoice = 1), and (2) one where the timer counts down from some preset value (TimerChoice = 2). In both cases, a text shape (TimeDisplay) displays the time in minutes:seconds form. In the second case, two button controls (TimerMinusButton and TimerPlusButton) are used to set the value. The timing will be controlled with a Timer object with an interval of 1 second (1000 milliseconds).
We allow changing problem type and factors while solving problems. It wouldn’t make sense to be able to change timer options while solving problems – the times would not be correct. We will only allow selection of timer options prior to clicking Start Practice. First, we write code to mark the circles next to the three timing options (only one option can be selected). Here are the locations of the circles (all 15 pixel diameters). This will define the ‘clickable’ areas.
Off – Circle is at (295, 210)
On, Count Up – Circle is at (295, 230)
On, Count Down – Circle is at (295, 250)
Add the shaded code to ButtonDownSub to detect the appropriate mouse clicks:
Sub MouseDownSub
X = GraphicsWindow.MouseX
Y = GraphicsWindow.MouseY
'problem selections - must always have at least one selected
* .*
* .*
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
'timer selections - only one can be selected
If (X > 295 And X < 310 And Y > 210 And Y < 265) Then
'problem selections - must always have at least one selected
If (Y > 210 And Y < 225) Then
'clicked timer off
TimerChoice = 0
MarkTimerChoice()
ElseIf (Y > 230 And Y < 245) Then
'clicked timer on - count up
TimerChoice = 1
MarkTimerChoice()
ElseIf (Y > 250 And Y < 265) Then
'clicked timer on - count down
TimerChoice = 2
MarkTimerChoice()
EndIf
EndIf
EndIf
EndSub
Note changes can only be made when StartStopButton displays a Start Practice caption.
This code uses a subroutine (MarkTimerChoice) to mark the selected option and unmark the others. That routine is:
Sub MarkTimerChoice
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(295, 210, 15, 15)
GraphicsWindow.FillEllipse(295, 230, 15, 15)
GraphicsWindow.FillEllipse(295, 250, 15, 15)
GraphicsWindow.BrushColor = "SlateGray"
GraphicsWindow.FillEllipse(298, 213 + TimerChoice * 20, 9, 9)
EndSub
The initial selection is Off. Add this line after the line setting the initial value of TimerChoice in InitializeProgram:
MarkTimerChoice()
Save and Run the program. Notice the initial selection of the Off option:
Choose other Timer options to make sure proper marking is done.
Other steps must be followed when we switch from one timing option to the next. We will use a variable (ProblemTime) to store the time value (whether counting up or down) in seconds. When counting down, ProblemTime will start at 30 times TimerIndex (a value set by the two timer control buttons). Those steps followed when changing options are:
- If Off (TimerChoice = 0) is selected: hide TimerDisplay, TimerMinusButton, TimerPlusButton.
- If On-Count Up (TimerChoice = 1) is selected, show TimerDisplay and hide TimerMinusButton and TimerPlusButton. Initialize ProblemTime to 0. Display ProblemTime.
- If On-Count Down (TimerChoice = 2) is selected, show TimerDisplay, TimerMinusButton, TimerPlusButton. Initialize ProblemTime to 30 times TimerIndex. Display ProblemTime.
Before attacking this code, add a line to InitializeProgram (after the call to MarkTimerChoice) setting an initial value for TimerIndex:
TimerIndex = 1
The steps listed above are all implemented in the MouseDownSub subroutine. The needed additions are shaded:
Sub MouseDownSub
X = GraphicsWindow.MouseX
Y = GraphicsWindow.MouseY
'problem selections - must always have at least one selected
* .*
* .*
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
'timer selections - only one can be selected
If (X > 295 And X < 310 And Y > 210 And Y < 265) Then
'problem selections - must always have at least one selected
If (Y > 210 And Y < 225) Then
'clicked timer off
TimerChoice = 0
MarkTimerChoice()
Shapes.HideShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
ElseIf (Y > 230 And Y < 245) Then
'clicked timer on - count up
TimerChoice = 1
MarkTimerChoice()
Shapes.ShowShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
ProblemTime = 0
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
ElseIf (Y > 250 And Y < 265) Then
'clicked timer on - count down
TimerChoice = 2
MarkTimerChoice()
Shapes.ShowShape(TimeDisplay)
Controls.ShowControl(TimerMinusButton)
Controls.ShowControl(TimerPlusButton)
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
EndIf
EndIf
EndIf
EndSub
This subroutine uses another subroutine FormatTime that converts ProblemTime (seconds) as a FormattedTime in 00:00 format:
Sub FormatTime
Minutes = Math.Floor(ProblemTime / 60)
Seconds = ProblemTime - Minutes * 60
If (Seconds < 10) Then
FormattedTime = Minutes + ":0" + Seconds
Else
FormattedTime = Minutes + ":" + Seconds
EndIf
EndSub
This subroutine takes the time (ProblemTime) in seconds and breaks it into minutes and seconds. Add this new code to your project.
Let’s check both timer options to make sure the window changes as desired. Save and Run the program. Click On-Count Up. You should see:
The ‘count-up’ time is displayed.
Now, choose On-Count Down to see:
The ‘count-down’ time is displayed along with adjustment button controls. In this mode, TimerIndex is used to initialize the ProblemTime variable. This value is changed by clicking on the +/- control buttons (TimerPlusButton and TimerMinusButton). We will keep TimerIndex between 1 and 60. This allows a maximum of 30 minutes (1800 seconds) for a timed flash card math session.
Add the shaded code to ButtonClickedSub to detect clicking on these buttons and adjustment of the TimerIndex value (along with the TimeDisplay):
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (B = StartStopButton) Then
StartStopButtonClicked()
ElseIf (B = ExitButton) Then
Program.End()
ElseIf (B = FactorButton) Then
FactorButtonClicked()
ElseIf (B = TimerPlusButton) Then
TimerIndex = TimerIndex + 1
If (TimerIndex > 60) Then
TimerIndex = 60
EndIf
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
ElseIf (B = TimerMinusButton) Then
TimerIndex = TimerIndex - 1
If (TimerIndex < 1) Then
TimerIndex = 1
EndIf
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
EndIf
EndSub
A Timer object will be used to control the time display. Add these at the end of InitializeProgram to set the Interval and establish the Tick event subroutine (TimerTickSub):
Timer.Interval = 1000
Timer.Tick = TimerTickSub
Timer.Pause()
Clicking Start Practice will start the timing process; the steps are:
- If Off (TimerChoice = 0) is selected, do nothing else.
- If On-Count Up (TimerChoice = 1) is selected:
- Initialize ProblemTime to zero; display ProblemTime.
- Start timer.
- If On-Count Down (TimerChoice = 2) is selected:
- Initialize ProblemTime to 30 times TimerIndex; display ProblemTime.
- Hide TimerPlusButton and TimerMinusButton.
- Start timer.
Clicking Stop Practice will stop the timing process. The corresponding steps:
- Stop timer.
- Show TimerPlusButton and TimerMinusButton if TimerChoice = 2.
Each of these steps is handled in the StartStopButton subroutine. The modified procedure (changes are shaded) is:
Sub StartStopButtonClicked
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
Controls.SetButtonCaption(StartStopButton, "Stop Practice")
Controls.HideControl(ExitButton)
NumberTried = 0
NumberCorrect = 0
Shapes.SetText(TriedDisplay, "0")
Shapes.SetText(CorrectDisplay, "0")
GetProblem()
If (TimerChoice <> 0) Then
If (TimerChoice = 1) Then
ProblemTime = 0
Else
ProblemTime = 30 * TimerIndex
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
EndIf
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
Timer.Resume()
EndIf
Else
Controls.SetButtonCaption(StartStopButton, "Start Practice")
Controls.ShowControl(ExitButton)
Shapes.SetText(ProblemDisplay, "")
Timer.Pause()
If (TimerChoice = 2) Then
Controls.ShowControl(TimerMinusButton)
Controls.ShowControl(TimerPlusButton)
EndIf
EndIf
EndSub
Make the indicated changes. We’re almost ready to try the timing – just one more procedure to code.
When the timer is running, the time display (TimeDisplay) is updated every second (we use an Interval property of 1000). The displayed time is incremented if counting up, decremented if counting down. The steps involved for counting up are:
- Increment ProblemTime by 1.
- Display ProblemTime.
- If ProblemTime is 1800 (30 minutes), stop solving problems.
Note we limit the total solving time to 30 minutes.
The steps for counting down are:
- Decrement ProblemTime by 1.
- Display ProblemTime.
- If ProblemTime is 0, stop solving problems.
The code to update the displayed time is placed in the TimerTickSub subroutine. The code that implements the above steps are:
Sub TimerTickSub
If (TimerChoice = 1) Then
ProblemTime = ProblemTime + 1
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
If ProblemTime >= 1800 Then
StartStopButtonClicked()
EndIf
Else
ProblemTime = ProblemTime - 1
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
If (ProblemTime = 0) Then
StartStopButtonClicked()
EndIf
EndIf
EndSub
Notice to stop solving problems, we simulate a click on Stop Practice (the StartStopButton button). Add this procedure to the project.
We’re done implementing the modifications to add timing in the flash card math project. Save and Run the project. You want to make sure all the timer options work correctly. First, check to see that the project still works correctly with no timer.
Once you are convinced the no timer option still works, stop solving problems and choose the On-Count Up option. Run the project. Make sure the timer increments properly. Here’s a run I just started:
Click Stop Practice at some point. You should also make sure the program automatically stops after 30 minutes (go have lunch while the program runs).
Choose the On-Count Down option. Change the amount of allowed time using the vertical scroll bar. Make sure it reaches a maximum of 30:00 (it has a minimum of 0:30). Run the project. Make sure the time decrements correctly. Here’s a run I made using a starting time of 1:00:
Make sure the program stops once the time elapses.
Code Design – Presenting Results
Once a user stops solving problems, we want to let he/she know how well they did in answering problems. The information of use would be:
- The number of problems tried.
- The number of correct answers.
- The percentage score (Score).
- If timing, amount of elapsed time and time spent (on average) on each problem.
If timing up, the elapsed time is equal to ProblemTime. If timing down, the elapsed time is equal to the initial amount of time minus ProblemTime.
All of this information is readily available from the current variable set. The results are presented in the StartStopButtonClicked subroutine (following clicking of Stop Practice). We will use a simple message box to relay the results. The modified StartStopButtonClicked subroutine (changes are shaded) that displays the results is:
Sub StartStopButtonClicked
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
Controls.SetButtonCaption(StartStopButton, "Stop Practice")
Controls.HideControl(ExitButton)
NumberTried = 0
NumberCorrect = 0
Shapes.SetText(TriedDisplay, "0")
Shapes.SetText(CorrectDisplay, "0")
GetProblem()
If (TimerChoice <> 0) Then
If (TimerChoice = 1) Then
ProblemTime = 0
Else
ProblemTime = 30 * TimerIndex
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
EndIf
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
Timer.Resume()
EndIf
Else
Controls.SetButtonCaption(StartStopButton, "Start Practice")
Controls.ShowControl(ExitButton)
Shapes.SetText(ProblemDisplay, "")
Timer.Pause()
If (TimerChoice = 2) Then
Controls.ShowControl(TimerMinusButton)
Controls.ShowControl(TimerPlusButton)
EndIf
If (NumberTried > 0) Then
CRLF = Text.GetCharacter(13)
Score = Math.Floor(100 * (NumberCorrect / NumberTried))
Message = "Problems Tried: " + NumberTried + CRLF
Message = Message + "Problems Correct: " + NumberCorrect + " (" + Score + "%)" + CRLF
If (TimerChoice = 0) Then
Message = Message + "Timer Off"
Else
If (TimerChoice = 2) Then
ProblemTime = 30 * TimerIndex - ProblemTime
EndIf
FormatTime()
Message = Message + "Elapsed Time: " + FormattedTime + CRLF
Message = Message + "Time Per Problem: " + Math.Floor(100 * (ProblemTime / NumberTried)) / 100 + " sec"
EndIf
GraphicsWindow.ShowMessage(Message, "Results")
EndIf
EndIf
EndSub
Add the noted changes.
One last time – Save and Run the project. Solve some problems and see the results. Make sure the results display correctly whether timing or not. Here is a set of results I received while using the timing down option:
**
**
Flash Card Math Quiz Project Code Listing
Here is the complete listing of the Flash Card Math Quiz Small Basic program:
' Flash Card Math
InitializeProgram()
Sub InitializeProgram
'graphics window
GraphicsWindow.Width = 430
GraphicsWindow.Height = 360
GraphicsWindow.Title = "Flash Card Math"
GraphicsWindow.BackgroundColor = "LightYellow"
'labels/scores
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontBold = "false"
GraphicsWindow.FontSize = 24
GraphicsWindow.DrawText(40, 10, "Tried:")
GraphicsWindow.DrawText(200, 10, "Correct:")
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(110, 10, 60, 30)
GraphicsWindow.FillRectangle(300, 10, 60, 30)
GraphicsWindow.BrushColor = "Yellow"
TriedDisplay = Shapes.AddText("0")
Shapes.Move(TriedDisplay, 125, 10)
CorrectDisplay = Shapes.AddText("0")
Shapes.Move(CorrectDisplay, 315, 10)
'problem display
GraphicsWindow.BrushColor = "Blue"
GraphicsWindow.FontSize = 72
ProblemDisplay = Shapes.AddText("")
Shapes.Move(ProblemDisplay, 50, 50)
'divider
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(10, 160, 410, 10)
'problem types/factor/timer
GraphicsWindow.BrushColor = GraphicsWindow.GetColorFromRGB(192, 192, 255)
GraphicsWindow.FillRectangle(10, 180, 130, 130)
GraphicsWindow.FillRectangle(150, 180, 130, 130)
GraphicsWindow.FillRectangle(290, 180, 130, 130)
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
GraphicsWindow.FontBold = "true"
GraphicsWindow.DrawText(15, 185, "Type(s)")
GraphicsWindow.DrawText(155, 185, "Factor")
GraphicsWindow.DrawText(295, 185, "Timer")
'problem types
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
GraphicsWindow.FontBold = "false"
GraphicsWindow.DrawText(35, 210, "Addition (+)")
GraphicsWindow.DrawText(35, 230, "Subtraction (-)")
GraphicsWindow.DrawText(35, 250, "Multiplication (x)")
GraphicsWindow.DrawText(35, 270, "Division (/)")
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(15, 210, 15, 15)
GraphicsWindow.FillRectangle(15, 230, 15, 15)
GraphicsWindow.FillRectangle(15, 250, 15, 15)
GraphicsWindow.FillRectangle(15, 270, 15, 15)
ProblemSelected[1] = "true"
ProblemSelected[2] = "false"
ProblemSelected[3] = "false"
ProblemSelected[4] = "false"
Selections = 1
ProblemTypeClicked = 1
MarkProblemType()
'factor
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(160, 210, 110, 30)
GraphicsWindow.BrushColor = "DarkRed"
GraphicsWindow.FontSize = 20
FactorDisplay = Shapes.AddText("Random")
Shapes.Move(FactorDisplay, 180, 212)
FactorChoice = -1
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
FactorButton = Controls.AddButton("Change", 170, 245)
Controls.SetSize(FactorButton, 90, 30)
'timer choices
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
GraphicsWindow.FontBold = "false"
GraphicsWindow.DrawText(315, 210, "Off")
GraphicsWindow.DrawText(315, 230, "On-Count Up")
GraphicsWindow.DrawText(315, 250, "On-Count Down")
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(295, 210, 15, 15)
GraphicsWindow.FillEllipse(295, 230, 15, 15)
GraphicsWindow.FillEllipse(295, 250, 15, 15)
TimerChoice = 0 ' 0-off, 1 -on/up, 2- on/down
MarkTimerChoice()
TimerIndex = 1
GraphicsWindow.BrushColor = "DarkBlue"
GraphicsWindow.FontSize = 20
TimeDisplay = Shapes.AddText("0:30")
Shapes.Move(TimeDisplay, 335, 277)
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
TimerPlusButton = Controls.AddButton("+", 390, 275)
Controls.SetSize(TimerPlusButton, 20, 30)
TimerMinusButton = Controls.AddButton("-", 300, 275)
Controls.SetSize(TimerMinusButton, 20, 30)
Shapes.HideShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
'buttons
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 14
StartStopButton = Controls.AddButton("Start Practice", 60, 320)
Controls.SetSize(StartStopButton, 130, 30)
ExitButton = Controls.AddButton("Exit", 230, 320)
Controls.SetSize(ExitButton, 130, 30)
Controls.ButtonClicked = ButtonClickedSub
GraphicsWindow.KeyDown = KeyDownSub
GraphicsWindow.MouseDown = MouseDownSub
Timer.Interval = 1000
Timer.Tick = TimerTickSub
Timer.Pause()
EndSub
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (B = StartStopButton) Then
StartStopButtonClicked()
ElseIf (B = ExitButton) Then
Program.End()
ElseIf (B = FactorButton) Then
FactorButtonClicked()
ElseIf (B = TimerPlusButton) Then
TimerIndex = TimerIndex + 1
If (TimerIndex > 60) Then
TimerIndex = 60
EndIf
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
ElseIf (B = TimerMinusButton) Then
TimerIndex = TimerIndex - 1
If (TimerIndex < 1) Then
TimerIndex = 1
EndIf
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
EndIf
EndSub
Sub StartStopButtonClicked
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
Controls.SetButtonCaption(StartStopButton, "Stop Practice")
Controls.HideControl(ExitButton)
NumberTried = 0
NumberCorrect = 0
Shapes.SetText(TriedDisplay, "0")
Shapes.SetText(CorrectDisplay, "0")
GetProblem()
If (TimerChoice <> 0) Then
If (TimerChoice = 1) Then
ProblemTime = 0
Else
ProblemTime = 30 * TimerIndex
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
EndIf
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
Timer.Resume()
EndIf
Else
Controls.SetButtonCaption(StartStopButton, "Start Practice")
Controls.ShowControl(ExitButton)
Shapes.SetText(ProblemDisplay, "")
Timer.Pause()
If (TimerChoice = 2) Then
Controls.ShowControl(TimerMinusButton)
Controls.ShowControl(TimerPlusButton)
EndIf
If (NumberTried > 0) Then
CRLF = Text.GetCharacter(13)
Score = Math.Floor(100 * (NumberCorrect / NumberTried))
Message = "Problems Tried: " + NumberTried + CRLF
Message = Message + "Problems Correct: " + NumberCorrect + " (" + Score + "%)" + CRLF
If (TimerChoice = 0) Then
Message = Message + "Timer Off"
Else
If (TimerChoice = 2) Then
ProblemTime = 30 * TimerIndex - ProblemTime
EndIf
FormatTime()
Message = Message + "Elapsed Time: " + FormattedTime + CRLF
Message = Message + "Time Per Problem: " + Math.Floor(100 * (ProblemTime / NumberTried)) / 100 + " sec"
EndIf
GraphicsWindow.ShowMessage(Message, "Results")
EndIf
EndIf
EndSub
Sub GetProblem
ProblemType = 0
While (ProblemType = 0)
P = Math.GetRandomNumber(4)
If (P = 1 And ProblemSelected[1]) Then
'Addition
ProblemType = P
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number + Factor
Problem = Number + " + " + Factor + " = "
ElseIf (P = 2 And ProblemSelected[2]) Then
'Subtraction
ProblemType = P
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer + Factor
Problem = Number + " - " + Factor + " = "
ElseIf (P = 3 And ProblemSelected[3]) Then
'Multiplication
ProblemType = P
Number = Math.GetRandomNumber(10) - 1
GetFactor()
CorrectAnswer = Number * Factor
Problem = Number + " x " + Factor + " = "
ElseIf (P = 4 And ProblemSelected[4]) Then
'Division
ProblemType = P
GetFactor()
CorrectAnswer = Math.GetRandomNumber(10) - 1
Number = CorrectAnswer * Factor
Problem = Number + " / " + Factor + " = "
EndIf
EndWhile
YourAnswer = ""
DigitNumber = 1
If (CorrectAnswer < 10) Then
NumberDigits = 1
Shapes.SetText(ProblemDisplay, Problem + "?")
Else
NumberDigits = 2
Shapes.SetText(ProblemDisplay, Problem + "??")
EndIf
EndSub
Sub GetFactor
If (FactorChoice = -1) Then
If (ProblemType = 4) Then
Factor = Math.GetRandomNumber(9)
Else
Factor = Math.GetRandomNumber(10) - 1
EndIf
Else
Factor = FactorChoice
EndIf
EndSub
Sub KeyDownSub
If (Controls.GetButtonCaption(StartStopButton) = "Stop Practice") Then
'can only check keystrokes when practicing-only allow number keys
'number is last character in keypressed
KeyPressed = Text.GetSubTextToEnd(GraphicsWindow.LastKey, Text.GetLength(GraphicsWindow.LastKey))
If (Text.GetCharacterCode(KeyPressed) >= Text.GetCharacterCode("0") And Text.GetCharacterCode(KeyPressed) <= Text.GetCharacterCode("9")) Then
YourAnswer = Text.Append(YourAnswer, KeyPressed)
If DigitNumber <> NumberDigits Then
DigitNumber = DigitNumber + 1
Shapes.SetText(ProblemDisplay, Problem + YourAnswer + "?")
Else
NumberTried = NumberTried + 1
'check answer
If (YourAnswer = CorrectAnswer) Then
NumberCorrect = NumberCorrect + 1
EndIf
Shapes.SetText(TriedDisplay, NumberTried)
Shapes.SetText(CorrectDisplay, NumberCorrect)
GetProblem()
EndIf
EndIf
EndIf
EndSub
Sub MouseDownSub
X = GraphicsWindow.MouseX
Y = GraphicsWindow.MouseY
'problem selections - must always have at least one selected
If (X > 15 And X < 30 And Y > 210 And Y < 285) Then
If (Y > 210 And Y < 225) Then
'clicked addition
ProblemTypeClicked = 1
ElseIf (Y > 230 And Y < 245) Then
'clicked subtraction
ProblemTypeClicked = 2
ElseIf (Y > 250 And Y < 265) Then
'clicked multiplication
ProblemTypeClicked = 3
ElseIf (Y > 270 And Y < 285) Then
'clicked division
ProblemTypeClicked = 4
EndIf
If (ProblemSelected[ProblemTypeClicked] And Selections <> 1) Then
'clear choice if not last one selected
Selections = Selections - 1
ProblemSelected[ProblemTypeClicked] = "false"
ProblemTypeClicked = -ProblemTypeClicked
MarkProblemType()
ElseIf (ProblemSelected[ProblemTypeClicked] = "false") Then
'mark choice
Selections = Selections + 1
ProblemSelected[ProblemTypeClicked] = "true"
MarkProblemType()
'make sure zero not selected factor if division selected
If (ProblemTypeClicked = 4 And FactorChoice = 0) Then
FactorButtonClicked()
EndIf
EndIf
EndIf
If (Controls.GetButtonCaption(StartStopButton) = "Start Practice") Then
'timer selections - only one can be selected
If (X > 295 And X < 310 And Y > 210 And Y < 265) Then
'problem selections - must always have at least one selected
If (Y > 210 And Y < 225) Then
'clicked timer off
TimerChoice = 0
MarkTimerChoice()
Shapes.HideShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
ElseIf (Y > 230 And Y < 245) Then
'clicked timer on - count up
TimerChoice = 1
MarkTimerChoice()
Shapes.ShowShape(TimeDisplay)
Controls.HideControl(TimerMinusButton)
Controls.HideControl(TimerPlusButton)
ProblemTime = 0
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
ElseIf (Y > 250 And Y < 265) Then
'clicked timer on - count down
TimerChoice = 2
MarkTimerChoice()
Shapes.ShowShape(TimeDisplay)
Controls.ShowControl(TimerMinusButton)
Controls.ShowControl(TimerPlusButton)
ProblemTime = 30 * TimerIndex
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
EndIf
EndIf
EndIf
EndSub
Sub MarkProblemType
If (ProblemTypeClicked < 0) Then
ProblemTypeClicked = Math.Abs(ProblemTypeClicked)
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillRectangle(15, 190 + ProblemTypeClicked * 20, 15, 15)
Else
GraphicsWindow.PenColor = "Black"
GraphicsWindow.PenWidth = 1
GraphicsWindow.DrawLine(18, 199 + ProblemTypeClicked * 20, 22, 203 + ProblemTypeClicked * 20)
GraphicsWindow.DrawLine(22, 203 + ProblemTypeClicked * 20, 28, 191 + ProblemTypeClicked * 20)
EndIf
EndSub
Sub FactorButtonClicked
'change factor choice
FactorChoice = FactorChoice + 1
If (FactorChoice > 9) Then
FactorChoice = -1
Shapes.SetText(FactorDisplay, "Random")
Else
If (ProblemSelected[4] And FactorChoice = 0) Then
'no zero if doing division
FactorChoice = 1
EndIf
Shapes.SetText(FactorDisplay, Text.Append(" ", FactorChoice))
EndIf
EndSub
Sub MarkTimerChoice
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(295, 210, 15, 15)
GraphicsWindow.FillEllipse(295, 230, 15, 15)
GraphicsWindow.FillEllipse(295, 250, 15, 15)
GraphicsWindow.BrushColor = "SlateGray"
GraphicsWindow.FillEllipse(298, 213 + TimerChoice * 20, 9, 9)
EndSub
Sub FormatTime
Minutes = Math.Floor(ProblemTime / 60)
Seconds = ProblemTime - Minutes * 60
If (Seconds < 10) Then
FormattedTime = Minutes + ":0" + Seconds
Else
FormattedTime = Minutes + ":" + Seconds
EndIf
EndSub
Sub TimerTickSub
If (TimerChoice = 1) Then
ProblemTime = ProblemTime + 1
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
If ProblemTime >= 1800 Then
StartStopButtonClicked()
EndIf
Else
ProblemTime = ProblemTime - 1
FormatTime()
Shapes.SetText(TimeDisplay, FormattedTime)
If (ProblemTime = 0) Then
StartStopButtonClicked()
EndIf
EndIf
EndSub
**
**
Flash Card Math Quiz Project Review
The Flash Card Math Quiz project is now complete. Save and Run the project and make sure it works as designed. Recheck that all options work and interact properly. Let your kids (or anyone else) have fun tuning up their basic math skills.
If there are errors in your implementation, go back over the steps of window and code design. Use the debugger when needed. Go over the developed code – make sure you understand how different parts of the project were coded. As mentioned in the beginning of this chapter, the completed project is saved as FlashCard in the HomeSB\HomeSB Projects\FlashCard folder.
While completing this project, new concepts and skills you should have gained include:
- How to make selections among options.
- Use of the KeyDown event for input.
- Using a message box to report results.
**
**
Flash Card Math Quiz Project Enhancements
Possible enhancements to the flash card math project include:
- As implemented, the only feedback a user gets about entered answers is an update of the score. Some kind of audible feedback would be a big improvement (a positive sound for correct answer, a negative sound for a wrong answer). We discuss adding sounds to a project in the final chapter – you might like to look ahead.
- When a user stops answering problems, it would be nice to have a review mode where the problems missed are presented. You would need some way to save each problem that was answered incorrectly.
- Kids like rewards. As you gain more programming skills, a nice visual display of some sort for good work would be a fun addition.
- Currently, once a problem is answered, the next problem is immediately displayed. Some kind of delay (perhaps make it optional and adjustable) might be desired.
Excerpt © Copyright 2010-2013 By Kidware Software LLC All Rights Reserved. Computer Science For Kids, the Computer Science For Kids logo, and related trade dress are trademarks or registered trademarks of Kidware Software LLC. Philip Conrod & Lou Tylee have co-authored dozens of books and tutorials for beginning Microsoft Basic, Small Basic, Visual Basic, and Visual C# developers of all ages for over 25 years.