/**
 * Group5Player2 of Rectangles
 * @author:    Hanhua Feng -- hanhua@cs.columbia.edu
 *             Miqdad Mohammed -- mm1723@columbia.edu
 *             Alex Nicksay -- adn24@columbia.edu
 *             David Vespe -- djv@columbia.edu
 *
 * Note: If there are name clashes, Please change the package name.
 */

package Rectangles;

import java.awt.Color;
import java.util.*;
import Rectangles.*;


/** 
 * The basic game interface.  
 *
 * Note: the actual game class is an implementation of this interface.
 * The reasons to use an interface instead of extending of IFCPlayer are:
 *   (1) The program can be compiled without the mediator software, and
 *   (2) A rotated board can be implemented with existing board.
 */

abstract class Board {
    /* the direction constants are re-defined, because sometime we need to
     * check up a table or doing bit operations over the constants 
     */
    public final static int EAST = 0; 
    public final static int WEST = 1;
    public final static int SOUTH = 2;
    public final static int NORTH = 3;
    public final static int STAY = 4;
    public final static int QUIT = -5;
    public final static int INVALID = -1;

    /** the random number seed */
    public static java.util.Random _rand = new java.util.Random();

    /* The players are also re-ordered.  The Player #0 is always my player
     * and other players are counted from 1 to n-1
     */

    /** get the x coordinate of my i-th agent
     */
    public abstract int getMyAgentX( int i );

    /** get the y coordinate of my i-th agent
     */
    public abstract int getMyAgentY( int i );

    /** get number of agents in each team
     */
    public abstract int getAgentNum();
    
    /** get number of teams
     */
    public abstract int getTeamNum();

    /** get the x coordinate of the i-th agent in the opp-th team
     */
    public abstract int getAgentX( int opp, int i );

    /** get the y coordinate of the i-th agent in the opp-th team
     */
    public abstract int getAgentY( int opp, int i );

    /** get the last move direction of the i-th agent in the opp-th team
     */
    public abstract int getLastMove( int opp, int i );

    /** get the board size
     */
    public abstract int size();

    /** is the square (x,y) black?
     */
    public abstract boolean squareIsBlack( int x, int y );

    /** is the square (x,y) colored?
     */
    public abstract boolean squareIsColored( int x, int y );

    /** get the color of the square (x,y)
     */
    public abstract int squareOwner( int x, int y );

    /** get the score of i-th team
     */
    public abstract int getScore( int i );

    /** map the move direction back to the original configuration
     */
    public abstract int unmapMove( int i );
    
    /** map the original move direction to the current configuration
     */
    public abstract int mapMove( int i );

    /** get the position of my i-th agent
     */
    public Position getMyAgentPos( int i ) {
        return new Position( getMyAgentX(i), getMyAgentY(i) );
    }

    /** get the position of the i-th agent of the opp-th team
     */
    public Position getAgentPos( int opp, int i ) {
        return new Position( getAgentX(opp,i), getAgentY(opp,i) );
    }

    /** move to agent i to place (x,y) in certain steps.  If there
     * are several paths that an agent can take, choose the most
     * destructive one to enemies.
     */
    public int moveTo( int agent, int x, int y, int steps ) {
        int x0 = getMyAgentX( agent );
        int y0 = getMyAgentY( agent );
/*
        int t = moveToSlow( x0, y0, x, y, steps );
        System.out.println( "Agent " + agent + " at " 
                            + getMyAgentX(agent) + "," + getMyAgentY(agent) 
                            + " is moving to " + x + "," + y + " direction " 
                            + t + ": " + unmapMove(t) );
*/
        if ( Math.abs( x0-x ) + Math.abs( y0-y ) >= steps - 1 )
            return moveToFast( x0, y0, x, y );
        return moveToSlow( x0, y0, x, y, steps );
    }

    /** move to from (x0,y0) to place (x,y) using the fastest and best way
     */
    int moveToFast( int x0, int y0, int x, int y ) {
        // If it is at the destination place, stay here
        if ( x0 == x && y0 == y )
            return QUIT;

        // check distance and directions
        int dx = x - x0;
        int dy = y - y0;
        boolean dxsgn = ( dx < 0 ), dysgn = ( dy < 0 );

        if ( dxsgn )
            dx = -dx;
        if ( dysgn )
            dy = -dy;

        // are the source and the destination in the same row?
        if ( 0 == dx )
            return dysgn ? NORTH : SOUTH;
        // same column?
        if ( 0 == dy )
            return dxsgn ? WEST : EAST;
        
        // now we have multiple paths
        int[] buf = new int[(dx+1)*(dy+1)];   // use one dimensional
        int s = dy + 1;                          // distance between lines
        
        // a typical dynamic-programming problem: O(dx*dy)
        buf[0] = 0;
        for ( int j=1; j<=dy; j++ )
            buf[j] = buf[j-1] 
                + getSquareOwnerScore( x, dysgn ? y + j : y - j );
                
        for ( int i=1; i<=dx; i++ ) {
            buf[i*s] = buf[(i-1)*s] 
                + getSquareOwnerScore( dxsgn ? x + i : x - i, y );
            
            for ( int j=1; j<=dy; j++ ) {
                buf[i*s+j] = Math.max( buf[(i-1)*s+j], buf[i*s+(j-1)] )
                    + getSquareOwnerScore( dxsgn ? x + i : x - i,
                                           dysgn ? y + j : y - j );
            }
        }
        
        if ( buf[(dx-1)*s+dy] > buf[dx*s+(dy-1)] )
            return dxsgn ? WEST : EAST;
        return dysgn ? NORTH : SOUTH;
    }

    /** move from (x0,y0) to place (x,y) in certain steps, which is at
     * lest two more than necessary.  If there are several paths that
     * an agent can take, choose the most destructive one to enemies.
     *
     * Theproblem is actually the longest simple path problem.  I use
     * a more simple model: find a more destructive nearby point that
     * it can go through and reach destination in certain steps.
     */
    public int moveToSlow( int x0, int y0, int x, int y, int steps ) {
        int sz = size();

        int xs = Math.max( x-steps, Math.max( x0-steps, 0 ) );
        int xe = Math.min( x+steps, Math.min( x0+steps, sz-1 ) );
        int ys = Math.max( y-steps, Math.max( y0-steps, 0 ) );
        int ye = Math.min( y+steps, Math.min( y0+steps, sz-1 ) );
        int score = -1;
        int dist = 0;
        int xm, ym;

        for ( int i=xs; i<xe; i++ )
            for ( int j=ys; j<ye; j++ ) {
                int x0dist = Math.abs(x0-i)+Math.abs(y0-j);
                if ( Math.abs(x-i) + Math.abs(y-j) + x0dist <= steps ) {
                    int s = getSquareOwnerScore( i, j );
                    if ( s > score || ( s == score && x0dist <= dist ) ) {
                        score = s;
                        dist = x0dist;
                        xm = i;
                        ym = j;
                    }
                }
            }

        return moveToFast( x0, y0, x, y );
    }

    /** the benefit to go over a square (x,y)
     *      Black squares        0
     *      White squares        my own score
     *      My squares           1
     *      Oppenent's squares   corresponding oppenent's score
     */
    public int getSquareOwnerScore( int x, int y ) {
        if ( squareIsBlack( x, y ) )
            return 0;
        if ( !squareIsColored( x, y ) )
            return getScore( 0 );
        int i = squareOwner( x, y );
        if ( 0 == i )
            return 1;
        return getScore( i );
    }

    /** find the square in this or neighboring squares, with highest
     * owner score
     * @return a direction to move to
     */
    public int getBestNextSquare( int x, int y ) {
        int sz = size();
        int m = STAY;
        int score = getSquareOwnerScore( x, y );
        if ( x > 0 ) {
            int s = getSquareOwnerScore( x-1, y );
            if ( s > score ) {
                m = WEST;
                score = s;
            }
        }

        if ( x < sz-1 ) {
            int s = getSquareOwnerScore( x+1, y );
            if ( s > score ) {
                m = EAST;
                score = s;
            }
        }

        if ( y > 0 ) {
            int s = getSquareOwnerScore( x, y-1 );
            if ( s > score ) {
                m = NORTH;
                score = s;
            }
        }

        if ( y < sz-1 ) {
            int s = getSquareOwnerScore( x, y+1 );
            if ( s > score ) {
                m = SOUTH;
                score = s;
            }
        }
        
        return m;
    }

    /** find a sub-quest that can be done in certain steps, given
     * the insertion point and extraction point.  This should be
     * most important part of the AI.  The quest should be accomplished
     * in time - even fail the quest for the extraction point.
     */
    Quest getQuest( int agent, int steps, Position extr ) {
        // nothing can be done without time
        if ( steps < 0 )
            return null;

        int x = getMyAgentX( agent );
        int y = getMyAgentY( agent );
        
        // already in place?
        if ( extr.at( x, y )  ) {

            if ( 1 == steps ) {
                // Only one step can be spent: stay there if the square
                // was contested
                if ( !squareIsBlack( x, y ) &&
                     !squareIsColored( x, y ) ) {
                    Quest q = new MoveQuest( this, agent, extr, STAY );
                    q.setDeadline( steps );
                    return q;
                } else
                    return null;
            } else if ( 2 == steps || 3 == steps ) {
                // Only two or three steps can be spent: change an adjacent
                // square
                Quest q = new MoveQuest( this, agent, extr, 
                                getBestNextSquare( x, y ) );
                q.setDeadline( steps );
                return q;
            } 

            return new MoveQuest( this, agent, extr );
        }
        return new MoveQuest( this, agent, extr );
    }

    /** check whether a horizontal line is fully colored (in my own
     * color)
     */
    boolean lineIsColored( int x0, int x1, int y ) {
        for ( int i=x0; i<=x1; i++ )
            if ( squareOwner( i, y ) != 0 )
                return false;
        return true;
    }

    /** check whether a vertical line is fully colored (in my own
     * color)
     */
    boolean vlineIsColored( int x, int y0, int y1 ) {
        for ( int i=y0; i<=y1; i++ )
            if ( squareOwner( x, i ) != 0 )
                return false;
        return true;
    }

    /** check whether a horizontal line can be colored
     */
    boolean lineIsPossible( int x0, int x1, int y ) {
        for ( int i=x0; i<=x1; i++ )
            if ( squareIsBlack( i, y ) )
                return false;
        return true;
    }

    /** check whether a vertical line can be colored
     */
    boolean vlineIsPossible( int x, int y0, int y1 ) {
        for ( int i=y0; i<=y1; i++ )
            if ( squareIsBlack( x, i ) )
                return false;
        return true;
    }

    /** calculate the proportion of a horizontal line which is black
     */
    double lineBlackPortion( int x0, int x1, int y ) {
        int n = 0;
        for ( int i=x0; i<=x1; i++ )
            if ( squareIsBlack( i, y ) )
                n++;
        return (double)n / (double)(x1-x0+1);
    }

    /** calculate the proportion of a vertical line which is black
     */
    double vlineBlackPortion( int x, int y0, int y1 ) {
        int n = 0;
        for ( int i=y0; i<=y1; i++ )
            if ( squareIsBlack( x, i ) )
                n++;
        return (double)n / (double)(y1-y0+1);
    }

