Created
May 5, 2012 16:39
-
-
Save hisui/2603882 to your computer and use it in GitHub Desktop.
プログラミング教材用テトリス(Win32)
This file contains hidden or 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
// build: g++ tetris.cpp -o tetris.exe gdi32.dll -Xlinker --enable-stdcall-fixup | |
#include <windows.h> | |
#include <time.h> | |
#include <algorithm> | |
#include <stdexcept> | |
static const int CELL_DIM_X = 17; | |
static const int CELL_DIM_Y = 17; | |
// ブロックを構成するセルを表現 | |
struct Cell | |
{ | |
int h; // 水平位置 | |
int v; // 垂直位置 | |
}; | |
enum { NUM_CASES = 7, NUM_CELLS = 4 }; | |
static const Cell BLOCK_CASES[NUM_CASES][NUM_CELLS] = { | |
{{0,0},{1,0},{2,0},{3,0}}, | |
{{0,0},{1,0},{2,0},{2,1}}, | |
{{0,1},{1,1},{2,1},{2,0}}, | |
{{0,0},{1,0},{2,0},{1,1}}, | |
{{0,0},{1,0},{0,1},{1,1}}, | |
{{0,0},{1,0},{1,1},{1,2}}, | |
{{0,1},{1,1},{1,0},{2,0}} | |
}; | |
// 現在落ちてるブロックの状態 | |
static const Cell *block_cells; // ブロックのパターン | |
static int block_angle90; // 回転 | |
static int block_ch; // 回転の重心 | |
static int block_cv; | |
static int block_base_h; // 平行移動 | |
static int block_base_v; | |
static int block_color = 0; // ブロックの色 | |
// 既に落ちてるブロックを非0で表現する表 | |
enum { STAGE_H = 11, STAGE_V = 20 }; | |
static int stage[STAGE_V][STAGE_H] = {0}; | |
// ゲームウィンドウ | |
static HWND game_window; | |
// クライアント領域の大きさ | |
static const int CLIENT_DIM_X = CELL_DIM_X * STAGE_H; | |
static const int CLIENT_DIM_Y = CELL_DIM_Y * STAGE_V; | |
// ブロックのカラーパターン | |
enum { NUM_COLORS = 6 }; | |
static const COLORREF BLOCK_RGB_COLORS[NUM_COLORS] = { | |
RGB(255, 0, 0), | |
RGB( 0, 255, 0), | |
RGB( 0, 0, 255), | |
RGB(255, 255, 0), | |
RGB( 0, 255, 255), | |
RGB(255, 0, 255) | |
}; | |
// [cos(rad) -sin(rad)] | |
// [sin(rad) cos(rad)] | |
void block_rotate(int &h, int &v, int angle90) | |
{ | |
switch(angle90 % 4) { | |
case 3: std::swap(h, v); v *= -1; break; | |
case 1: std::swap(h, v); h *= -1; break; | |
case 2: | |
h *= -1; | |
v *= -1; | |
break; | |
} | |
} | |
// ブロックの各セルの現在位置を取得 | |
void block_location(Cell *cells) | |
{ | |
for(int i = 0; i < NUM_CELLS; ++i) { | |
const Cell &cell = block_cells[i]; | |
int h = cell.h - block_ch; | |
int v = cell.v - block_cv; | |
block_rotate(h, v, block_angle90); | |
cells->h = h + block_ch + block_base_h; | |
cells->v = v + block_cv + block_base_v; | |
cells++; | |
} | |
} | |
// ブロックの当たり判定を行う | |
bool block_hittest() | |
{ | |
Cell cells[NUM_CELLS]; | |
block_location(cells); | |
for(int i = 0; i < NUM_CELLS; ++i) { | |
int h = cells[i].h; | |
int v = cells[i].v; | |
if(!(0 <= h && h < STAGE_H) || v >= STAGE_V || stage[v][h]) { | |
return true; | |
} | |
} | |
return false; | |
} | |
// ブロックを新しく落とす | |
void block_drop() | |
{ | |
int k = rand() % NUM_CASES; | |
block_cells = BLOCK_CASES[k]; | |
block_ch = block_base_h = 0; | |
block_cv = block_base_v = 0; | |
block_base_h = STAGE_H / 2 - 2;// てきとー | |
block_angle90 = 0; | |
block_color = (block_color + 1) % NUM_COLORS; | |
for(int i = 0; i < NUM_CELLS; ++i) { | |
block_ch += block_cells[i].h; | |
block_cv += block_cells[i].v; | |
} | |
block_ch = int(block_ch / double(NUM_CELLS)); | |
block_cv = int(block_cv / double(NUM_CELLS)); | |
if(block_hittest()) { // ゲーモーバー | |
exit(-1); // <(^_^;) めんどくっさいので即終了 | |
} | |
} | |
// ブロックを一つ下にずらす | |
void block_down() | |
{ | |
// 落下の処理 | |
++block_base_v; | |
if(block_hittest()) { // 地面についた! | |
--block_base_v; | |
Cell cells[NUM_CELLS]; | |
block_location(cells); | |
for(int i = 0; i < NUM_CELLS; ++i) { | |
stage[cells[i].v][cells[i].h] = block_color + 1; | |
} | |
block_drop(); | |
int n = -1; | |
// ブロックの除去 | |
for(int i = STAGE_V - 1; i >= 0; --i) { | |
bool clear = true; | |
for(int j = 0; j < STAGE_H; ++j) { | |
if(!stage[i][j]) { | |
clear = false; | |
if(n > i) { | |
memcpy(stage[n--], stage[i], sizeof(int) * STAGE_H); | |
} | |
break; | |
} | |
} | |
if(clear) n = (std::max)(n, i); | |
} | |
memset(stage, 0, sizeof(int) * STAGE_H * (n + 1)); | |
} | |
} | |
// セルを描画する | |
void show_cell(HDC hDC, int h, int v, int color) | |
{ | |
static HBRUSH color_brushes[NUM_COLORS] = {0}; | |
if(!*color_brushes) { | |
for(int i = 0; i < NUM_COLORS; ++i) { | |
color_brushes[i] = CreateSolidBrush(BLOCK_RGB_COLORS[i]); | |
} | |
} | |
SelectObject(hDC, color_brushes[color]); | |
Rectangle(hDC, | |
h * CELL_DIM_X, | |
v * CELL_DIM_Y, | |
h * CELL_DIM_X + CELL_DIM_X + 1, | |
v * CELL_DIM_Y + CELL_DIM_Y + 1); | |
} | |
// フレームの描画を行う | |
void show_frame(HDC hDC) | |
{ | |
static const HBRUSH BG_BRUSH = CreateHatchBrush( | |
HS_DIAGCROSS, RGB(130, 130, 90)); | |
// 背景 | |
RECT bg = {0, 0, CLIENT_DIM_X, CLIENT_DIM_Y}; | |
FillRect(hDC, &bg, BG_BRUSH); | |
// 落ちていくブロックの描画 | |
Cell cells[NUM_CELLS]; | |
block_location(cells); | |
for(int i = 0; i < NUM_CELLS; ++i) { | |
show_cell(hDC, cells[i].h, cells[i].v, block_color); | |
} | |
// 既に落ちたブロックの描画 | |
for(int i = STAGE_V - 1; i >= 0; --i) { | |
bool none = true; | |
for(int j = 0; j < STAGE_H; ++j) { | |
if(stage[i][j]) { | |
none = false; | |
show_cell(hDC, j, i, stage[i][j] - 1); | |
} | |
} | |
if(none) { | |
break; | |
} | |
} | |
} | |
// ゲームの状態を進行 | |
void PASCAL game_timer(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) | |
{ | |
block_down(); | |
InvalidateRect(game_window, NULL, TRUE); | |
} | |
// ゲームを開始 | |
void game_init() | |
{ | |
if(!SetTimer(game_window, 0, 700u, &game_timer)) { | |
throw std::runtime_error("タイマーの設定に失敗!"); | |
} | |
srand(time(NULL)); | |
block_drop(); | |
} | |
// WndProc | |
LRESULT CALLBACK window_procedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) | |
{ | |
switch(msg) { | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
break; | |
case WM_CREATE: | |
game_init(); | |
break; | |
case WM_KEYDOWN: | |
switch(wp) { | |
case VK_SPACE: | |
++block_angle90; // ブロックの回転 | |
if(block_hittest()) --block_angle90; | |
break; | |
case VK_LEFT: | |
--block_base_h; // ブロックを左にずらす | |
if(block_hittest()) ++block_base_h; | |
break; | |
case VK_RIGHT: | |
++block_base_h; // ブロックを右にずらす | |
if(block_hittest()) --block_base_h; | |
break; | |
case VK_DOWN: | |
block_down(); // ブロックの落下を促す | |
break; | |
} | |
InvalidateRect(game_window, NULL, TRUE); | |
break; | |
case WM_PAINT: | |
{ | |
// ダブルバッファリングで描画 | |
HDC hDC = GetDC(hWnd); | |
HDC hCompatibleDC = CreateCompatibleDC(hDC); | |
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, CLIENT_DIM_X, CLIENT_DIM_Y); | |
HBITMAP hOldBitmap = HBITMAP(SelectObject(hCompatibleDC, hBitmap)); | |
RECT rect = {0, 0, CLIENT_DIM_X, CLIENT_DIM_Y}; | |
FillRect(hCompatibleDC, &rect, HBRUSH(GetStockObject(BLACK_BRUSH))); | |
show_frame(hCompatibleDC); | |
PAINTSTRUCT ps; | |
hDC = BeginPaint( hWnd, &ps ); | |
BitBlt(hDC, 0, 0, CLIENT_DIM_X, CLIENT_DIM_Y, hCompatibleDC, 0, 0, SRCCOPY); | |
EndPaint(hWnd, &ps); | |
SelectObject(hCompatibleDC, hOldBitmap); | |
DeleteObject(hBitmap); | |
DeleteDC(hCompatibleDC); | |
} | |
break; | |
} | |
return DefWindowProc(hWnd, msg, wp, lp); | |
} | |
int main(int argc, char **argv) | |
{ | |
WNDCLASSEX wc; | |
wc.cbSize = sizeof(WNDCLASSEX); | |
wc.style = CS_HREDRAW | CS_VREDRAW; | |
wc.lpfnWndProc = window_procedure; | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.hInstance = GetModuleHandle(NULL); | |
wc.hIcon = NULL; | |
wc.hCursor = NULL; | |
wc.hbrBackground = HBRUSH(GetStockObject(WHITE_BRUSH)); | |
wc.lpszMenuName = NULL; | |
wc.lpszClassName = "TETRIS_WINDOW"; | |
wc.hIconSm = NULL; | |
if(RegisterClassEx(&wc) == 0) { | |
throw std::runtime_error("ウィンドウクラスの作成に失敗!"); | |
} | |
enum { WINDOW_STYLE = WS_OVERLAPPEDWINDOW &~(WS_THICKFRAME | WS_MAXIMIZEBOX) }; | |
// ウィンドウサイズをクライアント領域のサイズに合わせる | |
RECT rect = {0, 0, CLIENT_DIM_X, CLIENT_DIM_Y}; | |
AdjustWindowRect(&rect, WINDOW_STYLE, FALSE); | |
game_window = CreateWindowEx( | |
WS_EX_TOPMOST, | |
"TETRIS_WINDOW", | |
"tetris", | |
WINDOW_STYLE, | |
200, | |
200, | |
rect.right - rect.left, | |
rect.bottom - rect.top, | |
NULL, NULL, | |
GetModuleHandle(NULL), | |
NULL); | |
ShowWindow(game_window, SW_SHOW); | |
UpdateWindow(game_window); | |
MSG msg; | |
while(GetMessage(&msg, NULL, 0, 0) > 0) { | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment