/*
 * Generic triangular board representation
 * Implementation file
 * ---------------------------------------------------------------------------
 * $Id: triboard.cc,v 1.6 2003/03/17 16:48:23 hsteoh Exp hsteoh $
 */

#include <assert.h>
#include "triboard.h"


void triboard::copy(triboard &b) {
  wd=b.wd, ht=b.ht;
  ox=b.ox, oy=b.oy;

  board = new celltype[wd*ht];
  for (int y=0; y<ht; y++)
    for (int x=0; x<wd; x++)
      board[y*wd + x] = b.getcell(x,y);

  cells_left = b.cells_left;
}

triboard::triboard(unsigned int width, unsigned int height) {
  int i,j;

  ox=oy=0;				// for now

  assert(width>0 && height>0);
  wd = width;
  ht = height;

  // Allocate board
  board = new celltype[wd*ht];
  assert(board);
  clear();				// start with empty board
}

triboard &triboard::operator= (triboard &b) {
  delete [] board;
  copy(b);
  return *this;
}

triboard::~triboard() {
  delete [] board;
}

celltype triboard::getcell(int x, int y) {
  x += ox;				// convert to absolute board coors
  y += oy;

  if (x<0 || x>=wd || y<0 || y>=ht) {
    return BAD_CELL;			// no such cell
  } else {
    return board[y*wd + x];
  }
}

void triboard::setcell(int x, int y, celltype cell) {
  x += ox;
  y += oy;

  if (x>=0 && x<wd && y>=0 && y<ht) {
    celltype oldcell = board[y*wd + x];

    board[y*wd + x] = cell;
    if (oldcell==EMPTY_CELL && cell!=EMPTY_CELL)
      cells_left--;
    else if (oldcell!=EMPTY_CELL && cell==EMPTY_CELL)
      cells_left++;
  } // else { Probably want to throw exception }
}

celltype triboard::ngbr_of(int x, int y, int dir) {
  int nx, ny;

  if (!ngbr_coor(x,y,dir,nx,ny))	// calculate neighbour's coordinates
    return BAD_CELL;			// bad cell specified
  else
    return board[ny*wd + nx];
}

// Calculate neighbour's coordinates given (x,y) and a direction.
// Returns true if OK, false if bad parameters
int triboard::ngbr_coor(int x, int y, int dir, int &nx, int &ny) {
  x += ox;
  y += oy;
  if (x<0 || x>=wd || y<0 || y>=ht || dir<0 || dir>=6)
    return 0;

  switch(dir) {
  case 0:	nx = x-1 + (y%2);	ny = y-1;	break;
  case 1:	nx = x + (y%2);		ny = y-1;	break;
  case 2:	nx = x+1;		ny = y;		break;
  case 3:	nx = x + (y%2);		ny = y+1;	break;
  case 4:	nx = x-1 + (y%2);	ny = y+1;	break;
  case 5:	nx = x-1;		ny = y;		break;
  default:	return 0;
  }

  // Make sure resulting coors are actually legal
  return (nx>=0 && nx<wd && ny>=0 && ny<ht);
}

void triboard::mapcell(void (*f)(int x, int y, celltype cell, void *context),
                       void *context) {
  unsigned int i, j;

  for (j=0; j<ht; j++) {
    for (i=0; i<wd; i++) {
      f(i, j, board[j*wd + i], context);
    }
  }
}

void triboard::clear() {
  int i,j;

  for (j=0; j<ht; j++) {
    for (i=0; i<wd; i++) {
      board[j*wd + i] = EMPTY_CELL;
    }
  }
  cells_left=wd*ht;
}

