/* -*- Mode: C; tab-width: 4 -*- */
/* tetris --- autoplaying tetris game */

#if 0
static const char sccsid[] = "@(#)tetris.c	5.01 2000/12/19 xlockmore";

#endif

/*-
 * Copyright (c) 1998 by Jouk Jansen <joukj AT hrem.nano.tudelft.nl>
 *
 * 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.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * The author should like to be notified if changes have been made to the
 * routine.  Response will only be guaranteed when a VMS version of the
 * program is available.
 *
 * An autoplaying tetris mode for xlockmore
 * David Bagley changed much of it to look more like altetris code...
 * which a significant portion belongs to Q. Alex Zhao.
 *
 * Copyright (c) 1992 - 95    Q. Alex Zhao, azhao AT 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.
 *
 * Todo list:
 *  -Add option for the level of thinking the computer should perform
 *     (i.e. dummy = do nothing ..... genius = do always the best move
 *     possible)
 *  -Get welltris option to work better.
 *     draw more sophisticated squares (i.e. something like the bitmaps
 *       on tetris)
 *
 * Revision History:
 * 01-Dec-2000: Lifted code from polyominoes for variable size pieces
 *              Copyright (c) 2000 by Stephen Montgomery-Smith
 * 01-Nov-2000: Allocation checks
 * 02-Aug-2000: alwelltris trackmouse implemented
 * 27-Jul-2000: alwelltris all but trackmouse
 * 22-Jun-2000: trackmouse added, row clearing fixed, bonus pieces,
 * 01-Nov-1998: altetris additions by David Bagley
 * 01-Oct-1998: Created
 */

#ifdef STANDALONE
#define MODE_tetris
#define DEFAULTS "*delay: 600000 \n" \
	"*count: -500 \n" \
	"*cycles: 200 \n" \
	"*size: 0 \n" \
	"*ncolors: 200 \n" \
	"*fullrandom: True \n" \
	"*verbose: False \n" \

# define free_tetris 0
# define reshape_tetris 0
# define tetris_handle_event 0
#include "xlockmore.h"    /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h"    /* in xlockmore distribution */
#include "color.h"
#endif /* STANDALONE */

#ifdef MODE_tetris

#define DEF_BONUS "False"
#define DEF_CYCLE "True"
#define DEF_PLAIN "False"
#define DEF_TRACKMOUSE  "False"
#define DEF_WELL "False"

static Bool bonus;
static Bool cycle_p;
static Bool plain;
static Bool trackmouse;
static Bool well;

#define INTRAND(min,max) (NRAND((max+1)-(min))+(min))

