Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 63 additions & 9 deletions puzzle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,66 @@ inline void Board::unassign(const Position &position, const Stone &stone)
assign(position, empty);
}

bool Board::isValid(const Position &position) const
{
if (position.horizontal) {
// Check the single row
{
Counter counter;
for (size_t col = 0; col < size_; ++col) {
if (data_[position.row][col] != empty_) {
counter.add(data_[position.row][col]);
}
}
if (!counter.isValid()) {
return false;
}
}
// Check affected columns
for (size_t k = 0; k < position.size; ++k) {
size_t const col = position.col + k;
if (col >= size_) continue;
Counter counter;
for (size_t row = 0; row < size_; ++row) {
if (data_[row][col] != empty_) {
counter.add(data_[row][col]);
}
}
if (!counter.isValid()) {
return false;
}
}
} else {
// Check affected rows
for (size_t k = 0; k < position.size; ++k) {
size_t const row = position.row + k;
if (row >= size_) continue;
Counter counter;
for (size_t col = 0; col < size_; ++col) {
if (data_[row][col] != empty_) {
counter.add(data_[row][col]);
}
}
if (!counter.isValid()) {
return false;
}
}
// Check the single column
{
Counter counter;
for (size_t row = 0; row < size_; ++row) {
if (data_[row][position.col] != empty_) {
counter.add(data_[row][position.col]);
}
}
if (!counter.isValid()) {
return false;
}
}
}
return true;
}

void Board::print() const
{
printf("%s", string(4 * size_, '-').c_str());
Expand Down Expand Up @@ -390,13 +450,7 @@ void Solver::printSolution(const Solution &solution)
void Solver::findAssignment(Solutions &solutions, Board &board, Stones &stones, size_t layoutIndex,
Solution &solution) const
{
auto const boardValid = board.isValid();
if (!boardValid) {
// No more solutions possible, stop recursion
return;
}

if (boardValid && stones.empty()) {
if (stones.empty()) {
// Solution found, stop recursion
solutions.push_back(solution);
return;
Expand All @@ -414,7 +468,7 @@ void Solver::findAssignment(Solutions &solutions, Board &board, Stones &stones,
{
// Try to fit the stone in forward direction. If it works, move on. Later on clean up.
board.assign(positions[layoutIndex], stone);
if (board.isValid()) {
if (board.isValid(positions[layoutIndex])) {
solution.push_back({positions[layoutIndex], stone});
findAssignment(solutions, board, stones, layoutIndex + 1, solution);
solution.pop_back();
Expand All @@ -427,7 +481,7 @@ void Solver::findAssignment(Solutions &solutions, Board &board, Stones &stones,
Position reversed = positions[layoutIndex];
reversed.reverse = !reversed.reverse;
board.assign(reversed, stone);
if (board.isValid()) {
if (board.isValid(reversed)) {
solution.push_back({reversed, stone});
findAssignment(solutions, board, stones, layoutIndex + 1, solution);
solution.pop_back();
Expand Down
15 changes: 7 additions & 8 deletions puzzle.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <vector>
#include <array>
#include <iostream>
#include <stdexcept>

// A stone that can be set in the game board
struct Stone {
Expand Down Expand Up @@ -64,22 +65,20 @@ using Solutions = std::list<Solution>;
class Counter
{
public:
Counter() = default;
Counter() : seen_{} {}

void add(char value)
{
if (value < min_) {
std::cerr << "Character " << value << " is too small, please decrease Counter::min_ field from now " << min_ << " to (at least) " << value << std::endl;
exit(3);
throw std::out_of_range(std::string("Character ") + value + " is too small");
}
size_t const val = size_t(value - min_);
if (val >= seen_.size()) {
std::cerr << "Character " << value << " is too large, please increase Counter::seen_ field from now " << seen_.size() << " to (at least) " << val + 1 << std::endl;
exit(5);
throw std::out_of_range(std::string("Character ") + value + " is too large");
}

if (valid_) {
if (seen_[val] == true) {
if (seen_[val]) {
valid_ = false;
} else {
seen_[val] = true;
Expand All @@ -94,7 +93,7 @@ class Counter
private:
bool valid_ = true;
static constexpr char const min_ = '0';
std::vector<bool> seen_ = std::vector<bool>(80, false);
std::array<bool, 80> seen_;
};

// Game board with a matrix-like data structure
Expand Down Expand Up @@ -142,7 +141,6 @@ class Board
} else {
for (size_t i = 0, n = stone.fields.size(); i < n; ++i) {
assign(row, col, stone.fields[i]);
data_[row][col] = stone.fields[i];
row += position.horizontal ? 0 : 1;
col += position.horizontal ? 1 : 0;
}
Expand Down Expand Up @@ -178,6 +176,7 @@ class Board

return true;
}
bool isValid(const Position &position) const;
bool isFull() const
{
return size_ * size_ == fill_;
Expand Down
21 changes: 21 additions & 0 deletions unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,31 @@ class Variants
}
};

class ExceptionTest
{
public:
ExceptionTest()
{
Board board(3);
try {
board.assign(0, 0, '!');
board.isValid();
std::cout << "unit test failed at " << __FILE__ << ":" << __LINE__ << ". Exception not thrown for char '!'" << std::endl;
exit(127);
} catch (const std::out_of_range& e) {
// Expected
} catch (...) {
std::cout << "unit test failed at " << __FILE__ << ":" << __LINE__ << ". Wrong exception type thrown" << std::endl;
exit(127);
}
}
};

int main()
{
SmallGame small_game;
MediumGame medium_game;
LargeGame large_game;
Variants variants;
ExceptionTest exception_test;
}