    /** find the dangerness (between 0 and 1) of a line in certain steps
     */
    double dangerness( int x0, int x1, int y, int steps ) {
        double dgr = 0.0;
        int na = getAgentNum(), nt = getTeamNum();

        for ( int i=1; i<nt; i++ )
            for ( int j=0; j<na; j++ ) {

                double d;
                Position pos = getAgentPos( i, j );
                int dist = pos.distance( x0, x1, y );
                if ( dist > steps )
                    continue;

                if ( pos.wouldcut( x0, x1, y, getLastMove(i,j) ) )
                    d = Math.exp( 0.9 * steps );
                else
                    d = ( 0.1 * dist ) / steps;
                // System.out.println( " d= " + d );

                dgr = 1.0 - (1.0-dgr) * (1.0-d);
            }
/*
        System.out.println( "Dangerness: " + x0 + "-" + x1 + "," + y + " is: "
                            + dgr );
*/
        return dgr;
    }

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

    /** do check whether an agent is chased by other teams
     */
    void checkChasers() {
        int num_agents = getAgentNum();
        int num_teams = getTeamNum();

        if ( _chase_count == null )
            _chase_count = new int[num_agents];

        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[num_agents];
        _hist_y[0] = new int[num_agents];
         
        for ( int j=0; j<num_agents; j++ ) {
            _hist_x[0][j] = getMyAgentX( j );
            _hist_y[0][j] = getMyAgentY( j );
        }

        if ( null == _hist_x[2] )
            return;
        
        boolean[] _is_chased = new boolean[num_agents];

        for ( int i=1; i<num_teams; i++ )
            for (  int j=0; j<num_agents; j++ ) {
                int x = getAgentX( i, j );
                int y = getAgentY( i, j );
                for ( int k=0; k<num_agents; k++ ) {
                    if ( (x==_hist_x[0][k] && y==_hist_y[0][k] )
                         || (x==_hist_x[1][k] && y==_hist_y[1][k] )
                         || (x==_hist_x[2][k] && y==_hist_y[2][k] ) ) {
                        _is_chased[k] = true;
                    }
                }
            }

        for ( int i=0; i<num_agents; i++ ) {
            if ( _is_chased[i] ) {
                if ( _chase_count[i] < 6 )
                    _chase_count[i]++;
            } else if ( _chase_count[i] > 0 )
                _chase_count[i]--;
        }
    }

    /** whether or not this agent is chased
     */
    boolean chased( int i ) {
        return _chase_count[i] >= 4;
    }

    /** get an image of the board, whose pixels are true for
     * black images and false for others.  The image is
     * saved line by line
     */
    public final boolean[] getBlackMatrix() {
        int size = size();
        boolean[] ret = new boolean [size*size];
        for ( int i=0; i<size; i++ )
            for ( int j=0; j<size; j++ ) {
                ret[j*size+i] = squareIsBlack( i, j );
            }
        return ret;
    }

    /** get an image of the board, whose pixels are colors
     * for corresponding user.  
     */
    public final int[] getColorMatrix() {
        int size = size();
        int[] ret = new int [size*size];
        for ( int i=0; i<size; i++ )
            for ( int j=0; j<size; j++ ) {
                ret[i*size+j] = squareOwner( j, i );
            }
        return ret;
    }

    /** find an agent in a list that has a distance no more than steps, to
     * an rectangle
     */
    public final int locateNearestAgent( int[] index, boolean[] f, int steps,
                                         int left, int top, 
                                         int right, int bottom ) {
        for ( int i=0; i<index.length; i++ ) {
            if ( !f[i] )
                continue;

            if ( getMyAgentPos( index[i] ).distance( 
                     left, top, right, bottom ) <= steps )
                return i;
        }

        return -1;
    }
}


/** This class extends the abtract Board class, and actually implement
 * methods of getting board information, by calling the methods of
 * Rectangles
 */
class BoardPlayer extends Board implements IFCPlayer {
    static int _counter = -1;     // for color changing

    Rectangles _rect;             // the game
    int _size;                    // board size
    int _my_index;                // the value of indexOf()
    int[][] _colors;              // the cached matrix returned by _colors()
    Robot[][] _robots;            // the cached matrix returned by allRobots()
    Robot[] _my_robots;           // _robots[_my_index]
    MoveResult[] _log;            // returned by history()
    double[] _last_scores;        // the cached scores
    int [][] _last_moves;         // the cached last moves

    Mission _mission;             // the main mission of this player

    /** Placing all robots 
     * This method should be overrided by the sub classes
     */
    public Position[] initAgents( int n ) {
        return randomInitAgents( n );
    }

    /** Create an mission
     * This method should be overrided by the sub classes
     */
    public Mission initMission() {
        return null;
    }

    /** implementing IFCPlayer.register()
     */
    public Robot[] register( Rectangles rect ) throws Exception {
        _rect = rect;
        _size = rect.size();
        _counter++;
        _last_scores = new double[ _rect.numRobots() ];
        _my_robots = new Robot[ _rect.numRobots() ];

        Position pos[] = initAgents(_my_robots.length);
        
        for ( int i=0; i<_my_robots.length; i++ )
            _my_robots[i] = new Robot( pos[i].x(), pos[i].y() );
        return _my_robots;
    }

    /** generates random initial positions
     */
    public final Position[] randomInitAgents( int n ) {
        Position[] pos = new Position[n];
        for ( int i=0; i<n; i++ )
            pos[i] = new Position( _rand.nextInt(_size), 
                                   _rand.nextInt(_size) );
        return pos;
    }

    /** Stay where they were if the mission gets wrong
     */
    char[] _stay() {
        char[] moves = new char[ _my_robots.length ];
        for ( int i=0; i<_my_robots.length; i++ )
            moves[i] = _CSTAY;
        return moves;
    }

    /** implementing IFCPlayer.move()
     */
    public char[] move() throws Exception {
        getBoard();
        try {
            checkChasers();
            if ( _mission == null ) 
                _mission = initMission();
            int[] ms = _mission.move();
            if ( null == ms )
                return _stay();
            return unmapMoves( _mission._game, ms );
        } catch ( Exception e ) {
            e.printStackTrace();
        }
        return _stay();
    }

    /** implementing IFCPlayer.name()
     * It can be re-overrided.
     */
    public String name() throws Exception {
        return "Group 5 Test Player";
    }

    /** implementing IFCPlayer.interactive()
     */
    public boolean interactive() throws Exception {
        return false;
    }

    /** implementing IFCPlayer.color()
     * Changing robot colors
     */
    public Color color() throws Exception {
        int c = _counter % 5;
        return new Color( 0.5f - 0.05f * c, 1.0f - 0.1f * c,
                          1.0f - 0.1f * c );
    }

    private static char[] _move_unmap = new char[]{ 'E', 'W', 'S', 'N', 'Y' };

    final static char[] unmapMoves( Board game, int[] ms ) {
        char[] dirs = new char[ ms.length ];
        for ( int i=0; i<ms.length; i++ ) {
            int m = game.unmapMove( ms[i] );
            if ( m < 0 || m >= 5 )
                dirs[i] = 'Y';
            else
                dirs[i] = _move_unmap[m];

        }
        return dirs;
    }

    int[] mapMoves( char[] dirs ) {
        int[] ms = new int[ dirs.length ];
        for ( int i=0; i<ms.length; i++ ) {
            switch ( dirs[i] ) {
            case 'E':
            case 'e':
                ms[i] = EAST;
                break;
            case 'W':
            case 'w':
                ms[i] = WEST;
                break;
            case 'S':
            case 's':
                ms[i] = SOUTH;
                break;
            case 'N':
            case 'n':
                ms[i] = NORTH;
                break;
            case 'Y':
            case 'y':
                ms[i] = STAY;
                break;
            default:
                ms[i] = QUIT;
                break;
            }
        }
        return ms;
    }

    /** get and cache the board information
     */
    void getBoard() {
        try {
            _colors = _rect.colors();
            _robots = _rect.allRobots();
            _my_index = _rect.indexOf( this );
            _my_robots = _robots[_my_index];
            _log = _rect.history();
            
            _last_scores = _log[0].scores();
            
            double tmp = _last_scores[0];
            _last_scores[0] = _last_scores[_my_index];
            _last_scores[_my_index] = tmp;
            
            char[][] d = _log[0].moves();
            _last_moves = new int[d.length][];
            _last_moves[0] = mapMoves( d[ _my_index ] );
            for ( int i=1; i<d.length; i++ )
                _last_moves[i] = mapMoves( d[ _my_index==i ? 0 : i ] );
        } catch ( Exception e ) {
        }
    }

    public int getMyAgentX( int i ) {
        try {
            return _my_robots[i].xpos();
        } catch ( Exception e ) {
        }
        return 0;
    }

    public int getMyAgentY( int i ) {
        try {
            return _my_robots[i].ypos();
        } catch ( Exception e ) {
        }
        return 0;
    }

    public int getAgentNum() {
        try {
            return _rect.numRobots();
        } catch ( Exception e ) {
        }
        return 0;
    }
    
    public int getTeamNum() {
        try {
            return _rect.numPlayers();
        } catch ( Exception e ) {
        }
        return 0;
    }

    final int changeIndex( int i ) {
        if ( 0 == i )
            return _my_index;
        else if ( _my_index == i )
            return 0;
        return i;
    }

    public int getAgentX( int opp, int i ) {
        try {
            return _robots[changeIndex(opp)][i].xpos();
        } catch ( Exception e ) {
        }
        return 0;
    }

    public int getAgentY( int opp, int i ) {
        try {
            return _robots[changeIndex(opp)][i].ypos();
        } catch ( Exception e ) {
        }
        return 0;
    }

    public int getLastMove( int opp, int i ) {
        return _last_moves[opp][i];
    }

    public int size() {
        return _size;
    }

    public boolean squareIsBlack( int x, int y ) {
	if ( x < 0 || x >= _size || y < 0 || y >= _size )
            return true;
        return _colors[x][y] == _CFILLED;
    }

    public boolean squareIsColored( int x, int y ) {
        if ( x < 0 || x >= _size || y < 0 || y >= _size )
            return false;
        return _colors[x][y] > 0; 
    }

    public int squareOwner( int x, int y ) {
        if ( x < 0 || x >= _size || y < 0 || y >= _size )
            return _CFILLED;
        return changeIndex( _colors[x][y] );
    }

    public int getScore( int opp ) {
        opp = changeIndex( opp );
        if ( opp < 0 || opp >= _last_scores.length )
            return 0;
        return (int) _last_scores[opp];
    }

    public int unmapMove( int n ) {
        return n;
    }

    public int mapMove( int n ) {
        return n;
    }
}

/**
 * The cavalry strategy (updated)
 */

class CavalryMission extends Mission {
    Mission _mission;    // the sub mission
    Quest[] _quest;      // the quests for each agent
    Position[] _extr;    // the extraction point

    int _prev;           // the y-coordinate of the previous line
    int _curr;           // the current line
    int[] _base;         // the x-coordinate baseline of each agent

    int _enc_steps;      // the steps for horizontally enclosing the area
    int _min_dist;       // the minimum vertical distance to enclose
    int _std_dist;       // the standard vertical distance to enclose
    int _ext_dist;       // the extended vertical distance to enclose
    int _max_dist;       // the maximum vertical distance to enclose

