// Interactive lesson on the vertex of a quadratic curve. // Quick and dirty - needs to be revised, and things would certainly // be easier with a Rational class. import java.applet.Applet; import java.awt.*; import java.util.*; class VertexCanvas extends Canvas{ private int a, b, c; private int state; private Font f; public void init(){ f = new Font("TimesRoman", Font.PLAIN, 14); } public void setCoefficients(int aco, int bco, int cco, int s) { a = aco; b = bco; c = cco; state = s; repaint(); } public void paint( Graphics g) { String part2, part3; String part1 = "For the equation y = " + a + " x"; g.setFont( f ); g.drawString(part1, 10, 28); FontMetrics fm = g.getFontMetrics(); int newPosition = fm.stringWidth(part1) + 10; g.drawString("2", newPosition, 20); newPosition += fm.stringWidth("2"); if(b > 0){ part2 = " + " + b + " x"; } else if(b < 0){ part2 = " - " + Math.abs(b) + " x"; } else { part2 = ""; } g.drawString(part2, newPosition, 28); newPosition += fm.stringWidth(part2); if(c > 0){ part3 = " + " + c + ","; } else if(c < 0){ part3 = " - " + Math.abs(c) + ","; } else{ part3 = ""; } g.drawString(part3, newPosition, 28); } } // State dictionary // state 1 : // Awaiting answer to the first question - is there a maximum // or a minimum? Exit is to state2 on selecting either checkbox. // // state2 : // Allows user to read response. Exit is to state3 on a mousedown. // // state3 : // Awaiting entry of vertex answer. On syntactically invalid // input, transition is to state4. If the answer is correct or // just off by a sign or twice the right answer, the transition // is to state5. Otherwise the transition is to state6. // // state4 : // Lets user read response and then retry vertex answer. // Transition is to state3. // // state5 : // Lets user read response. Transition on a mousedown is to // state1. // // state6 : // Await input of b. Syntactically incorrect - go to state8. // b correct - go to state7. b incorrect - go to state 5. // // state7 : // Await input of a. On syntactically invalid input, go to // state9. Otherwise go to state 5. // // state8: // Let user read message that b is syntactically invalid. // Transition is to state6. // // state9: // Let user read message that a is syntactically invalid. // Transition is to state7. // public class Vertex extends Applet { private TextArea text1; private VertexCanvas equation; private CheckboxGroup maxmin; private Checkbox maxbox, minbox; private TextField text2; private int a, b, c; private int state; private int numerator, denominator; private Random r; public void init() { setBackground(Color.cyan); equation = new VertexCanvas(); equation.resize(50,200); text1 = new TextArea(6,200); text1.setEditable( false); text2 = new TextField(100); text2.setEditable( true); text2.setBackground(Color.yellow); setLayout( new GridLayout(5,1) ); add(equation); add(text1); maxmin = new CheckboxGroup(); add( maxbox = new Checkbox("maximum", maxmin, false)); add( minbox = new Checkbox("minimum", maxmin, false)); state = 1; r = new Random(); a = 0; while(a == 0){ a = Math.abs(r.nextInt()) % 10 - 5; } b = Math.abs(r.nextInt()) % 20 - 10; c = Math.abs(r.nextInt()) % 20 - 10; equation.setCoefficients(a,b,c,1); text1.setText(" first decide whether the curve has a maximum or minimum."); } public void setUpState1(){ state = 1; text2.setText(""); a = 0; while(a == 0){ a = Math.abs(r.nextInt()) % 10 - 5; } b = Math.abs(r.nextInt()) % 20 - 10; c = Math.abs(r.nextInt()) % 20 - 10; equation.setCoefficients(a,b,c,1); text1.setText(" first decide whether the curve has a maximum or minimum."); remove(text2); add(maxbox); add(minbox); maxbox.setState(false); minbox.setState(false); repaint(); } public void okstate1(boolean good){ String badstring; if(a < 0){ badstring = a + " is negative, the curve has a maximum."; } else{ badstring = a + " is positive, the curve has a minimum."; } if(good){ text1.setText(" That's right.\nClick away from the checkboxes for the next question."); } else{ text1.setText(" Incorrect - since a = " + badstring + "\nClick away from the boxes for the next question."); } state = 2; repaint(); } public void goodAnswer(){ state = 5; text1.setText("That's right. Click anywhere for another problem."); repaint(); } public void offBySign(){ state = 5; text1.setText("You are off by a sign. The answer should be -b/2a,\nbut you have b/2a. Click anywhere for another problem."); repaint(); } public void forgot2(){ state = 5; text1.setText("Maybe you forgot the 2 in -b/2a. Click anywhere for another problem."); repaint(); } public void talkToUser(){ state = 6; text1.setText("Your answer isn't right. Let's see if you are identifying a and b correctly\nEnter b in the yellow text field."); text2.setText(""); text2.requestFocus(); repaint(); } public void talkToUser1(){ state = 6; text1.setText("Try entering b in the yellow text field again."); text2.setText(""); text2.requestFocus(); repaint(); } public void analyzeAnswer(){ // Was user's answer correct? If not, try to figure out // what went wrong. // Answer should equal -b/2a if( 2*a*numerator == -b*denominator){ goodAnswer(); return; } // Maybe answer is off by a sign. if( 2*a*numerator == b*denominator){ offBySign(); return; } // Maybe user forgot the 2 in -b/2a. if(a*numerator == -b*denominator){ forgot2(); return; } // Maybe user misidentifies the coefficients. // See what user thinks a and b are. talkToUser(); } public void badInput(){ text1.setText("Invalid input. Click anywhere to retry."); state = 4; repaint(); } public void badDenominator(){ text1.setText("Denominators can't be zero. Click anywhere to retry."); state = 4; repaint(); } public void validateInput(String input){ Integer num, den; StringTokenizer tokens = new StringTokenizer( input, "/\n\r"); int numberof = tokens.countTokens(); if(numberof == 1){ try{ num = new Integer(input); } catch(Exception e) { badInput(); return; } numerator = num.intValue(); denominator = 1; } else if(numberof == 2){ String next = tokens.nextToken(); try{ num = new Integer(next); } catch(Exception e) { badInput(); return; } numerator = num.intValue(); next = tokens.nextToken(); try{ den = new Integer(next); } catch(Exception e) { badInput(); return; } denominator = den.intValue(); if(denominator == 0){ badDenominator(); return; } } else{ badInput(); return; } // If we get here, the input was syntactically valid, anyhow. // Let's see if the user's answer is correct. analyzeAnswer(); } public void badCheckb(){ text1.setText("Invalid input. Click anywhere to retry."); state = 8; repaint(); } public void checkValueb(String bString){ Integer bMaybe; int bb; try{ bMaybe = new Integer(bString); } catch(Exception e) { badCheckb(); return; } bb = bMaybe.intValue(); if( bb != b){ text1.setText("Oh! b should be " + b + ". Click anywhere for another problem."); state = 5; repaint(); return; } else{ state = 7; text1.setText("b is right. Let's see about a.\nEnter a in the yellow text field."); text2.setText(""); text2.requestFocus(); repaint(); } } public void badChecka(){ text1.setText("Invalid input. Click anywhere to retry."); state = 9; repaint(); } public void tryaAgain(){ state = 7; text1.setText("Try entering a again in the yellow text field."); text2.setText(""); text2.requestFocus(); repaint(); } public void checkValuea(String aString){ Integer aMaybe; int aa; try{ aMaybe = new Integer(aString); } catch(Exception e) { badChecka(); return; } aa = aMaybe.intValue(); if( aa != a){ text1.setText("Oh! a should be " + a + ". Click anywhere for another problem."); state = 5; repaint(); return; } else{ state = 5; text1.setText("Well, you have coefficient a right. I don't know how you went wrong.\nClick anywhere for another problem."); repaint(); return; } } public boolean action( Event e, Object o) { if(state == 1){ if( e.target instanceof Checkbox){ if( maxbox.getState() == true && a < 0){ okstate1(true); } else if( minbox.getState() == true && a > 0){ okstate1(true); } else{ okstate1(false); } } } else if(state == 3 && e.target == text2){ String input = e.arg.toString(); validateInput(input); } else if(state == 6 && e.target == text2){ String input = e.arg.toString(); checkValueb(input); } else if(state == 7 && e.target == text2){ String input = e.arg.toString(); checkValuea(input); } return(true); } public boolean mouseDown( Event e, int x, int y){ if(state == 2){ state = 3; remove(maxbox); remove(minbox); add(text2); text2.reshape(10,150,100,30); text1.setText("Now, what is the x coordinate of the vertex?\nEnter in the yellow text field either as a fraction, like 5/4 or -1/6,\nor an integer, like 3 or -2."); text2.requestFocus(); repaint(); } else if(state == 4){ state = 3; text2.setText(""); text1.setText("Now, what is the x coordinate of the vertex?\nEnter in the yellow text field either as a fraction, like 5/4 or -1/6,\nor an integer, like 3 or -2."); text2.requestFocus(); repaint(); } else if(state == 5){ state = 1; setUpState1(); } else if(state == 8){ talkToUser1(); } else if(state == 9){ tryaAgain(); } return(true); } }