// voting.java // Copyright 2015 by Jeffrey Rosenthal // All rights reserved. // jeff@math.toronto.edu // http://probability.ca/jeff/ import java.util.*; import java.lang.Math; import java.applet.*; import java.text.*; import java.awt.*; import java.awt.image.*; public class voting extends Applet implements Runnable { int speed = 3; //long pausemils = 64; long pausemils = pausefn(speed); int paramspacing = 30; int boxdiam = 15; boolean paused = false; int optimiseguy = -1; int prevoptimiseguy = -1; int inputnum, masteritnum; boolean randomisefirst = true; boolean CONTIN = false; int MINNUMPLAYERS = 1; int MAXNUMPLAYERS = 12; int MAXDISPLAY = 12; int NUMITS = 25; int numplayers = 4; double pos[] = new double[MAXNUMPLAYERS]; boolean play[] = new boolean[MAXNUMPLAYERS]; double maxunder, minover, newpos; double score[] = new double[MAXNUMPLAYERS]; double leftedge[] = new double[MAXNUMPLAYERS]; double rightedge[] = new double[MAXNUMPLAYERS]; double range[] = new double[MAXNUMPLAYERS]; int numtied[] = new int[MAXNUMPLAYERS]; int numwin; int i, j, randplayer; double maxrange, mm, scoresum; //double masterpos[] = new double[MAXNUMPLAYERS]; double masterpos = -99.0; double themedian = 0.5; int xspot, yspot, leftspot, rightspot; int xzero = 80; int yzero = 720 - 120; // replace "900" by window height ... int xaxislength = 660; int xfull = 600; int scorespot = xzero + xaxislength; int paramspot = scorespot + 200; int yspacing = 60; int axiswidth = 3; int markrad = 10; int circlerad = 12; int sq = 15; DecimalFormat df = new DecimalFormat("#.####"); DecimalFormat df1 = new DecimalFormat("#.#"); DecimalFormat df2 = new DecimalFormat("#.##"); Random rr = new Random(); double sigma = 0.1; double swapProb = 0.5; double mimicProb = 0.2; double medianProb = 0.2; double uniformProb = 0.05; double initPlayProb = 0.5; double power = 10.0; double penalty = 0.5/numplayers; Color darkgreen = new Color(0,128,0); Color lightgreen = new Color(0,188,0); Image holdImage; Graphics holdGraphics; // Font strongfont = new Font ("TimesRoman", Font.BOLD, 18); public void init() { holdImage = createImage(size().width, size().height); holdGraphics = holdImage.getGraphics(); randomisethem(0); //for (int kk=theplayer; kk Math.abs(prevpos-themedian)) ) ) { for (int kk=theplayer; kk maxunder) ) maxunder = pos[jj]; if ( (pos[jj] > pos[ii]) && (pos[jj] < minover) ) minover = pos[jj]; } } if (maxunder < 0.0) leftedge[ii] = 0.0; else leftedge[ii] = (maxunder+pos[ii])/2; if (minover > 1.0) rightedge[ii] = 1.0; else rightedge[ii] = (minover+pos[ii])/2; range[ii] = (rightedge[ii] - leftedge[ii]) / numtied[ii]; } else { range[ii] = 0.0; } } // Compute maximum range. maxrange = 0.0; for (int ii=0; ii maxrange) ) maxrange = range[ii]; } // Compute scores. if (CONTIN) { scoresum = 0.0; for (int ii=0; ii 0.0) score[ii] = score[ii] / scoresum - penalty; } else { numwin = 0; for (int ii=0; ii 0.0) score[ii] = score[ii] / numwin; } } public void paint(Graphics g) { int bot = imax(0, numplayers-MAXDISPLAY); // Background rectangle. g.setColor(Color.pink); g.fillRect(0,0,size().width,size().height); // Draw the number of milliseconds between updates, and NUMITS. g.setColor(Color.black); g.drawString("numplayers: " + numplayers, paramspot, paramspacing); g.drawString("NUMITS: " + NUMITS, paramspot, 2*paramspacing); g.drawString("swapProb: " + df1.format(swapProb), paramspot, 3*paramspacing); g.drawString("sigma: " + df1.format(sigma), paramspot, 4*paramspacing); g.drawString("mimicProb: " + df1.format(mimicProb), paramspot, 5*paramspacing); g.drawString("medianProb: " + df1.format(medianProb), paramspot, 6*paramspacing); g.drawString("uniformProb: " + df2.format(uniformProb), paramspot, 7*paramspacing); g.drawString("initPlayProb: " + df1.format(initPlayProb), paramspot, 8*paramspacing); g.drawString("power: " + df1.format(power), paramspot, 9*paramspacing); g.drawString("penalty: " + df.format(penalty), paramspot, 10*paramspacing); g.drawString("speed: " + speed, paramspot, 11*paramspacing); if (paused) g.drawString("[PAUSED]", paramspot, 300); //g.drawString("pausemils = " + pausemils, paramspot, 330); // Draw the plus/minus mouse spots. for (i=1; i<=11; i++) { g.setColor(Color.white); g.fillRect(paramspot - boxdiam*5/2, i*paramspacing-10, 2*boxdiam, boxdiam); g.setColor(Color.black); g.drawString("-", paramspot - boxdiam*9/4, i*paramspacing); g.drawString("+", paramspot - boxdiam*5/4, i*paramspacing); } // Draw the "REFRESH" button. xspot = scorespot + 50; yspot = yzero - 10; g.setColor(Color.yellow); g.fillRect(xspot, yspot, 60, 20); g.setColor(Color.black); g.drawString("REFRESH", xspot+5, yspot+15); // Draw the "CONTIN" button. xspot = scorespot + 100 + 50; yspot = yzero - 10; g.setColor(Color.yellow); if (CONTIN) { g.setColor(Color.cyan); } g.fillRect(xspot, yspot, 60, 20); g.setColor(Color.black); if (CONTIN) { g.drawString("CONTIN", xspot+5, yspot+15); } else { g.drawString("ORIG", xspot+5, yspot+15); } // Draw the "optimise" circles. g.setColor(Color.yellow); for (i=bot; i 0.0) g.setColor(darkgreen); leftspot = (int) (xzero + leftedge[i]*xfull); rightspot = (int) (xzero + rightedge[i]*xfull); g.fillRect(leftspot, yspot, rightspot-leftspot, 5); fillcircle(g, xspot, yspot, circlerad); } else { g.setColor(Color.red); g.fillRect(xspot-sq/2, yspot-sq/2, sq, sq); } //g.drawString(df.format(pos[i]), xspot+2*circlerad, yspot+5); g.drawString(df.format(pos[i]), xspot-9, yspot+25); g.setColor(Color.black); g.drawString("#" + (i+1) + ": r=" + df.format(range[i]) + "; s=" + df.format(score[i]), scorespot, yspot+5); //g.drawString(df.format(score[i]), 50, yspot+5); if ( (prevoptimiseguy==i) && (optimiseguy==-1) ) g.drawString("[done:" + NUMITS + "]", 10, yspot+5); if (optimiseguy==i) { g.drawString((masteritnum+1) + "/" + NUMITS, 10, yspot+5); g.setColor(Color.white); xspot = (int) (xzero + xfull*masterpos); g.fillRect(xspot-1, yspot-15, 3, 20); } } } public boolean action(Event e, Object o) { return true; } public void update(Graphics g) { paint(holdGraphics); g.drawImage(holdImage, 0, 0, this); } public void dosleep(long nummilisecs) { try { Thread.currentThread().sleep(nummilisecs); } catch (InterruptedException e) { } } public boolean veryclose(double rrr, double sss) { //return(rrr==sss); //return(Math.abs(rrr-sss) < 0.001); //return(Math.abs(rrr-sss) < 0.000001); return(Math.abs(rrr-sss) < 0.00000001); } public void fillcircle(Graphics g, int xx, int yy, int rr) { g.fillOval(xx-rr, yy-rr, 2*rr, 2*rr); } //public void keyPressed(KeyEvent e) { //if (e.getKeyCode() == 'y') //System.out.println("LEFT!!"); //} public boolean mouseDown(Event evt, int x, int y) { int thebut; int midpoint = paramspot - boxdiam*3/2; boolean inc = (x > midpoint); int bot = imax(0, numplayers-MAXDISPLAY); // Check for parameter modifications. if ( (x > paramspot - boxdiam*5/2) && (x < paramspot) && (y < paramspacing * 11 + 10) ) { thebut = (int) Math.floor( (y-10) / paramspacing); switch (thebut) { case 0: // numplayers if ( (inc) && (numplayersMINNUMPLAYERS) ) { // Decrement the number of players. numplayers--; prevoptimiseguy = optimiseguy = -1; penalty = 0.5/numplayers; randomisethem(0); } break; case 1: // NUMITS if (inc) { // Increment the number of iterations. NUMITS = NUMITS + 5; } else if (NUMITS > 5) { // Decrement the number of iterations. NUMITS = NUMITS - 5; } if (optimiseguy == -1) prevoptimiseguy = -1; break; case 2: // swapProb if (inc && (swapProb <= 0.9)) swapProb = swapProb + 0.1; if ((!inc) && (swapProb >= 0.1)) swapProb = swapProb - 0.1; break; case 3: // sigma if (inc && (sigma <= 0.9)) sigma = sigma + 0.1; if ((!inc) && (sigma >= 0.1)) sigma = sigma - 0.1; break; case 4: // mimicProb if (inc && (mimicProb <= 0.9)) mimicProb = mimicProb + 0.1; if ((!inc) && (mimicProb >= 0.1)) mimicProb = mimicProb - 0.1; break; case 5: // medianProb if (inc && (medianProb <= 0.9)) medianProb = medianProb + 0.1; if ((!inc) && (medianProb >= 0.1)) medianProb = medianProb - 0.1; break; case 6: // uniformProb if (inc && (uniformProb <= 0.99)) uniformProb = uniformProb + 0.01; if ((!inc) && (uniformProb >= 0.01)) uniformProb = uniformProb - 0.01; break; case 7: // initPlayProb if (inc && (initPlayProb <= 0.9)) initPlayProb = initPlayProb + 0.1; if ((!inc) && (initPlayProb >= 0.1)) initPlayProb = initPlayProb - 0.1; break; case 8: // power if (inc) power = power + 0.5; if ((!inc) && (power >= 0.5)) power = power - 0.5; break; case 9: // penalty if (inc) penalty = penalty + 0.01; if ((!inc) && (penalty >= 0.01)) penalty = penalty - 0.01; break; case 10: // speed if (inc && (speed < 10)) speed++; if ((!inc) && (speed > 1)) speed--; pausemils = pausefn(speed); break; } } // Check for optimisation request. if ( (x > scorespot - 25) && (x < scorespot - 15) ) { thebut = (int) Math.round( (yzero - 20 - y) / 45.0 ); //System.out.println(thebut); if ( (thebut >=1) && (thebut <= numplayers) && (optimiseguy == -1) ) { prevoptimiseguy = - 1; masteritnum = 0; masterpos = -99.0; optimiseguy = thebut-1+bot; } } // Check for REFRESH request. if ( (x > scorespot + 50) && (x < scorespot + 110) && (y > yzero - 10) && (y < yzero + 10) ) { prevoptimiseguy = optimiseguy = -1; randomisethem(0); } // Check for CONTIN/ORIG request. if ( (x > scorespot + 100 + 50) && (x < scorespot + 100 + 110) && (y > yzero - 10) && (y < yzero + 10) ) { CONTIN = !CONTIN; } repaint(); return true; } public boolean keyDown(Event evt, int key) { char keystroke = (char) key; if (keystroke == 'p') { // pause paused = !paused; repaint(); } if (keystroke == 'x') { // abort the optimisation attempt optimiseguy = -1; repaint(); } if (keystroke == 'c') { // cheat numplayers = 7; pos[0] = 0.5; pos[1] = 0.1; pos[2] = 0.9; pos[3] = 0.4; pos[4] = 0.6; // pos[5] = 0.2; // pos[6] = 0.8; play[0] = play[1] = play[2] = play[3] = play[4] = true; // play[5] = play[6] = true; pos[5] = Math.random(); pos[6] = Math.random(); play[5] = play[6] = false; randomisefirst = false; CONTIN = true; penalty = 0.1; speed = 10; NUMITS = 100; scoreit(); //pos[0] = Math.random(); //pos[1] = Math.random(); //pos[2] = Math.random(); //play[0] = play[1] = play[2] = true; //pausemils = pausefn(speed); //optimiseguy = 0; } if (keystroke == 'C') { // capital-Cheat numplayers = 12; mm = 0.05; pos[0] = 0.5; pos[1] = 0.1; pos[2] = 0.7 + mm; pos[3] = 0.9 + 0.75*mm; pos[4] = 0.3 - 0.25*mm; pos[5] = 0.5 + 1.25*mm; pos[6] = 0.1 + 0.5*mm; pos[7] = 0.1 - 0.25*mm; pos[8] = 0.5 - 0.5*mm; pos[9] = 0.3 + 0.25*mm; pos[10] = 0.7 - 0.25*mm; pos[11] = 0.9 - 0.5*mm; play[0] = play[1] = play[2] = play[3] = play[4] = play[5] = play[6] = play[7] = play[8] = play[9] = play[10] = play[11] = true; randomisefirst = false; speed = 10; scoreit(); } if (keystroke == 'd') { // discontinuous cheat numplayers = 10; pos[0] = 0.5; pos[1] = 0.1; pos[2] = 0.9; pos[3] = 0.3; pos[4] = 0.7; play[0] = play[1] = play[2] = play[3] = play[4] = true; randomisethem(5); CONTIN = true; penalty = 0.02; speed = 10; NUMITS = 50; scoreit(); } if (keystroke == 'D') { // latest cheat numplayers = 7; mm = 0.05; pos[0] = 0.5; pos[1] = 0.1; pos[2] = 0.7 + mm; play[0] = play[1] = play[2] = true; randomisethem(3); randomisefirst = true; speed = 10; scoreit(); } if (keystroke == 't') { // tell me the ranges and scores System.out.print("\nPositions & Ranges & Scores:\n"); for (int kk=0; kk= numplayers-thisplayer) || (thisplayer==optimiseguy)){ if ( (speed <= 7) || (numplayers-thisplayer > speed-7) || (thisplayer==optimiseguy) ) { repaint(); dosleep(pausemils); } } public int ifloor (double zz) { return( (int) Math.floor( zz ) ); } public int imax(int ii, int jj) { if (ii > jj) return(ii); return(jj); } public int imin(int ii, int jj) { if (ii < jj) return(ii); return(jj); } public long lpow2 (int nn) { if (nn<0) return(0); if (nn==0) return(1); return(2*lpow2(nn-1)); } public long pausefn(int thespeed) { if (thespeed==1) return(1024); else return(lpow2(9-thespeed)); } Thread t; public void start() { t = new Thread(this); t.start(); } public void stop() { t.stop(); t = null; } }