// DotGame.java

// Art Matheny <matheny@usf.edu>
// Academic Computing Department
// University of South Florida

// 4/17/96

import java.applet.*;
import java.awt.*;
import java.util.Random;

class dgVertex extends Canvas {
   public int group;

   public dgVertex (int x, int y, int vertex_size) {
      resize (vertex_size, vertex_size);
      move (x, y + 40);       // why 30? beats me!
      group = 0;
   }

   public void paint (Graphics g) {
      setBackground (Color.black);
      g.setColor (Color.black);
      g.drawRect (location().x, location().y,
            size().width, size().height );
   }
}

class dgEdge extends Canvas {
   private boolean drawn;
   private dgVertex v1, v2;

   public dgEdge (dgVertex end_point1, dgVertex end_point2) {
      drawn = false;
      v1 = end_point1;
      v2 = end_point2;

      Point p1 = v1.location();
      Point p2 = v2.location();
      int vertex_size = v1.size().width;
      if (p1.x == p2.x) {           // verticle edge
         resize (vertex_size, p2.y - p1.y - vertex_size);
         move (p1.x, p1.y + vertex_size);
      } else {                      // horizontal egde
         resize (p2.x - p1.x - vertex_size, vertex_size);
         move (p1.x + vertex_size, p1.y);
      }
   }

   public boolean isDrawn() {
      return drawn;
   }

   public void turnOn (Color c) {
      setBackground (c);
      drawn = true;
   }

   public void turnOff () {
      setBackground (Color.lightGray);
      drawn = false;
   }

   public void paint (Graphics g) {
      g.drawRect (location().x, location().y,
            size().width, size().height );
   }

   public int[] getGroups() {
      int grp[] = new int[2];
      if (v1.group > v2.group) {
         grp[0] = v1.group;
         grp[1] = v2.group;
      } else {
         grp[0] = v2.group;
         grp[1] = v1.group;
      }
      return grp;
   }

   public boolean mouseDown (Event evt, int mouse_x, int mouse_y) {
      if ( ! drawn) {
         turnOn (Color.black);
         return false;
      } return true;
   }

   public boolean mouseEnter (Event evt, int mouse_x, int mouse_y) {
      if ( ! drawn) setBackground (Color.orange);
      return true;
   }

   public boolean mouseExit (Event evt, int mouse_x, int mouse_y) {
      if ( ! drawn) setBackground (Color.lightGray);
      return true;
   }
}

class dgCell extends Canvas {
   private boolean claimed;
   private dgEdge left, top, right, bottom;

   public dgCell (dgEdge l, dgEdge t, dgEdge r, dgEdge b) {
      claimed = false;
      left = l;
      top = t;
      right = r;
      bottom = b;
      resize (top.size().width, left.size().height);
      move (top.location().x, left.location().y);
   }

   public void turnOn (Color c) {
      setBackground (c);
      claimed = true;
   }

   public void turnOff () {
      setBackground (Color.lightGray);
      claimed = false;
   }

   public int weight () {
      if (claimed) return 0;
      int w = 0;
      if (left.isDrawn() ) w++;
      if (top.isDrawn() ) w++;
      if (right.isDrawn() ) w++;
      if (bottom.isDrawn() ) w++;
      return w;
   }

   public dgEdge openSide () {
      if ( ! left.isDrawn() ) return left;
      if ( ! top.isDrawn() ) return top;
      if ( ! right.isDrawn() ) return right;
      if ( ! bottom.isDrawn() ) return bottom;
      return null;
   }

   public void paint (Graphics g) {
      g.drawRect (location().x, location().y,
            size().width, size().height );
   }
}

// This is the window where the game is played
class dgGame extends Frame {
   private dgVertex vertex[];
   private dgEdge edge[];
   private dgCell cell[];
   private int n_vertices;
   private int n_edges;
   private int n_cells;
   private int player;
   private int score[];
   private Color player_color[];
   private Label board_name[];
   private Label board_score[];
   private Random random;
   private Label status;

