Sunday, December 31, 2023

Race Car Math Estimation Game: Part 2

As I was playing with the initial prototype I had a flashback to circa 1983, appropriate given the 1980s inspiration for this game, namely Estimation Baseball, which I created at about the same time as I explained in my previous post. Back then, one of the major publishers of educational software was Plato. Jerry Morris, the principal of the school where I was teaching (Bluewater Elementary School in Bluewater, New Mexico) had purchased a variety of educational software titles for our school's five Apple II+ computers, including several titles published by Plato. During my flashback I recalled playing a clever math race car game for a single player. The game began with having the player take a "trial lap." I don't recall all the details, but basically the computer kept track of how long it took you to complete the lap while answering math problems correctly. Then, the race began with your race car pitted against a race car "driven" by the computer. The clever part is that the computer's car used the player's time during the trial lap. So, you were already being pushed to improve on your own ability. If you won the race, that new time was then used by the computer's race car in the next race.

Design Changes

This flashback motivated me to make two major changes to the design of my race car game. 

First, instead of this being a game for two players, it is now a single player game. I've incorporated the idea of the computer's race car being driven by the player's best time.

The second major design change is to move away from the three estimation thresholds that were inspired by the estimation baseball game to indicate an estimation that was close enough for a single, double, triple, or home run. Instead, I just use the accuracy of the estimation, again measured as a percent of the correct answer, to move the car. So, even if the estimation was way off, say only 1% of the correct answer (e.g., an estimate of 10 for the correct answer of 1000), your race car would move just a tiny bit. The farthest the car will go if answered perfectly is half as long as the race track. The race was won, of course, when one of the race cars crossed the finish line, but a variable also kept track of the number of laps (i.e., tries) it took to get there. The computer's race car would move according to the lowest (best) number of tries taken so far in the same increments.

Video Demonstration of This Second Prototype

Here is a short video demonstration of the current prototype of the game:


In this video, the player completes a total of four races. The first race is the trial lap, so the computer's race car does not move. Notice that it took the player 5 laps (aka tries) to complete the race. The data from the trial lap is then transferred into the field "best try" (labeled as "best so far" on the screen). (Programming note: These fields, obviously, will not be displayed when the game is finished. They are shown to help me program the game. At some point, I'll probably use variables instead of fields to keep track of this data.) 

On the second race, the player took 4 laps to complete the race. The computer's race car mimics the number of laps and estimations of the player's trial lap, which was 5 laps. The player wins the race.

On the third race, the player didn't do so well. After four laps, the player had not yet reached the finish line. But, the computer's race car finished the race in four laps - again mimicking the performance of the player during the second race.

For the fourth race, notice that the data in the field "best try," doesn't change. That's because the player's best try so far was in race two. For this fourth race, the player's performance improved and only needed three laps to finish the race. The computer's race car - again mimicking the performance of the player during the third race - would have needed another lap. So, the player won the fourth race.

Analyzing the Code

Just like in version 1.0 of the game, the most important code is in the button "Next Lap:"