    /** initialize the cavalry mission, with coordinates of starting points
     * of agents
     */
    public CavalryMission( Board game, int[] index, Position[] extr,
                           int origin, int[] base ) {
	super( game, index );
	_prev = _curr = origin;
	_base = base;
        _extr = extr;
	_quest = new Quest[index.length];

        initParam();
        enclose();
    }

    /** initialize the cavalry mission, with coordinates of staring
     * points of agents
     */
    public CavalryMission( Board game, int[] index, Position[] extr,
                           int origin, int base0, int basen ) {
	super( game, index );
	_prev = _curr = origin;
	_base = new int[ index.length ];
	for ( int i=0; i<_base.length; i++ )
	    _base[i] = i * (basen-base0) / (_base.length-1) + base0;
        _extr = extr;
	_quest = new Quest[index.length];

        initParam();
        enclose();
    }

    /** initialize the mission parameters
     */
    void initParam() {
        _enc_steps = ( _base[_base.length-1] - _base[0] - 1 ) 
            / ( _base.length-1 ) + 1;
        _min_dist = ( _enc_steps * 2 + 1 ) / 3 + 1;
        _std_dist = _enc_steps + 2;
        _ext_dist = _enc_steps + 4;
        _max_dist = _enc_steps + 8;
/*
        System.out.println( "distances: " + _min_dist + "," + _std_dist + ","
                            + _ext_dist + "," + _max_dist );
*/
    }

    /** evaluate the current status.  This function may introduce the
     * following missions and/or quests for agents
     * (1) Is now the time to enclose a region? (If borders are in
     * danger?)
     * (2) Is there any repair work to do?
     * (3) Can we advance?  
     */
    void evaluate() {
        // if there is a sub-mission, do the sub-mission
        if ( _mission != null )
            return;

        // Is this the last line? do enclosing if so
        if ( _curr >= _size - 1 ) {
            enclose();
            return;
        }

        // the index of the last agent
        int bx = _base.length-1;

        // this is the vertical distance that we have advanced.
        int dist = _curr - _prev;
        // do not enclose for a small area
        if ( dist < _min_dist )
            return;


        boolean _do_enclose = false, _must_enclose = false;
        boolean _do_repair_left = false, _do_repair_right = false;
        boolean _do_repair_start = false;

        // check the start line
        if ( _game.lineIsColored( _base[0], _base[bx], _prev ) ) {
            // if it is fully in my color, compute its dangerness
            double d = _game.dangerness( _base[0], _base[_base.length-1], 
                                         _prev, _enc_steps );

            // the maximum gain if we advance
            int next_gain = _base[bx]-_base[0] - 1;
            // the gain if we enclose
            int enc_gain = ( dist - 1 ) * next_gain;
            // whether the start line is in danger
            if ( next_gain * (1-d) < enc_gain * d / (_base.length-1) ) {
                // System.out.println( "The start line is in danger" );
                _do_enclose = true;
            }
        } else {
            // if part of the start line is colored by somebody else
            _do_repair_start = true;
            _must_enclose = true;
        }

        // check the side lines
        if ( !_game.vlineIsColored( _base[0], _prev, _curr ) ) {
            _must_enclose = true;
            _do_repair_left = true;
        }
        if ( !_game.vlineIsColored( _base[bx], _prev, _curr ) ) {
            _must_enclose = true;
            _do_repair_right = true;
        }

        // how much we can get from the current line?
        double d = _game.lineBlackPortion( _base[0], _base[bx], _curr );
        
        if ( _must_enclose ) {
            // emergency enclosing
            _must_enclose = ( dist >= _ext_dist )
                || ( dist >= _std_dist && d < 0.8 )
                || d < 0.5;
        } else {
            // if it is not an emergency, test serveral lines below
            int _test_end = -1;

            if ( _do_enclose ) {
                // if enclosing is suggested
                if ( dist >= _ext_dist )
                    _must_enclose = true;
                else if ( dist >= _std_dist )
                    _test_end = _ext_dist;
            } else {
                if ( dist >= _max_dist )
                    _must_enclose = true;
                else if ( dist >= _ext_dist )
                    _test_end = _max_dist;
            }

            if ( _test_end >= _curr) {
                _must_enclose = true;
                for ( int i=_curr+1; i<=_test_end; i++ ) {
                    double d1 = _game.lineBlackPortion( 
                        _base[0], _base[bx], i );
                    if ( d1 < d ) {
                        _must_enclose = false;
                        break;
                    }

                }
                    
            }
        }

        // if enclosing is not necessary, we advance
        if ( !_must_enclose )
            return;
        
        // now we must enclose
        if ( _do_repair_left ) {
            // using the leftmost agent to repair the left line
            _quest[0] = new VLineQuest( 
                _game, _index[0], new Position( _base[0], _curr+1 ),
                _base[0], _prev, _curr );
            _quest[0].setDeadline( _enc_steps * 5 / 2 );
/*
            System.out.println( "Agent 0 has a quest: "
                                + _base[0] + "," + _prev + "-" + _curr );
*/
        }
        if ( _do_repair_right ) {
            // using the rightmost agent to repair the right line
            _quest[bx] = new VLineQuest( 
                _game, _index[bx], new Position( _base[bx], _curr+1 ),
                _base[bx], _prev, _curr );
            _quest[bx].setDeadline( _enc_steps * 5 / 2 );
/*
            System.out.println( "Agent " + bx + " has a quest: "
                                + _base[bx] + "," + _prev + "-" + _curr );
*/
        }
        if ( _do_repair_start ) {
            // find the nearest agent to idx
            Position[] pos = new Position[_base.length];
            for ( int i=0; i<=bx; i++ )
                pos[i] = new Position( _base[i], _curr+1 );
            int idx = LineQuest.findBestAgent( 
                _game, _index, pos,
                _base[0]+1, _base[bx]-1, _prev );

            if ( idx == 0 && _quest[0] != null )
                idx++;
            if ( idx >= bx && _quest[bx] != null )
                idx = bx-1;

            if ( idx >= 0 && _quest[idx] != null ) {
                _quest[idx] = new LineQuest(
                    _game, _index[idx], new Position( _base[idx], _curr+1 ),
                    _base[0]+1, _base[bx]-1, _prev );
                _quest[idx].setDeadline( _enc_steps * 5 / 2 );
            }

            // System.out.println( "Repairing start line..." );
        }

        enclose();
    }

    /** line up the current line and go to the position of the next line
     */
    void enclose() {
        // System.out.println( "I am enclosing the area at line " + _curr );

        // check whether there are repairing quests
	int c = 0;
	for ( int i=0; i<_index.length; i++ )
	    if ( null == _quest[i] )
		c++;
	if ( 0 == c )
	    return;

        // we have to dismiss agents who have quests
	int[] new_index = new int[c];
	Position[] new_pos = new Position[c];

	int j=0;
	for ( int i=0; i<_quest.length; i++ )
	    if ( null == _quest[i] ) {
		new_pos[j] = new Position( _base[i], 
                                           _curr+1>=_size ? _curr : _curr+1 );
		new_index[j++] = _index[i];
	    }

        _prev = _curr;
        if ( _curr < _size - 1 ) {
            _mission = new LineMission( 
                _game, new_index, new_pos,
                _base[0], _base[_index.length-1], _curr );
            _curr++;
        } else {
            // System.out.println( "Enclosing the last line" );
            if ( _extr == null ) {
                _mission = new LineMission( 
                    _game, new_index, _extr,
                    _base[0], _base[_index.length-1], _curr );
            } else {
                j = 0;
                for ( int i=0; i<_quest.length; i++ )
                    if ( null == _quest[i] )
                        new_pos[j++] = _extr[i];
                _mission = new LineMission( 
                    _game, new_index, new_pos,
                    _base[0], _base[_index.length-1], _curr );
            }
        }

        ((LineMission)_mission)._forced = true;

        if ( c + 1 >= _index.length )
            _mission.setDeadline( _enc_steps * 2 );
        else
            _mission.setDeadline( _enc_steps * 3 );

	if ( _mission.finished() )
	    _mission = null;
    }

    /** the next move
     */
    public int[] next() {
/*
        System.out.println( "Cavalry of " + _base.length 
                            + "is line up at" + _curr + "from " + _base[0] 
                            + " to " + _base[_base.length-1] );
*/
        // check the current situation
	evaluate();
	
        // default is to stay where they were
	int[] ms = new int[_index.length];
	for ( int i=0; i<_index.length; i++ )
	    ms[i] = Board.INVALID;

        // Do their quests for agents who have quests
	boolean flag = false;
	for ( int i=0; i<_index.length; i++ )
	    if ( null != _quest[i] ) {
		ms[i] = _game.mapMove( _quest[i].questMove() );
                flag = true;
	    }
	
        // for the rest of the agents
	if ( _mission != null ) {
            // if there is a sub-mission, do this sub-misson
	    int[] mtmp = _mission.move();
	    if ( mtmp != null ) {
		int j=0;
		for ( int i=0; i<_index.length; i++ ) {
		    if ( Board.INVALID == ms[i] && j < mtmp.length ) {
			ms[i] = _game.mapMove( mtmp[j++] );
                    }

                    // note: Cavalry never dismiss agents
                    if ( Board.QUIT == ms[i] )
                        ms[i] = Board.STAY;
                }
		return ms;
	    }

            // Sub-mission ends
            _mission = null;
            if ( _prev < _size - 1 )
                evaluate();
	}

        if ( flag ) {
            // no sub-mission is available but some agents are busy
            for ( int i=0; i<ms.length; i++ ) {
                if ( _quest[i] != null && ms[i] == Board.QUIT )
                    _quest[i] = null;

                if ( ms[i] == Board.QUIT || ms[i] == Board.INVALID )
                    ms[i] = Board.STAY;
            }
	    return ms;   // this is not a good solution
        }

        // reach the end of the board?  If so, mission is done
	if ( finished() )
	    return null;

	_curr++;

	for ( int i=0; i<_index.length; i++ )
	    ms[i] = Board.SOUTH;

        // System.out.println( "The cavalry is advancing to " + _curr );

	return ms;
    }

    /** whether or not the mission is finished
     */
    public boolean finished() {
	return _curr >= _size - 1 && _mission == null;
    }

    Quest[] questArrayDelete( Quest[] a, int pos ) {
        Quest[] ret = new Quest[a.length-1];
        if ( pos > 0 )
            System.arraycopy( a, 0, ret, 0, pos );
        if ( pos < ret.length )
            System.arraycopy( a, pos+1, ret, pos, ret.length-pos );
        return ret;
    }

    /** delete an agent from the cavalry
     */
    public void delete( int agent ) {
        if ( _mission != null )
            return;
        int pos = locate( agent );
        if ( pos < 0 )
            return;

//        if ( _quest[pos] != null )
//            return;

        _index = intArrayDelete( _index, pos );
        _quest = questArrayDelete( _quest, pos );
        _base = intArrayDelete( _base, pos );
        if ( _base.length > 1 ) {
            _enc_steps = ( _base[_base.length-1] - _base[0] - 1 ) 
                / ( _base.length-1 ) * 3 / 2;
        } 
    }
}

class ChaseeQuest extends Quest {
    public ChaseeQuest( Board game, int agent ) {
        super( game, agent, Position._empty );
    }

    public boolean finished() {
        return !_game.chased( getAgent() );
    }

