/*
    bomb - automatic interactive visual stimulation
    Copyright (C) 1994  Scott Draves <spot@cs.cmu.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "defs.h"
#include "image.h"
#include "image_db.h"
#include "bomb.h"


int
random_byte()
{
    static int stage = 0;
    static unsigned int savebits;
    int r;

    if (0 == stage) {
       savebits = R;
       stage = 3;
    }
    r = savebits & 0xff;
    savebits = savebits >> 8;
    stage--;
    return r;
}

int quantize(double d) {
  int i = floor(d);
  double f = d - i;
  if (R8b < f*0xff)
    i++;
  return i;
}

void
change_rules2(int old_sym, int new_sym, board_t *dst)
{
   if (sym_tile4 == old_sym &&
       sym_one == new_sym) {
      int x, y;
      for (y = 0; y <= SMALL_YSIZE; y++)
	 for (x = 0; x <= SMALL_XSIZE; x++) {
	    int t = (*dst)[x][y];
	    (*dst)[SMALL_XSIZE+x][y] = t;
	    (*dst)[x][SMALL_YSIZE+y] = t;
	    (*dst)[SMALL_XSIZE+x][SMALL_YSIZE+y] = t;
	 }
   } else if (sym_mirror4 == old_sym &&
	      sym_one == new_sym) {
      int x, y;
      for (y = 0; y <= SMALL_YSIZE; y++)
	 for (x = 0; x <= SMALL_XSIZE; x++) {
	    int t = (*dst)[x][y];
	    (*dst)[XSIZE-x][y] = t;
	    (*dst)[x][YSIZE-y] = t;
	    (*dst)[XSIZE-x][YSIZE-y] = t;
	 }
   } else if (sym_one == old_sym &&
	      sym_tile4_stack2 == new_sym) {
      int x, y;
      int m = rule.mask;
      for (y = 0; y <= SMALL_YSIZE; y++)
	 for (x = 0; x <= SMALL_XSIZE; x++) {
	    int t = (*dst)[x][y];
	    (*dst)[x][y] = ((0xffff * t / m) << 16)
	       | (0xffff * (m - t/2) / m);
	 }
   } else if (sym_tile4_stack2 == old_sym &&
	      sym_one == new_sym) {
      int x, y;
      int m = rule.mask;
      for (y = 0; y <= SMALL_YSIZE; y++)
	 for (x = 0; x <= SMALL_XSIZE; x++) {
	    int t = (*dst)[x][y];
	    t = (t&0xffff) * m / 0xffff;
	    (*dst)[x][y] = t;
	    (*dst)[SMALL_XSIZE+x][y] = t;
	    (*dst)[x][SMALL_YSIZE+y] = t;
	    (*dst)[SMALL_XSIZE+x][SMALL_YSIZE+y] = t;
	 }
   }
}

void
pix2heat2(image8_t *fb, board_t *dst,
	  int x0, int y0, int x1, int y1)
{
   int x, y;
   int s = fb->stride;
   u_char *p = fb->p;
   for (y = y0; y < y1; y++)
      for (x = x0; x < x1; x++) {
	 int pix = p[x + y * s];
	 (*dst)[x+1][y+1] = (pix * rule.mask) >> 8;
      }
}

void
pix2heat(image8_t *fb, board_t *dst)
{
   pix2heat2(fb, dst, 0, 0, XSIZE, YSIZE);
}

static u_char ppmbuf[YSIZE][XSIZE][3];

#if use_mpeg
void
write_fb_mpeg(image8_t *fb)
{
   int x, y;

   for (y = 0; y < YSIZE; y++) {
      u_char *fbp = fb->p + fb->stride * y;
      for (x = 0; x < XSIZE; x++) {
	 int ci = ((int) fbp[x]) * 3;
	 ppmbuf[y][x][0] = current_cmap[ci];
	 ppmbuf[y][x][1] = current_cmap[ci+1];
	 ppmbuf[y][x][2] = current_cmap[ci+2];
      }
   }

   mpeg_frame((char *) ppmbuf);
}
#endif

void
write_fb_ppm(image8_t *fb)
{
   static int frame_num = 0;
   static u_char ppmbuf[YSIZE][XSIZE][3];
   int x, y;

   for (y = 0; y < YSIZE; y++) {
      u_char *fbp = fb->p + fb->stride * y;
      for (x = 0; x < XSIZE; x++) {
	 int ci = ((int) fbp[x]) * 3;
	 ppmbuf[y][x][0] = current_cmap[ci];
	 ppmbuf[y][x][1] = current_cmap[ci+1];
	 ppmbuf[y][x][2] = current_cmap[ci+2];
      }
   }

   if (1) {
      char fname[40];
      FILE *o;
    try_again:
      frame_num++;
      sprintf(fname, "dribble/%03d.ppm", frame_num);
      if (o = fopen(fname, "r")) {
	 fclose(o);
	 goto try_again;
      }
      if (NULL != (o = fopen(fname, "w"))) {
	 /* put parms in comment */
	 fprintf(o, "P6\n%d %d\n255\n", XSIZE, YSIZE);
	 fwrite(ppmbuf, 3, XSIZE * YSIZE, o);
	 fclose(o);
      } else
	 fprintf(stderr, "can't open %s\n", fname);
   }
}

