Skip to content

Instantly share code, notes, and snippets.

@azaika
Last active December 29, 2016 20:27
Show Gist options
  • Save azaika/d6e7c48b95ce1aef75116bf83feb7d02 to your computer and use it in GitHub Desktop.
Save azaika/d6e7c48b95ce1aef75116bf83feb7d02 to your computer and use it in GitHub Desktop.
#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