#include "stdafx.h"

#include <stdlib.h>

#include <time.h>

#include <conio.h>

#include <Windows.h>

//---------------------------------------------------------------------------------------------------


HANDLE consoleHandle;


enum eColor : int

{

clBlack,

clBlue,

clGreen,

clCyan,

clRed,

clPurple,

clYellow,

clWhite,

clBright,

};


void initVideo()

{

CONSOLE_CURSOR_INFO info;

info.bVisible = false;

info.dwSize = 1;

consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);

SetConsoleCursorInfo(consoleHandle, &info);

}


void gotoxy(int x, int y)

{

COORD p;

p.X = x;

p.Y = y;

SetConsoleCursorPosition(consoleHandle, p);

}


void putTile(int x, int y, int color)

{

gotoxy(x * 2, y);

SetConsoleTextAttribute(consoleHandle, (color != 0) * ((clBright | color) << 4));

putchar(' ');

putchar(' ');

}


void putString(int x, int y, int color, const char* s)

{

gotoxy(x, y);

SetConsoleTextAttribute(consoleHandle, clBright | color);

puts(s);

}


void putStringValue(int x, int y, int colorS, int colorV, const char* s, int value)

{

gotoxy(x, y);

SetConsoleTextAttribute(consoleHandle, clBright | colorS);

printf(s);

SetConsoleTextAttribute(consoleHandle, clBright | colorV);

printf("d", value);

}

//---------------------------------------------------------------------------------------------------


const int blockPatterns[7][4][4] =

{

{

{ 0, 0, 0, 0 },

{ 0, 1, 1, 0 },

{ 0, 1, 1, 0 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 0, 0, 1, 0 },

{ 0, 1, 1, 1 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 0, 1, 1, 0 },

{ 0, 0, 1, 1 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 0, 0, 1, 1 },

{ 0, 1, 1, 0 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 0, 1, 1, 1 },

{ 0, 1, 0, 0 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 0, 1, 1, 1 },

{ 0, 0, 0, 1 },

{ 0, 0, 0, 0 },

},

{

{ 0, 0, 0, 0 },

{ 1, 1, 1, 1 },

{ 0, 0, 0, 0 },

{ 0, 0, 0, 0 },

},

};

//---------------------------------------------------------------------------------------------------


#define MAP_WIDTH 14

#define MAP_HEIGHT 29

#define LEVEL_LINES 20

#define LEVEL_MAX 18

#define UPPER_MARGIN 3

#define SCAN_COUNT 20


int map[MAP_HEIGHT][MAP_WIDTH];

int currentBlock[4][4];

int currentBlockIndex, nextBlockIndex;

int blockX, blockY;

int score, hiScore;

int level;

int lines;


void initStage();


void putCurrentBlock(int draw = 1)

{

for (int y = 0; y < 4; ++y)

for (int x = 0; x < 4; ++x)

if (currentBlock[y][x] && blockY + y >= UPPER_MARGIN)

putTile(13 + blockX + x, blockY + y - UPPER_MARGIN, (currentBlockIndex + 1) * draw);

}


void putNextBlock()

{

putString(61, 1, clRed, "Next Block");

for (int y = 0; y < 4; ++y)

for (int x = 0; x < 4; ++x)

putTile(31 + x, 3 + y, blockPatterns[nextBlockIndex][y][x] * (nextBlockIndex + 1));

}


bool isBlocked()

{

for (int y = 0; y < 4; ++y)

for (int x = 0; x < 4; ++x)

if (currentBlock[y][x] && map[blockY + y][blockX + x])

return true;

return false;

}


void rotate(int isCW = 1)

{

int tempBlock[4][4];

memmove(tempBlock, currentBlock, sizeof(tempBlock));

if (isCW) putCurrentBlock(0);

for (int y = 0; y < 4; ++y)

for (int x = 0; x < 4; ++x)

if (isCW) currentBlock[x][3 - y] = tempBlock[y][x];

else currentBlock[3 - x][y] = tempBlock[y][x];

if (isBlocked())

rotate(!isCW);

else putCurrentBlock();

}


void bringNext()

{

blockX = 5;

blockY = 0;

memmove(currentBlock, blockPatterns[currentBlockIndex = nextBlockIndex], sizeof(currentBlock));

nextBlockIndex = rand() % 7;

putNextBlock();

if (isBlocked())

{

putString(35, 11, clRed, "           ");

putString(35, 12, clRed, " Game Over ");

putString(35, 13, clRed, "           ");

Sleep(3000);

initStage();

}

}


void drawMap()

{

putStringValue(5, 1, clRed, clYellow, "HiScore", hiScore);

putStringValue(5, 2, clRed, clYellow, "  Score", score);

putStringValue(5, 3, clRed, clYellow, "  Level", level);

for (int y = UPPER_MARGIN; y < MAP_HEIGHT - 1; ++y)

for (int x = 1; x < MAP_WIDTH - 1; ++x)

putTile(13 + x, y - UPPER_MARGIN, map[y][x]);

}


void release()

{

for (int y = 0; y < 4; ++y)

for (int x = 0; x < 4; ++x)

if (currentBlock[y][x]) map[blockY + y][blockX + x] = currentBlockIndex + 1;

int combo = 0;

for (int y = 3; y >= combo; --y)

{

if (blockY + y < MAP_HEIGHT - 2)

{

int sum = 0;

for (int x = 2; x < 12; ++x)

sum += map[blockY + y][x] != 0;

if (sum == 10)

{

combo++;

score += 25 * combo;

if (score > hiScore) hiScore = score;

for (int yy = blockY + y; yy > 0; --yy)

for (int xx = 2; xx < 12; ++xx)

map[yy][xx] = map[yy - 1][xx];

for (int xx = 2; xx < 12; ++xx)

map[0][xx] = 0;

if (++lines / LEVEL_LINES > level && level < LEVEL_MAX) level++;

drawMap();

y++;

}

}

}

bringNext();

}


void move(int dx, int dy)

{

putCurrentBlock(0);

blockX += dx;

if (dx && isBlocked()) blockX -= dx;

for (int y = 0; y < dy; y++)

{

blockY++;

if (isBlocked())

{

blockY--;

putCurrentBlock();

release();

return;

}

}

putCurrentBlock();

}


void initStage()

{

lines = 0;

level = 1;

srand((unsigned)time(NULL));

for (int y = 0; y < MAP_HEIGHT; ++y)

for (int x = 0; x < MAP_WIDTH; ++x)

map[y][x] = (x < 2) || (x > 11) || (y > MAP_HEIGHT - 3);

drawMap();

bringNext();

bringNext();

putCurrentBlock();

}

//---------------------------------------------------------------------------------------------------


int _tmain(int argc, _TCHAR* argv[])

{

initVideo();

initStage();

while (1)

{

for (int i = 0; i < SCAN_COUNT - level; ++i)

{

int isKey = false;

for (; i < SCAN_COUNT - level && !(isKey = _kbhit()); ++i) Sleep(15);

if (isKey)

{

switch (int key = _getch())

{

case 72: // UP

rotate();

break;

case 75: // LEFT

move(-1, 0);

break;

case 77: // RIGHT

move(1, 0);

break;

case 80: // DOWN

move(0, 1);

i = -1;

break;

case 32: // SPACE

move(0, MAP_HEIGHT);

i = -1;

break;

}

}

if (i < SCAN_COUNT - level) Sleep(15);

}

move(0, 1);

}

return 0;

}

//---------------------------------------------------------------------------------------------------