/* returns true if the screen is `boring'.  should use
   much more sophisticated analysis, but ... */
int
run_hist(image8_t *fb)
{
#define nsamp 3000
#define nframes 30
   static int samples = 0;
   static int hist[nsamp];
   static int sum = 0;
   int i;

   for (i = 0; i < (nsamp/nframes); i++) {
      int x = (5 + R%(fb->width-10));
      int y = (5 + R%(fb->height-10));
      u_char p = fb->p[fb->stride * y + x];
      hist[samples++] = p;
      sum += p;
   }

   if (samples >= nsamp) {
      double avg = sum / (double) nsamp;
      double var = 0;
      for (i = 0; i < nsamp; i++) {
	 double t = avg - hist[i];
	 var += t * t;
      }
      var = var / samples;
      samples = 0;
      sum = 0;

#if 0
      fprintf(stderr, "var = %g\n", var);
#endif
      if (var < 10.0) {
	     return 1;
      }
   }
   return 0;
}

int iclamp(int p, int n)
{
   if (p < 0)
      p = -p;
   return p%n;
}

void
cool(image8_t *fb)
{
   int i, j;
   for (j = 0; j < fb->height; j++) {
      u_char *p = &fb->p[j * fb->stride];
      for (i = 0; i < fb->width; i++)
	 p[i] = p[i] * 0.9;
   }
}

void
warm(image8_t *fb)
{
   int i, j;
   for (j = 0; j < fb->height; j++) {
      u_char *p = &fb->p[j * fb->stride];
      for (i = 0; i < fb->width; i++) {
	 int t = p[i] * 1.11111;
	 p[i] = (t > 255) ? 255 : t;
      }
   }
}



void
double_board(int vh, board_t *dst, int sym)
{
   int i, j;

   if (vh) {
      switch (sym) {
       case sym_tile4:
       case sym_mirror4:
       case sym_mirror2:
	 for (j = 0; j < YSIZE/4; j++) {
	    for (i = 1; i <= XSIZE/2; i++) {
	       (*dst)[i][j] = ((*dst)[i][2*j] + (*dst)[i][2*j+1]) / 2;
	    }
	 }
	 for (j = YSIZE/2-1; j >= YSIZE/4; j--) {
	    for (i = 1; i <= XSIZE/2; i++) {
	       (*dst)[i][j] = (*dst)[i][j-YSIZE/4];
	    }
	 }
	 break;
       case sym_one:
	 for (j = 0;  j< YSIZE/2; j++) {
	    for (i = 1; i <= XSIZE; i++) {
	       (*dst)[i][j] = ((*dst)[i][2*j] + (*dst)[i][2*j+1]) / 2;
	    }
	 }
	 for (j = YSIZE-1; j >= YSIZE/2; j--) {
	    for (i = 1; i <= XSIZE; i++) {
	       (*dst)[i][j] = (*dst)[i][j-YSIZE/2];
	    }
	 }
	 break;
       case sym_frame_buffer:
	 break;
      }
   } else {
      switch (sym) {
       case sym_tile4:
       case sym_mirror4:
       case sym_mirror2:
	 for (j = 1; j <= YSIZE/2; j++) {
	    for (i = 0; i < XSIZE/4; i++) {
	       (*dst)[i][j] = ((*dst)[2*i][j] + (*dst)[2*i+1][j]) / 2;
	    }
	    for (i = XSIZE/4+1; i <= XSIZE/2; i++) {
	       (*dst)[i][j] = (*dst)[i-XSIZE/4][j];
	    }
	 }
	 break;
       case sym_frame_buffer:
	 break;
       default:
	 for (j = 1; j <= YSIZE; j++) {
	    for (i = 0; i < XSIZE/2; i++) {
	       (*dst)[i][j] = ((*dst)[2*i][j] + (*dst)[2*i+1][j]) / 2;
	    }
	    for (i = XSIZE/2+1; i <= XSIZE; i++) {
	       (*dst)[i][j] = (*dst)[i-XSIZE/2][j];
	    }
	 }
	 break;
      }
   }
}

void
fill_board(fill_t *p)
{
   int r,g,b,x,y,f;
   board_t *dst;

   grad_state = 0;

   dst = &board[dbuf];

   /* there are bugs in the boundary conditions of this code */

   f = p->fill;
   if (rule.rule == rule_rd)
      f = fill_noise;

   switch (f) {
    case fill_hramp:
      for (y = 1; y <= YSIZE; y++)
	 for (x = 1; x <= XSIZE; x++)
	    (*dst)[x][y] = (x * 255) / XSIZE;
      break;
    case fill_vramp:
      for (y = 1; y <= YSIZE; y++)
	 for (x = 1; x <= XSIZE; x++)
	    (*dst)[x][y] = (y * 255) / YSIZE;
      break;
    case fill_grid:
      for (y = 1; y <= YSIZE; y++)
	 for (x = 1; x <= XSIZE; x++)
	    (*dst)[x][y] =
	       ((x * 16) / XSIZE) |
		  (((y * 16) / YSIZE) << 4);
      break;
    case fill_noise:
      for (y = 1; y <= YSIZE; y++)
	 for (x = 1; x <= XSIZE; x++)
	    (*dst)[x][y] = R;
      break;
    case fill_vnoise:
      for (y = 1; y <= YSIZE; y++)
	 for (x = 1; x <= XSIZE/2; x++)
	    (*dst)[x][y] = (*dst)[XSIZE-x][y] = R;
      break;
    case fill_sparse:
      for (y = 0; y <= YSIZE; y++)
	 for (x = 0; x <= XSIZE; x++)
	    (*dst)[x][y] = 0;
      for (r = 0; r < 10; r++)
	 (*dst)[1+R%XSIZE][1+R%YSIZE] = R;
      break;
   }

   /* XXX */
   if (rule_rug_image == rule.rule) {
      drive_with_image(current_image);
   } else if (rule_rd == rule.rule) {
      drive_with_image(current_image);
   } else if (rule_slip == rule.rule) {
      extern image8_t fb;
      extern void step_rule_rug(int frame, rule_t *p, image8_t *fb);
      step_rule_rug(0, &rule, &fb);
   } else {
      if (high_growth_game(&rule)) {
	 int n = 1+(R%4)*(R%4);
	 for (x = 0; x <= XSIZE+1; x++)
	    for (y = 0; y <= YSIZE+1; y++)
	       board2[dbuf][x][y] = 0;
	 for (x = 0; x < n; x++)
	    board2[dbuf][1+R%(XSIZE/2)][1+R%(YSIZE/2)] = R;
      } else {
	 for (x = 1; x <= XSIZE; x++)
	    for (y = 1; y <= YSIZE; y++)
	       board2[dbuf][x][y] = R;
      }
   }
}


#ifndef use_ddraw

/* these routines are the beginning of the abstract
   eight bit frame buffer interface, so they should take an image8, one of
   which happens to be associated with the real screen, or some such */
#if win_bomb
#include <windows.h>
	extern HWND hwnd;
#endif

