/* The source code here is derived from consoletetris, a C++ program written by bidepan2 and obtained from https://code.google.com/p/consoletetris/ under an MIT licence The code here has been simplified; had most of the OO aspects removed; translated into C#; and had a number of bugs fixed. I nonetheless owe a debt to bidepan2, which I freely acknowledge. One particular aspect of bidepan2's code I admire is the transform() function which rotates a block simply by changing it into another of the same shape but oriented 90° around This code is released an MIT licence. http://www.opensource.org/licenses/mit-license.php Peter Wilkinson www.quanglewangle.com */ /* to change the speed do a find on Sleep. The number is how many milliseconds the program sleeps for each loop */ using System; namespace citrus { class Program { static int x; static int y; static blocktype type; const int ROW = 14; const int COL = 18; static int[,] surface = new int[ROW, COL]; enum movedir { moveleft, moveright, movedown }; enum blocktype { IV, IH, // I bar up and on side SQU, // all squares look same! L1, L2, L3, L4, // lefthand Ls, rotated RL1, RL2, RL3, RL4, // righthand Ls rotated T1, T2, T3, T4, // Ts, rotated Z1, Z2, // Zs, rotated RZ1, RZ2, // righthand Zs rotated }; static int NUMBER_OF_BLOCKTYPES = Enum.GetValues(typeof(blocktype)).Length; // colors of blocks - must be in same order as blocklayouts (below) static ConsoleColor[] blockcolors = { ConsoleColor.Red, ConsoleColor.Red, ConsoleColor.White, ConsoleColor.Green, ConsoleColor.Green, ConsoleColor.Green, ConsoleColor.Green, ConsoleColor.Blue, ConsoleColor.Blue, ConsoleColor.Blue, ConsoleColor.Blue, ConsoleColor.Yellow, ConsoleColor.Yellow, ConsoleColor.Yellow, ConsoleColor.Yellow, ConsoleColor.Cyan, ConsoleColor.Cyan, ConsoleColor.Magenta, ConsoleColor.Magenta, }; // bitmaps of blocks - must be in same order as in blockcolors static int[,,] blocklayouts = { { { 1, 0, 0, 0 }, /* IV */ { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 } }, { { 1, 1, 1, 1 }, /* IH */ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 0, 0 }, /* SQU */ { 1, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 0, 0, 0 }, /* L1 */ { 1, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 1, 0 }, /* L2 */ { 1, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 0, 0 }, /* L3 */ { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 0, 1, 0 }, /* L4 */ { 1, 1, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 1, 0, 0 }, /* RL1 */ { 0, 1, 0, 0 }, { 1, 1, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 0, 0, 0 }, /* RL2 */ { 1, 1, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 0, 0 }, /* RL3 */ { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 1, 0 }, /* RL4 */ { 0, 0, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 1, 0 }, /* T1 */ { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 1, 0, 0 }, /* T2 */ { 1, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 1, 0, 0 }, /* T3 */ { 1, 1, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 0, 0, 0 }, /* T4 */ { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 1, 0, 0 }, /* Z1 */ { 0, 1, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 1, 0, 0 }, /* Z2 */ { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 1, 1, 0 }, /* RZ1 */ { 1, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 1, 0, 0, 0 }, /* RZ2 */ { 1, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 } }, }; static void drawActiveBlock() { for (int i=0; i<4; i++){ for (int j=0; j<4; j++){ if (blocklayouts[(int) type,j,i] > 0){ drawAt(' ' , blockcolors[(int) type], i+x,j+y); }else{ // drawAt(' ' , ConsoleColor.White, i+x, j+y); } } } } // transforming (rotating) is achived by turning one type of block into another static void transform(){ switch (type) { case blocktype.IH: type = blocktype.IV; break; case blocktype.IV: type = blocktype.IH; break; case blocktype.SQU: type = blocktype.SQU;break; case blocktype.L1: type = blocktype.L2; break; case blocktype.L2: type = blocktype.L3; break; case blocktype.L3: type = blocktype.L4; break; case blocktype.L4: type = blocktype.L1; break; case blocktype.RL1: type = blocktype.RL2; break; case blocktype.RL2: type = blocktype.RL3; break; case blocktype.RL3: type = blocktype.RL4; break; case blocktype.RL4: type = blocktype.RL1; break; case blocktype.T1: type = blocktype.T2; break; case blocktype.T2: type = blocktype.T3; break; case blocktype.T3: type = blocktype.T4; break; case blocktype.T4: type = blocktype.T1; break; case blocktype.Z1: type = blocktype.Z2; break; case blocktype.Z2: type = blocktype.Z1; break; case blocktype.RZ1: type = blocktype.RZ2; break; case blocktype.RZ2: type = blocktype.RZ1; break; } // some blocks can transform off the right hand side of the surface, so bring them back on int maxOff = 0; for (int r=0; r<4; r++){ for (int c=3; c>=0; c--){ if (blocklayouts[(int) type,r,c]!=0) { // if there is a painted tile at this pos in bounding box... if(x+c >= COL) { // its off the right hand side if(c > maxOff) { maxOff = c; // its the bigest off we have seen } } } } } x = (x-maxOff); // some blocks can transform off the bottom if rotated very close, so push them up for (int r=3; r>0; r--){ // start three cells below and work up for (int c=0; c<4; c++){ if (blocklayouts[(int) type,r,c]!=0) { // if there is a painted tile at this pos in bounding box... if(y+r > ROW) { // it would be off bottom of surface y = y-r; // bring it back by moving it up } } } } } // tests if a line is full - used after land to see if a line needs erasing static bool linefull(int row ) { for (int j=0; j1; r--){ for(int c=0; c 0){ drawAt(' ' , ConsoleColor.Gray, i+x,j+y); } } } } // create a new active block of random type at the middle of the top of the surface static void makeNewActiveBlock() { x = (COL-4) / 2; y = 0; Random random = new Random(); type = (blocktype) random.Next(0, NUMBER_OF_BLOCKTYPES); } // move the active block one cell in direction md - doesn't test if it is safe to move static void moveActiveBlock(movedir md){ switch (md) { case movedir.moveleft: x--; break; case movedir.moveright: x++; break; case movedir.movedown: y++; break; } } // test if a block of type t in position x, y would collide or be off the surface static bool collision(blocktype t, int x, int y) { for (int r=0; r<4; r++){ for (int c=0; c<4; c++){ if (blocklayouts[(int) type,r,c]!=0) { // if there is a painted tile at this pos in bounding box... if (!(x+c >= 0 && x+c < COL && y+r < ROW)){ // will it be off the surface? return true; } if (surface[y+r,x+c] != -1){ // on the surface but not empty? return true; } } } } return false; } // clear the surface to grey, mark all the cells as empty (-1) static void clearSurface() { for (int r=0; r