246 lines
7.3 KiB
C++
246 lines
7.3 KiB
C++
#include "Log.h"
|
|
#include "Puzzle.h"
|
|
#include "bx/string.h"
|
|
#include "imgui.h"
|
|
#include "rendering/Rendering.h"
|
|
|
|
#include "bx/bx.h"
|
|
#include <cassert>
|
|
|
|
namespace
|
|
{
|
|
static constexpr Generated::PuzPos Dirs[4]{
|
|
{-1, 0},
|
|
{0, -1},
|
|
{0, 1},
|
|
{1, 0},
|
|
};
|
|
|
|
Generated::StaticPuzzleData* StaticDataInstance = nullptr;
|
|
} // namespace
|
|
|
|
namespace Generated
|
|
{
|
|
void Setup(StaticPuzzleData& data)
|
|
{
|
|
StaticDataInstance = &data;
|
|
LOG("Setting up static puzzle data");
|
|
for (int32_t i = 0; i < BX_COUNTOF(data.Cards); ++i)
|
|
{
|
|
data.Cards[i].ModelHandle = Game::GameRendering::Get().GetModelHandleFromPath("models/w straight.glb");
|
|
}
|
|
}
|
|
|
|
StaticPuzzleData& GetStaticPuzzleData()
|
|
{
|
|
assert(StaticDataInstance != nullptr);
|
|
return *StaticDataInstance;
|
|
}
|
|
const StaticPuzzleCard& GetCard(const StaticPuzzleData& data, StaticPuzzleCardHandle H)
|
|
{
|
|
assert(IsValid(H));
|
|
return data.Cards[H.Idx];
|
|
}
|
|
|
|
bool HasElement(const PuzzleNode& node, PuzzleElementType::Enum search)
|
|
{
|
|
for (int32_t i = 0; i < Puzzle::Config::MaxElementsPerTile; ++i)
|
|
{
|
|
if (node.PlacedTypes[i] == search) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsEmpty(const PuzzleNode& node)
|
|
{
|
|
for (int32_t i = 0; i < Puzzle::Config::MaxElementsPerTile; ++i)
|
|
{
|
|
if (node.PlacedTypes[i] != PuzzleElementType::None) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IsValid(StaticPuzzleCardHandle h)
|
|
{
|
|
return h.Idx != UINT16_MAX;
|
|
}
|
|
|
|
uint8_t GetRemainingCount(const PuzzleCardStack& stack)
|
|
{
|
|
assert(stack.MaxAvailableCount >= stack.UsedCount);
|
|
return stack.MaxAvailableCount - stack.UsedCount;
|
|
}
|
|
|
|
const PuzzleNode& GetNodeAt(const PuzzleData& puz, PuzPos pos)
|
|
{
|
|
assert(pos.X < Puzzle::Config::MaxPuzzleSizeCards && pos.Y < Puzzle::Config::MaxPuzzleSizeCards && pos.X >= 0 &&
|
|
pos.Y >= 0);
|
|
return puz.PlacedNodes[pos.Y * Puzzle::Config::MaxPuzzleSizeCards + pos.X];
|
|
}
|
|
|
|
PuzzleElementType::Enum GetElementAt(const PuzzleData& puz, ElemPos pos)
|
|
{
|
|
assert(pos.ElemIdx < Puzzle::Config::MaxElementsPerTile);
|
|
const PuzzleNode& node = GetNodeAt(puz, pos.Position);
|
|
return node.PlacedTypes[pos.ElemIdx];
|
|
}
|
|
|
|
bool PuzzleSolver::IsPuzzleSolved(const PuzzleData& puzzle)
|
|
{
|
|
bool IsSolved = true;
|
|
for (uint32_t i = 0; i < puzzle.GoalPositionCount; ++i)
|
|
{
|
|
if (!IsExitSatisfied(puzzle, puzzle.GoalPositions[i]))
|
|
{
|
|
IsSolved = false;
|
|
break;
|
|
}
|
|
}
|
|
return IsSolved;
|
|
}
|
|
|
|
bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, ElemPos pos)
|
|
{
|
|
const PuzzleNode& goalNode = GetNodeAt(puzzle, pos.Position);
|
|
PuzzleElementType::Enum goalType = GetElementAt(puzzle, pos);
|
|
|
|
uint32_t currentPositionQueueIdx = 0;
|
|
uint32_t positionQueueCount = 0;
|
|
PuzPos positionQueue[Puzzle::Config::MaxTilesInPuzzle];
|
|
|
|
positionQueue[0] = pos.Position;
|
|
positionQueueCount++;
|
|
while (positionQueueCount > 0)
|
|
{
|
|
assert(currentPositionQueueIdx < Puzzle::Config::MaxTilesInPuzzle);
|
|
PuzPos currentPos = positionQueue[currentPositionQueueIdx];
|
|
for (PuzPos dir : Dirs)
|
|
{
|
|
PuzPos nextPos = currentPos + dir;
|
|
if (IsValidGoalConnection(puzzle, nextPos, currentPos, goalType))
|
|
{
|
|
const PuzzleNode& node = GetNodeAt(puzzle, nextPos);
|
|
for (PuzzleElementType::Enum e : node.PlacedTypes)
|
|
{
|
|
if (IsValidSource(e, goalType))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
positionQueue[positionQueueCount] = nextPos;
|
|
positionQueueCount++;
|
|
}
|
|
}
|
|
currentPositionQueueIdx++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PuzzleSolver::IsValidGoalConnection(const PuzzleData& puzzle,
|
|
PuzPos flowFrom,
|
|
PuzPos flowTo,
|
|
PuzzleElementType::Enum goalType)
|
|
{
|
|
const PuzzleNode& from = GetNodeAt(puzzle, flowFrom);
|
|
const PuzzleNode& to = GetNodeAt(puzzle, flowTo);
|
|
|
|
if (goalType == PuzzleElementType::WaterGoal)
|
|
{
|
|
return HasElement(from, PuzzleElementType::WaterIn) || HasElement(from, PuzzleElementType::WaterChannel) ||
|
|
HasElement(from, PuzzleElementType::WaterGoal);
|
|
}
|
|
else if (goalType == PuzzleElementType::ElectricGoal)
|
|
{
|
|
return HasElement(from, PuzzleElementType::ElectricIn) ||
|
|
HasElement(from, PuzzleElementType::ElectricGoal) || IsEmpty(from);
|
|
}
|
|
assert(false);
|
|
return false;
|
|
}
|
|
bool PuzzleSolver::IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType)
|
|
{
|
|
return (sourceType == PuzzleElementType::WaterIn && goalType == PuzzleElementType::WaterGoal) ||
|
|
(sourceType == PuzzleElementType::ElectricIn && goalType == PuzzleElementType::ElectricGoal);
|
|
}
|
|
|
|
} // namespace Generated
|
|
|
|
namespace Generated
|
|
{
|
|
bool RenderDebugUI(PuzzleData& obj)
|
|
{
|
|
bool dataChanged = false;
|
|
|
|
bool isVisible = true;
|
|
if (ImGui::Begin("Puzzle", &isVisible))
|
|
{
|
|
ImGui::Text("%s", obj.PuzzleName);
|
|
|
|
int32_t W = obj.WidthTiles;
|
|
int32_t H = obj.HeightTiles;
|
|
ImGui::PushID("width");
|
|
ImGui::SetNextItemWidth(40);
|
|
if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
|
|
{
|
|
obj.WidthTiles = uint8_t(W);
|
|
dataChanged = true;
|
|
}
|
|
ImGui::PopID();
|
|
ImGui::SameLine();
|
|
ImGui::Text("x");
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(40);
|
|
ImGui::PushID("height");
|
|
if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
|
|
{
|
|
obj.HeightTiles = uint8_t(H);
|
|
dataChanged = true;
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::End();
|
|
|
|
if (dataChanged)
|
|
{
|
|
char path[128]{0};
|
|
bx::snprintf(path, sizeof(path), "game/data/puzzles/%s.pzl", obj.PuzzleName);
|
|
Serializer ser;
|
|
ser.Init(path);
|
|
if (Save(&obj, 1, ser))
|
|
{
|
|
LOG("Saved to %s", path);
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("Failed to save to %s", path);
|
|
}
|
|
ser.Finish();
|
|
}
|
|
|
|
return isVisible;
|
|
}
|
|
|
|
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs)
|
|
{
|
|
lhs.X += rhs.X;
|
|
lhs.Y += rhs.Y;
|
|
return lhs;
|
|
}
|
|
PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
|
|
{
|
|
lhs += rhs;
|
|
return lhs;
|
|
}
|
|
PuzPos operator-=(PuzPos lhs, const PuzPos& rhs)
|
|
{
|
|
lhs.X -= rhs.X;
|
|
lhs.Y -= rhs.Y;
|
|
return lhs;
|
|
}
|
|
PuzPos operator-(PuzPos lhs, const PuzPos& rhs)
|
|
{
|
|
lhs -= rhs;
|
|
return lhs;
|
|
}
|
|
} // namespace Generated
|