unofficial version 0.8: chat, code cleanup
[netris.git] / board.c
diff --git a/board.c b/board.c
index 55983b477de3513218ebab9b019e0db1e029956f..c3a8d5cae44e69711ad4a6f7e1b698940eef6274 100644 (file)
--- a/board.c
+++ b/board.c
 #include "netris.h"
 #include <stdlib.h>
 
-#ifdef DEBUG_FALLING
-# define B_OLD
-#else
-# define B_OLD abs
-#endif
-
-static BlockType board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
-static BlockType oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+#include "board.h"
+
+static const char shapes[7][4][4][4] = {
+       { { {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 J
+         { {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} },
+         { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
+           {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
+         { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
+           {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
+         { {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} },
+         { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
+           {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
+         { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
+           {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
+         { {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} }, //rep
+         { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
+           {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, //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} }, //rep
+         { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+           {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
+         { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
+           {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } //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 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 ClearField(int 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)
@@ -46,123 +134,103 @@ ExtFunc void ClearField(int scr)
                }
 } //ClearField
 
-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];
 } //GetBlock
 
-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);
                board[scr][y][x] = type;
                changed[scr][y] |= 1 << x;
        }
 } //SetBlock
 
-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);
+
+       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];
                                }
-                               RobotCmd(0, "\n");
-                       } //robot
-                       changed[scr][y] = 0;
+                       changed[scr][y] = 0; //reset
                        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]);
-                               }
                } //changed row
-       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]);
-                       any = 1;
-               }
        return any;
 } //RefreshBoard
 
-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;
 } //GlanceFunc
 
-ExtFunc int ShadowFunc(int scr, int y, int x, BlockType type, void *data)
+int ShadowFunc(int scr, int y, int x, unsigned char type)
 { //draw shadow
        SetBlock(scr, y, x, BT_shadow);
        return 0;
 } //ShadowFunc
 
-ExtFunc int PlotFunc(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, type);
        return 0;
 }
-ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling, int shadow)
+void PlotShape(char shape, int scr, int y, int x, int shadow)
 { //put shape on field
-       if (shadow && scr == me) {
+       if (shadow) {
                for (shadowy = y - 1; shadowy >= 0; shadowy--)
                        if (!ShapeFits(shape, scr, shadowy, x))
                                break;
-               ShapeIterate(shape, scr, shadowy + 1, x, falling, ShadowFunc, NULL);
+               ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
        } //draw shadow
-       ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL);
+       ShapeIterate(shape, scr, y, x, PlotFunc);
 } //PlotShape
 
-ExtFunc int EraseFunc(int scr, int y, int x, BlockType type, void *data)
+int EraseFunc(int scr, int y, int x, unsigned char type)
 {
        SetBlock(scr, y, x, BT_none);
        return 0;
 }
-ExtFunc void EraseShape(Shape *shape, int scr, int y, int x, int shadow)
+void EraseShape(char shape, int scr, int y, int x, int shadow)
 { //remove block from field
-       ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL);
+       ShapeIterate(shape, scr, y, x, EraseFunc);
        if (shadow && scr == me) //draw shadow
-               ShapeIterate(shape, scr, shadowy + 1, x, 0, EraseFunc, NULL);
+               ShapeIterate(shape, scr, shadowy + 1, x, EraseFunc);
 } //EraseShape
 
-ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data)
+int CollisionFunc(int scr, int y, int x, unsigned char type)
 {
        return GetBlock(scr, y, x) > BT_none;
 }
-ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x)
+int ShapeFits(char shape, int scr, int y, int x)
 { //check if there's nothing in the way
-       return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL);
+       return !ShapeIterate(shape, scr, y, x, CollisionFunc);
 } //ShapeFits
 
-ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data)
+int VisibleFunc(int scr, int y, int x, unsigned char type)
 {
        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);
 } //ShapeVisible
 
-ExtFunc int MovePiece(int scr, int deltaY, int deltaX)
+int MovePiece(int scr, int deltaY, int deltaX)
 {
        int result;
 
@@ -175,28 +243,36 @@ ExtFunc int MovePiece(int scr, int deltaY, int deltaX)
                Players[scr].curX += deltaX;
        }
        PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
-               1, 1);
+               scr == me);
        return result;
 } //MovePiece
 
-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, 1);
-       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;
+       /* (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) {
+               short int slideX;
+               for (slideX = 0; slideX < 2; slideX = -slideX) {
+                       if (slideX >= 0) slideX++; //slide more
+                       if (result = ShapeFits(newshape, scr, Players[scr].curY,
+                               Players[scr].curX+slideX)) break;
+               } //slide left and right
+               if (result) Players[scr].curX += slideX;
+       } //try to fit if it doesn't
+       if (result) Players[scr].curShape = newshape;
        PlotShape(Players[scr].curShape, scr,
-                       Players[scr].curY, Players[scr].curX, 1, 1);
+                       Players[scr].curY, Players[scr].curX, scr == me);
        return result;
 } //RotatePiece
 
-ExtFunc int DropPiece(int scr)
+int DropPiece(int scr)
 {
        int count = 0;
 
@@ -204,26 +280,70 @@ ExtFunc int DropPiece(int scr)
                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--;
+               count++;
        }
        PlotShape(Players[scr].curShape, scr,
-               Players[scr].curY, Players[scr].curX, 1, 0);
+               Players[scr].curY, Players[scr].curX, 0);
        return count;
 } //DropPiece
 
-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 & 16  && !BlockFree(scr, x, y-1, z & 208)) return 0;
+       if (curblock & 32  && !BlockFree(scr, x, y+1, z & 224)) return 0;
+       if (curblock & 64  && !BlockFree(scr, x+1, y, z & 112)) return 0;
+       if (curblock & 128 && !BlockFree(scr, x-1, y, z & 176)) return 0;
+       if ((z = GetBlock(scr, y-1, x)) & 32) 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 & 16)  BlockFall(scr, x, y-1, z & 208);
+       if (GetBlock(scr, y, x) & z & 32)  BlockFall(scr, x, y+1, z & 224);
+       if (GetBlock(scr, y, x) & z & 64)  BlockFall(scr, x+1, y, z & 112);
+       if (GetBlock(scr, y, x) & z & 128) BlockFall(scr, x-1, y, z & 174);
+       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 & 160) == 0)) {
+                       //doesn't stick left/up => topleft block
+                               if (BlockFree(scr, x, y, 240)) {
+                                       BlockFall(scr, x, y, 240);
+                                       fallen++;
+                               } //move blocks down
+                       } //block present
+               } //handle line
+       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)
+       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)
@@ -231,32 +351,35 @@ ExtFunc void CopyLine(int scr, int from, int 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++) {
+                               SetBlock(scr, from, x, GetBlock(scr, from, x)&239);
+                               if (from>1)
+                                       SetBlock(scr, from-2, x, GetBlock(scr, from-2, x)&223);
+                       } //don't stick blocks to line which we'll remove
+               } //full lines
                CopyLine(scr, from++, to++);
        }
-       return from - to;
+       linescleared += from - to;
+       } while (CheckFall(scr));
+       return linescleared;
 }
 
-ExtFunc void FreezePiece(int scr)
+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);
+       // remove me! :)
 }
 
-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,
@@ -265,14 +388,16 @@ ExtFunc void InsertJunk(int scr, int count, int column)
                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);
+                       SetBlock(scr, y, x, (x == column) ? BT_none : color + 1
+                               + 64 * (x != column-1 && x < Players[scr].boardWidth-1)
+                               + 128 * (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 again as far as possible
                else break;
        PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
-               1, 1);
+               scr == me);
 } //InoertJunk
 
 /*