This is Othello.m in view mode; [Download] [Up]
/* Generated by Interface Builder */ #import "Othello.h" @implementation Othello - saveGame:sender { NXStream *theStream; if (!savePanel) savePanel = [SavePanel new]; if ([savePanel runModal]) { theStream = NXOpenMemory(NULL, 0, NX_WRITEONLY); NXWrite(theStream, theField, NUM_SPACES * sizeof(short)); NXWrite(theStream, &theTurn, sizeof(short)); NXFlush(theStream); NXSaveToFile(theStream, [savePanel filename]); NXClose(theStream); } return self; } - openGame:sender { NXStream *theStream; static const char *const othType[2] = {"oth", NULL}; id openPanel = [[OpenPanel new] allowMultipleFiles:NO]; if ([openPanel runModalForTypes:othType]) { theStream = NXMapFile([openPanel filename], NX_READONLY); NXRead(theStream, theField, NUM_SPACES * sizeof(short)); NXRead(theStream, &theTurn, sizeof(short)); NXClose(theStream); [self updateField:self]; } return self; } int count_pieces(theField, thePlayer) short *theField, thePlayer; { int i, count=0; for(i=0; i<NUM_SPACES; i++) { if(theField[i]==thePlayer) count++; } return count; } - newGame:sender { bzero(&theField, NUM_SPACES * sizeof(short)); /* this needs to be computed-- algorithm to find the middle for squares * and place the appropriate pieces in those squares. */ theField[27]= WHITE; theField[28]= BLACK; theField[35]= BLACK; theField[36]= WHITE; [self updateField:self]; theTurn = WHITE; theField[NUM_SPACES]=theTurn; [status setStringValue:"White's turn."]; [white_number setIntValue:count_pieces(theField, WHITE)]; [black_number setIntValue:count_pieces(theField, BLACK)]; return self; } - updatePlace:(int) theCell forPlayer:(int) thePlayer { switch(thePlayer) { case WHITE: [[playField findCellWithTag:theCell] setIcon:"White_piece"]; break; case BLACK: [[playField findCellWithTag:theCell] setIcon:"Black_piece"]; break; default: [[playField findCellWithTag:theCell] setIcon:""]; } return self; } - updateField:sender { int i; for(i=0; i<NUM_SPACES; i++) { switch(theField[i]) { case WHITE: [[playField findCellWithTag:i] setIcon:"White_piece"]; break; case BLACK: [[playField findCellWithTag:i] setIcon:"Black_piece"]; break; default: [[playField findCellWithTag:i] setIcon:""]; } } return self; } int opp(player) int player; { if (player == WHITE) { return BLACK; } return WHITE; } - (BOOL) checkDirection:(int) xDelta:(int) yDelta fromCell:(int)theCell forPlayer:(int) thePlayer { int x = x(theCell)+xDelta; int y = y(theCell)+yDelta; int deltaCell = cell(x, y); if(x < 0 || x > FIELD_WIDTH-1 || y < 0 || y > FIELD_HEIGHT-1) { return NO; } while(theField[deltaCell] == opp(thePlayer)) { x = x(deltaCell)+xDelta; y = y(deltaCell)+yDelta; if(x < 0 || x > FIELD_WIDTH-1 || y < 0 || y > FIELD_HEIGHT-1) { return NO; } deltaCell=cell(x, y); } if (theField[deltaCell]==thePlayer) { return YES; } return NO; } - (void) doDirection:(int) xDelta:(int) yDelta fromCell:(int)theCell forPlayer:(int) thePlayer { int deltaCell=cell((x(theCell)+xDelta), (y(theCell)+yDelta)); while(theField[deltaCell] == opp(thePlayer)) { theField[deltaCell] = thePlayer; [self updatePlace:deltaCell forPlayer:thePlayer]; deltaCell=cell((x(deltaCell)+xDelta), (y(deltaCell)+yDelta)); } } - (BOOL) allDirectionsFromCell:(int)theCell flipPieces:(BOOL) move forPlayer:(int) thePlayer { int xDelta, yDelta, x, y; BOOL validMove=NO; for (yDelta=-1; yDelta<2; yDelta++) { y = y(theCell) + yDelta; if (y < 0 || y > FIELD_HEIGHT-1) continue; /* off board */ for (xDelta=-1; xDelta<2; xDelta++) { x = x(theCell) + xDelta; if (x < 0 || x > FIELD_WIDTH-1) continue; /* off board */ if (theField[cell(x, y)] != opp(thePlayer)) continue; /* not flanked by opponent */ /* there is an ajacent opponent this direction-- check to see if there * is a flank and make the changes if there is. the eight directions * that need to be changed are independent-- we can safely make the * board changes w/o having to worry about an affect on the other * directions. */ if ([self checkDirection:xDelta:yDelta fromCell:theCell forPlayer:thePlayer]) { if (move) [self doDirection:xDelta:yDelta fromCell:theCell forPlayer:thePlayer]; validMove=YES; } } } return validMove; } - (BOOL) allDirectionsFromCell:(int)theCell flipPieces:(BOOL) move { return [self allDirectionsFromCell:theCell flipPieces:move forPlayer:theTurn]; } - (BOOL) canPlay:(int) thePlayer { int i; /* if there is any move at all, immediately return YES. */ for(i=0; i<NUM_SPACES; i++) { if(theField[i] == EMPTY) if([self allDirectionsFromCell:i flipPieces:NO forPlayer:thePlayer]) { return YES; } } /* otherwise, return NO. */ return NO; } - (char *) playerToString:(int) thePlayer { char *s; s = malloc(6*sizeof(char)); if (thePlayer == WHITE) { strcpy(s, "White"); } else strcpy(s, "Black"); return s; } - (char *) turnString { char *s; s = malloc(15*sizeof(char)); strcpy(s, [self playerToString:theTurn]); strcat(s, "'s turn."); return s; } - moveMade:sender { char msg[25]; int theCell; int white,black; if ((theCell = [sender selectedTag]) < 0) return self;/* invalid click trap */ if (theField[theCell]) return self; /* bad move -- already occupied */ /* try the move */ if([self allDirectionsFromCell:theCell flipPieces:NO]) { [self backup]; [self allDirectionsFromCell:theCell flipPieces:YES]; theField[theCell] = theTurn; /* place the piece */ [self updatePlace: theCell forPlayer:theTurn]; /* check to see if the next player has a valid move available * this could be combined w/the above but would dog out the board * refresh and wouldn't be aesthetically pleasing */ if([self canPlay:opp(theTurn)]) { theTurn=opp(theTurn); theField[NUM_SPACES]=theTurn; sprintf(msg, "%s's turn.", [self playerToString:theTurn]); } else { if(![self canPlay:theTurn]) { white=count_pieces(theField, WHITE); black=count_pieces(theField, BLACK); if (black > white) { sprintf(msg, "Black wins by %d.", black - white); } else if (white > black) { sprintf(msg, "White wins by %d.", white - black); } else sprintf(msg, "Tie game."); } else sprintf(msg, "%s has no move. %s's turn.", [self playerToString:opp(theTurn)], [self playerToString:theTurn]); } } else sprintf(msg, "Invalid move. %s's turn.", [self playerToString:theTurn]); [white_number setIntValue:count_pieces(theField, WHITE)]; [black_number setIntValue:count_pieces(theField, BLACK)]; [status setStringValue:msg]; return self; } - undo:sender { [self swap]; return self; } - swap { short temp[NUM_SPACES+1]; char msg[20]; bcopy(theBckField, temp, (NUM_SPACES+1) * sizeof(short)); bcopy(theField, theBckField, (NUM_SPACES+1) * sizeof(short)); bcopy(temp, theField, (NUM_SPACES+1) * sizeof(short)); [self updateField:self]; theTurn=theField[NUM_SPACES]; sprintf(msg, "%s", [self turnString]); [status setStringValue:msg]; return self; } - backup { bcopy(theField, theBckField, NUM_SPACES * sizeof(short)); theBckField[NUM_SPACES] = theTurn; return self; } - appDidInit:sender { [self newGame:self]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.