puzzle setup

This commit is contained in:
Asuro
2025-02-22 03:04:36 +01:00
parent 833d78c142
commit d7ed08eb08
2 changed files with 232 additions and 0 deletions

112
src/game/Puzzle.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include "Puzzle.h"
#include <cassert>
namespace
{
static constexpr Puzzle::PuzPos Dirs[4]{
{-1, 0},
{0, -1},
{0, 1},
{1, 0},
};
}
namespace Puzzle
{
bool PuzzleNode::HasElement(PuzzleElementType search) const
{
for (int32_t i = 0; i < Config::MaxElementsPerTile; ++i)
{
if (PlacedTypes[i] == search) return true;
}
return false;
}
bool PuzzleNode::IsEmpty() const
{
for (int32_t i = 0; i < Config::MaxElementsPerTile; ++i)
{
if (PlacedTypes[i] != PuzzleElementType::None) return false;
}
return true;
}
uint8_t PuzzleCardStack::GetRemainingCount()
{
assert(MaxAvailableCount >= UsedCount);
return MaxAvailableCount - UsedCount;
}
bool PuzzleSolver::IsPuzzleSolved(const PuzzleData& puzzle)
{
bool IsSolved = true;
for (uint32_t i = 0; i < puzzle.GoalPositionCount; ++i)
{
}
return IsSolved;
}
bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, ElemPos pos)
{
const PuzzleNode& goalNode = puzzle.GetNodeAt(pos.Position);
PuzzleElementType goalType = puzzle.GetElementAt(pos);
uint32_t currentPositionQueueIdx = 0;
uint32_t positionQueueCount = 0;
PuzPos positionQueue[Config::MaxTilesInPuzzle];
positionQueue[0] = pos.Position;
positionQueueCount++;
while (positionQueueCount > 0)
{
assert(currentPositionQueueIdx < Config::MaxTilesInPuzzle);
PuzPos currentPos = positionQueue[currentPositionQueueIdx];
for (PuzPos dir : Dirs)
{
PuzPos nextPos = currentPos + dir;
if (IsValidGoalConnection(puzzle, nextPos, currentPos, goalType))
{
const PuzzleNode& node = puzzle.GetNodeAt(nextPos);
for (PuzzleElementType 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 goalType)
{
const PuzzleNode& from = puzzle.GetNodeAt(flowFrom);
const PuzzleNode& to = puzzle.GetNodeAt(flowTo);
if (goalType == PuzzleElementType::WaterGoal)
{
return from.HasElement(PuzzleElementType::WaterIn) || from.HasElement(PuzzleElementType::WaterChannel) ||
from.HasElement(PuzzleElementType::WaterGoal);
}
else if (goalType == PuzzleElementType::ElectricGoal)
{
return from.HasElement(PuzzleElementType::ElectricIn) || from.HasElement(PuzzleElementType::ElectricGoal) ||
from.IsEmpty();
}
assert(false);
return false;
}
bool PuzzleSolver::IsValidSource(PuzzleElementType sourceType, PuzzleElementType goalType)
{
return (sourceType == PuzzleElementType::WaterIn && goalType == PuzzleElementType::WaterGoal) ||
(sourceType == PuzzleElementType::ElectricIn && goalType == PuzzleElementType::ElectricGoal);
}
} // namespace Puzzle

120
src/game/Puzzle.h Normal file
View File

@@ -0,0 +1,120 @@
#pragma once
#include <cstdint>
namespace Puzzle
{
struct Config
{
static constexpr uint32_t CardSize = 2;
static constexpr uint32_t NodesPerCard = CardSize * CardSize;
static constexpr uint32_t MaxElementsPerTile = 4;
static constexpr uint32_t MaxPuzzleSizeCards = 16;
static constexpr uint32_t MaxCardsInPuzzle = MaxPuzzleSizeCards * MaxPuzzleSizeCards;
static constexpr uint32_t MaxPuzzleSizeTiles = 16 * CardSize;
static constexpr uint32_t MaxTilesInPuzzle = MaxPuzzleSizeTiles * MaxPuzzleSizeTiles;
static constexpr uint32_t MaxAvailableStacks = 16;
static constexpr uint32_t MaxGoalPositions = 16;
};
struct PuzPos
{
int8_t X = 0;
int8_t Y = 0;
PuzPos& operator+=(const PuzPos& rhs)
{
X += rhs.X;
Y += rhs.Y;
return *this;
}
friend PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
{
lhs += rhs;
return lhs;
}
};
struct ElemPos
{
PuzPos Position;
uint8_t ElemIdx = 0;
};
enum class PuzzleElementType : uint8_t
{
None,
WaterIn,
WaterGoal,
WaterChannel,
ElectricIn,
ElectricGoal,
Blocked,
Bridge,
};
struct PuzzleNode
{
PuzzleElementType PlacedTypes[Config::MaxElementsPerTile]{PuzzleElementType::None};
bool HasElement(PuzzleElementType search) const;
bool IsEmpty() const;
};
struct StaticPuzzleCard
{
PuzzleNode Nodes[Config::NodesPerCard];
};
struct StaticPuzzleCardHandle
{
uint16_t Idx = UINT16_MAX;
};
struct PlacedPuzzleCard
{
StaticPuzzleCardHandle RefCard;
PuzPos Position;
uint8_t Rotation = 0;
bool IsLocked = false;
};
struct PuzzleCardStack
{
StaticPuzzleCardHandle RefCard;
uint8_t MaxAvailableCount = 0;
uint8_t UsedCount = 0;
uint8_t GetRemainingCount();
};
struct PuzzleData
{
uint32_t AvailableCardCount = 0;
PuzzleCardStack AvailableCards[Config::MaxAvailableStacks];
uint32_t PlacedCardCount = 0;
PlacedPuzzleCard PlacedCards[Config::MaxCardsInPuzzle];
// Indexed by board position
PuzzleNode PlacedNodes[Config::MaxTilesInPuzzle];
uint32_t GoalPositionCount = 0;
ElemPos GoalPositions[Config::MaxGoalPositions];
const PuzzleNode& GetNodeAt(PuzPos pos) const;
PuzzleElementType GetElementAt(ElemPos pos) const;
};
struct PuzzleSolver
{
bool IsPuzzleSolved(const PuzzleData& puzzle);
bool IsExitSatisfied(const PuzzleData& puzzle, ElemPos pos);
// This assumes flowFrom is already verified to be connected.
bool IsValidGoalConnection(const PuzzleData& puzzle,
PuzPos flowFrom,
PuzPos flowTo,
PuzzleElementType goalType);
bool IsValidSource(PuzzleElementType sourceType, PuzzleElementType goalType);
};
} // namespace Puzzle