1:  global varCar1x, varCar2x, varNum1range, varNum2range, varNum1, varNum2, varLap1, varLap2, varLap3  
2:  global varDistance1, varCar2Lap  
3:    
4:  on mouseup  
5:      
6:    //reset fields  
7:    put empty into field "num1 field"  
8:    put empty into field "num2 field"  
9:    put empty into field "answer field"  
10:    put empty into field "percent field"  
11:    put empty into field "display answer"  
12:      
13:    //This will determine how fast car2 will get to the finish line  
14:    put the number of lines in field "best try" into varCar2Tries  
15:      
16:    //put random(varNum1range) into varNum1 - I'll activate this after all testing is done  
17:    //put random(varNum2range) into varNum2 - I'll activate this after all testing is done  
18:    put 10 into varNum1 //I'll delete this and next line after all testing is done  
19:    put 10 into varNum2  
20:      
21:    put varNum1 into field "num1 field"  
22:    put varNum2 into field "num2 field"  
23:      
24:    put varNum1 * varNum2 into varAnswer  
25:    Ask "what is the answer?"  
26:    put it into varResponse  
27:    put varAnswer into field "display answer"  
28:    put varResponse into field "answer field"  
29:      
30:    //compute absolute percentage of correct answer  
31:    put abs(varAnswer-varResponse) into varDistance  
32:    put varAnswer-varDistance into varDistance2  
33:    put round((varDistance2/varAnswer),2) into varPercent  
34:    put varPercent into field "percent field"  
35:      
36:    put 50 into varDistance1 //Note: This variable is actually initialized in the card script; I put it here temporarily during testing.  
37:      
38:    //Move the player's race car a distance that depends on the accuracy of the player's guess   
39:    put (varDistance1*varPercent)+varCar1x into varCar1x  
40:    if varCar1x >= 100 then put 101 into varCar1x //This keeps the car from going far to the right after crossing the finish line.  
41:    //From version 0.1 - I may revert back these, so I'm keeping them for now  
42:    //if varPercent>=.95 then put varLap1+varCar1x into varCar1x  
43:    //if varPercent>=.80 and varPercent<.95 then put varLap2+varCar1x into varCar1x  
44:    //if varPercent>=.75 and varPercent<.80 then put varLap3+varCar1x into varCar1x  
45:      
46:    //move the race car  
47:    set the moveSpeed to 200  
48:    move button "car1" to ((varCar1x*8)+100),70 // in 2 seconds without waiting  
49:      
50:    //move the second race car  
51:    if field "best try" is not empty then  
52:     add 1 to varCar2Lap //Note: This is initialized to 1 in the card script  
53:     put item 1 of line varCar2Lap of field "best try" into varPercent2  
54:     put (varPercent2)+varCar2x into varCar2x  
55:     move button "car2" to ((varCar2x*8)+100),160// in 2 seconds  
56:    end if  
57:      
58:    //show the key data to help with troubleshooting (to be deleted later)  
59:    put (varDistance1*varPercent)&comma&varCar1x&comma&location of button "car1"&return after field "data"  
60:      
61:    If varCar1x>=100 then  
62:     if field "best try" is empty then wait 2 seconds  
63:     answer "You win!"  
64:     move button "car1" to 1110,70 in 1 second  
65:     if field "best try" is empty then  
66:       put field "data" into field "best try"  
67:     end if  
68:     if the number of lines in field "data" < the number of lines in field "best try" then  
69:       put field "data" into field "best try"  
70:     end if  
71:     opencard  
72:    end if  
73:      
74:    If varCar2x>=100 then  
75:     answer "Sorry, you lost this race! Try again."  
76:     opencard  
77:    end if  
78:      
79:  end mouseup  

The code is clearly just an updated version of the code from version 1.0, so I'll only go over some of the key changes.

First, lines 30-34 take the player's answer to the math question and determines the accuracy of the answer as a percent. 

In line 36 the variable varDistance1 is used to set the maximum distance the race will travel. That is, if the answer is exactly correct, the car will only go half (i.e., 50) the length of the race car track. I can easily change this as needed after getting feedback from the game play.

Line 39 computes the distance the player's race car will go. 

Lines 46-48 move the player's race car. You'll notice that I've commented out the modifier "in 2 seconds without waiting" in line 48. This was how I moved the race car in version 1. Instead, I now move the race car according to the "speed limit" denoted in line 47.

Lines 50-56 move the computer's racing car using either the player's time for the trial lap or the player's best time. Line 51 is used to determine if the player just completed the trial lap. If it's empty, then they have not completed the trial lap yet. If it is not empty, they have. So, if field "best try" is empty, then the computer's race car does not move. Recall from version 1 that I had been keeping track of the player's race results in the field "data" (labeled as "testing data" on the screen). When the player completes the trial lap, the data from the trial lap is automatically transferred into the field "best try" because, obviously, the player's only lap is their best time .... so far. This all happens in lines 61-72.

