* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: board.c,v 1.15 1999/05/16 06:56:24 mhw Exp $
*/
#include "netris.h"
#include <stdlib.h>
-#ifdef DEBUG_FALLING
-# define B_OLD
-#else
-# define B_OLD abs
-#endif
+#include "board.h"
+
+static const char shapes[7][4][4][4] = {
+ /*
+ * 4 rotations of 4x4 pixels per shape
+ * high nibble signifies joinage (left, right, top, bottom, from MSB)
+ * low nibble identifies block type (typically 2..8)
+ */
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x47, 0xC7, 0x97, 0x00},
+ {0x00, 0x00, 0x27, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp horizontal
+ { {0x00, 0x17, 0x00, 0x00}, {0x00, 0x37, 0x00, 0x00},
+ {0x47, 0xA7, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt vertical
+ { {0x17, 0x00, 0x00, 0x00}, {0x67, 0xC7, 0x87, 0x00},
+ {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt horizontal
+ { {0x00, 0x57, 0x87, 0x00}, {0x00, 0x37, 0x00, 0x00},
+ {0x00, 0x27, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // J (yellow)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x53, 0xC3, 0x83, 0x00},
+ {0x23, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp horizontal
+ { {0x43, 0x93, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
+ {0x00, 0x23, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp vertical
+ { {0x00, 0x00, 0x13, 0x00}, {0x43, 0xC3, 0xA3, 0x00},
+ {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt horizontal
+ { {0x00, 0x13, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
+ {0x00, 0x63, 0x83, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // L (cyan)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x48, 0xD8, 0x88, 0x00},
+ {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing down
+ { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xB8, 0x00, 0x00},
+ {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing left
+ { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xE8, 0x88, 0x00},
+ {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing up
+ { {0x00, 0x18, 0x00, 0x00}, {0x00, 0x78, 0x88, 0x00},
+ {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // T (white)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
+ {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
+ { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
+ {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // standing
+ { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
+ {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
+ { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
+ {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // S (green)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
+ {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
+ { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
+ {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // standing
+ { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
+ {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
+ { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
+ {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // Z (red)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
+ {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
+ { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
+ {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} }, // standing
+ { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
+ {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
+ { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
+ {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, // I, stick (blue)
+
+ { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+ {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
+ { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+ {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
+ { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+ {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
+ { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+ {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } // O, square (purple)
+};
+
+int ShapeIterate(char s, int scr, int y, int x, ShapeDrawFunc func)
+{ //Draw a certain shape using <ShapeDrawFunc>
+ int i, j, result;
+ char type, rotation;
+
+ type = s / 4;
+ rotation = s & 3;
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 4; j++)
+ if (shapes[type][rotation][i][j])
+ if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j]))
+ return result;
+ return 0;
+}
+
+
+float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
+
+char ChooseOption(float options[7])
+{ //Return a random piece with given piece weight
+ int i;
+ float total = 0, val;
+
+ for (i = 0; i < 7; i++) total += options[i];
+ val = Random(0, 32767) / 32768.0 * total;
+ for (i = 0; i < 7; i++) if ((val -= options[i]) < 0)
+ return i << 2;
+ return 0;
+}
+
-static BlockType board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
-static BlockType oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
-static int falling[MAX_SCREENS][MAX_BOARD_WIDTH];
-static int oldFalling[MAX_SCREENS][MAX_BOARD_WIDTH];
+static int shadowy;
-ExtFunc void CleanupBoard(int scr)
-{
- CleanupScreen(scr);
+void ClearField(int scr)
+{ //Empty the whole field (all blocks BT_none)
+ int y, x;
+
+ for (y = Players[scr].boardHeight - 1; y >= 0; --y)
+ for (x = 0; x < Players[scr].boardWidth; ++x) {
+ oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
+ }
}
-ExtFunc BlockType GetBlock(int scr, int y, int x)
-{
+unsigned char GetBlock(int scr, int y, int x)
+{ //Returns the block on field at position (x,y)
if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
return BT_wall;
else if (y >= Players[scr].boardHeight)
return BT_none;
else
- return abs(board[scr][y][x]);
+ return board[scr][y][x];
}
-ExtFunc void SetBlock(int scr, int y, int x, BlockType type)
+void SetBlock(int scr, int y, int x, unsigned char type)
{
- if (y >= 0 && y < Players[scr].boardHeight &&
- x >= 0 && x < Players[scr].boardWidth) {
- if (y < Players[scr].boardVisible)
- falling[scr][x] += (type < 0) - (board[scr][y][x] < 0);
+ if (y >= 0 && y < Players[scr].boardHeight
+ && x >= 0 && x < Players[scr].boardWidth) {
board[scr][y][x] = type;
changed[scr][y] |= 1 << x;
}
}
-ExtFunc int RefreshBoard(int scr)
-{
+int RefreshBoard(int scr)
+{ //draw changes to screen
int y, x, any = 0;
unsigned int c;
- BlockType b;
-
- for (y = Players[scr].boardVisible - 1; y >= 0; --y)
- if ((c = changed[scr][y])) {
- if (robotEnable) {
- RobotCmd(0, "RowUpdate %d %d", scr, y);
- for (x = 0; x < Players[scr].boardWidth; ++x) {
- b = board[scr][y][x];
- if (fairRobot)
- b = abs(b);
- RobotCmd(0, " %d", b);
- }
- RobotCmd(0, "\n");
- }
- changed[scr][y] = 0;
- any = 1;
- for (x = 0; c; (c >>= 1), (++x))
- if ((c & 1) && B_OLD(board[scr][y][x])!=oldBoard[scr][y][x]) {
- PlotBlock(scr, y, x, B_OLD(board[scr][y][x]));
- oldBoard[scr][y][x] = B_OLD(board[scr][y][x]);
+
+ for (y = Players[scr].boardVisible - 1; y >= 0; y--)
+ if ((c = changed[scr][y])) { // line changed
+ for (x = 0; c; (c >>= 1), x++)
+ if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) {
+ PlotBlock(scr, y, x, board[scr][y][x]);
+ oldBoard[scr][y][x] = board[scr][y][x];
}
- }
- if (robotEnable)
- RobotTimeStamp();
- for (x = 0; x < Players[scr].boardWidth; ++x)
- if (oldFalling[scr][x] != !!falling[scr][x]) {
- oldFalling[scr][x] = !!falling[scr][x];
- PlotUnderline(scr, x, oldFalling[scr][x]);
+ changed[scr][y] = 0; // reset
any = 1;
- }
+ } //changed row
return any;
}
-ExtFunc int GlanceFunc(int scr, int y, int x, BlockType type, void *data)
+int GlanceFunc(int scr, int y, int x, unsigned char type)
{
- PlotBlock1(scr, 20 - y, x * 2, type);
+ PlotBlockXY(y, x, type);
return 0;
}
-ExtFunc int PlotFunc(int scr, int y, int x, BlockType type, void *data)
-{
- SetBlock(scr, y, x, type);
+int ShadowFunc(int scr, int y, int x, unsigned char type)
+{ //draw shadow
+ SetBlock(scr, y, x, BT_shadow);
return 0;
}
-ExtFunc int EraseFunc(int scr, int y, int x, BlockType type, void *data)
+int PlotFunc(int scr, int y, int x, unsigned char type)
{
- SetBlock(scr, y, x, BT_none);
+ SetBlock(scr, y, x, type);
return 0;
}
-
-ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data)
-{
- return GetBlock(scr, y, x) != BT_none;
+void PlotShape(char shape, int scr, int y, int x, int shadow)
+{ //put shape on field
+ if (shadow) {
+ for (shadowy = y - 1; shadowy >= 0; shadowy--)
+ if (!ShapeFits(shape, scr, shadowy, x))
+ break;
+ ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
+ } //draw shadow
+ ShapeIterate(shape, scr, y, x, PlotFunc);
}
-ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data)
+int EraseFunc(int scr, int y, int x, unsigned char type)
{
- return (y >= 0 && y < Players[scr].boardVisible &&
- x >= 0 && x < Players[scr].boardWidth);
+ SetBlock(scr, y, x, BT_none);
+ return 0;
}
-
-ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling)
-{
- ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL);
+void EraseShape(char shape, int scr, int y, int x, int shadow)
+{ //remove block from field
+ ShapeIterate(shape, scr, y, x, EraseFunc);
+ if (shadow && scr == me) // draw shadow
+ ShapeIterate(shape, scr, shadowy + 1, x, EraseFunc);
}
-ExtFunc void EraseShape(Shape *shape, int scr, int y, int x)
+int CollisionFunc(int scr, int y, int x, unsigned char type)
{
- ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL);
+ return GetBlock(scr, y, x) > BT_none;
+}
+int ShapeFits(char shape, int scr, int y, int x)
+{ //check if there's nothing in the way
+ return !ShapeIterate(shape, scr, y, x, CollisionFunc);
}
-ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x)
+int VisibleFunc(int scr, int y, int x, unsigned char type)
{
- return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL);
+ return (y >= 0 && y < Players[scr].boardVisible &&
+ x >= 0 && x < Players[scr].boardWidth);
}
-
-ExtFunc int ShapeVisible(Shape *shape, int scr, int y, int x)
+int ShapeVisible(char shape, int scr, int y, int x)
{
- return ShapeIterate(shape, scr, y, x, 0, VisibleFunc, NULL);
+ return ShapeIterate(shape, scr, y, x, VisibleFunc);
}
-ExtFunc int MovePiece(int scr, int deltaY, int deltaX)
+int MovePiece(int scr, int deltaY, int deltaX)
{
int result;
EraseShape(Players[scr].curShape, scr,
- Players[scr].curY, Players[scr].curX);
+ Players[scr].curY, Players[scr].curX, 1);
result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY,
- Players[scr].curX + deltaX);
+ Players[scr].curX + deltaX);
if (result) {
Players[scr].curY += deltaY;
Players[scr].curX += deltaX;
}
- PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1);
+ PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
+ scr == me);
return result;
}
-ExtFunc int RotatePiece(int scr, int dir)
+int RotatePiece(int scr, int dir)
{
+ char newshape;
int result;
- EraseShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX);
- result = ShapeFits(dir ? Players[scr].curShape->rotateTo
- : Players[scr].curShape->rotateFrom,
- scr, Players[scr].curY, Players[scr].curX);
- if (result)
- Players[scr].curShape = dir ? Players[scr].curShape->rotateTo
- : Players[scr].curShape->rotateFrom;
+ EraseShape(Players[scr].curShape, scr, Players[scr].curY,
+ Players[scr].curX, 1);
+ /* (inc|dec)rement only 3 least significant bits which indicate rotation */
+ newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
+ result = ShapeFits(newshape, scr, Players[scr].curY, Players[scr].curX);
+ if (!result) {
+ // move if it doesn't fit anymore
+ short int slideX;
+ for (slideX = 0; slideX < 2; slideX = -slideX) {
+ // slide left and right
+ if (slideX >= 0) slideX++; // slide more
+ if (result = ShapeFits(newshape, scr, Players[scr].curY,
+ Players[scr].curX+slideX)) break;
+ }
+ if (result) Players[scr].curX += slideX;
+ }
+ if (result) Players[scr].curShape = newshape;
PlotShape(Players[scr].curShape, scr,
- Players[scr].curY, Players[scr].curX, 1);
+ Players[scr].curY, Players[scr].curX, scr == me);
return result;
}
-ExtFunc int DropPiece(int scr)
+int DropPiece(int scr)
{
int count = 0;
EraseShape(Players[scr].curShape, scr,
- Players[scr].curY, Players[scr].curX);
+ Players[scr].curY, Players[scr].curX, 1);
while (ShapeFits(Players[scr].curShape, scr,
- Players[scr].curY - 1, Players[scr].curX)) {
- --Players[scr].curY;
- ++count;
+ Players[scr].curY - 1, Players[scr].curX)) {
+ Players[scr].curY--;
+ count++;
}
PlotShape(Players[scr].curShape, scr,
- Players[scr].curY, Players[scr].curX, 1);
+ Players[scr].curY, Players[scr].curX, 0);
return count;
}
-ExtFunc int LineIsFull(int scr, int y)
-{
+int BlockFree(int scr, int x, int y, unsigned char z)
+{ //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
+ unsigned char curblock;
+
+ if (y == 0) return 0; // at bottom
+ curblock = GetBlock(scr, y, x) & z;
+ if (curblock & 0x10 && !BlockFree(scr, x, y-1, z & 0xD0)) return 0;
+ if (curblock & 0x20 && !BlockFree(scr, x, y+1, z & 0xE0)) return 0;
+ if (curblock & 0x40 && !BlockFree(scr, x+1, y, z & 0x70)) return 0;
+ if (curblock & 0x80 && !BlockFree(scr, x-1, y, z & 0xB0)) return 0;
+ if ((z = GetBlock(scr, y-1, x)) & 0x20) return 1; // stuck to block below
+ if (z > BT_none) return 0; // some other piece below
+ return 1; // nothing below
+}
+
+int BlockFall(int scr, int x, int y, unsigned char z)
+{ //Drop down block (x,y) and those sticking to it mask <z>
+ if (GetBlock(scr, y, x) & z & 0x10) BlockFall(scr, x, y-1, z & 0xD0);
+ if (GetBlock(scr, y, x) & z & 0x20) BlockFall(scr, x, y+1, z & 0xE0);
+ if (GetBlock(scr, y, x) & z & 0x40) BlockFall(scr, x+1, y, z & 0x70);
+ if (GetBlock(scr, y, x) & z & 0x80) BlockFall(scr, x-1, y, z & 0xB0);
+ SetBlock(scr, y-1, x, GetBlock(scr, y, x));
+ SetBlock(scr, y, x, BT_none);
+}
+
+int CheckFall(int scr)
+{ //Drop any free blocks on field
+ int xloop, x, x2, y, fallen = 0;
+ unsigned char z;
+
+ if (!Game.gravity) return 0;
+ for (y = Players[scr].boardHeight - 1; y > 0; y--)
+ for (x = 0; x < Players[scr].boardWidth; x++) {
+ if ((z = GetBlock(scr, y, x)) > BT_none && (z & 0xA0) == 0) {
+ // block present which doesn't stick left/up => topleft block
+ if (BlockFree(scr, x, y, 0xF0)) {
+ BlockFall(scr, x, y, 0xF0); // move blocks down
+ fallen++;
+ }
+ } //block present
+ }
+ return fallen;
+}
+
+int LineIsFull(int scr, int y)
+{ //return 0 if any blocks present on line <y>
int x;
- for (x = 0; x < Players[scr].boardWidth; ++x)
- if (GetBlock(scr, y, x) == BT_none)
+ for (x = 0; x < Players[scr].boardWidth; x++)
+ if (GetBlock(scr, y, x) <= BT_none)
return 0;
return 1;
}
-ExtFunc void CopyLine(int scr, int from, int to)
-{
+void CopyLine(int scr, int from, int to)
+{ //move blocks on line <from> to line <to>
int x;
if (from != to)
SetBlock(scr, to, x, GetBlock(scr, from, x));
}
-ExtFunc int ClearFullLines(int scr)
-{
- int from, to;
+int ClearFullLines(int scr)
+{ //remove full lines, return lines cleared
+ int from, to, x, linescleared = 0;
+ do {
from = to = 0;
while (to < Players[scr].boardHeight) {
- while (LineIsFull(scr, from))
- ++from;
+ while (LineIsFull(scr, from)) {
+ from++; // skip
+ for (x = 0; x<Players[scr].boardWidth; x++) {
+ // don't stick blocks to line which we'll remove
+ SetBlock(scr, from, x, GetBlock(scr, from, x) & 0xEF);
+ if (from > 1)
+ SetBlock(scr, from-2, x, GetBlock(scr, from-2, x) & 0xDF);
+ }
+ }
CopyLine(scr, from++, to++);
}
- return from - to;
-}
-
-ExtFunc void FreezePiece(int scr)
-{
- int y, x;
- BlockType type;
-
- for (y = 0; y < Players[scr].boardHeight; ++y)
- for (x = 0; x < Players[scr].boardWidth; ++x)
- if ((type = board[scr][y][x]) < 0)
- SetBlock(scr, y, x, -type);
+ linescleared += from - to;
+ } while (CheckFall(scr));
+ return linescleared;
}
-ExtFunc void InsertJunk(int scr, int count, int column)
-{
+void InsertJunk(int scr, int color, int count, int column)
+{ //add <count> junklines with hole at <column> to <scr> by team <color>
int y, x;
EraseShape(Players[scr].curShape, scr,
- Players[scr].curY, Players[scr].curX);
+ Players[scr].curY, Players[scr].curX, 1);
for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
CopyLine(scr, y, y + count);
for (y = 0; y < count; ++y)
for (x = 0; x < Players[scr].boardWidth; ++x)
- SetBlock(scr, y, x, (x == column) ? BT_none : BT_white);
- Players[scr].curY += count; //move piece up..
+ SetBlock(scr, y, x, x == column ? BT_none : color + 1
+ + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1)
+ + 0x80 * (x != column+1 && x > 0));
+ Players[scr].curY += count; // move piece up..
for (y = 0; y < count; ++y)
if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
- Players[scr].curY--; //..and down as far as possible
+ Players[scr].curY--; // ...and down again as far as possible
else break;
- PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1);
+ PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
+ scr == me);
}
-/*
- * vi: ts=4 ai
- * vim: noai si
- */