diff --git a/puzzle.cpp b/puzzle.cpp index c6fbc64..1d47dac 100644 --- a/puzzle.cpp +++ b/puzzle.cpp @@ -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()); @@ -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; @@ -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(); @@ -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(); diff --git a/puzzle.h b/puzzle.h index d520e5d..5c91a16 100644 --- a/puzzle.h +++ b/puzzle.h @@ -35,6 +35,7 @@ #include #include #include +#include // A stone that can be set in the game board struct Stone { @@ -64,22 +65,20 @@ using Solutions = std::list; 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; @@ -94,7 +93,7 @@ class Counter private: bool valid_ = true; static constexpr char const min_ = '0'; - std::vector seen_ = std::vector(80, false); + std::array seen_; }; // Game board with a matrix-like data structure @@ -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; } @@ -178,6 +176,7 @@ class Board return true; } + bool isValid(const Position &position) const; bool isFull() const { return size_ * size_ == fill_; diff --git a/unit_tests.cpp b/unit_tests.cpp index f4bd4ed..32e69bf 100644 --- a/unit_tests.cpp +++ b/unit_tests.cpp @@ -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; }