/**
 * The base class of the cavalry strategy 
 * @author:    Hanhua Feng -- hanhua@cs.columbia.edu
 *             Miqdad Mohammed -- mm1723@columbia.edu
 *             Alex Nicksay -- adn24@columbia.edu
 *             David Vespe -- djv@columbia.edu
 */

package Rectangles;

import ui.*;
import java.util.*;
import java.io.*;
import java.awt.Color;

public class Group5Player1 extends G5Cavalry implements IFCPlayer {

    int[][] _hist_x = new int[3][];
    int[][] _hist_y = new int[3][];

    public Robot[] register( Rectangles rect ) throws Exception {
	init( rect, rect.numRobots() );

	int gap = ( (_size + _nr_robots/2) / _nr_robots - 1 ) & (~1);
	if ( gap < 0 )
	    gap = 0;
	setParam( gap, gap+gap+1, _rand.nextInt(8) );
	return initRobots();
    }

    public void rewind() throws Exception {
	int gap = _gap - 2;
	if ( gap < 0 )
	    gap = 0;
	setParam( gap, gap+gap+1, _orientation );
	if ( _yreturn )
	    _ypos = _size - _width;
	else
	    _ypos = 0;
    }

    public char[] move() throws Exception {
	try 
	{
            if ( _game.numPlayers() == 2 ) {
                if ( chased() )
                    return stay();

                savePosition();
            }
	    return super.move();
	}
	catch ( Exception e )
	{
	    e.printStackTrace();
	}
	return stay();
    }

    public String name() throws Exception {
	return "Annealing Cavalry";
    }

    public Color color() throws Exception {
	return new Color( 0.5f, 1.0f, 1.0f );
    }

    public boolean interactive() throws Exception {
        return false;
    }

    void savePosition() throws Exception {
        for ( int i=_hist_x.length-1; i>0; i-- ) {
            _hist_x[i] = _hist_x[i-1];
            _hist_y[i] = _hist_y[i-1];
        }
        _hist_x[0] = new int[_nr_robots];
        _hist_y[0] = new int[_nr_robots];
        
        for ( int j=0; j<_nr_robots; j++ ) {
            _hist_x[0][j] = _my_robots[j].xpos();
            _hist_y[0][j] = _my_robots[j].ypos();
        }
    }

    boolean chased() throws Exception {
        int n = 0;
        if ( _hist_x[0] == null || _hist_x[1] == null )
            return false;

        Robot[][] all_robots = _game.allRobots();
        for ( int i=0; i<_game.numPlayers(); i++ ) {
            if ( i == _game.indexOf( this ) )
                continue;
            for ( int j=0; j<_nr_robots; j++ ) {
                int x = all_robots[i][j].xpos();
                int y = all_robots[i][j].ypos();
                
                for ( int k=0; k<_nr_robots; k++ ) {

                    if ( ( x == _my_robots[k].xpos() 
                           && y == _my_robots[k].ypos() )
                         || (x == _hist_x[0][k]) && (y == _hist_y[0][k])
                         || (x==_hist_x[1][k]) && (y==_hist_y[1][k]) )
                        n++;
                }
            }
        }

        return n > _nr_robots / 2;
    }
}
/**
 * The base class for project 1 : Rectangles
 * @author:    Hanhua Feng -- hanhua@cs.columbia.edu
 *             Miqdad Mohammed -- mm1723@columbia.edu
 *             Alex Nicksay -- adn24@columbia.edu
 *             David Vespe -- djv@columbia.edu
 */
/*
package Rectangles;

import ui.*;
import java.util.*;
import java.io.*;
*/
class G5Base implements IFCConstants {
    public static final int _CISVERT = 0x4;

    Rectangles _game;
    static Random _rand;
    int _size;
    int _nr_robots;
    Robot[] _my_robots = null;

    public G5Base() {
        if ( null == _rand )
            _rand = new Random();
    }
    
    public final void init( Rectangles rect, int num ) throws Exception{
	_game = rect;
	_size = _game.size();
	_nr_robots = num;
    }

    public Robot[] initRobots() throws Exception {
	return randomInitRobots();
    }

    public final Robot[] randomInitRobots() throws Exception {
	_my_robots = new Robot[ _nr_robots ];
	for ( int i=0; i<_nr_robots; i++ )
	    _my_robots[i] = new Robot( _rand.nextInt(_size), 
				       _rand.nextInt(_size) );
	return _my_robots;
    }

    void validatePos( Vertex pos ) throws Exception {
	int x = pos.xpos();
	int y = pos.ypos();

	if ( x < 0 )
	    pos.setXpos( 0 );
	else if ( x >= _size )
	    pos.setXpos( _size-1 );
	    
	if ( y < 0 )
	    pos.setYpos( 0 );
	else if ( y >= _size )
	    pos.setYpos( _size-1 );
    }

    char[] stay() {
        char[] moves = new char[ _nr_robots ];
        for ( int i=0; i<_nr_robots; i++ )
            moves[i] = _CSTAY;
        return moves;
    }

    char[] moveTo( Vertex[] positions ) throws Exception {
	char[] moves = new char[ _nr_robots ];

	boolean moved = false;

	for ( int i=0; i<_nr_robots; i++ )
	{
	    if ( _my_robots[i].xpos() == positions[i].xpos() ) {
		if ( _my_robots[i].ypos() == positions[i].ypos() )
		    moves[i] = _CSTAY;
		else 
		{
		    if ( _my_robots[i].ypos() < positions[i].ypos() )
			moves[i] = _CSOUTH;
		    else
			moves[i] = _CNORTH;
		    moved = true;
		}
	    } 
	    else 
	    {
		if ( _my_robots[i].xpos() < positions[i].xpos() )
		    moves[i] = _CEAST;
		else
		    moves[i] = _CWEST;

		moved = true;
	    }
	}

	if ( moved )
	    return moves;
	return null;
    }

    /*
     * HF - note: These tables translate directions according to the
     * location of the origin and the x/y orientation.  We have
     * totally 8 orientations.
     *
     * origin at: SE(southeast), NE(northeast), SW(southwest), NW(northwest)
     * x/y orientation: V(landscape), default: portrait
     * corresponding actions - F(flipping vertically) 
     *                         M(mirroring horizontally)
     *                         T(transpose x and y)
     *  index      orientation      actions
     *    0            SE             FM  (rotate 180)
     *    1            NE             M
     *    2            SW             F
     *    3            NW             -
     *    4            SEV            TFM (tranpose anti-diagonally)
     *    5            NEV            TM  (rotate 90CW)
     *    6            SWV            TF  (rotate 90CCW)
     *    7            NWV            T
     */
    private char[] __rotate_east = { 'W', 'W', 'E', 'E', 'N', 'S', 'N', 'S' };
    private char[] __rotate_west = { 'E', 'E', 'W', 'W', 'S', 'N', 'S', 'N' };
    private char[] __rotate_south = { 'N', 'S', 'N', 'S', 'W', 'W', 'E', 'E' };
    private char[] __rotate_north = { 'S', 'N', 'S', 'N', 'E', 'E', 'W', 'W' };

    final char rotateMove( char move, int orient ) throws Exception {
	switch ( move )
	{
	case _CEAST:
	    return __rotate_east[ orient ];
	case _CWEST:
	    return __rotate_west[ orient ];
	case _CNORTH:
	    return __rotate_north[ orient ];
	case _CSOUTH:
	    return __rotate_south[ orient ];
	default:
	    return move;
	}
    }

    final Vertex rotatePos( int x, int y, int orient ) throws Exception {
	if ( ( orient & _CISVERT ) != 0 )
	{
	    int t = x;
	    x = y;
	    y = t;
	}

        switch ( orient & 0x3 ) 
	{
	case _CSOUTHEAST:
	    y = _size - y - 1;
	case _CNORTHEAST:
	    x = _size - x - 1;
	    break;
	case _CSOUTHWEST:
	    y = _size - y - 1;
	    break;
	default:
	    break;
	}

	return new Vertex( x, y );
    }
}
/**
 * The base cavalry class
 * @author:    Hanhua Feng -- hanhua@cs.columbia.edu
 *             Miqdad Mohammed -- mm1723@columbia.edu
 *             Alex Nicksay -- adn24@columbia.edu
 *             David Vespe -- djv@columbia.edu
 */
/*
package Rectangles;

import ui.*;
import java.util.*;
import java.io.*;
*/
class G5Cavalry extends G5Base {
    int _orientation;

    int _gap;
    int _gap_s;
    int _step = 1;
    int _width;

    int _xpos;
    int _ypos;
    int _round;

    boolean _return, _yreturn;
    Vertex[] _new_pos;

    private int calcWidth( int gap ) {
	int width = ( gap + 2 ) * ( _nr_robots / 2 );
	if ( (_nr_robots & 1 ) != 0 )
	    width += ( ( gap + 1 ) / 2 ) + 1;
	return width;
    }

