implement state editing
This commit is contained in:
@ -20,7 +20,7 @@ constexpr int MENU_COLS = 3;
|
|||||||
// Camera Controls
|
// Camera Controls
|
||||||
constexpr float SIM_SPEED = 4.0;
|
constexpr float SIM_SPEED = 4.0;
|
||||||
constexpr float CAMERA_FOV = 120.0;
|
constexpr float CAMERA_FOV = 120.0;
|
||||||
constexpr float CAMERA_DISTANCE = 100.0;
|
constexpr float CAMERA_DISTANCE = 20.0;
|
||||||
constexpr float MIN_CAMERA_DISTANCE = 2.0;
|
constexpr float MIN_CAMERA_DISTANCE = 2.0;
|
||||||
constexpr float MAX_CAMERA_DISTANCE = 2000.0;
|
constexpr float MAX_CAMERA_DISTANCE = 2000.0;
|
||||||
constexpr float ZOOM_SPEED = 2.5;
|
constexpr float ZOOM_SPEED = 2.5;
|
||||||
|
|||||||
@ -252,7 +252,15 @@ public:
|
|||||||
public:
|
public:
|
||||||
auto Hash() const -> int;
|
auto Hash() const -> int;
|
||||||
|
|
||||||
auto AddBlock(Block block) -> bool;
|
auto AddColumn() const -> State;
|
||||||
|
|
||||||
|
auto RemoveColumn() const -> State;
|
||||||
|
|
||||||
|
auto AddRow() const -> State;
|
||||||
|
|
||||||
|
auto RemoveRow() const -> State;
|
||||||
|
|
||||||
|
auto AddBlock(const Block &block) -> bool;
|
||||||
|
|
||||||
auto GetBlock(int x, int y) const -> Block;
|
auto GetBlock(int x, int y) const -> Block;
|
||||||
|
|
||||||
|
|||||||
@ -92,10 +92,10 @@ public:
|
|||||||
const State ¤t) -> void;
|
const State ¤t) -> void;
|
||||||
|
|
||||||
auto DrawKlotski(const State &state, int hov_x, int hov_y, int sel_x,
|
auto DrawKlotski(const State &state, int hov_x, int hov_y, int sel_x,
|
||||||
int sel_y) -> void;
|
int sel_y, int block_add_x, int block_add_y) -> void;
|
||||||
|
|
||||||
auto DrawMenu(const MassSpringSystem &masssprings, int current_preset)
|
auto DrawMenu(const MassSpringSystem &masssprings, int current_preset,
|
||||||
-> void;
|
const State ¤t_state) -> void;
|
||||||
|
|
||||||
auto DrawTextures() -> void;
|
auto DrawTextures() -> void;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -45,7 +45,47 @@ auto Block::Collides(const Block &other) const -> bool {
|
|||||||
|
|
||||||
auto State::Hash() const -> int { return std::hash<std::string>{}(state); }
|
auto State::Hash() const -> int { return std::hash<std::string>{}(state); }
|
||||||
|
|
||||||
auto State::AddBlock(Block block) -> bool {
|
auto State::AddColumn() const -> State {
|
||||||
|
State newstate = State(width + 1, height, restricted);
|
||||||
|
|
||||||
|
for (const auto &block : *this) {
|
||||||
|
newstate.AddBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto State::RemoveColumn() const -> State {
|
||||||
|
State newstate = State(width - 1, height, restricted);
|
||||||
|
|
||||||
|
for (const auto &block : *this) {
|
||||||
|
newstate.AddBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto State::AddRow() const -> State {
|
||||||
|
State newstate = State(width, height + 1, restricted);
|
||||||
|
|
||||||
|
for (const auto &block : *this) {
|
||||||
|
newstate.AddBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto State::RemoveRow() const -> State {
|
||||||
|
State newstate = State(width, height - 1, restricted);
|
||||||
|
|
||||||
|
for (const auto &block : *this) {
|
||||||
|
newstate.AddBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto State::AddBlock(const Block &block) -> bool {
|
||||||
if (block.x + block.width > width || block.y + block.height > height) {
|
if (block.x + block.width > width || block.y + block.height > height) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
105
src/main.cpp
105
src/main.cpp
@ -25,10 +25,11 @@ auto apply_state(MassSpringSystem &mass_springs, StateGenerator generator)
|
|||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto solve_closure(MassSpringSystem &mass_springs, const State board) -> void {
|
auto populate_masssprings(MassSpringSystem &mass_springs,
|
||||||
|
const State ¤t_state) -> void {
|
||||||
std::pair<std::unordered_set<std::string>,
|
std::pair<std::unordered_set<std::string>,
|
||||||
std::vector<std::pair<std::string, std::string>>>
|
std::vector<std::pair<std::string, std::string>>>
|
||||||
closure = board.Closure();
|
closure = current_state.Closure();
|
||||||
for (const auto &state : closure.first) {
|
for (const auto &state : closure.first) {
|
||||||
Vector3 pos =
|
Vector3 pos =
|
||||||
Vector3(static_cast<float>(GetRandomValue(-10000, 10000)) / 1000.0,
|
Vector3(static_cast<float>(GetRandomValue(-10000, 10000)) / 1000.0,
|
||||||
@ -53,6 +54,14 @@ auto solve_closure(MassSpringSystem &mass_springs, const State board) -> void {
|
|||||||
<< " Bytes for springs." << std::endl;
|
<< " Bytes for springs." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto clear_masssprings(MassSpringSystem &masssprings,
|
||||||
|
const State ¤t_state) -> std::string {
|
||||||
|
masssprings.masses.clear();
|
||||||
|
masssprings.springs.clear();
|
||||||
|
masssprings.AddMass(MASS, Vector3Zero(), false, current_state.state);
|
||||||
|
return current_state.state;
|
||||||
|
}
|
||||||
|
|
||||||
auto main(int argc, char *argv[]) -> int {
|
auto main(int argc, char *argv[]) -> int {
|
||||||
// if (argc < 2) {
|
// if (argc < 2) {
|
||||||
// std::cout << "Missing .klotski file." << std::endl;
|
// std::cout << "Missing .klotski file." << std::endl;
|
||||||
@ -83,6 +92,9 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
float frametime;
|
float frametime;
|
||||||
|
bool has_block_add_xy = false;
|
||||||
|
int block_add_x = -1;
|
||||||
|
int block_add_y = -1;
|
||||||
int hov_x = 0;
|
int hov_x = 0;
|
||||||
int hov_y = 0;
|
int hov_y = 0;
|
||||||
int sel_x = 0;
|
int sel_x = 0;
|
||||||
@ -95,6 +107,7 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
int time_measure_count = 0;
|
int time_measure_count = 0;
|
||||||
while (!WindowShouldClose()) {
|
while (!WindowShouldClose()) {
|
||||||
frametime = GetFrameTime();
|
frametime = GetFrameTime();
|
||||||
|
std::string previous_state = current_state.state;
|
||||||
|
|
||||||
// Mouse handling
|
// Mouse handling
|
||||||
const int board_width = GetScreenWidth() / 2.0 - 2 * BOARD_PADDING;
|
const int board_width = GetScreenWidth() / 2.0 - 2 * BOARD_PADDING;
|
||||||
@ -121,12 +134,58 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
hov_y = (m.y - MENU_HEIGHT - y_offset) / (block_size + 2 * BLOCK_PADDING);
|
hov_y = (m.y - MENU_HEIGHT - y_offset) / (block_size + 2 * BLOCK_PADDING);
|
||||||
}
|
}
|
||||||
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
||||||
sel_x = hov_x;
|
// If we clicked a block...
|
||||||
sel_y = hov_y;
|
if (current_state.GetBlock(hov_x, hov_y).IsValid()) {
|
||||||
|
sel_x = hov_x;
|
||||||
|
sel_y = hov_y;
|
||||||
|
}
|
||||||
|
// If we clicked empty space...
|
||||||
|
else {
|
||||||
|
// Select a position
|
||||||
|
if (!has_block_add_xy) {
|
||||||
|
if (hov_x >= 0 && hov_x < current_state.width && hov_y >= 0 &&
|
||||||
|
hov_y < current_state.height) {
|
||||||
|
block_add_x = hov_x;
|
||||||
|
block_add_y = hov_y;
|
||||||
|
has_block_add_xy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we have already selected a position
|
||||||
|
else {
|
||||||
|
int block_add_width = hov_x - block_add_x + 1;
|
||||||
|
int block_add_height = hov_y - block_add_y + 1;
|
||||||
|
if (block_add_width <= 0 || block_add_height <= 0) {
|
||||||
|
block_add_x = -1;
|
||||||
|
block_add_y = -1;
|
||||||
|
has_block_add_xy = false;
|
||||||
|
} else if (block_add_x >= 0 &&
|
||||||
|
block_add_x + block_add_width <= current_state.width &&
|
||||||
|
block_add_y >= 0 &&
|
||||||
|
block_add_y + block_add_height <= current_state.height) {
|
||||||
|
bool success = current_state.AddBlock(
|
||||||
|
Block(block_add_x, block_add_y, block_add_width,
|
||||||
|
block_add_height, false));
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
block_add_x = -1;
|
||||||
|
block_add_y = -1;
|
||||||
|
has_block_add_xy = false;
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) {
|
||||||
|
if (current_state.RemoveBlock(hov_x, hov_y)) {
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
|
} else if (has_block_add_xy) {
|
||||||
|
block_add_x = -1;
|
||||||
|
block_add_y = -1;
|
||||||
|
has_block_add_xy = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key handling
|
// Key handling
|
||||||
std::string previous_state = current_state.state;
|
|
||||||
if (IsKeyPressed(KEY_W)) {
|
if (IsKeyPressed(KEY_W)) {
|
||||||
if (current_state.MoveBlockAt(sel_x, sel_y, Direction::NOR)) {
|
if (current_state.MoveBlockAt(sel_x, sel_y, Direction::NOR)) {
|
||||||
sel_y--;
|
sel_y--;
|
||||||
@ -156,19 +215,34 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
previous_state = current_state.state;
|
previous_state = current_state.state;
|
||||||
} else if (IsKeyPressed(KEY_R)) {
|
} else if (IsKeyPressed(KEY_R)) {
|
||||||
current_state = generators[current_preset]();
|
current_state = generators[current_preset]();
|
||||||
previous_state = current_state.state;
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
} else if (IsKeyPressed(KEY_C)) {
|
|
||||||
solve_closure(masssprings, current_state);
|
|
||||||
renderer.UpdateWinningStates(masssprings, win_conditions[current_preset]);
|
|
||||||
} else if (IsKeyPressed(KEY_G)) {
|
} else if (IsKeyPressed(KEY_G)) {
|
||||||
masssprings.masses.clear();
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
masssprings.springs.clear();
|
populate_masssprings(masssprings, current_state);
|
||||||
masssprings.AddMass(MASS, Vector3Zero(), false, current_state.state);
|
renderer.UpdateWinningStates(masssprings, win_conditions[current_preset]);
|
||||||
previous_state = current_state.state;
|
} else if (IsKeyPressed(KEY_C)) {
|
||||||
|
// We also need to clear the graph, in case the state has been edited.
|
||||||
|
// Then the graph would contain states that are impossible.
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
} else if (IsKeyPressed(KEY_I)) {
|
} else if (IsKeyPressed(KEY_I)) {
|
||||||
renderer.mark_solutions = !renderer.mark_solutions;
|
renderer.mark_solutions = !renderer.mark_solutions;
|
||||||
} else if (IsKeyPressed(KEY_O)) {
|
} else if (IsKeyPressed(KEY_O)) {
|
||||||
renderer.connect_solutions = !renderer.connect_solutions;
|
renderer.connect_solutions = !renderer.connect_solutions;
|
||||||
|
} else if (IsKeyPressed(KEY_T)) {
|
||||||
|
current_state.restricted = !current_state.restricted;
|
||||||
|
} else if (IsKeyPressed(KEY_LEFT) && current_state.width > 1) {
|
||||||
|
current_state = current_state.RemoveColumn();
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
|
} else if (IsKeyPressed(KEY_RIGHT) && current_state.width < 9) {
|
||||||
|
current_state = current_state.AddColumn();
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
|
|
||||||
|
} else if (IsKeyPressed(KEY_UP) && current_state.height > 1) {
|
||||||
|
current_state = current_state.RemoveRow();
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
|
} else if (IsKeyPressed(KEY_DOWN) && current_state.height < 9) {
|
||||||
|
current_state = current_state.AddRow();
|
||||||
|
previous_state = clear_masssprings(masssprings, current_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous_state != current_state.state) {
|
if (previous_state != current_state.state) {
|
||||||
@ -206,8 +280,9 @@ auto main(int argc, char *argv[]) -> int {
|
|||||||
renderer.UpdateCamera(masssprings, current_state);
|
renderer.UpdateCamera(masssprings, current_state);
|
||||||
renderer.UpdateTextureSizes();
|
renderer.UpdateTextureSizes();
|
||||||
renderer.DrawMassSprings(masssprings, current_state);
|
renderer.DrawMassSprings(masssprings, current_state);
|
||||||
renderer.DrawKlotski(current_state, hov_x, hov_y, sel_x, sel_y);
|
renderer.DrawKlotski(current_state, hov_x, hov_y, sel_x, sel_y, block_add_x,
|
||||||
renderer.DrawMenu(masssprings, current_preset);
|
block_add_y);
|
||||||
|
renderer.DrawMenu(masssprings, current_preset, current_state);
|
||||||
renderer.DrawTextures();
|
renderer.DrawTextures();
|
||||||
std::chrono::high_resolution_clock::time_point re =
|
std::chrono::high_resolution_clock::time_point re =
|
||||||
std::chrono::high_resolution_clock::now();
|
std::chrono::high_resolution_clock::now();
|
||||||
|
|||||||
@ -176,7 +176,8 @@ auto Renderer::DrawMassSprings(const MassSpringSystem &masssprings,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Renderer::DrawKlotski(const State &state, int hov_x, int hov_y, int sel_x,
|
auto Renderer::DrawKlotski(const State &state, int hov_x, int hov_y, int sel_x,
|
||||||
int sel_y) -> void {
|
int sel_y, int block_add_x, int block_add_y)
|
||||||
|
-> void {
|
||||||
BeginTextureMode(klotski_target);
|
BeginTextureMode(klotski_target);
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
@ -244,13 +245,22 @@ auto Renderer::DrawKlotski(const State &state, int hov_x, int hov_y, int sel_x,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw editing starting position
|
||||||
|
if (block_add_x >= 0 && block_add_y >= 0) {
|
||||||
|
DrawCircle(x_offset + BOARD_PADDING + block_add_x * BLOCK_PADDING * 2 +
|
||||||
|
BLOCK_PADDING + block_add_x * block_size + block_size / 2,
|
||||||
|
y_offset + BOARD_PADDING + block_add_y * BLOCK_PADDING * 2 +
|
||||||
|
BLOCK_PADDING + block_add_y * block_size + block_size / 2,
|
||||||
|
block_size / 10.0, Fade(BLACK, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
DrawLine(GetScreenWidth() / 2 - 1, 0, GetScreenWidth() / 2 - 1,
|
DrawLine(GetScreenWidth() / 2 - 1, 0, GetScreenWidth() / 2 - 1,
|
||||||
GetScreenHeight() - MENU_HEIGHT, BLACK);
|
GetScreenHeight() - MENU_HEIGHT, BLACK);
|
||||||
EndTextureMode();
|
EndTextureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Renderer::DrawMenu(const MassSpringSystem &masssprings, int current_preset)
|
auto Renderer::DrawMenu(const MassSpringSystem &masssprings, int current_preset,
|
||||||
-> void {
|
const State ¤t_state) -> void {
|
||||||
BeginTextureMode(menu_target);
|
BeginTextureMode(menu_target);
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
@ -264,7 +274,7 @@ auto Renderer::DrawMenu(const MassSpringSystem &masssprings, int current_preset)
|
|||||||
auto draw_btn = [&](int x, int y, std::string text, Color color) {
|
auto draw_btn = [&](int x, int y, std::string text, Color color) {
|
||||||
int posx = MENU_PAD + x * (MENU_PAD + btn_width);
|
int posx = MENU_PAD + x * (MENU_PAD + btn_width);
|
||||||
int posy = MENU_PAD + y * (MENU_PAD + btn_height);
|
int posy = MENU_PAD + y * (MENU_PAD + btn_height);
|
||||||
DrawRectangle(posx, posy, btn_width, btn_height, Fade(color, 0.5));
|
DrawRectangle(posx, posy, btn_width, btn_height, Fade(color, 0.6));
|
||||||
DrawRectangleLines(posx, posy, btn_width, btn_height, color);
|
DrawRectangleLines(posx, posy, btn_width, btn_height, color);
|
||||||
DrawText(text.data(), posx + BUTTON_PAD, posy + BUTTON_PAD,
|
DrawText(text.data(), posx + BUTTON_PAD, posy + BUTTON_PAD,
|
||||||
btn_height - 2 * BUTTON_PAD, WHITE);
|
btn_height - 2 * BUTTON_PAD, WHITE);
|
||||||
@ -284,16 +294,20 @@ auto Renderer::DrawMenu(const MassSpringSystem &masssprings, int current_preset)
|
|||||||
std::format("Lock Camera to Current State (L): {}", camera.target_lock),
|
std::format("Lock Camera to Current State (L): {}", camera.target_lock),
|
||||||
DARKGREEN);
|
DARKGREEN);
|
||||||
|
|
||||||
draw_btn(1, 0, std::format("Reset Board State (R)"), DARKBLUE);
|
draw_btn(1, 0, std::format("Reset State + Graph (R)"), DARKBLUE);
|
||||||
draw_btn(1, 1, std::format("Preset (M/N): {}", current_preset), DARKBLUE);
|
draw_btn(1, 1, std::format("Add/Remove Col/Row (Arrow Keys)"), DARKBLUE);
|
||||||
draw_btn(1, 2, std::format("Print Board State to Console (P)"), DARKBLUE);
|
draw_btn(1, 2, std::format("Print Board State to Console (P)"), DARKBLUE);
|
||||||
|
|
||||||
draw_btn(2, 0,
|
draw_btn(2, 0,
|
||||||
|
std::format("Preset (M/N): {}, {} (T)", current_preset,
|
||||||
|
current_state.restricted ? "Restricted" : "Free"),
|
||||||
|
DARKPURPLE);
|
||||||
|
draw_btn(2, 1, std::format("Populate Graph (G), Clear Graph (C)"),
|
||||||
|
DARKPURPLE);
|
||||||
|
draw_btn(2, 2,
|
||||||
std::format("Mark (I): {} / Connect (O): {}", mark_solutions,
|
std::format("Mark (I): {} / Connect (O): {}", mark_solutions,
|
||||||
connect_solutions),
|
connect_solutions),
|
||||||
DARKPURPLE);
|
DARKPURPLE);
|
||||||
draw_btn(2, 1, std::format("Solve Board Closure (C)"), DARKPURPLE);
|
|
||||||
draw_btn(2, 2, std::format("Clear Graph (G)"), DARKPURPLE);
|
|
||||||
|
|
||||||
DrawLine(0, MENU_HEIGHT - 1, GetScreenWidth(), MENU_HEIGHT - 1, BLACK);
|
DrawLine(0, MENU_HEIGHT - 1, GetScreenWidth(), MENU_HEIGHT - 1, BLACK);
|
||||||
EndTextureMode();
|
EndTextureMode();
|
||||||
|
|||||||
Reference in New Issue
Block a user