static XrmOptionDescRec opts[] =
{
  {(char *) "-bonus", (char *) ".tetris.bonus", XrmoptionNoArg, (caddr_t) "on"},
  {(char *) "+bonus", (char *) ".tetris.bonus", XrmoptionNoArg, (caddr_t) "off"},
  {(char *) "-cycle", (char *) ".tetris.cycle", XrmoptionNoArg, (caddr_t) "on"},
  {(char *) "+cycle", (char *) ".tetris.cycle", XrmoptionNoArg, (caddr_t) "off"},
  {(char *) "-plain", (char *) ".tetris.plain", XrmoptionNoArg, (caddr_t) "on"},
  {(char *) "+plain", (char *) ".tetris.plain", XrmoptionNoArg, (caddr_t) "off"},
#ifndef DISABLE_INTERACTIVE
  {(char *) "-trackmouse", (char *) ".tetris.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
  {(char *) "+trackmouse", (char *) ".tetris.trackmouse", XrmoptionNoArg, (caddr_t) "off"},
#endif
  {(char *) "-well", (char *) ".tetris.well", XrmoptionNoArg, (caddr_t) "on"},
  {(char *) "+well", (char *) ".tetris.well", XrmoptionNoArg, (caddr_t) "off"}
};

static argtype vars[] =
{
  {(void *) & bonus, (char *) "bonus", (char *) "Bonus", (char *) DEF_BONUS, t_Bool},
  {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE, t_Bool},
  {(void *) & plain, (char *) "plain", (char *) "Plain", (char *) DEF_PLAIN, t_Bool},
#ifndef DISABLE_INTERACTIVE
  {(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool},
#endif
  {(void *) & well, (char *) "well", (char *) "Well", (char *) DEF_WELL, t_Bool}
};
static OptionStruct desc[] =
{
  {(char *) "-/+bonus", (char *) "turn on/off larger bonus pieces"},
  {(char *) "-/+cycle", (char *) "turn on/off colour cycling"},
  {(char *) "-/+plain", (char *) "turn on/off plain pieces"},
#ifndef DISABLE_INTERACTIVE
  {(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"},
#endif
  {(char *) "-/+well", (char *) "turn on/off the welltris mode"}
};

ENTRYPOINT ModeSpecOpt tetris_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};

#ifdef USE_MODULES
ModStruct   tetris_description =
{"tetris", "init_tetris", "draw_tetris", "release_tetris",
 "refresh_tetris", "change_tetris", (char *) NULL, &tetris_opts,
 600000, -40, 200, -100, 64, 1.0, "",
 "Shows an autoplaying tetris game", 0, NULL};

#endif

#include "bitmaps/gray1.xbm"

#define MINGRIDSIZE 10
#define MAXPIXELSIZE 12
#define MINSIZE 6
#define MAX_SIDES 4

#define NUM_FLASHES 16
#define BITMAPS 256

#define DELTA 1
#define WELL_WIDTH 8
#define WELL_DEPTH 12
#define WELL_PERIMETER (MAX_SIDES*WELL_WIDTH)
#define BASE_WIDTH (WELL_WIDTH*tp->xs+DELTA)
#define ENDPT (3*BASE_WIDTH)

#define THRESHOLD(x) ((x+1)*10)
#define EMPTY (-1)

typedef enum {FALL, DROP, MOVE_LEFT, MOVE_RIGHT, ROTATE, REFLECT} move_t;

typedef struct {
  int  squares, polyomino_number;
  int  xpos, ypos;
  int  size, color_number;
  int  random_rotation;
  int  random_reflection;  /* not used */
  long  random_number;
} thing_t;

typedef struct {
  int  rotation;
  int  reflection;  /* not used */
  int  leadingEmptyWidth, leadingEmptyHeight;
  int  *shape;
  int  size;
} Polyominoes;

typedef struct {
  int  number;
  int  *start;
} Mode;

typedef struct {
  Polyominoes *polyomino;
  Mode  mode;
} Polytris;

static Polytris polytris[2];

typedef struct {
  int pmid, cid;
} fieldstruct;

typedef struct {
  Bool   painted;
  int   object, orient;
  GC   gc;
  Colormap cmap;
  Bool   cycle_p, mono_p, no_colors, use3D;
  unsigned long blackpixel, whitepixel, fg, bg;
  int   direction;
  unsigned long colour;
  int   p_type;
  int   sidemoves;

  int   xb, yb;
  int   xs, ys;
  int   width, height;
  int   pixelmode;
  XImage *images[BITMAPS];
  XColor     *colors;
  int   ncolors;
  int   ncols, nrows;
  int   rows, level;
  Bool    bonus, bonusNow;
  thing_t     curPolyomino;

  /* tetris */
  fieldstruct *field;

  /* welltris */
  Bool well;
  int frozen_wall[MAX_SIDES];
  fieldstruct wall[WELL_DEPTH+WELL_WIDTH][WELL_PERIMETER];
  fieldstruct base[WELL_WIDTH][WELL_WIDTH];
  Pixmap graypix;
} trisstruct;

#define ARR(i,j) (((i)<0||(i)>=tp->curPolyomino.size||\
(j)<0||(j)>=tp->curPolyomino.size)?-2:\
(polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].shape[(j) * tp->curPolyomino.size + (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(tp->ys))
#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-1)%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-1)%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 ((tp->ys/45)+1)         /* 1/2 of gap between pentominoes. */
#define T ((tp->ys<=12)?1:(G*2))  /* Thickness of walls of pentominoes. */
#define R ((tp->ys<=12)?1:(G*6))  /* Amount of rounding. */
#define RT ((tp->ys<=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,

#if 0
/* 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,
#endif

 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 trisstruct *triss = (trisstruct *) NULL;

/* Setup: Normal, Bonus */
static int area_kinds[] = {3, 3};
static int omino_squares[] = {4, 5};
static int start_ominoes[] = {7, 18};
static int max_ominoes[] = {19, 63};

static int
ominos[] =
{
  /* Side of smallest square that contains shape */
  /* Number of orientations */
  /* Position, Next Position, (not used yet),
     number_down, used_for_start?
     Polyomino (side^2 values) */

  /* Normal pieces  4 squares */
  2,
  1,
  0, 0, 0,   2,   6, 3,  12, 9,

  3,
  16,
  0, 1, 4,     2,   0, 0, 0,  6, 5, 1,   8, 0, 0,
  1, 2, 7,     0,   0, 2, 0,  0, 10, 0,  0, 12, 1,
  2, 3, 6,     0,   0, 0, 0,  0, 0, 2,   4, 5, 9,
  3, 0, 5,     0,   0, 4, 3,  0, 0, 10,  0, 0, 8,
  4, 5, 0,     1,   0, 0, 0,  4, 5, 3,   0, 0, 8,
  5, 6, 3,     0,   0, 6, 1,  0, 10, 0,  0, 8, 0,
  6, 7, 2,     0,   0, 0, 0,  2, 0, 0,   12, 5, 1,
  7, 4, 1,     0,   0, 0, 2,  0, 0, 10,  0, 4, 9,
  8, 9, 8,     2,   0, 0, 0,  4, 7, 1,   0, 8, 0,
  9, 10, 11,   0,   0, 2, 0,  0, 14, 1,  0, 8, 0,
  10, 11, 10,  0,   0, 0, 0,  0, 2, 0,   4, 13, 1,
  11, 8, 9,    0,   0, 0, 2,  0, 4, 11,  0, 0, 8,
  12, 13, 14,  2,   0, 0, 0,  4, 3, 0,   0, 12, 1,
  13, 12, 15,  0,   0, 0, 2,  0, 6, 9,   0, 8, 0,
  14, 15, 12,  1,   0, 0, 0,  0, 6, 1,   4, 9, 0,
  15, 14, 13,  0,   0, 2, 0,  0, 12, 3,  0, 0, 8,

  4,
  2,
  0, 1, 0,  2,   0, 0, 0, 0,  0, 0, 0, 0,   4, 5, 5, 1,   0, 0, 0, 0,
  1, 0, 1,  0,   0, 0, 2, 0,  0, 0, 10, 0,  0, 0, 10, 0,  0, 0, 8, 0,

  /* Bonus pieces  5 squares */
  3,
  37,
  0, 1, 0,     2,   0, 0, 0,  6, 5, 3,    8, 0, 8,
  1, 2, 3,     0,   0, 6, 1,  0, 10, 0,   0, 12, 1,
  2, 3, 2,     0,   0, 0, 0,  2, 0, 2,    12, 5, 9,
  3, 0, 1,     0,   0, 4, 3,  0, 0, 10,   0, 4, 9,
  4, 5, 8,     2,   0, 0, 0,  6, 7, 1,    12, 9, 0,
  5, 6, 11,    0,   0, 2, 0,  0, 14, 3,   0, 12, 9,
  6, 7, 10,    0,   0, 0, 0,  0, 6, 3,    4, 13, 9,
  7, 4, 9,     0,   0, 6, 3,  0, 12, 11,  0, 0, 8,
  8, 9, 4,     1,   0, 0, 0,  4, 7, 3,    0, 12, 9,
  9, 10, 7,    0,   0, 6, 3,  0, 14, 9,   0, 8, 0,
  10, 11, 6,   0,   0, 0, 0,  6, 3, 0,    12, 13, 1,
  11, 8, 5,    0,   0, 0, 2,  0, 6, 11,   0, 12, 9,
  12, 13, 12,  2,   4, 7, 1,  0, 10, 0,   0, 8, 0,
  13, 14, 15,  0,   2, 0, 0,  14, 5, 1,   8, 0, 0,
  14, 15, 14,  0,   0, 2, 0,  0, 10, 0,   4, 13, 1,
  15, 12, 13,  0,   0, 0, 2,  4,  5, 11,  0, 0, 8,
  16, 17, 19,  2,   6, 5, 1,  10, 0, 0,   8, 0, 0,
  17, 18, 18,  0,   2, 0, 0,  10, 0, 0,   12, 5, 1,
  18, 19, 17,  0,   0, 0, 2,  0, 0, 10,   4, 5, 9,
  19, 16, 16,  0,   4, 5, 3,  0, 0, 10,   0, 0, 8,
  20, 21, 22,  2,   2, 0, 0,  12, 5, 3,   0, 0, 8,
  21, 20, 23,  0,   0, 6, 1,  0, 10, 0,   4, 9, 0,
  22, 23, 20,  1,   0, 0, 2,  6, 5, 9,    8, 0, 0,
  23, 22, 21,  0,   4, 3, 0,  0, 10, 0,   0, 12, 1,
  24, 25, 25,  2,   4, 3, 0,  0, 12, 3,   0, 0, 8,
  25, 26, 24,  0,   0, 6, 1,  6, 9, 0,    8, 0, 0,
  26, 27, 27,  0,   2, 0, 0,  12, 3, 0,   0, 12, 1,
  27, 24, 26,  0,   0, 0, 2,  0, 6, 9,    4, 9, 0,
  28, 29, 32,  2,   0, 2, 0,  6, 13, 1,   8, 0, 0,
  29, 30, 35,  0,   0, 2, 0,  4, 11, 0,   0, 12, 1,
  30, 31, 34,  0,   0, 0, 2,  4, 7, 9,    0, 8, 0,
  31, 28, 33,  0,   4, 3, 0,  0, 14, 1,   0, 8, 0,
  32, 33, 28,  1,   0, 2, 0,  4, 13, 3,   0, 0, 8,
  33, 34, 31,  0,   0, 6, 1,  4, 11, 0,   0, 8, 0,
  34, 35, 30,  0,   2, 0, 0,  12, 7, 1,   0, 8, 0,
  35, 32, 29,  0,   0, 2, 0,  0, 14, 1,   4, 9, 0,
  36, 36, 36,  2,   0, 2, 0,  4, 15, 1,   0, 8, 0,

  4,
  24,
  0, 1, 4,     2,   0, 0, 0, 0,  6, 5, 5, 1,   8, 0, 0, 0,   0, 0, 0, 0,
  1, 2, 7,     0,   0, 2, 0, 0,  0, 10, 0, 0,  0, 10, 0, 0,  0, 12, 1, 0,
  2, 3, 6,     0,   0, 0, 0, 0,  0, 0, 0, 2,   4, 5, 5, 9,   0, 0, 0, 0,
  3, 0, 5,     0,   0, 4, 3, 0,  0, 0, 10, 0,  0, 0, 10, 0,  0, 0, 8, 0,
  4, 5, 0,     1,   0, 0, 0, 0,  4, 5, 5, 3,   0, 0, 0, 8,   0, 0, 0, 0,
  5, 6, 3,     0,   0, 6, 1, 0,  0, 10, 0, 0,  0, 10, 0, 0,  0, 8, 0, 0,
  6, 7, 2,     0,   0, 0, 0, 0,  2, 0, 0, 0,   12, 5, 5, 1,  0, 0, 0, 0,
  7, 4, 1,     0,   0, 0, 2, 0,  0, 0, 10, 0,  0, 0, 10, 0,  0, 4, 9, 0,
  8, 9, 12,    2,   0, 0, 0, 0,  4, 7, 5, 1,   0, 8, 0, 0,   0, 0, 0, 0,
  9, 10, 15,   0,   0, 2, 0, 0,  0, 10, 0, 0,  0, 14, 1, 0,  0, 8, 0, 0,
  10, 11, 14,  0,   0, 0, 0, 0,  0, 0, 2, 0,   4, 5, 13, 1,  0, 0, 0, 0,
  11, 8, 13,   0,   0, 0, 2, 0,  0, 4, 11, 0,  0, 0, 10, 0,  0, 0, 8, 0,
  12, 13, 8,   1,   0, 0, 0, 0,  4, 5, 7, 1,   0, 0, 8, 0,   0, 0, 0, 0,
  13, 14, 11,  0,   0, 2, 0, 0,  0, 14, 1, 0,  0, 10, 0, 0,  0, 8, 0, 0,
  14, 15, 10,  0,   0, 0, 0, 0,  0, 2, 0, 0,   4, 13, 5, 1,  0, 0, 0, 0,
  15, 12, 9,   0,   0, 0, 2, 0,  0, 0, 10, 0,  0, 4, 11, 0,  0, 0, 8, 0,
  16, 17, 20,  2,   0, 0, 0, 0,  4, 5, 3, 0,   0, 0, 12, 1,  0, 0, 0, 0,
  17, 18, 23,  0,   0, 0, 2, 0,  0, 6, 9, 0,   0, 10, 0, 0,  0, 8, 0, 0,
  18, 19, 22,  0,   0, 0, 0, 0,  4, 3, 0, 0,   0, 12, 5, 1,  0, 0, 0, 0,
  19, 16, 21,  0,   0, 0, 2, 0,  0, 0, 10, 0,  0, 6, 9, 0,   0, 8, 0, 0,
  20, 21, 16,  1,   0, 0, 0, 0,  0, 6, 5, 1,   4, 9, 0, 0,   0, 0, 0, 0,
  21, 22, 19,  0,   0, 2, 0, 0,  0, 10, 0, 0,  0, 12, 3, 0,  0, 0, 8, 0,
  22, 23, 18,  0,   0, 0, 0, 0,  0, 0, 6, 1,   4, 5, 9, 0,   0, 0, 0, 0,
  23, 20, 17,  0,   0, 2, 0, 0,  0, 12, 3, 0,  0, 0, 10, 0,  0, 0, 8, 0,

  5,
  2,
  0, 1, 0,  2,   0, 0, 0, 0, 0,  0, 0, 0, 0, 0,   4, 5, 5, 5, 1,   0, 0, 0, 0, 0,   0, 0, 0, 0, 0,
  1, 0, 1,  0,   0, 0, 2, 0, 0,  0, 0, 10, 0, 0,  0, 0, 10, 0, 0,  0, 0, 10, 0, 0,  0, 0, 8, 0, 0,
};

static int
image_needed(int n) {
  int i;

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

static void
leadingEmptyWidth(Polyominoes * polyo)
{
  int i, j;

  for (i = 0; i < polyo->size; i++) {
    for (j = 0; j < polyo->size; j++) {
      if (polyo->shape[j * polyo->size + i]) {
        polyo->leadingEmptyWidth = i;
        return;
      }
    }
  }
  polyo->leadingEmptyWidth = polyo->size;
}

static void
leadingEmptyHeight(Polyominoes * polyo)
{
  int i, j;

  for (j = 0; j < polyo->size; j++) {
    for (i = 0; i < polyo->size; i++) {
      if (polyo->shape[j * polyo->size + i]) {
        polyo->leadingEmptyHeight = j;
        return;
      }
    }
  }
  polyo->leadingEmptyHeight = polyo->size;
}

static Bool
readPolyominoes(void)
{
  int   size, n, polyindex = 0, sum = 0;
  int   counter, start_counter;
  int   polyomino, kinds, i, j, bonustype;

  for (bonustype = 0; bonustype < 2; bonustype++) {
    counter = 0, start_counter = 0;
    polytris[bonustype].mode.number = start_ominoes[bonustype];
    for (kinds = 0; kinds < area_kinds[bonustype]; kinds++) {
      size = ominos[polyindex++];
      if (size > omino_squares[bonustype]) {
        (void) printf("tetris corruption: size %d\n", size);
        return False;
      }
      n = ominos[polyindex++];
      if (sum > max_ominoes[bonustype]) {
        (void) printf("tetris corruption: n %d\n", n);
        return False;
      }
      for (polyomino = 0; polyomino < n; polyomino++) {
        if (polyomino + counter < max_ominoes[bonustype]) {
          sum = polyomino + counter;
        } else {
          (void) printf("tetris corruption: bonustype %d, polyomino %d, counter %d\n",
            bonustype, polyomino, counter);
          return False;
        }
        if (polyomino != ominos[polyindex]) {
          (void) printf("tetris corruption: polyomino %d, ominos[polyindex] %d\n",
            polyomino, ominos[polyindex]);
          return False;
        }
        polyindex++;  /* This is only there to "read" it */
        polytris[bonustype].polyomino[sum].rotation =
          ominos[polyindex++] + counter;
        polytris[bonustype].polyomino[sum].reflection =
          ominos[polyindex++] + counter; /* not used */

        if (ominos[polyindex++]) {
          if (start_counter < start_ominoes[bonustype]) {
            polytris[bonustype].mode.start[start_counter++] = sum;
          } else {
            (void) printf("tetris corruption: bonustype %d, polyomino %d, start_counter %d\n",
              bonustype, polyomino, start_counter);
            return False;
          }
        }
        polytris[bonustype].polyomino[sum].size = size;
        if ((polytris[bonustype].polyomino[sum].shape = (int *) malloc(size *
            size * sizeof(int))) == NULL) {
          (void) printf("tetris out of memory\n");
          return False;
	}
        for (j = 0; j < size; j++) {
          for (i = 0; i < size; i++) {
            polytris[bonustype].polyomino[sum].shape[j * size + i] =
        ominos[polyindex++];
          }
        }
        leadingEmptyWidth(&(polytris[bonustype].polyomino[sum]));
        leadingEmptyHeight(&(polytris[bonustype].polyomino[sum]));
      }
      counter += n;
    }
  }
  return True;
}

#ifdef DEBUG
static void
writePolyominoes(trisstruct * tp)
{
  int   size = 0;
  int   polyomino, i, j, bonustype;

  for (bonustype = 0; bonustype < 2; bonustype++) {
    for (polyomino = 0; polyomino < max_ominoes[bonustype]; polyomino++) {
      (void) printf("polyomino %d, rotation %d, reflection %d, size %d\n",
        polyomino, polytris[bonustype].polyomino[polyomino].rotation,
        polytris[bonustype].polyomino[polyomino].reflection,
        polytris[bonustype].polyomino[polyomino].size);
      size = polytris[bonustype].polyomino[polyomino].size;
      for (j = 0; j < size; j++) {
        (void) printf(" ");
        for (i = 0; i < size; i++) {
          (void) printf(" %d", polytris[bonustype].polyomino[polyomino].shape[j * size + i]);
        }
      }
      (void) printf("\n");
    }
    (void) printf("Starts:");
    for (polyomino = 0; polyomino < start_ominoes[bonustype]; polyomino++) {
      (void) printf(" %d", polytris[bonustype].mode.start[polyomino]);
    }
    (void) printf("\n");
  }
}
#endif

static void
setColour(ModeInfo * mi, int cid /* , int exor */)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  long colour;

  if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
    if (tp->ncolors >= start_ominoes[(int) tp->bonusNow] + 2)
      colour = cid + 2;
    else
      colour = 1; /* Just in case */
    XSetBackground(display, tp->gc, tp->blackpixel);
    XSetForeground(display, tp->gc, tp->colors[colour].pixel);
  } else {
    if (MI_NPIXELS(mi) > start_ominoes[(int) tp->bonusNow])
      colour = MI_PIXEL(mi, cid * MI_NPIXELS(mi) / start_ominoes[(int) tp->bonusNow]);
    else
      colour = MI_WHITE_PIXEL(mi);
    XSetForeground(display, tp->gc, colour);
    XSetBackground(display, tp->gc, MI_BLACK_PIXEL(mi));
  }
}

static void
drawSquare(ModeInfo * mi, int pmid, int cid, int col, int row)
{
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  setColour(mi, cid /* , 0 */);
  if (tp->pixelmode)
    XFillRectangle(display, window, tp->gc,
      tp->xb + col * tp->xs, tp->yb + row * tp->ys, tp->xs, tp->ys);
  else {
    if (pmid < 0 || pmid >= 256)
      (void) printf("pmid = %d\n", pmid);
/*-
 * PURIFY on SunOS4 and on Solaris 2 reports a 120 byte memory leak on
 * the next line */
    (void) XPutImage(display, window, tp->gc, tp->images[pmid], 0, 0,
      tp->xb + col * tp->xs, tp->yb + row * tp->ys,
      tp->xs, tp->ys);
  }
}

static void
xorSquare(ModeInfo * mi, int i, int j)
{
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  XSetFunction(display, tp->gc, GXxor);
  XSetBackground(display, tp->gc, 0);
  XSetForeground(display, tp->gc, 0xFFFFFF);
  XFillRectangle(display, window, tp->gc,
    tp->xb + i * tp->xs, tp->yb + j * tp->ys,
    tp->xs, tp->ys);
  XSetFunction(display, tp->gc, GXcopy);
  XSetBackground(display, tp->gc, tp->blackpixel);
  XSetForeground(display, tp->gc, tp->whitepixel);
}

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

  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;
  XFillPolygon(display, window, gc, trapazoid_list, MAX_SIDES,
    Convex, CoordModePrevious);
}

static void
drawWallSquare(ModeInfo * mi, GC gc, int i, int j)
{
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int ulx, uly, lrx, lry, base_x, plateau_x, w, offsetx, offsety;

  offsetx = tp->width / 2 - 3 * (WELL_WIDTH * tp->xs) / 2 - DELTA;
  offsety = tp->height / 2 - 3 * (WELL_WIDTH * tp->xs) / 2 - DELTA;
  w = i / WELL_WIDTH;
  i -= (w * WELL_WIDTH);
  ulx = 3 * BASE_WIDTH / 2 + (i - (WELL_WIDTH / 2)) *
    (2 * tp->xs * (WELL_DEPTH - j) / WELL_DEPTH + tp->xs) +
    2 * DELTA;
  uly = j * WELL_WIDTH * tp->xs / WELL_DEPTH + 2 * DELTA;
  lrx = 3 * BASE_WIDTH / 2 + (i + 1 - (WELL_WIDTH / 2)) *
    (2 * tp->xs * (WELL_DEPTH - j - 1) / WELL_DEPTH + tp->xs) +
    DELTA * (i / 4) - 3 * DELTA;
  lry = (j + 1) * WELL_WIDTH * tp->xs / WELL_DEPTH - DELTA;
  base_x = tp->xs + (WELL_DEPTH - j) * 2 * tp->xs / WELL_DEPTH -
    5 * DELTA;
  plateau_x = tp->xs + (WELL_DEPTH - j - 1) * 2 *
    tp->xs / WELL_DEPTH - 5 * DELTA;
  {
    XSetFillStyle(display, gc, FillOpaqueStippled);
    XSetStipple(display, gc, tp->graypix);
  }
  switch (w) {
  case 0:
    XFillTrapazoid(display, window, gc,
      ulx + offsetx, uly + offsety, base_x, 0,
      lrx + offsetx, lry + offsety, plateau_x, 0);
    break;
  case 1:
    XFillTrapazoid(display, window, gc,
      ENDPT - uly + offsetx, ulx + offsety, 0, base_x,
      ENDPT - lry + offsetx, lrx + offsety, 0, plateau_x);
    break;
  case 2:
    XFillTrapazoid(display, window, gc,
      ENDPT - 1 - ulx + offsetx, ENDPT - uly + offsety, -base_x, 0,
      ENDPT - 1 - lrx + offsetx, ENDPT - lry + offsety, -plateau_x, 0);
    break;
  case 3:
    XFillTrapazoid(display, window, gc,
      uly + offsetx, ENDPT - 1 - ulx + offsety, 0, -base_x,
      lry + offsetx, ENDPT - 1 - lrx + offsety, 0, -plateau_x);
    break;
  default:
    (void) fprintf (stderr, "trapazoid kinds range 0-3, got %d.\n", w);
  }
  {
    XSetFillStyle(display, gc, FillSolid);
  }
}

static void
clearTrapazoid(ModeInfo * mi, int i, int j)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  XSetForeground(display, tp->gc, tp->blackpixel);
  drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
}

