374 lines
9.1 KiB
C++
374 lines
9.1 KiB
C++
#include <luna/String.h>
|
|
#include <luna/Utf8.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <ui/Alignment.h>
|
|
#include <ui/App.h>
|
|
#include <ui/Font.h>
|
|
#include <ui/Layout.h>
|
|
|
|
static ui::Color colors[] = {
|
|
ui::Color::from_rgb(255, 255, 0), ui::Color::from_rgb(255, 230, 0), ui::Color::from_rgb(255, 210, 0),
|
|
ui::Color::from_rgb(255, 190, 0), ui::Color::from_rgb(255, 170, 0), ui::Color::from_rgb(255, 150, 0),
|
|
ui::Color::from_rgb(255, 130, 0), ui::Color::from_rgb(255, 110, 0), ui::Color::from_rgb(255, 90, 0),
|
|
ui::Color::from_rgb(255, 70, 0), ui::Color::from_rgb(255, 50, 0),
|
|
};
|
|
|
|
struct Tile
|
|
{
|
|
int number { 0 };
|
|
int color { 0 };
|
|
};
|
|
|
|
class GameWidget final : public ui::Widget
|
|
{
|
|
public:
|
|
GameWidget(ui::Window* window, ui::Widget* parent) : ui::Widget(window, parent)
|
|
{
|
|
}
|
|
|
|
void show_tree(int indent) override
|
|
{
|
|
os::println("%*s- 2048 GameWidget (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
|
|
m_rect.height);
|
|
}
|
|
|
|
static constexpr int MARGIN = 5;
|
|
|
|
Result<void> draw(ui::Canvas& canvas) override
|
|
{
|
|
int width = m_rect.width / 4;
|
|
int height = m_rect.height / 4;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
auto subcanvas = canvas.subcanvas(
|
|
ui::Rect { width * j + MARGIN, height * i + MARGIN, width - MARGIN, height - MARGIN });
|
|
int index = i * 4 + j;
|
|
TRY(draw_tile(index, subcanvas));
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override
|
|
{
|
|
if (!request.pressed) return ui::EventResult::DidNotHandle;
|
|
|
|
bool should_add_tile = false;
|
|
|
|
switch (request.code)
|
|
{
|
|
case moon::K_UpArrow: {
|
|
bool changed;
|
|
changed = move_up();
|
|
if (changed) should_add_tile = true;
|
|
join_up();
|
|
changed = move_up();
|
|
if (changed) should_add_tile = true;
|
|
}
|
|
break;
|
|
case moon::K_LeftArrow: {
|
|
bool changed;
|
|
changed = move_left();
|
|
if (changed) should_add_tile = true;
|
|
join_left();
|
|
changed = move_left();
|
|
if (changed) should_add_tile = true;
|
|
}
|
|
break;
|
|
case moon::K_DownArrow: {
|
|
bool changed;
|
|
changed = move_down();
|
|
if (changed) should_add_tile = true;
|
|
join_down();
|
|
changed = move_down();
|
|
if (changed) should_add_tile = true;
|
|
}
|
|
break;
|
|
case moon::K_RightArrow: {
|
|
bool changed;
|
|
changed = move_right();
|
|
if (changed) should_add_tile = true;
|
|
join_right();
|
|
changed = move_right();
|
|
if (changed) should_add_tile = true;
|
|
}
|
|
break;
|
|
case moon::K_Home: {
|
|
reset();
|
|
return ui::EventResult::DidHandle;
|
|
}
|
|
break;
|
|
default: return ui::EventResult::DidNotHandle;
|
|
}
|
|
|
|
if (should_add_tile) add_tile();
|
|
|
|
return ui::EventResult::DidHandle;
|
|
}
|
|
|
|
bool move_left()
|
|
{
|
|
Tile new_tiles[16];
|
|
|
|
bool changed = false;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int pos = 0;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
if (tiles[i * 4 + j].number != 0)
|
|
{
|
|
new_tiles[i * 4 + pos] = tiles[i * 4 + j];
|
|
pos += 1;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
return changed;
|
|
}
|
|
|
|
void join_left()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
auto& from_tile = tiles[i * 4 + j];
|
|
auto& to_tile = tiles[i * 4 + j + 1];
|
|
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
{
|
|
from_tile.number *= 2;
|
|
from_tile.color += 1;
|
|
to_tile.number = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool move_right()
|
|
{
|
|
Tile new_tiles[16];
|
|
|
|
bool changed = false;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int pos = 3;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
if (tiles[i * 4 + j].number != 0)
|
|
{
|
|
new_tiles[i * 4 + pos] = tiles[i * 4 + j];
|
|
pos -= 1;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
return changed;
|
|
}
|
|
|
|
void join_right()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
for (int j = 1; j < 4; j++)
|
|
{
|
|
auto& from_tile = tiles[i * 4 + j];
|
|
auto& to_tile = tiles[i * 4 + j - 1];
|
|
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
{
|
|
from_tile.number *= 2;
|
|
from_tile.color += 1;
|
|
to_tile.number = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool move_up()
|
|
{
|
|
Tile new_tiles[16];
|
|
|
|
bool changed = false;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
int pos = 0;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (tiles[i * 4 + j].number != 0)
|
|
{
|
|
new_tiles[pos * 4 + j] = tiles[i * 4 + j];
|
|
pos += 1;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
return changed;
|
|
}
|
|
|
|
void join_up()
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
auto& from_tile = tiles[i * 4 + j];
|
|
auto& to_tile = tiles[i * 4 + j + 4];
|
|
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
{
|
|
from_tile.number *= 2;
|
|
from_tile.color += 1;
|
|
to_tile.number = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool move_down()
|
|
{
|
|
Tile new_tiles[16];
|
|
|
|
bool changed = false;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
int pos = 3;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (tiles[i * 4 + j].number != 0)
|
|
{
|
|
new_tiles[pos * 4 + j] = tiles[i * 4 + j];
|
|
pos -= 1;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
return changed;
|
|
}
|
|
|
|
void join_down()
|
|
{
|
|
for (int i = 1; i < 4; i++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
auto& from_tile = tiles[i * 4 + j];
|
|
auto& to_tile = tiles[i * 4 + j - 4];
|
|
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
{
|
|
from_tile.number *= 2;
|
|
from_tile.color += 1;
|
|
to_tile.number = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void add_tile()
|
|
{
|
|
bool can_add_tile = false;
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (tiles[i].number == 0)
|
|
{
|
|
can_add_tile = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!can_add_tile)
|
|
{
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
int start;
|
|
do {
|
|
start = rand() % 16;
|
|
} while (tiles[start].number != 0);
|
|
tiles[start].number = 2;
|
|
tiles[start].color = 0;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
tiles[i].number = 0;
|
|
tiles[i].color = 0;
|
|
}
|
|
|
|
add_tile();
|
|
}
|
|
|
|
Tile tiles[16];
|
|
|
|
private:
|
|
Result<void> draw_tile(int index, ui::Canvas& canvas)
|
|
{
|
|
auto tile = tiles[index];
|
|
|
|
if (tile.number == 0)
|
|
{
|
|
canvas.fill(ui::GRAY);
|
|
return {};
|
|
}
|
|
|
|
canvas.fill(colors[tile.color]);
|
|
|
|
auto fmt = TRY(String::format("%d"_sv, tile.number));
|
|
|
|
auto font = ui::Font::default_bold_font();
|
|
auto rect = ui::align({ 0, 0, canvas.width, canvas.height },
|
|
{ 0, 0, (int)fmt.length() * font->width(), font->height() },
|
|
ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
|
|
auto subcanvas = canvas.subcanvas(rect);
|
|
|
|
Utf8StringDecoder decoder(fmt.chars());
|
|
wchar_t buf[4096];
|
|
TRY(decoder.decode(buf, sizeof(buf)));
|
|
|
|
font->render(buf, ui::BLACK, subcanvas);
|
|
|
|
return {};
|
|
}
|
|
};
|
|
|
|
Result<int> luna_main(int, char**)
|
|
{
|
|
srand((unsigned)time(NULL));
|
|
|
|
ui::App app;
|
|
TRY(app.init());
|
|
|
|
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 400 }));
|
|
app.set_main_window(window);
|
|
|
|
window->set_background(ui::BLACK);
|
|
window->set_title("2048");
|
|
|
|
auto* game = TRY(window->create_main_widget<GameWidget>());
|
|
game->reset();
|
|
|
|
return app.run();
|
|
}
|