Event-Driven Calculator

(Using GameEngine)

<<Previous | ToC | Next >>

You got here by working your way through building a calculator in the GameEngine, and maybe getting it to (sort of) work. Or not. Now let's make it better. What sorts of things need fixing?

0. Some (or all) buttons do nothing.

0a. Breakpoints show the events are calculating, but the numbers don't show.

1. It doesn't do anything with the decimal point.

1a. The decimal point works fine, but some fractional results look weird.

2. It doesn't do anything with the Clear button.

3. It should accept 'number + number', but it ignores the first number.

4. It would be nice to see visually what function is pending.

5. Can your calculator accept keystrokes from the physical keyboard?

6. Call for a Mentor to work with you:

-- or use the "Ask" feature in Zoom.


7. Look at my calculator program for comparison

8. Try some other program...

If you haven't been writing any code, then of course you have nothing to fix or improve. Or you might be dissatisfied with what you have. In either case, you can go back (start over) here.
 

Do Nothing

If nothing at all happens when you click on a button in your running calculator window, or if you can't get that far, you should review the suggestions in the Trouble-Shooting page. In particular, if you have a game window but clicks in it don't do anything, you should (see the "Using the Java Debugger" page) set a breakpoint on the first (non-comment) line of the "public boolean ClickEvt" method (most IDEs won't let you set breakpoints on comment lines), then run it and click on a widget.

If you get the breakpoints, but the numbers you get from the GetInfo method are zero or something else, we can work on that. First try a digit button, say your number 3 button. Did you get a 3 from GetInfo? If so, it is working correctly. Did you get 51? That means you gave GameMaker a quoted '3' for the information field. Quit your program and go back to GameMaker and enter the correct numbers with no apostrophes. Then Build and try again.

What about the function buttons? Did you get 43 from the Plus button? That's normal, 43 is the ASCII code for the Plus symbol. When you cast it to char, the character value will be a '+'. Google "ASCII character chart" (or look at my ASCII Table in the "Need to Know" page) to see the numeric values of all the characters. Step through the ClickEvt method in the debugger to see what it is doing. Is each step what you expected on that line? If not, that is your problem.

Basically, the number that is showing in the "I=" field of a particular button in GameMaker should be the number you are testing for in your ClickEvt method. When the IF-test value matches the value you got from GetInfo, then that command will do whatever you tell it to do.
 

Numbers Don't Show

When you do your calculation, you are using variables that are (probably) double type, but the display widget takes text. Back when you did this in English, you could just 'print result' and it did what you wanted. Then in Java, System.out.println still did what you wanted. Now you need to do the conversion directly, as part of putting the result into the display text widget. This is described in the previous page (the green text in the sample code, plus the description below) but it's easy to miss.
 

Decimal Point

Let's work through this one. What happens when you click the decimal point button, when the program is running? You might say "nothing" but that's not quite the case. How do you know? In the program listing window, set a breakpoint (click in the left margin) on the first line inside the ClickEvt method. It should stop there in the debugger when you click that button. Verify that the value you got from GetInfo is a period ('.' = 46, see the ASCII chart in Need to Know). If it's not, you need to fix it in GameMaker. If it is, you also need a line in ClickEvt to activate when it is. Can you do that? If not, not to worry, just read on...

What do you think should happen when the user clicks the decimal point? What happens in the 4-function calculator you can buy at Walmart or BestBuy? The dot goes into the number on the display, same as the digits do. Can you make that happen in your program? If you are having trouble thinking about it, perhaps you can get some hints from one of the other students or a Mentor. In the last resort somebody can tell you what to do, but if you want to be a programmer out in the real world, you can't be doing that. Try puzzling it out yourself first. Hint: You will need an IF connected to the other IFs with an else.

Remember, the decimal point is part of the number, not a function operator, so it needs to be handled in the same place the digits are. Once you have the decimal point in the input string, the number conversion will do the right thing with it. It will work.

Still stuck?

Let's assume you now know that your decimal point button is getting its clicks in ClickEvt. Now you should ask yourself, what do you want to do with those clicks? Right now you have no code to respond to it, what do you think you need to add? We are depending on Double.parseDouble to convert from our keyed-in string of digits as a text string, into a (double) floating point number that you can do math on. However, right now the only thing in that string is a sequence of digits, no decimal point. What do you think you need to do to fix that?

Notice that the code we have for building that string of digits concatenates them one digit at a time by (automatic) integer-to-String conversion. That won't work for the decimal point, because it's a chararacter, not an (integer) numeral. But the first thing we did in this subroutine is to convert the operators to character (char) type in variable btnchr. Do you remember that? You can use it here, Java will concatenate characters just fine.

Suppose the user clicks in 1, 2, '.', 3, 4. The digits came in in order with the decimal point between the 2 and the 3, but it got ignored, so all you saw is "1234". If you handled the decimal point when it came in, what would you do with it? It's not a numeral, so you can't just include it somehow in your existing code, you need to add a new line or two to handle it. What do you think that should be? Put it in, then see if it works.

Still stuck? That's what the Mentor is here for. By the way, Mentors make mistakes too, cut us some slack if we get it wrong. That's what the Delete key is for 
 

Fractional Results Look Weird

Suppose you do the simple calculation "1-0.1=" what do you get? It should be "0.9" but maybe it shows "0.90000001" instead. What's up with that? It's not your fault, your code is correct, but Java bungled the rounding in their decimal conversion (the system library code).

It's a Math thing, people like you and me, we think in decimal, but the computer thinks in binary. The original computers were also decimal, but the engineers figured that doing the math in binary was faster and used less hardware, and they dould do the conversion at the beginning and end, and it would be good enough. Except it wasn't, people got wrong answers like you saw. So the IEEE set up a standard for binary arithmetic (I was on the committee), and now everybody does the internal calculations correctly, but the standard doesn't say anything about conversion, so some people still get that one wrong.

Like I said, it's a math thing. when you divide 10/3 in decimal, you get an unending sequence of digits 3.3333..., but if you divide it by 2 or 5, then it comes out even. That's because 2 and 5 multiplied together is a multiple of ten (ten even, same thing). In binary the divisor must exactly divide a power of two for that to work, and ten doesn't.

2/10 = 0.0011001100110011... in binary
Anyway, so 1/10 = 0.1 (in decimal) = 0.00011001100110011... in binary, and it comes out 0.09999999.. when they convert it to decimal, but they need to notice that and round it correctly, which they didn't in this case. You can fix it yourself, but it's tricky. Perhaps another day 
 

Clear Button

OK, maybe "tricky" was a little overstated. There are really only two things that need to happen happen, two "use cases":
1. The user has been entering digits, and the aWord string is not empty. If that is the case, measure how long the string is, then take a substring exactly one character less.

Let's think a little about string length. Recall from your English that a string (variable or constant of type String in Java) is a sequence of characters, kind of like an array, but you can't index the characters directly. Instead you use system subroutines to do that. Java has a whole bunch of useful methods in class String that you should eventually become familiar with, and I have corresponding "safe" subroutines to do the same thing without throwing unnecessary exceptions (see "String Tools" in the "Need to Know" page).

Anyway the two you need to know about today are string length (see my StrLength in "String Tools" in the "Need to Know" page), which returns the number of characters in the string, like if the user clicked in 5 digits and a decimal point, you see six characters in the string, and substring (see my Substring in "String Tools" in the "Need to Know" page), which is a little more complicated. Let's say the user entered "123.45". Or rather let's do a little test program like this:

String word = "123.45";
int lxx = JavaGame.StrLength(word); // =6
String fraction = JavaGame.Substring(4,2,word); // ="45"
String allbutlast = JavaGame.Substring(0,lxx-1,word);
In the case of fraction, you can think of it as "skip over four characters (the '123.') then take the next two." The next line skips over no characters, and takes the length less 1, that is, all but the last digit.

If the length is only one digit to start with, then clear it to empty (""). The display code you already have should handle it fine.

2. The user has not yet entered any digits after the function, or else has not yet even entered a new function. The aWord string is empty in both cases, so that is how you tell this case from #1; just set the res value to 0.0 and (again) the display code you already have will work fine. You might also want to set the operator to '+' like at ther beginning.

Try it. Remember, this is your program, you get to write the code.
 

Starting with an Initial Number (No Function)

If you want to do "2+3=" and get 5, you need to think about what that first 2 with no preceding function means. You are starting from nothing, the initial res=0.0, and in your head you are probably thinking that you just put it into res, if you have a non-empty input string in aWord but nothing in the function (optor). And that would be exactly right, but you can't know it without trying all the use cases (in your mind, or better, on paper and pencil). The initial case is empty res, so putting the unaccompanied input there gets the user what they want.

The second use case is where the user has completed the previous calculation with '=', and now wants to start over. Formally they should press Clear, then proceed as if they had just started (with res=0), but you can offer them a shortcut: just type the initial value of the next calculation and your calculator will do the Right Thing.

Me, I was thinking like our previous text-based calculator, that every input must have an operrator, even the first, so I was going to insert an implied Plus if no function was given, or rather initialize the operator to '+'. I like your solution better, because mine does The Wrong Thing after an equal.

See? You can do this, but you do need to think about it. IBM's corporate motto for many years was "THINK". That's what programmers have always needed to do.
 

Visual Display of Pending Function

The 4-function calculator you can get at Amazon for less money than it costs to ship it, they expect you to remember what function button you pushed, and I guess most people do. Me, I'm getting to the age that I get distracted and I forget what I was doing.

You can give your function buttons a (slightly) animated appearance of staying pressed by changing the color slightly when clicked, then back again when you finish the operation being saved. Darker looks more pushed, and the ClickEvt parameter whom gives you a reference to the rectangle widget being clicked, and there's a SetColor method you can use to change its color. You will need another persistent instance variable -- I called mine FnPressed -- declared type GameWgt  at the top with the other three persistent variables and initialized = null (shown in blue here):

/// (..$) End generated class variables ($) (do not modify this line)

  char optor = ' '; // added instance variables..
  double res = 0.0;
  String aWord = "";
  GameWgt FnPressed = null;

public String toString() {return "(CalcGE)";} //~toString


When you get a function key click (not equal, but that test is already there) you save not only the key code in optor, but also the reference whom in FnPressed, and change the color of that widget. Then when you actually do that function, you lighten the (saved) rectangle's color and reset FnPressed back to null.

  } // end of doing pending op
if (FnPressed != null) {
  FnPressed.SetColor(0x666666);
  FnPressed = null;}
if (btnchr != '=') {
  FnPressed = whom;
  if (whom != null) whom.SetColor(0x333333);
  optor = btnchr;}
else optor = ' '; // already did it


Does that make sense? Put it in and see if it works, then convince yourself that you know why it works.

OK, what happens if the user clicks minus then changes their mind and clicks the Clear button to start a new calculation instead? The minus key is still operative and button remains highlighted, even after the result on the display is cleared (try it). What can your program do to prevent that? I think you can figure it out.
 

Physical Keystrokes

Mousing over to a button on the screen and then clicking it is generally faster than you can type a whole command line, but if your hand is already on the number pad, pressing another (single) key on a keyboard is generally faster than mousing and clicking. Does your calculator program accept input from the keyboard? Perhaps you would like it to. What do you think you need to add to make that happen? If you did the Pong game, then you already know how to get keyboard input into your program. Do you think you can connect those keystrokes to your existing calculator? If you get stuck, you can always...

Call a Mentor, or...
 

Look at My Calculator

 

Next

Let's try an other event-driven program, maybe a game with a little more challenge...

<<Previous | ToC | Next >>

Revised: 2023 March 1