implement immovable blocks (walls) and add two presets
This commit is contained in:
@ -11,20 +11,23 @@ R4599121212..................................
|
||||
F449921....111222..11......111111....
|
||||
|
||||
# RushHour 1
|
||||
R6642..13..21..13..................ba..........12............21..............
|
||||
|
||||
# RushHour 2
|
||||
R664231........13................12ba..........1221..12..12..21........31....
|
||||
|
||||
# RushHour 3
|
||||
# RushHour 2
|
||||
R66421231....1213..31........ba..121212..21................1221..........21..
|
||||
|
||||
# RushHour 4
|
||||
# RushHour 3
|
||||
R664231....12....1221......12..ba..1212..21..12....12......21......21..21....
|
||||
|
||||
# RushHour 5
|
||||
# RushHour 4
|
||||
R66421321....12....1212....13......ba....31....12........12..21..21....21....
|
||||
|
||||
# RushHour + Walls 1
|
||||
R66421221..AA..........1221..12ba............1221..1321..........21..31......
|
||||
|
||||
# RushHour + Walls 2
|
||||
R664221....1212AA31..........12ba..12........12..21........21..12....21..AA..
|
||||
|
||||
# Dad's Puzzler
|
||||
F4503bb..21......21..1111....121221......21..
|
||||
|
||||
|
||||
@ -58,5 +58,7 @@ constexpr Color BLOCK_COLOR = DARKGREEN;
|
||||
constexpr Color HL_BLOCK_COLOR = GREEN;
|
||||
constexpr Color TARGET_BLOCK_COLOR = RED;
|
||||
constexpr Color HL_TARGET_BLOCK_COLOR = ORANGE;
|
||||
constexpr Color WALL_COLOR = BLACK;
|
||||
constexpr Color HL_WALL_COLOR = DARKGRAY;
|
||||
|
||||
#endif
|
||||
|
||||
@ -21,8 +21,11 @@ enum Direction {
|
||||
// A block is represented as a 2-digit string "wh", where w is the block width
|
||||
// and h the block height.
|
||||
// The target block (to remove from the board) is represented as a 2-letter
|
||||
// string "xy", where x is the block width and y the block height and
|
||||
// lower-case string "xy", where x is the block width and y the block height and
|
||||
// width/height are represented by [abcdefghi] (~= [123456789]).
|
||||
// Immovable blocks are represented as a 2-letter upper-case string "XY", where
|
||||
// X is the block width and Y the block height and width/height are represented
|
||||
// by [ABCDEFGHI] (~= [123456789]).
|
||||
class Block {
|
||||
public:
|
||||
int x;
|
||||
@ -30,16 +33,21 @@ public:
|
||||
int width;
|
||||
int height;
|
||||
bool target;
|
||||
bool immovable;
|
||||
|
||||
public:
|
||||
Block(int _x, int _y, int _width, int _height, bool _target)
|
||||
: x(_x), y(_y), width(_width), height(_height), target(_target) {
|
||||
Block(int _x, int _y, int _width, int _height, bool _target, bool _immovable)
|
||||
: x(_x), y(_y), width(_width), height(_height), target(_target),
|
||||
immovable(_immovable) {
|
||||
if (_x < 0 || _x + _width >= 10 || _y < 0 || _y + _height >= 10) {
|
||||
std::cerr << "Block must fit on a 9x9 board!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Block(int _x, int _y, int _width, int _height, bool _target)
|
||||
: Block(_x, _y, _width, _height, _target, false) {}
|
||||
|
||||
Block(int _x, int _y, std::string block) : x(_x), y(_y) {
|
||||
if (block == "..") {
|
||||
this->x = 0;
|
||||
@ -50,20 +58,34 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const std::array<char, 9> chars{'a', 'b', 'c', 'd', 'e',
|
||||
const std::array<char, 9> target_chars{'a', 'b', 'c', 'd', 'e',
|
||||
'f', 'g', 'h', 'i'};
|
||||
|
||||
target = false;
|
||||
for (const char c : chars) {
|
||||
for (const char c : target_chars) {
|
||||
if (block.contains(c)) {
|
||||
target = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<char, 9> immovable_chars{'A', 'B', 'C', 'D', 'E',
|
||||
'F', 'G', 'H', 'I'};
|
||||
|
||||
immovable = false;
|
||||
for (const char c : immovable_chars) {
|
||||
if (block.contains(c)) {
|
||||
immovable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target) {
|
||||
width = static_cast<int>(block.at(0)) - static_cast<int>('a') + 1;
|
||||
height = static_cast<int>(block.at(1)) - static_cast<int>('a') + 1;
|
||||
} else if (immovable) {
|
||||
width = static_cast<int>(block.at(0)) - static_cast<int>('A') + 1;
|
||||
height = static_cast<int>(block.at(1)) - static_cast<int>('A') + 1;
|
||||
} else {
|
||||
width = std::stoi(block.substr(0, 1));
|
||||
height = std::stoi(block.substr(1, 1));
|
||||
@ -81,7 +103,7 @@ public:
|
||||
|
||||
bool operator==(const Block &other) {
|
||||
return x == other.x && y == other.y && width && other.width &&
|
||||
target == other.target;
|
||||
target == other.target && immovable == other.immovable;
|
||||
}
|
||||
|
||||
bool operator!=(const Block &other) { return !(*this == other); }
|
||||
@ -103,12 +125,13 @@ public:
|
||||
};
|
||||
|
||||
// A state is represented by a string "MWHXYblocks", where M is "R"
|
||||
// (restricted) or "F" (free), W is the board width, H is the board height, X is
|
||||
// the target block x goal, Y is the target block y goal and blocks is an
|
||||
// enumeration of each of the board's cells, with each cell being a 2-letter or
|
||||
// 2-digit block representation (a 3x3 board would have a string representation
|
||||
// with length 5 + 3*3 * 2). The board's cells are enumerated from top-left to
|
||||
// bottom-right with each block's pivot being its top-left corner.
|
||||
// (restricted) or "F" (free), W is the board width, H is the board height, X
|
||||
// is the target block x goal, Y is the target block y goal and blocks is an
|
||||
// enumeration of each of the board's cells, with each cell being a 2-letter
|
||||
// or 2-digit block representation (a 3x3 board would have a string
|
||||
// representation with length 5 + 3*3 * 2). The board's cells are enumerated
|
||||
// from top-left to bottom-right with each block's pivot being its top-left
|
||||
// corner.
|
||||
class State {
|
||||
public:
|
||||
static constexpr int prefix = 5;
|
||||
@ -250,6 +273,8 @@ public:
|
||||
|
||||
auto ToggleTarget(int x, int y) -> bool;
|
||||
|
||||
auto ToggleWall(int x, int y) -> bool;
|
||||
|
||||
auto ToggleRestricted() -> void;
|
||||
|
||||
auto MoveBlockAt(int x, int y, Direction dir) -> bool;
|
||||
|
||||
@ -167,6 +167,10 @@ auto InputHandler::HandleKeys() -> void {
|
||||
state.current_state.ToggleTarget(sel_x, sel_y);
|
||||
state.ClearGraph();
|
||||
state.edited = true;
|
||||
} else if (IsKeyPressed(KEY_Y)) {
|
||||
state.current_state.ToggleWall(sel_x, sel_y);
|
||||
state.ClearGraph();
|
||||
state.edited = true;
|
||||
} else if (IsKeyPressed(KEY_LEFT) && state.current_state.width > 1) {
|
||||
state.current_state = state.current_state.RemoveColumn();
|
||||
state.ClearGraph();
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#endif
|
||||
|
||||
// TODO: Click states in the graph to display them in the board
|
||||
// TODO: Add walls (unmoveable blocks)
|
||||
|
||||
// NOTE: Tracy uses a huge amount of memory. For longer testing disable Tracy.
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ auto Block::Hash() const -> int {
|
||||
}
|
||||
|
||||
auto Block::Invalid() -> Block {
|
||||
Block block = Block(0, 0, 1, 1, false);
|
||||
Block block = Block(0, 0, 1, 1, false, false);
|
||||
block.width = 0;
|
||||
block.height = 0;
|
||||
return block;
|
||||
@ -27,12 +27,20 @@ auto Block::ToString() const -> std::string {
|
||||
return std::format("{}{}",
|
||||
static_cast<char>(width + static_cast<int>('a') - 1),
|
||||
static_cast<char>(height + static_cast<int>('a') - 1));
|
||||
} else if (immovable) {
|
||||
return std::format("{}{}",
|
||||
static_cast<char>(width + static_cast<int>('A') - 1),
|
||||
static_cast<char>(height + static_cast<int>('A') - 1));
|
||||
} else {
|
||||
return std::format("{}{}", width, height);
|
||||
}
|
||||
}
|
||||
|
||||
auto Block::GetPrincipalDirs() const -> int {
|
||||
if (immovable) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (width > height) {
|
||||
return Direction::EAS | Direction::WES;
|
||||
} else if (height > width) {
|
||||
@ -194,7 +202,7 @@ auto State::RemoveBlock(int x, int y) -> bool {
|
||||
|
||||
auto State::ToggleTarget(int x, int y) -> bool {
|
||||
Block block = GetBlock(x, y);
|
||||
if (!block.IsValid()) {
|
||||
if (!block.IsValid() || block.immovable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -217,6 +225,20 @@ auto State::ToggleTarget(int x, int y) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto State::ToggleWall(int x, int y) -> bool {
|
||||
Block block = GetBlock(x, y);
|
||||
if (!block.IsValid() || block.target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the new target
|
||||
block.immovable = !block.immovable;
|
||||
int index = GetIndex(block.x, block.y);
|
||||
state.replace(index, 2, block.ToString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto State::ToggleRestricted() -> void {
|
||||
restricted = !restricted;
|
||||
state.replace(0, 1, restricted ? "R" : "F");
|
||||
@ -224,7 +246,7 @@ auto State::ToggleRestricted() -> void {
|
||||
|
||||
auto State::MoveBlockAt(int x, int y, Direction dir) -> bool {
|
||||
Block block = GetBlock(x, y);
|
||||
if (!block.IsValid()) {
|
||||
if (!block.IsValid() || block.immovable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -285,6 +307,10 @@ auto State::GetNextStates() const -> std::vector<State> {
|
||||
: Direction::NOR | Direction::EAS | Direction::SOU |
|
||||
Direction::WES;
|
||||
|
||||
if (b.immovable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirs & Direction::NOR) {
|
||||
State north = *this;
|
||||
if (north.MoveBlockAt(b.x, b.y, Direction::NOR)) {
|
||||
|
||||
@ -241,6 +241,12 @@ auto Renderer::DrawKlotski() -> void {
|
||||
} else {
|
||||
c = TARGET_BLOCK_COLOR;
|
||||
}
|
||||
} else if (block.immovable) {
|
||||
if (block.Covers(input.sel_x, input.sel_y)) {
|
||||
c = HL_WALL_COLOR;
|
||||
} else {
|
||||
c = WALL_COLOR;
|
||||
}
|
||||
}
|
||||
DrawRectangle(x_offset + BOARD_PADDING + block.x * BLOCK_PADDING * 2 +
|
||||
BLOCK_PADDING + block.x * block_size,
|
||||
@ -360,7 +366,7 @@ auto Renderer::DrawMenu(const std::vector<Vector3> &masses) -> void {
|
||||
DARKGREEN);
|
||||
|
||||
// Center column
|
||||
draw_btn(1, 0, std::format("Select (LMB) / Move (W, A, S, D) / Target (T)"),
|
||||
draw_btn(1, 0, std::format("Select (LMB) / Move (WASD) / Target/Wall (T/Y)"),
|
||||
DARKBLUE);
|
||||
draw_btn(1, 1, std::format("Add/Remove Col/Row (Arrow Keys)"), DARKBLUE);
|
||||
draw_btn(1, 2, std::format("Add/Remove Block (LMB/RMB) / Set Goal (MMB)"),
|
||||
|
||||
Reference in New Issue
Block a user