    public int expectedRounds( Position pos ) {
        return 1000;
    }

    public int questNext() {
        int agent = getAgent();

        if ( !_game.chased( agent ) )
            return Board.QUIT;

        int x = _game.getMyAgentX( agent );
        int y = _game.getMyAgentY( agent );

        int n = _game._rand.nextInt(4);
        switch ( n ) {
        case 0:
            if ( x > 0 && ( _game.squareIsBlack(x-1,y) 
                            || _game.squareOwner(x-1,y) > 0 ) )
                return Board.WEST;
            break;
        case 1:
            if ( x < _size - 1 && ( _game.squareIsBlack(x+1,y) 
                                    || _game.squareOwner(x+1,y) > 0 ) )
                return Board.EAST;
            break;
        case 2:
            if ( y > 0 && ( _game.squareIsBlack(x,y-1) 
                            || _game.squareOwner(x,y-1) > 0 ) )
                return Board.NORTH;
            break;
        case 3:
            if ( y < _size - 1 && ( _game.squareIsBlack(x,y+1) 
                                    || _game.squareOwner(x,y+1) > 0 ) )
                return Board.SOUTH;
            break;
        }

        return Board.STAY;
    }
}

/** The "Royal Cavalry"
 */
public class Group5Player2 extends BoardPlayer {

    /** The initial position
     */
    public Position[] initAgents( int n ) {
        int n1 = n / 2;
        int n2 = n - n1;
        if ( n < 4 ) {
            n1 = n;
            n2 = 0;
        }

        int dist = 1; 
        if ( n1 > 1 ) 
            dist = _size / 2 / (Math.max(n1,n2)-1);

        Position[] pos = new Position[n];

        for ( int i=0; i<n1; i++ )
            pos[i] = new Position( _size/2 + dist * (i-1) + 1, 0 );
        for ( int i=0; i<n2; i++ )
            pos[i+n1] = new Position( dist*i + dist-1, _size-1 );
 
        return pos;
    }

    /** initialize the Royal Cavalry Mission
     */
    public Mission initMission() {
        int[] index = new int[getAgentNum()];
        for ( int i=0; i<index.length; i++ )
            index[i] = i;
        return new RoyalCavalryMission( this, index );
    }

    public String name() {
        return "Royal Cavalry";
    }
}

/**
 * finishing a line by multiple agents
 */

class LineMission extends Mission {
    int _x0, _x1;       // the range of x-dimension
    int _y;             // the y-coordinate
    Position[] _extr;   // the extraction points
    boolean _forced = false;  // force to finish 

    int _min_cost;      // the minimum cost

    /** initialize the mission with the line location and extraction points
     */
    public LineMission( Board game, int[] index, Position[] extr,
                        int x0, int x1, int y ) {
        super( game, index );
        if ( x1 >= x0 ) {
            _x0 = x0;
            _x1 = x1;
        } else {
            _x1 = x0;
            _x0 = x1;
        }
        _y = y;

        if ( extr == null ) {
            _extr = new Position[index.length];
            for ( int i=0; i<_extr.length; i++ )
                _extr[i] = Position._empty;
        } else
            _extr = extr;

        setDeadline( expectedRounds() );
    }

    /** is the mission done?
     */
    public boolean finished() {
        for ( int i=_x0; i<=_x1; i++ )
            if ( _game.squareOwner( i, _y ) != 0 )
                return false;

        for ( int i=0; i<_index.length; i++ )
            if ( !_extr[i].at( _game.getMyAgentX(i), _game.getMyAgentY(i) ) )
                return false;

        return true;
    }

    /** is that possible to draw the line? (no square is permanent
     * removed)
     */
    public boolean possible() {
        if ( _forced )
            return true;

        for ( int i=_x0; i<=_x1; i++ )
            if ( _game.squareIsBlack( i, _y ) )
                return false;
        return true;
    }

    /** sort by index.  
     */
    public int[] sort( int[] ix ) {
        int[] idx = new int[ix.length];
        for ( int i=0; i<idx.length; i++ )
            idx[i] = i | (ix[i]<<16);
        java.util.Arrays.sort( idx );
        for ( int i=0; i<idx.length; i++ )
            idx[i] &= 0xffff;
        return idx;
    }

    /** calculate the cost for an agent to draw a line from l to r.
     * The insertion point is (ix,iy) and the extraction point is
     * extr.
     */
    public int calcCost( int l, int r, int ix, int iy, Position extr ) {
        // find the left-most uncolored square
        while ( l <= r && ( _game.squareOwner( l, _y ) == 0 
                            || _game.squareIsBlack( l, _y ) ) )
            l++;
        // find the right-most uncolored square
        while ( l <= r && ( _game.squareOwner( r, _y ) == 0 
                            || _game.squareIsBlack( r, _y ) ) )
            r--;
        // if no square is needed to be colored, moving towards the
        // extraction point directly
        if ( r < l )
            return extr.distance( ix, iy );

        int cost1 = Math.abs(ix-l) + Math.abs(iy-_y) + (r-l) 
            + extr.distance( r, _y );
        int cost2 = Math.abs(ix-r) + Math.abs(iy-_y) + (r-l)
            + extr.distance( l, _y );
        return Math.min( cost1, cost2 );
    }

    /** calculate the cost for all agents to draw a line, given the
     * left border for each agent.  The agents are indexed by idx.
     */
    public double evaluate( int[] left, int[] idx, int[] ix, int[] iy ) {
        int cost = 0;
        int sum = 0;
        for ( int i=0; i<_index.length; i++ ) {
            int r = _x1;
            if ( i < _index.length-1 )
                r = left[idx[i+1]]-1;
            int c = calcCost( left[idx[i]], r, ix[idx[i]], iy[idx[i]], 
                              _extr[idx[i]] );
            sum += c;
            // the cost is the maximum costs for all agents plus a scaled sum
            if ( c > cost )
                cost = c;
        }
        return cost + 0.0001 * sum;
    }

    /** calculate the minimum cost to draw a line by multiple agents.
     * @return the left-border for each agent.  If the corresponding element
     * for an agent is negative, this agent directly goes to the extraction
     * point
     */
    public int[] evaluate() {
        int[] left = new int[_index.length];
        int[] ix = new int[_index.length];
        int[] iy = new int[_index.length];

        for ( int i=0; i<_index.length; i++ ) {
            ix[i] = _game.getMyAgentX( _index[i] );
            iy[i] = _game.getMyAgentY( _index[i] );
        }

        // sort the agents by their x-coordinates
        int[] idx = sort( ix );

        // set-up the initial combination
        left[idx[0]] = _x0;
        for ( int i=1; i<_index.length; i++ ) {
            int x = ix[idx[i]];
            left[idx[i]] = Math.max( 
                _x0, Math.min( _x1+1, (ix[idx[i-1]] + ix[idx[i]]+1)/2 ) );
/*
            if ( x < _x0 )
                left[idx[i]] = _x0;
            else if ( x > _x1 )
                left[idx[i]] = Math.min( _x1+1, left[idx[i-1]]+1 );
            else
                left[idx[i]] = x;
*/
        }


        // hill climbing - find the initial cost
        double cost = evaluate( left, idx, ix, iy );

        // hill climbing - move the left border until the cost changes
        // added sidewalk
        int shdr_steps = 10;
        for (;;) {
/*
            System.out.print( "Cost=" + cost
                              + " X=" + _x0 + "~" + _x1 + " Y=" + _y + "  " );
            for ( int i=0; i<_index.length; i++ ) {
                System.out.print( " ["+_index[idx[i]]+"] " + left[idx[i]] );
            }
            System.out.println( "" );
*/
            int idx_chg = -1;
            int new_left = -1;
            double cost_tmp = cost;
            int shdr_count = 1;
            for ( int i=1; i<_index.length; i++ ) {
                int tmp = left[idx[i]];
                int l = left[idx[i-1]];
                int r = _x1+1;
                if ( i < _index.length-1 )
                    r = left[idx[i+1]];
                
                // iterate all possible left borders for agent idx[i]
                for ( int j=l; j<=r; j++ ) {
                    left[idx[i]] = j;
                    double c = evaluate( left, idx, ix, iy );
                    if ( c < cost_tmp ) {
                        idx_chg = i;
                        new_left = j;
                        cost_tmp = c;
                    } else if ( shdr_steps-- > 0 && c == cost_tmp ) {
                        if ( _game._rand.nextInt(shdr_count) == 0 ) {
                            idx_chg = i;
                            new_left = j;
                            cost_tmp = c;
                        }
                        shdr_count++;
                    }
                }
                left[idx[i]] = tmp;
            }
            if ( idx_chg < 0 ) 
                break;
            cost = cost_tmp;
            left[idx[idx_chg]] = new_left;
        }
/*        
        System.out.print( "Cost=" + cost
                          + " X=" + _x0 + "~" + _x1 + " Y=" + _y + "  " );
        for ( int i=0; i<_index.length; i++ ) {
            System.out.print( " ["+_index[idx[i]]+"] " + left[idx[i]] );
        }
        System.out.println( "" );
*/
        // find the first destination square for each agent
        for ( int i=0; i<_index.length; i++ ) {
            int l = left[idx[i]];
            int r = _x1;
            if ( i < _index.length-1 )
                r = left[idx[i+1]]-1;

            while ( l <= r && ( _game.squareOwner( l, _y ) == 0
                                || _game.squareIsBlack( l, _y ) ) ) {
                // System.out.println( "l++" );
                l++;
            }
            while ( l <= r && ( _game.squareOwner( r, _y ) == 0 
                                || _game.squareIsBlack( r, _y ) ) ) {
                // System.out.println( "r--" );
                r--;
            }

            if ( l > r )
                left[idx[i]] = -1;
            else {
                int cost1 = Math.abs(ix[idx[i]]-l) + Math.abs(iy[idx[i]]-_y) 
                    + (r-l) + _extr[idx[i]].distance( r, _y );
                int cost2 = Math.abs(ix[idx[i]]-r) + Math.abs(iy[idx[i]]-_y) 
                    + (r-l) + _extr[idx[i]].distance( l, _y );
                left[idx[i]] = ( cost1<=cost2 ? l : r );

                // System.out.println( "cost1 :" + cost1  + "  cost2: " + cost2 );
            }
        }

        _min_cost = (int)Math.floor(cost);
/*
        System.out.print( " X=" + _x0 + "~" + _x1 + " Y=" + _y + "  " );
        for ( int i=0; i<_index.length; i++ ) {
            System.out.print( " ["+idx[i]+"] " + left[idx[i]] );
        }
        System.out.println( "" );
*/
        return left;
    }

    /** the minimum steps to finish the mission
     */
    public int expectedRounds() {
        evaluate();
        return _min_cost;
    }

    /** the next moves
     */
    public int[] next() {
        int[] ms = new int[_index.length];
        _deadline--;

        if ( !possible() || _deadline < 0 ) {
            for ( int i=0; i<_index.length; i++ ) {
                if ( _extr[i].valid() )
                    ms[i] = _game.moveTo( 
                        _index[i], _extr[i].x(), _extr[i].y(), 0 );
                else
                    ms[i] = Board.QUIT;
            }
            return checkEnd( ms );
        }

        int[] opt = evaluate();
        for ( int i=0; i<_index.length; i++ ) {
            if ( opt[i] >= 0 )
                ms[i] = _game.moveTo( _index[i], opt[i], _y, 0 );
            else if ( _extr[i].valid() )
                ms[i] = _game.moveTo( _index[i], _extr[i].x(), _extr[i].y(), 
                                      _deadline );
            else
                ms[i] = _game.moveTo(
                    _index[i], _game.getMyAgentX(_index[i]),
                    _game.getMyAgentY(_index[i]), _deadline );
        }

        return checkEnd( ms );
    }

