8
code:cpp
# include <Siv3D.hpp> // OpenSiv3D v0.2.8
enum class Direction
{
Up,
Right,
Down,
Left
};
constexpr Point ToPoint(Direction direction)
{
switch (direction)
{
case Direction::Up:
return Point::Up();
case Direction::Right:
return Point::Right();
case Direction::Down:
return Point::Down();
default:
return Point::Left();
}
}
enum class MoveType
{
Move,
Merge,
None
};
constexpr MoveType GetMoveType(int32 my, int32 target)
{
if (target == 0 && my != 0)
{
return MoveType::Move;
}
else if ((my != 0) && (my == target))
{
return MoveType::Merge;
}
else
{
return MoveType::None;
}
}
struct AnimationData
{
AnimationData()
{
indices = Grid<Array<int32>>(4, 4);
for (auto i : step(16))
{
}
}
Grid<Array<int32>> indices;
Array<Point> mergePositions;
Stopwatch moveTimer;
Stopwatch mergeTimer;
};
std::pair<Grid<int32>, AnimationData> AllUp(Grid<int32> grid)
{
AnimationData animation;
//move
for (auto x : step(4))
{
for (auto y : Range(1, 3))
{
for (auto i : step(y))
{
const Point targetPos = Point(x, (y - i) - 1);
const Point myPos = Point(x, (y - i));
int32& myValue = gridmyPos; const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
//merge
for (auto x : step(4))
{
for (auto y : Range(1, 3))
{
const Point targetPos = Point(x, y - 1);
const Point myPos = Point(x, y);
int32& myValue = gridmyPos; const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Merge)
{
++valueAtTarget;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
//move
for (auto x : step(4))
{
for (auto y : Range(1, 3))
{
for (auto i : step(y))
{
const Point targetPos = Point(x, (y - i) - 1);
const Point myPos = Point(x, (y - i));
int32& myValue = gridmyPos; const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
return { grid, animation };
}
std::pair<Grid<int32>, AnimationData> AllRight(Grid<int32> grid)
{
AnimationData animation;
//move
for (auto y : step(4))
{
for (auto x : { 2, 1, 0 })
{
for (auto i : step(3 - x))
{
const Point targetPos = Point((x + i) + 1, y);
const Point myPos = Point((x + i), y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
//merge
for (auto y : step(4))
{
for (auto x : { 2, 1, 0 })
{
const Point targetPos = Point(x + 1, y);
const Point myPos = Point(x, y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Merge)
{
++valueAtTarget;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
//move
for (auto y : step(4))
{
for (auto x : { 2, 1, 0 })
{
for (auto i : step(3 - x))
{
const Point targetPos = Point((x + i) + 1, y);
const Point myPos = Point((x + i), y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
return { grid, animation };
}
std::pair<Grid<int32>, AnimationData> AllDown(Grid<int32> grid)
{
AnimationData animation;
//move
for (auto x : step(4))
{
for (auto y : { 2, 1, 0 })
{
for (auto i : step(3 - y))
{
const Point targetPos = Point(x, (y + i) + 1);
const Point myPos = Point(x, (y + i));
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
//merge
for (auto x : step(4))
{
for (auto y : { 2, 1, 0 })
{
const Point targetPos = Point(x, y + 1);
const Point myPos = Point(x, y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Merge)
{
++valueAtTarget;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
//move
for (auto x : step(4))
{
for (auto y : { 2, 1, 0 })
{
for (auto i : step(3 - y))
{
const Point targetPos = Point(x, (y + i) + 1);
const Point myPos = Point(x, (y + i));
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
return { grid, animation };
}
std::pair<Grid<int32>, AnimationData> AllLeft(Grid<int32> grid)
{
AnimationData animation;
//move
for (auto y : step(4))
{
for (auto x : Range(1, 3))
{
for (auto i : step(x))
{
const Point targetPos = Point((x - i) - 1, y);
const Point myPos = Point((x - i), y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
//merge
for (auto y : step(4))
{
for (auto x : Range(1, 3))
{
const Point targetPos = Point(x - 1, y);
const Point myPos = Point(x, y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Merge)
{
++valueAtTarget;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
//move
for (auto y : step(4))
{
for (auto x : Range(1, 3))
{
for (auto i : step(x))
{
const Point targetPos = Point((x - i) - 1, y);
const Point myPos = Point((x - i), y);
const MoveType moveType = GetMoveType(myValue, valueAtTarget);
if (moveType == MoveType::Move)
{
valueAtTarget = myValue;
myValue = 0;
animation.indicesmyPos.clear(); }
}
}
}
return { grid, animation };
}
Array<Point> GetEmptyPoints(const Grid<int32>& grid)
{
Array<Point> emptyPoints;
for (auto p : step(grid.size()))
{
{
emptyPoints << p;
}
}
return emptyPoints;
}
constexpr std::pair<Color, Color> GetBlockColors(int32 value)
{
constexpr std::array<Color, 12> blockColors =
{
Color(0),
Color(238, 228, 218), // 2
Color(237, 224, 200), // 4
Color(242, 177, 121), // 8
Color(245, 149, 99), // 16
Color(246, 124, 95), // 32
Color(246, 94, 59), // 64
Color(237, 207, 114), // 128
Color(237, 294, 97), // 256
Color(237, 200, 80), // 512
Color(237, 197, 63), // 1024
Color(237, 194, 46), // 2048
};
const Color blockColor = (value < 12) ? blockColorsvalue : Color(60, 58, 50); const Color textColor = (value <= 2) ? Color(119, 110, 101) : Color(249, 246, 242);
return{ blockColor, textColor };
}
constexpr size_t GetFontIndex(int32 value)
{
return value <= 6 ? 0 : value <= 9 ? 1 : value <= 11 ? 2 : 3;
}
void Main()
{
Graphics::SetBackground(ColorF(0.8, 0.9, 1.0));
constexpr double CellSize = 100.0;
const std::array<Font, 4> fonts =
{
Font(46, Typeface::Heavy),
Font(38, Typeface::Heavy),
Font(30, Typeface::Heavy),
Font(28, Typeface::Heavy),
};
Grid<int32> grid = {
{ 1, 1, 1, 0 },
{ 1, 0, 1, 1 },
{ 1, 1, 1, 0 },
{ 1, 1, 0, 1 } };
//{
// Array<Point> initialSpawnPoints = Range(0, 15).asArray().choice(2).map([](int32 n) { return Point(n%4, n/4); });
// for (auto& spawnPoint : initialSpawnPoints)
// {
// }
//}
bool isGameOver = false;
AnimationData animation;
Grid<int32> oldGrid = grid;
bool reverseFlag = false;
while (System::Update())
{
Optional<Direction> direction;
if (KeyUp.down())
{
direction = Direction::Up;
}
else if (KeyRight.down())
{
direction = Direction::Right;
}
else if (KeyDown.down())
{
direction = Direction::Down;
}
else if (KeyLeft.down())
{
direction = Direction::Left;
}
if (animation.moveTimer.isRunning() || animation.mergeTimer.isRunning())
{
direction = none;
}
if (direction)
{
animation = AnimationData();
oldGrid = grid;
//Print << animation.indices;
if (direction == Direction::Up)
{
std::tie(grid, animation) = AllUp(grid);
reverseFlag = false;
}
else if (direction == Direction::Right)
{
std::tie(grid, animation) = AllRight(grid);
reverseFlag = true;
}
else if (direction == Direction::Down)
{
std::tie(grid, animation) = AllDown(grid);
reverseFlag = true;
}
else if (direction == Direction::Left)
{
std::tie(grid, animation) = AllLeft(grid);
reverseFlag = false;
}
//Print << animation.indices;
if (grid != oldGrid)
{
const Array<Point> emptyPoints = GetEmptyPoints(grid);
if (!emptyPoints)
{
isGameOver = true;
}
else
{
const Point spawnPoint = emptyPoints.choice();
}
animation.moveTimer.start();
}
}
const auto getRect = &(const Point& p) {
return RectF(p * CellSize, CellSize);
};
const int cycleT1 = 100;
const int cycleT2 = 100;
if (cycleT1 <= animation.moveTimer.ms())
{
animation.moveTimer.reset();
animation.mergeTimer.start();
}
else if (cycleT2 <= animation.mergeTimer.ms())
{
animation.mergeTimer.reset();
}
if (animation.moveTimer.isRunning())
{
Transformer2D t(Mat3x2::Translate(120, 40));
RectF(400).stretched(5).rounded(3).draw(ColorF(0.5));
std::unordered_map<Point, Point> animationPoints;
for (auto p : step(animation.indices.size()))
{
const auto currentIndices = animation.indicesp; for (auto index : currentIndices)
{
{
}
}
}
const double progress = 1.0 * animation.moveTimer.ms() / cycleT1;
for (auto p : step(Size(4, 4)))
{
const RectF rect(p * CellSize, CellSize);
rect.stretched(-5).rounded(3).draw(ColorF(0.6));
}
for (auto pp : step(Size(4, 4)))
{
Point p = pp;
if (reverseFlag)
{
p = Point(3 - pp.x, 3 - pp.y);
}
const int32 value = oldGridp; RectF rect(p * CellSize, CellSize);
if (value)
{
Vec2 positionVec = rect.center();
if (animationPoints.find(p) != animationPoints.end())
{
positionVec = Math::Lerp(getRect(p).center(), getRect(animationPointsp).center(), progress); }
rect.setCenter(positionVec);
rect.stretched(-5).rounded(3).draw(blockColor);
}
}
}
else
{
double deltaSize = 0.0;
std::set<int> mergedIndices;
if (animation.mergeTimer.isRunning())
{
const double progress = 1.0 * animation.mergeTimer.ms() / cycleT2;
deltaSize = -10.0*Abs((progress * 2) - 1.0);
for (auto p : step(animation.indices.size()))
{
const auto currentIndices = animation.indicesp; int count = 0;
for (auto index : currentIndices)
{
{
++count;
}
}
if (2 <= count)
{
mergedIndices.emplace(p.y * 4 + p.x);
}
}
}
Transformer2D t(Mat3x2::Translate(120, 40));
RectF(400).stretched(5).rounded(3).draw(ColorF(0.5));
for (auto p : step(Size(4, 4)))
{
const int32 value = gridp; const RectF rect(p * CellSize, CellSize);
double currentDeltaSize = 0.0;
if (mergedIndices.find(p.y * 4 + p.x) != mergedIndices.end())
{
currentDeltaSize = deltaSize;
}
if (value)
{
rect.stretched(currentDeltaSize - 5).rounded(3).draw(blockColor);
}
else
{
rect.stretched(currentDeltaSize - 5).rounded(3).draw(ColorF(0.6));
}
}
}
if (isGameOver)
{
Window::ClientRect().draw(ColorF(1.0, 0.5));
}
}
}