static void
drawTrapazoid(ModeInfo * mi, /* int pmid, */ int cid, int i, int j)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  setColour(mi, cid /* , 0 */ );
  drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
}

static void
freezeTrapazoid(ModeInfo * mi, int i, int j)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  XSetFunction(display, tp->gc, GXxor);
  XSetBackground(display, tp->gc, 0);
  XSetForeground(display, tp->gc, 0xFFFFFF);
  drawWallSquare(mi, tp->gc, i, j); /* pmid not used yet */
  XSetFunction(display, tp->gc, GXcopy);
  XSetBackground(display, tp->gc, tp->blackpixel);
  XSetForeground(display, tp->gc, tp->whitepixel);
}

static int
checkWall(int pmid, int base_i, int 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 == WELL_WIDTH - 1)
    pmid = CHECKRIGHT((unsigned int) pmid);
  if (base_j == WELL_WIDTH - 1)
    pmid = CHECKDOWN((unsigned int) pmid);
  return pmid;
}

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

static int
checkBottom(int pmid, int i, int j)
{
  if (pmid < 0 || j != WELL_DEPTH - 1)
    return pmid;
  switch (i / WELL_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;
}

/* Wall number ... imagine a 4 hour clock...
     0
    3+1
     2
 */
static void
wall_to_base(int *base_x, int *base_y, int wall_x, int wall_y)
{
  switch (wall_x / WELL_WIDTH)
  {
  case 0:
    *base_x = wall_x;
    *base_y = wall_y - WELL_DEPTH;
    break;
  case 1:
    *base_x = WELL_WIDTH - 1 + WELL_DEPTH - wall_y;
    *base_y = wall_x - WELL_WIDTH;
    break;
  case 2:
    *base_x = WELL_PERIMETER - 1 - WELL_WIDTH - wall_x;
    *base_y = WELL_WIDTH - 1 + WELL_DEPTH - wall_y;
    break;
  case 3:
    *base_x = wall_y - WELL_DEPTH;
    *base_y = WELL_PERIMETER - 1 - wall_x;
    break;
  default:
   (void) fprintf (stderr, "wall_to_base kinds range 0-3, got %d.\n",
               wall_x / WELL_WIDTH);
  }
}

static void
dropWall(ModeInfo *mi, int w)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int i, j, k, l, lines, base_x, base_y;
  Bool fits;

  lines = 0;
  for (j = WELL_DEPTH - 1; j >= 0 && lines == 0; j--)
    for (i = 0; i < WELL_WIDTH; i++)
      if (tp->wall[j][w * WELL_WIDTH + i].pmid != EMPTY)
        lines = WELL_DEPTH - j;
  if (lines > 0) {
    fits = True;
    j = 0;
    while (j < (WELL_WIDTH / 2 + lines - 1) && fits) {
      j++;
      for (l = WELL_DEPTH - j; l < WELL_DEPTH; l++)
        for (i = 0; i < WELL_WIDTH; i++)
          if (tp->wall[l][w * WELL_WIDTH + i].pmid != EMPTY) {
            wall_to_base(&base_x, &base_y,
              w * WELL_WIDTH + i, l + j);
            if (tp->base[base_y][base_x].pmid != EMPTY)
              fits = False;
          }
    }
    if (!fits)
      j--;
    if (j > 0)
    {
      for (l = WELL_DEPTH - 1; l >= 0; l--)
        for (i = 0; i < WELL_WIDTH; i++)
          if (tp->wall[l][w * WELL_WIDTH + i].pmid != EMPTY) {
            k = w * WELL_WIDTH + i;
            if (l + j >= WELL_DEPTH) {
              wall_to_base(&base_x, &base_y, k, l + j);
              tp->base[base_y][base_x] = tp->wall[l][k];
              tp->base[base_y][base_x].pmid =
                checkWall(tp->base[base_y][base_x].pmid, base_x, base_y);
              tp->wall[l][k].pmid = EMPTY;
              clearTrapazoid(mi, k, l);
              drawSquare(mi, tp->base[base_y][base_x].pmid,
                tp->base[base_y][base_x].cid, base_x, base_y);
            } else {
              tp->wall[l + j][k] = tp->wall[l][k];
              tp->wall[l + j][k].pmid =
                checkBottom(tp->wall[l + j][k].pmid, k, l + j);
              tp->wall[l][k].pmid = EMPTY;
              clearTrapazoid(mi, k, l);
              drawTrapazoid(mi, /* tp->wall[l][k].pmid, */ tp->wall[l][k].cid,
                k, l + j);
            }
            XFlush(display);
          }
    }
  }
}

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

  for (j = 0; j < WELL_DEPTH; j++)
    for (i = 0; i < WELL_WIDTH; i++) {
      freezeTrapazoid(mi, w * WELL_WIDTH + i, j);
    }
  XFlush(MI_DISPLAY(mi));
}

static Bool
allFrozen(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int w;

  for (w = 0; w < MAX_SIDES; w++)
    if (tp->frozen_wall[w] == MAX_SIDES) {
      freezeWall(mi, w);
      XFlush(MI_DISPLAY(mi));
    }
  for (w = 0; w < MAX_SIDES; w++)
    if (!tp->frozen_wall[w])
      return False;
  return True;
}

static void
checkFreeze(ModeInfo *mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int w;

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

static int
curPixmap(trisstruct *tp, int i, int j)
{
  int pmid = 0;

  if (ARR(i,j) != ARR(i-1,j))
    pmid |= LEFT;
  if (ARR(i,j) != ARR(i,j+1))
    pmid |= DOWN;
  if (ARR(i,j) != ARR(i+1,j))
    pmid |= RIGHT;
  if (ARR(i,j) != ARR(i,j-1))
    pmid |= UP;
  if (ARR(i,j) != ARR(i-1,j+1))
    pmid |= LEFT_DOWN;
  if (ARR(i,j) != ARR(i+1,j+1))
    pmid |= RIGHT_DOWN;
  if (ARR(i,j) != ARR(i+1,j-1))
    pmid |= RIGHT_UP;
  if (ARR(i,j) != ARR(i-1,j-1))
    pmid |= LEFT_UP;
  return pmid;
}

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

  if (pmid == EMPTY)
    return EMPTY;
  wallRotation = (MAX_SIDES - i / WELL_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);
}

