Last active
August 29, 2015 14:11
-
-
Save MORTAL2000/f4701a1f70e7e1e038fc 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 <iomanip> | |
#include <iostream> | |
#include <vector> | |
#include <random> | |
#ifdef __linux__ | |
/***************************************************************************** | |
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"; | |
} | |
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; | |
coord.X = x; | |
coord.Y = y; | |
SetConsoleCursorPosition(hStdOut, coord); | |
} | |
COORD getXY() | |
{ | |
if (!GetConsoleScreenBufferInfo(hStdOut, &csbi)) | |
std::cerr << "ERROR GetConsoleScreenBufferInfo - getXY : " | |
<< GetLastError() << std::endl; | |
COORD position; | |
position.X = csbi.dwCursorPosition.X; | |
position.Y = csbi.dwCursorPosition.Y; | |
return position; | |
} | |
SHORT getPosX() | |
{ | |
if (!GetConsoleScreenBufferInfo(hStdOut, &csbi)) | |
std::cerr << "ERROR GetConsoleScreenBufferInfo - getX : " | |
<< GetLastError() << std::endl; | |
return csbi.dwCursorPosition.X; | |
} | |
SHORT getPosY() | |
{ | |
if (!GetConsoleScreenBufferInfo(hStdOut, &csbi)) | |
std::cerr << "ERROR GetConsoleScreenBufferInfo - getY : " | |
<< GetLastError() << std::endl; | |
return csbi.dwCursorPosition.Y; | |
} | |
static DWORD cCharsWritten = 0; | |
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, bool dummy = true) | |
{ | |
FillConsoleOutputCharacter(hStdOut, c, 1, coord, &cCharsWritten); | |
} | |
#else | |
#error "OS not supported!" | |
#endif | |
using Matrix = std::vector<std::vector<int>>; | |
constexpr std::initializer_list<size_t> il = | |
{ | |
0, 1, 2, 3 | |
}; | |
constexpr 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 | |
}; | |
constexpr std::initializer_list<size_t> ilBoardRow = | |
{ | |
0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | |
}; | |
const size_t mSize = 22; | |
static 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 Block : private NonCopyable | |
{ | |
public: | |
Block() | |
{ | |
mBlock.resize(size(), std::vector<int>(size(), 0)); | |
mCoord.X = 0; | |
mCoord.Y = 0; | |
} | |
void rotate() | |
{ | |
++mRotationCount; | |
while (mRotationCount > ROTATIONS_IN_CIRCLE) | |
{ | |
mRotationCount -= ROTATIONS_IN_CIRCLE; | |
} | |
} | |
int& getDim(int row, int column) | |
{ | |
switch (mRotationCount % ROTATIONS_IN_CIRCLE) | |
{ | |
default: | |
return mBlock[row][column]; | |
case 1: | |
return mBlock[size() - column - 1][row]; | |
case 2: | |
return mBlock[size() - row - 1][size() - column - 1]; | |
case 3: | |
return mBlock[column][size() - row - 1]; | |
} | |
} | |
size_t size() const | |
{ | |
return il.size(); | |
} | |
private: | |
static const int ROTATIONS_IN_CIRCLE = 4; | |
int mRotationCount = 0; | |
COORD mCoord; | |
Matrix mBlock; | |
}; | |
class Board : private NonCopyable | |
{ | |
public: | |
Board() | |
{ | |
mField.resize(size(), std::vector<int>(rowSize(), 0)); | |
mCoord.X = 0; | |
mCoord.Y = 0; | |
} | |
int& getDim(int row, int column) | |
{ | |
return mField[row][column]; | |
} | |
size_t size() const | |
{ | |
return ilBoard.size(); | |
} | |
size_t rowSize() const | |
{ | |
return ilBoardRow.size(); | |
} | |
private: | |
COORD mCoord; | |
Matrix mField; | |
}; | |
class Collidable : private NonCopyable | |
{ | |
public: | |
Collidable() | |
{ | |
mStage.resize(size(), std::vector<int>(rowSize(), 0)); | |
mCoord.X = 0; | |
mCoord.Y = 0; | |
} | |
int& getDim(int row, int column) | |
{ | |
return mStage[row][column]; | |
} | |
size_t size() const | |
{ | |
return ilBoard.size(); | |
} | |
size_t rowSize() const | |
{ | |
return ilBoardRow.size(); | |
} | |
private: | |
COORD mCoord; | |
Matrix mStage; | |
}; | |
class Tetris : private NonCopyable | |
{ | |
public: | |
Tetris() | |
: board(), block(), stage() | |
{}; | |
private: | |
bool makeBlocks(); | |
void moveBlock(int, int); | |
void collidable(); | |
bool isCollide(int, int); | |
bool rotateBlock(); | |
virtual void display(){}; | |
virtual void GameOverScreen() {}; | |
Random getRandom{ 0, (il.size() - 1) }; | |
Collidable stage; | |
Block block; | |
int y = 0; | |
int x = block.size(); | |
protected: | |
void initField(); | |
void spawnBlock(); | |
void userInput(); | |
bool gameOver = false; | |
Board board; | |
}; | |
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)) | |
{ | |
board.getDim(*i, *j) = stage.getDim(*i, *j) = 9; | |
} | |
else | |
{ | |
board.getDim(*i, *j) = stage.getDim(*i, *j) = 0; | |
} | |
} | |
} | |
makeBlocks(); | |
display(); | |
} | |
bool Tetris::makeBlocks() | |
{ | |
x = block.size(); | |
y = 0; | |
int blockType = getRandom(); | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
block.getDim(i, j) = 0; | |
block.getDim(i, j) = blockList[blockType][i][j]; | |
} | |
} | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
board.getDim(i, j + il.size()) = stage.getDim(i, j + il.size()) + block.getDim(i, j); | |
if ( board.getDim(i, j + il.size() ) > 1) | |
{ | |
return gameOver = true; | |
} | |
} | |
} | |
return false; | |
} | |
void Tetris::moveBlock(int x2, int y2) | |
{ | |
//Remove block | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
board.getDim(y + i, x + j) -= block.getDim(i, j); | |
} | |
} | |
//Update coordinates | |
x = x2; | |
y = y2; | |
// assign a block with the updated value | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
board.getDim(y + i, x + j) += block.getDim(i, j); | |
} | |
} | |
display(); | |
} | |
void Tetris::collidable() | |
{ | |
for (auto i : ilBoard) | |
{ | |
for (auto j : ilBoardRow) | |
{ | |
stage.getDim(i, j) = board.getDim(i, j); | |
} | |
} | |
} | |
bool Tetris::isCollide(int x2, int y2) | |
{ | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
if (block.getDim(i, j) && stage.getDim(y2 + i, x2 + j) != 0) | |
{ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
void Tetris::userInput() | |
{ | |
char key; | |
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(il.size(), std::vector<int>(il.size(), 0)); | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
temp[i][j] = block.getDim(i, j); | |
} | |
} | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
block.getDim(i, j) = temp[il.size() - 1 - j][i]; | |
} | |
} | |
if (isCollide(x, y)) | |
{ // And stop if it overlaps not be rotated | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
block.getDim(i, j) = temp[i][j]; | |
} | |
} | |
return true; | |
} | |
for (auto i : il) | |
{ | |
for (auto j : il) | |
{ | |
board.getDim(y + i, x + j) -= temp[i][j]; | |
board.getDim(y + i, x + j) += block.getDim(i, j); | |
} | |
} | |
display(); | |
return false; | |
} | |
void Tetris::spawnBlock() | |
{ | |
if (!isCollide(x, y + 1)) | |
{ | |
moveBlock(x, y + 1); | |
} | |
else | |
{ | |
collidable(); | |
makeBlocks(); | |
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 += 4; | |
coord.Y++; | |
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 (board.getDim(i, j)) | |
{ | |
case 0: | |
std::cout << ' '; | |
break; | |
case 9: | |
std::cout << '@'; | |
break; | |
default: | |
std::cout << '#'; | |
break; | |
} | |
} | |
std::cout << std::endl; | |
} | |
COORD coord = { 0, 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
first draft