puzzle setup
This commit is contained in:
112
src/game/Puzzle.cpp
Normal file
112
src/game/Puzzle.cpp
Normal 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
120
src/game/Puzzle.h
Normal 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
|
||||||
Reference in New Issue
Block a user