/*
 * SplatRules
 * description: subclass of the Rules object: rules for the game Splat
 * history:
 *	2/15/93 [Erik Kay] - created
 *	6/13/93	[Erik Kay] - converted from Rules into SplatRules
 */

#import "SplatRules.h"

#import "SplatView.h"
#import "SplatGame.h"
#import "Player.h"

@implementation SplatRules

- init
{
    [super init];
    possible_moves.prev = possible_moves.next = NULL;
    return self;
}

// return a list of valid moves
- (List *)validMoves:(Board *)b forPlayer:(square_state)piece
{
    int rows, cols, i;
    List *boardlist = [[List alloc] init];
    Board *newb, *tmpb;
    move_list *mvlist;
    location *from, *to;
    
    rows = [b rows];
    cols = [b cols];
    tmpb = [[[Board alloc] initCols:cols Rows:rows] clearBoard];

    // cycle through the list of possible moves, looking for available moves
    for (mvlist = &possible_moves; mvlist != NULL; mvlist = mvlist->next) {
	from = &(mvlist->mv.from);
	i = from->col + from->row * cols;
	if (b->board[i] != piece)
	    continue;
	to = &(mvlist->mv.to);
	i = to->col + to->row * cols;
	if (b->board[i] != SQUARE_EMPTY) // can only move on to empty squares
	    continue;
	// is this a move or a jump?
	if (IS_MOVE(mvlist->mv)) {
	    if (tmpb->board[i] == SQUARE_EMPTY) {
		newb = [b trueCopy];
		[self addPiece:piece onBoard:newb at:to];
		[tmpb setPiece:piece at:to];
	    } else continue;
	} else {
	    newb = [b trueCopy];
	    [self movePiece:&mvlist->mv onBoard:newb];
	}
	[newb setCurrentMove:&mvlist->mv];
	[boardlist addObject:newb];
    }
    [tmpb free];
    return boardlist;
}

// is a move a valid move or jump?
- (BOOL)validMove:(move *)mv onBoard:(Board *)b
{
    int value;
    value = [b pieceAt:mv->to.col :mv->to.row];
    if (value != SQUARE_EMPTY)
    	return NO;
    if (((abs(mv->from.row - mv->to.row) <= 1) && 
    			(abs(mv->from.col - mv->to.col) <= 1)) ||
    	((abs(mv->from.row - mv->to.row) <= 2) && 
			(abs(mv->from.col - mv->to.col) <= 2)))
	return YES;
    return NO;
}

// do a move on the board
- doMove:(move *)mv onBoard:(Board *)b
{
    square_state piece;
    piece = [b pieceAt:mv->from.col :mv->from.row];
    if ((abs(mv->from.col - mv->to.col) < 2) && (abs(mv->from.row - mv->to.row) < 2))
	[self addPiece:piece onBoard:b at:mv->to.col :mv->to.row];
    else
	[self movePiece:mv onBoard:b];
    return self;
}

// undo the last move
// This is done by re-doing all of the moves except for the last one
- (Board *)undoMove:(move_list *)mvlist
{
    Board *newBoard;
    int count = 0;
    
    newBoard = [initialBoard trueCopy];
    // do all but the last move;
    while (mvlist->next) {
    	if ((mvlist->next->next)) { 
	    [self doMove:&mvlist->mv onBoard:newBoard];
	    count++;
	}
	mvlist = mvlist->next;
    }
    return newBoard;
}

// add a piece to the board (a copy)
- addPiece:(square_state)piece onBoard:(Board *)b at:(int)col :(int)row
{
    int r, rm, c, cm, value, rows, cols, i;
    
    rows = [b rows];
    cols = [b cols];
    b->board[col + row * cols] = piece;
    if (piece == SQUARE_ONE) {
	b->numone++;
    } else {
	b->numtwo++;
    }
    for (cm = -1; cm <= 1; cm++) {
	c = col + cm;
	if ((c < 0) || (c > (cols-1)))
	    continue;
	for (rm = -1; rm <= 1; rm++) {
	    r = row + rm;
	    if ((r < 0) || (r > (rows-1)))
		continue;
	    i = c + r * cols;
	    value = b->board[i];
	    if ((value == SQUARE_BLOCKED) || (value == SQUARE_EMPTY))
	    	continue;
	    if (value != piece) {
	    	b->board[i] = piece;
		if (piece == SQUARE_ONE) {
		    b->numone++;
		    b->numtwo--;
		} else {
		    b->numtwo++;
		    b->numone--;
		}
	    }
	}
    }
    return self;
}

- addPiece:(square_state)piece onBoard:(Board *)b at:(location *)loc
{
    return [self addPiece:piece onBoard:b at:loc->col :loc->row];
}

// move a piece from one square to another
- movePiece:(move *)mv onBoard:(Board *)b
{
    [self addPiece:[b pieceAt:mv->from.col :mv->from.row] onBoard:b
    	at:mv->to.col :mv->to.row];
    [b setPiece:SQUARE_EMPTY at:mv->from.col :mv->from.row];
    return self;
}

// build up a list of possible moves
- setInitialBoard:(Board *)b
{
    int count, rows, cols;
    move mv;
    move_list *mvlist, *next;
    
    [super setInitialBoard:b];
    mvlist = &possible_moves;
    rows = [b rows];
    cols = [b cols];
    count = rows * cols;
    next = NULL;
    for (mv.from.col = 0; mv.from.col < cols; mv.from.col++) {
    	for (mv.from.row = 0; mv.from.row < rows; mv.from.row++) {
	    for (mv.to.col = mv.from.col - 2; mv.to.col <= mv.from.col + 2; mv.to.col++) {
		if ((mv.to.col < 0) || (mv.to.col > (cols - 1)))
		    continue;
		for (mv.to.row = mv.from.row - 2; mv.to.row <= mv.from.row + 2; mv.to.row++) {
		    if ((mv.to.row == mv.from.row) && (mv.to.col == mv.from.col))
		    	continue;
		    if ((mv.to.row < 0) || (mv.to.row > (rows - 1)))
			continue;
		    if ([b pieceAt:&mv.to] == SQUARE_BLOCKED)
		    	continue;
		    if ([b pieceAt:&mv.from] == SQUARE_BLOCKED)
		    	continue;
		    if (next) {
		    	mvlist->next = next;
			next->prev = mvlist;
			mvlist = next;
		    }
		    mvlist->mv = mv;
		    next = malloc(sizeof(move_list));
		}
	    }
	}
    }
    mvlist->next = NULL;
    free(next);
    return self;
}

- free
{
    move_list *list;
    list = possible_moves.next;
    while (list->next) {
	list = list->next;
	free(list->prev);
    }
    free(list);
    [super free];
    return self;
}

// test to see if player has any moves left
- (BOOL)gameOver:(Board *)b forPlayer:(square_state)player
{
    int count;
    List *moves;
    moves = [self validMoves:b forPlayer:player];
    count = [moves count];
    [[moves freeObjects] free];
    if (count == 0) {
	return YES;
    } else {
	return NO;
    }
}

// who's the winner?
- winner:game
{
    int num1, num2;
    Board *b;
    b = [game board];
    num1 = [b numberOfPiece:[[game player1] pieceType]];
    num2 = [b numberOfPiece:[[game player2] pieceType]];
    if (num1 > num2)
    	return [game player1];
    else if (num1 < num2)
	return [game player2];
    else
	return NULL;
}

@end
