Last active
December 29, 2016 20:27
-
-
Save azaika/d6e7c48b95ce1aef75116bf83feb7d02 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 <Siv3D.hpp> | |
#include <array> | |
#include <vector> | |
#include <algorithm> | |
using ShapeType = std::array<std::array<bool, 4>, 4>; | |
struct TetrominoInfo { | |
ShapeType shape; | |
Color color; | |
}; | |
constexpr std::array<TetrominoInfo, 7> Tetromino = { | |
TetrominoInfo{ | |
{ | |
0, 0, 1, 0, | |
0, 0, 1, 0, | |
0, 0, 1, 0, | |
0, 0, 1, 0 | |
}, | |
Palette::Cyan | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 1, 1, 1, | |
0, 0, 0, 0 | |
}, | |
Palette::Royalblue | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
0, 0, 1, 0, | |
1, 1, 1, 0, | |
0, 0, 0, 0 | |
}, | |
Palette::Darkorange | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
0, 1, 1, 0, | |
0, 1, 1, 0, | |
0, 0, 0, 0 | |
}, | |
Palette::Gold | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
0, 0, 1, 1, | |
0, 1, 1, 0, | |
0, 0, 0, 0 | |
}, | |
Palette::Lightgreen | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
0, 0, 1, 0, | |
0, 1, 1, 1, | |
0, 0, 0, 0 | |
}, | |
Palette::Mediumorchid | |
}, | |
TetrominoInfo{ | |
{ | |
0, 0, 0, 0, | |
1, 1, 0, 0, | |
0, 1, 1, 0, | |
0, 0, 0, 0 | |
}, | |
Palette::Orangered | |
} | |
}; | |
std::array<std::array<ShapeType, 4>, 7> rotatedShape; | |
void initShape() { | |
for (int i : step(static_cast<int>(Tetromino.size()))) { | |
const auto& baseShape = Tetromino[i].shape; | |
// 普通 | |
rotatedShape[i][0] = baseShape; | |
for (auto&& p : step(Point(4, 4))) { | |
// 右回転 | |
rotatedShape[i][1][p.y][p.x] = baseShape[p.x][4 - p.y - 1]; | |
// 反転 | |
rotatedShape[i][2][p.y][p.x] = baseShape[4 - p.y - 1][4 - p.x - 1]; | |
// 左回転 | |
rotatedShape[i][3][p.y][p.x] = baseShape[4 - p.x - 1][p.y]; | |
} | |
} | |
} | |
using TetrominoKind = int; | |
void drawCell(TetrominoKind kind, const Point& pos) { | |
Color c = Tetromino[kind].color; | |
Rect(pos * 20, 20, 20) | |
.draw(c) | |
.drawFrame(2.0, 0.0, c.lerp(Palette::Black, 0.4)); | |
} | |
void drawTetromino(TetrominoKind kind, int rotateState, const Point& pos) { | |
const auto& shape = rotatedShape[kind][rotateState]; | |
for (auto&& p : step(Point(4, 4))) | |
if (shape[p.y][p.x]) | |
drawCell(kind, pos + p); | |
} | |
constexpr Size MaxBoardSize(10, 20); | |
void Main() { | |
Graphics::SetBackground(Palette::White); | |
// 適当に座標ずらす | |
Graphics2D::SetTransform(Mat3x2::Translate(Window::Width() / 2 - (MaxBoardSize.x / 2 * 20), 30)); | |
initShape(); | |
using LineType = std::array<Optional<TetrominoKind>, MaxBoardSize.x>; | |
std::vector<LineType> board(MaxBoardSize.y); | |
auto updateBoard = [&board]() { | |
Erase_if(board, [](const LineType& line) { | |
return std::all_of(line.begin(), line.end(), [](const auto& c) { return c.has_value(); }); | |
}); | |
board.resize(20); | |
}; | |
auto drawBoard = [&board]() { | |
for (auto&& p : step({ 10, static_cast<int>(board.size()) })) { | |
// 上から見た高さ | |
const int y = MaxBoardSize.y - 1 - p.y; | |
auto&& cell = board[p.y][p.x]; | |
if (cell) | |
drawCell(cell.value(), { p.x, y }); | |
} | |
}; | |
auto collisionCheck = [&board] (const ShapeType& shape, const Point& pos) -> bool { | |
for (auto&& p : step(Point(4, 4))) { | |
const Point cellPos = pos + p; | |
if (shape[p.y][p.x]) { | |
if (cellPos.x < 0 || cellPos.x >= MaxBoardSize.x || cellPos.y >= MaxBoardSize.y) | |
return false; | |
if (board[MaxBoardSize.y - cellPos.y - 1][cellPos.x].has_value()) | |
return false; | |
} | |
} | |
return true; | |
}; | |
auto writeToBoard = [&board](TetrominoKind kind, int rotateState, const Point& pos) { | |
const auto& shape = rotatedShape[kind][rotateState]; | |
for (auto&& p : step(Point(4, 4))) | |
if (shape[p.y][p.x]) | |
board[MaxBoardSize.y - (pos.y + p.y) - 1][pos.x + p.x] = kind; | |
}; | |
// 落ちてるやつの左上の座標 | |
Point fallingPos; | |
// 落ちてるやるの種類 | |
TetrominoKind fallingKind; | |
// 落ちてるやつの回転状態 | |
int fallingRotate = 0; | |
// 前の自動落下からの経過フレーム | |
int fallingInterval = 0; | |
auto setFalling = [&fallingKind, &fallingPos, &fallingInterval]() { | |
fallingKind = Random(0, 6); | |
fallingPos = Point(3, 0); | |
fallingInterval = 0; | |
}; | |
setFalling(); | |
// 横の長押しの判定用 | |
int beforeKeyCode = 0, horizontalInterval = 0; | |
while (System::Update()) { | |
++fallingInterval; | |
++horizontalInterval; | |
// 強制落下 | |
if (fallingInterval >= (Input::KeyDown.pressed ? 10: 30)) { | |
if (collisionCheck(rotatedShape[fallingKind][fallingRotate], fallingPos + Point::Down)) | |
++fallingPos.y; | |
else { | |
writeToBoard(fallingKind, fallingRotate, fallingPos); | |
setFalling(); | |
} | |
fallingInterval = 0; | |
} | |
// 横移動 | |
if (Input::KeyLeft.pressed) { | |
// 前と押したキーが同じで一定間隔経っていたら | |
if (horizontalInterval >= 15 && beforeKeyCode == Input::KeyLeft.code) { | |
horizontalInterval = 0; | |
if (collisionCheck(rotatedShape[fallingKind][fallingRotate], fallingPos + Point::Left)) | |
--fallingPos.x; | |
} | |
beforeKeyCode = Input::KeyLeft.code; | |
} | |
if (Input::KeyRight.pressed) { | |
// 前と押したキーが同じで一定間隔経っていたら | |
if (horizontalInterval >= 15 && beforeKeyCode == Input::KeyRight.code) { | |
horizontalInterval = 0; | |
if (collisionCheck(rotatedShape[fallingKind][fallingRotate], fallingPos + Point::Right)) | |
++fallingPos.x; | |
} | |
beforeKeyCode = Input::KeyRight.code; | |
} | |
// 回転 | |
if (Input::KeyX.clicked || Input::KeyUp.clicked || Input::KeyZ.clicked) { | |
int nextRotate = ( | |
Input::KeyX.clicked || Input::KeyUp.clicked | |
? (fallingRotate == 3 ? 0 : fallingRotate + 1) | |
: (fallingRotate == 0 ? 3 : fallingRotate - 1) | |
); | |
// 適当に移動できそうなら試行する(オリジナルルール(面倒くさかった)) | |
constexpr Point delta[4] = { | |
{0, 0}, | |
{1, 0}, | |
{-1, 0}, | |
{0, 1} | |
}; | |
for (auto&& d : delta) { | |
if (collisionCheck( | |
rotatedShape[fallingKind][nextRotate], | |
fallingPos + d | |
)) { | |
fallingPos += d; | |
fallingRotate = nextRotate; | |
break; | |
} | |
} | |
} | |
// ハードドロップ | |
if (Input::KeySpace.clicked) { | |
while (collisionCheck(rotatedShape[fallingKind][fallingRotate], fallingPos + Point::Down)) | |
++fallingPos.y; | |
writeToBoard(fallingKind, fallingRotate, fallingPos); | |
setFalling(); | |
} | |
// 列を消す | |
updateBoard(); | |
// ボードの背景を初期化 | |
Rect(0, 0, MaxBoardSize * 20).draw(Palette::Darkgray); | |
// 落ちている途中のテトロミノを描画 | |
drawTetromino(fallingKind, fallingRotate, fallingPos); | |
// すでに落ちたブロックたちを描画 | |
drawBoard(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment