/*
# GENERIC X-BASED WELLTRIS
#
#	utils.c
#
###
#
#  Copyright (c) 1993 - 2001		David A. Bagley, bagleyd@tux.org
#
#  Taken from GENERIC X-BASED TETRIS
#
#  Copyright (c) 1992 - 1995		Q. Alex Zhao, azhao@cc.gatech.edu
#
#			All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appear in all copies and
#  that both that copyright notice and this permission notice appear in
#  supporting documentation, and that the name of the author not be
#  used in advertising or publicity pertaining to distribution of the
#  software without specific, written prior permission.
#
#  This program is distributed in the hope that it will be "playable",
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#  create_images() Copyright (c) 2000 by Stephen Montgomery-Smith
*/

#include	"welltris.h"
#include	"polyomino.h"

#include	"wicon.xbm"

#define BITMAPS 256

#if 0
/* Working on making trapazoids with a 3D look */
#define TRAPCONST
#endif

Atom            delw;

static GC       revGC, bigGC, tinyGC, xorGC;
static GC       thingGCs[MAX_START_POLYOMINOES];

static char    *winName = "GENERIC WELLTRIS";
static char    *iconName = "WELLTRIS";

static int      titleLen, titleWidth, authorLen, authorWidth;
static int      titleX, titleY, authorX, authorY;
static int      sX;
static int      sLevelY, sRowsY, sScoreY;

static int      topRWidth, topWidth, topHeight, topMidX, topMidY;
static int      frameX, frameY, frameW, frameH;

static XImage *images[2][BITMAPS];

typedef struct {
    int             squares, polyomino_number;
    int             xpos, ypos;
    int             size, color_number;
    int             bonus, random_rotation, random_reflection;
    long            random_number;
}               thing_t;
typedef struct {
    int             rotation;
    int             reflection;
    int             start_height;
    int             shape[MAX_SQUARES][MAX_SQUARES];
    int             size;
}               Polyominoes;
typedef struct {
    int             number[MAX_MODES][MAX_TYPES];
    int             start[MAX_START_POLYOMINOES][MAX_TYPES];
    int             turn_style;
}               Mode;
typedef struct {
    Polyominoes     polyomino[MAX_POLYOMINOES];
    Mode            mode[MAX_MODES];
    int             diagonal_switch;
}               Polytris;
static Polytris tris[MAX_SQUARES - MIN_SQUARES + 1];
static int frozen_wall[MAX_SIDES];

/*
static XPoint big_trapazoid[MAX_SIDES][MAX_SIDES] =
  {{4 * DELTA, 2 * DELTA},
    {ENDPT - 8 * DELTA, 0},
    {4 * DELTA - BASE_WIDTH, BASE_WIDTH - 4 * DELTA},
    {-BASE_WIDTH, 0},
   {ENDPT - 2 * DELTA, 3 * DELTA},
    {0, ENDPT - 7 * DELTA},
    {4 * DELTA - BASE_WIDTH, 4 * DELTA - BASE_WIDTH},
    {0, -BASE_WIDTH - DELTA},
   {ENDPT - 3 * DELTA, ENDPT - 2 * DELTA},
    {6 * DELTA - ENDPT, 0},
    {BASE_WIDTH - 4 * DELTA, 4 * DELTA - BASE_WIDTH},
    {BASE_WIDTH + 2 * DELTA, 0},
   {2 * DELTA, ENDPT - 4 * DELTA},
    {0, 7 * DELTA - ENDPT},
    {BASE_WIDTH - 4 * DELTA, BASE_WIDTH - 4 * DELTA},
    {0, BASE_WIDTH + DELTA}};
*/

static char    *thingFGs[MAX_START_POLYOMINOES] = {
    "Red", "Green", "Blue", "Yellow", 
    "Magenta", "Cyan", "FireBrick", "GreenYellow",
    "SlateBlue", "Khaki", "Plum", "Violet",
    "DarkTurquoise", "Gold", "Orchid", "Turquoise",
    "Orange", "OrangeRed", "VioletRed", "BlueViolet",
    "SeaGreen", "Pink", "ForestGreen", "SkyBlue",
    "Coral", "Wheat", "Goldenrod", "IndianRed",
    "SpringGreen", "CornflowerBlue", "Thistle", "Aquamarine",
    "CadetBlue", "LightSteelBlue", "NavyBlue", "SteelBlue",
    "YellowGreen", "DarkViolet", "MediumSeaGreen", "DarkSlateGray",
    "LightGray", "MediumVioletRed", "Sienna", "MediumAquamarine",
    "MediumBlue", "Navy", "DarkOliveGreen", "DarkGreen",
    "DimGray", "Tan", "MediumTurquoise", "DarkSlateBlue",
    "Maroon", "Gray", "Black"
};

static unsigned char gray1_bits[] = {0x01, 0x02};
static Pixmap graypix;

static thing_t  curThing, nextThing;
typedef struct {
    int             pmid, cid;
} Field;

static Field    wall[MAX_DEPTH+MAX_WIDTH][MAX_PERIMETER],
                base[MAX_WIDTH][MAX_WIDTH];

#define ARR(i,j,t) (((i)<0||(i)>=t.size||\
(j)<0||(j)>=t.size)?-2:\
(tris[t.squares].polyomino[t.polyomino_number].shape[j][i]>0))

#define ROUND8(n) ((((n)+7)/8)*8)

/* Defines to index the bitmaps.  A set bit indicates that an edge or
   corner is required. */
#define LEFT 1 /* (1<<0) */
#define DOWN 2 /* (1<<1) */
#define RIGHT 4 /* (1<<2) */
#define UP 8 /* (1<<3) */
#define LEFT_DOWN 16 /* (1<<4) */
#define RIGHT_DOWN 32 /* (1<<5) */
#define RIGHT_UP 64 /* (1<<6) */
#define LEFT_UP 128 /* (1<<7) */
#define IS_LEFT(n) ((n)&LEFT)
#define IS_DOWN(n) ((n)&DOWN)
#define IS_RIGHT(n) ((n)&RIGHT)
#define IS_UP(n) ((n)&UP)
#define IS_LEFT_DOWN(n) ((n)&LEFT_DOWN)
#define IS_RIGHT_DOWN(n) ((n)&RIGHT_DOWN)
#define IS_RIGHT_UP(n) ((n)&RIGHT_UP)
#define IS_LEFT_UP(n) ((n)&LEFT_UP)

#define CHECKLEFT(x) ((x)|LEFT)
#define CHECKDOWN(x) ((x)|DOWN)
#define CHECKRIGHT(x) ((x)|RIGHT)
#define CHECKUP(x) ((x)|UP)

/* Defines to access the bitmaps. */
#define BITNO(x,y) ((x)+(y)*ROUND8(boxsize))
#define SETBIT(x,y) {data[BITNO(x,y)/8] |= 1<<(BITNO(x,y)%8);}
#define RESBIT(x,y) {data[BITNO(x,y)/8] &= ~(1<<(BITNO(x,y)%8));}
#define TWOTHIRDSBIT(x,y) {if ((x+y+2)%3) SETBIT(x,y) else RESBIT(x,y)}
#define HALFBIT(x,y) {if ((x+y)%2) SETBIT(x,y) else RESBIT(x,y)}
#define THIRDBIT(x,y) {if (!((x+y+2)%3)) SETBIT(x,y) else RESBIT(x,y)}
#define THREEQUARTERSBIT(x,y) \
  {if ((y%2)||((x+2+y/2+1)%2)) SETBIT(x,y) else RESBIT(x,y)}
#define NOTHALFBIT(x,y) {if ((x+y)%2) RESBIT(x,y) else SETBIT(x,y)}

/* Parameters for bitmaps. */
#define G ((boxsize/45)+1)         /* 1/2 of gap between pentominoes. */
#define T ((boxsize<=12)?1:(G*2))  /* Thickness of walls of pentominoes. */
#define R ((boxsize<=12)?1:(G*6))  /* Amount of rounding. */
#define RT ((boxsize<=12)?1:(G*3)) /* Thickness of wall of rounded parts.
                                  Here 3 is an approximation to 2 sqrt(2).  */
#define RR 0   /* Roof ridge thickness */

/* A list of those bitmaps we need to create to display any polyomino. */
static int images_needed[] =
{
/* This needed for mononimo*/
 LEFT|RIGHT|UP|DOWN|LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,

 LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,

 LEFT|RIGHT_UP|RIGHT_DOWN,
 RIGHT|LEFT_UP|LEFT_DOWN,
 UP|LEFT_DOWN|RIGHT_DOWN,
 DOWN|LEFT_UP|RIGHT_UP,
 LEFT|RIGHT_UP,
 RIGHT|LEFT_UP,
 UP|LEFT_DOWN,
 DOWN|LEFT_UP,
 LEFT|RIGHT_DOWN,
 RIGHT|LEFT_DOWN,
 UP|RIGHT_DOWN,
 DOWN|RIGHT_UP,


/* These needed for hexonimoes*/
 LEFT,
 RIGHT,
 UP,
 DOWN,
 LEFT_DOWN|RIGHT_UP|RIGHT_DOWN,
 LEFT_UP|RIGHT_UP|RIGHT_DOWN,
 LEFT_UP|LEFT_DOWN|RIGHT_DOWN,
 LEFT_UP|LEFT_DOWN|RIGHT_UP,
 

 LEFT|UP|RIGHT_DOWN,
 LEFT|DOWN|RIGHT_UP,
 RIGHT|UP|LEFT_DOWN,
 RIGHT|DOWN|LEFT_UP,
 LEFT|UP,
 LEFT|DOWN,
 RIGHT|UP,
 RIGHT|DOWN,

 LEFT|RIGHT,
 UP|DOWN,

 RIGHT|UP|DOWN,
 LEFT|UP|DOWN,
 LEFT|RIGHT|DOWN,
 LEFT|RIGHT|UP,

 -1
};