static void
drawBox(ModeInfo * mi, int pmid, int cid, int i, int j)
{
  if (j < WELL_DEPTH) {
    drawTrapazoid(mi, /* pmid, */ cid, i, j);
  } else {
    int base_i, base_j;

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

static void
clearSquare(ModeInfo * mi, int col, int row)
{
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  XSetForeground(display, tp->gc, tp->blackpixel);
  XFillRectangle(display, window, tp->gc,
    tp->xb + col * tp->xs, tp->yb + row * tp->ys, tp->xs, tp->ys);
}

static void
drawPolyomino(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int   i, j;

  if (tp->well) {
    int xi, yj, base_i, base_j;
    int temp_base[WELL_WIDTH][WELL_WIDTH];

    /* Initialize */
    for (i = 0; i < WELL_WIDTH; i++)
      for (j = 0; j < WELL_WIDTH; j++)
        temp_base[j][i] = 0;
    /* Find pieces and overlap them if necessary */
    for (i = 0; i < tp->curPolyomino.size; i++) {
      for (j = 0; j < tp->curPolyomino.size; j++) {
        if (polytris[(int) tp->bonusNow].polyomino
                    [tp->curPolyomino.polyomino_number].shape
                    [j * tp->curPolyomino.size + i]) {
          xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
          yj = tp->curPolyomino.ypos + j;
          if (yj >= WELL_DEPTH) {
            wall_to_base(&base_i, &base_j, xi, yj);
            xi = checkWall(rotateIndex(curPixmap(tp, i, j), 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 < tp->curPolyomino.size; i++)
        for (j = 0; j < tp->curPolyomino.size; j++)
          if (polytris[(int) tp->bonusNow].polyomino
                      [tp->curPolyomino.polyomino_number].shape
                      [j * tp->curPolyomino.size + i]) {
            xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
            yj = tp->curPolyomino.ypos + j;
            if (yj >= WELL_DEPTH) {
              wall_to_base(&base_i, &base_j, xi, yj);
              if (temp_base[base_j][base_i]) {
                drawSquare(mi, temp_base[base_j][base_i],
                  tp->curPolyomino.color_number, base_i, base_j);
                temp_base[base_j][base_i] = 0;
              }
            } else if (yj >= 0) {
              drawTrapazoid(mi, /* tp->curPolyomino.pmid, */
                tp->curPolyomino.color_number, xi, yj);
            }
          }
    }
  } else {
    for (i = 0; i < tp->curPolyomino.size; i++) {
      for (j = 0; j < tp->curPolyomino.size; j++) {
        if (polytris[(int) tp->bonusNow].polyomino
                    [tp->curPolyomino.polyomino_number].shape
                    [j * tp->curPolyomino.size + i]) {
          drawSquare(mi, curPixmap(tp, i, j),
            tp->curPolyomino.color_number,
            tp->curPolyomino.xpos + i, tp->curPolyomino.ypos + j);
        }
      }
    }
  }
}

static void
drawPolyominoDiff(ModeInfo *mi, thing_t * old)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int   i, j, ox, oy;

  if (tp->well) {
    int xi, yj, base_i, base_j;
    int temp_base[WELL_WIDTH][WELL_WIDTH];

    /* Initialize */
    for (i = 0; i < WELL_WIDTH; i++)
      for (j = 0; j < WELL_WIDTH; j++)
        temp_base[j][i] = 0;
    /* Find pieces and overlap them if necessary */
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if (polytris[(int) tp->bonusNow].polyomino
                    [tp->curPolyomino.polyomino_number].shape
                    [j * tp->curPolyomino.size + i]) {
          xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
          yj = tp->curPolyomino.ypos + j;
          if (yj >= WELL_DEPTH) {
            wall_to_base(&base_i, &base_j, xi, yj);
            xi = checkWall(rotateIndex(curPixmap(tp, i, j), 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 < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if (polytris[(int) tp->bonusNow].polyomino
                    [tp->curPolyomino.polyomino_number].shape
                    [j * tp->curPolyomino.size + i]) {
          xi = (tp->curPolyomino.xpos + i) % WELL_PERIMETER;
          yj = tp->curPolyomino.ypos + j;
          if (yj >= WELL_DEPTH) {
            wall_to_base(&base_i, &base_j, xi, yj);
            if (temp_base[base_j][base_i] > 0) {
              drawSquare(mi, temp_base[base_j][base_i],
                tp->curPolyomino.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(mi, /* tp->curPolyomino.pmid, */
                tp->curPolyomino.color_number, xi, yj);
            }
          }
    /* Erase */
    for (i = 0; i < old->size; i++)
      for (j = 0; j < old->size; j++) {
        xi = (old->xpos + i) % WELL_PERIMETER;
        yj = old->ypos + j;
        ox = (xi - tp->curPolyomino.xpos + WELL_PERIMETER) % WELL_PERIMETER;
        oy = yj - tp->curPolyomino.ypos;
        if (yj >= WELL_DEPTH) {
          wall_to_base(&base_i, &base_j, xi, yj);
          if (polytris[(int) tp->bonusNow].polyomino
                      [old->polyomino_number].shape
                      [j * old->size + i] &&
            ((ox >= tp->curPolyomino.size) || (oy < 0) ||
             (oy >= tp->curPolyomino.size) || !temp_base[base_j][base_i]))
            clearSquare(mi, base_i, base_j);
        } else if (yj >= 0) {
           if (polytris[(int) tp->bonusNow].polyomino
                       [old->polyomino_number].shape
                       [j * old->size + i] &&
             ((ox >= tp->curPolyomino.size) || (oy < 0) ||
              (oy >= tp->curPolyomino.size) || !polytris
		[(int) tp->bonusNow].polyomino
                [tp->curPolyomino.polyomino_number].shape
                [oy * tp->curPolyomino.size + ox]))
              clearTrapazoid(mi, xi, yj);
          }
        }
  } else {
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if ((tp->curPolyomino.ypos + j >= 0) &&
          polytris[(int) tp->bonusNow].polyomino
            [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
          drawSquare(mi, curPixmap(tp, i, j),
            tp->curPolyomino.color_number,
            tp->curPolyomino.xpos + i, tp->curPolyomino.ypos + j);
        }
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++) {
        ox = old->xpos + i - tp->curPolyomino.xpos;
        oy = old->ypos + j - tp->curPolyomino.ypos;
        if (polytris[(int) tp->bonusNow].polyomino[old->polyomino_number].shape[j * tp->curPolyomino.size + i] &&
          ((ox < 0) || (ox >= tp->curPolyomino.size) ||
          (oy < 0) || (oy >= tp->curPolyomino.size) ||
          !polytris[(int) tp->bonusNow].polyomino
            [tp->curPolyomino.polyomino_number].shape[oy * tp->curPolyomino.size + ox])) {
          clearSquare(mi, (old->xpos + i), (old->ypos + j));
        }
      }
  }
}

static void
redoNext(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int next_start, i;

  next_start = (int) (tp->curPolyomino.random_number % polytris[(int) tp->bonusNow].mode.number);
  tp->curPolyomino.color_number = next_start;

  tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].mode.start[next_start];
  for (i = 0; i < tp->curPolyomino.random_rotation; i++)
    tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
      [tp->curPolyomino.polyomino_number].rotation;
  tp->curPolyomino.size = polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].size;
  tp->curPolyomino.ypos = -polytris[(int) tp->bonusNow].polyomino
    [tp->curPolyomino.polyomino_number].leadingEmptyHeight;
}

static void
newPolyomino(ModeInfo * mi) {
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  tp->curPolyomino.random_number = LRAND();
  tp->curPolyomino.random_rotation = NRAND(MAX_SIDES);
#if 0
  tp->curPolyomino.random_reflection = NRAND(2);  /* Future? */
#endif
  if (tp->bonus) {
    if (trackmouse) {
      tp->bonusNow = 0;
    } else {
      /* Unlikely to finish rows, so show bonus pieces this way */
      tp->bonusNow = !NRAND(16);
    }
  }
  redoNext(mi);
  if (tp->well) {
    int w;

    /* pick a free spot on the rim, not all frozen else this is an infinite
       loop */
    /* tp->curPolyomino.xpos = (WELL_WIDTH - tp->curPolyomino.size) / 2; */
    tp->curPolyomino.xpos = NRAND(WELL_WIDTH - tp->curPolyomino.size + 1);
    do {
      w = NRAND(MAX_SIDES);
    } while (tp->frozen_wall[w]);
    tp->curPolyomino.xpos += w * WELL_WIDTH;
  } else {
    tp->curPolyomino.xpos = NRAND(tp->ncols - tp->curPolyomino.size + 1);
    /* tp->curPolyomino.xpos = (tp->ncols - tp->curPolyomino.size) / 2; */
  }
}

static void
putBox(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int   i, j;
  int   x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos, pos;

  for (i = 0; i < tp->curPolyomino.size; i++)
    for (j = 0; j < tp->curPolyomino.size; j++)
      if ((y + j >= 0) && polytris[(int) tp->bonusNow].polyomino
        [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
        if (tp->well) {
          /* Set pieces and overlap them if necessary */
          int xi, yj;

          xi = (x + i) % WELL_PERIMETER;
          yj = y + j;
          if (yj >= WELL_DEPTH) {
            int base_i, base_j;

            wall_to_base(&base_i, &base_j, xi, yj);
            xi = checkWall(rotateIndex(curPixmap(tp, i, j), xi),
              base_i, base_j);
            if (!tp->base[base_j][base_i].pmid)
              tp->base[base_j][base_i].pmid = xi;
            else
              tp->base[base_j][base_i].pmid &= xi;
            tp->base[base_j][base_i].cid = tp->curPolyomino.color_number;
          } else {
            tp->wall[yj][xi].pmid =
              checkSides(checkBottom(rotateIndex(curPixmap(tp, i,
              j), xi), xi, yj), xi);
            tp->wall[yj][xi].cid = tp->curPolyomino.color_number;
            tp->frozen_wall[xi / WELL_WIDTH] = MAX_SIDES;
          }
        } else {
          pos = (y + j) * tp->ncols + x + i;
          tp->field[pos].pmid = curPixmap(tp, i, j);
          tp->field[pos].cid = tp->curPolyomino.color_number;
        }
      }
}

static Bool
overlapping(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int       i, j;
  int       x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos;
  Bool gradualAppear = tp->well;

  if (tp->well) {
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if (polytris[(int) tp->bonusNow].polyomino
                [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
          int xi, yj;

          xi = (x + i) % WELL_PERIMETER;
          yj = y + j;
          if (yj < WELL_DEPTH) {
            if (tp->frozen_wall[xi / WELL_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) && (tp->wall[yj][xi].pmid >= 0))
                return True;
            } else {
/* This method does not allow turning polyomino to an area above screen */
              if ((yj < 0) || (tp->wall[yj][xi].pmid >= 0))
                return True;
            }
          }
          else if (yj < WELL_DEPTH + WELL_WIDTH) {
            int base_i, base_j;

            wall_to_base(&base_i, &base_j, xi, yj);
            if (tp->base[base_j][base_i].pmid >= 0)
              return True;
          }
          else
            return True;
        }
  } else {
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if (polytris[(int) tp->bonusNow].polyomino
            [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
          if ((y + j >= tp->nrows) || (x + i < 0) || (x + i >= tp->ncols))
            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 ((y + j >= 0) &&
                (tp->field[(y + j) * tp->ncols + x + i].pmid >= 0))
              return True;
          } else {
/* This method does not allow turning polyomino to an area above screen */
            if ((y + j < 0) ||
                (tp->field[(y + j) * tp->ncols + x + i].pmid >= 0))
              return True;
          }
        }
  }
  return False;
}

static Bool
atBottom(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int       i, j;
  int       x = tp->curPolyomino.xpos, y = tp->curPolyomino.ypos;

  if (tp->well) {
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if (polytris[(int) tp->bonusNow].polyomino
            [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
          int xi, yj;

          xi = (x + i) % WELL_PERIMETER;
          yj = y + j;
          if (yj < -1)
            return False;
          if (yj < WELL_DEPTH - 1) {
            if (tp->frozen_wall[xi / WELL_WIDTH])
              return True;
            if (tp->wall[yj + 1][xi].pmid >= 0)
              return True;
          } else if (yj < WELL_DEPTH + WELL_WIDTH - 1) {
            int base_i, base_j;

            wall_to_base(&base_i, &base_j, xi, yj + 1);
            if (tp->base[base_j][base_i].pmid >= 0)
              return True;
          } else
            return True;
        }
  } else {
    for (i = 0; i < tp->curPolyomino.size; i++)
      for (j = 0; j < tp->curPolyomino.size; j++)
        if ((y + j >= -1) && polytris[(int) tp->bonusNow].polyomino
          [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
          if ((y + j >= tp->nrows - 1) || (x + i < 0) || (x + i >= tp->ncols) ||
              (tp->field[(y + j + 1) * tp->ncols + x + i].pmid >= 0))
            return True;
  }
  return False;
}

static Bool
atBaseFully(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int i, j;

  for (i = 0; i < tp->curPolyomino.size; i++)
    for (j = 0; j < tp->curPolyomino.size; j++)
      if (tp->curPolyomino.ypos + j < WELL_DEPTH &&
          polytris[(int) tp->bonusNow].polyomino
           [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
    return False;
  return True;
}

static Bool
atBasePartially(ModeInfo * mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int i, j;

  for (i = 0; i < tp->curPolyomino.size; i++)
    for (j = 0; j < tp->curPolyomino.size; j++)
        if ((tp->curPolyomino.ypos + j >= WELL_DEPTH) &&
            polytris[(int) tp->bonusNow].polyomino
              [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i])
          return True;
  return False;
}

static Bool
wallChange(ModeInfo *mi, thing_t oldthing, thing_t newthing)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int             w = -1, i, j;
  int             x = tp->curPolyomino.xpos;

  for (i = 0; i < oldthing.size; i++)
    for (j = 0; j < oldthing.size; j++)
      if (polytris[(int) tp->bonusNow].polyomino[oldthing.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
        if (w == -1)
          w = ((x + i) % WELL_PERIMETER) / WELL_WIDTH;
        else if (w != ((x + i) % WELL_PERIMETER) / WELL_WIDTH)
          return True;
      }
  for (i = 0; i < newthing.size; i++)
    for (j = 0; j < newthing.size; j++)
      if (polytris[(int) tp->bonusNow].polyomino
                [tp->curPolyomino.polyomino_number].shape[j * tp->curPolyomino.size + i]) {
          if (w != ((x + i) % WELL_PERIMETER) / WELL_WIDTH)
            return True;
      }
  return False;
}

static void
tryMove(ModeInfo * mi, move_t move)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  thing_t   old;
  int       i;
  int       skip = False;
  int       cw = False; /* I am not sure if this is the original */

  old = tp->curPolyomino;
  switch (move) {
    case FALL:
      tp->curPolyomino.ypos++;
      break;

    case DROP:
      do { /* so fast you can not see it ;) */
        tp->curPolyomino.ypos ++;
      } while (!overlapping(mi));
      tp->curPolyomino.ypos--;
      break;

    case MOVE_LEFT:
      if (tp->well) {
        tp->curPolyomino.xpos = (tp->curPolyomino.xpos + WELL_PERIMETER - 1) % WELL_PERIMETER;
        if (atBaseFully(mi) || (atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino)))
          skip = True;
      } else
        tp->curPolyomino.xpos--;
      break;

    case MOVE_RIGHT:
      if (tp->well) {
        tp->curPolyomino.xpos = (tp->curPolyomino.xpos + WELL_PERIMETER + 1) % WELL_PERIMETER;
        if (atBaseFully(mi) || (atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino)))
          skip = True;
      } else
        tp->curPolyomino.xpos++;
      break;

    case ROTATE:
        if (cw)
          for (i = 0; i < MAX_SIDES - 1; i++)
            tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
              [tp->curPolyomino.polyomino_number].rotation;
        else /* ccw */
          tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
            [tp->curPolyomino.polyomino_number].rotation;
      tp->curPolyomino.xpos = old.xpos;
      tp->curPolyomino.ypos = old.ypos;
      if (tp->well && atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino))
          skip = True;
      break;

    case REFLECT: /* reflect on y axis */
      tp->curPolyomino.polyomino_number = polytris[(int) tp->bonusNow].polyomino
        [tp->curPolyomino.polyomino_number].reflection;
      tp->curPolyomino.xpos = old.xpos;
      tp->curPolyomino.ypos = old.ypos;
      if (tp->well && atBasePartially(mi) && wallChange(mi, old, tp->curPolyomino))
          skip = True;
      break;
  }

  if (!skip && !overlapping(mi)) {
    drawPolyominoDiff(mi, &old);
  } else {
    tp->curPolyomino = old;
    tp->sidemoves = tp->ncols / 2;
  }
}

static void
fillLines(ModeInfo *mi)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  int             i, j;

  XFlush(display);
  if (tp->well) {
    for (j = 0; j < WELL_DEPTH + WELL_WIDTH; j++)
      for (i = 0; i < WELL_PERIMETER; i++) {
         drawBox(mi, BITMAPS - 1,
           LRAND() % polytris[(int) tp->bonusNow].mode.number,
                 i, j);
         XFlush(display);
      }
  } else {
    for (j = 0; j <= tp->nrows / 2; j++) {
      for (i = 0; i < tp->ncols; i++) {
         drawSquare(mi, BITMAPS - 1,
           LRAND() % polytris[(int) tp->bonusNow].mode.number,
                 i, j);
         drawSquare(mi, BITMAPS - 1,
           LRAND() % polytris[(int) tp->bonusNow].mode.number,
                 i, tp->nrows - j - 1);
      }
      XFlush(display);
    }
  }
}

static void
free_images(trisstruct *tp)
{
  int n;

  if (tp->images[BITMAPS - 1] != None) {
    (void) XDestroyImage(tp->images[BITMAPS - 1]);
    tp->images[BITMAPS - 1] = None;
  }
  for (n = 0; n < BITMAPS - 1; n++) {
    /* Don't bother to free duplicates */
    if (IS_UP(n) && IS_DOWN(n) && IS_LEFT(n) && IS_RIGHT(n))
      tp->images[n] = None;
    else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
      tp->images[n] = None;
    else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
      tp->images[n] = None;
    else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
      tp->images[n] = None;
    else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
      tp->images[n] = None;
    else if (tp->images[n] != None) {
      (void) XDestroyImage(tp->images[n]);
      tp->images[n] = None;
    }
  }
}

static void
free_tetris_screen(ModeInfo *mi, trisstruct *tp)
{
  Display *display = MI_DISPLAY(mi);

  if (tp == NULL) {
    return;
  }
  if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
    MI_WHITE_PIXEL(mi) = tp->whitepixel;
    MI_BLACK_PIXEL(mi) = tp->blackpixel;
#ifndef STANDALONE
    MI_FG_PIXEL(mi) = tp->fg;
    MI_BG_PIXEL(mi) = tp->bg;
#endif
    if (tp->colors != NULL) {
#ifdef STANDALONE
	Screen *screen = MI_SCREENPTR(mi);
#endif
      if (tp->ncolors && !tp->no_colors)
	free_colors(
#ifdef STANDALONE
		screen,
#else
		display,
#endif
		tp->cmap, tp->colors, tp->ncolors);
      free(tp->colors);
      tp->colors = (XColor *) NULL;
    }
    if (tp->cmap != None) {
      XFreeColormap(display, tp->cmap);
      tp->cmap = None;
    }
  }
  if (tp->gc != None) {
     XFreeGC(display, tp->gc);
     tp->gc = None;
  }
  if (tp->graypix != None) {
    XFreePixmap(display, tp->graypix);
    tp->graypix = None;
  }
  if (tp->field != NULL) {
     free(tp->field);
     tp->field = (fieldstruct *) NULL;
  }
  free_images(tp);
  tp = NULL;
}

static int
checkLines(ModeInfo *mi)
{
  trisstruct *tp = &triss[MI_SCREEN(mi)];
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);

  if (tp->well) {
    int             lSet[2][WELL_WIDTH], nset[2];
    int             i, j, k, *tmp;

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

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

    if (nset[0] || nset[1]) {
        fieldstruct temp_base[WELL_WIDTH][WELL_WIDTH];
        for (k = 0; k < ((NUM_FLASHES / (nset[0] + nset[1])) / 2) * 2; k++) {
            for (j = 0; j < WELL_WIDTH; j++) {
                if (lSet[0][j] == WELL_WIDTH)
                  for (i = 0; i < WELL_WIDTH; i++)
                    xorSquare(mi, i, j);
                if (lSet[1][j] == WELL_WIDTH)
                  for (i = 0; i < WELL_WIDTH; i++)
                    xorSquare(mi, j, i);
            }
            XFlush(display);
        }

        for (j = 0; j < WELL_WIDTH; j++)
          for (i = 0; i < WELL_WIDTH; i++)
            temp_base[j][i] = tp->base[j][i];
        if (nset[0]) {
          for (j = 0; j < WELL_WIDTH / 2; j++)
            if (lSet[0][j] == WELL_WIDTH)
              for (i = 0; i < WELL_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 = WELL_WIDTH - 1; j >= WELL_WIDTH / 2; j--)
            if (lSet[0][j] == WELL_WIDTH)
              for (i = 0; i < WELL_WIDTH; i++) {
                for (k = j; k < WELL_WIDTH - 1; k++)
                  temp_base[k][i] = temp_base[k + 1][i];
                temp_base[WELL_WIDTH - 1][i].pmid = EMPTY;
                if (j < WELL_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 < WELL_WIDTH / 2; i++)
            if (lSet[1][i] == WELL_WIDTH)
              for (j = 0; j < WELL_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 = WELL_WIDTH - 1; i >= WELL_WIDTH / 2; i--)
            if (lSet[1][i] == WELL_WIDTH)
              for (j = 0; j < WELL_WIDTH; j++) {
                for (k = i; k < WELL_WIDTH - 1; k++)
                  temp_base[j][k] = temp_base[j][k + 1];
                temp_base[j][WELL_WIDTH - 1].pmid = EMPTY;
                if (i < WELL_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 < WELL_WIDTH; j++)
          for (i = 0; i < WELL_WIDTH; i++)
            if ((temp_base[j][i].cid != tp->base[j][i].cid) ||
                (temp_base[j][i].pmid != tp->base[j][i].pmid))
            {
              tp->base[j][i] = temp_base[j][i];
              if (tp->base[j][i].pmid >= 0)
                drawSquare(mi, tp->base[j][i].pmid, tp->base[j][i].cid, i, j);
              else
                clearSquare(mi, i, j);
            }
       XFlush(display);
    }

    return (nset[0] + nset[1]);
  } else {
    int      *lSet, nset = 0;
    int       i, j, y;

    if ((lSet = (int *) calloc(tp->nrows, sizeof (int))) == NULL) {
        free_tetris_screen(mi, tp);
        return -1; /* error */
    }
    for (j = 0; j < tp->nrows; j++) {
      for (i = 0; i < tp->ncols; i++)
        if (tp->field[j * tp-> ncols + i].pmid >= 0)
          lSet[j]++;
      if (lSet[j] == tp->ncols)
        nset++;
    }
    if (nset) {
      for (i = 0; i < ((NUM_FLASHES / nset) % 2) * 2; i++) {
        for (j = 0; j < tp->nrows; j++) {
          if (lSet[j] == tp->ncols)
            for (i = 0; i < tp->ncols; i++)
              xorSquare(mi, i, j);
        }
        XFlush(display);
      }

      for (j = tp->nrows - 1; j >= 0; j--) {
        if (lSet[j] == tp->ncols) {
          for (y = j; y > 0; y--)
            for (i = 0; i < tp->ncols; i++)
              tp->field[y * tp->ncols + i] =
                 tp->field[(y - 1) * tp->ncols + i];
          for (i = 0; i < tp->ncols; i++)
            tp->field[i].pmid = EMPTY;

          XCopyArea(display, window, window, tp->gc,
            tp->xb, tp->yb,
            tp->xs * tp->ncols, j * tp->ys,
            tp->xb, tp->yb + tp->ys);
          XSetForeground(display, tp->gc, tp->blackpixel);
          XFillRectangle(display, window, tp->gc,
            tp->xb, tp->yb, tp->xs * tp->ncols, tp->ys);

          for (i = j; i > 0; i--)
            lSet[i] = lSet[i - 1];
          lSet[0] = 0;

          if (j > 0)
            for (i = 0; i < tp->ncols; i++) {
              int       tmp = tp->field[j * tp->ncols + i].pmid;

              if ((tmp >= 0) && (tmp != CHECKDOWN(tmp))) {
                tp->field[j * tp->ncols + i].pmid = CHECKDOWN(tmp);
                  drawSquare(mi,
                    tp->field[j * tp->ncols + i].pmid, tp->field[j * tp->ncols + i].cid,
                    i, j);
              }
            }

          j++;

          if (j < tp->nrows)
            for (i = 0; i < tp->ncols; i++) {
              int       tmp = tp->field[j * tp->ncols + i].pmid;

              if ((tmp >= 0) && (tmp != CHECKUP(tmp))) {
                tp->field[j * tp->ncols + i].pmid = CHECKUP(tmp);
                drawSquare(mi,
                  tp->field[j * tp->ncols + i].pmid, tp->field[j * tp->ncols + i].cid,
                  i, j);
              }
            }

          XFlush(display);
        }
      }
    }

    if (lSet)
      free(lSet);
    return nset;
  }
}

static void
gameOver(ModeInfo *mi)
{
  /* maybe more stuff later */
  fillLines(mi);
}

static Bool
create_an_image(ModeInfo *mi, trisstruct *tp, int n)
{
  int x, y;
  unsigned char * data;

  if ((data = (unsigned char *) malloc(sizeof(char)*(tp->ys*ROUND8(tp->ys)/8))) == NULL) {
    return False;  /* no biggie, will not use images */
  }

  for (y=0;y<tp->ys;y++) for (x=0;x<tp->ys;x++) {
     if (!tp->use3D) {
#ifdef SMALL_BELLYBUTTON
        if (x >= tp->ys / 2 && x <= tp->ys / 2 + 1 &&
            y >= tp->ys / 2 && y <= tp->ys / 2 + 1)
          NOTHALFBIT(x,y)
        else
#endif
          HALFBIT(x,y)
      } else if ((x>=y && x<=tp->ys-y-1 && IS_UP(n))
          || (x<=y && x<=tp->ys-y-1 && y<tp->ys/2 && !IS_LEFT(n))
          || (x>=y && x>=tp->ys-y-1 && y<tp->ys/2 && !IS_RIGHT(n)))
        SETBIT(x,y)
      else if ((x<=y && x<=tp->ys-y-1 && IS_LEFT(n))
          || (x>=y && x<=tp->ys-y-1 && x<tp->ys/2 && !IS_UP(n))
          || (x<=y && x>=tp->ys-y-1 && x<tp->ys/2 && !IS_DOWN(n)))
        TWOTHIRDSBIT(x,y)
      else if ((x>=y && x>=tp->ys-y-1 && IS_RIGHT(n))
          || (x>=y && x<=tp->ys-y-1 && x>=tp->ys/2 && !IS_UP(n))
          || (x<=y && x>=tp->ys-y-1 && x>=tp->ys/2 && !IS_DOWN(n)))
        HALFBIT(x,y)
      else if ((x<=y && x>=tp->ys-y-1 && IS_DOWN(n))
          || (x<=y && x<=tp->ys-y-1 && y>=tp->ys/2 && !IS_LEFT(n))
          || (x>=y && x>=tp->ys-y-1 && y>=tp->ys/2 && !IS_RIGHT(n)))
        THIRDBIT(x,y)
  }

  if (IS_LEFT(n))
    for (y=0;y<tp->ys;y++)
      for (x=G;x<G+T;x++)
        SETBIT(x,y)
  if (IS_RIGHT(n))
    for (y=0;y<tp->ys;y++)
      for (x=G;x<G+T;x++)
        SETBIT(tp->ys-1-x,y)
  if (IS_UP(n))
    for (x=0;x<tp->ys;x++)
      for (y=G;y<G+T;y++)
        SETBIT(x,y)
  if (IS_DOWN(n))
    for (x=0;x<tp->ys;x++)
      for (y=G;y<G+T;y++)
        SETBIT(x,tp->ys-1-y)
  if (IS_LEFT(n))
    for (y=0;y<tp->ys;y++)
      for (x=0;x<G;x++)
        RESBIT(x,y)
  if (IS_RIGHT(n))
    for (y=0;y<tp->ys;y++)
      for (x=0;x<G;x++)
        RESBIT(tp->ys-1-x,y)
  if (IS_UP(n))
    for (x=0;x<tp->ys;x++)
      for (y=0;y<G;y++)
        RESBIT(x,y)
  if (IS_DOWN(n))
    for (x=0;x<tp->ys;x++)
      for (y=0;y<G;y++)
        RESBIT(x,tp->ys-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,tp->ys-1-y)
        else
          RESBIT(x,tp->ys-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(tp->ys-1-x,y)
        else
          RESBIT(tp->ys-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(tp->ys-1-x,tp->ys-1-y)
        else
          RESBIT(tp->ys-1-x,tp->ys-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,tp->ys-1-y)
    for (x=G;x<G+T;x++)
      for(y=0;y<G;y++)
        SETBIT(x,tp->ys-1-y)
    for (x=0;x<G+T;x++)
      for(y=G;y<G+T;y++)
        SETBIT(x,tp->ys-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(tp->ys-1-x,y)
    for (x=G;x<G+T;x++)
      for(y=0;y<G;y++)
        SETBIT(tp->ys-1-x,y)
    for (x=0;x<G+T;x++)
      for(y=G;y<G+T;y++)
        SETBIT(tp->ys-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(tp->ys-1-x,tp->ys-1-y)
    for (x=G;x<G+T;x++)
      for(y=0;y<G;y++)
        SETBIT(tp->ys-1-x,tp->ys-1-y)
    for (x=0;x<G+T;x++)
      for(y=G;y<G+T;y++)
        SETBIT(tp->ys-1-x,tp->ys-1-y)
  }
#ifdef LARGE_BELLYBUTTON
  if (!tp->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=tp->ys-G-T;y<tp->ys;y++)
          SETBIT(x,y)
    if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
      for (x=tp->ys-G-T;x<tp->ys;x++)
        for(y=0;y<G+T;y++)
          SETBIT(x,y)
    if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
      for (x=tp->ys-G-T;x<tp->ys;x++)
        for(y=tp->ys-G-T;y<tp->ys;y++)
          SETBIT(x,y)
  } else
#else
  if (tp->use3D)
#endif
  {
    if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n))
      for (x=0;x<tp->ys/2-RR;x++)
        for(y=0;y<tp->ys/2-RR;y++)
          THREEQUARTERSBIT(x,y)
    if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n))
      for (x=0;x<tp->ys/2-RR;x++)
        for(y=tp->ys/2+RR;y<tp->ys;y++)
          THREEQUARTERSBIT(x,y)
    if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n))
      for (x=tp->ys/2+RR;x<tp->ys;x++)
        for(y=0;y<tp->ys/2-RR;y++)
          THREEQUARTERSBIT(x,y)
    if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n))
      for (x=tp->ys/2+RR;x<tp->ys;x++)
        for(y=tp->ys/2+RR;y<tp->ys;y++)
          THREEQUARTERSBIT(x,y)
  }

  if ((tp->images[n] = XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
           1, XYBitmap, 0, (char *) data, tp->xs, tp->ys, 8, 0)) == None) {
     free(data);
     return False;
  }
  tp->images[n]->byte_order = MSBFirst;
  tp->images[n]->bitmap_unit = 8;
  tp->images[n]->bitmap_bit_order = LSBFirst;
  return True;
}

static Bool
create_images(ModeInfo *mi, trisstruct *tp)
{
  int n;

  (void) create_an_image(mi, tp, BITMAPS - 1);
  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))
      tp->images[n] = tp->images[BITMAPS - 1];
    else if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n)))
      tp->images[n] = tp->images[n & ~LEFT_UP];
    else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n)))
      tp->images[n] = tp->images[n & ~LEFT_DOWN];
    else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n)))
      tp->images[n] = tp->images[n & ~RIGHT_UP];
    else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n)))
      tp->images[n] = tp->images[n & ~RIGHT_DOWN];
    else if (image_needed(n)) {
      if (!create_an_image(mi, tp, n))
        return False;
    }
  }
  return True;
}

#ifndef STANDALONE
ENTRYPOINT void
refresh_tetris(ModeInfo * mi)
{
  trisstruct *tp;

  if (triss == NULL)
    return;
  tp = &triss[MI_SCREEN(mi)];
  if (!tp->well && tp->field == NULL)
    return;

  if (!tp->painted)
    return;
  MI_CLEARWINDOW(mi);
  if (tp->well) {
    int i, j;

    for (i = 0; i < WELL_PERIMETER; i++) {
      for (j = 0; j < WELL_DEPTH; j++) {
        if (tp->wall[j][i].pmid >= 0) {
          drawTrapazoid(mi, /*  tp->wall[j][i].pmid, */ tp->wall[j][i].cid, i, j);
        }
        if (tp->frozen_wall[i / WELL_WIDTH]) {
          freezeTrapazoid(mi, i, j);
        }
      }
    }
    for (i = 0; i < WELL_WIDTH; i++)
      for (j = 0; j < WELL_WIDTH; j++)
        if (tp->base[j][i].pmid >= 0)
          drawSquare(mi, tp->base[j][i].pmid, tp->base[j][i].cid, i, j);
  } else {
    int col, row, pos;

    for (col = 0; col < tp->ncols; col++)
      for (row = 0; row < tp->nrows; row++) {
        pos = row * tp->ncols + col;
#ifdef DEBUG
        (void) printf("pmid %d, cid %d, %d %d\n",
          tp->field[pos].pmid, tp->field[pos].cid, col, row);
#endif
        if (tp->field[pos].pmid != EMPTY)
          drawSquare(mi, tp->field[pos].pmid, tp->field[pos].cid, col, row);
    }
  }
  drawPolyomino(mi);
}

