An important strategy in programming is to break down a problem into small parts, then tackle each part separately by building and testing blocks of code. Though the ultimate goal of this tutorial is to build an complete math blaster that generates random problems and checks them, you'll start with a more focused problem: defining a list and displaying that list in the user interface.
To begin, create a test user interface in the Designer. The final user interface will be different, but don't worry about that for now. Create a new project named "MathBlaster" and drag out the following components:
Now that you've set up the user interface for this initial sub-goal,
open the Blocks Editor. For this app, you need a variable to hold the
list of numbers to be added up. For now, define the list with the
numbers 5, 11, and 7, and later on we'll make it so the numbers are
randomly generated. In the Blocks Editor, drag out an initialize
global block from the Variables folder, a make
a list block from the List folder, and three number blocks from the
Math folder. Name and connect the blocks in the following manner:
The numbers list variable, like all variables, is
part of the app's hidden memory. If you want such data to appear in
the user interface, it must be displayed explicitly, typically in a
label. You want the list displayed when the app starts, so you'll add
blocks to set the NumbersLabel in
the Screen1.Initialize event handler:
The list is
displayed in the default manner that occurs when you place a list
directly into a label. Specifically, the items of the list are
separated with spaces and the entire list is displayed within
parenthesis.
A second sub-goal for the app is to compute the total of the numbers in order to check the student's arithmetic. Keep in mind that you want blocks that will sum the list of numbers generically, i.e., even if the items were changed or items were added, the computation would still work.
In the final app you won't show the answer at all, you'll just compare the sum to whatever the user enters. But you're still working on an intermediate goal here, a test app. Your immediate goal is to sum the numbers and just display the result in the label named SumLabel.
If you're like most people, your "algorithm" for summing numbers is something like the following:
App Inventor, and most languages, provide a foreach
construct, which basically says, "do the blocks inside for each item
in the list":
If the list has three items, like our sample, the blocks inside the foreach will be repeated three times. If the list has four items, the blocks inside will be repeated four times, and so on. For the sample numbers list, the variable item will hold the value 5 on the first iteration, 11 on the second, and 7 on the third. So foreach is exactly what you want.
But what do you want to do for each of those iterations-- what blocks
go within the foreach? You want to add the next number in the list to
a running total. Anytime your app needs to remember something,
like a running total, you define a variable. In this case, you can
define a variable "total" and give it an initial value of 0:
For each item in your list, you add the item to the variable total.
When the foreach completes (three iterations in this case), you can
display the calculated total in a label:
Now your app can create and display a list of numbers, and even add them up. Now its time to create the user interface for the complete version of MathBlaster. You'll need to rename some of the components, but your ultimate goal is for the user interface to appear as on the right. You'll need:
Now that your app knows how to add, you can set up it so the user can
answer and have her answer checked. When the user clicks the
AnswerButton, the app should still add up the numbers in the list.
Instead of displaying that answer, the app should compare it-- the
computed total-- to the answer submitted by the user in AnswerTextBox.
Depending on the result, the app should display "correct" or
"incorrect" in the AnswerCheckLabel:
If you code the app with the blocks above, the app will not behave correctly. Before moving on, try to identify and fix the bug. When you are ready, click on "View Answer"
The given solution works the first time the AnswerButton is clicked, but not the second. The problem is that the variable total is not reinitialized to zero on each click. So after the first time the button is clicked, total is set to 23. When the second click occurs, and AnswerButton.Click is triggered again, the list items will be added to the current value of total (23) and the result will be 46.
The solution is simple: set total to zero at the top
of the event handler:
The app thus far isn't much fun because the same problem is asked over and over. The next step is to add a "Next Problem" button and make it so that the app generates random problems each time it is clicked.
For now, assume that you always want to generate exactly three random numbers. Later, you can modify the code so that problems of different sizes are generated. Also, assume that the numbers should be randomly chosen within the range 1-20.
Here are the blocks for the NextButton.Click event
handler:
The blocks first empty out the numbers list by setting it to an empty make a list block. Then add items to list is called to add the three random numbers. Finally, the NumbersLabel is updated to display the list and the AnswerTextBox is modified to erase the previous answer.
So you now have a "Math Blaster" app but all the problems are exactly three numbers long. The next step is to eliminate this restriction so questions of different lengths will be generated and asked.
Your task is to change the app so that the list of numbers has
between two and five items on each question. For such a task, foreach
isn't useful as you are not trying to iterate over the items of an
existing list (you are creating a new one!). Instead, you need a while
or for range as in the following solution:
With a for range, the blocks within the "do" are performed some number of times as defined by a range. Think of the range i as a counter or index. In this case, it starts out as 1, on the first iteration. Each time the inner block (add items to list) is performed, the step (1) is added to i, until it reaches the end, which in this case is a random number between 2 and 5. So if the random integer generated is 4, the range will be 1-4 and the inner block will be performed four times.
Note that the blocks within the NextButton.Click
event handler should be copied to the Screen.Initialize
event handler, as the first question should also be generated with a
random number of items as opposed to the "fixed" three items in the numbers
list definition. Furthermore, the numbers definition
should have an empty make a list block within it
instead of the three items that we used for the initial version of
the app. An empty make a list block is a common
pattern for defining a "dynamic" list: you are defining a list to
hold items, but the items are generated as the app runs. Here are the
blocks for these updates:
As is, the app displays the list of numbers within parenthesis and with spaces, not + signs, separating the numbers. Your next task is to change this so that a proper addition equation is displayed. For example, for the default list, your app should display "5+11+7", not "(5 11 7)"
App Inventor doesn't provide a simple way to format list items nicely, so you'll have to use iteration-- a foreach-- to walk through the list of numbers and build a text string with "+" between the numbers. You will build the text in NumbersLabel step-by-step, adding a number and a plus on each iteration:
Iteration | NumbersLabel |
---|---|
1 | 5 |
2 | 5+11 |
3 | 5+11+7 |
Here are the updated blocks:
Instead
of just dumping the list into NumbersLabel, the label is built a
number at a time, with the join block taking care of
adding the "+" between each number.
The blocks above are close but not quite right. Before moving on, test the app and try to identify the issues and fix them. When you are ready, click on "View Solution"
The given solution leaves an additional "+" at the end of the equation, e.g., 5+11+7+. There are a number of ways to fix this, including using a different iteration block such as while-do or for range.
If the foreach block is retained, you need to add some blocks to "post-process" the label and remove the last "+":The segment block takes a part (segment) of some text, in this case starting from the first character and taking all but the last character (the unwanted "+").
Note that you'll need to update the Screen.Initialize event handler with these blocks as well so the first question will appear correctly
Effective software engineers know that having redundant code (blocks) in an app is bad practice: if something changes, you have to find all copies of the code and make the change. Copying and pasting blocks, as done above, is a sub-optimal solution. A better solution involves refactoring the blocks and defining a procedure.