void image8_set_cmap(int first, int n, int *cmap) {
#if win_bomb
	int i;
	LOGPALETTE *lp = malloc(sizeof(LOGPALETTE) +
		n * sizeof(PALETTEENTRY));
	HDC  hdc;
	HPALETTE ph;

	lp->palVersion = 0x300;
	lp->palNumEntries = n;
	for (i = 0; i < n; i++) {
		PALETTEENTRY *pe = &lp->palPalEntry[i];
		pe->peRed = cmap[i*3];
		pe->peGreen = cmap[i*3+1];
		pe->peBlue = cmap[i*3+2];
//		pe->peFlags = 0;
		pe->peFlags = PC_NOCOLLAPSE;
	}

	hdc = GetDC (hwnd) ;
	
	ph = CreatePalette(lp);
	if (NULL == ph) {
		message ("CreatePalette failed") ;
	   return FALSE ;
	}
	if (NULL == SelectPalette(hdc, ph, FALSE)) {
		message("SelectPalette failed") ;
	   return FALSE ;
	}
	DeleteObject(ph);
	if (GDI_ERROR == RealizePalette(hdc)) {
		message ( "RealizePalette failed") ;
	  return FALSE ;
	}
	ReleaseDC (hwnd, hdc) ;
	free(lp);
#endif
#if vga_bomb
   int m[256 * 3];
   int i;
   for (i = 0; i < 256 * 3; i++)
      m[i] = cmap[i]>>2;
   vga_setpalvec(first, n, m);
#endif
#if (xws_bomb|ogl_bomb)
   int i;
   XColor c[256];
   for (i = 0; i < n; i++) {
      c[i].pixel = i+first;
      c[i].flags = DoRed|DoGreen|DoBlue;
      c[i].red   = cmap[(i+first)*3+0] << 8;
      c[i].green = cmap[(i+first)*3+1] << 8;
      c[i].blue  = cmap[(i+first)*3+2] << 8;
   }
   XStoreColors(disp, xws_cmap, c, n);
#endif
#if mac_bomb
	int i;
	ColorSpec ctable[256];
#if use_sioux
	return;
#endif
	for (i = 0; i < 256; i++)
	{
		ctable[i].rgb.red   = cmap[3 * i] << 8;
		ctable[i].rgb.green = cmap[3 * i + 1] << 8;
		ctable[i].rgb.blue  = cmap[3 * i + 2] << 8;
	}

	SetEntries(first, n - 1, ctable);
#endif
}

#if mac_bomb && pix_rep
#ifdef copy_bits
#include <Quickdraw.h>
#include <QDOffscreen.h>
PixMapPtr offscreenPixMapP, windowPixMapP;
Rect windowRect, offscreenRect;
#else
unsigned char *real_screen_base;
int real_screen_stride;
#endif
#endif
/* int world_x, world_y; */

