apps: Add 2048 prototype
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
b3dc027ba0
commit
db2963d7bf
366
apps/2048.cpp
Normal file
366
apps/2048.cpp
Normal file
@ -0,0 +1,366 @@
|
||||
#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:
|
||||
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.key)
|
||||
{
|
||||
case 'w': {
|
||||
bool changed;
|
||||
changed = move_up();
|
||||
if (changed) should_add_tile = true;
|
||||
join_up();
|
||||
changed = move_up();
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
bool changed;
|
||||
changed = move_left();
|
||||
if (changed) should_add_tile = true;
|
||||
join_left();
|
||||
changed = move_left();
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 's': {
|
||||
bool changed;
|
||||
changed = move_down();
|
||||
if (changed) should_add_tile = true;
|
||||
join_down();
|
||||
changed = move_down();
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'd': {
|
||||
bool changed;
|
||||
changed = move_right();
|
||||
if (changed) should_add_tile = true;
|
||||
join_right();
|
||||
changed = move_right();
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'r': {
|
||||
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 argc, char** argv)
|
||||
{
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
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");
|
||||
|
||||
GameWidget game;
|
||||
window->set_main_widget(game);
|
||||
game.reset();
|
||||
|
||||
window->draw();
|
||||
|
||||
return app.run();
|
||||
}
|
@ -45,3 +45,5 @@ luna_app(about.cpp about)
|
||||
target_link_libraries(about PUBLIC ui)
|
||||
luna_app(taskbar.cpp taskbar)
|
||||
target_link_libraries(taskbar PUBLIC ui)
|
||||
luna_app(2048.cpp 2048)
|
||||
target_link_libraries(2048 PUBLIC ui)
|
||||
|
Loading…
Reference in New Issue
Block a user