import java.io.*;
import java.util.*;

/********
 * 
 * Implements the board for playing clobber.
 * 
 * @author gtowell
 *
 */
public class ClobberBoard
{
	/**
	 * The actual thing that holds the board
	 * There are only 3 acceptable characters on the board, 'w', 'b' and ' ' (space)
	 * At the beginning of a game only 'w' and 'b' are acceptable
	 * I use an array of char here because I need 2 bits and char is, I think, the next smallest
	 * primitive type, using 16 bits.  If I was really worried about space, I could use two boolean arrays.
	 */
	protected char[][] theboard;
	
	/**
	 * This method makes a brand new instance of a board that tis otherwise identical
	 * to the boeard of the instance on whcih this method was called.  This will come in handy 
	 * when building a game tree.
	 * @return the new clobberboard
	 */
	public ClobberBoard makeCopy()
	{
			return new ClobberBoard(theboard);
	}
	
	/**
	 * A copy constructor for clobberboards.  That is, this constructor creates a new clobberboard
	 * that is identical to the clobberboard passed in as an arguement except that the two objects
	 * use completely different chunks of memory.  Note that this constructor is protected.  This is
	 * intentional.  This constructor should never be called directly, but only through the 
	 * public makeCopy method.
	 * @param cb the clobberboard to be copied.
	 */
	protected ClobberBoard(char[][] oldboard)
	{
		theboard = new char[oldboard.length][oldboard[0].length];
		for (int h=0; h<oldboard.length; h++)
			for (int w=0; w<oldboard[h].length; w++)
				theboard[h][w]=oldboard[h][w];
	}
	
	/**
	 * Sets up a board by reading it from a file
	 * 
	 * @param filename the name of the file to read from
	 */
	public ClobberBoard(String filename)
	{
		ArrayList<char[]> aa = new ArrayList<char[]>();
		try
		{
			BufferedReader br = new BufferedReader(new FileReader(filename));
			while (br.ready())
			{
				String ss = br.readLine().trim();
				aa.add(ss.toCharArray());
			}
		}
		catch (Exception e)
		{
			System.err.println("Problem reading the board");
		}
		theboard = new char[aa.size()][];
		int i=0;
		for (Object o : aa)
		{
			theboard[i++]=(char[])o;
		}
	}
	
	/**
	 * Make a checkerboard style clobber board
	 * @param width the width of the board
	 * @param height the height of the board
	 */
	public ClobberBoard(int width, int height)
	{
		this(width, height, false);
	}
	
	/**
	 * Make a clobberboard.
	 * @param width the width of the board
	 * @param height the height of the board
	 * @param randomize if false, then a checkerboard.  If true, then a randomized board
	 * with equal number of black and white
	 */
	public ClobberBoard(int width, int height, boolean randomize)
	{
		theboard=new char[height][width];
		char prev ='b';
		for (int y=0; y<height; y++)
			for (int x=0;x<width; x++)
			{
				theboard[y][x]=prev;
				if (prev=='w')
					prev='b';
				else
					prev='w';
			}
		if (randomize)
		{
			char[] linear = new char[width*height];
			int p=0;
			for (int y=0; y<height; y++)
				for (int x=0; x<width; x++)
					linear[p++]=theboard[y][x];
			Random r = new Random();
			for (int i=linear.length; i>2; i--)
			{
				int j = r.nextInt(i);
				char tmp=linear[i-1];
				linear[i-1]=linear[j];
				linear[j]=tmp;
			}
			p=0;
			for (int y=0; y<height; y++)
				for (int x=0; x<width; x++)
					theboard[y][x]=linear[p++];
		}
	}
	
	/**
	 * A string rendering of the board
	 */
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		sb.append(" ");
		for (int y=0; y<theboard[0].length; y++)
			sb.append(" " + y);
		sb.append("\n");
		for (int x=0; x<theboard.length; x++)
		{
			sb.append(x + " ");
			for (int y=0; y<theboard[x].length; y++)
			{
				sb.append(theboard[x][y]+" ");
			}
			sb.append("\n");
		}
		return sb.toString();
	}
	/**
	 * determine if a clobbering move to this location is possible
	 * @param x  the x location to look at
	 * @param y the y location to look at
	 * @param cl the color to be clobbered
	 * @return true if this is a clobberable location
	 */
	private boolean clobber(int x, int y, char cl)
	{
		if (x<0)
			return false;
		if (y<0)
			return false;
		if (y>=theboard.length)
			return false;
		if (x>=theboard[y].length)
			return false;
		return theboard[y][x]==cl;
	}
	
	/***
	 * Actually make a move, clobbering it.
	 * @param aa the move to be made
	 */
	public void makeMove(Amove aa)
	{
		char mover = theboard[aa.getStartY()][aa.getStartX()];
		if (mover=='_')
		{
			System.err.println(aa + " is not a black or white");
			return;
		}
		char onto='b';
		if (onto==mover)
			onto='w';
		// check to see if the proposed move is legal
		ArrayList<Amove> al = legalMoves(aa.getStartX(), aa.getStartY(), mover, onto);
		boolean goodmove=false;
		for (Amove taa : al)
			if (taa.same(aa))
				goodmove=true;
		if (!goodmove)
		{
				System.err.println(aa + " is not a legal move");
				return;
		}
		// make this move
		theboard[aa.getStartY()][aa.getStartX()]='_';		
		theboard[aa.getEndY()][aa.getEndX()]=mover;
	}
	/**
	 * Determine all of the legal moves from a given point
	 * @param x the x point to look for legal moves from
	 * @param y the y point to look for legal moves from
	 * @param mover the color tile to be moved
	 * @param onto
	 * @return a list of all the possible moves from that spot
	 */
	public ArrayList<Amove> legalMoves(int x, int y, char mover, char onto)
	{
		ArrayList<Amove> am = new ArrayList<Amove>();
		if (theboard[y][x]==mover)
		{
			if (clobber(x-1,y,onto))
				am.add(new Amove(x,y	,x-1,y));
			if (clobber(x,y-1,onto))
				am.add(new Amove(x,y,x,y-1));
			if (clobber(x+1,y,onto))
				am.add(new Amove(x,y,x+1,y));
			if (clobber(x,y+1,onto))
				am.add(new Amove(x,y,x,y+1));
		}
		return am;
	}
	
	/**
	 * Build a list of all the legal moves for a player
	 * @param mover the player (w or b) whose moves are being collected
	 * @return a list of all the available moves
	 */
	public ArrayList<Amove> legalMoves(char mover)
	{
		ArrayList<Amove> am = new ArrayList<Amove>();
		char onto='b';	
		if (mover==onto) 
			onto='w';
		for (int y=0; y<theboard.length; y++)
			for (int x=0; x<theboard[y].length; x++)
			{
				am.addAll(legalMoves(x,y,mover,onto));
			}
		return am;
	}
}