void image8_flush() {
#if win_bomb
	HBITMAP bm;
	int i, j;
 	HDC hdc;
    extern image8_t fb;
	extern int frame;
	extern double fps;
    hdc = GetDC (hwnd) ;

	bm = CreateBitmap(320, 200, 1, 8, fb.p);
	DrawBitmap(hdc, bm);
	DeleteObject(bm);

	if (0) {
		RECT r;
        char buf[20];
		r.left = 80;
		r.right = 180;
		r.top = 402;
		r.bottom = 420;
	    FillRect(hdc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
		sprintf(buf, "%.3g    ", fps);
		SetTextColor(hdc, RGB(255,255,255));
		SetBkColor(hdc, RGB(0,0,0));
	    TextOut(hdc, 80, 405, buf, strlen(buf));
	}

	ReleaseDC (hwnd, hdc) ;
#endif

#if mac_bomb && pix_rep
#ifdef copy_bits
/* AJW */
  if ((*offscreenPixMapP->pmTable)->ctSeed != (*windowPixMapP->pmTable)->ctSeed)
  {
  	DebugStr("\pCopybits converting colours.");
  	(*offscreenPixMapP->pmTable)->ctSeed = (*windowPixMapP->pmTable)->ctSeed;
  }
  CopyBits((BitMapPtr)offscreenPixMapP, (BitMapPtr)windowPixMapP, &offscreenRect, &windowRect, srcCopy, nil);

#else
  int i, j;
  int s = real_screen_stride;
  int s4 = real_screen_stride/4;
  for (i = 0; i < 200; i++) {
#if 1
     unsigned long *p = (unsigned long *) (real_screen_base + 2 * i * s);
     unsigned long *q = (unsigned long *) (fb.p + i * fb.stride);
     for (j = 0; j < 320; j += 4) {
     	unsigned long a;
        unsigned long b = *q++;
        unsigned long c;
        unsigned long d;
        
        a = b & 0xFF;
        d = (a << 8) | (a);
        a = b & 0xFF00;
        d |= (a << 16) | (a << 8);
        a = b & 0xFF0000;
        c = (a >> 8) | (a >> 16);
        a = b & 0xFF000000;
        c |= (a) | (a >> 8);
        
         p[0] = c;
         p[1] = d;
         /* lose next two lines for more speed */
         p[s4] = c;
         p[s4+1] = d;
         p+=2;
       }

#else
     unsigned char *p = (real_screen_base + 2 * i * s);
     unsigned char *q = (fb.p + i * fb.stride);
     for (j = 0; j < 320; j++) {
        unsigned char b = *q++;
        
         p[0] = b;
         p[1] = b;
         p[s] = b;
         p[s+1] = b;
         p+=2;
       }
#endif

     }
#endif
#endif

#if mac_bomb && 0
if (1) {
   static int frames = -1;
   static int startTicks;
   unsigned char str[16];
   int framesSec;

   frames++;
   if ((frames & 15) == 0) {
      framesSec = (frames * 6000) / (TickCount() - startTicks);
      NumToString(framesSec, str);
      TextMode(srcCopy);
      MoveTo(30, 30);
      DrawString(str);
      startTicks = TickCount();
      frames = 0;
   }
}
#endif

#if xws_bomb
   if (1 == pix_rep)
      XPutImage(disp, win, gc, xws_image, 0, 0, 0, 0, 320, 200);
   else {
      extern image8_t fb;
      int i, j, ii, jj;

      /* copy fb.p to image_buf, replicating pixels */
      for (i = 0; i < 200; i++)
	 for (j = 0; j < 320; j++)
	    for (ii = 0; ii < pix_rep; ii++)
	       for (jj = 0; jj < pix_rep; jj++)
		  image_buf[(i*pix_rep + ii) * (320*pix_rep) +
			    (j*pix_rep + jj)] =
		     fb.p[i * fb.stride + j];
      XPutImage(disp, win, gc, xws_image, 0, 0, 0, 0,
		pix_rep*320, pix_rep*200);
   }
#endif
#if ogl_bomb
   if (1) {
      extern image8_t fb;
      int buf[200][80];
      int i, j;
      for (i = 0; i < 200; i++) {
	 int *p = (int *)(fb.p + fb.stride * (199 - i));
	 for (j = 0; j < 80; j++)
	    buf[i][j] = p[j];
      }
      glDrawPixels(320, 200, GL_COLOR_INDEX, GL_BYTE, buf);
   }
#endif
}

#endif

void hex_tile(board_t *to, double angle, int *pattern, int pat_len, int n, int m) {
   int x, y, t;
   int dx, dy;
   int y0, y1;
   double th;
   y0 = (n * 200) / m;
   y1 = ((n+1) * 200) / m;

   for  (y = y0; y < y1; y++) {
      for (x = 0; x <= 320; x++) {
	 double yy = (6.0 * y) / 200.0;
	 double xx = (6.0 * x) / 320.0 + yy * .5773502;
	 int xi = (int) xx;
	 int yi = (int) yy;
	 double xr = xx - xi;
	 double yr = yy - yi;
	 int pi = 2*xi + 2*yi + (xr > yr);
	 switch (pattern[pi%pat_len]) {
	  case 0:
	    th = 0.0;
	  doth:
	    th += angle;
	    dx = 128 * (1.0 + cos(th));
	    dy = 128 * (1.0 + sin(th));
	    if (dx < 0)   dx = 0;
	    if (dx > 255) dx = 255;
	    if (dy < 0)   dy = 0;
	    if (dy > 255) dy = 255;
	    break;
	  case 1:
	    th = M_PI / 3;
	    goto doth;
	  case 2:
	    th = 2 * M_PI / 3;
	    goto doth;
	  case 3:
	    dx = dy = 128;
	    break;
	 }
	 (*to)[x][y] = (dx << 8) | dy;
      }
   }
}

void spiral(board_t *to, double angle, int n, int m) {
   int x, y, t;
   int dx, dy;
   int y0, y1;
   y0 = 1 + (n * 200) / m;
   y1 = 1 + ((n+1) * 200) / m;

   for  (y = y0; y < y1; y++) {
      for (x = 1; x < 320; x++) {
	 double th = angle + atan2(y - 100, x - 160);
	 dx = 128 * (1.0 + cos(th));
	 dy = 128 * (1.0 + sin(th));
	 if (dx < 0)   dx = 0;
	 if (dx > 255) dx = 255;
	 if (dy < 0)   dy = 0;
	 if (dy > 255) dy = 255;
	 (*to)[x][y] = (dx << 8) | dy;
      }
   }
}

#define filter_gap 3

void image2grad(board_t *from, board_t *to, int s, int n, int m) {
   int x, y, t;
   int dx, dy;
   int y0, y1;
   int d;
   y0 = 1 + (n * 200) / m;
   y1 = 1 + ((n+1) * 200) / m;

   s = s / filter_gap;
   d = s * s;
   s =  s * filter_gap;

   for  (y = y0; y < y1; y++) {
      for (x = 1; x < 320; x++) {
	 int i, j, dx, dy;
	 int p00, p01, p10, p11;
	 p00 = p01 = p10 = p11 = 0;
	 if (x > s && x < (320-s) &&
	     y > s && y < (200-s)) {
	    for (i = 0; i < s; i+=filter_gap)
	       for (j = 0; j < s; j+=filter_gap) {
		  p00 += (*from)[x+i][y+j];
		  p01 += (*from)[x+i][y-j];
		  p10 += (*from)[x-i][y+j];
		  p11 += (*from)[x-i][y-j];
	       }
	 } else {
	    for (i = 0; i < s; i+=filter_gap)
	       for (j = 0; j < s; j+=filter_gap) {
		  p00 += (*from)[(x+i)%320][(y+j)%200];
		  p01 += (*from)[(x+i)%320][(y-j+200)%200];
		  p10 += (*from)[(x-i+320)%320][(y+j)%200];
		  p11 += (*from)[(x-i+320)%320][(y-j+200)%200];
	       }
	 }
	 dx = (p00 - p10) + (p01 - p11);
	 dy = (p00 - p01) + (p10 - p11);
	 dx = (dx / d + 128);
	 dy = (dy / d + 128);

	 if (dx < 0)   dx = 0;
	 if (dx > 255) dx = 255;
	 if (dy < 0)   dy = 0;
	 if (dy > 255) dy = 255;
	 (*to)[x][y] = (dx << 8) | dy;
      }
   }   
}


#if 0
void image2grad(board_t *from, board_t *to, int s) {
   int x, y, t;
   int mx = 320 - s;
   int my = 200 - s;
   for  (y = 1; y <= my; y++) {
      for (x = 1; x <= mx; x++) {
	 int p00 = (*from)[x  ][y  ];
	 int p01 = (*from)[x  ][y+s];
	 int p10 = (*from)[x+s][y  ];
	 int p11 = (*from)[x+s][y+s];
	 int dx = (p00 - p10) + (p01 - p11);
	 int dy = (p00 - p01) + (p10 - p11);

	 dx = (dx + 512) / 4;
	 dy = (dy + 512) / 4;

	 if (dx < 0)   dx = 0;
	 if (dx > 255) dx = 255;
	 if (dy < 0)   dy = 0;
	 if (dy > 255) dy = 255;

	 (*to)[x][y] = (dx << 8) | dy;
      }
   }
}
#endif

void blur(board_t *from, board_t *to) {
   int x, y;

   fprintf(stderr, "blur ");

   for  (y = 1; y <= 200; y++) {
      for (x = 1; x <= 320; x++) {
	 (*to)[x][y] = (((*from)[x  ][y-1])+
			((*from)[x-1][y  ])+
			((*from)[x  ][y  ])+
			((*from)[x+1][y  ])+
			((*from)[x  ][y+1])) / 5;
      }
   }
}


void
pen(board_t *dboard, int mask, int x, int y, int bsize)
{
   int i, j;
   if (x >= 1 && y >= 1 && x <= XSIZE-bsize && y <= YSIZE-bsize) {
      switch (bsize) {
       case 1:
	 (*dboard)[x][y] = mask;
	 break;
       case 2:
	 for (i = 0; i < 2; i++)
	    for (j = 0; j < 2; j++)
	       (*dboard)[x+i][y+j] = mask;
	 break;
       case 3:
	 for (i = 0; i < 3; i++)
	    for (j = 0; j < 3; j++)
	       (*dboard)[x+i][y+j] = mask;
	 break;
       case 4:
	 for (i = 0; i < 4; i++)
	    for (j = 0; j < 4; j++)
	       (*dboard)[x+i][y+j] = mask;
	 break;
       default:
	 for (i = 0; i < bsize; i++)
	    for (j = 0; j < bsize; j++)
	       (*dboard)[x+i][y+j] = mask;
	 break;
      }
   }
}