    /** return null if all agents are going to stay
     */
    int[] checkEnd( int[] ms ) {
        for ( int i=0; i<ms.length; i++ )
            if ( ms[i] != Board.QUIT )
                return ms;
        return null;
    }
}

/**
 * A quest to draw a horizontal line
 */

class LineQuest extends Quest {

    int _x0;               // the start point 
    int _x1;               // the ending point
    int _y;                // the y coordinate of the line

    int _left;             // the leftmost uncolored square
    int _right;            // the rightmost uncolored square

    static int _min_cost;  // a cost value returned by findBestAgent

    /** Static method: find the best agent to do a line quest.
     * @return the index of agent index table
     */
    public static int findBestAgent( Board game, int[] index, Position[] extr,
                                     int x0, int x1, int y ) {
        LineQuest quest = new LineQuest( game, 0, extr[0],
                                         x0, x1, y );
        int agent = -1;
        int rounds = 10000000;

        for ( int i=0; i<index.length; i++ ) {
            quest._index[0] = index[i];
            quest._extr = extr[i];
            int r = quest.expectedRounds(
                new Position( game.getMyAgentX(index[i]),
                              game.getMyAgentY(index[i]) ) );
            if ( r < rounds ) {
                r = rounds;
                agent = i;
            }
        }
        return agent;
    }

    /** Initialize the line quest, giving the line position
     */
    public LineQuest( Board game, int agent, Position extr, 
                      int x0, int x1, int y ) {
        super( game, agent, extr );
        _x0 = x0;
        _x1 = x1;
        _y = y;
    }

    /** Evaluate the current situation: compute _left and _right
     */
    final void evaluate() {
        _left = -1;

        for ( int i=_x0; i<=_x1; i++ )
            if ( _game.squareOwner( i, _y ) != 0 ) {
                _left = i;
                break;
            }
        
        if ( _left >= 0 ) {
            for ( int i=_x1; i>=_left; i-- )
                if ( _game.squareOwner( i, _y ) != 0 ) {
                    _right = i;
                    break;
                }
        }
    }

    /** calculate the number of steps if we go from left to right
     */
    final int _calcLeftToRight( Position pos ) {
        return ( _right - _left ) + pos.distance( _left, _y )
            + _extr.distance( _right, _y );
    }

    /** calculate the number of steps if we go from right to left
     */
    final int _calcRightToLeft( Position pos ) {
        return ( _right - _left) + pos.distance( _right, _y )
            + _extr.distance( _left, _y );
    }

    /** the expected steps is the smaller one of two ways
     */
    public int expectedRounds( Position pos ) {
        int path1 = _calcLeftToRight( pos );
        int path2 = _calcRightToLeft( pos );
        return Math.max( path1, path2 );
    }

    /** is that possible to draw the line? (no square is permanent
     * removed)
     */
    public boolean possible() {
        if ( _left < 0 )
            return true;
        return ! _game.squareIsBlack( _left, _y );
    }

    /** this line has already been drawn
     */
    public boolean finished() {
        return _left < 0;
    }

    /** move towards finishing the quest
     */
    public int questNext() {
        // calculate the leftmost and rightmost uncolored square
        evaluate();

        // whether this line has already been drawn
        if ( finished() ) {
            if ( _extr.valid() )
                return _game.moveTo( getAgent(), 
                                     _extr.x(), _extr.y(), _deadline );
            return Board.QUIT;
        }

        // whether it is possible to finish the quest
        if ( !possible() )
            return _game.moveTo( getAgent(), _extr.x(), _extr.y(), _deadline );

        // compute the shortest path
        int path1 = _calcLeftToRight( _extr );
        int path2 = _calcRightToLeft( _extr );

        // if it cannot finish the line, go to the extraction point directly
        if ( path1 > _deadline && path2 > _deadline )
            return _game.moveTo( getAgent(), _extr.x(), _extr.y(), _deadline );
        
        // selecting a shortest path
        if ( path1 < path2 ) 
            return _game.moveTo( getAgent(), _left, _y, 
                                 _deadline - _extr.distance( _right, _y )
                                 - ( _right - _left ) );
        return _game.moveTo( getAgent(), _right, _y,
                             _deadline - _extr.distance( _left, _y ) 
                             - ( _right - _left ) );
    }
}

/**
 * The base mission class.  A mission is a task that accomplished by one
 * or multiple agents.
 */

abstract class Mission {
    Board      _game;       // the game board
    int        _size;       // the game size (cached from _game)
    int[]      _index;      // index of the robots

    int        _deadline;   // the deadline of this mission
    double[]   _cost;       // the cost of the mission if the deadline is over
    double     _cost_per_step; // the per-step cost if the deadline is over

    /** Initialize a mission with several robots (not necessarily be all)
     */
    public Mission( Board game, int[] index ) {
        _game = game;
        _size = game.size();
        _index = index;
    }
    
    /** Set a deadline for a mission
     */
    public void setDeadline( int deadline ) {
        _deadline = deadline;
    }

    /** set the per-step cost
     */
    public void setCost( double cost ) {
        _cost = null;
        _cost_per_step = cost;
    }

    /** set the cost array 
     */
    public void setCost( double[] cost, double default_cost ) {
        _cost = cost;
        _cost_per_step = default_cost;
    }

    /** the move function
     */
    public final int[] move() {
        if ( 0 == _index.length )
            return null;

        int[] ms = next();
        if ( null == ms )
            return null;

        int[] ums = new int[ ms.length ];
        for ( int i=0; i<ms.length; i++ )
            ums[i] = _game.unmapMove( ms[i] );
/*
        System.out.println( toString() );
        for ( int i=0; i<ms.length; i++ )
            System.out.print( " ?" + ms[i] );
        System.out.println();
        for ( int i=0; i<ums.length; i++ )
            System.out.print( "  " + ums[i] );
        System.out.println();
*/
        return ums;
    }

    /** whether or not the mission has accomplished
     */
    public abstract boolean finished();

    /** This is the operation function that we must override
     */
    public abstract int[] next();

    /** Locate the index of an agent
     */
    public final int locate( int agent ) {
        for ( int i=0; i<_index.length; i++ )
            if ( _index[i] == agent )
                return i;
        return -1;
    }

    int[] intArrayDelete( int[] a, int pos ) {
        int[] ret = new int[a.length-1];
        if ( pos > 0 )
            System.arraycopy( a, 0, ret, 0, pos );
        if ( pos < ret.length )
            System.arraycopy( a, pos+1, ret, pos, ret.length-pos );
        return ret;
    }

    /** Delete an agent
     */
    public void delete( int agent ) {
        int pos = locate( agent );
        if ( pos < 0 )
            return;
        _index = intArrayDelete( _index, pos );
    }
}

class MoveQuest extends Quest {
    int _dir = Board.QUIT;

    public MoveQuest( Board game, int agent, Position extr ) {
        super( game, agent, extr );
    }

    public MoveQuest( Board game, int agent, Position extr, int dir ) {
        super( game, agent, extr );
        _dir = dir;
    }

    public boolean finished() {
        return true;
    }

    public int expectedRounds( Position insr ) {
        if ( _dir == Board.QUIT )
            return insr.distance( _extr );
        if ( _dir == Board.STAY )
            return insr.distance( _extr ) + 1;
        return insr.distance( _extr ) + 2;
    }

    public int questNext() {
        if ( _dir != Board.QUIT ) {
            _dir = Board.QUIT;
            return _dir;
        }
        return _game.moveTo( getAgent(), _extr.x(), _extr.y(), _deadline );
    }
}

/**
 * the position of a point
 */
final class Position {
    int _x, _y;
    public static final Position _empty = new Position();

    /** default constructor: create an invalid position
     */
    public Position() {
        _x = -1;
        _y = -1;
    }

    /** constructor
     */
    public Position( int x, int y ) {
        _x = x;
        _y = y;
    }

    /** get the x coordinate
     */
    public final int x() {
        return _x;
    }

    /** get the y coordinate
     */
    public final int y() {
        return _y;
    }

    /** is the position valid?
     */
    public final boolean valid() {
        return _x >= 0;
    }

    /** whether two position are the same
     */
    public final boolean equalTo( Position pos ) {
        return _x == pos._x && _y == pos._y;
    }

    /** the distance between two points.  If one of the positions
     * is invalid, the distance is zero.  This means the invalid point
     * is at everywhere.
     */
    public final int distance( Position pos ) {
        if ( !valid() || !pos.valid() )
            return 0;
        return Math.abs( _x - pos._x ) + Math.abs( _y - pos._y );
    }

    /** get the distance between two points, and the second point is
     * given by two coordinates.
     */
    public final int distance( int x, int y ) {
        if ( !valid() )
            return 0;
        return Math.abs( _x - x ) + Math.abs( _y - y );
    }

    /** get the distance between a line segment to a point
     */
    public final int distance( int x0, int x1, int y ) {
        if ( !valid() )
            return 0;

        if ( x0 > x1 ) {
            int t = x0;
            x0 = x1;
            x1 = t;
        }
            
        if ( _x < x0 )
            return distance( x0, y );
        if ( _x > x1 )
            return distance( x1, y );

        return Math.abs(_y-y);
    }

   /** get the distance between a vertical line segment to a point
     */
    public final int distancev( int x, int y0, int y1 ) {
        if ( !valid() )
            return 0;

        if ( y0 > y1 ) {
            int t = y0;
            y0 = y1;
            y1 = t;
        }
            
        if ( _y < y0 )
            return distance( x, y0 );
        if ( _y > y1 )
            return distance( x, y1 );

        return Math.abs(_x-x);
    }

    /** compute the distance to the border of a rectangle 
     */
    public final int distance( int left, int top, int right, int bottom ) {
        return Math.min( 
            Math.min( distance( left, right, top ),
                      distance( left, right, bottom ) ),
            Math.min( distancev( left, top, bottom ),
                      distancev( right, top, bottom ) ) );
                     
    }

    /** check whether a point is inside a rectangle
     */
    public final boolean inRect( int left, int top, int right, int bottom ) {
        return _x > right && _x < left && _y < top && _y > bottom;
    }

    /** check whether a point is on the border of a rectangle
     */
    public final boolean onRect( int left, int top, int right, int bottom ) {
        return ( ( _x == left || _x == right ) && _y >= top && _y <= bottom )
            || ( ( _y == top || _y == bottom ) && _x >= left && _x <= right );
    }


    /** check whether a moving point (this) will cut a line segment 
     * between (x0,y) and (x1,y).
     */
    public final boolean wouldcut( int x0, int x1, int y, int dir ) {
        if ( _x < x0 || _x > x1 ) {
            if ( y == _y )                
                return true;
        } else if ( _y < y && dir == Board.SOUTH 
                    || _y > y && dir == Board.NORTH )
            return true;
        return false;
    }