static Bool     atBaseFully();
static Bool     atBasePartially();
static Bool     wallChange();
static void     wall_to_base();
static void     dropWall();
static void     freezeWall();
static void     drawTrapazoid();
static void     drawPlainSquare();
static void     drawSquare();
static void     XFillTrapazoid();
static int      readInt();
static void     readPolyominoes();

/* ------------------------------------------------------------------ */

static unsigned long
getColor(name)
    char            name[];
{
    XColor          tmp;

    if (!useColor)
	return fg;
    if (XParseColor(display, colormap, name, &tmp) == 0) {
	(void) fprintf(stderr, "Welltris: invalid color '%s'.\n", name);
	return fg;
    }
    if (XAllocColor(display, colormap, &tmp) == 0) {
	(void) fprintf(stderr, "Welltris: can't allocate color '%s'.\n", name);
	return fg;
    }
    return tmp.pixel;
}

/* ------------------------------------------------------------------ */

static Bool
image_needed(n)
    int n;
{
    int i;

    for (i = 0; images_needed[i] != -1; i++)
	if (n == images_needed[i])
	    return True;
    return False;
}

/* ------------------------------------------------------------------ */

static Bool
create_an_image(n, use3D, next)
    int n;
    Bool use3D;
    int next;
{
    unsigned int x, y, boxsize;
    unsigned char * data;

  
    boxsize = (next) ? NEXTBOXSIZE : BOXSIZE;

    if ((data = malloc(sizeof(char)*(boxsize*ROUND8(boxsize)/8))) == NULL) {
	return False;  /* no biggie, will not use images */
    }
    for (y=0;y<boxsize;y++) for (x=0;x<boxsize;x++) {
	if (!use3D) {
#ifdef SMALL_BELLYBUTTON
	    if (x >= boxsize / 2 && x <= boxsize / 2 + 1 &&
		    y >= boxsize / 2 && y <= boxsize / 2 + 1)
		NOTHALFBIT(x,y)
	    else
#endif
	    HALFBIT(x,y)
	} else if ((x>=y && x<=boxsize-y-1 && IS_UP(n))
		|| (x<=y && x<=boxsize-y-1 && y<boxsize/2 && !IS_LEFT(n))
		|| (x>=y && x>=boxsize-y-1 && y<boxsize/2 && !IS_RIGHT(n)))
	    SETBIT(x,y)
	else if ((x<=y && x<=boxsize-y-1 && IS_LEFT(n))
		|| (x>=y && x<=boxsize-y-1 && x<boxsize/2 && !IS_UP(n))
		|| (x<=y && x>=boxsize-y-1 && x<boxsize/2 && !IS_DOWN(n)))
	    TWOTHIRDSBIT(x,y)
	else if ((x>=y && x>=boxsize-y-1 && IS_RIGHT(n))
		|| (x>=y && x<=boxsize-y-1 && x>=boxsize/2 && !IS_UP(n))
		|| (x<=y && x>=boxsize-y-1 && x>=boxsize/2 && !IS_DOWN(n)))
	    HALFBIT(x,y)
	else if ((x<=y && x>=boxsize-y-1 && IS_DOWN(n))
		|| (x<=y && x<=boxsize-y-1 && y>=boxsize/2 && !IS_LEFT(n))
		|| (x>=y && x>=boxsize-y-1 && y>=boxsize/2 && !IS_RIGHT(n)))
	    THIRDBIT(x,y)
    }

    if (IS_LEFT(n))
        for (y=0;y<boxsize;y++) for (x=G;x<G+T;x++)
          SETBIT(x,y)
    if (IS_RIGHT(n))
        for (y=0;y<boxsize;y++) for (x=G;x<G+T;x++)
          SETBIT(boxsize-1-x,y)
    if (IS_UP(n))
        for (x=0;x<boxsize;x++) for (y=G;y<G+T;y++)
          SETBIT(x,y)
    if (IS_DOWN(n))
        for (x=0;x<boxsize;x++) for (y=G;y<G+T;y++)
          SETBIT(x,boxsize-1-y)
    if (IS_LEFT(n))
        for (y=0;y<boxsize;y++) for (x=0;x<G;x++)
          RESBIT(x,y)
    if (IS_RIGHT(n))
        for (y=0;y<boxsize;y++) for (x=0;x<G;x++)
          RESBIT(boxsize-1-x,y)
    if (IS_UP(n))
        for (x=0;x<boxsize;x++) for (y=0;y<G;y++)
          RESBIT(x,y)
    if (IS_DOWN(n))
        for (x=0;x<boxsize;x++) for (y=0;y<G;y++)
          RESBIT(x,boxsize-1-y)

    if (IS_LEFT(n) && IS_UP(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(x,y)
            else
              RESBIT(x,y)
          }
    if (IS_LEFT(n) && IS_DOWN(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(x,boxsize-1-y)
            else
              RESBIT(x,boxsize-1-y)
          }
    if (IS_RIGHT(n) && IS_UP(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(boxsize-1-x,y)
            else
              RESBIT(boxsize-1-x,y)
          }
    if (IS_RIGHT(n) && IS_DOWN(n))
        for (x=G;x<=G+R;x++)
          for (y=G;y<=R+2*G-x;y++) {
            if (x+y>R+2*G-RT)
              SETBIT(boxsize-1-x,boxsize-1-y)
            else
              RESBIT(boxsize-1-x,boxsize-1-y)
          }

    if (!IS_LEFT(n) && !IS_UP(n) && IS_LEFT_UP(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(x,y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(x,y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(x,y)
    }
    if (!IS_LEFT(n) && !IS_DOWN(n) && IS_LEFT_DOWN(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(x,boxsize-1-y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(x,boxsize-1-y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(x,boxsize-1-y)
    }
    if (!IS_RIGHT(n) && !IS_UP(n) && IS_RIGHT_UP(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(boxsize-1-x,y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(boxsize-1-x,y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(boxsize-1-x,y)
    }
    if (!IS_RIGHT(n) && !IS_DOWN(n) && IS_RIGHT_DOWN(n)) {
        for (x=0;x<G;x++) for(y=0;y<G;y++)
          RESBIT(boxsize-1-x,boxsize-1-y)
        for (x=G;x<G+T;x++) for(y=0;y<G;y++)
          SETBIT(boxsize-1-x,boxsize-1-y)
        for (x=0;x<G+T;x++) for(y=G;y<G+T;y++)
          SETBIT(boxsize-1-x,boxsize-1-y)
    }

#ifdef LARGE_BELLYBUTTON
    if (!use3D) {
        if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
          for (x=0;x<G+T;x++) for(y=0;y<G+T;y++)
            SETBIT(x,y)
        if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
          for (x=0;x<G+T;x++) for(y=boxsize-G-T;y<boxsize;y++)
            SETBIT(x,y)
        if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
          for (x=boxsize-G-T;x<boxsize;x++) for(y=0;y<G+T;y++)
            SETBIT(x,y)
        if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
          for (x=boxsize-G-T;x<boxsize;x++) for(y=boxsize-G-T;y<boxsize;y++)
            SETBIT(x,y)
    } else
#else
    if (use3D)
#endif
    {
        if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
          for (x=0;x<boxsize/2-RR;x++) for(y=0;y<boxsize/2-RR;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
          for (x=0;x<boxsize/2-RR;x++) for(y=boxsize/2+RR;y<boxsize;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
          for (x=boxsize/2+RR;x<boxsize;x++) for(y=0;y<boxsize/2-RR;y++)
            THREEQUARTERSBIT(x,y)
        if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
          for (x=boxsize/2+RR;x<boxsize;x++) for(y=boxsize/2+RR;y<boxsize;y++)
            THREEQUARTERSBIT(x,y)
    }

    if ((images[next][n] = XCreateImage(display, visual,
	    1, XYBitmap, 0, (char *) data, boxsize, boxsize, 8, 0)) == None) {
	(void) free((void *) data);
	return False;
    }
    images[next][n]->byte_order = MSBFirst;
    images[next][n]->bitmap_unit = 8;
    images[next][n]->bitmap_bit_order = LSBFirst;
    return True;
}

/* ------------------------------------------------------------------ */

static Bool
create_images(use3D, next)
  Bool use3D;
  int next;
{
  int n;
  
  if (!create_an_image(BITMAPS - 1, use3D, next))
    return False;
  for (n = 0; n < BITMAPS - 1; n++) {
    /* Avoid duplication of identical images. */
    if (IS_UP(n) && IS_DOWN(n) && IS_LEFT(n) && IS_RIGHT(n))
      images[next][n] = images[next][BITMAPS - 1];
    else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
      images[next][n] = images[next][n & ~LEFT_UP];
    else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
      images[next][n] = images[next][n & ~LEFT_DOWN];
    else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
      images[next][n] = images[next][n & ~RIGHT_UP];
    else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
      images[next][n] = images[next][n & ~RIGHT_DOWN];
    else if (image_needed(n)) {
      if (!create_an_image(n, use3D, next))
	return False;
    }
  }
  return True;
}

/* ------------------------------------------------------------------ */

static int
polyPixmap(i, j, cur)
   int i, j;
   Bool cur;
{
  unsigned int pmid = 0;
  thing_t  *aThing;

  aThing = (cur) ? &curThing : &nextThing;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j,(*aThing)))
    pmid |= LEFT;
  if (ARR(i,j,(*aThing)) != ARR(i,j+1,(*aThing)))
    pmid |= DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j,(*aThing)))
    pmid |= RIGHT;
  if (ARR(i,j,(*aThing)) != ARR(i,j-1,(*aThing)))
    pmid |= UP;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j+1,(*aThing)))
    pmid |= LEFT_DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j+1,(*aThing)))
    pmid |= RIGHT_DOWN;
  if (ARR(i,j,(*aThing)) != ARR(i+1,j-1,(*aThing)))
    pmid |= RIGHT_UP;
  if (ARR(i,j,(*aThing)) != ARR(i-1,j-1,(*aThing)))
    pmid |= LEFT_UP;
  return pmid;
}

static int
rotateIndex(pmid, i)
    int pmid, i;
{
    int wallRotation, pm1, pm0;

    if (pmid == EMPTY)
	return EMPTY;
    wallRotation = (MAX_SIDES - i / MAX_WIDTH) % MAX_SIDES;
    pm1 = (0xF0 & pmid) << wallRotation;
    pm0 = (0xF & pmid) << wallRotation;
    pm1 = (0xF0 & pm1) | ((pm1 & 0xF00) >> MAX_SIDES);
    pm0 = (0xF & pm0) | ((pm0 & 0xF0) >> MAX_SIDES);
    return (pm1 | pm0);
}

void
inits(argc, argv)
    int             argc;
    char           *argv[];
{
    XSetWindowAttributes att;
    unsigned int    attvm;
    XTextProperty   wName, iName;
    XClassHint      classhints;
    XEvent          ev;
    XGCValues       gcv;
    unsigned long   gcvm;
    int             i, j, turn_style, number_polyominoes; 

    (void) SRAND(time(NULL));
    readPolyominoes();

    turn_style = tris[next.squares - MIN_SQUARES].mode[next.diagonal].turn_style;
    if (turn_style < 0 || turn_style >= MAX_TYPES)
    {
      (void) fprintf(stderr, "Welltris: corrupted polyomino file\n");
      (void) fprintf(stderr, "\tturn_style = %d.\n", turn_style);
      exit(1);
    }
    number_polyominoes = tris[next.squares - MIN_SQUARES].mode
      [next.diagonal].number[next.mixed][turn_style];
    if (number_polyominoes <= 0 || number_polyominoes > MAX_START_POLYOMINOES)
    {
      (void) fprintf(stderr, "Welltris: corrupted polyomino file\n");
      (void) fprintf(stderr, "\tnumber_polyominoes = %d.\n",
               number_polyominoes);
      exit(1);
    }
    for (j = 0; j < MAX_DEPTH; j++)
         for (i = 0; i < MAX_PERIMETER; i++) {
                wall[j][i].pmid = EMPTY;
                wall[j][i].cid = 0;
         }
    for (j = 0; j < MAX_WIDTH; j++)
         for (i = 0; i < MAX_WIDTH; i++) {
                base[j][i].pmid = EMPTY;
                base[j][i].cid = 0;
         }
    for (i = 0; i < MAX_SIDES; i++)
      frozen_wall[i] = 0;

    titleLen = strlen(MSG_TITLE);
    titleWidth = XTextWidth(bigFont, MSG_TITLE, titleLen);
    authorLen = strlen(MSG_AUTHOR);
    authorWidth = XTextWidth(tinyFont, MSG_AUTHOR, authorLen);

    frameW = GAME_WIDTH - 2;
    frameH = GAME_WIDTH - 2;
    topRWidth = NEXTBOXSIZE * MAX_SQUARES + OFFSET;
    topHeight = frameH + OFFSET * 2 + 4;
    if (titleWidth > topRWidth)
	topRWidth = titleWidth;
    if (authorWidth > topRWidth)
	topRWidth = authorWidth;
    topMidX = frameW + OFFSET + 4;
    topMidY = topHeight / 2 + bigFont->ascent;
    topWidth = topMidX + topRWidth;
    frameX = frameY = OFFSET + 2;

    titleX = (topRWidth - titleWidth) / 2 + topMidX;
    titleY = OFFSET + 2 + bigFont->ascent;
    authorX = (topRWidth - authorWidth) / 2 + topMidX;
    authorY = OFFSET + 2 + bigFont->ascent + bigFont->descent +
	tinyFont->ascent;

    sX = topMidX + OFFSET;
    sScoreY = topHeight - OFFSET - 2 - tinyFont->descent;
    sRowsY = sScoreY - tinyFont->descent - tinyFont->ascent - 2;
    sLevelY = sRowsY - tinyFont->descent - tinyFont->ascent - 2;

    sizehints.width = (sizehints.min_width =
	(sizehints.max_width = topWidth));
    sizehints.height = (sizehints.min_height =
	(sizehints.max_height = topHeight));

    theCursor = XCreateFontCursor(display, XC_exchange);

    /* arrow keys or number pad */
    XRebindKeysym(display, XK_R10, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_4, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_Left, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Left, NULL, 0,
	(unsigned char *) "j", sizeof(unsigned char));
    XRebindKeysym(display, XK_R8, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_8, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
#if	defined(UP_REFLECT)
    /* UP key to reflect */
    XRebindKeysym(display, XK_Up, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
#else
    /* UP key to rotate */
    XRebindKeysym(display, XK_Up, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
#endif
    XRebindKeysym(display, XK_KP_Up, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_R5, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Divide, NULL, 0,
	(unsigned char *) "i", sizeof(unsigned char));
    XRebindKeysym(display, XK_R11, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_5, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_Begin, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Begin, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
    XRebindKeysym(display, XK_R12, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_6, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_Right, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_Right, NULL, 0,
	(unsigned char *) "l", sizeof(unsigned char));
    XRebindKeysym(display, XK_R14, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
    XRebindKeysym(display, XK_KP_2, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
#if	defined(DOWN_ROTATE)
    /* DOWN key to rotate */
    XRebindKeysym(display, XK_Down, NULL, 0,
	(unsigned char *) "k", sizeof(unsigned char));
#else
    /* DOWN key to drop */
    XRebindKeysym(display, XK_Down, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));
#endif
    XRebindKeysym(display, XK_KP_Down, NULL, 0,
	(unsigned char *) " ", sizeof(unsigned char));

    /* create windows */
    attvm = CWBackPixel | CWEventMask | CWDontPropagate | CWCursor;
    att.background_pixel = bg;
    att.event_mask = ExposureMask | KeyPressMask |
	StructureNotifyMask | FocusChangeMask;
    att.do_not_propagate_mask = KeyReleaseMask |
	ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
	ButtonMotionMask | Button1MotionMask | Button2MotionMask |
	Button3MotionMask | Button4MotionMask | Button5MotionMask;
    att.cursor = theCursor;

    mainWin = XCreateWindow(display, DefaultRootWindow(display),
	sizehints.x, sizehints.y, topWidth, topHeight, 0,
	CopyFromParent, InputOutput, CopyFromParent, attvm, &att);

    attvm = CWBackPixel | CWBorderPixel | CWEventMask;
    att.border_pixel = fg;
    att.event_mask = ExposureMask;

    blockWin = XCreateWindow(display, mainWin,
	frameX-2, frameY-2, frameW, frameH, 2,
	CopyFromParent, InputOutput, CopyFromParent, attvm, &att);

    /* WM hints */
    XStringListToTextProperty(&winName, 1, &wName);
    XStringListToTextProperty(&iconName, 1, &iName);

    wmhints.icon_pixmap = XCreateBitmapFromData(display,
	mainWin, (char *) wicon_bits, wicon_width, wicon_height);
    classhints.res_name = "welltris";
    classhints.res_class = "Welltris";

    XSetWMProperties(display, mainWin, &wName, &iName,
	argv, argc, &sizehints, &wmhints, &classhints);

    delw = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, mainWin, &delw, 1);

    /* GC's */

    gcvm = GCForeground | GCBackground | GCFunction |
	GCFont | GCGraphicsExposures;

    gcv.function = GXcopy;
    gcv.foreground = fg;
    gcv.background = bg;
    gcv.font = bigFont->fid;
    gcv.graphics_exposures = False;
    bigGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.font = tinyFont->fid;
    tinyGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.foreground = bg;
    gcv.background = fg;
    revGC = XCreateGC(display, mainWin, gcvm, &gcv);

    gcv.background = bg;
    for (i = 0; i < MAX_START_POLYOMINOES; i++) {
	gcv.foreground = getColor(thingFGs[i]);
	if (gcv.foreground == bg)
	    gcv.foreground = fg;
	thingGCs[i] = XCreateGC(display, blockWin, gcvm, &gcv);
    }

    gcv.foreground = 0xFFFFFF;
    gcv.background = 0;
    gcv.function = GXxor;
    xorGC = XCreateGC(display, blockWin, gcvm, &gcv);

    graypix = XCreateBitmapFromData(display, blockWin,
        (char *) gray1_bits, 2, 2);

    (void) create_images(use3D, 0);
    (void) create_images(use3D, 1);

    /* new things */
    newThing();
    newThing();

    /* the last thing is to wait for mapped */
    XMapWindow(display, blockWin);
    XMapRaised(display, mainWin);
    XNextEvent(display, &ev);
}

/* ------------------------------------------------------------------ */

void
newThing()
{
    int w;

    curThing = nextThing;
    current = next;
    nextThing.random_number = LRAND();
    nextThing.random_rotation = NRAND(MAX_SIDES);
    nextThing.random_reflection = NRAND(2);
    nextThing.bonus = next.bonus;
    /* not all frozen else this is an infinite loop */
    /* curThing.xpos = (MAX_WIDTH - curThing.size) / 2; */
    curThing.xpos = NRAND(MAX_WIDTH - curThing.size + 1);
    do {
      w = NRAND(MAX_SIDES);
    } while (frozen_wall[w]);
    curThing.xpos += w * MAX_WIDTH;
    redoNext();
}

/* ------------------------------------------------------------------ */

void
drawTitle()
{
    XDrawString(display, mainWin, bigGC,
	titleX, titleY, MSG_TITLE, titleLen);
    XDrawString(display, mainWin, tinyGC,
	authorX, authorY, MSG_AUTHOR, authorLen);
}

/* ------------------------------------------------------------------ */

void
drawStatus()
{
    char            buf[30];

    (void) sprintf(buf, "Score: %d", score);
    XDrawImageString(display, mainWin, tinyGC, sX, sScoreY, buf, strlen(buf));

    (void) sprintf(buf, "Level: %d    ", level);
    XDrawImageString(display, mainWin, tinyGC, sX, sLevelY, buf, strlen(buf));

    (void) sprintf(buf, "Rows: %d", rows);
    XDrawImageString(display, mainWin, tinyGC, sX, sRowsY, buf, strlen(buf));
}

/* ------------------------------------------------------------------ */

void
clearScreen()
{
    XFillRectangle(display, blockWin, revGC,
      0, 0, GAME_WIDTH - 1, GAME_WIDTH - 1);
}

/* ------------------------------------------------------------------ */

static void
drawBox(pmid, cid, i, j)
    int             pmid, cid, i, j;
{
    if (j < 0) {
	return;
    } else if (j < MAX_DEPTH) {
	drawTrapazoid(blockWin, thingGCs[cid],
#ifdef TRAPCONST
            pmid,
#endif
            i, j);
    } else {
        int base_i, base_j;

        wall_to_base(&base_i, &base_j, i, j);
        drawSquare(pmid, cid, base_i, base_j);
    }
}

/* ------------------------------------------------------------------ */

static int
checkWall(pmid, base_i, base_j)
    int             pmid, base_i, base_j;
{
    if (pmid < 0)
	return pmid;
    /* Check if next to well wall */
    if (base_i == 0)
	pmid = CHECKLEFT((unsigned int) pmid);
    if (base_j == 0)
	pmid = CHECKUP((unsigned int) pmid);
    if (base_i == MAX_WIDTH - 1)
	pmid = CHECKRIGHT((unsigned int) pmid);
    if (base_j == MAX_WIDTH - 1)
	pmid = CHECKDOWN((unsigned int) pmid);
    return pmid;
}

static int
checkSides(pmid, i)
    int             pmid, i;
{
    if (pmid < 0)
	return pmid;
    if (i == 0 || i == 3 * MAX_WIDTH - 1)
	pmid = CHECKLEFT((unsigned int) pmid);
    else if (i == MAX_WIDTH - 1 || i == 2 * MAX_WIDTH)
	pmid = CHECKRIGHT((unsigned int) pmid);
    else if (i == MAX_WIDTH || i == 4 * MAX_WIDTH - 1)
	pmid = CHECKUP((unsigned int) pmid);
    else if (i == 2 * MAX_WIDTH - 1 || i == 3 * MAX_WIDTH)
	pmid = CHECKDOWN((unsigned int) pmid);
   return pmid;
}

static int
checkBottom(pmid, i, j)
    int             pmid, i, j;
{
    if (pmid < 0 || j != MAX_DEPTH - 1)
	return pmid;
    switch (i / MAX_WIDTH) {
    case 0:
	return CHECKDOWN((unsigned int) pmid);
    case 1:
	return CHECKLEFT((unsigned int) pmid);
    case 2:
	return CHECKUP((unsigned int) pmid);
    case 3:
	return CHECKRIGHT((unsigned int) pmid);
    }
    return EMPTY;
}

/* ------------------------------------------------------------------ */

void
drawField()
{
    int             i, j;

    for (i = 0; i < MAX_PERIMETER; i++)
        for (j = 0; j < MAX_DEPTH; j++) {
	    if (wall[j][i].pmid >= 0)
                drawTrapazoid(blockWin, thingGCs[wall[j][i].cid],
#ifdef TRAPCONST
                    wall[j][i].pmid,
#endif
                    i, j);
	    if (frozen_wall[i / MAX_WIDTH])
		drawTrapazoid(blockWin, xorGC,
#ifdef TRAPCONST
                    BITMAPS - 1,
#endif
                    i, j);
        }
    for (i = 0; i < MAX_WIDTH; i++)
        for (j = 0; j < MAX_WIDTH; j++)
	    if (base[j][i].pmid >= 0)
		drawSquare(base[j][i].pmid, base[j][i].cid, i, j);
}

/* ------------------------------------------------------------------ */

void
drawThing()
{
    int             i, j, xi, yj;
    int             base_i, base_j;
    int             temp_base[MAX_WIDTH][MAX_WIDTH];

    /* Initialize */
    for (i = 0; i < MAX_WIDTH; i++)
        for (j = 0; j < MAX_WIDTH; j++)
	    temp_base[j][i] = 0;
    /* Find pieces and overlap them if necessary */
    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (curThing.xpos + i) % MAX_PERIMETER;
		yj = curThing.ypos + j;
		if (yj >= MAX_DEPTH) {
		    wall_to_base(&base_i, &base_j, xi, yj);
		    xi = checkWall(rotateIndex(polyPixmap(i, j, True), xi),
			    	base_i, base_j);
		    if (!temp_base[base_j][base_i])
			temp_base[base_j][base_i] = xi;
		    else
			temp_base[base_j][base_i] &= xi;
	        }
	    }
    /* Now draw */
    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (curThing.xpos + i) % MAX_PERIMETER;
		yj = curThing.ypos + j;
		if (yj >= MAX_DEPTH) {
		    wall_to_base(&base_i, &base_j, xi, yj);
		    if (temp_base[base_j][base_i]) {
			drawSquare(temp_base[base_j][base_i],
			    curThing.color_number, base_i, base_j);
			temp_base[base_j][base_i] = 0;
		    }
		} else if (yj >= 0) {
		    drawTrapazoid(blockWin, thingGCs[curThing.color_number],
#ifdef TRAPCONST
			checkSides(checkBottom(rotateIndex(polyPixmap(i,
			    j, True), xi), xi, yj), xi),
#endif
			xi, yj);
	        }
	    }
}

/* ------------------------------------------------------------------ */

static void
drawThingDiff(old)
    thing_t        *old;
{
    int             i, j, xi, yj, ox, oy;
    int             base_i, base_j;
    int             temp_base[MAX_WIDTH][MAX_WIDTH];

    /* Initialize */
    for (i = 0; i < MAX_WIDTH; i++)
        for (j = 0; j < MAX_WIDTH; j++)
	    temp_base[j][i] = 0;
    /* Find pieces and overlap them if necessary */
    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (curThing.xpos + i) % MAX_PERIMETER;
		yj = curThing.ypos + j;
		if (yj >= MAX_DEPTH) {
		    wall_to_base(&base_i, &base_j, xi, yj);
		    xi = checkWall(rotateIndex(polyPixmap(i, j, True), xi),
			    	base_i, base_j);
		    if (!temp_base[base_j][base_i])
			temp_base[base_j][base_i] = xi;
		    else
			temp_base[base_j][base_i] &= xi;
	        }
	    }
    /* Now draw */
    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (curThing.xpos + i) % MAX_PERIMETER;
		yj = curThing.ypos + j;
		if (yj >= MAX_DEPTH) {
		    wall_to_base(&base_i, &base_j, xi, yj);
		    if (temp_base[base_j][base_i] > 0) {
			drawSquare(temp_base[base_j][base_i],
			    curThing.color_number, base_i, base_j);
			/* negate for erase */
			temp_base[base_j][base_i] = -temp_base[base_j][base_i];
		    }
		} else if (yj >= 0) {
		    drawTrapazoid(blockWin, thingGCs[curThing.color_number],
#ifdef TRAPCONST
			checkSides(checkBottom(rotateIndex(polyPixmap(i,
			    j, True), xi), xi, yj), xi),
#endif
			xi, yj);
	        }
	    }
    /* Erase */
    for (i = 0; i < old->size; i++)
	for (j = 0; j < old->size; j++) {
	    xi = (old->xpos + i) % MAX_PERIMETER;
	    yj = old->ypos + j;
	    ox = (xi - curThing.xpos + MAX_PERIMETER) % MAX_PERIMETER;
	    oy = yj - curThing.ypos;
	    if (yj >= MAX_DEPTH) {
		wall_to_base(&base_i, &base_j, xi, yj);
		if (tris[old->squares].polyomino
                        [old->polyomino_number].shape[j][i] &&
		    ((ox >= curThing.size) || (oy < 0) ||
		    (oy >= curThing.size) || !temp_base[base_j][base_i]))
		    drawPlainSquare(blockWin, revGC, base_i, base_j);
	    } else if (yj >= 0) {
		if (tris[old->squares].polyomino
                        [old->polyomino_number].shape[j][i] &&
		    ((ox >= curThing.size) || (oy < 0) ||
		    (oy >= curThing.size) || !tris[curThing.squares].polyomino
                        [curThing.polyomino_number].shape[oy][ox]))
		    drawTrapazoid(blockWin, revGC,
#ifdef TRAPCONST
                        BITMAPS - 1,
#endif
		        xi, yj);
	    }
	}

}

/* ------------------------------------------------------------------ */

void
drawNext()
{
    int             x, y;
    int             i, j;

    x = topMidX + (topRWidth - nextThing.size * NEXTBOXSIZE) / 2;
    y = topMidY - nextThing.size * NEXTBOXSIZE / 2;
    for (i = 0; i < nextThing.size; i++)
	for (j = 0; j < nextThing.size; j++)
	    if (tris[nextThing.squares].polyomino
		    [nextThing.polyomino_number].shape[j][i])
		(void) XPutImage(display, mainWin,
		    thingGCs[nextThing.color_number],
		    images[1][polyPixmap(i, j, False)],
		    0, 0, x + NEXTBOXSIZE * i, y + NEXTBOXSIZE * j,
		    NEXTBOXSIZE, NEXTBOXSIZE);
}

/* ------------------------------------------------------------------ */

void
clearNext()
{
    XFillRectangle(display, mainWin, revGC,
	topMidX, topMidY - NEXTBOXSIZE * MAX_SQUARES / 2,
	topRWidth, NEXTBOXSIZE * MAX_SQUARES);
}

/* ------------------------------------------------------------------ */

void
redoNext()
{
    int turn_style = tris[next.squares - MIN_SQUARES].mode[next.diagonal].turn_style;
    int next_start, i;

    nextThing.squares = next.squares - MIN_SQUARES + nextThing.bonus;
    next_start = nextThing.random_number % tris[nextThing.squares].mode
	[next.diagonal].number[(nextThing.bonus) ? NOMIX : next.mixed][turn_style];
    nextThing.color_number = next_start;

    if (next.mixed && !nextThing.bonus)
    {
	nextThing.squares = 0;
	while (next_start >= tris[nextThing.squares].mode
	    [next.diagonal].number[NOMIX][turn_style])
	{
	    next_start -= tris[nextThing.squares].mode
		[next.diagonal].number[NOMIX][turn_style];
	    nextThing.squares++;
	}
    }
    nextThing.polyomino_number = tris[nextThing.squares].mode
	[next.diagonal].start[next_start][turn_style];
    if (tris[nextThing.squares].mode[next.diagonal].turn_style > NONE)
	for (i = 0; i < nextThing.random_rotation; i++)
	    nextThing.polyomino_number = tris[nextThing.squares].polyomino
		[nextThing.polyomino_number].rotation;
    if (tris[nextThing.squares].mode[next.diagonal].turn_style == ALL)
	for (i = 0; i < nextThing.random_reflection; i++)
	    nextThing.polyomino_number = tris[nextThing.squares].polyomino
		[nextThing.polyomino_number].reflection;
    nextThing.size = tris[nextThing.squares].polyomino
	[nextThing.polyomino_number].size;
    nextThing.ypos = -tris[nextThing.squares].polyomino
	[nextThing.polyomino_number].start_height;
}

/* ------------------------------------------------------------------ */

void
banner(msg)
    char            msg[];
{
    int             mlen = strlen(msg);
    int             w = XTextWidth(bigFont, msg, mlen);
    int             x = (topRWidth - w)/2 + topMidX;

    XFillRectangle(display, mainWin, revGC,
	x - 60, topMidY - bigFont->ascent - 5,
	w + 120, bigFont->ascent + bigFont->descent + 10);
    XDrawString(display, mainWin, bigGC, x, topMidY, msg, mlen);
}

/* ------------------------------------------------------------------ */

void
putBox()
{
    int             i, j, xi, yj;
    int             base_i, base_j;

    /* Set pieces and overlap them if necessary */
    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (curThing.xpos + i) % MAX_PERIMETER;
		yj = curThing.ypos + j;
		if (yj >= MAX_DEPTH) {
		    wall_to_base(&base_i, &base_j, xi, yj);
		    xi = checkWall(rotateIndex(polyPixmap(i, j, True), xi),
			    	base_i, base_j);
		    if (!base[base_j][base_i].pmid)
			base[base_j][base_i].pmid = xi;
		    else
			base[base_j][base_i].pmid &= xi;
            	    base[base_j][base_i].cid = curThing.color_number;
		} else if (yj >= 0) {
		    wall[yj][xi].pmid =
			checkSides(checkBottom(rotateIndex(polyPixmap(i,
			    j, True), xi), xi, yj), xi);
		    wall[yj][xi].cid = curThing.color_number;
		    frozen_wall[xi / MAX_WIDTH] = MAX_SIDES;
	        }
	    }
}

/* ------------------------------------------------------------------ */

Bool
overlapping()
{
    int             i, j, xi, yj;
    int             x = curThing.xpos, y = curThing.ypos;

    for (i = 0; i < curThing.size; i++)
	for (j = 0; j < curThing.size; j++)
	    if (tris[curThing.squares].polyomino
		    [curThing.polyomino_number].shape[j][i]) {
		xi = (x + i) % MAX_PERIMETER;
		yj = y + j;
		if (yj < MAX_DEPTH) {
		    if (frozen_wall[xi / MAX_WIDTH])
			return True; 
		    if (gradualAppear) {
/* This method one can turn polyomino to an area above of screen, also
   part of the polyomino may not be visible initially */          
			if ((yj >= 0) && (wall[yj][xi].pmid >= 0))
			    return True;
		    } else {
/* This method does not allow turning polyomino to an area above screen */
			if ((yj < 0) || (wall[yj][xi].pmid >= 0))
			    return True;
		    }
		}
		else if (yj < MAX_DEPTH + MAX_WIDTH) {
		    int base_i, base_j;

		    wall_to_base(&base_i, &base_j, xi, yj);
		    if (base[base_j][base_i].pmid >= 0)
			return True;
		}
		else
		    return True;
	    }

    return False;
}

/* ------------------------------------------------------------------ */

Bool
atBottom()
{
    int             i, j, xi, yj;
    int             x = curThing.xpos, y = curThing.ypos;

    for (i = 0; i < curThing.size; i++)
      for (j = 0; j < curThing.size; j++)
	if (tris[curThing.squares].polyomino
                [curThing.polyomino_number].shape[j][i]) {
          xi = (x + i) % MAX_PERIMETER;
          yj = y + j;
    	  if (yj < -1)
            return False;
          if (yj < MAX_DEPTH - 1) {
            if (frozen_wall[xi / MAX_WIDTH])
	      return True; 
            if (wall[yj + 1][xi].pmid >= 0)
	      return True;
          }
          else if (yj < MAX_DEPTH + MAX_WIDTH - 1) {
            int base_i, base_j;
            wall_to_base(&base_i, &base_j, xi, yj + 1);
            if (base[base_j][base_i].pmid >= 0)
	      return True;
          }
          else
            return True;
        }

    return False;
}

/* ------------------------------------------------------------------ */

static Bool
atBaseFully()
{
    int             i, j;

    for (i = 0; i < curThing.size; i++)
      for (j = 0; j < curThing.size; j++)
	if (curThing.ypos + j < MAX_DEPTH && tris[curThing.squares].polyomino
              [curThing.polyomino_number].shape[j][i])
 
          return False;
     return True;
}            

/* ------------------------------------------------------------------ */

static Bool
atBasePartially()
{
    int             i, j;

    for (i = 0; i < curThing.size; i++)
      for (j = 0; j < curThing.size; j++)
        if (curThing.ypos + j >= MAX_DEPTH && tris[curThing.squares].polyomino
              [curThing.polyomino_number].shape[j][i])
            
          return True;
     return False;
}
         
/* ------------------------------------------------------------------ */

static Bool
wallChange(old, new)
thing_t old, new;
{
    int             w = -1, i, j;
    int             x = curThing.xpos;

    for (i = 0; i < old.size; i++)
      for (j = 0; j < old.size; j++)
	if (tris[old.squares].polyomino[old.polyomino_number].shape[j][i]) {
          if (w == -1)
            w = ((x + i) % MAX_PERIMETER) / MAX_WIDTH;
          else if (w != ((x + i) % MAX_PERIMETER) / MAX_WIDTH)
            return True;
        }
    for (i = 0; i < new.size; i++)
      for (j = 0; j < new.size; j++)
	if (tris[new.squares].polyomino[new.polyomino_number].shape[j][i])
          if (w != ((x + i) % MAX_PERIMETER) / MAX_WIDTH)
            return True;
    return False;
}

/* ------------------------------------------------------------------ */

void
tryMove(move)
    move_t          move;
{
    thing_t         old;
    int             i, skip = False;

    old = curThing;

    switch (move) {
    case FALL:
	curThing.ypos++;
	break;

    case DROP:
	do {
	    curThing.ypos++;
	    score += level + !grid;
	} while (!overlapping());
	curThing.ypos--;
	break;

    case MOVE_LEFT:
	curThing.xpos = (curThing.xpos + MAX_PERIMETER - 1) % MAX_PERIMETER;
        if (atBaseFully() || (atBasePartially() && wallChange(old, curThing)))
          skip = True;
	break;

    case MOVE_RIGHT:
	curThing.xpos = (curThing.xpos + 1) % MAX_PERIMETER;
        if (atBaseFully() || (atBasePartially() && wallChange(old, curThing)))
          skip = True;
	break;

    case ROTATE:
        if (tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style > NONE)
        {
          if (cw)
            for (i = 0; i < MAX_SIDES - 1; i++)
              curThing.polyomino_number = tris[curThing.squares].polyomino
                [curThing.polyomino_number].rotation;
          else /* ccw */
            curThing.polyomino_number = tris[curThing.squares].polyomino
              [curThing.polyomino_number].rotation;
	  curThing.xpos = old.xpos;
	  curThing.ypos = old.ypos;
          if (atBasePartially() && wallChange(old, curThing))
            skip = True;
	}
	break;

    case REFLECT: /* reflect on y axis */
        if (tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style == ALL)
        {
          curThing.polyomino_number = tris[curThing.squares].polyomino
            [curThing.polyomino_number].reflection;
	  curThing.xpos = old.xpos;
	  curThing.ypos = old.ypos;
          if (atBasePartially() && wallChange(old, curThing))
            skip = True;
	}
	break;
    }

    if (!skip && !overlapping())
	drawThingDiff(&old);
    else
	curThing = old;
}

/* ------------------------------------------------------------------ */

void
fillLines()
{
    int             i, j;
    int turn_style = tris[current.squares - MIN_SQUARES].mode[current.diagonal].turn_style;
    
    XSync(display, True);
    for (j = 0; j < MAX_DEPTH + MAX_WIDTH; j++)
        for (i = 0; i < MAX_PERIMETER; i++) {
		drawBox(BITMAPS - 1,
                        NRAND(tris[current.squares - MIN_SQUARES].mode
                          [current.diagonal].number[current.mixed][turn_style]),
                        i, j);
	        XSync(display, True);
	    }
}
							
/* ------------------------------------------------------------------ */

static void
freezeWall(w)
int w;
{
    int i, j;

    for (j = 0; j < MAX_DEPTH; j++)
	for (i = 0; i < MAX_WIDTH; i++)
	    drawTrapazoid (blockWin, xorGC,
#ifdef TRAPCONST
                BITMAPS - 1,
#endif
	        w * MAX_WIDTH + i, j);
    if (beep)
	XBell(display, BVOLUME);
    XSync(display, False);
}

/* ------------------------------------------------------------------ */

static void
wall_to_base(base_x, base_y, wall_x, wall_y)
int *base_x, *base_y, wall_x, wall_y;
{
  switch (wall_x / MAX_WIDTH)
  {
    case 0:
      *base_x = wall_x;
      *base_y = wall_y - MAX_DEPTH;
      break;
    case 1:
      *base_x = MAX_WIDTH - 1 + MAX_DEPTH - wall_y;
      *base_y = wall_x - MAX_WIDTH;
      break;
    case 2:
      *base_x = MAX_PERIMETER - 1 - MAX_WIDTH - wall_x;
      *base_y = MAX_WIDTH - 1 + MAX_DEPTH - wall_y;
      break;
    case 3:
      *base_x = wall_y - MAX_DEPTH;
      *base_y = MAX_PERIMETER - 1 - wall_x;
      break;
    default:
     (void) fprintf (stderr, "wall_to_base kinds range 0-3, got %d.\n",
               wall_x / MAX_WIDTH);
      exit(-1);
  }
}

/* ------------------------------------------------------------------ */

static void
dropWall(w)
int w;
{
  int i, j, k, l, fits, lines, base_x, base_y;
  lines = 0;
  for (j = MAX_DEPTH - 1; j >= 0 && lines == 0; j--)
    for (i = 0; i < MAX_WIDTH; i++)
      if (wall[j][w * MAX_WIDTH + i].pmid != EMPTY)
        lines = MAX_DEPTH - j;
  if (lines > 0) {
    fits = True;
    j = 0;
    while (j < (MAX_WIDTH / 2 + lines - 1) && fits) {
      j++;
      for (l = MAX_DEPTH - j; l < MAX_DEPTH; l++)
        for (i = 0; i < MAX_WIDTH; i++)
          if (wall[l][w * MAX_WIDTH + i].pmid != EMPTY) {
            wall_to_base(&base_x, &base_y,
              w * MAX_WIDTH + i, l + j);
            if (base[base_y][base_x].pmid != EMPTY)
              fits = False;
          }
    }
    if (!fits)
      j--;
    if (j > 0)
    {
      for (l = MAX_DEPTH - 1; l >= 0; l--)
        for (i = 0; i < MAX_WIDTH; i++)
          if (wall[l][w * MAX_WIDTH + i].pmid != EMPTY) {
            k = w * MAX_WIDTH + i;
            if (l + j >= MAX_DEPTH) {
              wall_to_base(&base_x, &base_y, k, l + j);
              base[base_y][base_x] = wall[l][k];
              base[base_y][base_x].pmid = checkWall(base[base_y][base_x].pmid,
                base_x, base_y);
              wall[l][k].pmid = EMPTY;
              drawTrapazoid(blockWin, revGC,
#ifdef TRAPCONST
                BITMAPS - 1,
#endif
                k, l);
              drawSquare(base[base_y][base_x].pmid, base[base_y][base_x].cid,
                base_x, base_y);
            } else {
              wall[l + j][k] = wall[l][k];
              wall[l + j][k].pmid = checkBottom(wall[l + j][k].pmid, k, l + j);
              wall[l][k].pmid = EMPTY;
              drawTrapazoid(blockWin, revGC,
#ifdef TRAPCONST
                BITMAPS - 1,
#endif
                k, l);
              drawTrapazoid(blockWin, thingGCs[wall[l][k].cid],
#ifdef TRAPCONST
                wall[l + j][k].pmid,
#endif
                k, l + j);
            }
            XFlush(display);
          }
      if (beep)
        XBell(display, BVOLUME);
      XSync(display, False);
    }
  }
}

/* ------------------------------------------------------------------ */

Bool
allFrozen()
{
  int w;

  for (w = 0; w < MAX_SIDES; w++)
    if (frozen_wall[w] == MAX_SIDES) {
      freezeWall(w);
      XSync(display, False);
    }
  for (w = 0; w < MAX_SIDES; w++)
    if (!frozen_wall[w])
      return False;
  return True;
}

/* ------------------------------------------------------------------ */

void
checkFreeze()
{
  int w;

  for (w = 0; w < MAX_SIDES; w++) {
    if (!frozen_wall[w])
      dropWall(w);
    else {
      frozen_wall[w]--;
      if (!frozen_wall[w]) {
        freezeWall(w);
        dropWall(w);
      }
    }
  }
}

/* ------------------------------------------------------------------ */

int
checkLines()
{
    int             lSet[2][MAX_WIDTH], nset[2];
    int             i, j, k, *tmp;

    nset[0] = nset[1] = 0;

    /* Find filled rows */
    for (j = 0; j < MAX_WIDTH; j++) {
        lSet[0][j] = 0;
        for (i = 0; i < MAX_WIDTH; i++)
            if (base[j][i].pmid >= 0)
                lSet[0][j]++;
        if (lSet[0][j] == MAX_WIDTH)
            nset[0]++;
    }
    /* Find filled columns */
    for (i = 0; i < MAX_WIDTH; i++) {
        lSet[1][i] = 0;
        for (j = 0; j < MAX_WIDTH; j++)
            if (base[j][i].pmid >= 0)
                lSet[1][i]++;
        if (lSet[1][i] == MAX_WIDTH)
            nset[1]++;
    }

    if (nset[0] || nset[1]) {
        Field temp_base[MAX_WIDTH][MAX_WIDTH];

        for (k = 0; k < ((NUM_FLASHES / (nset[0] + nset[1])) / 2) * 2; k++) {
            for (j = 0; j < MAX_WIDTH; j++) {
                if (lSet[0][j] == MAX_WIDTH)
                  for (i = 0; i < MAX_WIDTH; i++)
                    drawPlainSquare(blockWin, xorGC, i, j);
                if (lSet[1][j] == MAX_WIDTH)
                  for (i = 0; i < MAX_WIDTH; i++)
                    drawPlainSquare(blockWin, xorGC, j, i);
            }
            XFlush(display);
        }

        for (j = 0; j < MAX_WIDTH; j++)
          for (i = 0; i < MAX_WIDTH; i++)
            temp_base[j][i] = base[j][i];
        if (nset[0]) {
          for (j = 0; j < MAX_WIDTH / 2; j++)
            if (lSet[0][j] == MAX_WIDTH)
              for (i = 0; i < MAX_WIDTH; i++) {
                for (k = j; k > 0; k--)
                  temp_base[k][i] = temp_base[k - 1][i];
                temp_base[0][i].pmid = EMPTY;
		if (j > 0) {
		  tmp = &temp_base[j][i].pmid;
		  if (*tmp >= 0)
		     *tmp = CHECKDOWN((unsigned int) *tmp);
		}
		tmp = &temp_base[j + 1][i].pmid;
		if (*tmp >= 0)
		   *tmp = CHECKUP((unsigned int) *tmp);
              }
          for (j = MAX_WIDTH - 1; j >= MAX_WIDTH / 2; j--)
            if (lSet[0][j] == MAX_WIDTH)
              for (i = 0; i < MAX_WIDTH; i++) {
                for (k = j; k < MAX_WIDTH - 1; k++)
                  temp_base[k][i] = temp_base[k + 1][i];
                temp_base[MAX_WIDTH - 1][i].pmid = EMPTY;
		if (j < MAX_WIDTH - 1) {
		  tmp = &temp_base[j][i].pmid;
		  if (*tmp >= 0)
		     *tmp = CHECKUP((unsigned int) *tmp);
		}
		tmp = &temp_base[j - 1][i].pmid;
		if (*tmp >= 0)
		   *tmp = CHECKDOWN((unsigned int) *tmp);
              }
        }
        if (nset[1]) {
          for (i = 0; i < MAX_WIDTH / 2; i++)
            if (lSet[1][i] == MAX_WIDTH)
              for (j = 0; j < MAX_WIDTH; j++) {
                for (k = i; k > 0; k--)
                  temp_base[j][k] = temp_base[j][k - 1];
                temp_base[j][0].pmid = EMPTY;
		if (i > 0) {
		  tmp = &temp_base[j][i].pmid;
		  if (*tmp >= 0)
		     *tmp = CHECKRIGHT((unsigned int) *tmp);
		}
		tmp = &temp_base[j][i + 1].pmid;
		if (*tmp >= 0)
		   *tmp = CHECKLEFT((unsigned int) *tmp);
              }
          for (i = MAX_WIDTH - 1; i >= MAX_WIDTH / 2; i--)
            if (lSet[1][i] == MAX_WIDTH)
              for (j = 0; j < MAX_WIDTH; j++) {
                for (k = i; k < MAX_WIDTH - 1; k++)
                  temp_base[j][k] = temp_base[j][k + 1];
                temp_base[j][MAX_WIDTH - 1].pmid = EMPTY;
		if (i < MAX_WIDTH - 1) {
		  tmp = &temp_base[j][i].pmid;
		  if (*tmp >= 0)
		     *tmp = CHECKLEFT((unsigned int) *tmp);
		}
		tmp = &temp_base[j][i - 1].pmid;
		if (*tmp >= 0)
		   *tmp = CHECKRIGHT((unsigned int) *tmp);
              }
        }
        for (j = 0; j < MAX_WIDTH; j++)
          for (i = 0; i < MAX_WIDTH; i++)
            if ((temp_base[j][i].cid != base[j][i].cid) ||
                (temp_base[j][i].pmid != base[j][i].pmid))
            {
              base[j][i] = temp_base[j][i];
              if (base[j][i].pmid >= 0)
                drawSquare(base[j][i].pmid, base[j][i].cid, i, j);
              else
                drawPlainSquare(blockWin, revGC, i, j);
            }
        XFlush(display);

	if (beep)
	    XBell(display, BVOLUME);
	XSync(display, False);
    }

    return (nset[0] + nset[1]);
}

/* ------------------------------------------------------------------ */

void
drawGrid()
{
  GC gc;
  int i, k, square_x, sum, sum_var;

  if (grid)
    gc = tinyGC;
  else
    gc = revGC;
  sum = sum_var = MAX_WIDTH * BOXSIZE + DELTA - 1;
  square_x = 0;
  for (i = 0; i < MAX_WIDTH; i++)
  {
    XDrawLine(display, blockWin, gc, square_x, 0, sum_var, sum);
    XDrawLine(display, blockWin, gc,
      ENDPT - 1, square_x, ENDPT - 1 - sum, sum_var);
    XDrawLine(display, blockWin, gc, ENDPT - 1 - square_x, ENDPT - 1,
      ENDPT - 1 - sum_var, ENDPT - 1 - sum);
    XDrawLine(display, blockWin, gc,
      0, ENDPT - 1 - square_x, sum, ENDPT - 1 - sum_var);
    square_x += 3 * BOXSIZE;
    sum_var += BOXSIZE;
  }
  sum = ENDPT;
  square_x = 0;
  k = (MAX_WIDTH * BOXSIZE) / MAX_DEPTH;
  for (i= 0; i <= MAX_DEPTH; i++)
  {
    XFillRectangle(display, blockWin, gc,
      square_x, square_x, 1, sum);
    XFillRectangle(display, blockWin, gc,
      square_x, square_x, sum, 1);
    XFillRectangle(display, blockWin, gc,
      square_x + sum - 1, square_x, 1, sum);
    XFillRectangle(display, blockWin, gc,
      square_x, square_x + sum - 1, sum, 1);
    square_x += k;
    sum -= (2 * k);
  }
#if 0
  sum = MAX_WIDTH * BOXSIZE + DELTA;
  square_x = square_y = sum;
  for (i= 0; i <= MAX_WIDTH ; i++)
  {
    XFillRectangle(display, blockWin, gc, square_x, square_y, 1, sum);
    square_x += BOXSIZE;
  }
  square_x = square_y = sum;
  for (i= 0; i <= MAX_WIDTH ; i++)
  {
    XFillRectangle(display, blockWin, gc, square_x, square_y, sum, 1);
    square_y += BOXSIZE;
  }
#endif
  XSync(display, False);
}

/* ------------------------------------------------------------------ */

static void
drawTrapazoid (win, gc,
#ifdef TRAPCONST
  pmid,
#endif
  i, j)
Window win;
GC gc;
#ifdef TRAPCONST
  int pmid;
#endif
int i, j;
{
  int ulx, uly, lrx, lry, base_x, plateau_x, w;

  w = i / MAX_WIDTH;
  i -= (w * MAX_WIDTH);
  ulx = 3 * BASE_WIDTH / 2 + (i - (MAX_WIDTH / 2)) *
    (2 * BOXSIZE * (MAX_DEPTH - j) / MAX_DEPTH + BOXSIZE) + 2 * DELTA;
  uly = j * MAX_WIDTH * BOXSIZE/MAX_DEPTH + 2*DELTA;
  lrx = 3 * BASE_WIDTH / 2 + (i + 1 - (MAX_WIDTH / 2)) *
    (2 * BOXSIZE * (MAX_DEPTH - j - 1) / MAX_DEPTH + BOXSIZE) +
    DELTA * (i / 4) - 3 * DELTA;
  lry = (j + 1) * MAX_WIDTH * BOXSIZE / MAX_DEPTH - DELTA;
  base_x = BOXSIZE + (MAX_DEPTH - j) * 2 * BOXSIZE / MAX_DEPTH -
    5 * DELTA;
  plateau_x = BOXSIZE + (MAX_DEPTH - j - 1) * 2 *
    BOXSIZE / MAX_DEPTH - 5 * DELTA;
  switch (w)
  {
    case 0:
      XFillTrapazoid (display, win, gc,
#ifdef TRAPCONST
  pmid,
#endif
        ulx, uly, base_x, 0, lrx, lry, plateau_x, 0);
      break;
    case 1:
      XFillTrapazoid (display, win, gc,
#ifdef TRAPCONST
  pmid,
#endif
        ENDPT - uly, ulx, 0, base_x, ENDPT - lry, lrx, 0, plateau_x);
      break;
    case 2:
      XFillTrapazoid (display, win, gc,
#ifdef TRAPCONST
  pmid,
#endif
        ENDPT - 1 - ulx, ENDPT - uly, -base_x, 0,
        ENDPT - 1 - lrx, ENDPT - lry, -plateau_x, 0);
      break;
    case 3:
      XFillTrapazoid (display, win, gc,
#ifdef TRAPCONST
  pmid,
#endif
         uly, ENDPT - 1 - ulx, 0, -base_x,
         lry, ENDPT - 1 - lrx, 0, -plateau_x);
      break;
    default:
      (void) fprintf (stderr, "trapazoid kinds range 0-3, got %d.\n", w);
  }
}

/* ------------------------------------------------------------------ */

static void
drawPlainSquare (win, gc, i, j)
Window win;
GC gc;
int i, j;
{
  XFillRectangle (display, win, gc,
    i * BOXSIZE + BASE_WIDTH + 2 * DELTA - 2,
    j * BOXSIZE + BASE_WIDTH + 2 * DELTA - 2,
    BOXSIZE - 2 * DELTA + 2, BOXSIZE - 2 * DELTA + 2);
}

/* ------------------------------------------------------------------ */

static void
drawSquare(pmid, cid, i, j)
    int             pmid, cid, i, j;
{
  (void) XPutImage(display, blockWin,
    thingGCs[cid], images[0][pmid % BITMAPS],
    0, 0, i * BOXSIZE + BASE_WIDTH + DELTA - 1,
    j * BOXSIZE + BASE_WIDTH + DELTA - 1, BOXSIZE, BOXSIZE);
}


/* ------------------------------------------------------------------ */

static void
XFillTrapazoid (display, window, gc,
#ifdef TRAPCONST
  pmid,
#endif
  ulx, uly, base_x, base_y, lrx, lry, plateau_x, plateau_y)
Display *display;
Window window;
GC gc;
#ifdef TRAPCONST
  int pmid;
#endif
int ulx, uly, base_x, base_y, lrx, lry, plateau_x, plateau_y;
{
  static XPoint trapazoid_list[MAX_SIDES + 1];

  if (gc != revGC && gc != xorGC) {
    XSetFillStyle(display, gc, FillOpaqueStippled);
    XSetStipple(display, gc, graypix);
  }
  trapazoid_list[0].x = ulx;
  trapazoid_list[0].y = uly;
  trapazoid_list[1].x = base_x;
  trapazoid_list[1].y = base_y;
  trapazoid_list[2].x = lrx - base_x - ulx;
  trapazoid_list[2].y = lry - base_y - uly;
  trapazoid_list[3].x = -plateau_x;
  trapazoid_list[3].y = -plateau_y;
  trapazoid_list[4].x = ulx + plateau_x - lrx;
  trapazoid_list[4].y = uly + plateau_y - lry;
  XFillPolygon(display, window, gc, trapazoid_list, MAX_SIDES,
    Convex, CoordModePrevious);
  XDrawLines(display, window, gc, trapazoid_list, MAX_SIDES + 1,
    CoordModePrevious);
  if (gc != revGC && gc != xorGC) {
    XSetFillStyle(display, gc, FillSolid);
  }
#ifdef TRAPCONST
  /* Code should be added to use pmid info */
  if (use3D && gc != revGC && gc != xorGC) {
    trapazoid_list[0].x = ulx + (lrx - ulx) / 4;
    trapazoid_list[0].y = uly + (lry - uly) / 4;
    trapazoid_list[1].x = (lrx - plateau_x - ulx) / 2;
    trapazoid_list[1].y = (lry - plateau_y - uly) / 2;
    trapazoid_list[2].x = plateau_x / 2;
    trapazoid_list[2].y = plateau_y / 2;
    trapazoid_list[3].x = (ulx + base_x - lrx) / 2;
    trapazoid_list[3].y = (uly + base_y - lry) / 2;
    XFillPolygon(display, window, gc, trapazoid_list, MAX_SIDES,
      Convex, CoordModePrevious);
  }
#endif
}

/* ------------------------------------------------------------------ */

void
realTime(tv)
    struct timeval *tv;
{
    while (tv->tv_usec < 0) {
	tv->tv_sec--;
	tv->tv_usec += MILLION;
    }
    while (tv->tv_usec >= MILLION) {
	tv->tv_sec++;
	tv->tv_usec -= MILLION;
    }
}

/* ------------------------------------------------------------------ */

static char
readChar(lc, cc)
int *lc;
int *cc;
{
  int lcold = *lc, ccold = *cc;

  if (*lc >= sizeof(polyomino_data)/sizeof(polyomino_data[0]))
    return ' ';
  if (*cc == strlen(polyomino_data[*lc])) {
    (*lc)++;
    *cc = 0;
  } else
    (*cc)++;
  return polyomino_data[lcold][ccold];
}

/* ------------------------------------------------------------------ */

#define BASE 10
#define MAXLOOP 100

static int
readInt(lc, cc, c)
int *lc;
int *cc;
char *c;
{
  Bool first = False;
  int loopcount = 0;
  int number;

  while (!first) {
    *c = readChar(lc, cc);
    if (*c >= '0' && *c <='9')
      first = True;
    if (loopcount++ >= MAXLOOP)
    {
      (void) fprintf(stderr, "Welltris: readInt1 infinite loop.\n");
      exit(1);
    }
  }
  number = *c - '0';
  first = False;
  loopcount = 0;
  while (!first) {
    *c = readChar(lc, cc);
    if (*c < '0' || *c > '9')
      first = True;
    else
      number = BASE * number + *c - '0';
    if (loopcount++ >= MAXLOOP)
    {
      (void) fprintf(stderr, "Welltris: readInt2 infinite loop.\n");
      exit(1);
    }
  }
  return number;
}

/* ------------------------------------------------------------------ */

static void
readPolyominoes()
{
  char c;
  int i, j, k, sq, polyomino, sum, start, n, size = 0, height, diag,
    game, toss = 0;
  int counter[MAX_SQUARES - MIN_SQUARES + 1];
  int start_counter[MAX_SQUARES - MIN_SQUARES + 1][MAX_MODES][MAX_TYPES];
  int lc = 0, cc = 0;

  for (sq = 0; sq <= MAX_SQUARES - MIN_SQUARES; sq++)
  {
    counter[sq] = 0;
    for (polyomino = 0; polyomino < MAX_POLYOMINOES; polyomino++)
      for (j = 0; j < MAX_SQUARES; j++)
        for (i = 0; i < MAX_SQUARES; i++)
          tris[sq].polyomino[polyomino].shape[j][i] = 0;
    for (j = 0; j < MAX_TYPES; j++)
    {
      for (polyomino = 0; polyomino < MAX_START_POLYOMINOES; polyomino++)
      {
        tris[sq].mode[NODIAG].start[polyomino][j] = 0;
        tris[sq].mode[DIAGONAL].start[polyomino][j] = 0;
      }
      start_counter[sq][NODIAG][j] = 0;
      start_counter[sq][DIAGONAL][j] = 0;
      tris[sq].mode[NODIAG].number[NOMIX][j] = 0;
      tris[sq].mode[DIAGONAL].number[NOMIX][j] = 0;
      tris[sq].mode[NODIAG].number[MIXED][j] = 0;
      tris[sq].mode[DIAGONAL].number[MIXED][j] = 0;
    }
    tris[sq].mode[NODIAG].turn_style = NONE;
    tris[sq].mode[DIAGONAL].turn_style = NONE;
  }
    c = readChar(&lc, &cc);
    while (lc < sizeof(polyomino_data)/sizeof(polyomino_data[0]) - 1)
    {
      if (c == '+')
      {
        k = readInt(&lc, &cc, &c);
        sq = k - MIN_SQUARES;
        for (i = 0; i < MAX_TYPES; i++)
        {
       	  k = readInt(&lc, &cc, &c);
          tris[sq].mode[NODIAG].number[NOMIX][i] = k;
        }
       	k = readInt(&lc, &cc, &c);
        tris[sq].mode[NODIAG].turn_style = k;
       	k = readInt(&lc, &cc, &c);
        tris[sq].diagonal_switch = k;
        if (tris[sq].diagonal_switch == True)
        {
          for (i = 0; i < MAX_TYPES; i++)
          {
       	    k = readInt(&lc, &cc, &c);
            tris[sq].mode[DIAGONAL].number[NOMIX][i] = k;
          }
       	  k = readInt(&lc, &cc, &c);
          tris[sq].mode[DIAGONAL].turn_style = k;
        }
        c = readChar(&lc, &cc);
      }
      if (c == '*')
      {
       	size = readInt(&lc, &cc, &c);
        c = readChar(&lc, &cc);
      }
#if 0
      if (c == '~') /* Useful for debugging Things */
      {
       	toss = readInt(&lc, &cc, &c);
      }
#endif
      if (c == '#')
      {
       	k = readInt(&lc, &cc, &c);
        sq = k - MIN_SQUARES;
       	n = readInt(&lc, &cc, &c);
        if (tris[sq].diagonal_switch == True)
       	  n = readInt(&lc, &cc, &c);
#if 0
        for (polyomino = 0; polyomino <= toss; polyomino++)
          while ((c = readChar(&lc, &cc) && c != '\n');
#endif
        for (polyomino = 0; polyomino < n - toss; polyomino++)
        {
          sum = polyomino + counter[sq];
            /* This is only there to "read" input file */
       	  k = readInt(&lc, &cc, &c);
       	  k = readInt(&lc, &cc, &c);
          tris[sq].polyomino[sum].rotation =
            k + counter[sq] - toss;
       	  k = readInt(&lc, &cc, &c);
          tris[sq].polyomino[sum].reflection =
            k + counter[sq] - toss;
          for (game = JUMPIN; game <= GRADUAL; game++)
          {
       	    height = readInt(&lc, &cc, &c);
            if (!gradualAppear && game == JUMPIN)
              tris[sq].polyomino[sum].start_height = height;
            else if (gradualAppear && game == GRADUAL)
              tris[sq].polyomino[sum].start_height = height;
            for (diag = NODIAG; diag <= tris[sq].diagonal_switch; diag++)
            {
       	      start = readInt(&lc, &cc, &c);
              if (game == JUMPIN)
              {
                if (sq == 0 ||
                    tris[sq - 1].mode[diag].turn_style == NONE)
                {
                  i = start_counter[sq][diag][NONE];
                  tris[sq].mode[diag].start[i][NONE] = sum;
                  start_counter[sq][diag][NONE]++;
                }
                if ((sq == 0 ||
                     tris[sq - 1].mode[diag].turn_style < ALL) &&
                    start != NONE)
                {
                  i = start_counter[sq][diag][NOREFL];
                  tris[sq].mode[diag].start[i][NOREFL] = sum;
                  start_counter[sq][diag][NOREFL]++;
                }
                if (start == ALL)
                {
                  i= start_counter[sq][diag][ALL];
                  tris[sq].mode[diag].start[i][ALL] = sum;
                  start_counter[sq][diag][ALL]++;
                }
              }
            }
          }
	  tris[sq].polyomino[sum].size = size;
          for (j = 0; j < size; j++)
            for (i = 0; i < size; i++)
            {
       	      k = readInt(&lc, &cc, &c);
              tris[sq].polyomino[sum].shape[j][i] = k;
            }
        }
        counter[sq] += n - toss;
        toss = 0;
        c = readChar(&lc, &cc);
      }
    }
    for (i = 0; i <= MAX_SQUARES - MIN_SQUARES; i++)
      for (j = 0; j < MAX_TYPES; j++)
        for (k = 0; k <= i; k++)
        {
          tris[i].mode[NODIAG].number[MIXED][j] +=
            tris[k].mode[NODIAG].number[NOMIX][j];
          if (tris[i].diagonal_switch == True)
             /*since k & i are not independent*/
            tris[i].mode[DIAGONAL].number[MIXED][j] +=
              tris[k].mode[DIAGONAL].number[NOMIX][j];
        }
}