ENTRYPOINT void
change_tetris(ModeInfo * mi) {
  trisstruct *tp;

  if (triss == NULL)
    return;
  tp = &triss[MI_SCREEN(mi)];
  if (!tp->well && tp->field == NULL)
    return;

  tryMove(mi, ROTATE);
}
#endif

ENTRYPOINT void
release_tetris(ModeInfo * mi) {
  int bonustype, number;

  if (triss != NULL) {
    int   screen;

    for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
       free_tetris_screen(mi, &triss[screen]);
    free(triss);
    triss = (trisstruct *) NULL;
    for (bonustype = 0; bonustype < 2; bonustype++) {
      if (polytris[bonustype].polyomino != NULL) {
        for (number = 0; number < max_ominoes[bonustype]; number++) {
          if (polytris[bonustype].polyomino[number].shape != NULL)
            free(polytris[bonustype].polyomino[number].shape);
        }
        free(polytris[bonustype].polyomino);
        polytris[bonustype].polyomino = (Polyominoes *) NULL;
      }
      if (polytris[bonustype].mode.start != NULL) {
        free(polytris[bonustype].mode.start);
        polytris[bonustype].mode.start = (int *) NULL;
      }
    }
  }
}

#ifndef STANDALONE
extern char *background;
extern char *foreground;
#endif

ENTRYPOINT void
init_tetris(ModeInfo * mi) {
  Display *   display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);
  int size = MI_SIZE(mi);
  int   i, j, bonustype;
  trisstruct *tp;

  if (triss == NULL) {
    MI_INIT(mi, triss);
    for (bonustype = 0; bonustype < 2; bonustype++) {
      if (((polytris[bonustype].mode.start = (int *) malloc(start_ominoes[bonustype] *
            sizeof(int))) == NULL) ||
          ((polytris[bonustype].polyomino = (Polyominoes *) malloc(max_ominoes[bonustype] *
            sizeof(Polyominoes))) == NULL)) {
        release_tetris(mi);
        return;
      }
    }
    if (!readPolyominoes()) {
      release_tetris(mi);
      return;
    }
#ifdef DEBUG
    writePolyominoes(tp);
#endif
  }
   tp = &triss[MI_SCREEN(mi)];

  if (MI_IS_FULLRANDOM(mi)) {
    tp->bonus = (Bool) (LRAND() & 1);
    tp->well = !(NRAND(3));
    tp->use3D = (Bool) (NRAND(4));
  } else {
    tp->bonus = bonus;
    tp->well = well;
    tp->use3D = !plain;
  }

  if (tp->gc == None) {
    tp->blackpixel = MI_BLACK_PIXEL(mi);
    if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
      XColor      color;

#ifndef STANDALONE
      tp->fg = MI_FG_PIXEL(mi);
      tp->bg = MI_BG_PIXEL(mi);
#endif
      tp->whitepixel = MI_WHITE_PIXEL(mi);
      if ((tp->cmap = XCreateColormap(display, window,
          MI_VISUAL(mi), AllocNone)) == None) {
        free_tetris_screen(mi, tp);
        return;
      }
      XSetWindowColormap(display, window, tp->cmap);
      (void) XParseColor(display, tp->cmap, "black", &color);
      (void) XAllocColor(display, tp->cmap, &color);
      MI_BLACK_PIXEL(mi) = color.pixel;
      (void) XParseColor(display, tp->cmap, "white", &color);
      (void) XAllocColor(display, tp->cmap, &color);
      MI_WHITE_PIXEL(mi) = color.pixel;
#ifndef STANDALONE
      (void) XParseColor(display, tp->cmap, background, &color);
      (void) XAllocColor(display, tp->cmap, &color);
      MI_BG_PIXEL(mi) = color.pixel;
      (void) XParseColor(display, tp->cmap, foreground, &color);
      (void) XAllocColor(display, tp->cmap, &color);
      MI_FG_PIXEL(mi) = color.pixel;
#endif
      tp->colors = (XColor *) NULL;
      tp->ncolors = 0;
    }
    if ((tp->gc = XCreateGC(display, MI_WINDOW(mi),
        (unsigned long) 0, (XGCValues *) NULL)) == None) {
      free_tetris_screen(mi, tp);
      return;
    }
#if 0
    {
      XGCValues       gcv;
      gcv.function = GXxor;
      gcv.foreground = tp->fg;
      gcv.background = tp->bg;
    }