    /** check whether this point is at (x,y)
     */
    public final boolean at( int x, int y ) {
        return _x == x && _y == y;
    }

    /** generate a random point in a rectangle with a width of nx and
     * a height of ny
     */
    public static final Position random( int nx, int ny ) {
        return new Position( Board._rand.nextInt(nx), Board._rand.nextInt(ny) );
    }
}

/**
 * The quest class.  A quest is a simple misson accomplished by a
 * single agent.  We also give an extration point to a quest so that
 * the number of steps can be calculated exactly.  For this reason, if
 * the number of steps needed is much less than required, this quest
 * can retrieve a sub-quest to the system.
 */

abstract class Quest extends Mission {
    boolean  _deleted = false;  // whether the agent has been removed
    Quest    _sub;              // the sub-quest
    Position _extr;             // the extraction point

    /** initialize a quest, given the agent id and an extraction point 
     */
    public Quest( Board game, int agent, Position extr ) {
        super( game, new int[]{ agent } );
        _extr = extr;
    }

    /** get the agent working on this quest
     */
    public final int getAgent() {
        return _index[0];
    }

    /** get current position of the robot
     */
    public Position currPos() {
        return new Position( _game.getMyAgentX( _index[0] ),
                             _game.getMyAgentY( _index[0] ) );
    }

    /** pause the current quest and run the sub-quest
     */
    public void runSubQuest( Quest quest ) {
        _sub = quest;
    }

    /** find and run a sub-quest in the system, given deadline and
     * extraction position
     */
    public void runSubQuest( int deadline, Position pos ) {
        Quest quest = _game.getQuest( _index[0], deadline, pos );
        if ( quest != null )
            runSubQuest( quest );
    }

    /** the expected rounds to run the whole quest, including the sub-quest
     */
    public int expectedRounds() {
        if ( _sub != null )
            return _sub.expectedRounds() + expectedRounds( _sub._extr );
        return expectedRounds( new Position( _game.getMyAgentX(_index[0]),
                                             _game.getMyAgentY(_index[0]) ) );
    }

    /** given the insertion position, compute the expected rounds to
     * run the quest, without considering the sub-quest
     */
    public abstract int expectedRounds( Position insr );

    /** whether or not the quest is possible, not considering deadline
     */
    public boolean possible() {
        return true;
    }

    /** the next() method is final
     */
    public final int[] next() {
        if ( _sub != null ) {
            int m = _sub.move()[0];
            if ( Board.QUIT != m )
                return new int[]{ m };
        }
        int m = questMove();
        _deadline--;
        if ( m == Board.QUIT )
            return null;
        return new int[]{ m };
    }

    /** the questMove() method is final */
    public final int questMove() {
        if ( _deleted )
            return Board.QUIT;
        return _game.unmapMove( questNext() );
    }

    /** the derived class must override this class
     */
    public abstract int questNext();

    /** Delete an agent
     */
    public void delete( int agent ) {
        _deleted = true;
    }

}


/**
 * Paint a specified rectangle in shortest time: Modified Abhinav's
 * algorithm
 */
class RectMission extends Mission {
    int _left, _top, _right, _bottom;  // the borders of the rectangle
    SortedSet _heap;     // this SortedSet is used for priority queue

    // a relation between an agent and a square
    class Pair {
        int agent;       // the agent
        Position cell;   // the coordinates of a square
        int idx;         // the index of the cell
        int cost;        // the cost

        public Pair( int a, Position c, int i ) {
            agent = a;
            cell = c;
            idx = i;
            cost = c.distance( _game.getMyAgentX(a), _game.getMyAgentY(a) );
        }
    }

    // An operator class used for sorting
    public class MyComparator implements Comparator {
        public int compare( Object o1, Object o2 ) {
            if ( ((Pair)o1).cost < ((Pair)o2).cost )
                return -1;
            if ( ((Pair)o1).cost > ((Pair)o2).cost )
                return 1;
            return 0;
        }
        
        public boolean equals( Object o1, Object o2 ) {
            return ((Pair)o1).cost == ((Pair)o2).cost;
        }
    }     
    
    /**
     * Given the border coordinates of a rectangle, initilize a RectMission. 
     */
    public RectMission( Board _game, int[] index, 
                        int left, int top, int right, int bottom ) {
        super( _game, index );

        _left = left;
        _top = top;
        _right = right;
        _bottom = bottom;

        _heap = new TreeSet( new MyComparator() );
    }

    /** Whether or not we have finished the rectangle (or failed)
     */
    public boolean finished() {
        for ( int i=_left; i<=_right; i++ ) {
            // excludes black squares and squares in my colors
            if ( !_game.squareIsBlack( i, _top )
                 &&_game.squareOwner( i, _top ) != 0 )
                return false;
            if ( !_game.squareIsBlack( i, _bottom )
                 &&_game.squareOwner( i, _bottom ) != 0 )
                return false;
        }

        for ( int i=_top+1; i<_bottom; i++ ) {
            if ( !_game.squareIsBlack( _left, i )
                 &&_game.squareOwner( _left, i ) != 0 )
                return false;
            if ( !_game.squareIsBlack( _right, i )
                 &&_game.squareOwner( _right, i ) != 0 )
                return false;
        }

        for ( int i=_top+1; i<_bottom; i++ )
            for ( int j=_left+1; j<_right; j++ )
                if ( !_game.squareIsBlack( j, i ) )
                    return true;
        return false;
    }

    /** enumerate all unpainted cells in the rectangle 
     */
    public Position[] enumerate() {
        Position[] ret = new Position[(_right-_left+_bottom-_top+2)*2];
        int c = 0;

        // add all unpainted squares to the list
        for ( int i=_left; i<=_right; i++ ) {
            // excludes black squares and squares in my colors
            if ( !_game.squareIsBlack( i, _top )
                 &&_game.squareOwner( i, _top ) != 0 )
                ret[c++] = new Position( i, _top );

            if ( !_game.squareIsBlack( i, _bottom )
                 &&_game.squareOwner( i, _bottom ) != 0 )
                ret[c++] = new Position( i, _bottom );
        }

        for ( int i=_top+1; i<_bottom; i++ ) {
            if ( !_game.squareIsBlack( _left, i )
                 &&_game.squareOwner( _left, i ) != 0 )
                ret[c++] = new Position( _left, i );
            if ( !_game.squareIsBlack( _right, i )
                 &&_game.squareOwner( _right, i ) != 0 )
                ret[c++] = new Position( _right, i );
        }

        // shrink the array if not filled
        if ( c < ret.length ) {
            Position[] tmp = new Position[c];
            System.arraycopy( ret, 0, tmp, 0, c );
            return tmp;
        }
        return ret;
    }

    /** check possibilty of finishing the task
     */
    public boolean possible() {
        for ( int i=_left; i<=_right; i++ ) {
            if ( _game.squareIsBlack( i, _top ) )
                return false;
            if ( _game.squareIsBlack( i, _bottom ) )
                return false;
        }
        
        for ( int i=_top+1; i<_bottom; i++ ) {
            if ( _game.squareIsBlack( _left, i ) )
                return false;
            if ( _game.squareIsBlack( _right, i ) )
                return false;
        }
        return true;
    }

    /** add one more agent to the mission
     */
    public void add( int i ) {
        // already in this mission?
        if ( locate( i ) >= 0 )
            return;
        // allocate more space
        int[] tmp = new int[_index.length+1];

        // copy other agents
        if ( _index.length > 0 )
            System.arraycopy( _index, 0, tmp, 0, _index.length );

        // add this agent to the list
        tmp[_index.length] = i;
        _index = tmp;
    }

    /** check whether the border of the task is what we want
     */
    public boolean checkBorder( int left, int top, int right, int bottom ) {
        if ( left == _left && top == _top 
             && right == _right && bottom == _bottom ) 
            return true;
        return false;
    }

    // delete is the same as the super class

    /** Most important part.  Search the optimal movement
     */
    public Position[] evaluate() {
        // clear the heap
        _heap.clear();
        
        int n = _index.length;         // the number of agents
        Position[] cells = enumerate();// enumerate unpainted squares
        int c = cells.length;          // the number of unpainted squares
        Pair p[][] = new Pair[n][];    // all <agent,square> pairs

        // create all pairs, evaluate their cost and add them to the heap
        for ( int i=0; i<n; i++ ) {
            p[i] = new Pair[c];
            for ( int j=0; j<c; j++ ) {
                p[i][j] = new Pair( _index[i], cells[j], j );
                _heap.add( p[i][j] );
            }
        }

        // return the destintation position for each agent
        Position[] ret = new Position[ _index.length ];
        int cnt = 0;       // counter of number of agents that have a dest.
        
        // do until all agents have an destination or no pairs can be found
        for ( ; cnt<_index.length && !_heap.isEmpty(); ) {
            Pair x = (Pair) _heap.first();     // the pair with smallest cost
            int agent_idx = locate( x.agent ); // the index of the base agent
            int cell_idx = x.idx;              // the index of the square
            int cost = x.cost;                 // its cost

            if ( null == ret[agent_idx] ) {
                // do if this agent does not have a destination
                // set the destination of this agent.
                cnt++;
                ret[agent_idx] = x.cell;
            }

            if ( cost > 0 
                 || _game.squareOwner( cells[cell_idx].x(), 
                                      cells[cell_idx].y() ) >= 0 ) {
                // On this condition the cell will not be removed if
                // (1) it is occupied by one of my agent, and
                // (2) it is white

                // remove all pairs of this cell from the heap
                for ( int i=0; i<n; i++ )
                    _heap.remove( p[i][cell_idx] );
            } else
                _heap.remove( p[agent_idx][cell_idx] );
            
            // remove the cell itself from the list
            cells[cell_idx] = Position._empty;

            // for each cell
            for ( int i=0; i<c; i++ )
                if ( cells[i].valid() ) {
                    // if the cell is still avaiable, adjust the cost
                    // of the cell to this (best) agent to be the sum
                    // of the minimum cost and the distance between
                    // the minimum-cost cell to this cell.  This must
                    // have the cost increased, because of triangle
                    // property.
                    _heap.remove( p[agent_idx][i] );
                    p[agent_idx][i].cost = cost 
                        + cells[i].distance( cells[cell_idx] );
                    _heap.add( p[agent_idx][i] );
                }
        }

        return ret;
    }

    /** find the next moves
     */
    public int[] next() {
        // no agents?
        if ( _index.length == 0 )
            return null;

        // mission accomplished?
        if ( finished() )
            return null;

        // deadline missed?
        if ( _deadline < 0 )
            return null;

        // get all destination squares
        Position[] dst = evaluate();
        int[] ms = new int[_index.length];

        // do movements
        for ( int i=0; i<_index.length; i++ ) {
            if ( dst[i] == null )
                ms[i] = Board.QUIT;
            else
                ms[i] = _game.moveTo( 
                    _index[i], dst[i].x(), dst[i].y(), _deadline );
        }

        // the caller can individually dismiss an agent from the
        // RectMission

        return ms;
    }
}

/** The most dirty class that do complicated things.  This must be
 * the root mission.
 */
class RoyalCavalryMission extends Mission {
    Mission[] _mission = new Mission[8];    // all possible missions
    // _mission[0-1] for Cavalry and [2-5] for RectMission

    Quest[] _chasee;                        // anti-chaser quests
    boolean[] _is_free;                     // free agent pool

    /** constructor
     */
    public RoyalCavalryMission( Board game, int[] index ) {
        super( game, index );
        int n1, n2;

        _chasee = new Quest[index.length];
        _is_free = new boolean[index.length];

        if ( index.length <= 1 )
            return;

        if ( index.length >= 4 ) {
            n1 = index.length/2;
            n2 = index.length - n1;
        } else {
            n1 = index.length;
            n2 = 0;
        }

        int[] index1 = new int[n1];
        int[] index2 = new int[n2];
        System.arraycopy( index, 0, index1, 0, n1 );
        System.arraycopy( index, n1, index2, 0, n2 ); 

        _mission[0] = new CavalryMission( 
            game, index1, null, 0, _size/2, _size-1 );

        if ( n2 > 0 ) {
            _mission[1] = new CavalryMission( 
                new TransBoard( game, TransBoard.FLIP ),
                index2, null, 0, 0, _size/2-1 );
        }
    }

    /** mission impossible: never accomplished 
     */
    public boolean finished() {
        return false;
    }

    /** do actual movement
     */
    public int[] next() {
/*
        for ( int i=0; i<_mission.length; i++ ) {
            if ( null == _mission[i] )
                continue;
            System.out.print( "Mission [" + i + "]: " );
            for ( int j=0; j<_mission[i]._index.length; j++ )
                System.out.print( " " + _mission[i]._index[j] );
            System.out.println();
        }
        System.out.print( "Anti-chaser agents: " );
        for ( int i=0; i<_chasee.length; i++ ) {
            if ( null == _chasee[i] )
                continue;
            System.out.println( " " + _chasee[i].getAgent() );
        }
        System.out.println();
        System.out.print( "Free agents: " );
        for ( int i=0; i<_index.length; i++ ) {
            if ( _is_free[i] )
                System.out.print( " " + i );
        }
        System.out.println();
*/
        // check whether there are some agent is chased
        for ( int i=0; i<_index.length; i++ ) {
            if ( _game.chased( i ) ) {
                // System.out.println( "Agent " + i + " is being chased" );

                if ( _is_free[i] ) {
                    // if it's a free agent, remove from the free pool
                    _is_free[i] = false;
                } else {
                    // else, remove from missions
                    for ( int k=0; k<_mission.length; k++ ) {
                        if ( _mission[k] != null )
                            _mission[k].delete( i );
                    }
                }

                // check whether or not it is already doing anti-chaser quest
                int pos = -1;
                for ( int j=0; j<_chasee.length; j++ )
                    if ( _chasee[j] != null && _chasee[j]._index[0] == i )
                        pos = j;

                // no previous quest existed
                if ( pos < 0 ) {
                    // find an empty slot to accommodate the quest
                    pos = 0;
                    while ( _chasee[pos] != null )
                        pos++;
                    // initialize and anti-chaser quest
                    _chasee[pos] = new ChaseeQuest( _game, i );
                }
                    
            }
        }

        // now we begin to do missions
        int[][] mss = new int[_mission.length][];

        // do all missions and get moves
        for ( int k=0; k<_mission.length; k++ )
            if ( _mission[k] != null )
                mss[k] = _mission[k].move();

        int[] ms = new int[_index.length];
        for ( int i=0; i<ms.length; i++ ) 
            ms[i] = Board.QUIT;

        // do all anti-chaser quests
        for ( int i=0; i<ms.length; i++ ) {
            if ( _chasee[i] != null ) {
                int agent = _chasee[i]._index[0];

                ms[agent] = _game.mapMove( _chasee[i].questMove() );
                if ( ms[agent] == Board.QUIT ) {
                    _chasee[i] = null;
                }
            }
        }

        // merge all moves of sub-missions, and dismiss finished missions
        for ( int k=0; k<_mission.length; k++ )
            if ( mss[k] != null ) {
                for ( int i=0; i<_mission[k]._index.length; i++ ) {
                    // CavalryMission never dismiss agent, but RectMission do
                    if ( mss[k][i] == Board.QUIT )
                        _mission[k].delete( _mission[k]._index[i] );
                    else
                        ms[_mission[k]._index[i]] = _game.mapMove( mss[k][i] );
                }
            } else
                _mission[k] = null;

        // recycle all freed agents
        for ( int i=0; i<ms.length; i++ ) {
            if ( ms[i] == Board.QUIT ) {
                // set it free
                _is_free[i] = true;
                
                ms[i] = Board.STAY;
            }
        }

        // check how many agents are free
        int free_count = 0;
        for ( int i=0; i<ms.length; i++ )
            if ( _is_free[i] )
                free_count++;

        if ( free_count > 0 ) {
            // if there are some free agents, find possible rectangles
            boolean[] blacks = _game.getBlackMatrix();
            int[] ret = Tools.findRects( blacks, _size, 100 );

            if ( null == ret || ret.length < 16 ) {
                // try the third stage rect finder
                int[] gain = new int[100];
                int[] ret2 = Tools.findRects( blacks, _size, gain );
                if ( null == ret )
                    ret = ret2;
                else {
                    int[] tmp = new int[ret.length+ret2.length];
                    System.arraycopy( ret, 0, tmp, 0, ret.length );
                    System.arraycopy( ret2, 0, tmp, ret.length, ret2.length );
                    ret = tmp;
                }
            }

/*
            if ( ret.length == 0 )
                System.out.println( "No rectangles found" );
            for ( int i=0; i<ret.length; i+=4 )
                System.out.println( "Rect: #" + i/4 + " = " + ret[i] + "," 
                                    + ret[i+1] + "," + ret[i+2] + ","
                                    + ret[i+3] );
*/

            if ( null != ret ) {
                 
                // try incrementing the distance
                for ( int i=1; i<_size && free_count > 0; i<<=1 ) {
                    // for each rectangle found
                    for ( int j=0; j<ret.length && free_count > 0; j+=4 ) {
                        // find an agent within the specified distance
                        int idx = _game.locateNearestAgent( 
                            _index, _is_free, i,
                            ret[j], ret[j+1], ret[j+2], ret[j+3] );
                        
                        if ( idx < 0 )
                            continue;
                        
                        // check whether such RectMission existed
                        for ( int k=2; k<_mission.length; k++ ) {
                            if ( _mission[k] == null )
                                continue;
                            
                            if ( ((RectMission)_mission[k]).checkBorder(
                                     ret[j], ret[j+1], ret[j+2], ret[j+3] ) ) {
                                // if there is some identical mission
                                // add this agent to this mission
                                ((RectMission)_mission[k]).add( _index[idx] );
                                // this agent is no longer free
                                _is_free[ _index[idx] ] = false;
                                free_count--;
                                break;
                            }
                        }
                        
                        // no previous missions?
                        if ( _is_free[ _index[idx] ] ) {
                            // find an empty slot for the new mission
                            for ( int k=2; k<_mission.length; k++ )
                                if ( _mission[k] == null ) {
                                    // create a RectMission
                                    _mission[k] = new RectMission( 
                                        _game, new int[]{ _index[idx] },
                                        ret[j], ret[j+1], ret[j+2], ret[j+3] );
                                    // this agent is no longer free
                                    _is_free[ _index[idx] ] = false;
                                    free_count--;
                                    break;
                                }
                        }
                    }
                }
            } // if ( null != ret )
        } // if ( free_count > 0 )
/*
        for ( int i=0; i<ms.length; i++ )
            System.out.print( "  " + ms[i] );
        System.out.println();
*/
        return ms;
    }    
}

/**
 * The toolbox class.  This class contains only static members.
 */
class Tools {

    /** find rectangles in the matrix
     *
     * @param src the board, saved in one dimensional boolean array,
     *            line by line.
     * @param n   the number of rectangles expected
     * @return an array of integers with 4*n elements.  For each
     * rectangle, the coordinates borders are in the order of
     * left, top, right, and bottom, all inclusive.
     */
    public static final int[] findRects( boolean[][] src, int n ) {
        int size = src.length;
        boolean[] tmp = new boolean[size*size];
        for ( int i=0; i<size; i++ )
            for ( int j=0; j<size; j++ )
                tmp[i*size+j] = src[j][i];
        return findRects( tmp, size, n );
    }

    /** find rectangles in the boolean matrix, which is saved in one
     * dimensional array.  These rectangles contains no black squares
     */
    public static final int[] findRects( boolean[] src, int size, int n ) {
        boolean[] tmp = dilate( src, size );
        for ( int i=0; i<tmp.length; i++ )
            tmp[i] = !tmp[i];
        int[] ret = _findRects( tmp, size, n );
        if ( null == ret )
            return null;

        for ( int i=0; i<ret.length; i+= 4 ) {
            ret[i]--;
            ret[i+1]--;
            ret[i+2]++;
            ret[i+3]++;
        }
        return ret;
    }

    /** count the number of black square in a horizontal line
     */
    static final int countLine( boolean[] src, int size,
                                int x0, int x1, int y ) {
        int ret = 0;
        for ( int i=x0; i<=x1; i++ )
            if ( src[y*size+i] )
                ret++;
        return ret;
    }

    /** count the number of black square in a horizontal line
     */
    static final int countVLine( boolean[] src, int size,
                                 int x, int y0, int y1 ) {
        int ret = 0;
        for ( int i=y0; i<=y1; i++ )
            if ( src[i*size+x] )
                ret++;
        return ret;
    }


    /** find rectangles in the boolean matrix, which is saved in one
     * dimensional array.  These rectangles may contain black squares
     */
    public static final int[] findRects( boolean[] src, int size, 
                                         int[] gain ) {

        int n = gain.length;
        boolean[] tmp = new boolean[src.length];
        System.arraycopy( src, 0, tmp, 0, src.length );

        // find all black rectangles, note that _findRects trashes the input
        int[] brect = _findRects( tmp, size, 1000 );
        if ( brect == null ) // this won't happen
            return new int[]{ 0, 0, size-1, size-1 };

        // allocate the return buffer
        int idx = 0;
        int[] ret = new int[n*4];

    outer_loop:
        // for each black rectangles
        for ( int k=0; k<brect.length; k+=4 ) {
            // get the surrounding colored rectangle
            int left = brect[k]-1;
            int top = brect[k+1]-1;
            int right = brect[k+2]+1;
            int bottom = brect[k+3]+1;

            int g0 = 0;             // g0, g1 are for computing gains

            if ( top > 0 
                 && 0 == countLine( src, size, left, right, top-1 ) ) {
                 top--;
                 g0 += right-left-1;
            }
            if ( left > 0 
                 && 0 == countVLine( src, size, left-1, top, bottom ) ) {
                 left--;
                 g0 += bottom-top-1;
            }

            int n0 = right-left-1;  // n0, n1 are the numbers of black squares

            // expand vertically
            for ( int i=bottom; i<size; i++ ) {
                // if (left,i+1) is black, no need to continue
                if ( src[i*size+left] )
                    break;
                // the gain is incremented by the number of non-black square
                // of the previous line
                g0 += (right-left-1) - n0;
                // check number of black square on (left+1,i)-(right-1,i)
                n0 = countLine( src, size, left+1, right-1, i );

                int g1 = g0;
                int n1 = i-top-1;
                // expand horizontally
                for ( int j=right; j<size; j++ ) {
                    // if either (j,top) or (j,i) is black
                    if ( src[top*size+j] || src[i*size+j] )
                        break;
                    // add non-black squares in the previous line
                    g1 += (i-top-1) - n1;
                    // count the number of black squares on (j,top+1)-(j,i-1)
                    n1 = countVLine( src, size, j, top+1, i-1 );
                    if ( n0 == 0 && n1 == 0 && g1 > 0 ) {
                        // this rectangle is acceptable
                        gain[idx] = g1;
                        ret[idx*4] = left;
                        ret[idx*4+1] = top;
                        ret[idx*4+2] = j;
                        ret[idx*4+3] = i;
                        idx++;

                        if ( idx >= n )
                            break outer_loop;
                    }
                }
            }
        }

        // if there is not enough rectangles, shrink the array
        if ( idx <= n ) {
            int[] tmp_ret = ret;
            ret = new int[idx*4];
            System.arraycopy( tmp_ret, 0, ret, 0, idx*4 );
        }

        return ret;        
    }

