Created
June 20, 2020 09:53
-
-
Save neguse/39c4307d10d8048e6314d5e4dc29d938 to your computer and use it in GitHub Desktop.
10年前のテトリスと今のテトリス
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<windows.h> | |
#include <mmsystem.h> | |
#include <stdarg.h> | |
#include <vector> | |
#include <fstream> | |
namespace Debug { | |
using namespace std; | |
static const int DEBUG_BUF_SIZE = 1024; | |
void Trace(const char* fmt, ...){ | |
char buf[DEBUG_BUF_SIZE] = {'\0'}; | |
va_list args; | |
va_start(args, fmt); | |
vsprintf(buf, fmt, args); | |
va_end(args); | |
OutputDebugStringA(buf); | |
} | |
void Open(const char* fmt, ...){ | |
char buf[DEBUG_BUF_SIZE] = {'\0'}; | |
va_list args; | |
va_start(args, fmt); | |
vsprintf(buf, fmt, args); | |
va_end(args); | |
MessageBoxA(0, buf, "ERROR", MB_OK); | |
} | |
void Abort(){ | |
OutputDebugStringA("program aborted\n"); | |
abort(); | |
} | |
} // namespace Debug | |
#if _DEBUG | |
// デバッグ時 | |
#define TRACE(...) Debug::Trace(__VA_ARGS__) | |
#ifdef _WIN32 | |
// Visual C++用 | |
#define ASSERT_STR ("Assertion failed (%s) at %s(line:%d)\n") | |
#define ASSERT(e) ((e) ? (void)0 : ((Debug::Open(ASSERT_STR, #e, __FILE__, __LINE__), Debug::Abort()))) | |
#define VERIFY_STR ("Verify failed (%s) at %s(line:%d)\n") | |
#define VERIFY(e) ((e) ? (void)0 : ((Debug::Open(VERIFY_STR, #e, __FILE__, __LINE__), Debug::Abort()))) | |
#else | |
// GCC用 | |
#define ASSERT_STR ("Assertion failed (%s) at %s(line:%d) %s\n") | |
#define ASSERT(e) ((e) ? (void)0 : ((Lib::Debug::Trace(ASSERT_STR, #e, __FILE__, __LINE__, __func__), Lib::Debug::Abort()))) | |
#define VERIFY_STR ("Verify failed (%s) at %s(line:%d)\n") | |
#define VERIFY(e) ((e) ? (void)0 : ((Lib::Debug::Trace(VERIFY_STR, #e, __FILE__, __LINE__, __func__), Lib::Debug::Abort()))) | |
#endif | |
#else | |
// 非デバッグ時 | |
#define TRACE(fmt, ...) | |
#define ASSERT(e) | |
#define VERIFY(e) (e) | |
#endif | |
typedef unsigned int u32; | |
typedef unsigned short u16; | |
typedef unsigned char u8; | |
typedef signed int s32; | |
typedef signed short s16; | |
typedef signed char s8; | |
typedef float f32; | |
typedef double f64; | |
static const u32 WND_W = 800; | |
static const u32 WND_H = 800; | |
static const u32 DISP_W = 200; | |
static const u32 DISP_H = 200; | |
static const u32 FIELD_W = 12; | |
static const u32 FIELD_H = 30; | |
enum { | |
KEY_LEFT, | |
KEY_RIGHT, | |
KEY_UP, | |
KEY_DOWN, | |
KEY_BTN1, | |
KEY_BTN2, | |
KEY_MAX, | |
}; | |
enum { | |
F_NONE, // 空 | |
F_FIXBLOCK, // 固定済み | |
F_MYBLOCK, // 動かし中 | |
F_MAX, | |
}; | |
static const u32 F_COLOR[F_MAX] = { | |
0x000000, | |
0xffffff, | |
0xffffff, | |
}; | |
static const u32 MYBLOCK_TYPE = 6; | |
static const u32 MYBLOCK_ROT = 4; | |
static const u32 MYBLOCK_DEF_L = 4; | |
static const u8 MYBLOCK_DEF[MYBLOCK_TYPE][MYBLOCK_ROT][MYBLOCK_DEF_L * MYBLOCK_DEF_L + 1] = { | |
{ // 四角 | |
"0000" | |
"0000" | |
"1100" | |
"1100", | |
"0000" | |
"0000" | |
"1100" | |
"1100", | |
"0000" | |
"0000" | |
"1100" | |
"1100", | |
"0000" | |
"0000" | |
"1100" | |
"1100", | |
}, | |
{ // 棒 | |
"1000" | |
"1000" | |
"1000" | |
"1000", | |
"1111" | |
"0000" | |
"0000" | |
"0000", | |
"0001" | |
"0001" | |
"0001" | |
"0001", | |
"0000" | |
"0000" | |
"0000" | |
"1111", | |
}, | |
{ // Z | |
"0000" | |
"0000" | |
"1100" | |
"0110", | |
"0000" | |
"0100" | |
"1100" | |
"1000", | |
"0000" | |
"0000" | |
"1100" | |
"0110", | |
"0000" | |
"0100" | |
"1100" | |
"1000", | |
}, | |
{ // S | |
"0000" | |
"0000" | |
"0110" | |
"1100", | |
"0000" | |
"1000" | |
"1100" | |
"0100", | |
"0000" | |
"0000" | |
"0110" | |
"1100", | |
"0000" | |
"1000" | |
"1100" | |
"0100", | |
}, | |
{ // L | |
"0000" | |
"1000" | |
"1000" | |
"1100", | |
"0000" | |
"1110" | |
"1000" | |
"0000", | |
"0000" | |
"0110" | |
"0010" | |
"0010", | |
"0000" | |
"0000" | |
"0010" | |
"1110", | |
}, | |
{ // 逆L | |
"0000" | |
"0010" | |
"0010" | |
"0110", | |
"0000" | |
"0000" | |
"1000" | |
"1110", | |
"0000" | |
"1100" | |
"1000" | |
"1000", | |
"0000" | |
"1110" | |
"0010" | |
"0000", | |
}, | |
}; | |
u32 KEY_DEFS[KEY_MAX] = { | |
VK_LEFT, | |
VK_RIGHT, | |
VK_UP, | |
VK_DOWN, | |
'Z', | |
'X', | |
}; | |
static const wchar_t* CLASS_NAME = L"tet"; | |
class WndClass { | |
public: | |
WNDCLASSEX m_wc; | |
public: | |
WndClass(WNDPROC handler) { | |
WNDCLASSEX wc = { | |
sizeof( WNDCLASSEX ), CS_CLASSDC, handler, 0L, 0L, | |
GetModuleHandle( NULL ), NULL, NULL, NULL, NULL, | |
CLASS_NAME, NULL | |
}; | |
m_wc = wc; | |
RegisterClassEx(&m_wc); | |
} | |
~WndClass() { | |
UnregisterClass( CLASS_NAME, m_wc.hInstance ); | |
} | |
}; | |
class Window { | |
private: | |
HWND m_hWindow; | |
bool m_isQuit; | |
u32 m_width, m_height; | |
public: | |
Window(u32 w, u32 h) | |
: m_hWindow(NULL) | |
, m_isQuit(false) | |
, m_width(w) | |
, m_height(h) | |
{ | |
static WndClass cls(handler); | |
RECT rect; | |
rect.left = 0; | |
rect.top = 0; | |
rect.right = m_width; | |
rect.bottom = m_height; | |
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false); | |
m_hWindow = CreateWindow( CLASS_NAME, CLASS_NAME, | |
WS_OVERLAPPEDWINDOW, 100, 100, rect.right, rect.bottom, | |
NULL, NULL, cls.m_wc.hInstance, NULL ); | |
ShowWindow(m_hWindow, SW_SHOWDEFAULT); | |
UpdateWindow(m_hWindow); | |
} | |
HWND getHandle() { | |
return m_hWindow; | |
} | |
bool isQuit() { | |
return m_isQuit; | |
} | |
u32 getWidth() const { | |
return m_width; | |
} | |
u32 getHeight() const { | |
return m_height; | |
} | |
static LRESULT WINAPI handler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { | |
switch( msg ) | |
{ | |
case WM_DESTROY: | |
PostQuitMessage( 0 ); | |
return 0; | |
} | |
return DefWindowProc( hWnd, msg, wParam, lParam ); | |
} | |
void doEvents() { | |
MSG msg; | |
while ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) ) { | |
if (msg.message == WM_QUIT) { | |
m_isQuit = true; | |
break; | |
} | |
TranslateMessage( &msg ); | |
DispatchMessage( &msg ); | |
} | |
} | |
}; | |
class DIBSection { | |
private: | |
u32 m_width, m_height; | |
u32* m_pBits; | |
HBITMAP hBitmap, hPrevBmp; | |
HDC hMemDC; | |
public: | |
DIBSection(HWND hWnd, u32 w, u32 h) | |
: m_width(w) | |
, m_height(h) | |
{ | |
// setup bitmapinfo | |
BITMAPINFO bmi; | |
memset(&bmi, 0, sizeof(bmi)); | |
{ | |
BITMAPINFOHEADER& head = bmi.bmiHeader; | |
head.biSize = sizeof(BITMAPINFOHEADER); | |
head.biBitCount = 32; | |
head.biPlanes = 1; | |
head.biWidth = w; | |
head.biHeight = -(int)h; | |
} | |
// create DIBSection | |
hBitmap = CreateDIBSection( | |
NULL, | |
&bmi, | |
DIB_RGB_COLORS, | |
(void**)&m_pBits, | |
NULL, | |
0); | |
ASSERT(hBitmap); | |
HDC hDC = GetDC(hWnd); | |
hMemDC = CreateCompatibleDC(hDC); | |
ReleaseDC(hWnd, hDC); | |
hPrevBmp = (HBITMAP)SelectObject(hMemDC, hBitmap); | |
} | |
~DIBSection() { | |
SelectObject(hMemDC, hPrevBmp); | |
DeleteObject(hBitmap); | |
} | |
void setPixel(u32 x, u32 y, u32 color) { | |
if (x < m_width && y < m_height) { | |
y = m_height - y - 1; | |
m_pBits[x + y * m_width] = color; | |
} | |
} | |
void fill(u32 x, u32 y, u32 w, u32 h, u32 color) { | |
for (u32 py = y; py < y + h; py++) { | |
for (u32 px = x; px < x + w; px++) { | |
setPixel(px, py, color); | |
} | |
} | |
} | |
HDC getDDB() const { | |
return hMemDC; | |
} | |
u32 getWidth() const { | |
return m_width; | |
} | |
u32 getHeight() const { | |
return m_height; | |
} | |
}; // class DIBSection | |
static bool getBlock (u32 x, u32 y, u32 btype, u32 rot) { | |
y = MYBLOCK_DEF_L - y - 1; | |
const u8* def = MYBLOCK_DEF[btype][rot]; | |
return def[x + y * MYBLOCK_DEF_L] == '1'; | |
} | |
class Field { | |
private: | |
static const u32 BLOCK_L = 10; | |
static const u32 BLOCK_D = 1; | |
u8 *m_pField; | |
u32 m_width, m_height; | |
public: | |
Field(u32 w, u32 h) | |
: m_width(w) | |
, m_height(h) | |
{ | |
m_pField = new u8[m_width * m_height]; | |
clear(); | |
} | |
~Field() { | |
delete[] m_pField; | |
} | |
void eraseLine(u32 y) { | |
for (u32 py = y + 1; py < m_height - 1; py++) { | |
for (u32 px = 1; px < m_width - 1; px++) { | |
at(px, py - 1) = at(px, py); | |
} | |
} | |
} | |
u32 doErase() { | |
u32 count = 0; | |
for (u32 y = 1; y < m_height - 1; y++) { | |
bool erase = true; | |
for (u32 x = 1; x < m_width - 1; x++) { | |
if (at(x, y) == F_NONE) { | |
erase = false; | |
break; | |
} | |
} | |
if (erase) { | |
eraseLine(y); | |
count++; | |
continue; | |
} | |
} | |
return count; | |
} | |
void clear() { | |
for (u32 x = 0; x < m_width; x++) { | |
for (u32 y = 0; y < m_height; y++) { | |
at(x, y) = F_NONE; | |
} | |
} | |
for (u32 y = 0; y < m_height; y++) { | |
at(0, y) = F_FIXBLOCK; | |
at(m_width - 1, y) = F_FIXBLOCK; | |
} | |
for (u32 x = 0; x < m_width; x++) { | |
at(x, 0) = F_FIXBLOCK; | |
at(x, m_height - 1) = F_FIXBLOCK; | |
} | |
} | |
void fix() { | |
for (u32 x = 0; x < m_width; x++) { | |
for (u32 y = 0; y < m_height; y++) { | |
if (at(x, y) == F_MYBLOCK) { | |
at(x, y) = F_FIXBLOCK; | |
} | |
} | |
} | |
} | |
void reset() { | |
for (u32 x = 0; x < m_width; x++) { | |
for (u32 y = 0; y < m_height; y++) { | |
if (at(x, y) == F_MYBLOCK) { | |
at(x, y) = F_NONE; | |
} | |
} | |
} | |
} | |
u8& at(u32 x, u32 y) { | |
ASSERT(x < m_width && y < m_height); | |
return m_pField[x + m_width * y]; | |
} | |
bool isOk(u32 x, u32 y, u32 btype, u32 rot) { | |
for (u32 px = 0; px < MYBLOCK_DEF_L; px++) { | |
for (u32 py = 0; py < MYBLOCK_DEF_L; py++) { | |
u32 fx = px + x; | |
u32 fy = py + y; | |
if (getBlock(px, py, btype, rot) && (at(fx, fy) == F_FIXBLOCK)) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
// おけたらtrue | |
// おけなかったら、無理やり置いてfalse | |
bool putBlock(u32 x, u32 y, u32 btype, u32 rot) { | |
bool ret = true; | |
for (u32 px = 0; px < MYBLOCK_DEF_L; px++) { | |
for (u32 py = 0; py < MYBLOCK_DEF_L; py++) { | |
u32 fx = px + x; | |
u32 fy = py + y; | |
if (getBlock(px, py, btype, rot)) { | |
if (at(fx, fy) == F_FIXBLOCK) { | |
ret = false; | |
} else { | |
at(fx, fy) = F_MYBLOCK; | |
} | |
} | |
} | |
} | |
return ret; | |
} | |
void render(DIBSection& disp) { | |
for (u32 x = 0; x < m_width; x++) { | |
for (u32 y = 0; y < m_height; y++) { | |
// TRACE("%d", at(x, y)); | |
disp.fill( | |
x * BLOCK_L + BLOCK_D, | |
y * BLOCK_L + BLOCK_D, | |
BLOCK_L - BLOCK_D * 2, | |
BLOCK_L - BLOCK_D * 2, | |
F_COLOR[at(x, y)]); | |
} | |
// TRACE("\n"); | |
} | |
// TRACE("\n"); | |
} | |
}; // class Field | |
static void Flip(DIBSection& disp, Window& wnd ) { | |
HDC hdc = GetDC(wnd.getHandle()); | |
StretchBlt(hdc, | |
0, 0, wnd.getWidth(), wnd.getHeight(), | |
disp.getDDB(), | |
0, 0, | |
disp.getWidth(), disp.getHeight(), | |
SRCCOPY); | |
ReleaseDC(wnd.getHandle(), hdc); | |
} | |
static bool isPush(u32 key) { | |
ASSERT(key < KEY_MAX); | |
return GetAsyncKeyState(KEY_DEFS[key]); | |
} | |
static u32 selectType(u32 prevType) { | |
u32 type; | |
do { | |
type = (rand()) % MYBLOCK_TYPE; | |
} while (type == prevType); | |
return type; | |
} | |
static const int X_INIT_POS = FIELD_W / 2; | |
static const int Y_INIT_POS = 20; | |
static const int FALL_D_INIT = 5; | |
int WINAPI WinMain( | |
HINSTANCE hInstance , | |
HINSTANCE hPrevInstance , | |
LPSTR lpCmdLine , | |
int nCmdShow ) | |
{ | |
Window wnd(WND_W, WND_H); | |
DIBSection disp(wnd.getHandle(), DISP_W, DISP_H); | |
Field f(FIELD_W, FIELD_H); | |
u32 x = X_INIT_POS; | |
u32 y = Y_INIT_POS; | |
u32 rot = 0; | |
u32 type = selectType(0xff); | |
u32 fall_d = FALL_D_INIT; | |
while (!wnd.isQuit()) { | |
f.reset(); | |
if (isPush(KEY_LEFT)) { | |
if (f.isOk(x - 1, y, type, rot)) { | |
x--; | |
} | |
} | |
if (isPush(KEY_RIGHT)) { | |
if (f.isOk(x + 1, y, type, rot)) { | |
x++; | |
} | |
} | |
if (isPush(KEY_BTN2)) { | |
u32 nrot = (rot + MYBLOCK_ROT - 1) % MYBLOCK_ROT; | |
if (f.isOk(x, y, type, nrot)) { | |
rot = nrot; | |
} | |
} | |
if (isPush(KEY_BTN1)) { | |
u32 nrot = (rot + 1) % MYBLOCK_ROT; | |
if (f.isOk(x, y, type, nrot)) { | |
rot = nrot; | |
} | |
} | |
if (isPush(KEY_DOWN)) { | |
fall_d = 0; | |
} | |
if (fall_d == 0) { | |
if (f.isOk(x, y - 1, type, rot)) { | |
y--; | |
} else { | |
f.putBlock(x, y, type, rot); | |
f.fix(); | |
f.doErase(); | |
x = X_INIT_POS; | |
y = Y_INIT_POS; | |
rot = 0; | |
type = selectType(type); | |
} | |
fall_d = FALL_D_INIT; | |
} | |
fall_d--; | |
f.putBlock(x, y, type, rot); | |
disp.fill(0, 0, DISP_W, DISP_H, 0x00); | |
f.render(disp); | |
wnd.doEvents(); | |
Flip(disp, wnd); | |
Sleep(100); | |
} | |
return 0; | |
} | |
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 "ng.h" | |
enum { | |
KEY_UP, | |
KEY_DOWN, | |
KEY_RIGHT, | |
KEY_LEFT, | |
KEY_1, | |
KEY_2, | |
}; | |
// (320, 240) ~ (-320, -240) | |
static const vec2 WindowSize = {320, 240}; | |
static const vec2 kCellSize = {18, 18}; | |
static const ivec2 BoardSize = {10, 20}; | |
ngColor kBlack(0x00, 0x00, 0x00, 0xff); | |
ngColor kWhite(0xff, 0xff, 0xff, 0xff); | |
ngColor kGray(0x80, 0x80, 0x80, 0xff); | |
enum class Cell { | |
None, | |
Block, // TODO Color | |
}; | |
// 67 | |
// 45 | |
// 23 | |
// 01 | |
const int kTetriminoTypes = 7; | |
const ivec2 kTetriminoXY = {2, 4}; | |
const int kTetriminoS = 8; | |
const bool kTetrimino[kTetriminoTypes][kTetriminoS] = { | |
{1, 0, 1, 0, 1, 0, 1, 0}, // I | |
{1, 1, 1, 1, 0, 0, 0, 0}, // O | |
{0, 1, 1, 1, 1, 0, 0, 0}, // S | |
{1, 0, 1, 1, 0, 1, 0, 0}, // Z | |
{1, 1, 0, 1, 0, 1, 0, 0}, // J | |
{1, 1, 1, 0, 1, 0, 0, 0}, // L | |
{0, 1, 1, 1, 0, 1, 0, 0}, // T | |
}; | |
struct Board { | |
int minoType; | |
int minoRot; | |
ivec2 minoPos; | |
std::vector<Cell> cell; | |
void Init() { | |
cell.resize(BoardSize.x * BoardSize.y); | |
for (int x = 0; x < BoardSize.x; x++) { | |
for (int y = 0; y < BoardSize.y; y++) { | |
SetAt({x, y}, Cell::None); | |
} | |
// SetAt({x, 0}, Cell::Block); | |
} | |
for (int y = 0; y < BoardSize.y; y++) { | |
// SetAt({0, y}, Cell::Block); | |
// SetAt({BoardSize.x - 1, y}, Cell::Block); | |
} | |
NextMino(); | |
} | |
Cell GetAt(ivec2 pos) { | |
if (0 <= pos.x && pos.x < BoardSize.x) { | |
if (0 <= pos.y && pos.y < BoardSize.y) { | |
return cell[pos.x + pos.y * BoardSize.x]; | |
} | |
} | |
if (pos.y >= BoardSize.y) { | |
return Cell::None; | |
} else { | |
return Cell::Block; | |
} | |
} | |
bool SetAt(ivec2 pos, Cell c) { | |
if (0 <= pos.x && pos.x < BoardSize.x) { | |
if (0 <= pos.y && pos.y < BoardSize.y) { | |
cell[pos.x + pos.y * BoardSize.x] = c; | |
return true; | |
} | |
} | |
return false; | |
} | |
void RenderCell(ngProcess& p, ivec2 pos, Cell c) { | |
ngColor col; | |
switch (c) { | |
case Cell::Block: | |
col = kBlack; | |
break; | |
case Cell::None: | |
col = kGray; | |
} | |
p.Rect(col, col, vec2{pos.x * (kCellSize.x + 2), pos.y * (kCellSize.y + 2)}, | |
kCellSize * 0.5f); | |
} | |
bool NextMino() { | |
minoPos = ivec2{BoardSize.x / 2, BoardSize.y - kTetriminoXY.y}; | |
minoRot = 0; | |
minoType = rand() % kTetriminoTypes; | |
if (MinoCollision()) { | |
return false; | |
} | |
return true; | |
} | |
void EraseLine(int yo) { | |
for (int y = yo; y < BoardSize.y; y++) { | |
for (int x = 0; x < BoardSize.x; x++) { | |
SetAt({x, y}, GetAt({x, y + 1})); | |
} | |
} | |
} | |
int Erase() { | |
int lines = 0; | |
for (int y = 0; y < BoardSize.y;) { | |
int n = 0; | |
for (int x = 0; x < BoardSize.x; x++) { | |
if (GetAt({x, y}) == Cell::None) { | |
break; | |
} | |
n++; | |
} | |
if (n == BoardSize.x) { | |
EraseLine(y); | |
lines++; | |
} else { | |
y++; | |
} | |
} | |
return lines; | |
} | |
void FixMino() { | |
for (int x = 0; x < kTetriminoXY.x; x++) { | |
for (int y = 0; y < kTetriminoXY.y; y++) { | |
int index = x + y * kTetriminoXY.x; | |
if (kTetrimino[minoType][index]) { | |
ivec2 pos = minoPos + MinoRotated(ivec2({x, y})); | |
SetAt(pos, Cell::Block); | |
} | |
} | |
} | |
} | |
bool MinoCollision() { | |
for (int x = 0; x < kTetriminoXY.x; x++) { | |
for (int y = 0; y < kTetriminoXY.y; y++) { | |
int index = x + y * kTetriminoXY.x; | |
if (kTetrimino[minoType][index]) { | |
ivec2 pos = minoPos + MinoRotated(ivec2({x, y})); | |
if (GetAt(pos) == Cell::Block) return true; | |
} | |
} | |
} | |
return false; | |
} | |
bool MoveMino(ivec2 dpos) { | |
ivec2 tmpPos = minoPos; | |
minoPos += dpos; | |
if (MinoCollision()) { | |
minoPos = tmpPos; | |
return false; | |
} | |
return true; | |
} | |
bool RotMino(int r) { | |
int tmpRot = minoRot; | |
minoRot = (minoRot + r + 4) % 4; | |
if (MinoCollision()) { | |
minoRot = tmpRot; | |
return false; | |
} | |
return true; | |
} | |
ivec2 MinoRotated(ivec2 pos) { | |
switch (minoRot % 4) { | |
case 0: | |
return pos; | |
case 1: | |
return {-pos.y, pos.x}; | |
case 2: | |
return {-pos.x, -pos.y}; | |
case 3: | |
return {pos.y, -pos.x}; | |
default: | |
abort(); | |
} | |
} | |
void Render(ngProcess& p) { | |
p.Push(translate(mat3(1.f), {-WindowSize.x + 30, -WindowSize.y + 30})); | |
// render board | |
for (int x = 0; x < BoardSize.x; x++) { | |
for (int y = 0; y < BoardSize.y; y++) { | |
RenderCell(p, {x, y}, GetAt({x, y})); | |
} | |
} | |
// render mino | |
for (int x = 0; x < kTetriminoXY.x; x++) { | |
for (int y = 0; y < kTetriminoXY.y; y++) { | |
int index = x + y * kTetriminoXY.x; | |
if (kTetrimino[minoType][index]) { | |
ivec2 pos = minoPos + MinoRotated(ivec2({x, y})); | |
RenderCell(p, pos, Cell::Block); | |
} | |
} | |
} | |
p.Pop(); | |
} | |
}; | |
int main(void) { | |
auto proc = ngProcess::NewProcess(); | |
proc->MapKeyboard('w', KEY_UP); | |
proc->MapKeyboard('a', KEY_LEFT); | |
proc->MapKeyboard('s', KEY_DOWN); | |
proc->MapKeyboard('d', KEY_RIGHT); | |
proc->MapKeyboard('z', KEY_1); | |
proc->MapKeyboard('x', KEY_2); | |
proc->MapMouseButton(1, KEY_1); | |
proc->MapMouseButton(3, KEY_2); // right mouse button | |
if (!proc->Init()) { | |
return 1; | |
} | |
Board b; | |
b.Init(); | |
const float kTimeToDrop = 0.8; | |
float timeToDrop = kTimeToDrop; | |
int scores = 0; | |
bool drop = false; | |
proc->Run([&](ngProcess& p, float dt) { | |
// update | |
p.Clear(kWhite); | |
if (p.IsJustPressed(KEY_LEFT)) { | |
b.MoveMino({-1, 0}); | |
} | |
if (p.IsJustPressed(KEY_RIGHT)) { | |
b.MoveMino({1, 0}); | |
} | |
if (p.IsJustPressed(KEY_1)) { | |
b.RotMino(1); | |
} | |
if (p.IsJustPressed(KEY_2)) { | |
b.RotMino(-1); | |
} | |
if (p.IsJustPressed(KEY_DOWN)) { | |
drop = true; | |
} | |
timeToDrop -= dt; | |
if (timeToDrop < 0 || drop) { | |
if (!b.MoveMino({0, -1})) { | |
b.FixMino(); | |
int lines = b.Erase(); | |
scores += lines; | |
if (!b.NextMino()) { | |
// TODO ゲームオーバー | |
} | |
drop = false; | |
} | |
timeToDrop = kTimeToDrop; | |
} | |
b.Render(p); | |
p.Text(kBlack, {150, 200}, 30, fmt::format(u8"score:{0}", scores).c_str()); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment