//
//  AtomicGame.m
//  Gridlock
//
//  Created by Brian on Sun Feb 08 2004.
//  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
//

#import "AtomicGame.h"

@implementation AtomicGame

-(void)reset {
  [super reset];
  [self setGrid:[DCHypergrid gridWithRows:[[[self configurationInfo] objectForKey:@"rows"] intValue]
                                  columns:[[[self configurationInfo] objectForKey:@"cols"] intValue]]];
}

-(int)ownerOfPosition:(DCHypergridPosition *)pos {
  int value = [self valueAtPosition:pos];
  if (value==0) return 0;
  else if (value>0) return 1;
  else return 2;
}
-(int)ownerOfRow:(int)r column:(int)c {
  int value = [self valueAtRow:r column:c];
  if (value==0) return 0;
  else if (value>0) return 1;
  else return 2;
}

-(int)numberOfPositionsOwnedByPlayer:(int)pnum {
  NSEnumerator *pe = [[self grid] positionEnumerator];
  id pos;
  int total = 0;
  while ((pos=[pe nextObject])) {
    if (pnum==[self ownerOfPosition:pos]) ++total;
  }
  return total;
}

-(BOOL)isAnyPositionOwnedByPlayer:(int)pnum {
  NSEnumerator *pe = [[self grid] positionEnumerator];
  id pos;
  while ((pos=[pe nextObject])) {
    if (pnum==[self ownerOfPosition:pos]) return YES;
  }
  return NO;
}

-(int)maxStablePiecesForRow:(int)r column:(int)c {
  BOOL endrow = (r==0 || r==[self numberOfRows]-1);
  BOOL endcol = (c==0 || c==[self numberOfColumns]-1);
  if (endrow && endcol) return 1;
  if (endrow || endcol) return 2;
  return 3;
}
-(int)maxStablePiecesForPosition:(DCHypergridPosition *)pos {
  return [self maxStablePiecesForRow:[pos row] column:[pos column]];
}

-(int)cellValueDelta {
  return ([self currentPlayerNumber]==1) ? 1 : -1;
}

-(void)removePieceFromGrid:(DCHypergrid *)theGrid atRow:(int)r column:(int)c {
  [theGrid setValue:[theGrid valueAtRow:r column:c]-[self cellValueDelta] atRow:r column:c];
}

-(void)addCapturingPieceToGrid:(DCHypergrid *)theGrid atRow:(int)r column:(int)c {
  [theGrid setValue:[self cellValueDelta]*(1+abs([theGrid valueAtRow:r column:c])) atRow:r column:c];
}

// returns YES if following the explostion there are more unstable positions *and* cells are owned by both players
-(BOOL)performExplosionIterationInGrid:(DCHypergrid *)theGrid 
                         fromPositions:(NSArray *)positions 
                 nextUnstablePositions:(NSMutableArray *)nextUnstablePositions {
  // this method may be performance-sensitive, avoiding object allocations
  int i;
  int maxr = [self numberOfRows];
  int maxc = [self numberOfColumns];
  for(i=0; i<[positions count]; i++) {
    id pos = [positions objectAtIndex:i];
    int r = [pos row];
    int c = [pos column];
    // distribute pieces to orthogonal adjacent positions
    if (r>0) {
      [self removePieceFromGrid:theGrid atRow:r column:c];
      [self addCapturingPieceToGrid:theGrid atRow:r-1 column:c];
    }
    if (r<maxr-1) {
      [self removePieceFromGrid:theGrid atRow:r column:c];
      [self addCapturingPieceToGrid:theGrid atRow:r+1 column:c];
    }
    if (c>0) {
      [self removePieceFromGrid:theGrid atRow:r column:c];
      [self addCapturingPieceToGrid:theGrid atRow:r column:c-1];
    }
    if (c<maxc-1) {
      [self removePieceFromGrid:theGrid atRow:r column:c];
      [self addCapturingPieceToGrid:theGrid atRow:r column:c+1];
    }
  }
  
  // now check for new ustable positions, and determine if both players still own cells
  {
    int r, c;
    BOOL hasP1 = NO;
    BOOL hasP2 = NO;
    [nextUnstablePositions removeAllObjects];
    for(r=0; r<maxr; r++) {
      for(c=0; c<maxc; c++) {
        int value = [theGrid valueAtRow:r column:c];
        int owner = (value==0) ? 0 : ((value>0) ? 1 : 2);
        if (owner==1) hasP1 = YES;
        if (owner==2) hasP2 = YES;
        if (abs(value)>[self maxStablePiecesForRow:r column:c]) {
          [nextUnstablePositions addObject:[DCHypergridPosition positionWithRow:r column:c]];
        }
      }
    }
    return ([nextUnstablePositions count]>0 && hasP1 && hasP2);
  }
}

-(BOOL)prepareMoveSequence:(NSArray *)move {
  id pos = [move lastObject];
  [self resetFutureGrid];
  [self addCapturingPieceToGrid:[self futureGrid] atRow:[pos row] column:[pos column]];
  if (abs([self valueAtPosition:pos])>=[self maxStablePiecesForPosition:pos]) {
    // move causes an explosion, compute resulting grid
    NSMutableArray *explodingPositions = [NSMutableArray arrayWithObject:pos];
    BOOL stillGoing = YES;
    
    // repeat until explosions stop or opponent is dead
    while (stillGoing) {
      stillGoing = [self performExplosionIterationInGrid:[self futureGrid] 
                                           fromPositions:explodingPositions
                                   nextUnstablePositions:explodingPositions];
    }
  }
  return YES;
}

-(NSArray *)allValidMoveSequences {
  NSMutableArray *moves = [NSMutableArray array];
  int pnum = [self currentPlayerNumber];
  NSEnumerator *pe = [[self grid] positionEnumerator];
  id pos;
  while ((pos=[pe nextObject])) {
    int owner = [self ownerOfPosition:pos];
    if (owner==0 || owner==pnum) {
      [moves addObject:[pos arrayWithSelf_]];
    }
  }
  return moves;
}

-(BOOL)isGameOver {
  return ([self moveCount]>2 && !([self isAnyPositionOwnedByPlayer:1] && [self isAnyPositionOwnedByPlayer:2]));
}

-(int)winningPlayer {
  if ([self isAnyPositionOwnedByPlayer:1]) return 1;
  return 2;
}

@end
