// DotGame.java

// Art Matheny <matheny@usf.edu>
// Academic Computing Department
// University of South Florida

// 4/17/96

import java.applet.Applet;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Event;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Point;
import java.awt.TextField;
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;
	}
}


