import java.awt.*;
import java.lang.*;
import java.net.*;
import java.util.StringTokenizer;
import java.util.Vector;

class Player {
public int last_move[];
public int own_number;
private Game game;
public Player(int in_num, Game in_game) {
  game = in_game;
  last_move = new int[4];
  for (int i=0;i<4;i++) {
    last_move[i]=-1;
  }
  own_number = in_num;
}
public void do_move(int x1, int y1, int x2, int y2) {
  game.grid[x1][y1][x2][y2] = own_number;
  last_move[0]=x1;
  last_move[1]=y1;
  last_move[2]=x2;
  last_move[3]=y2;
}
public void do_move() {
  int tmp_numplay = game.numplay;
  int bestSquare;
  if ((bestSquare = game.find_good_spot()) >= 0) {
    do_move(bestSquare/64,(bestSquare/16)%4, (bestSquare/4)%4, bestSquare%4);
    return;
  } else return;
}
}



class Game {
public int grid[][][][];
public int maxplay = 8;
public int numplay;
public int curplay;
public int winplay;
public int winnum[];
public int c_play;
public int h_play;
public Player player[];
  
public Game(int num1, int num2) {
  h_play = num1;
  c_play = num2;
  numplay = c_play + h_play;
  curplay = 0;
  winplay = -1;
  winnum = new int[4];
  player = new Player[numplay];
  for (int i=0;i<numplay;i++) player[i] = new Player(i, this);
  grid = new int[4][4][4][4];
  for (int i1=0;i1<4;++i1) for (int i2=0;i2<4;++i2) 
    for (int i3=0;i3<4;++i3) for (int i4=0;i4<4;++i4) {
      grid[i1][i2][i3][i4] = -1;
    }
}
public void new_game(int num1, int num2) {
  h_play = num1;
  c_play = num2;
  numplay = c_play + h_play;
  curplay = 0;
  winplay = -1;
  winnum = new int[4];
  player = new Player[numplay];
  for (int i=0;i<numplay;i++) player[i] = new Player(i, this);
  for (int i1=0;i1<4;++i1) for (int i2=0;i2<4;++i2) 
    for (int i3=0;i3<4;++i3) for (int i4=0;i4<4;++i4) {
      grid[i1][i2][i3][i4] = -1;
    }
}
public boolean proc_move(int x1, int y1, int x2, int y2) {
  if (grid[x1][y1][x2][y2] > -1) {
    return false;
  } else {
    player[curplay].do_move(x1, y1, x2, y2);
    if (check_win(x1, y1, x2, y2)) {
      winplay = curplay;
    } else {
      curplay = (curplay + 1) % numplay;
    }
    while (curplay >= h_play) {
      player[curplay].do_move();
      x1 = player[curplay].last_move[0];
      y1 = player[curplay].last_move[1];
      x2 = player[curplay].last_move[2];
      y2 = player[curplay].last_move[3];
      if (check_win(x1, y1, x2, y2)) {
	winplay=curplay;
	break;
      } else {
	curplay = (curplay + 1) % numplay;
      } 
    }
    return true;
  }
}
private boolean check_win(int x1, int y1, int x2, int y2) {
  int j, k;
  for (int ix1=-1;ix1<2;++ix1) for (int iy1=-1;iy1<2;++iy1)
    for (int ix2=-1;ix2<2;++ix2) for (int iy2=-1;iy2<2;++iy2) {
      if ((ix1 + ix2 + iy1 + iy2 >= 0) && 
	  (ix1 != 0 || ix2 != 0 || iy1 != 0 || iy2 != 0)) {
	j=1;
	winnum[0] = 64*x1+16*y1+4*x2+y2; 
	k=1;
	while (x1+k*ix1>=0 && x1+k*ix1<4 && y1+k*iy1>=0 && y1+k*iy1<4 &&
	       x2+k*ix2>=0 && x2+k*ix2<4 && y2+k*iy2>=0 && y2+k*iy2<4 &&
	       grid[x1+k*ix1][y1+k*iy1][x2+k*ix2][y2+k*iy2] == curplay) {
	  winnum[j] = 64*(x1+k*ix1) + 16*(y1+k*iy1) + 4*(x2+k*ix2) + 
	    (y2+k*iy2); 
	  ++k;
	  ++j;
	}
	k=-1;
	while (x1+k*ix1>=0 && x1+k*ix1<4 && y1+k*iy1>=0 && y1+k*iy1<4 &&
	       x2+k*ix2>=0 && x2+k*ix2<4 && y2+k*iy2>=0 && y2+k*iy2<4 &&
	       grid[x1+k*ix1][y1+k*iy1][x2+k*ix2][y2+k*iy2] == curplay) {
	  winnum[j] = 64*(x1+k*ix1) + 16*(y1+k*iy1) + 4*(x2+k*ix2) + 
	    (y2+k*iy2); 
	  --k;
	  ++j;
	}
	if (j==4) {
	  winplay = curplay; 
	  return true;
	}
      }
    }
  return false;
}
public int find_good_spot() {
  int i, ix1, iy1, ix2, iy2;
  int x1min, x1max, y1min, y1max, x2min, x2max, y2min, y2max;
  int x1, y1, x2, y2;
  int j, occNum, occPlay;
  int bestSquare, squareVal, bestVal;
  int evalGrid[][][][] = new int[4][4][4][4];
  bestSquare = -1;
  bestVal = -1;
  for (x1=0;x1<4;x1++) for (y1=0;y1<4;y1++) 
    for (x2=0;x2<4;x2++) for (y2=0;y2<4;y2++) {
      if (grid[x1][y1][x2][y2] == -1) {
	evalGrid[x1][y1][x2][y2] = 0;
	bestSquare = 64*x1+16*y1+4*x2+y2;
	bestVal = 0;
      } else evalGrid[x1][y1][x2][y2] = -1;
    }
  for (i = 1; i<=40; i++) {
    /* representing 1/2 of the 3^4 possible directions on board not
       counting 0 direction or double-counting opposite directions */
    ix1 = i > 13 ? 1 : 0; 
    if (i - 27*ix1 > 4) iy1 = 1;
    else if (i - 27*ix1 < -4) iy1 = -1;
    else iy1 = 0;
    if (i - 27*ix1 - 9*iy1 > 1) ix2 = 1;
    else if (i - 27*ix1 - 9*iy1 < -1) ix2 = -1;
    else ix2 = 0;
    iy2 = (i - 27*ix1 - 9*iy1 - 3*ix2);
    /* iterate over the squares where a vector in a certain direction
       could possibly begin */
    x1min = y1min = x2min = y2min = 0;
    x1max = y1max = x2max = y2max = 3;
    if (ix1 == 1) x1max = 0;
    if (iy1 == 1) y1max = 0;
    if (iy1 == -1) y1min = 3;
    if (ix2 == 1) x2max = 0;
    if (ix2 == -1) x2min = 3;
    if (iy2 == 1) y2max = 0;
    if (iy2 == -1) y2min = 3;
    for (x1 = x1min; x1<=x1max; x1++) for (y1 = y1min; y1<=y1max; y1++)
      for (x2 = x2min; x2<=x2max; x2++) for (y2 = y2min; y2<=y2max; y2++) {
	/* test to see if row is dominated by one player, and by how much */
	occPlay = -1; occNum = 0;
	for (j=0;j<4;j++) {
	  if (grid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] != -1) {
	    if (occPlay == -1) {
	      occPlay = grid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2];
	      occNum = 1;
	    } else if (occPlay == 
		       grid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2]) {
	      occNum += 1;
	    } else {
	      occNum = -1;
	      break;
	    }
	  }
	}
	/* having determined how much value there is to this row, we 
	   consider upgrades to the evaluation values of these squares */
	for (j=0;j<4;j++) {
	  if (grid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] == -1) {
	    squareVal = 0;
	    if (occNum == 3) {
	      squareVal = 500 - (occPlay<curplay ? numplay+occPlay : occPlay);
	      if (evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] < squareVal)
		evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] = squareVal;
	    } else if (occNum == 2) {
	      squareVal = 200 - (occPlay<curplay ? numplay+occPlay : occPlay);
	      if (250 > evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2]) {
		if (150 < evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2])
		  squareVal += 10;
		if (ix1*ix2*iy1*iy2 != 0 ||
		    40 < evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2]) 
		  squareVal += 1;
	      }
	    } else if (occNum == 1) {
	      squareVal = 50 - (occPlay<curplay ? numplay+occPlay : occPlay);
	      if (ix1*ix2*iy1*iy2 != 0) squareVal += 5;
	    } else {
	      if (ix1*ix2*iy1*iy2 != 0) squareVal += 5;
	    }
	    if (evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] < squareVal)
	      evalGrid[x1+j*ix1][y1+j*iy1][x2+j*ix2][y2+j*iy2] = squareVal;
	    if (squareVal > bestVal || (squareVal == bestVal &&
					Math.random() < 0.1)) {
	      bestSquare = 64*(x1+j*ix1) + 16*(y1+j*iy1) + 4*(x2+j*ix2)
		+ (y2+j*iy2);
	      bestVal = squareVal;
	    }
	  }
	}
      }
  }
  return bestSquare;
}
}