#endif
  }
  MI_CLEARWINDOW(mi);
  tp->painted = False;

  tp->width = MI_WIDTH(mi);
  tp->height = MI_HEIGHT(mi);

  free_images(tp);
  if (tp->width < 2 + 2 * tp->well)
    tp->width = 2 + 2 * tp->well;
  if (tp->height < 2 + 2 * tp->well)
    tp->height = 2 + 2 * tp->well;
  if (size >= MAXPIXELSIZE  &&
        (MINGRIDSIZE * size < tp->width && MINGRIDSIZE * size < tp->height)) {
      if (!tp->well) {
        tp->xs = size;
        tp->ys = size;
      } else {
        tp->xs = tp->ys = MAX(MINSIZE, MIN(tp->width, tp->height) /
            ((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE));
      }
  } else {
    if (size < -MINSIZE)
      tp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(tp->width, tp->height) /
        ((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE))) - MINSIZE + 1) + MINSIZE;
    else if (size < MINSIZE)
      tp->ys = MINSIZE;
    else
      tp->ys = MIN(size, MAX(MINSIZE, MIN(tp->width, tp->height) /
        ((tp->well) ? 3 * MINGRIDSIZE : MINGRIDSIZE)));
    tp->xs = tp->ys;
  }
  if (tp->ys >= MAXPIXELSIZE) {
    tp->xs = tp->ys = (tp->ys / 12) * 12;
    if (!create_images(mi, tp)) {
      free_images(tp);
      tp->pixelmode = True;
    } else
      tp->pixelmode = False;
  } else
    tp->pixelmode = True;
  if (tp->well) {
    if ((tp->graypix = XCreateBitmapFromData(display, window,
         (char *) gray1_bits, gray1_width, gray1_height)) == None) {
      free_tetris_screen(mi, tp);
      return;
    }
    tp->ncols = MAX_SIDES * WELL_WIDTH;
    tp->nrows = WELL_DEPTH;
    tp->xb = tp->width / 2 - tp->xs * WELL_WIDTH / 2;
    tp->yb = tp->height / 2 - tp->ys * WELL_WIDTH / 2;
    for (i = 0; i < WELL_PERIMETER; i++) {
      for (j = 0; j < WELL_DEPTH + WELL_WIDTH; j++) {
        tp->wall[j][i].pmid = EMPTY;
        tp->wall[j][i].cid = 0;
      }
    }
    for (i = 0; i < WELL_WIDTH; i++) {
      for (j = 0; j < WELL_WIDTH; j++) {
        tp->base[j][i].pmid = EMPTY;
        tp->base[j][i].cid = BITMAPS - 1;
      }
    }
    for (i = 0; i < MAX_SIDES; i++) {
      tp->frozen_wall[i] = 0;
    }
  } else {
    tp->ncols = MAX(tp->width / tp->xs, 5);
    tp->nrows = MAX(tp->height / tp->ys, 5);
    tp->xb = (tp->width - tp->xs * tp->ncols) / 2;
    tp->yb = (tp->height - tp->ys * tp->nrows) / 2;
    if (tp->field != NULL)
      free(tp->field);
    if ((tp->field = (fieldstruct *) malloc(tp->ncols * tp->nrows *
        sizeof (fieldstruct))) == NULL) {
      free_tetris_screen(mi, tp);
      return;
    }
    for (i = 0; i < tp->ncols * tp->nrows; i++) {
      tp->field[i].pmid = EMPTY;
      tp->field[i].cid = 0;
    }
  }
  tp->rows = 0;
  tp->level = 0;
  tp->bonusNow = False;

  /* Set up colour map */
  tp->direction = (LRAND() & 1) ? 1 : -1;
  if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
#ifdef STANDALONE
	Screen *screen = MI_SCREENPTR(mi);
#endif
    if (tp->colors != NULL) {
      if (tp->ncolors && !tp->no_colors)
	free_colors(
#ifdef STANDALONE
		screen,
#else
		display,
#endif
		tp->cmap, tp->colors, tp->ncolors);
      free(tp->colors);
      tp->colors = (XColor *) NULL;
    }
    tp->ncolors = MI_NCOLORS(mi);
    if (tp->ncolors < 2)
      tp->ncolors = 2;
    if (tp->ncolors <= 2)
      tp->mono_p = True;
    else
      tp->mono_p = False;

    if (tp->mono_p)
      tp->colors = (XColor *) NULL;
    else
      if ((tp->colors = (XColor *) malloc(sizeof (*tp->colors) *
          (tp->ncolors + 1))) == NULL) {
        free_tetris_screen(mi, tp);
        return;
      }
    tp->cycle_p = has_writable_cells(
#ifdef STANDALONE
	screen, MI_VISUAL(mi)
#else
	mi
#endif
	);
    if (tp->cycle_p) {
      if (MI_IS_FULLRANDOM(mi)) {
        if (!NRAND(8))
          tp->cycle_p = False;
        else
          tp->cycle_p = True;
      } else {
        tp->cycle_p = cycle_p;
      }
    }
    if (!tp->mono_p) {
      if (!(LRAND() % 10))
        make_random_colormap(
#ifdef STANDALONE
		screen, MI_VISUAL(mi),
		tp->cmap, tp->colors, &tp->ncolors,
		True, True, &tp->cycle_p, True
#else
		mi,
		tp->cmap, tp->colors, &tp->ncolors,
		True, True, &tp->cycle_p
#endif
		);
      else if (!(LRAND() % 2))
        make_uniform_colormap(
#ifdef STANDALONE
		screen, MI_VISUAL(mi),
		tp->cmap, tp->colors, &tp->ncolors,
		True, &tp->cycle_p, True
#else
		mi,
		tp->cmap, tp->colors, &tp->ncolors,
		True, &tp->cycle_p
#endif
		);
      else
        make_smooth_colormap(
#ifdef STANDALONE
		screen, MI_VISUAL(mi),
		tp->cmap, tp->colors, &tp->ncolors,
		True, &tp->cycle_p, True
#else
		mi,
		tp->cmap, tp->colors, &tp->ncolors,
		True, &tp->cycle_p
#endif
		);
    }
    XInstallColormap(display, tp->cmap);
    if (tp->ncolors < 2) {
      tp->ncolors = 2;
      tp->no_colors = True;
    } else
      tp->no_colors = False;
    if (tp->ncolors <= 2)
      tp->mono_p = True;

    if (tp->mono_p)
      tp->cycle_p = False;

  }
  /* initialize object */
  newPolyomino(mi);
}

static Bool
moveOne(ModeInfo *mi, move_t move)
{
  Display    *display = MI_DISPLAY(mi);
  trisstruct *tp = &triss[MI_SCREEN(mi)];

  if (move == FALL) {
    if (trackmouse) {
      tp->sidemoves = tp->sidemoves + 1;
      if (tp->sidemoves < tp->ncols / 2) {
        Window      r, c;
        int         rx, ry, cx, cy;
        unsigned int m;

        (void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
          &r, &c, &rx, &ry, &cx, &cy, &m);
        if (tp->well) {
          cx = cx - tp->width / 2;
          cy = cy - tp->height / 2;
          if (ABS(cx) <= tp->xs * tp->ncols / 2 ||
              ABS(cy) <= tp->ys * tp->nrows / 2) {
            int ominopos, mousepos, diff;
            double wall;

            /* Avoid atan2: DOMAIN error message */
            if (cy == 0 && cx == 0)
              wall = 0.0;
            else
              wall = 2.0 * (atan2((double) cy, (double) cx) + 3 * M_PI / 4) / M_PI;
            if (wall < 0.0)
              wall += 4.0;
            mousepos = (int) (wall * WELL_WIDTH);
            ominopos = tp->curPolyomino.xpos;
            diff = mousepos - ominopos;
            if (diff < 0)
              diff += WELL_WIDTH * MAX_SIDES;
            if (diff > WELL_WIDTH * (MAX_SIDES / 2))
              move = MOVE_LEFT;
            else if (diff > 0)
              move = MOVE_RIGHT;
          }
        } else {
          cx = (cx - tp->xb) / tp->xs;
          cy = (cy - tp->yb) / tp->ys;
          if (!(cx < 0 || cy < 1 || cx >= tp->ncols || cy > tp->nrows)) {
            if (cx > tp->curPolyomino.xpos +
  polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].leadingEmptyWidth) {
              move = MOVE_RIGHT;
            } else if (cx < tp->curPolyomino.xpos +
  polytris[(int) tp->bonusNow].polyomino[tp->curPolyomino.polyomino_number].leadingEmptyWidth) {
              move = MOVE_LEFT;
            }
          }
        }
      } else {
        tp->sidemoves = 0;
      }
    } else if (!NRAND(4)) {
      if (LRAND() & 1)
        move = ROTATE;
      else if (LRAND() & 1)
        move = MOVE_LEFT;
      else
        move = MOVE_RIGHT;
    }
  }
  if ((move == DROP) || ((move == FALL) && atBottom(mi))) {
    putBox(mi);
    {
      int       lines;

      if ((lines = checkLines(mi)) < 0) {
        return False;
      }
      tp->rows += lines;
      if (tp->rows > THRESHOLD(tp->level)) {
        tp->level++;
        if (tp->bonus) {
          tp->bonusNow = True; /* No good deed goes unpunished */
        }
      }
    }
    XFlush(display);
    if (tp->well) {
      if (allFrozen(mi)) {
        gameOver(mi);
        init_tetris(mi);
        return False;
      }
    }
    newPolyomino(mi);
    if (tp->well) {
      checkFreeze(mi);
    }
    if (overlapping(mi)) {
      gameOver(mi);
      init_tetris(mi);
      return False;
    }
    drawPolyomino(mi);
    return True;
  } else {
    tryMove(mi, move);
    if (tp->rows > THRESHOLD(tp->level)) {
      tp->level++;
      if (tp->bonus) {
        tp->bonusNow = True;
      }
    }
    return False;
  }
}

ENTRYPOINT void
draw_tetris(ModeInfo * mi)
{
  trisstruct *tp;

  if (triss == NULL)
    return;
  tp = &triss[MI_SCREEN(mi)];
  if (!tp->well && tp->field == NULL)
    return;

  if (tp->no_colors) {
    init_tetris(mi);
    return;
  }
  tp->painted = True;
  MI_IS_DRAWN(mi) = True;

  /* Rotate colours */
  if (tp->cycle_p) {
    rotate_colors(
#ifdef STANDALONE
		MI_SCREENPTR(mi),
#else
		MI_DISPLAY(mi),
#endif
		tp->cmap, tp->colors, tp->ncolors, tp->direction);
    if (!(LRAND() % 1000))
      tp->direction = -tp->direction;
  }

  (void) moveOne(mi, FALL);
}

XSCREENSAVER_MODULE ("Tetris", tetris)

#endif /* MODE_tetris */
