// File: TPGame.java // Author: Peter Ross // Created: 20 March 2000 // // This displays the matrix for a simple two-person zero-sum game. // You can drag the sliders about to alter the four payoffs; they remain // integers in the range -10 to +10. The appropriate mixed or pure strategy // for each player gets displayed. import java.applet.Applet; import java.awt.*; import java.lang.Double; import java.lang.Math; import java.awt.event.*; // Layout looks like this, without the lines or numbers: // 1 2 3 4 5 // +------+------+------+------+------+ // 1 | | | 1/2 | | 1/2 | // +------+------+------+------+------+ // 2 | | | CA | | CB | // +------+------+------+------+------+ // 3 | 1/2 | RA |slider| |slider| // +------+------+------+------+------+ // 4 | | | 0 | | 0 | // +------+------+------+------+------+ // 5 | 1/2 | RB |slider| |slider| // +------+------+------+------+------+ // 6 | | | 0 | | 0 | // +------+------+------+------+------+ public class TPGame extends Applet implements AdjustmentListener { // p is a simple container that holds labels, scrollbars // and whatever. It exists only to provide some control // over size and background colour (if wanted). One can // even do without it! Panel p = new Panel(); // Create various components, without saying where they go as yet: Label label13 = new Label("1/2",Label.CENTER); Label label15 = new Label("1/2",Label.CENTER); // Make these two wide, to force larger column width: Label label23 = new Label(" CA ",Label.CENTER); Label label25 = new Label(" CB ",Label.CENTER); Label label31 = new Label("1/2",Label.RIGHT); Label label32 = new Label("RA",Label.CENTER); Label label43 = new Label("0",Label.CENTER); Label label45 = new Label("0",Label.CENTER); Label label51 = new Label("1/2",Label.RIGHT); Label label52 = new Label("RB",Label.CENTER); Label label63 = new Label("0",Label.CENTER); Label label65 = new Label("0",Label.CENTER); // Why 11, you may ask, in the following lines? The actual // slider is 1 wide (3rd arg), and the value is where the left // edge of the slider is. So the max value is 11-1=10. Scrollbar scrollbar33 = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-10,11); Scrollbar scrollbar35 = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-10,11); Scrollbar scrollbar53 = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-10,11); Scrollbar scrollbar55 = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-10,11); public void init() { // Set the type of layout manager (this grid layout runs left to // right, starting new row every 5 items) and the size of the // whole panel. Java is weird here; it doesn't care about the // claim that there are 6 rows -- if you go on adding components // it goes on adding rows, till you stop. The cells are all // equal width; use the GridBagLayout layout manager for more // delicate control if you need it. p.setLayout(new GridLayout(6,5)); p.setSize(new Dimension(500,100)); // Where does the panel go? .. into the space reserved for // `this' instance of an applet, so call this's `add' method // to insert it: this.add(p); // Now add components, to the panel, one row at a time. Note // the idea of simply adding an empty label in various places, // as a primitive way to space things. Java doesn't provide // fine-grained placement by default -- after all, think what // might need to happen if you resize your whole window. p.add(new Label()); p.add(new Label()); p.add(label13); p.add(new Label()); p.add(label15); // Row 2: p.add(new Label()); p.add(new Label()); label23.setForeground(Color.red); p.add(label23); p.add(new Label()); label25.setForeground(Color.red); p.add(label25); // Row 3: p.add(label31); label32.setForeground(Color.red); p.add(label32); p.add(scrollbar33); p.add(new Label()); p.add(scrollbar35); // Row 4: p.add(new Label()); p.add(new Label()); p.add(label43); p.add(new Label()); p.add(label45); // Row 5: p.add(label51); label52.setForeground(Color.red); p.add(label52); p.add(scrollbar53); p.add(new Label()); p.add(scrollbar55); // Row 6: p.add(new Label()); p.add(new Label()); p.add(label63); p.add(new Label()); p.add(label65); // Those scrollbars get changed by the user, and when that // happens they call an adjustmentValueChanged function. Which // one? The one defined in `this' class, of course: scrollbar33.addAdjustmentListener(this); scrollbar35.addAdjustmentListener(this); scrollbar53.addAdjustmentListener(this); scrollbar55.addAdjustmentListener(this); } // Here is the adjustmentValueChanged method mentioned above: public void adjustmentValueChanged(AdjustmentEvent e) { int v = e.getValue(); // Find out which scrollbar moved and update the label below it: if(e.getAdjustable() == scrollbar33) { label43.setText(String.valueOf(v)); } else if(e.getAdjustable() == scrollbar35) { label45.setText(String.valueOf(v)); } else if(e.getAdjustable() == scrollbar53) { label63.setText(String.valueOf(v)); } else if(e.getAdjustable() == scrollbar55) { label65.setText(String.valueOf(v)); } // Update the strategy display: strategy_update(); } // Now, functions needed for the strategy update work. // Get the highest common factor of two positive ints: private int hcf(int a, int b) { if(a > b) return(hcf(b,a)); if(a==0) return(b); return(hcf(a,b-a)); } // Given two ints n and d, return string "n/d" in lowest terms: private String fract(int n, int d) { int h; if(n==0) return("0"); else if(d==1) return(String.valueOf(n)); else { h = hcf(n,d); return(String.valueOf(n/h)+"/"+String.valueOf(d/h)); } } // Calculate the mixed strategy for the game. // The payoff layout is: // ca cb // +------- // ra| a c // | // rb| b d // private void strategy_update() { int a, b, c, d; String s; int ra, rb, ca, cb; a = scrollbar33.getValue(); b = scrollbar53.getValue(); c = scrollbar35.getValue(); d = scrollbar55.getValue(); // First check for dominance: if(a >= b && c >=d) { ra = 1; rb = 0; ca = (a <=c)? 1 : 0; cb = 1 - ca; } else if (a <= b && c <= d) { ra = 0; rb = 1; ca = (b <= d)? 1 : 0; cb = 1 - ca; } else if (a <= c && b <=d) { ca = 1; cb = 0; ra = (a >= b)? 1 : 0; rb = 1 - ra; } else if ( a >= c && b >= d) { ca = 0; cb = 1; ra = (c >= d)? 1 : 0; rb = 1 - ra; } else // then check for saddle points: if( a <= c && a >= b ) { // a is a saddle ra = 1; rb = 0; ca = 1; cb = 0; } else if( b <= d && b >= a ) { // b is a saddle ra = 0; rb = 1; ca = 1; cb = 0; } else if( c <= a && c >= d ) { // c is a saddle ra = 1; rb =0; ca = 0; cb = 1; } else if( d <= b && d >= c ) { // d is a saddle ra = 0; rb = 1; ca = 0; cb = 1; } else { // resort to Williams' [Compleat Strategyst, Dover 1986] // "oddments" method: ra = Math.abs(b-d); rb = Math.abs(a-c); ca = Math.abs(c-d); cb = Math.abs(a-b); } // The checks for saddle points above are sequential, and so // will miss the fact that a player's two choices may be // identical, in which case a random choice is OK. Test for that: if(a==c && b==d) { ca = 1; cb = 1; } if(a==b && c==d) { ra = 1; rb = 1; } // Now display all: label31.setText(fract(ra,ra+rb)); label51.setText(fract(rb,ra+rb)); label13.setText(fract(ca,ca+cb)); label15.setText(fract(cb,ca+cb)); } }