   public dgGame (int width, int height, int window_width,
         int window_height, String name[]) {
      super ("Dot Game Grid");
      n_vertices = width*height;
      n_edges = 2*n_vertices - width - height;
      n_cells = (width - 1)*(height - 1);
      vertex = new dgVertex[n_vertices];
      edge = new dgEdge[n_edges];
      cell = new dgCell[n_cells];
      score = new int[2];
      player_color = new Color[2];
      player_color[0] = Color.green;
      player_color[1] = Color.yellow;
      board_name = new Label[2];
      board_name[0] = new Label (name[0], Label.RIGHT);
      board_name[1] = new Label (name[1], Label.RIGHT);
      board_score = new Label[2];
      board_score[0] = new Label("0");
      board_score[0].setBackground (player_color[0]);
      board_score[1] = new Label("0");
      board_score[1].setBackground (player_color[1]);
      random = new Random();
      status = new Label();
      int cell_size, vertex_size, border, row, col, vrtx;

      setBackground (Color.lightGray);
      resize (window_width, window_height);

      // Get grid dimensions
      cell_size = window_width/width;
      if (cell_size > window_height/height)
            cell_size = window_height/height;
      vertex_size = cell_size/6;
      if (vertex_size < 3) vertex_size = 3;
      cell_size -= vertex_size;
      border = (window_width - (width - 1)*cell_size - vertex_size)/2;

      // Define vertices
      int vertex_count = 0;
      for (row=0 ; row<height ; row++) {
         for (col=0 ; col<width ; col++) {
            vertex[vertex_count] = new dgVertex (
                  border + col*cell_size,
                  border + row*cell_size,
                  vertex_size);
            add (vertex[vertex_count]);
            vertex_count++;
         }
      }

      // Define edges
      int edge_count = 0;
      for (row=0 ; row<height ; row++) {
         for (col=1 ; col<width ; col++) {
            vrtx = row*width + col;
            edge[edge_count] = new dgEdge (
                  vertex[vrtx - 1], vertex[vrtx]);
            add (edge[edge_count]);
            edge_count++;
         }
      }
      for (row=1 ; row<height ; row++) {
         for (col=0 ; col<width ; col++) {
            vrtx = row*width + col;
            edge[edge_count] = new dgEdge (
                  vertex[vrtx - width], vertex[vrtx]);
            add (edge[edge_count]);
            edge_count++;
         }
      }

      // Define cells
      int cell_count = 0;
      for (row=0 ; row<height-1 ; row++) {
         for (col=0 ; col<width-1 ; col++) {
            cell[cell_count] = new dgCell (
                  edge[n_edges/2 +row*width + col],
                  edge[row*(width-1) + col],
                  edge[n_edges/2 +row*width + col + 1],
                  edge[(row+1)*(width-1) + col]);
            add (cell[cell_count]);
            cell_count++;
         }
      }

      // Score board
      Panel panel = new Panel();
      panel.setLayout (new GridLayout (2, 3) );
      panel.add (board_name[0]);
      panel.add (board_score[0]);
      panel.add (new Button ("Clear") );
      panel.add (board_name[1]);
      panel.add (board_score[1]);
      panel.add (new Button ("Quit") );
      this.add ("South", panel);

      // Initialize the scores
      resetGame();

      // Status line
      this.add ("North", status);
   }

   public void clearBoard () {
      int k;
      for (k=0 ; k<n_cells ; k++) cell[k].turnOff  ();
      for (k=0 ; k<n_edges ; k++) edge[k].turnOff  ();
   }

   public void resetGame () {
      player = 0;
      score[0] = score[1] = 0;
      board_score[0].setText ("0");
      board_score[1].setText ("0");

      // Highlight first player on score board
      showPlayer();

      // Initialize the vertex groups
      for (int v=0 ; v<n_vertices ; v++) vertex[v].group = v;

      // Does Java Man make the first move?
      while (board_name[player].getText().equalsIgnoreCase ("Java Man")
            && JavaMan() != null) {
         ScanGrid();
      }
   }

   // Java Man's move
   private dgEdge JavaMan () {

      // Take cells if possible
      int m;
      for (m=0 ; m<n_cells ; m++) {
         if (cell[m].weight() == 3) {
            dgEdge edg = cell[m].openSide();
            edg.turnOn (Color.magenta);
            mergeGroup (edg);
            return edg;
         }
      }

      // Count group connections
      int grp[];
      int v, w;
      int connect[][] = new int[n_vertices][n_vertices];
      for (v=0 ; v<n_vertices ; v++)
         for (w=0 ; w<=v ; w++) connect[v][w] = 0;
      for (m=0 ; m<n_edges ; m++) {
         grp = edge[m].getGroups();
         connect[grp[0]][grp[1]]++;
      }

      // Find how many cells we have to concede
      int best = n_vertices;
      for (v=1 ; v<n_vertices ; v++)
         for (w=0 ; w<v ; w++)
            if (connect[v][w] > 0 && best > connect[v][w])
               best = connect[v][w];

      // Pick a move that concedes the minimum
      int r = random.nextInt() % n_edges;
      if (r < 0) r = -r;
      for (m=0 ; m<n_edges ; m++) {
         if ( ! edge[r].isDrawn() ) {
            grp = edge[r].getGroups();
            if (connect[grp[0]][grp[1]] == best) {
               edge[r].turnOn (Color.magenta);
               mergeGroup (edge[r]);
               return edge[r];
            }
         }
         r++;
         if (r >= n_edges) r = 0;
      }
      return null;
   }

