
public class ARCodelet implements Codelet {
    static ARRandom rnd = new ARRandom();

    ARArray array;
    int score;
    int sx;
    int sy;
    int ex;
    int ey;
    String path;

    public ARCodelet( ARArray arr ) {
	array = arr;

	score = Integer.MIN_VALUE;
	sx = 0;
	sy = 0;
	ex = 0;
	ey = 0;
	path = null;
    }

    public ARCodelet( ARArray arr, int x, int y, String p ) {
	array = arr;
	ex = 0;
	ey = 0;
	path = p;
	setStart( x, y );
    }


    public void randomize() {
	int w = array.getWidth();
	int h = array.getHeight();
	int n = w + h;

	double s = (double)( ( w < h ) ? w : h );
	int x = rnd.nextInt( n );

	if ( s > 30.0 ) {
	    s = 10.0;
	}

	    //
	    // Okay, so given than 'n' is the maximum length
	    // that I'm interested in generating and that I
	    // want to favor 's'-ish sized ones as opposed
	    // to longer or shorter ones,  I'm going to
	    // remap 'x' through a special cubic function.
	    //
	    //  f(x) = a(2x-n)^3 + bx^2 + c(x-n) + d
	    //
	    //  with the constraints that:
	    //		 f(0)   = 1
	    //		 f(n/2) = s
	    //		f'(n/2) = 0
	    //		 f(n)   = n
	    //
	    // this will favor values near s.
	    //
	
	double N = (double)n;
	double a = ( N - 1.0 ) / ( 2.0 * N * N * N );
	double b = 4.0 * ( N - a * N * N * N - s ) / ( N * N );
	double c = -b * N;
	double d = s - 3.0 * b * N * N / 4.0;

	double l = a * (double)( (2*x-n) * (2*x-n) * (2*x-n) )
		+  b * (double)( x * x )
		+  c * (double)( x - n )
		+  d
		;

	int len = (int)( l + 0.5 );

	    //
	    // I cannot figure out how this ever happens,
	    // but alas, I'll fudge it for now.
	    //
	if ( len <= 0 ) {
	    len = 1;
	}

	    //
	    // Now, create a new path of this length
	    //
	char[] p = new char[ len ];

	char[] directionTable = {
	    'N', 'S', 'S', 'E', 'E', 'W'
	};

	for ( int ii=0; ii < len; ++ii ) {
	    p[ii] = directionTable[ rnd.nextInt( directionTable.length ) ];
	}

	path = new String( p );

	    //
	    // pick a starting point
	    //
	setStart( rnd.nextInt( w ), rnd.nextInt( h ) );
    }

    public void setStart( int x, int y ) {
	int w = array.getWidth();
	int h = array.getHeight();

	sx = x;
	sy = y;

	    //
	    // find the ending point
	    //
	ex = sx;
	ey = sy;

	for ( int ii=0; ii < path.length(); ++ii ) {
	    switch ( path.charAt( ii ) ) {
	    case 'N':
		--ey;
		break;
	    case 'S':
		++ey;
		break;
	    case 'E':
		++ex;
		break;
	    case 'W':
		--ex;
		break;
	    }

	    if ( ex < 0 ) {
		ex = 0;
	    } else if ( ey < 0 ) {
		ey = 0;
	    } else if ( ex >= w ) {
		ex = w-1;
	    } else if ( ey >= h ) {
		ey = h-1;
	    }
	}

	    //
	    // score the path
	    //
	score = array.scorePath( path, sx, sy, false );
    }

    public boolean isComplete() {
	return ( sx == 0 && sy == 0 );
    }

    public int getScore() {
	return score;
    }

    public int getFinalScore() {
	try {

	    ARCodelet c = new ARCodelet( array, 0, 0, "" );
	    ARCodelet nc = (ARCodelet)c.combineWith( this );

	    sx = 0;
	    sy = 0;
	    path = nc.getPath();
	    score = nc.getScore();

	} catch ( Throwable t ) {
	    System.err.println( t );
	}

	return array.scorePath( path, 0, 0, true );
    }

    public Codelet combineWith( Codelet o ) {
	ARCodelet other = (ARCodelet)o;

	int dx = other.sx - ex;
	int dy = other.sy - ey;

	int len = Math.abs( dx ) + Math.abs( dy )
	    + path.length() + other.path.length();
	
	char[] p = new char[ len ];

	int ii = 0;

	    //
	    // add first path
	    //
	while ( ii < path.length() ) {
	    p[ ii ] = path.charAt( ii );
	    ++ii;
	}

	    //
	    // make a connection between the two
	    //
	if ( dx < 0 ) {
	    while ( dx != 0 ) {
		p[ ii ] = 'W';
		++ii;
		++dx;
	    }
	} else if ( dx > 0 ) {
	    while ( dx != 0 ) {
		p[ ii ] = 'E';
		++ii;
		--dx;
	    }
	}

	if ( dy < 0 ) {
	    while ( dy != 0 ) {
		p[ ii ] = 'N';
		++ii;
		++dy;
	    }
	} else if ( dy > 0 ) {
	    while ( dy != 0 ) {
		p[ ii ] = 'S';
		++ii;
		--dy;
	    }
	}

	int jj=0;

	while ( jj < other.path.length() ) {
	    p[ ii ] = other.path.charAt( jj );
	    ++ii;
	    ++jj;
	}

	ARCodelet n = new ARCodelet( array, sx, sy, new String( p ) );

	return n;
    }

    public String getPath() {
	return new String( path );
    }
}
