Last active
August 29, 2015 14:11
-
-
Save MORTAL2000/e9adc83b3d9c59d2ba71 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <algorithm> | |
#include <random> | |
#if defined(__linux__) || defined(__APPLE__) | |
/***************************************************************************** | |
kbhit() and getch() for Linux/UNIX | |
Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer | |
Release date: ? | |
This code is public domain (no copyright). | |
You can do whatever you want with it. | |
****************************************************************************/ | |
#include <sys/time.h> /* struct timeval, select() */ | |
/* ICANON, ECHO, TCSANOW, struct termios */ | |
#include <termios.h> /* tcgetattr(), tcsetattr() */ | |
#include <stdlib.h> /* atexit(), exit() */ | |
#include <unistd.h> /* read() */ | |
#include <stdio.h> /* printf() */ | |
static struct termios g_old_kbd_mode; | |
static void cooked(void) | |
{ | |
tcsetattr(0, TCSANOW, &g_old_kbd_mode); | |
} | |
static void raw(void) | |
{ | |
static char init; | |
/**/ | |
struct termios new_kbd_mode; | |
if (init) | |
{ | |
return; | |
} | |
/* put keyboard (stdin, actually) in raw, unbuffered mode */ | |
tcgetattr(0, &g_old_kbd_mode); | |
memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); | |
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); | |
new_kbd_mode.c_cc[VTIME] = 0; | |
new_kbd_mode.c_cc[VMIN] = 1; | |
tcsetattr(0, TCSANOW, &new_kbd_mode); | |
/* when we exit, go back to normal, "cooked" mode */ | |
atexit(cooked); | |
init = 1; | |
} | |
static int _kbhit(void) | |
{ | |
struct timeval timeout; | |
fd_set read_handles; | |
int status; | |
raw(); | |
/* check stdin (fd 0) for activity */ | |
FD_ZERO(&read_handles); | |
FD_SET(0, &read_handles); | |
timeout.tv_sec = timeout.tv_usec = 0; | |
status = select(0 + 1, &read_handles, NULL, NULL, &timeout); | |
if (status < 0) | |
{ | |
printf("select() failed in kbhit()\n"); | |
exit(1); | |
} | |
return status; | |
} | |
static int _getch(void) | |
{ | |
unsigned char temp; | |
raw(); | |
/* stdin = fd 0 */ | |
if (read(0, &temp, 1) != 1) | |
{ | |
return 0; | |
} | |
return temp; | |
} | |
struct COORD { short X; short Y; }; | |
bool gotoxy(unsigned short x = 1, unsigned short y = 1) | |
{ | |
if ((x == 0) || (y == 0)) | |
{ | |
return false; | |
} | |
std::cout << "\x1B[" << y << ";" << x << "H"; | |
return true | |
} | |
void clearScreen(bool moveToStart = true) | |
{ | |
std::cout << "\x1B[2J"; | |
if (moveToStart) | |
{ | |
gotoxy(1, 1); | |
} | |
} | |
inline | |
void print(const std::string& str, COORD& coord) | |
{ | |
gotoxy(coord.X, coord.Y); | |
std::cout << str << std::flush; | |
} | |
inline | |
void print(const char* str, COORD& coord) | |
{ | |
gotoxy(coord.X, coord.Y); | |
std::cout << str << std::flush; | |
} | |
inline | |
void print(char c, COORD& coord) | |
{ | |
gotoxy(coord.X, coord.Y); | |
std::cout << c << std::flush; | |
} | |
#elif _WIN32 | |
#include <conio.h> /* kbhit(), getch() */ | |
#include <Windows.h> | |
#include <tchar.h> | |
static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
static CONSOLE_SCREEN_BUFFER_INFO csbi; | |
void clearScreen() | |
{ | |
DWORD count; | |
DWORD cellCount; | |
COORD homeCoords = { 0, 0 }; | |
if (!GetConsoleScreenBufferInfo(hStdOut, &csbi)) | |
std::cerr << "ERROR GetConsoleScreenBufferInfo - clearScreen : " | |
<< GetLastError() << std::endl; | |
cellCount = csbi.dwSize.X *csbi.dwSize.Y; | |
FillConsoleOutputCharacter( hStdOut, (TCHAR) ' ',cellCount, homeCoords, &count); | |
FillConsoleOutputAttribute( hStdOut, csbi.wAttributes, cellCount, homeCoords, &count); | |
SetConsoleCursorPosition(hStdOut, homeCoords); | |
} | |
void gotoxy(int x, int y) | |
{ | |
COORD coord{ x, y }; | |
SetConsoleCursorPosition(hStdOut, coord); | |
} | |
static DWORD cCharsWritten; | |
inline | |
void print(const std::string& str, COORD& coord) | |
{ | |
WriteConsoleOutputCharacter(hStdOut, str.c_str(), str.length(), coord, &cCharsWritten); | |
} | |
inline | |
void print(const TCHAR* str, COORD& coord) | |
{ | |
WriteConsoleOutputCharacter(hStdOut, str, _tcslen(str), coord, &cCharsWritten); | |
} | |
inline | |
void print(TCHAR c, COORD& coord) | |
{ | |
FillConsoleOutputCharacter(hStdOut, c, 1, coord, &cCharsWritten); | |
} | |
#else | |
#error "OS not supported!" | |
#endif | |
using Matrix = std::vector<std::vector<int>>; | |
const std::initializer_list<size_t> ilBlock = | |
{ | |
0, 1, 2, 3 | |
}; | |
const std::initializer_list<size_t> ilBoard = | |
{ | |
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 | |
}; | |
const std::initializer_list<size_t> ilBoardRow = | |
{ | |
0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | |
}; | |
static const std::vector<Matrix> blockList = | |
{ | |
{ | |
{ 0, 1, 0, 0 }, | |
{ 0, 1, 0, 0 }, | |
{ 0, 1, 0, 0 }, | |
{ 0, 1, 0, 0 } | |
}, | |
{ | |
{ 0, 0, 0, 0 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 1, 0, 0 }, | |
{ 0, 1, 0, 0 } | |
}, | |
{ | |
{ 0, 0, 1, 0 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 1, 0, 0 }, | |
{ 0, 0, 0, 0 } | |
}, | |
{ | |
{ 0, 1, 0, 0 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 0, 1, 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 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 0, 0, 0 } | |
}, | |
{ | |
{ 0, 0, 0, 0 }, | |
{ 0, 1, 1, 0 }, | |
{ 0, 0, 1, 0 }, | |
{ 0, 0, 1, 0 } | |
} | |
}; | |
class NonCopyable | |
{ | |
public: | |
NonCopyable() = default; | |
private: | |
NonCopyable(const NonCopyable &) = delete; | |
NonCopyable(const NonCopyable &&) = delete; | |
NonCopyable& operator = (const NonCopyable&) = delete; | |
}; | |
class Random : private NonCopyable | |
{ | |
public: | |
Random(int min, int max) | |
: mUniformDistribution(min, max) | |
{} | |
int operator()() | |
{ | |
return mUniformDistribution(mEngine); | |
} | |
private: | |
std::default_random_engine mEngine{ std::random_device()() }; | |
std::uniform_int_distribution<int> mUniformDistribution; | |
}; | |
class Tetris : private NonCopyable | |
{ | |
public: | |
Tetris() | |
{ | |
mBlock.resize(ilBlock.size(), std::vector<int>(ilBlock.size(), 0)); | |
mBoard.resize(ilBoard.size(), std::vector<int>(ilBoardRow.size(), 0)); | |
mStage.resize(ilBoard.size(), std::vector<int>(ilBoardRow.size(), 0)); | |
}; | |
private: | |
bool makeBlocks(); | |
void moveBlock(int, int); | |
bool isCollide(int, int); | |
bool rotateBlock(); | |
void isFull(); | |
void checkLine(); | |
virtual void display(){}; | |
virtual void GameOverScreen() {}; | |
Random getRandom{ 0, static_cast<int>(blockList.size() - 1) }; | |
Matrix mStage; | |
Matrix mBlock; | |
int y = 0; | |
int x = ilBlock.size(); | |
protected: | |
void initField(); | |
void spawnBlock(); | |
void userInput(); | |
bool gameOver = false; | |
Matrix mBoard; | |
}; | |
void Tetris::initField() | |
{ | |
for (auto i = ilBoard.begin(); i != ilBoard.end() - 1; ++i) | |
{ | |
for (auto j = ilBoardRow.begin(); j != ilBoardRow.end() - 1; ++j) | |
{ | |
if ((*j == 0) || (*j == ilBoardRow.size() - 2) || (*i == ilBoard.size() - 2)) | |
{ | |
mBoard[*i][*j] = mStage[*i][*j] = 9; | |
} | |
else | |
{ | |
mBoard[*i][*j] = mStage[*i][*j] = 0; | |
} | |
} | |
} | |
makeBlocks(); | |
display(); | |
} | |
bool Tetris::makeBlocks() | |
{ | |
x = ilBlock.size(); | |
y = 0; | |
int blockType = getRandom(); | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
mBlock[i][j] = blockList[blockType][i][j]; | |
} | |
} | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
mBoard[i][j + ilBlock.size()] = mStage[i][j + ilBlock.size()] + mBlock[i][j]; | |
} | |
} | |
return false; | |
} | |
void Tetris::isFull() | |
{ | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
if (mBoard[i][j + ilBlock.size()] > 1) | |
{ | |
gameOver = true; | |
} | |
} | |
} | |
} | |
void Tetris::moveBlock(int x2, int y2) | |
{ | |
//Remove block | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
mBoard[y + i][x + j] -= mBlock[i][j]; | |
} | |
} | |
//Update coordinates | |
x = x2; | |
y = y2; | |
// assign a block with the updated value | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
mBoard[y + i][x + j] += mBlock[i][j]; | |
} | |
} | |
display(); | |
} | |
void Tetris::checkLine() | |
{ | |
std::copy(mBoard.begin(), mBoard.end(), mStage.begin()); | |
for (auto i = ilBoard.begin() + 1; i != ilBoard.end() - 2; ++i) | |
{ | |
bool isCompeteLine = true; | |
for (auto j = ilBoardRow.begin() + 1; j != ilBoardRow.end() - 1; ++j) | |
{ | |
if (mStage[*i][*j] == 0) | |
{ | |
isCompeteLine = false; | |
} | |
} | |
if (isCompeteLine) | |
{ | |
for (auto k : ilBlock) | |
{ | |
std::copy(mStage[*i - 1 - k].begin(), mStage[*i - 1 - k].end(), mStage[*i - k].begin()); | |
} | |
} | |
} | |
std::copy(mStage.begin(), mStage.end(), mBoard.begin()); | |
} | |
bool Tetris::isCollide(int x, int y) | |
{ | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
if (mBlock[i][j] && mStage[y + i][x + j] != 0) | |
{ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
void Tetris::userInput() | |
{ | |
const char key = _getch(); | |
switch (key) | |
{ | |
case 'd': | |
if (!isCollide(x + 1, y)) | |
{ | |
moveBlock(x + 1, y); | |
} | |
break; | |
case 'a': | |
if (!isCollide(x - 1, y)) | |
{ | |
moveBlock(x - 1, y); | |
} | |
break; | |
case 's': | |
if (!isCollide(x, y + 1)) | |
{ | |
moveBlock(x, y + 1); | |
} | |
break; | |
case ' ': | |
rotateBlock(); | |
} | |
} | |
bool Tetris::rotateBlock() | |
{ | |
Matrix temp(ilBlock.size(), std::vector<int>(ilBlock.size(), 0)); | |
std::copy(mBlock.begin(), mBlock.end(), temp.begin()); | |
// Rotate | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
if (i < j) | |
{ | |
std::swap(mBlock[i][j], mBlock[j][i]); | |
} | |
} | |
std::reverse(mBlock[i].begin(), mBlock[i].end()); | |
} | |
if (isCollide(x, y)) | |
{ // And stop if it overlaps not be rotated | |
std::copy(temp.begin(), temp.end(), mBlock.begin()); | |
return true; | |
} | |
for (auto i : ilBlock) | |
{ | |
for (auto j : ilBlock) | |
{ | |
mBoard[y + i][x + j] -= temp[i][j]; | |
mBoard[y + i][x + j] += mBlock[i][j]; | |
} | |
} | |
display(); | |
return false; | |
} | |
void Tetris::spawnBlock() | |
{ | |
if (!isCollide(x, y + 1)) | |
{ | |
moveBlock(x, y + 1); | |
} | |
else | |
{ | |
checkLine(); | |
makeBlocks(); | |
isFull(); | |
display(); | |
} | |
} | |
class Game : public Tetris | |
{ | |
public: | |
Game() = default; | |
int menu(); | |
void gameLoop(); | |
private: | |
void introScreen(); | |
virtual void display(); | |
virtual void gameOverScreen(); | |
size_t GAMESPEED = 20000; | |
}; | |
void Game::gameOverScreen() | |
{ | |
COORD coord = { 0, 0 }; | |
coord.Y += 2; | |
print(" ##### # # # ####### ####### # # ####### ######", coord); coord.Y++; | |
print("# # # # ## ## # # # # # # # #", coord);coord.Y++; | |
print("# # # # # # # # # # # # # # #", coord);coord.Y++; | |
print("# #### # # # # # ##### # # # # ##### ######", coord); coord.Y++; | |
print("# # ####### # # # # # # # # # #", coord); coord.Y++; | |
print("# # # # # # # # # # # # # #", coord); coord.Y++; | |
print(" ##### # # # # ####### ####### # ####### # #", coord);coord.Y += 2; | |
print("Press any key and enter", coord); | |
gotoxy((coord.X = strlen("Press any key and enter") + 1), coord.Y++); | |
char a; | |
std::cin >> a; | |
} | |
void Game::gameLoop() | |
{ | |
size_t time = 0; | |
initField(); | |
while (!gameOver) | |
{ | |
if (_kbhit()) | |
{ | |
userInput(); | |
} | |
if (time < GAMESPEED) | |
{ | |
time++; | |
} | |
else | |
{ | |
spawnBlock(); | |
time = 0; | |
} | |
} | |
} | |
int Game::menu() | |
{ | |
introScreen(); | |
int select_num = 0; | |
std::cin >> select_num; | |
switch (select_num) | |
{ | |
case 1: | |
case 2: | |
case 3: | |
break; | |
default: | |
select_num = 0; | |
break; | |
} | |
return select_num; | |
} | |
void Game::introScreen() | |
{ | |
clearScreen(); | |
COORD coord = { 0, 0 }; | |
print("#==============================================================================#", coord); coord.Y += 2; | |
print("####### ####### ####### ###### ### #####", coord); coord.Y++; | |
print(" # # # # # # # #", coord); coord.Y++; | |
print(" # # # # # # #", coord); coord.Y++; | |
print(" # ##### # ###### # #####", coord); coord.Y++; | |
print(" # # # # # # #", coord); coord.Y++; | |
print(" # # # # # # # #", coord); coord.Y++; | |
print(" # ####### # # # ### ##### made for fun ", coord); coord.Y += 5; | |
print(" <Menu>", coord); coord.Y++; | |
print(" 1: Start Game", coord); coord.Y++; | |
print(" 2: Quit", coord); coord.Y += 2; | |
print("#==============================================================================#", coord); coord.Y++; | |
print("Choose >> ", coord); | |
coord.X = strlen("Choose >> "); | |
gotoxy(coord.X, coord.Y); | |
} | |
void Game::display() | |
{ | |
clearScreen(); | |
for (auto i : ilBoard) | |
{ | |
for (auto j : ilBoardRow) | |
{ | |
switch (mBoard[i][j]) | |
{ | |
case 0: | |
std::cout << ' '; | |
break; | |
case 9: | |
std::cout << '@'; | |
break; | |
default: | |
std::cout << '#'; | |
break; | |
} | |
} | |
std::cout << std::endl; | |
} | |
COORD coord = { 0, static_cast<short>(ilBoard.size()) }; | |
print(" A: left S: down D: right Rotation[Space]", coord); | |
if (gameOver) | |
{ | |
clearScreen(); | |
gameOverScreen(); | |
} | |
} | |
int main() | |
{ | |
Game game; | |
switch (game.menu()) | |
{ | |
case 1: | |
game.gameLoop(); | |
break; | |
case 2: | |
return 0; | |
default: | |
COORD coord = { 0, 0 }; | |
print("Choose 1~2", coord); | |
return -1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment