This is SplatRules.m in view mode; [Download] [Up]
/*
* 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.