    /** dilate an image
     */
    public static final boolean[] dilate( boolean[] src, int size ) {
        boolean[] ret = new boolean [size*size];

        // for each of the periphery squares
        for ( int i=0; i<size; i++ )
            ret[i] = ret[i*size] = 
                ret[(size-1)*size+i] = ret[i*size+(size-1)] = true;

        // for each of non-periphery squares
        for ( int i=1; i<size-1; i++ )
            for ( int j=1; j<size-1; j++ ) {
            inner_loop:  // search all surrounding 8 squares and itself
                for ( int k=-1; k<=1; k++ ) 
                    for ( int l=-1; l<=1; l++ ) {
                        if ( src[(i+k)*size+(j+l)] ) {
                            ret[i*size+j] = true;
                            break inner_loop;
                        }
                    }
            }

        return ret;
    }

    /**
     * find n rectangles in the matrix.  The input should be true for
     * non-black squares.  Note the original matrix is trashed.
     * @return an array of integers with 4n elements.  For each
     * rectangle, the coordinates borders are in the order of
     * left, top, right, bottom, all inclusive.
     */
    private static final int[] _findRects( boolean[] mat, int size, int n ) {
        // allocate space for return value
        int idx = 0;
        int[] ret = new int[n*4];

        // for each of the squars
        for ( int i=1; i<size-1; i++ )
            for ( int j=1; j<size-1; j++ ) {

                // if a non-black square is found
                if ( mat[i*size+j] ) {
                    // the rectangles are at least 1 by 1
                    int width=1, height=1;
                    boolean exp_w = true, exp_h = true;

                    // if horizontally or vertically expansible
                    while ( exp_w || exp_h ) {
                        // Expansions of the two dimensions alternate.
                        // If we have several solutions, a solution
                        // that has less width/height difference is
                        // taken.
                        
                        if ( exp_w ) {
                            // expand the width of the rectangle
                            for ( int k=0; k<height; k++ )
                                if ( !mat[(i+k)*size+(j+width)] ) {
                                    // if a black square is found in
                                    // the next column
                                    exp_w = false;
                                    break;
                                }
                            if ( exp_w ) {
                                // remove squares that has been included
                                // from the working image
                                for ( int k=0; k<height; k++ )
                                    mat[(i+k)*size+(j+width)] = false;
                                width++;
                            }
                        }

                        if ( exp_h ) {
                            // expand the height of the rectangle
                            for ( int k=0; k<width; k++ )
                                if ( !mat[(i+height)*size+(j+k)] ) {
                                    exp_h = false;
                                    break;
                                }
                            if ( exp_h ) {
                                for ( int k=0; k<width; k++ )
                                    mat[(i+height)*size+(j+k)] = false;
                                height++;
                            }
                        }
                    } // end of while

                    // we may need to dilate and remove this rectangles
                    // from the working copy of the image to avoid
                    // conflict of interests of two squares
                    for ( int k=0; k<width; k++ )
                        mat[(i-1)*size+(j+k)] = 
                            mat[(i+height)*size+(j+k)] = false;
                    for ( int k=-1; k<=height; k++ )
                        mat[(i+k)*size+(j-1)] =
                            mat[(i+k)*size+(j+width)] = false;
                    
                    // copy the information of the rectangle found to
                    // the return array
                    ret[idx++] = j;
                    ret[idx++] = i;
                    ret[idx++] = j+width-1;
                    ret[idx++] = i+height-1;

                    // if enough rectangles have been collected.
                    if ( idx >= ret.length-3 )
                        return ret;
                }
            }

        // if no such rectangles can be found, return null;
        if ( 0 == idx )
            return null;

        // if there is not enough rectangles, shrink the array
        if ( idx <= ret.length ) {
            int[] tmp_ret = ret;
            ret = new int[idx];
            System.arraycopy( tmp_ret, 0, ret, 0, idx );
        }

        return ret;
    }
}

class TransBoard extends Board {
    final static int MIRROR    = 1;
    final static int FLIP      = 2;
    final static int TRANSPOSE = 4;
    /*
     * 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            NW             -
     *    1            NE             M
     *    2            SW             F
     *    3            SE             FM  (rotate 180)
     *    4            NWV            T
     *    5            NEV            TM  (rotate 90CW)
     *    6            SWV            TF  (rotate 90CCW)
     *    7            SEV            TFM (tranpose anti-diagonally)
     */

    int _orient;
    Board _board;

    public TransBoard( Board board, int orient ) {
        _board = board;
        _orient = orient;
    }

    private static int[][] __map_move = { 
        { EAST, WEST, EAST, WEST, SOUTH, SOUTH, NORTH, NORTH }, // EAST
        { WEST, EAST, WEST, EAST, NORTH, NORTH, SOUTH, SOUTH }, // WEST
        { SOUTH, SOUTH, NORTH, NORTH, EAST, WEST, EAST, WEST }, // SOUTH
        { NORTH, NORTH, SOUTH, SOUTH, WEST, EAST, WEST, EAST } }; // NORTH

    final static int mapMove( int move, int orient ) {
        if ( move < 0 || move >= 4 )
            return move;
        return __map_move[move][orient];
    }

    final static Position mapPos( Position pos, int orient, int size ) {
        if ( pos.valid() )
            return mapPos( pos.x(), pos.y(), orient, size );
        return pos;
    }

    final static Position mapPos( int x, int y, int orient, int size ) {
        if ( ( orient & TRANSPOSE ) != 0 ) {
            int t = x;
            x = y;
            y = t;
        }

        switch ( orient & 3 ) {
        case (FLIP|MIRROR):
            y = size - y - 1;
        case MIRROR:
            x = size - x - 1;
            break;
        case FLIP:
            y = size - y - 1;
            break;
        default:
            break;
        }

        return new Position( x, y );
    }

    public final int mapX( int x, int y, int size ) {
        switch ( _orient ) {
        case 0:
        case FLIP:
            return x;
        case MIRROR:
        case (FLIP|MIRROR):
            return size - x - 1;
        case TRANSPOSE:
        case (TRANSPOSE|FLIP):
            return y;
        case TRANSPOSE|MIRROR:
        case (TRANSPOSE|FLIP|MIRROR):
            return size - y - 1;
        default:
            return -1;
        }
    }

    public int mapY( int x, int y, int size ) {
        switch ( _orient ) {
        case 0:
        case MIRROR:
            return y;
        case FLIP:
        case (FLIP|MIRROR):
            return size - y - 1;
        case TRANSPOSE:
        case TRANSPOSE|MIRROR:
            return x;
        case (TRANSPOSE|FLIP):
        case (TRANSPOSE|FLIP|MIRROR):
            return size - x - 1;
        default:
            return -1;
        }
    }

    public int size() {
        return _board.size();
    }

    public int getMyAgentX( int i ) {
        return mapX( _board.getMyAgentX(i), _board.getMyAgentY(i),
                     _board.size() );
    }

    public int getMyAgentY( int i ) {
        return mapY( _board.getMyAgentX(i), _board.getMyAgentY(i),
                     _board.size() );
    }

    public int getAgentNum() {
        return _board.getAgentNum();
    }

    public int getTeamNum() {
        return _board.getTeamNum();
    }

    public int getAgentX( int opp, int i ) {
        return mapX( _board.getAgentX(opp,i), _board.getAgentY(opp,i),
                     _board.size() );
    }

    public int getAgentY( int opp, int i ) {
        return mapY( _board.getAgentX(opp,i), _board.getAgentY(opp,i),
                     _board.size() );
    }

    public int getLastMove( int opp, int i ) {
        int m = _board.getLastMove(opp,i);
        if ( m < 0 || m >= 4 )
            return m;
        return __map_move[m][_orient];
    }


    public boolean squareIsBlack( int x, int y ) {
        int s = _board.size();
        return _board.squareIsBlack( mapX(x,y,s), mapY(x,y,s) );
    }

    public boolean squareIsColored( int x, int y ) {
        int s = _board.size();
        return _board.squareIsColored( mapX(x,y,s), mapY(x,y,s) );
    }

    public int squareOwner( int x, int y ) {
        int s = _board.size();
        return _board.squareOwner( mapX(x,y,s), mapY(x,y,s) );
    }

    public int getScore( int i ) {
        return _board.getScore(i);
    }    

    private static int[][] __unmap_move = { 
        { EAST, WEST, EAST, WEST, SOUTH, NORTH, SOUTH, NORTH }, // EAST
        { WEST, EAST, WEST, EAST, NORTH, SOUTH, NORTH, SOUTH }, // WEST
        { SOUTH, SOUTH, NORTH, NORTH, EAST, EAST, WEST, WEST }, // SOUTH
        { NORTH, NORTH, SOUTH, SOUTH, WEST, WEST, EAST, EAST } }; // NORTH

    public int unmapMove( int move ) {
        if ( move < 0 || move >= 4 )
            return _board.unmapMove( move );
/*
        System.out.println( "T " + move + "=>" + __unmap_move[move][_orient] 
                            + "=>" 
                            + _board.unmapMove( __unmap_move[move][_orient] ) 
                            + " with orientation " + _orient );
*/
        // return __unmap_move[move][_orient];
        return _board.unmapMove( __unmap_move[move][_orient] );
    }

    public int mapMove( int move ) {
        return mapMove( _board.mapMove( move ), _orient );
    }
}

class VLineMission extends LineMission {

    public VLineMission( Board game, int[] index, Position[] extr, 
                         int x, int y0, int y1 ) {

        super( new TransBoard( game, TransBoard.TRANSPOSE ),
               index, mapPositions( extr, game.size() ),
               y0, y1, x );
    }

    private static Position[] mapPositions( Position[] extr, int size ) {
        Position[] new_extr = new Position[extr.length];

        for ( int i=0; i<extr.length; i++ )
            new_extr[i] = TransBoard.mapPos( extr[i], 
                                             TransBoard.TRANSPOSE, size );
        return new_extr;
    }
}

class VLineQuest extends LineQuest {
    public VLineQuest( Board game, int agent, Position extr, 
                       int x, int y0, int y1 ) {
        super( new TransBoard( game, TransBoard.TRANSPOSE ),
               agent, 
               TransBoard.mapPos( 
                   extr, TransBoard.TRANSPOSE, game.size() ), 
               y0, y1, x );
    }
}
