/*
 * Atom-4 triboard
 * Implementation file
 *
 * $Id: board4.cc,v 1.3 2003/02/18 22:23:14 hsteoh Exp hsteoh $
 */

#include "board4.h"


board4 &board4::operator= (board4 &b) {
  this->triboard::operator= (b);
  return *this;
}

board4::~board4() {}

int board4::check_legal(int x, int y) {
  int prev_occupied=0;			// 1 if cell has any players' marble;
					// 0 otherwise
  int cur_occupied=0;
  color4 cur_ngbr;
  int i;

  // Check that (x,y) is currently unoccupied
  if (getcell(x,y) != EMPTY_CELL)
    return 0;				// cannot put a marble here

  // Check neighbours of (x,y). Here, we loop 7 times and take i%6 so that
  // we will automagically compare dir=0 with dir=5 with no additional code
  // :-)
  for (i=0; i<7; i++) {
    cur_ngbr = ngbr_of(x,y,i%6);
    cur_occupied = (cur_ngbr.colortype() != -1);
    if (cur_occupied && prev_occupied) {
      return 1;				// found adjacent pair
    }
    prev_occupied = cur_occupied;
  }
  return 0;				// no neighbouring pairs to attach to
}

void board4::compute_splash(int x, int y, color4 actor,
                            elist<boardchange> &delta) {
  color4 propagator = actor.propagator();
  int i;

  delta.clear();
  if (propagator==BAD_CELL) return;	// no propagator found; nothing to do

  delta.append(boardchange(x,y,actor));
  for (i=0; i<6; i++) {
    int ox=x, oy=y, nx, ny;
    color4 affected;

    // Propagate effect over any propagators in this direction
    while (ngbr_coor(ox,oy,i,nx,ny) && getcell(nx,ny)==propagator) {
      ox=nx, oy=ny;
    }

    affected = getcell(nx,ny);
    if (affected.colortype() != -1) {
      color4 result = actor.mix(affected);
      if (result != affected)		// only add delta if actually changed
        delta.append(boardchange(nx, ny, result));
    }
  } // endfor
}

void board4::applychanges(elist<boardchange> &delta, elist<boardchange> *undo)
{
  elistiter<boardchange> it;

  if (undo) undo->clear();
  for (it=delta.headp(); it; it++) {
    int x=(*it).x, y=(*it).y;

    if (undo) undo->append(boardchange(x,y, getcell(x,y)));
    setcell(x,y, (*it).cell);
  }
}

int board4::count_marbles(color4 marble, int x, int y, int dir, int max) {
  int count=0;

  while (ngbr_coor(x,y,dir,x,y)) {
    if (getcell(x,y)==marble) {
      count++;
      if (count>=max) return max;
    } else break;			// stop at first non-matching marble
  }
  return count;
}

int board4::check_row(color4 piece, int x, int y, int length) {
  int nx,ny;
  int i;

  if (getcell(x,y)!=piece) return 0;

  // Check if there is a sufficiently long row of the same color in any of
  // the 3 pairs of directions
  for (i=0; i<3; i++) {
    int ri=(i+3)%6;			// reverse direction of i

    // Scan forwards and backwards for consecutive matching marbles.
    // The +1 counts the current position (x,y), which is not counted by
    // count_marbles().
    if (count_marbles(piece,x,y,i,length-1) +
        count_marbles(piece,x,y,ri,length-1) + 1 >= length) {
      return 1;				// found row of at least (length)
    }
  }
  return 0;				// no row found
}

int board4::check_win(elist<boardchange> &lastchanges, color4 piece, int len) {
  elistiter<boardchange> it;

  for (it=lastchanges.headp(); it; it++) {
    if (check_row(piece, (*it).x, (*it).y, len))
      return 1;
  }
  return 0;
}

