/* Gridlock Copyright (c) 2002-2003 by Brian Nenninger. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import #import "DCHypergrid.h" #import "AccessorMacros.h" #import "CocoaAdditions.h" @interface Game : NSObject { DCHypergrid *grid; DCHypergrid *futureGrid; int playerNumber; NSDictionary *configurationInfo; int moveCount; int _nrows; int _ncols; } /** Returns true if the given position exists in the game. Subclasses should not need to override. */ -(BOOL)isPositionValid:(DCHypergridPosition *)pos; -(BOOL)isValidRow:(int)row column:(int)col; /** Returns a deep copy of the game, duplicating all state. The default implementation creates a new object of the receiver's class and copies the grid and playerNumber ivars. Subclasses should override if they need to copy additional state, in which case they should also call the superclass implementation. The returned object has a retain count of 1 and should be released by the caller when needed. */ -(Game *)copy; /** Makes the given game a copy of the receiver, by copying all necessary information. The given game can be assumed to have a grid with the same dimensions as the receiver. The default implementation copies the grid values and ivars defined in this class. Superclasses should override to copy additional state if needed. */ -(void)copyValuesToGame:(Game *)newGame; /** Resets the game to its beginning state. Subclasses which override must call the superclass implementation. */ -(void)reset; /** Creates a grid with dimensions and initial positions from the "rows", "cols", and "positions" keys in the game's configurationInfo dictionary. Normally called by subclasses in their -reset methods. */ -(void)createGridFromConfiguration; /** Returns the number of rows in the game board. Default implementation returns [[self grid] numberOfRows]. Subclasses should not override unless they don't use the grid ivar. */ -(int)numberOfRows; /** Returns the number of columns in the game board. Default implementation returns [[self grid] numberOfColumns]. Subclasses should not override unless they don't use the grid ivar. */ -(int)numberOfColumns; /** Returns the DCHypergrid object that the game uses. Default implementation returns the grid ivar; subclasses should not normally need to override. */ idAccessor_h(grid, setGrid) /** Returns the number of moves made in the game. Subclasses should not need to override. */ -(int)moveCount; // accessors for configuration dictionary. The dictionary must not be modified after it is assigned. -(NSDictionary *)configurationInfo; -(void)setConfigurationInfo:(NSDictionary *)value; /** Returns YES if the given array contains positions representing the beginning of a legal move. It should return NO if the array represents a complete move to which no more positions can be added (in which case -isCompleteMoveSequenceValid: would return YES). Should return NO if the the argument is empty or nil. The default implementation examines the moves returned by -allValidMoveSequences. Subclasses *must* override if they implement -allValidMoveSequences in terms of -inferredValidMoveSequences to avoid an infinite loop. */ -(BOOL)isPartialMoveSequenceValid:(NSArray *)positions; /** Returns YES if the given array contains positions represents a complete legal move. It is possible for both -isPartialMoveSequenceValid: and -isCompleteMoveSequenceValid: to return YES for the same array; for example if (A,B) and (A,B,C) are legal moves then both methods should return YES when given an argument of (A,B). If the argument is empty or nil, should return true if a pass is legal. The default implementation examines the moves returned by -allValidMoveSequences. Subclasses *must* override if they implement -allValidMoveSequences in terms of -inferredValidMoveSequences to avoid an infinite loop. */ -(BOOL)isCompleteMoveSequenceValid:(NSArray *)positions; /** Returns YES if a pass is a valid move. Implemented by calling -isCompleteMoveSequenceValid: with a nil argument. Subclasses should not need to override. */ -(BOOL)isPassValid; /** Must be implemented by subclasses. Called when the given move is being considered or about to be made. This method should update internal state so that -positionsOfAllChangingCells and -futureValueAtPosition: return correct values for the given move. Normally a subclass will do this by setting values in the futureGrid ivar (after first calling -resetFutureGrid). If a subclass doesn't use futureGrid, it must override -positionsOfAllChangingCells and -futureValueAtPosition: . The move must be valid; if it is not the results are undefined. */ -(BOOL)prepareMoveSequence:(NSArray *)positions; /** Provides access to the grid which holds the values which the game would have if the move specified in -prepareMoveSequence: were to be executed. Subclasses should update this grid in their implementations of -prepareMoveSequence: . */ -(DCHypergrid *)futureGrid; -(void)setFutureGrid:(DCHypergrid *)value; /** Creates futureGrid if it hasn't been initialized, setting it to a grid equal to the grid ivar. Subclasses will normally call this method at the beginning of -prepareMoveSequence: */ -(void)resetFutureGrid; /** Updates the state of the game by executing the move given in the last call to -prepareMoveSequence: The default implementation copies the values stored by the futureGrid ivar if it is non-nil. If futureGrid is nil, it calls -positionsOfAllChangingCells to get the positions to be updated, and calls -futureValueAtPosition: for each of those positions. Subclasses do not need to override. */ -(void)updateFromPreparedMove; /** Returns all cells which would change state if the move previously specified by a call to -prepareMoveSequence: were to be executed. The default implemenation determines the changed positions by inspecting the futureGrid ivar. Subclasses should override if they don't use futureGrid. */ -(NSArray *)positionsOfAllChangingCells; /** Called when a pass is being considered or about to be made. Implemented by calling -prepareMoveSequence: with a nil argument. Subclasses should not need to override. */ -(BOOL)preparePass; /** Executes the move, updates the present state of the game, and returns YES. The default implementation calls -prepareMoveSequence:, uses -futureValueAtPosition: to update the game state, and calls -moveFinised. Subclasses do not need to override, but can for optimization. The move must be valid; if it is not the results are undefined. */ -(BOOL)makeMoveSequence:(NSArray *)positions; /** Called to execute a pass. Implemented by calling -makeMoveSequence: with a nil argument. Subclasses should not need to override. */ -(BOOL)pass; /** Called when a move has just been made. The default implementation increments the move count and player number, subclasses which override should call the superclass implementation. */ -(void)didMakeMove:(NSArray *)positions; /** Returns the value of the cell at the given position. Default implementation forwards the call to the grid ivar. */ -(int)valueAtPosition:(DCHypergridPosition *)pos; -(int)valueAtRow:(int)r column:(int)c; /** Sets the value of the cell at the given position. Default implementation forwards the call to the grid ivar. */ -(void)setValue:(int)value atPosition:(DCHypergridPosition *)pos; -(void)setValue:(int)value atRow:(int)r column:(int)c; /** Helper method to set the values of multiple cells listed in the given array. The elements of the array must be arrays of 3 objects, all of which respond to -intValue, giving the row, column, and value in that order. Generally this method is used when setting a starting position read from a configuration file (see Games.plist). */ -(void)setGridValuesFromArray:(NSArray *)array; /** Helper method to initialize a game by setting the values of cells in the "first" rows (row 0 and up for player 1, highest numbered row and down for player 2). */ -(void)fillFirstRows:(int)numRows; /** Returns the value that the cell at the given position would have if the move previously specified by a call to -prepareMoveSequence: were to be executed. The default implementation looks in the futureGrid ivar for the future value. Subclasses should override if they don't use futureGrid. */ -(int)futureValueAtPosition:(DCHypergridPosition *)pos; /** Returns the number of players in the game. The default implementation returns 2. */ -(int)numberOfPlayers; /** Must be implemented by subclasses. Returns the number (1-based) of the player whose turn it is. */ -(int)currentPlayerNumber; -(void)setCurrentPlayerNumber:(int)value; /** Returns the player number (1-based) who will move after the given player number. The default implementation returns 1+([self currentPlayerNumber]%[self numberOfPlayers]). */ -(int)playerNumberMovingAfterPlayer:(int)pnum; /** Returns the number (1-based) of the player whose turn it will next be. The default implementation returns [self playerNumberMovingAfterPlayer:[self currentPlayerNumber]]. */ -(int)nextPlayerNumber; /** Sets the player number to the next player. Subclasses should not need to override. */ -(void)incrementPlayerNumber; /** Returns YES if player scores should be shown. The default implentation returns NO. */ -(BOOL)showScores; /** Returns a game-specific score for the given player. The default implementation returns the number of cells with value equal to the player number. */ -(int)scoreForPlayer:(int)pnum; /** Returns the number of the player who has won, or 0 if there is no winner. Should only be called if the game is over (-isGameOver returns YES). The default implementation returns the player with the highest score as determined by -scoreForPlayer:. */ -(int)winningPlayer; /** Returns an array of positions which contain the "winning" pieces. The default implementation returns nil, subclasses should override if applicable (e.g. Connect Four and Gomoku). */ -(NSArray *)positionsOfWinningPieces; /** Must be implemented by subclasses. Returns YES if the game is over, NO if not. */ -(BOOL)isGameOver; /** Must be implemented by subclasses. Returns an array containing all legal move sequences for the current player. Subclasses may implement this method by calling -inferredValidMoveSequences (see below), but if they do, they *must* override -isPartialMoveSequenceValid: and -isCompleteMoveSequenceValid:, since the default implementations of those methods call -allValidMoveSequences, and -inferredValidMoveSequences calls them. */ -(NSArray *)allValidMoveSequences; /** Returns an array containing all legal move sequences for the current player. Used by AI classes. Uses -isPartialMoveSequenceValid: and -isCompleteMoveSequenceValid: to build all possible sequences of valid moves. This method should only ever be called by subclasses as a means of implementing allValidMoveSequences. If this is done, the subclass *must* override -isPartialMoveSequenceValid: and -isCompleteMoveSequenceValid: to avoid infinite recursion. */ -(NSArray *)inferredValidMoveSequences; /** Returns a human-readable description of the given move sequence. The default implementation labels columns as letters starting with 'a', rows as numbers starting with 1, and returns a string like "c4-d5". */ -(NSString *)descriptionForMove:(NSArray *)move; -(DCHypergridPosition *)randomPosition; -(void)fillRandomEmptyCellsWithValue:(int)value count:(int)count; -(id)propertyList; -(void)updateFromPropertyList:(id)plist; @end