public class fourx extends java.applet.Applet implements Runnable {
  Thread thread;
  FourCanvas canvas;
  FourControls controls;
  public Game game;
  public Image piece_image[];  
  int maxplay;
  public int h_play, c_play;


  public void init() {

    h_play = 1;
    c_play = 1;
    game = new Game(h_play, c_play);
    
/* All images should be of equal size. Transparency is also good. */
    piece_image = new Image[game.maxplay];
    piece_image[0] = getImage(getDocumentBase(), "bluedot.gif");
    piece_image[1] = getImage(getDocumentBase(), "reddot.gif");
    piece_image[2] = getImage(getDocumentBase(), "greydot.gif");
    piece_image[3] = getImage(getDocumentBase(), "greendot.gif");
    piece_image[4] = getImage(getDocumentBase(), "yeldot.gif");
    piece_image[5] = getImage(getDocumentBase(), "purpdot.gif");
    piece_image[6] = getImage(getDocumentBase(), "pinkdot.gif");
    piece_image[7] = getImage(getDocumentBase(), "orndot.gif");
    setLayout(new BorderLayout());
    canvas = new FourCanvas(this);
    controls = new FourControls(this);
    add("Center", canvas);
    add("South", controls);
  }
  public boolean handleEvent(Event e) {
    if (e.id == Event.WINDOW_DESTROY) {
      System.exit(0);
    }
    return false;
  }
  public void n_g() {
    h_play = controls.h_num.getSelectedIndex();
    c_play = controls.c_num.getSelectedIndex();
    game.new_game(h_play, c_play);
    canvas.repaint();
  }
  public void start() {
    thread = new Thread(this);
    thread.start();
  }
  public void stop() {
    thread.stop();
  }
  public void run() {
    while (true) {
      try {
	Thread.currentThread().sleep(10000);
      } catch (InterruptedException e){}
    }
  } 
}