   private void showPlayer () {
      board_name[player].setBackground (Color.orange);
      board_name[player ^ 1].setBackground (Color.lightGray);
   }

   private void ScanGrid() {
      Color c = player_color[player];
      int filled_cells, count;

      filled_cells = 0;
      do {
         count = 0;
         for (int k=0 ; k<n_cells ; k++) {
            if (cell[k].weight() == 4) {
               count++;
               cell[k].turnOn (c);
            }
         }
         filled_cells += count;
      } while (count > 0);
      if (filled_cells > 0) {
         score[player] += filled_cells;
         board_score[player].setText (
               (new Integer (score[player])).toString() );
      } else {
         player ^= 1;
         showPlayer();
      }
   }

   private void mergeGroup (dgEdge edg) {
      int grp[] = edg.getGroups();
      for (int v=0 ; v<n_vertices ; v++)
         if (vertex[v].group == grp[0]) vertex[v].group = grp[1];
   }
      
   public boolean mouseDown (Event evt, int mouse_x, int mouse_y) {
      if (evt.target instanceof dgEdge) {
         for (int k=0 ; k<n_edges ; k++) if (edge[k].isDrawn() )
            edge[k].setBackground (Color.black);
         mergeGroup ( (dgEdge)(evt.target) );
         ScanGrid();
         while (board_name[player].getText().equalsIgnoreCase ("Java Man")
               && JavaMan() != null) {
            ScanGrid();
         }
         return true;
      }
      return false;
   }

   public boolean action (Event evt, Object arg) {
      if (evt.target instanceof Button) {
         String button_value = (String) arg;
         clearBoard();
         resetGame();
         if (button_value.equals ("Quit") ) dispose();
         return true;
      }
      return false;
   }
}

public class DotGame extends Applet {
   private dgGame game;
   private TextField name[];
   private Choice grid_size;

   public void init () {
      game = null;
      name = new TextField[2];
      name[0] = new TextField ("You", 15);
      name[1] = new TextField ("Java Man", 15);

      // Options panel
      Panel options_panel = new Panel();
      GridBagLayout gridbag = new GridBagLayout();
      GridBagConstraints c = new GridBagConstraints();
      options_panel.setLayout (gridbag);
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 1.0;

      // Player names
      Label player1_label =
         new Label ("Player 1 Name", Label.RIGHT);
      c.gridwidth = 1;
      gridbag.setConstraints (player1_label, c);
      options_panel.add (player1_label);
      c.gridwidth = GridBagConstraints.REMAINDER;
      gridbag.setConstraints (name[0], c);
      options_panel.add (name[0]);
      Label player2_label =
         new Label ("Player 2 Name", Label.RIGHT);
      c.gridwidth = 1;
      gridbag.setConstraints (player2_label, c);
      options_panel.add (player2_label);
      c.gridwidth = GridBagConstraints.REMAINDER;
      gridbag.setConstraints (name[1], c);
      options_panel.add (name[1]);

      // Grid size selector
      Label game_size_label =
         new Label ("Grid Size", Label.RIGHT);
      c.gridwidth = 1;
      gridbag.setConstraints (game_size_label, c);
      options_panel.add (game_size_label);
      grid_size = new Choice();
         grid_size.addItem ("four");
         grid_size.addItem ("five");
         grid_size.addItem ("six");
         grid_size.addItem ("seven");
         grid_size.addItem ("eight");
         grid_size.addItem ("nine");
         grid_size.addItem ("ten");
         grid_size.select ("six");
      c.gridwidth = GridBagConstraints.REMAINDER;
      gridbag.setConstraints (grid_size, c);
      options_panel.add (grid_size);

      // Play button
      Button play_button = new Button ("Play the Game");
      gridbag.setConstraints (play_button, c);
      options_panel.add (play_button);

      // Add the options panel
      add (options_panel);
      resize (preferredSize() );
   }

   public void start() {
      if (game != null && ! game.isVisible() ) game.show();
   }

   public void stop () {
      if (game != null && game.isVisible() ) game.hide();
   }

   public boolean action (Event evt, Object arg) {
      if (evt.target instanceof Button) {
         showStatus ("Setting up the game");
         int grid = grid_size.getSelectedIndex() + 4;
         if (name[0].getText().length()==0) name[0].setText ("You");
         if (name[1].getText().length()==0) name[1].setText ("Java Man");
         String handle[] = new String[2];
         handle[0] = name[0].getText();
         handle[1] = name[1].getText();
         if (game != null) game.dispose();
         game = new dgGame (grid, grid, 224, 340, handle);
         if ( ! game.isVisible() ) {
            game.show();
         }
         showStatus (handle[0] + " vs " + handle[1]);
         return true;
      }
      return false;
   }
}