    public void setParam( int gap, int step, int orientation ) {
	_gap = gap;
	_step = step;
	_orientation = orientation;

	if ( step <= 0 )
	    _step = 1;

	_width = calcWidth( gap );
	if ( _width >= _size ) 
	{
	    _gap = _size / ( _nr_robots/2 + 1) - 2;
	    if ( _gap < 0 )
		_gap = 0;
	    _width = calcWidth( _gap );
	}

	_gap_s = ( _gap + 1 ) / 2;
    }

    private final int calcYOff( int idx ) {
	if ( ( _nr_robots & 1 ) != 0 && idx == _nr_robots-1 ) 
	    return ( _gap + 2 ) * ( _nr_robots/2 );

	return _gap_s + ( _gap + 2 ) * ( idx/2 ) + ( ~_gap & idx & 1 );
    }

    private final int calcYOff1( int idx ) {
	if ( idx == _nr_robots - 1 ) 
	    return _width-1;

	return ( _gap + 2 ) * ( (idx + 1)/2 ) - ( idx & 1 );
    }

    public Robot[] initRobots() throws Exception {
	_my_robots = new Robot[ _nr_robots ];
	
	for ( int i=0; i<_nr_robots; i++ ) 
        {
	    Vertex pos = rotatePos( 0, calcYOff(i), _orientation );
	    validatePos( pos );
	    _my_robots[i] = new Robot( pos.xpos(), pos.ypos() );
	}

	_xpos = _ypos = _round = 0;
	_return = _yreturn = false;
	_new_pos = null;

	return _my_robots;
    }

    public char[] move() throws Exception {
	char[] moves;

	if ( _new_pos != null ) 
	{
	    moves = moveTo( _new_pos );
	    if ( moves != null )
		return moves;
	    _new_pos = null;
	}

	moves = moveDefault();
	for ( int i=0; i<moves.length; i++ )
	    moves[i] = rotateMove( moves[i], _orientation );
	return moves;
    }

    public void rewind() throws Exception {
    }

    private final Vertex[] calcNewPos() throws Exception {
	if ( _yreturn )
	{
	    _ypos = ( _ypos > _width ? _ypos - _width : 0 );
	    if ( 0 == _ypos )
	    {
		_yreturn = false;
		rewind();
	    }
	}
	else
	{
	    _ypos = ( _ypos + _width + _width >= _size ? 
		      _size - _width : _ypos + _width );
	    if ( _ypos + _width == _size )
	    {
		_yreturn = true;
		rewind();
	    }
	}

	_new_pos = new Vertex[ _nr_robots ];

	for ( int i=0; i<_nr_robots; i++ )
	{
	    _new_pos[i] = rotatePos( _return ? 0 : _size-1,
				     _ypos + calcYOff1( i ),
				     _orientation );
	    validatePos( _new_pos[i] );
	}

	_round = _gap_s;
	_return = !_return;

	return _new_pos;
    }

    private final char[] moveDefault() throws Exception {
	char[] moves = new char[ _nr_robots ];

	if ( _round < _gap_s ) 
	{
	    for ( int i=0; i<_nr_robots/2; i++ )
	    {
		moves[i+i] = _CNORTH;
		moves[i+i+1] = _CSOUTH;
	    }
	    
	    if ( ( _nr_robots & 1 ) != 0 )
		moves[ _nr_robots - 1 ] = _CSOUTH;
	}
	else if ( _round < _gap_s + _step )
        {	    
	    if ( _return )
	    {
		for ( int i=0; i<_nr_robots; i++ )
		    moves[i] = _CWEST;
		_xpos--;

		if ( 0 == _xpos )
		    calcNewPos();
	    }
	    else
	    {
		for ( int i=0; i<_nr_robots; i++ )
		    moves[i] = _CEAST;
		_xpos++;

		if ( _xpos == _size - 1 )
		    calcNewPos();
	    }
	}
	else if ( _round < _gap_s*2 + _step )
	{
	    for ( int i=0; i<_nr_robots/2; i++ )
	    {
		moves[i+i] = _CSOUTH;
		moves[i+i+1] = _CNORTH;
	    }
	    
	    if ( ( _nr_robots & 1 ) != 0 )
		moves[ _nr_robots - 1] = _CNORTH;
	}
	else 
	{
	    if ( _return )
	    {
		for ( int i=0; i<_nr_robots; i++ )
		    moves[i] = _CWEST;
		_xpos--;
	    }
	    else
	    {
		for ( int i=0; i<_nr_robots; i++ )
		    moves[i] = _CEAST;
		_xpos++;
	    }

	    _round = -1;
	}

	if ( _round == _gap_s - 1 )
	{
	    if ( ( _return && 0 == _xpos ) 
		 || ( !_return && _xpos == _size - 1 ) )
		calcNewPos();
	}

	_round++;
	return moves;
    }
}