class FourControls extends Panel {
  fourx app_;
  public Button new_game;
  public Choice h_num;
  public Choice c_num;
  public Label h_txt;
  public Label c_txt;

  public FourControls(fourx app) {
    app_ = app;
    add(new_game = new Button("New Game"));
    add(h_txt = new Label("Humans:"));
    add(h_num = new Choice());
    add(c_txt = new Label("Robots:"));
    add(c_num = new Choice());
    
    h_num.addItem("0");
    h_num.addItem("1");
    h_num.addItem("2");
    h_num.addItem("3");
    h_num.addItem("4");
    c_num.addItem("0");
    c_num.addItem("1");
    c_num.addItem("2");
    c_num.addItem("3");
    c_num.addItem("4");
    h_num.select(1);
    c_num.select(1);
  }

  public boolean action(Event ev, Object arg) {
    if (ev.target instanceof Button) {
      String label = (String)arg;
      if (arg == "New Game") {
	app_.n_g();
      }
      return true;
    }
    return false;
  }
}

class FourCanvas extends Canvas {
  fourx app_;
  int prevw, prevh;
  Image offimage;

  public FourCanvas(fourx app) {
    app_ = app;
  }
  public void paint(Graphics g) {
    update(g);
  }
  public synchronized void update(Graphics g) {
    Dimension d = size();
    try {
      if (offimage == null || d.width != prevw || d.height != prevh) {
	prevw = d.width;
	prevh = d.height;
	offimage = createImage(d.width, d.height);
      }
      Graphics offg = offimage.getGraphics();
      offPaint(offg);
      g.drawImage(offimage, 0, 0, this);
    } catch (java.lang.OutOfMemoryError e) {
      System.out.println("Applet Banner ran out of memory");
      System.exit(1);
    }
  }
  void offPaint(Graphics g) {
    Dimension d = size();
    int i, j, k, l;
    g.clearRect(0, 0, d.width-1, d.height-1);
    g.setColor(Color.red);
    g.drawLine(0, 0, d.width-1, 0);
    g.drawLine(d.width-1, 0, d.width-1, d.height-1);
    g.drawLine(d.width-1, d.height-1, 0, d.height-1);
    g.drawLine(0, d.height-1, 0, 0);
    g.setColor(Color.black);
    for (i=0;i < 4;++i) {
      for (j=0;j<4;++j) {
	for (k=1;k<4;++k) {
	  g.drawLine(5+75*i+17*k, 5+75*j, 5+75*i+17*k, 73+75*j);
	  g.drawLine(5+75*i, 5+75*j+17*k, 73+75*i, 5+75*j+17*k);
	}
      }
    }
    for (i=0;i<app_.game.numplay;++i) {
      g.drawImage(app_.piece_image[i], 330, 20+i*30, this);
      if (i < app_.game.h_play) g.drawString("H" + (i+1), 350, 30+i*30);
      else g.drawString("R" + (i + 1 - app_.game.h_play), 350, 30+i*30);
      if (i==app_.game.curplay) g.drawString("***", 310, 30+i*30);
    }
    for (i=0;i<4;++i) for (j=0;j<4;++j) for (k=0;k<4;++k) for (l=0;l<4;++l) {
      if (app_.game.grid[i][j][k][l] >= 0) {
	g.drawImage(app_.piece_image[app_.game.grid[i][j][k][l]],
		    7+75*i+17*k, 7+75*j+17*l, this);
      }
    }
    if (app_.game.winplay >= 0) {
      g.drawString("** Victory for Player " + (app_.game.winplay + 1) + " **",
		   50, 350);
      for (i=0;i<4;++i) {
	g.drawLine(6+75*(app_.game.winnum[i]/64)+17*((app_.game.winnum[i]/4)%4),
		   6+75*((app_.game.winnum[i]/16)%4)+17*(app_.game.winnum[i]%4),
		   22+75*(app_.game.winnum[i]/64)+17*((app_.game.winnum[i]/4)%4),
		   22+75*((app_.game.winnum[i]/16)%4)+17*(app_.game.winnum[i]%4));
	g.drawLine(22+75*(app_.game.winnum[i]/64)+17*((app_.game.winnum[i]/4)%4),
		   6+75*((app_.game.winnum[i]/16)%4)+17*(app_.game.winnum[i]%4),
		   6+75*(app_.game.winnum[i]/64)+17*((app_.game.winnum[i]/4)%4),
		   22+75*((app_.game.winnum[i]/16)%4)+17*(app_.game.winnum[i]%4));
      }
    }
  }
  public boolean mouseUp(Event evt, int x, int y) {
    int x1, x2, y1, y2;

    if ((x-5) % 75 < 68 && (y-5) % 75 < 68 && app_.game.winplay == -1) {
      x1=(x-5)/75;
      y1=(y-5)/75;
      x2=(x-75*x1-5)/17;
      y2=(y-75*y1-5)/17;
      if (app_.game.proc_move(x1, y1, x2, y2)) {
	repaint();
      }
    }
    return true;
  }
}
    
      