Line 61 checks to see if the player has crossed the finish line yet: Is the position of car1 (defined as the car's "nose") equal or greater than 100? If not, then the rest of the code is skipped and the player has to press the "Next Lap" button for the next lap. If the player has crossed the finish line, then line 65 checks to see if the field "best try" is empty. If it is, then the player just completed the trial and the race data is transferred from the field "data" to the field "best try." If this is not the trial lap, line 68 checks to see if the number of lines in field "data" is less than the number of lines in field "best try." If it is, then the player outscored their best time and their new data is now the "best time" data and it is transferred to field "best try," replacing the data that was there previously.

Notice that the opencard command is triggered in line 71 if the race is over. This resets both race cars back to their starting positions and re-initializes a bunch of variables.

Lines 74-77 are only triggered if the computer's race car crosses the finish line before the player's race car. In this case, the player loses that race and again the opencard command is triggered, this time with line 76. The player is now challenged to try a little harder in the next race.

Questions about the Game Play

As this point, I'm uncertain if the game play will be fun using this strategy of number of laps needing to win any single race. Currently, I have it set so that the player wins if there is a tie. That is, if both the player and the computer takes the same number of tries (i.e., laps) to cross the finish line, then the player wins. If the math problems are difficult, it will be virtually impossible to obtain exact answers. So, depending on the difficulty of the math problems, I really don't know how many laps it will take to win a race. Only feedback from game play will determine this.

Some ideas I have now include automatically increasing or decreasing the challenge of the game as the game play warrants. For example, if the player set a personal best score by accident, such as by coincidentally getting the exact or almost the the exact answer several times in a row, they may never be able to repeat this performance and beat this time. I could add an algorithm that checks to see how many times the player has either won or lost in a row, then adjust the game difficulty after a certain number of wins or losses has been encountered.

I'm also not sure if the animation is appropriate for interesting game play. As you saw in the video demonstration, first the player's race car moves, then the computer's race car moves. I had played around with both cars moving simultaneously, but this sometime led the computer's race car finishing before the player's race car with the same number of laps. Although the animation felt right, I would have to change the design of the game away from using the total number of laps to determine the winner of a race. But, that might be what I need to so. So, I may revert back to this strategy at some point.

I'm also not sure of the right language to use in the game. So far, I've been using the word "lap" to mean try. I don't know much about racing, but each try doesn't seem like a lap to me. I also referred to the first race as the trial lap, so that's an obvious contradiction. I need to work on this.

Next Steps

Not surprisingly, there are an assortment of design changes that I need to do. Here are a few of the most pressing.

The Need for a Timer

Something that is obviously missing from the game is a timer for each math question. Recall that the educational value of this game is to learn how to make good, quick estimations. If the player has unlimited time for each math problem, then of course they can just work out the correct answer. Instead, the player needs to be pushed to come up with an estimation as quickly as they can. In the original game of Estimation Baseball, I think I gave the player about 9-12 seconds to enter an answer, with 3-4 seconds for each "strike." The amount of time given to the player is an important design decision. It can also be something that varies throughout the game. More or less time could be given depending on how well the player is doing.

Review the Future of the "Next Lap" Button

I need to consider if I should continue to let the player decide when the next race will start, or make it automatic, but with the option to pause the race. I see value in both approaches.

Improve the Graphic Design and Add Sound Effects

Right now, it's obvious to me which race car I drive and which race car the computer drives, but I doubt it would be so obvious to anyone else playing the game. I need to design the game to not only make this clear, but also to add some excitement by having the player choose and name their race car. 

If you watched the video above, you probably were expecting lots of race car sound effects, such as the "vroom, vroom" of the race car engines being throttled. Sorry for your disappointment! But, I realize there are so many ways for appropriate sound effects to really enhance the game play. 

Review and Enhance the Game Play

As already mentioned above, I need to seriously consider how to optimize the game play of this game. I need to fully capitalize on the race car metaphor here. Beyond the sound effects already mentioned, I can envision a grandstand of spectators who cheer and applaud when a race is won, or who voice disappointment when a race is lost. These are tricky design decisions. I don't ever want to do anything other challenge and engage the player. A close race that ends in defeat can be a very motivating event to try to do better. But, constantly losing can be a recipe for a failed game. I remember well how the Plato software challenged me to keep playing - and to keep doing mathematics. Even though the Plato software was meant for elementary school students, I remember enjoying the challenge of the game even as an adult. That's something special that I hope to recreate in this game.






Sunday, December 17, 2023

Race Car Math Estimation Game: Part 1

I began a new project to create a mathematics game for elementary school students to develop estimation skills. The inspiration for this game came a few weeks ago as I was getting ready to teach a class in our doctoral design thinking course. I had dusted off an old Apple IIe computer that I had in my office to show to the class. I thought they might find it interesting to see what was the state-of-the-art technology around 1983 or so. 

I had two goals. The first was just to have the class get a good sense of the design of the machine's interface design (e.g., keyboard entry only and no internet) and the state of graphics and text. The second was to show the class some computer games I had designed back when I was an elementary school teacher to see if they agreed that the design of the games - however old - was still good. As I like to say, "good design, like good music, stands the test of time." Back in the early 1980s I taught myself how to program the Apple IIe using the BASIC programming language (which was built-in to the IIe). I had programmed a bunch of games and simulations to help my students learn and enjoy some vexxing ideas, such as fractions - the bane of all 4th or 5th graders. I showed the original version of Mineshaft, a game to learn fractions. Mineshaft remains probably the best educational game I've ever designed.

Another game I demonstrated was one I had forgotten about. It was called "Estimation Baseball." (I had mentioned this game in a post way back in December 2016 when I was taking inventory of my career's design work up to that time.) As the name suggests, the game used a baseball theme for two players. The computer would "pitch" a difficult math problem, such as 983 X 317, and the student had to about 10 seconds to enter their best guess as to the answer. The computer would beep every 3 or so seconds (akin to strikes). If no estimation was entered, the student "struck out." Estimations that were way off resulted in "ground outs" or "fly outs." However, the player could hit singles, doubles, triples, or home runs if their estimation was good - it all depended on how good the estimation was. I don't remember the cut-offs for the estimations, but it was something like 95% accuracy for a home run, 85% for a triple, etc. I remember the students really enjoying the game. Students had no time to to work out the exact answer, so a math problem like 983 X 317 needed to be quickly transformed into something like 1000 X 300, or 300,000, which is about 96% accurate resulting in a home run. I also built different "leagues" into the game for different skill levels, from "little league" to "hall of fame." Estimation games, like this, get away from the stress and drudgery of finding the one and only one correct answer and instead have students really explore and discover mathematics in their own way,

Initial Design of the Race Car Estimation Game

I continue to this day to think that estimation remains a very important math skill. (How quickly can you come up with 20ish% tip on that restaurant bill?) I decided to build a new version of Estimation Baseball using LiveCode. But, instead of baseball I thought I'd use a race car theme for the game. (I've learned that there are a lot of people who really don't care for baseball and don't know the rules.) My idea is two players compete by moving their race cars forward by estimating math problems. The better the estimate, the further the race car will go.

It's been awhile since I last blogged about the development of a LiveCode project over time and I thought this would be a good candidate. So far, I've spent only about two hours on the project. Here's a screen snapshot of the first rough prototype:



The field "data" on the left (labeled as "Testing Data" on the screen) is temporary - it provides me with quick feedback on whether the underlying variables are being computed correctly. The first number in each row shows the horizontal position of the car on the race track, which is numbered from 0-100. The next two numbers show the x,y position of the car on the screen.

The button "Opencard" does just that - it opens the card, which restarts the game from the very beginning.

I actually made a lot of progress in a very short time. So far, only one of the race cars moves, but it does so based on the accuracy of the estimation to a math problem. For testing purposes, I'm using very simple multiplication problems where each number (the "multiplier" and the “multiplicand," to be exact) is a random number between 1 and 10. Actually, I've just been using 10 X 10 for my testing so far to make it easy for me to troubleshoot the algorithms I've programmed.

Looking at the Code

The majority of the code is in the button "Next Lap." However, there is also some code in the card script to initialize the various game variables and empties any fields on the screen:

1:  on opencard  
2:      
3:    global varCar1x, varCar2x, varNum1range, varNum2range, varNum1, varNum2, varLap1, varLap2, varLap3  
4:      
5:    put empty into field "percent field"  
6:    put empty into field "num1 field"  
7:    put empty into field "num2 field"  
8:    put empty into field "answer field"  
9:    put 10 into varNum1range  
10:    put 10 into varNum2range  
11:      
12:    put 0 into varCar1x  
13:    put 0 into varCar2x  
14:      
15:    set the location of button "car1" to 100,70  
16:      
17:    put 20 into varLap1  
18:    put 10 into varLap2  
19:    put 5 into varLap3  
20:      
21:    put empty into field "data"  
22:      
23:  end opencard  

Here's the code in the button "Next Lap:"

1:  global varCar1x, varCar2x, varNum1range, varNum2range, varNum1, varNum2, varLap1, varLap2, varLap3  
2:    
3:  on mouseup  
4:       
5:    //reset fields  
6:    put empty into field "num1 field"  
7:    put empty into field "num2 field"  
8:    put empty into field "answer field"  
9:    put empty into field "percent field"  
10:      
11:    //put random(varNum1range) into varNum1 - I'll activate this after all testing is done  
12:    //put random(varNum2range) into varNum2 - I'll activate this after all testing is done  
13:    put 10 into varNum1 //I'll delete this and next line after all testing is done  
14:    put 10 into varNum2  
15:      
16:    put varNum1 into field "num1 field"  
17:    put varNum2 into field "num2 field"  
18:      
19:    put varNum1 * varNum2 into varAnswer  
20:    Ask "what is the answer?"  
21:    put it into varResponse  
22:    put varResponse into field "answer field"  
23:      
24:    //compute absolute percentage of correct answer  
25:    put abs(varAnswer-varResponse) into varDistance  
26:    put varAnswer-varDistance into varDistance2  
27:    put round((varDistance2/varAnswer),2) into varPercent  
28:    put varPercent into field "percent field"  
29:      
30:    //put varCar1x into message  
31:    if varPercent>=.95 then put varLap1+varCar1x into varCar1x  
32:    if varPercent>=.80 and varPercent<.95 then put varLap2+varCar1x into varCar1x  
33:    if varPercent>=.75 and varPercent<.80 then put varLap3+varCar1x into varCar1x  
34:      
35:    //move the race car  
36:    move button "car1" to ((varCar1x*8)+100),70 in 2 seconds  
37:      
38:    //show the key data to help with troubleshooting (to be deleted later)  
39:    put varCar1x&comma&location of button "car1"&return after field "data"  
40:      
41:  end mouseup  

For now, I'm using the convenient "Ask" command to get the student's estimation answer, shown in line 20. I'll change that later to an input field on the screen.

Lines 31-33 show the code to determine the percentage correct of the student's estimation. For now, I'm using the cutoffs of 95%, 80%, and 75%. These may change as I need to tweak the game play in future versions. Notice lines 17-19 in the card script above. These determine just how far the car will go for each percentage - right now these are 20, 10, and 5. I also expect these to change as the game design progresses.

Final Thoughts

I'm optimistic that elementary school students will find this to be a fun game. I think it's a good example of embedding mathematics into game play, in contrast with the common strategy of sugarcoating mathematics with a game context.