├── .gitignore ├── sudoku ├── sudoku.h ├── statistics.cpp ├── statistics.h ├── stringify.h ├── parser.h ├── output.h ├── position.cpp ├── position.h ├── game.h ├── output.cpp ├── parser.cpp ├── board.h ├── game.cpp ├── cell.h ├── region.h ├── cell.cpp ├── region.cpp ├── operations.h ├── stringify.cpp └── board.cpp ├── CMakeLists.txt ├── tests ├── parser_test.cpp ├── region_test.cpp ├── cells_test.cpp ├── position.cpp ├── hello.cpp ├── cell.cpp ├── utils_test.cpp └── acutest.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | cmake-build-debug/ 3 | -------------------------------------------------------------------------------- /sudoku/sudoku.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/25/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "cell.h" 8 | #include "region.h" 9 | #include "board.h" 10 | -------------------------------------------------------------------------------- /sudoku/statistics.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/26/18. 3 | // 4 | 5 | #include "statistics.h" 6 | 7 | namespace sudoku { 8 | 9 | Statistics::Statistics(const Board &board) 10 | { 11 | emptyCount = board.availableCells().size(); 12 | 13 | valid = board.isValid(); 14 | filled = emptyCount == 0; 15 | solved = filled && valid; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /sudoku/statistics.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/26/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "board.h" 8 | 9 | namespace sudoku { 10 | 11 | struct Statistics { 12 | size_t emptyCount {}; 13 | bool filled {false}; 14 | bool valid {true}; 15 | bool solved {true}; 16 | 17 | explicit Statistics(const Board &board); 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /sudoku/stringify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.h" 4 | #include "cell.h" 5 | #include "region.h" 6 | #include "board.h" 7 | #include 8 | 9 | namespace sudoku { 10 | using std::string; 11 | using std::vector; 12 | 13 | string stringify(const Position& position); 14 | string stringify(const vector& positions); 15 | string stringify(const Cell& cell); 16 | string stringify(const Cells& cells); 17 | string stringify(const Row& row); 18 | string stringify(const Column& column); 19 | string stringify(const Box& box); 20 | string stringify(const Board& board); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /sudoku/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/11/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using std::array; 12 | using std::string; 13 | using std::experimental::optional; 14 | using std::experimental::nullopt; 15 | 16 | namespace sudoku { 17 | 18 | class Parser { 19 | public: 20 | explicit Parser(const string&); 21 | optional> result() const; 22 | 23 | private: 24 | array numbers; 25 | bool success { }; 26 | 27 | bool parse(const string&); 28 | }; 29 | 30 | 31 | optional> parse(const string&); 32 | } 33 | -------------------------------------------------------------------------------- /sudoku/output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.h" 4 | #include "cell.h" 5 | #include "region.h" 6 | #include "board.h" 7 | #include 8 | 9 | using namespace sudoku; 10 | using std::vector; 11 | using std::ostream; 12 | 13 | ostream& operator<<(ostream& os, const Position& position); 14 | ostream& operator<<(ostream& os, const vector& positions); 15 | ostream& operator<<(ostream& os, const Cell& cell); 16 | ostream& operator<<(ostream& os, const Cells& cells); 17 | ostream& operator<<(ostream& os, const Row& cell); 18 | ostream& operator<<(ostream& os, const Column& cell); 19 | ostream& operator<<(ostream& os, const Box& cell); 20 | ostream& operator<<(ostream& os, const Board& cell); 21 | -------------------------------------------------------------------------------- /sudoku/position.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/18/18. 3 | // 4 | 5 | #include "position.h" 6 | 7 | namespace sudoku { 8 | 9 | bool Position::operator==(const Position& other) const 10 | { 11 | return x == other.x && y == other.y; 12 | } 13 | 14 | bool Position::operator!=(const Position& other) const 15 | { 16 | return !(*this == other); 17 | } 18 | 19 | bool Position::operator<(const Position& other) const 20 | { 21 | return y < other.y || (y == other.y && x < other.x); 22 | } 23 | 24 | bool Position::operator<=(const Position& other) const 25 | { 26 | return *this < other || *this == other; 27 | } 28 | 29 | bool Position::operator>(const Position& other) const 30 | { 31 | return !(*this <= other); 32 | } 33 | 34 | bool Position::operator>=(const Position& other) const 35 | { 36 | return !(*this < other); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /sudoku/position.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/18/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace sudoku { 10 | 11 | struct Position { 12 | using is_transparent = void; 13 | 14 | int x; 15 | int y; 16 | 17 | Position() = delete; 18 | 19 | Position(int x, int y) : x(x), y(y) { 20 | assert(x >= 1 && x <= 9 && y >= 1 && y <= 9); 21 | } 22 | 23 | Position(const Position &) = default; 24 | 25 | Position &operator=(const Position &) = default; 26 | 27 | bool operator==(const Position &other) const; 28 | 29 | bool operator!=(const Position &other) const; 30 | 31 | bool operator<(const Position &other) const; 32 | 33 | bool operator<=(const Position &other) const; 34 | 35 | bool operator>(const Position &other) const; 36 | 37 | bool operator>=(const Position &other) const; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /sudoku/game.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/26/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "board.h" 8 | #include "statistics.h" 9 | #include 10 | #include 11 | 12 | namespace sudoku { 13 | 14 | using std::vector; 15 | using std::experimental::optional; 16 | using std::experimental::nullopt; 17 | 18 | class Game { 19 | public: 20 | explicit Game(const Board& board); 21 | 22 | Statistics statistics; 23 | vector steps; 24 | 25 | Board currentBoard() const; 26 | 27 | auto put(Cell step) -> vector::iterator; 28 | void rollback(vector::iterator step); 29 | 30 | protected: 31 | Board initialBoard; 32 | }; 33 | 34 | using StepFinder = std::function(const Board&, const Statistics&)>; 35 | 36 | optionalfind(const Game& game, const vector& stepFinders); 37 | Game solve(Board board, vector&& stepFinders); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(Sudoku) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | file(GLOB SUDOKU_LIB ${CMAKE_CURRENT_SOURCE_DIR} sudoku/*.cpp sudoku/*.h) 8 | 9 | add_executable(Sudoku 10 | main.cpp 11 | ${SUDOKU_LIB}) 12 | 13 | #Setup CMake to run tests 14 | enable_testing() 15 | 16 | #I like to keep test files in a separate source directory called test 17 | file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} tests/*.cpp) 18 | 19 | #Run through each source 20 | foreach(testSrc ${TEST_SRCS}) 21 | # Extract the filename without an extension (NAME_WE) 22 | get_filename_component(testName ${testSrc} NAME_WE) 23 | 24 | #Add compile target 25 | add_executable(${testName} ${testSrc} ${SUDOKU_LIB}) 26 | 27 | #Finally add it to test execution - 28 | #Notice the WORKING_DIRECTORY and COMMAND 29 | add_test(NAME ${testName} 30 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testBin 31 | COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/testBin/${testName} ) 32 | endforeach(testSrc) 33 | -------------------------------------------------------------------------------- /tests/parser_test.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/parser.h" 2 | #include "acutest.h" 3 | 4 | using namespace std; 5 | using namespace sudoku; 6 | 7 | void test_basic() 8 | { 9 | const char* sample = R"( 10 | 2 1 4 | 6 9 3 | 7 5 8 11 | 9 5 7 | 2 1 8 | 3 6 4 12 | 6 3 8 | 5 7 4 | 2 9 1 13 | ------+-------+------ 14 | 4 6 3 | 9 8 5 | 1 2 7 15 | 1 7 9 | 4 6 2 | 8 3 5 16 | 8 2 5 | 7 3 1 | 6 4 9 17 | ------+-------+------ 18 | 5 8 6 | 1 2 9 | 4 7 3 19 | 7 4 1 | 3 5 6 | 9 8 2 20 | 3 9 2 | 8 4 7 | 5 1 6)"; 21 | 22 | auto result = parse(sample); 23 | TEST_CHECK(!!result); 24 | 25 | auto numbers = *result; 26 | 27 | TEST_CHECK(numbers[0] == 2); 28 | TEST_CHECK(numbers[10] == 5); 29 | TEST_CHECK(numbers[20] == 8); 30 | TEST_CHECK(numbers[30] == 9); 31 | TEST_CHECK(numbers[40] == 6); 32 | TEST_CHECK(numbers[50] == 1); 33 | TEST_CHECK(numbers[60] == 4); 34 | TEST_CHECK(numbers[70] == 8); 35 | TEST_CHECK(numbers[80] == 6); 36 | } 37 | 38 | TEST_LIST = { 39 | { "basic", test_basic }, 40 | { 0 } 41 | }; 42 | -------------------------------------------------------------------------------- /sudoku/output.cpp: -------------------------------------------------------------------------------- 1 | #include "output.h" 2 | #include "stringify.h" 3 | #include 4 | 5 | ostream& operator<<(ostream& os, const Position& position) 6 | { 7 | os << stringify(position); 8 | return os; 9 | } 10 | 11 | ostream& operator<<(ostream& os, const vector& positions) 12 | { 13 | os << stringify(positions); 14 | return os; 15 | } 16 | 17 | ostream& operator<<(ostream& os, const Cell& cell) 18 | { 19 | os << stringify(cell); 20 | return os; 21 | } 22 | 23 | ostream& operator<<(ostream& os, const Cells& cells) 24 | { 25 | os << stringify(cells); 26 | return os; 27 | } 28 | 29 | ostream& operator<<(ostream& os, const Row& row) 30 | { 31 | os << stringify(row); 32 | return os; 33 | } 34 | 35 | ostream& operator<<(ostream& os, const Column& column) 36 | { 37 | os << stringify(column); 38 | return os; 39 | } 40 | 41 | ostream& operator<<(ostream& os, const Box& box) 42 | { 43 | os << stringify(box); 44 | return os; 45 | } 46 | 47 | ostream& operator<<(ostream& os, const Board& board) 48 | { 49 | os << stringify(board); 50 | return os; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tests/region_test.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/region.h" 2 | #include "../sudoku/operations.h" 3 | #include "acutest.h" 4 | 5 | using namespace std; 6 | using namespace sudoku; 7 | 8 | void test_basic() 9 | { 10 | Row row1 {1, {{1, 1, 1}, {2, 1, 5}, {3, 1}}}; 11 | 12 | TEST_CHECK(row1.y == 1); 13 | TEST_CHECK(row1.contain({3,1})); 14 | 15 | Row row2 = row1; 16 | TEST_CHECK(row2.y == 1); 17 | TEST_CHECK(row2.contain({3,1})); 18 | 19 | Column col1 {2, {{2, 1, 5}, {2, 2}, {2, 3, 9}}}; 20 | 21 | auto result = setOp::difference(row1.cells, col1.cells); 22 | TEST_CHECK(result.size() == 2); 23 | TEST_CHECK(setOp::contain(result, Cell {1, 1, 1})); 24 | TEST_CHECK(setOp::contain(result, Cell {3, 1})); 25 | 26 | auto e = col1.availableCells(); 27 | auto n = col1.filledCells(); 28 | TEST_CHECK(e.size() == 1); 29 | TEST_CHECK(n.size() == 2); 30 | TEST_CHECK(setOp::merged(e, n) == col1.cells); 31 | } 32 | 33 | void test_isValid() 34 | { 35 | Column col1 { 2, { {2, 1, 5}, {2, 2}, { 2, 3, 9}}}; 36 | 37 | TEST_CHECK(col1.isValid()); 38 | } 39 | 40 | TEST_LIST = { 41 | { "basic", test_basic }, 42 | { "isValid", test_isValid}, 43 | { 0 } 44 | }; 45 | -------------------------------------------------------------------------------- /sudoku/parser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/11/18. 3 | // 4 | 5 | #include "parser.h" 6 | 7 | namespace sudoku { 8 | 9 | optional> parse(const string& str) 10 | { 11 | Parser parser { str }; 12 | return parser.result(); 13 | } 14 | 15 | bool Parser::parse(const string& str) 16 | { 17 | size_t count = 0; 18 | 19 | std::copy_if(str.begin(), str.end(), numbers.begin(), [&count, size = numbers.size()](char c) { 20 | if ((c < '0' || c > '9') && c != '.') 21 | return false; 22 | 23 | if (count >= size) 24 | return false; 25 | 26 | count++; 27 | return true; 28 | }); 29 | 30 | std::transform(numbers.begin(), numbers.end(), numbers.begin(), [](int n) -> int{ 31 | n -= '0'; 32 | return (n < 0 || n > 9) ? 0 : n; 33 | }); 34 | 35 | return true; 36 | 37 | } 38 | 39 | Parser::Parser(const string& str) 40 | { 41 | success = parse(str); 42 | } 43 | 44 | optional> Parser::result() const 45 | { 46 | if (success) 47 | return numbers; 48 | else 49 | return nullopt; 50 | } 51 | } -------------------------------------------------------------------------------- /tests/cells_test.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/cell.h" 2 | #include "../sudoku/operations.h" 3 | #include "acutest.h" 4 | 5 | using namespace std; 6 | using namespace sudoku; 7 | 8 | void test_basic() { 9 | Cell cell1{1, 1}, cell2{2, 1}, cell3{3, 1}; 10 | Cells cells { cell1, cell2, cell3 }; 11 | 12 | TEST_CHECK(cells.size() == 3); 13 | 14 | TEST_CHECK(setOp::contain(cells, cell2)); 15 | TEST_CHECK(!setOp::contain(cells, Cell{3, 3})); 16 | 17 | // Test copy constructor 18 | Cells result2{cells}; 19 | TEST_CHECK(cells == result2); 20 | } 21 | 22 | void test_operators() 23 | { 24 | Cells cells { {1, 1, 5}, {2, 1}, {3, 1}}; 25 | 26 | // Test add 27 | Cells result = setOp::merged(cells, cells); 28 | TEST_CHECK(cells == result); 29 | 30 | result = setOp::merged(cells, {Cell { 3, 3}}); 31 | TEST_CHECK(result.size() == 4); 32 | TEST_CHECK(setOp::contain(result, Cell { 3, 3})); 33 | 34 | // Test subtract 35 | result = setOp::difference(cells, {Cell {3, 3}}); 36 | TEST_CHECK(result.size() == 3); 37 | TEST_CHECK(cells == result); 38 | 39 | result = setOp::difference(result, cells); 40 | TEST_CHECK(result.empty()); 41 | } 42 | 43 | TEST_LIST = { 44 | { "Cells basic", test_basic}, 45 | { "Cells operation", test_operators }, 46 | { 0 } 47 | }; 48 | -------------------------------------------------------------------------------- /sudoku/board.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/22/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "cell.h" 8 | #include "region.h" 9 | #include 10 | #include 11 | 12 | namespace sudoku { 13 | 14 | using std::vector; 15 | 16 | class Board { 17 | public: 18 | Board(); 19 | 20 | Board(const Board& other) = default; 21 | Board& operator=(const Board& other) = default; 22 | 23 | explicit Board(const std::array& numbers); 24 | explicit Board(Cells&& cells); 25 | 26 | const Cell& cell(int x, int y) const; 27 | 28 | const BoundRow& row(int y) const; 29 | const BoundColumn& column(int x) const; 30 | const BoundBox& box(int x, int y) const; 31 | 32 | bool isValid() const; 33 | Cells availableCells() const; 34 | Cells filledCells() const; 35 | 36 | Board put(Cell step) const; 37 | 38 | Cells cells; 39 | vector rows; 40 | vector columns; 41 | vector boxes; 42 | 43 | private: 44 | static int cellIndex(int x, int y); 45 | static int boxIndex(int x, int y); 46 | 47 | void build(); 48 | const Cell& cellAt(const Position& position) const; 49 | Cells collectCells(const std::vector& positions) const; 50 | }; 51 | 52 | }; 53 | 54 | -------------------------------------------------------------------------------- /sudoku/game.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/26/18. 3 | // 4 | 5 | #include "game.h" 6 | 7 | namespace sudoku { 8 | 9 | Game::Game(const Board& board) 10 | : initialBoard(board) 11 | , statistics(Statistics { board }) 12 | { 13 | } 14 | 15 | Board Game::currentBoard() const 16 | { 17 | Board board { initialBoard }; 18 | for (const auto& step : steps) 19 | board = board.put(step); 20 | return board; 21 | } 22 | 23 | auto Game::put(Cell step) -> vector::iterator 24 | { 25 | auto board = currentBoard().put(step); 26 | statistics = Statistics { board }; 27 | steps.emplace_back(step); 28 | return steps.end() - 1; 29 | }; 30 | 31 | void Game::rollback(vector::iterator step) 32 | { 33 | steps.erase(step, steps.end()); 34 | statistics = Statistics { currentBoard() }; 35 | } 36 | 37 | optionalfind(const Game& game, const vector& stepFinders) { 38 | const auto board = game.currentBoard(); 39 | const auto& statistics = game.statistics; 40 | 41 | for (auto& finder : stepFinders) { 42 | if (auto step = finder(board, statistics)) 43 | return step; 44 | } 45 | return nullopt; 46 | } 47 | 48 | Game solve(Board board, vector&& stepFinders) { 49 | Game game {board}; 50 | 51 | while (game.statistics.valid && !game.statistics.solved) { 52 | if (auto step = find(game, stepFinders)) 53 | game.put(*step); 54 | else 55 | break; 56 | } 57 | 58 | return game; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /tests/position.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/position.h" 2 | #include "acutest.h" 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace sudoku; 8 | 9 | void test_basic() 10 | { 11 | Position pos1 { 3, 1 }; 12 | Position pos2 { pos1 }; 13 | Position pos3 { 4, 2 }; 14 | Position pos4 { 1, 3 }; 15 | 16 | array ts = {pos1, pos2, pos3, pos4}; 17 | 18 | TEST_CHECK(ts[0].x == 3); 19 | TEST_CHECK(ts[0].y == 1); 20 | 21 | // Copy constructor 22 | 23 | TEST_CHECK(ts[1].x == 3); 24 | TEST_CHECK(ts[1].y == 1); 25 | 26 | // Equal operator 27 | 28 | TEST_CHECK(ts[0] == ts[1]); 29 | TEST_CHECK(ts[0] != ts[2]); 30 | TEST_CHECK(!(ts[0] != ts[1])); 31 | TEST_CHECK(!(ts[0] == ts[2])); 32 | 33 | // Compare operator 34 | 35 | TEST_CHECK(ts[0] == ts[0]); 36 | TEST_CHECK(ts[0] >= ts[1]); 37 | TEST_CHECK(ts[0] <= ts[1]); 38 | TEST_CHECK(ts[0] < ts[2]); 39 | TEST_CHECK(ts[0] < ts[3]); 40 | 41 | TEST_CHECK(ts[1] == ts[0]); 42 | TEST_CHECK(ts[1] >= ts[1]); 43 | TEST_CHECK(ts[1] < ts[2]); 44 | TEST_CHECK(ts[1] < ts[3]); 45 | 46 | TEST_CHECK(ts[2] > ts[0]); 47 | TEST_CHECK(ts[2] >= ts[1]); 48 | TEST_CHECK(ts[2] == ts[2]); 49 | TEST_CHECK(ts[2] < ts[3]); 50 | 51 | TEST_CHECK(ts[3] > ts[0]); 52 | TEST_CHECK(ts[3] >= ts[1]); 53 | TEST_CHECK(ts[3] > ts[2]); 54 | TEST_CHECK(ts[3] == ts[3]); 55 | } 56 | 57 | void test_set() 58 | { 59 | Position pos1 { 1, 2 }, pos2 { 2, 2 }, pos3 { 3, 1 }; 60 | array ts {pos1, pos2, pos3}; 61 | 62 | set result; 63 | 64 | result.insert(ts[0]); 65 | TEST_CHECK(result.size() == 1); 66 | 67 | result.insert(ts[0]); 68 | TEST_CHECK(result.size() == 1); 69 | 70 | result.insert(ts[1]); 71 | result.insert(ts[2]); 72 | TEST_CHECK(result.size() == 3); 73 | 74 | auto found = result.find(ts[2]); 75 | TEST_CHECK(found != result.end()); 76 | } 77 | 78 | TEST_LIST = { 79 | { "basic", test_basic }, 80 | { "set of position", test_set }, 81 | { 0 } 82 | }; 83 | -------------------------------------------------------------------------------- /tests/hello.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/parser.h" 2 | #include "../sudoku/board.h" 3 | #include "../sudoku/cell.h" 4 | #include "../sudoku/operations.h" 5 | #include "acutest.h" 6 | 7 | using namespace std; 8 | using namespace sudoku; 9 | 10 | void test_basic() { 11 | Cells cells{{1, 1, 5}, 12 | {2, 1}, 13 | {3, 1}}; 14 | 15 | Board board; 16 | 17 | TEST_CHECK(board.cell(1, 1).x() == 1); 18 | TEST_CHECK(board.cell(5, 6).y() == 6); 19 | TEST_CHECK(board.row(7).y == 7); 20 | TEST_CHECK(board.row(7).contain(Cell(1, 7))); 21 | TEST_CHECK(board.column(3).x == 3); 22 | TEST_CHECK(board.column(3).contain(Cell{3, 5})); 23 | TEST_CHECK(board.box(3, 3).y == 3); 24 | TEST_CHECK(board.box(3, 3).contain(Cell{9, 7})); 25 | 26 | TEST_CHECK(board.rows.size() == 9); 27 | TEST_CHECK(board.columns.size() == 9); 28 | TEST_CHECK(board.boxes.size() == 9); 29 | } 30 | 31 | const char* sampleBoard = R"( 32 | 2 1 4 | 6 9 3 | 7 5 8 33 | 9 5 7 | 2 1 8 | 3 6 4 34 | 6 3 8 | 5 7 4 | 2 9 1 35 | ------+-------+------ 36 | 4 6 3 | 9 8 5 | 1 2 7 37 | 1 7 9 | 4 6 2 | 8 3 5 38 | 8 2 5 | 7 3 1 | 6 4 9 39 | ------+-------+------ 40 | 5 8 6 | 1 2 9 | 4 7 3 41 | 7 4 1 | 3 5 6 | 9 8 2 42 | 3 9 2 | 8 4 7 | 5 1 6)"; 43 | 44 | void test_basic_with_data() 45 | { 46 | auto result = parse(sampleBoard); 47 | TEST_CHECK(!!result); 48 | 49 | Board board { *result }; 50 | 51 | TEST_CHECK(board.cell(1, 1) == 2); 52 | TEST_CHECK(board.cell(2, 2) == 5); 53 | TEST_CHECK(board.cell(3, 3) == 8); 54 | TEST_CHECK(board.cell(4, 4) == 9); 55 | TEST_CHECK(board.cell(5, 5) == 6); 56 | TEST_CHECK(board.cell(6, 6) == 1); 57 | TEST_CHECK(board.cell(7, 7) == 4); 58 | TEST_CHECK(board.cell(8, 8) == 8); 59 | TEST_CHECK(board.cell(9, 9) == 6); 60 | 61 | 62 | TEST_CHECK(board.row(7).cell(5) == 2); 63 | TEST_CHECK(board.column(3).cell(5) == 9); 64 | TEST_CHECK(board.box(3, 3).cell(5) == 8); 65 | } 66 | 67 | TEST_LIST = { 68 | { "basic", test_basic }, 69 | { "basic 2", test_basic_with_data }, 70 | { 0 } 71 | }; 72 | -------------------------------------------------------------------------------- /sudoku/cell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/21/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "position.h" 8 | #include 9 | #include 10 | 11 | namespace sudoku { 12 | 13 | using std::function; 14 | using std::vector; 15 | 16 | class Cell { 17 | Position pos; 18 | 19 | public: 20 | mutable int number; 21 | int x() const { return pos.x; } 22 | int y() const { return pos.y; } 23 | 24 | Cell() = delete; 25 | Cell(int x, int y, int number = 0) : pos {x, y}, number {number} { 26 | assert(number >= 0 && number <= 9); 27 | } 28 | explicit Cell(const Position& pos, int number = 0) : pos {pos}, number {number} { 29 | assert(number >= 0 && number <= 9); 30 | } 31 | 32 | Cell(const Cell&) = default; 33 | Cell& operator=(const Cell&) = default; 34 | 35 | Cell& operator=(int number); 36 | 37 | bool operator==(const Cell& other) const; 38 | bool operator!=(const Cell& other) const; 39 | 40 | bool operator<(const Cell& other) const; 41 | bool operator<=(const Cell& other) const; 42 | 43 | bool operator>(const Cell& other) const; 44 | bool operator>=(const Cell& other) const; 45 | 46 | bool operator==(const Position& other) const; 47 | bool operator!=(const Position& other) const; 48 | 49 | bool operator<(const Position& other) const; 50 | bool operator<=(const Position& other) const; 51 | 52 | bool operator>(const Position& other) const; 53 | bool operator>=(const Position& other) const; 54 | 55 | bool operator==(int number) const { return this->number == number; } 56 | bool operator!=(int number) const { return !(*this == number); } 57 | explicit operator bool() const { return number > 0; } 58 | 59 | const Position& position() const; 60 | 61 | Cell with(int number) const; 62 | 63 | // Predicates 64 | 65 | static bool isEmpty(const Cell& cell); 66 | static bool hasNumber(const Cell& cell); 67 | }; 68 | 69 | using Cells = vector; 70 | using CellPredicate = function; 71 | 72 | vector numbersInCells(const Cells& cells); 73 | 74 | }; 75 | 76 | 77 | -------------------------------------------------------------------------------- /sudoku/region.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/21/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "cell.h" 8 | #include "operations.h" 9 | 10 | namespace sudoku { 11 | 12 | using std::vector; 13 | 14 | class Board; 15 | 16 | class Region { 17 | public: 18 | Cells cells; 19 | 20 | const Cell& cell(int n) const; 21 | 22 | bool isValid() const; 23 | bool contain(const Cell& cell) const; 24 | bool contain(int number) const; 25 | Cells overlap(const Cells &cells) const; 26 | 27 | vector numbers() const; 28 | vector availableNumbers() const; 29 | 30 | Cells availableCells() const; 31 | Cells filledCells() const; 32 | 33 | protected: 34 | explicit Region(Cells&& cells) : cells(move(cells)) {} 35 | }; 36 | 37 | class WithBoard { 38 | public: 39 | explicit WithBoard(const Board* board) : board { board } {} 40 | 41 | WithBoard(const WithBoard&) = default; 42 | WithBoard& operator=(const WithBoard& other) = default; 43 | 44 | protected: 45 | const Board* board; 46 | }; 47 | 48 | struct Row : public Region { 49 | const char* kind() const { return "Row"; }; 50 | 51 | int y; 52 | 53 | Row(int y, Cells&& cells) : Region(move(cells)), y(y) {} 54 | }; 55 | 56 | struct Column : public Region { 57 | const char* kind() const { return "Column"; }; 58 | 59 | int x; 60 | 61 | Column (int x, Cells&& cells) : Region(move(cells)), x(x) {} 62 | }; 63 | 64 | struct Box : public Region { 65 | const char* kind() const { return "Box"; }; 66 | 67 | int x; 68 | int y; 69 | 70 | Box(int x, int y, Cells&& cells) : Region(move(cells)), x(x), y(y) {} 71 | }; 72 | 73 | struct BoundRow : public Row, public WithBoard { 74 | BoundRow(const Board* board, int y, Cells&&cells); 75 | }; 76 | 77 | struct BoundColumn : public Column, public WithBoard { 78 | BoundColumn(const Board* board, int x, Cells&&cells); 79 | }; 80 | 81 | struct BoundBox : public Box, public WithBoard { 82 | BoundBox(const Board* board, int x, int y, Cells&&cells); 83 | 84 | vector rows() const; 85 | vector columns() const; 86 | }; 87 | 88 | vector allNumbers(); 89 | } 90 | -------------------------------------------------------------------------------- /sudoku/cell.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/21/18. 3 | // 4 | 5 | #include "cell.h" 6 | #include "operations.h" 7 | 8 | namespace sudoku { 9 | 10 | using namespace std; 11 | 12 | Cell& Cell::operator=(int number) 13 | { 14 | this->number = number; 15 | return *this; 16 | } 17 | 18 | bool Cell::operator==(const Cell& other) const 19 | { 20 | return pos == other.pos; 21 | } 22 | 23 | bool Cell::operator!=(const Cell& other) const 24 | { 25 | return !(*this == other); 26 | } 27 | 28 | bool Cell::operator<(const Cell& other) const 29 | { 30 | return pos < other.pos; 31 | } 32 | 33 | bool Cell::operator<=(const Cell& other) const 34 | { 35 | return *this < other || *this == other; 36 | } 37 | 38 | bool Cell::operator>(const Cell& other) const 39 | { 40 | return !(*this <= other); 41 | } 42 | 43 | bool Cell::operator>=(const Cell& other) const 44 | { 45 | return !(*this < other); 46 | } 47 | 48 | const Position& Cell::position() const 49 | { 50 | return pos; 51 | } 52 | 53 | bool Cell::operator==(const Position& other) const 54 | { 55 | return pos == other; 56 | } 57 | 58 | bool Cell::operator!=(const Position& other) const 59 | { 60 | return !(*this == other); 61 | } 62 | 63 | bool Cell::operator<(const Position& other) const 64 | { 65 | return pos < other; 66 | } 67 | 68 | bool Cell::operator<=(const Position& other) const 69 | { 70 | return pos < other || pos == other; 71 | } 72 | 73 | bool Cell::operator>(const Position& other) const 74 | { 75 | return !(pos <= other); 76 | } 77 | 78 | bool Cell::operator>=(const Position& other) const 79 | { 80 | return !(pos < other); 81 | } 82 | 83 | Cell Cell::with(int number) const 84 | { 85 | Cell cell { *this }; 86 | cell.number = number; 87 | return cell; 88 | } 89 | 90 | // Cell Predicates 91 | 92 | bool Cell::isEmpty(const Cell& cell) 93 | { 94 | return !cell; 95 | } 96 | 97 | bool Cell::hasNumber(const Cell& cell) 98 | { 99 | return !!cell; 100 | } 101 | 102 | vector numbersInCells(const Cells& cells) 103 | { 104 | return setOp::map(cells, [](const Cell& cell) { 105 | return cell.number; 106 | }); 107 | } 108 | 109 | }; 110 | -------------------------------------------------------------------------------- /tests/cell.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/cell.h" 2 | #include "../sudoku/operations.h" 3 | #include "acutest.h" 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace sudoku; 9 | // 10 | // Created by Basuke Suzuki on 5/21/18. 11 | // 12 | 13 | void test_basic() 14 | { 15 | Cell cell1 { 3, 1, 5 }; 16 | Cell cell2 { cell1 }; 17 | Cell cell3 { 4, 2 }; 18 | Cell cell4 { 1, 3 }; 19 | array ts = {cell1, cell2, cell3, cell4}; 20 | 21 | TEST_CHECK(ts[0].x() == 3); 22 | TEST_CHECK(ts[0].y() == 1); 23 | 24 | // Copy constructor 25 | 26 | TEST_CHECK(ts[1].x() == 3); 27 | TEST_CHECK(ts[1].y() == 1); 28 | 29 | // Equal operator 30 | 31 | TEST_CHECK(ts[0] == ts[1]); 32 | TEST_CHECK(ts[0] != ts[2]); 33 | TEST_CHECK(!(ts[0] != ts[1])); 34 | TEST_CHECK(!(ts[0] == ts[2])); 35 | 36 | // Compare operator 37 | 38 | TEST_CHECK(ts[0] == ts[0]); 39 | TEST_CHECK(ts[0] >= ts[1]); 40 | TEST_CHECK(ts[0] <= ts[1]); 41 | TEST_CHECK(ts[0] < ts[2]); 42 | TEST_CHECK(ts[0] < ts[3]); 43 | 44 | TEST_CHECK(ts[1] == ts[0]); 45 | TEST_CHECK(ts[1] >= ts[1]); 46 | TEST_CHECK(ts[1] < ts[2]); 47 | TEST_CHECK(ts[1] < ts[3]); 48 | 49 | TEST_CHECK(ts[2] > ts[0]); 50 | TEST_CHECK(ts[2] >= ts[1]); 51 | TEST_CHECK(ts[2] == ts[2]); 52 | TEST_CHECK(ts[2] < ts[3]); 53 | 54 | TEST_CHECK(ts[3] > ts[0]); 55 | TEST_CHECK(ts[3] >= ts[1]); 56 | TEST_CHECK(ts[3] > ts[2]); 57 | TEST_CHECK(ts[3] == ts[3]); 58 | 59 | TEST_CHECK(cell1 == 5); 60 | TEST_CHECK(cell1 != 7); 61 | TEST_CHECK(!!cell1); 62 | TEST_CHECK(!!cell2); 63 | TEST_CHECK(!cell3); 64 | TEST_CHECK(!cell4); 65 | TEST_CHECK(cell4 != 7); 66 | } 67 | 68 | void test_set() 69 | { 70 | Cell cell1 { 1, 2 }, cell2 { 2, 2 }, cell3 { 3, 1 }; 71 | array ts {cell1, cell2, cell3}; 72 | 73 | set result; 74 | 75 | result.insert(ts[0]); 76 | TEST_CHECK(result.size() == 1); 77 | 78 | result.insert(ts[0]); 79 | TEST_CHECK(result.size() == 1); 80 | 81 | result.insert(ts[1]); 82 | result.insert(ts[2]); 83 | TEST_CHECK(result.size() == 3); 84 | 85 | auto found = result.find(ts[2]); 86 | TEST_CHECK(found != result.end()); 87 | 88 | } 89 | 90 | void test_positionConversion() 91 | { 92 | Cell cell1 { 1, 2, 5 }; 93 | Position pos1 { 1, 2 }; 94 | TEST_CHECK(cell1.position() == pos1); 95 | } 96 | 97 | TEST_LIST = { 98 | { "Cell basic", test_basic }, 99 | { "set of cell", test_set }, 100 | { "Position conversion", test_positionConversion }, 101 | { 0 } 102 | }; 103 | -------------------------------------------------------------------------------- /sudoku/region.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/21/18. 3 | // 4 | 5 | #include "region.h" 6 | #include "operations.h" 7 | #include "board.h" 8 | 9 | namespace sudoku { 10 | 11 | const Cell& Region::cell(int n) const 12 | { 13 | return cells[n - 1]; 14 | } 15 | 16 | bool Region::isValid() const { 17 | return filledCells().size() == numbers().size(); 18 | } 19 | 20 | bool Region::contain(const Cell& cell) const 21 | { 22 | return setOp::contain(cells, cell); 23 | } 24 | 25 | bool Region::contain(const int number) const 26 | { 27 | return setOp::contain(numbers(), number); 28 | } 29 | 30 | Cells Region::overlap(const Cells &cells) const 31 | { 32 | return setOp::overlapped(this->cells, cells); 33 | } 34 | 35 | vector Region::numbers() const 36 | { 37 | return numbersInCells(filledCells()); 38 | } 39 | 40 | vector Region::availableNumbers() const 41 | { 42 | return setOp::difference(allNumbers(), numbers()); 43 | } 44 | 45 | Cells Region::availableCells() const 46 | { 47 | return setOp::filter(cells, Cell::isEmpty); 48 | } 49 | 50 | Cells Region::filledCells() const 51 | { 52 | return setOp::filter(cells, Cell::hasNumber); 53 | } 54 | 55 | BoundRow::BoundRow(const Board* board, int y, Cells&&cells) 56 | : Row(y, std::move(cells)) 57 | , WithBoard(board) 58 | { 59 | } 60 | 61 | BoundColumn::BoundColumn(const Board* board, int x, Cells&&cells) 62 | : Column(x, std::move(cells)) 63 | , WithBoard(board) 64 | { 65 | } 66 | 67 | BoundBox::BoundBox(const Board* board, int x, int y, Cells&&cells) 68 | : Box(x, y, std::move(cells)) 69 | , WithBoard(board) 70 | { 71 | } 72 | 73 | vector BoundBox::rows() const 74 | { 75 | vector result; 76 | for (const auto& row : board->rows) { 77 | 78 | if (!row.overlap(cells).empty()) 79 | result.emplace_back(row); 80 | } 81 | // auto ys = setOp::transform(cells, [](const Cell& cell) { 82 | // return cell.y(); 83 | // }); 84 | // 85 | // set result = setOp::transform(ys, [board = this->board](const int& y) { 86 | // return board->row(y); 87 | // }); 88 | 89 | return result; 90 | } 91 | 92 | vector BoundBox::columns() const 93 | { 94 | vector result; 95 | return result; 96 | 97 | // auto xs = setOp::transform(cells, [](const Cell& cell) { 98 | // return cell.x(); 99 | // }); 100 | // 101 | // return setOp::transform(xs, [board = this->board](const int& x) { 102 | // return board->column(x); 103 | // }); 104 | } 105 | 106 | vector allNumbers() 107 | { 108 | return { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 109 | } 110 | } -------------------------------------------------------------------------------- /sudoku/operations.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/22/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | namespace sudoku { 13 | using std::vector; 14 | using std::function; 15 | using std::experimental::optional; 16 | using std::experimental::nullopt; 17 | } 18 | 19 | namespace sudoku::setOp { 20 | 21 | using std::vector; 22 | using std::function; 23 | 24 | template 25 | vector range(T start, int stop, int step = 0) 26 | { 27 | if (step == 0) { 28 | step = start <= stop ? 1 : - 1; 29 | } 30 | 31 | vector result; 32 | if (step > 0) { 33 | for (int i = start; i <= stop; i += step) 34 | result.emplace_back(i); 35 | } else { 36 | for (int i = start; i >= stop; i += step) 37 | result.emplace_back(i); 38 | } 39 | return result; 40 | } 41 | 42 | template 43 | bool contain(const vector& src, const T& item) 44 | { 45 | return std::find(src.begin(), src.end(), item) != src.end(); 46 | } 47 | 48 | template 49 | void forEach(vector& vec, function&& task) 50 | { 51 | for (auto& item : vec) { 52 | task(item); 53 | } 54 | }; 55 | 56 | template 57 | vector filter(const vector& src, function&& predicate) 58 | { 59 | vector result; 60 | for (const auto& item : src) { 61 | if (predicate(item)) 62 | result.emplace_back(item); 63 | } 64 | return result; 65 | }; 66 | 67 | template 68 | vector map(const vector& vec, function&& transformer) 69 | { 70 | vector result; 71 | for (const auto& item : vec) { 72 | result.emplace_back(transformer(item)); 73 | } 74 | return result; 75 | }; 76 | 77 | template 78 | void add(vector& vec, const vector& other) 79 | { 80 | for (const auto& item : other) { 81 | if (!contain(vec, item)) 82 | vec.emplace_back(item); 83 | } 84 | std::sort(vec.begin(), vec.end()); 85 | } 86 | 87 | template 88 | vector merged(const vector& src, const vector& other) 89 | { 90 | vector result { src }; 91 | add(result, other); 92 | return result; 93 | } 94 | 95 | template 96 | void subtract(vector& vec, const vector& other) 97 | { 98 | vector result { }; 99 | for (const auto& item : vec) { 100 | if (!contain(other, item)) 101 | result.emplace_back(item); 102 | } 103 | vec = std::move(result); 104 | } 105 | 106 | template 107 | vector difference(const vector& src, const vector& other) 108 | { 109 | vector result { src }; 110 | subtract(result, other); 111 | return result; 112 | } 113 | 114 | template 115 | void product(vector& vec, const vector& other) 116 | { 117 | vector result { }; 118 | for (const auto& item : vec) { 119 | if (contain(other, item)) 120 | result.emplace_back(item); 121 | } 122 | vec = std::move(result); 123 | } 124 | 125 | template 126 | vector overlapped(const vector &src, const vector &other) 127 | { 128 | vector result { src }; 129 | product(result, other); 130 | return result; 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /tests/utils_test.cpp: -------------------------------------------------------------------------------- 1 | #include "../sudoku/operations.h" 2 | #include "../sudoku/cell.h" 3 | #include "acutest.h" 4 | #include 5 | 6 | using namespace std; 7 | using namespace sudoku; 8 | 9 | void test_range() 10 | { 11 | auto result = setOp::range(10, 20); 12 | TEST_CHECK(result[0] == 10); 13 | TEST_CHECK(result[10] == 20); 14 | TEST_CHECK(result.size() == (20 - 10 + 1)); 15 | 16 | auto result2 = setOp::range(10, 20, 3); 17 | TEST_CHECK(result2[0] == 10); 18 | TEST_CHECK(result2[3] == 19); 19 | TEST_CHECK(result2.size() == 4); 20 | 21 | auto result3 = setOp::range(5, 1); 22 | TEST_CHECK(result3.size() == 5); 23 | TEST_CHECK(result3[0] == 5); 24 | TEST_CHECK(result3[4] == 1); 25 | 26 | } 27 | 28 | void test_contain() 29 | { 30 | auto test = setOp::range(10, 20); 31 | TEST_CHECK(setOp::contain(test, 10)); 32 | TEST_CHECK(setOp::contain(test, 15)); 33 | TEST_CHECK(setOp::contain(test, 20)); 34 | 35 | TEST_CHECK(!setOp::contain(test, 9)); 36 | TEST_CHECK(!setOp::contain(test, 21)); 37 | } 38 | 39 | void test_forEach() 40 | { 41 | vector test = setOp::range(1, 10); 42 | int sum = 0; 43 | setOp::forEach(test, [&sum](int& item) { 44 | sum += item; 45 | }); 46 | TEST_CHECK(sum == 55); 47 | } 48 | 49 | void test_filter() 50 | { 51 | auto test = setOp::range(1, 10); 52 | auto result = setOp::filter(test, [](auto val) { 53 | return val % 3 == 0; 54 | }); 55 | 56 | TEST_CHECK(result.size() == 3); 57 | } 58 | 59 | void test_map() 60 | { 61 | auto test = setOp::range(1, 4); 62 | auto result = setOp::map(test, [](int val) { 63 | return 'a' + val - 1; 64 | }); 65 | vector expected { 'a', 'b', 'c', 'd'}; 66 | 67 | TEST_CHECK(result == expected); 68 | } 69 | 70 | void test_add() 71 | { 72 | auto test = setOp::range(3, 10); 73 | setOp::add(test, setOp::range(1, 4)); 74 | 75 | auto expected = setOp::range(1, 10); 76 | TEST_CHECK(test == expected); 77 | } 78 | 79 | void test_merged() 80 | { 81 | auto test = setOp::range(1, 4); 82 | auto result = setOp::merged(test, setOp::range(10, 3)); 83 | 84 | auto expected = setOp::range(1, 10); 85 | TEST_CHECK(result == expected); 86 | } 87 | 88 | void test_subtract() 89 | { 90 | auto test = setOp::range(1, 4); 91 | setOp::subtract(test, setOp::range(3, 10)); 92 | 93 | auto expected = setOp::range(1, 2); 94 | TEST_CHECK(test == expected); 95 | } 96 | 97 | void test_difference() 98 | { 99 | auto test = setOp::range(1, 5); 100 | auto result = setOp::difference(test, setOp::range(4, 6)); 101 | 102 | auto expected = setOp::range(1, 3); 103 | TEST_CHECK(result == expected); 104 | } 105 | 106 | void test_product() 107 | { 108 | auto test = setOp::range(1, 4); 109 | setOp::product(test, setOp::range(3, 10)); 110 | 111 | auto expected = setOp::range(3, 4); 112 | TEST_CHECK(test == expected); 113 | } 114 | 115 | void test_overlapped() 116 | { 117 | auto test = setOp::range(1, 5); 118 | auto result = setOp::overlapped(test, setOp::range(4, 6)); 119 | 120 | TEST_CHECK(result == setOp::range(4, 5)); 121 | 122 | result = setOp::overlapped(test, setOp::range(8, 10)); 123 | TEST_CHECK(result.empty()); 124 | } 125 | 126 | TEST_LIST = { 127 | { "range", test_range}, 128 | { "contain", test_contain}, 129 | { "forEach", test_forEach}, 130 | { "filter", test_filter}, 131 | { "map", test_map}, 132 | { "add", test_add}, 133 | { "merged", test_merged}, 134 | { "subtract", test_subtract}, 135 | { "difference", test_difference}, 136 | { "product", test_product}, 137 | { "overlapped", test_overlapped}, 138 | { 0 } 139 | }; 140 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sudoku/sudoku.h" 3 | #include "sudoku/parser.h" 4 | #include "sudoku/game.h" 5 | #include "sudoku/output.h" 6 | 7 | using std::vector; 8 | using std::cout; 9 | using std::endl; 10 | 11 | namespace sudoku { 12 | 13 | }; 14 | 15 | const char* easy1 = R"( 16 | . 6 . | 1 . 2 | . 3 . 17 | 7 . 8 | . 6 . | 9 . 2 18 | . 1 . | 9 . 8 | . 7 . 19 | ------+-------+------ 20 | 2 . 6 | . 4 . | 1 . 3 21 | . 9 . | 6 . 7 | . 8 . 22 | 1 . 4 | . 9 . | 7 . 5 23 | ------+-------+------ 24 | . 2 . | 5 . 1 | . 4 . 25 | 9 . 5 | . 3 . | 8 . 1 26 | . 3 . | 4 . 9 | . 5 . 27 | )"; 28 | 29 | const char* hard = R"( 30 | . . . | . 6 . | 2 . . 31 | . . 4 | . . 5 | . 6 . 32 | . 7 . | 4 . . | . . . 33 | ------+-------+------ 34 | . . 8 | . . 3 | . . 9 35 | . 6 . | . 9 . | . 2 . 36 | 4 . . | 7 . . | 5 . . 37 | ------+-------+------ 38 | . . . | . . 7 | . 3 . 39 | . 2 . | 6 . . | 1 . . 40 | . . 6 | . 8 . | . . . 41 | )"; 42 | 43 | const char* sample = R"( 44 | 2 1 4 | 6 9 3 | 7 5 8 45 | 9 5 7 | 2 1 8 | 3 6 4 46 | 6 3 8 | 5 7 4 | 2 9 1 47 | ------+-------+------ 48 | 4 6 3 | 9 8 5 | 1 2 7 49 | 1 7 9 | 4 6 2 | 8 3 5 50 | 8 2 5 | 7 3 1 | 6 4 9 51 | ------+-------+------ 52 | 5 8 6 | 1 2 9 | 4 7 3 53 | 7 4 1 | 3 5 6 | 9 8 2 54 | 3 9 2 | 8 4 7 | 5 1 6 55 | )"; 56 | 57 | struct LastNumberInRegion { 58 | auto operator()(const Board& board, const Statistics& statistics) -> optional { 59 | for (const auto& box : board.boxes) { 60 | if (auto step = lastNumberInRegion(box)) 61 | return step; 62 | } 63 | 64 | for (const auto& row : board.rows) { 65 | if (auto step = lastNumberInRegion(row)) 66 | return step; 67 | } 68 | 69 | for (const auto& column : board.columns) { 70 | if (auto step = lastNumberInRegion(column)) 71 | return step; 72 | } 73 | 74 | return nullopt; 75 | } 76 | 77 | optional lastNumberInRegion(const Region& region) { 78 | auto cells = region.availableCells(); 79 | auto numbers = region.availableNumbers(); 80 | 81 | if (cells.size() == 1 && numbers.size() == 1) 82 | return (*cells.begin()).with(*numbers.begin()); 83 | 84 | return nullopt; 85 | } 86 | 87 | }; 88 | 89 | struct CellInBoxScreenedByRowsAndColumns { 90 | auto operator()(const Board& board, const Statistics& statistics) -> optional { 91 | for (auto number : allNumbers()) { 92 | for (const auto &box : board.boxes) { 93 | if (!box.contain(number)) { 94 | if (auto cell = find(box, number)) 95 | return cell; 96 | } 97 | } 98 | } 99 | return nullopt; 100 | } 101 | 102 | optional find(const BoundBox& box, int number) 103 | { 104 | auto cells = box.availableCells(); 105 | 106 | for (const auto& row : box.rows()) { 107 | if (row.contain(number)) 108 | setOp::subtract(cells, row.cells); 109 | } 110 | 111 | // for (auto row : box.columns()) { 112 | // cells = setOp::subtract(cells, row.cells); 113 | // } 114 | 115 | if (cells.size() != 1) 116 | return nullopt; 117 | 118 | auto step = *(cells.begin()); 119 | step.number = number; 120 | return step; 121 | } 122 | }; 123 | 124 | optional finderFunc(const Board& board, const Statistics& statistics) { 125 | return nullopt; 126 | } 127 | 128 | struct FinderClass { 129 | auto operator()(const Board& board, const Statistics& statistics) -> optional { 130 | return nullopt; 131 | } 132 | }; 133 | 134 | int main() { 135 | auto numbers = *sudoku::parse(easy1); 136 | Board board {numbers}; 137 | 138 | Game game {board}; 139 | 140 | std::cout << board << std::endl; 141 | auto game2 = sudoku::solve(board, { 142 | FinderClass(), 143 | LastNumberInRegion(), 144 | CellInBoxScreenedByRowsAndColumns(), 145 | }); 146 | 147 | std::cout << game2.currentBoard(); 148 | return 0; 149 | } -------------------------------------------------------------------------------- /sudoku/stringify.cpp: -------------------------------------------------------------------------------- 1 | #include "stringify.h" 2 | #include 3 | 4 | namespace sudoku { 5 | 6 | static char numchar(int c) 7 | { 8 | if (!c) 9 | return '.'; 10 | 11 | return '0' + static_cast(c); 12 | } 13 | 14 | template 15 | static string join(const C& items, const string& delimiter) 16 | { 17 | using T = typename C::value_type; 18 | vector itemStrs; 19 | 20 | std::transform(items.begin(), items.end(), std::back_inserter(itemStrs), [](const T& item) { 21 | return stringify(item); 22 | }); 23 | 24 | string result; 25 | for (auto p = items.begin(); p != items.end(); ++p) { 26 | result += (p == items.begin()) ? "[" : ", "; 27 | result += stringify(*p); 28 | } 29 | result += "]"; 30 | return result; 31 | } 32 | 33 | template 34 | static string stringifyRegionBody(const R& region) 35 | { 36 | string result; 37 | 38 | result += "|"; 39 | 40 | int i = 1; 41 | for (const auto& cell : region.cells) { 42 | result += ' '; 43 | result += numchar(cell.number); 44 | 45 | if (i++ % 3 == 0) 46 | result += " |"; 47 | } 48 | 49 | return result; 50 | } 51 | 52 | string stringify(const Position& position) 53 | { 54 | string result; 55 | 56 | result = '('; 57 | result += numchar(position.x); 58 | result += ", "; 59 | result += numchar(position.y); 60 | result += ')'; 61 | 62 | return result; 63 | } 64 | 65 | string stringify(const vector& positions) 66 | { 67 | return join(positions, ", "); 68 | } 69 | 70 | string stringify(const Cell& cell) 71 | { 72 | string result; 73 | 74 | result = '('; 75 | result += numchar(cell.x()); 76 | result += ", "; 77 | result += numchar(cell.y()); 78 | result += ": "; 79 | if (cell.number) { 80 | result += numchar(cell.number); 81 | } else { 82 | result += "."; 83 | } 84 | result += ')'; 85 | 86 | return result; 87 | } 88 | 89 | string stringify(const Cells& cells) 90 | { 91 | return join(cells, ", "); 92 | } 93 | 94 | string stringify(const Row& row) 95 | { 96 | string result { row.kind() }; 97 | 98 | result += " ["; 99 | result += numchar(row.y); 100 | result += "] "; 101 | result += stringifyRegionBody(row); 102 | 103 | return result; 104 | } 105 | 106 | string stringify(const Column& column) 107 | { 108 | string result { column.kind() }; 109 | 110 | result += " ["; 111 | result += numchar(column.x); 112 | result += "] "; 113 | result += stringifyRegionBody(column); 114 | 115 | return result; 116 | } 117 | 118 | string stringify(const Box& box) 119 | { 120 | string result { box.kind() }; 121 | 122 | result += " ["; 123 | result += numchar(box.x); 124 | result += ", "; 125 | result += numchar(box.y); 126 | result += "] "; 127 | result += stringifyRegionBody(box); 128 | 129 | return result; 130 | } 131 | 132 | string stringify(const Board& board) 133 | { 134 | string result; 135 | 136 | const auto firstRow = board.row(1); 137 | 138 | result += " "; 139 | for (const auto& cell : firstRow.cells) { 140 | result += ' '; 141 | result += numchar(cell.x()); 142 | if (cell.x() % 3 == 0) 143 | result += " "; 144 | } 145 | result += "\n"; 146 | 147 | const char* separator = " +-------+-------+-------\n"; 148 | result += separator; 149 | 150 | const auto column = board.column(1); 151 | 152 | for (const auto& row : board.rows) { 153 | result += numchar(row.y); 154 | result += stringifyRegionBody(row); 155 | result += "\n"; 156 | 157 | if (row.y % 3 == 0) 158 | result += separator; 159 | } 160 | 161 | return result; 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /sudoku/board.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Basuke Suzuki on 5/22/18. 3 | // 4 | 5 | #include "board.h" 6 | #include "operations.h" 7 | 8 | namespace sudoku { 9 | 10 | template 11 | static auto buildCells(ITER p) -> Cells 12 | { 13 | Cells cells; 14 | for (int y = 1; y <= 9; y++) { 15 | for (int x = 1; x <= 9; x++) { 16 | cells.emplace_back(Cell {x, y, *(p++)}); 17 | } 18 | } 19 | return cells; 20 | } 21 | 22 | struct ZeroGenerator { 23 | int operator*() { return 0; } 24 | ZeroGenerator& operator++() { return *this; } 25 | ZeroGenerator operator++(int) { return *this; } 26 | }; 27 | 28 | Board::Board() 29 | : cells(buildCells(ZeroGenerator {})) 30 | { 31 | build(); 32 | } 33 | 34 | Board::Board(const std::array& numbers) 35 | : cells(buildCells(numbers.begin())) 36 | { 37 | build(); 38 | } 39 | 40 | Board::Board(Cells&& cells) 41 | : cells { std::move(cells) } 42 | { 43 | build(); 44 | } 45 | 46 | // template 47 | // Board::Board(const Container& numbers) 48 | // : Board() 49 | // { 50 | // auto dest = cells.begin(); 51 | // auto stop = cells.end(); 52 | // 53 | // for (auto& number : numbers) { 54 | // if (dest == stop) 55 | // break; 56 | // 57 | // (*dest++).number = number; 58 | // } 59 | // 60 | // build(); 61 | // } 62 | // 63 | // Board::Board(const std::initializer_list& numbers) 64 | // : Board(numbers) 65 | // { 66 | // } 67 | // 68 | // template 69 | // Board::Board(const std::vector& numbers) { 70 | // auto ptr = numbers.begin(); 71 | // for (int y = 1; y <= 9; y++) { 72 | // for (int x = 1; x <= 9; x++) { 73 | // cells.emplace_back({x, y, *ptr++}); 74 | // } 75 | // } 76 | // build(); 77 | // } 78 | 79 | int Board::cellIndex(int x, int y) 80 | { 81 | assert(x >= 1 && x <= 9); 82 | assert(y >= 1 && y <= 9); 83 | return (y - 1) * 9 + (x - 1); 84 | } 85 | 86 | int Board::boxIndex(int x, int y) 87 | { 88 | assert(x >= 1 && x <= 3); 89 | assert(y >= 1 && y <= 3); 90 | return (y - 1) * 3 + (x - 1); 91 | } 92 | 93 | void Board::build() 94 | { 95 | rows.clear(); 96 | columns.clear(); 97 | boxes.clear(); 98 | 99 | auto r = allNumbers(); 100 | 101 | for (auto y : r) { 102 | std::vector positions; 103 | std::transform(r.begin(), r.end(), back_inserter(positions), [y](int x) { 104 | return Position {x, y}; 105 | }); 106 | rows.emplace_back(BoundRow {this, y, collectCells(positions)}); 107 | } 108 | 109 | for (auto x : r) { 110 | std::vector positions; 111 | std::transform(r.begin(), r.end(), back_inserter(positions), [x](int y) { 112 | return Position {x, y}; 113 | }); 114 | columns.emplace_back(BoundColumn {this, x, collectCells(positions)}); 115 | } 116 | 117 | auto r2 = setOp::range(0, 2); 118 | 119 | for (auto y0 : r2) { 120 | auto startY = y0 * 3; 121 | 122 | for (auto x0 : r2) { 123 | auto startX = x0 * 3; 124 | 125 | vector positions; 126 | 127 | for (auto y : setOp::range(startY + 1, startY + 3)) { 128 | for (auto x : setOp::range(startX + 1, startX + 3)) { 129 | positions.emplace_back(Position {x, y}); 130 | } 131 | } 132 | 133 | boxes.emplace_back(BoundBox {this, x0 + 1, y0 + 1, collectCells(positions)}); 134 | } 135 | } 136 | } 137 | 138 | Cells Board::collectCells(const vector& positions) const 139 | { 140 | Cells result; 141 | 142 | std::transform(positions.begin(), 143 | positions.end(), 144 | back_inserter(result), 145 | [this](const Position& position) { 146 | return cellAt(position); 147 | }); 148 | 149 | return result; 150 | } 151 | 152 | const Cell& Board::cellAt(const Position& position) const 153 | { 154 | auto found = find(cells.begin(), cells.end(), Cell { position }); 155 | return *found; 156 | } 157 | 158 | const Cell& Board::cell(int x, int y) const 159 | { 160 | return cellAt({x, y}); 161 | } 162 | 163 | const BoundRow& Board::row(int y) const 164 | { 165 | assert(y >= 1 && y <= 9); 166 | return rows[y - 1]; 167 | } 168 | 169 | const BoundColumn& Board::column(int x) const 170 | { 171 | assert(x >= 1 && x <= 9); 172 | return columns[x - 1]; 173 | } 174 | 175 | const BoundBox& Board::box(int x, int y) const 176 | { 177 | return boxes[boxIndex(x, y)]; 178 | } 179 | 180 | Board Board::put(Cell step) const 181 | { 182 | Cells cells = this->cells; 183 | auto pos = std::find(cells.begin(), cells.end(), step); 184 | *pos = step; 185 | 186 | return Board {std::move(cells)}; 187 | } 188 | 189 | bool Board::isValid() const 190 | { 191 | for (const auto& row : rows) { 192 | if (!row.isValid()) 193 | return false; 194 | } 195 | 196 | for (const auto& column : columns) { 197 | if (!column.isValid()) 198 | return false; 199 | } 200 | 201 | for (const auto& box : boxes) { 202 | if (!box.isValid()) 203 | return false; 204 | } 205 | 206 | return true; 207 | } 208 | 209 | Cells Board::availableCells() const 210 | { 211 | return setOp::filter(cells, Cell::isEmpty); 212 | } 213 | 214 | Cells Board::filledCells() const 215 | { 216 | return setOp::filter(cells, Cell::hasNumber); 217 | } 218 | } -------------------------------------------------------------------------------- /tests/acutest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Acutest -- Another C/C++ Unit Test facility 3 | * 4 | * 5 | * Copyright (c) 2013-2017 Martin Mitas 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | * IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef ACUTEST_H__ 27 | #define ACUTEST_H__ 28 | 29 | 30 | /************************ 31 | *** Public interface *** 32 | ************************/ 33 | 34 | /* By default, "acutest.h" provides the main program entry point (function 35 | * main()). However, if the test suite is composed of multiple source files 36 | * which include "acutest.h", then this causes a problem of multiple main() 37 | * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all 38 | * compilation units but one. 39 | */ 40 | 41 | /* Macro to specify list of unit tests in the suite. 42 | * The unit test implementation MUST provide list of unit tests it implements 43 | * with this macro: 44 | * 45 | * TEST_LIST = { 46 | * { "test1_name", test1_func_ptr }, 47 | * { "test2_name", test2_func_ptr }, 48 | * ... 49 | * { 0 } 50 | * }; 51 | * 52 | * The list specifies names of each test (must be unique) and pointer to 53 | * a function implementing it. The function does not take any arguments 54 | * and has no return values, i.e. every test function has to be compatible 55 | * with this prototype: 56 | * 57 | * void test_func(void); 58 | */ 59 | #define TEST_LIST const struct test__ test_list__[] 60 | 61 | 62 | /* Macros for testing whether an unit test succeeds or fails. These macros 63 | * can be used arbitrarily in functions implementing the unit tests. 64 | * 65 | * If any condition fails throughout execution of a test, the test fails. 66 | * 67 | * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows 68 | * also to specify an error message to print out if the condition fails. 69 | * (It expects printf-like format string and its parameters). The macros 70 | * return non-zero (condition passes) or 0 (condition fails). 71 | * 72 | * That can be useful when more conditions should be checked only if some 73 | * preceding condition passes, as illustrated in this code snippet: 74 | * 75 | * SomeStruct* ptr = allocate_some_struct(); 76 | * if(TEST_CHECK(ptr != NULL)) { 77 | * TEST_CHECK(ptr->member1 < 100); 78 | * TEST_CHECK(ptr->member2 > 200); 79 | * } 80 | */ 81 | #define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) 82 | #define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) 83 | 84 | 85 | /* printf-like macro for outputting an extra information about a failure. 86 | * 87 | * Note it does not output anything if there was not (yet) failed condition 88 | * in the current test. Intended use is to output some computed output 89 | * versus the expected value, e.g. like this: 90 | * 91 | * if(!TEST_CHECK(produced == expected)) { 92 | * TEST_MSG("Expected: %d", expected); 93 | * TEST_MSG("Produced: %d", produced); 94 | * } 95 | * 96 | * The macro can deal with multi-line output fairly well. It also automatically 97 | * adds a final new-line if there is none present. 98 | */ 99 | #define TEST_MSG(...) test_message__(__VA_ARGS__) 100 | 101 | /* Maximal output per TEST_MSG call. Longer messages are cut. 102 | * You may define another limit prior including "acutest.h" 103 | */ 104 | #ifndef TEST_MSG_MAXSIZE 105 | #define TEST_MSG_MAXSIZE 1024 106 | #endif 107 | 108 | 109 | /********************** 110 | *** Implementation *** 111 | **********************/ 112 | 113 | /* The unit test files should not rely on anything below. */ 114 | 115 | #include 116 | #include 117 | #include 118 | #include 119 | 120 | #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) 121 | #define ACUTEST_UNIX__ 1 122 | #include 123 | #include 124 | #include 125 | #include 126 | #include 127 | #endif 128 | 129 | #if defined(__gnu_linux__) 130 | #define ACUTEST_LINUX__ 1 131 | #include 132 | #include 133 | #endif 134 | 135 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 136 | #define ACUTEST_WIN__ 1 137 | #include 138 | #include 139 | #endif 140 | 141 | #ifdef __cplusplus 142 | #include 143 | #endif 144 | 145 | 146 | /* Note our global private identifiers end with '__' to mitigate risk of clash 147 | * with the unit tests implementation. */ 148 | 149 | 150 | #ifdef __cplusplus 151 | extern "C" { 152 | #endif 153 | 154 | 155 | struct test__ { 156 | const char* name; 157 | void (*func)(void); 158 | }; 159 | 160 | extern const struct test__ test_list__[]; 161 | 162 | int test_check__(int cond, const char* file, int line, const char* fmt, ...); 163 | void test_message__(const char* fmt, ...); 164 | 165 | 166 | #ifndef TEST_NO_MAIN 167 | 168 | static char* test_argv0__ = NULL; 169 | static size_t test_list_size__ = 0; 170 | static const struct test__** tests__ = NULL; 171 | static char* test_flags__ = NULL; 172 | static size_t test_count__ = 0; 173 | static int test_no_exec__ = -1; 174 | static int test_no_summary__ = 0; 175 | static int test_skip_mode__ = 0; 176 | 177 | static int test_stat_failed_units__ = 0; 178 | static int test_stat_run_units__ = 0; 179 | 180 | static const struct test__* test_current_unit__ = NULL; 181 | static int test_current_already_logged__ = 0; 182 | static int test_verbose_level__ = 2; 183 | static int test_current_failures__ = 0; 184 | static int test_colorize__ = 0; 185 | 186 | #define TEST_COLOR_DEFAULT__ 0 187 | #define TEST_COLOR_GREEN__ 1 188 | #define TEST_COLOR_RED__ 2 189 | #define TEST_COLOR_DEFAULT_INTENSIVE__ 3 190 | #define TEST_COLOR_GREEN_INTENSIVE__ 4 191 | #define TEST_COLOR_RED_INTENSIVE__ 5 192 | 193 | static int 194 | test_print_in_color__(int color, const char* fmt, ...) 195 | { 196 | va_list args; 197 | char buffer[256]; 198 | int n; 199 | 200 | va_start(args, fmt); 201 | vsnprintf(buffer, sizeof(buffer), fmt, args); 202 | va_end(args); 203 | buffer[sizeof(buffer)-1] = '\0'; 204 | 205 | if(!test_colorize__) { 206 | return printf("%s", buffer); 207 | } 208 | 209 | #if defined ACUTEST_UNIX__ 210 | { 211 | const char* col_str; 212 | switch(color) { 213 | case TEST_COLOR_GREEN__: col_str = "\033[0;32m"; break; 214 | case TEST_COLOR_RED__: col_str = "\033[0;31m"; break; 215 | case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break; 216 | case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break; 217 | case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break; 218 | default: col_str = "\033[0m"; break; 219 | } 220 | printf("%s", col_str); 221 | n = printf("%s", buffer); 222 | printf("\033[0m"); 223 | return n; 224 | } 225 | #elif defined ACUTEST_WIN__ 226 | { 227 | HANDLE h; 228 | CONSOLE_SCREEN_BUFFER_INFO info; 229 | WORD attr; 230 | 231 | h = GetStdHandle(STD_OUTPUT_HANDLE); 232 | GetConsoleScreenBufferInfo(h, &info); 233 | 234 | switch(color) { 235 | case TEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; 236 | case TEST_COLOR_RED__: attr = FOREGROUND_RED; break; 237 | case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; 238 | case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; 239 | case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; 240 | default: attr = 0; break; 241 | } 242 | if(attr != 0) 243 | SetConsoleTextAttribute(h, attr); 244 | n = printf("%s", buffer); 245 | SetConsoleTextAttribute(h, info.wAttributes); 246 | return n; 247 | } 248 | #else 249 | n = printf("%s", buffer); 250 | return n; 251 | #endif 252 | } 253 | 254 | int 255 | test_check__(int cond, const char* file, int line, const char* fmt, ...) 256 | { 257 | const char *result_str; 258 | int result_color; 259 | int verbose_level; 260 | 261 | if(cond) { 262 | result_str = "ok"; 263 | result_color = TEST_COLOR_GREEN__; 264 | verbose_level = 3; 265 | } else { 266 | if(!test_current_already_logged__ && test_current_unit__ != NULL) { 267 | printf("[ "); 268 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED"); 269 | printf(" ]\n"); 270 | } 271 | result_str = "failed"; 272 | result_color = TEST_COLOR_RED__; 273 | verbose_level = 2; 274 | test_current_failures__++; 275 | test_current_already_logged__++; 276 | } 277 | 278 | if(test_verbose_level__ >= verbose_level) { 279 | va_list args; 280 | 281 | printf(" "); 282 | 283 | if(file != NULL) { 284 | if(test_verbose_level__ < 3) { 285 | #ifdef ACUTEST_WIN__ 286 | const char* lastsep1 = strrchr(file, '\\'); 287 | const char* lastsep2 = strrchr(file, '/'); 288 | if(lastsep1 == NULL) 289 | lastsep1 = file-1; 290 | if(lastsep2 == NULL) 291 | lastsep2 = file-1; 292 | file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; 293 | #else 294 | const char* lastsep = strrchr(file, '/'); 295 | if(lastsep != NULL) 296 | file = lastsep+1; 297 | #endif 298 | } 299 | printf("%s:%d: Check ", file, line); 300 | } 301 | 302 | va_start(args, fmt); 303 | vprintf(fmt, args); 304 | va_end(args); 305 | 306 | printf("... "); 307 | test_print_in_color__(result_color, result_str); 308 | printf("\n"); 309 | test_current_already_logged__++; 310 | } 311 | 312 | return (cond != 0); 313 | } 314 | 315 | void 316 | test_message__(const char* fmt, ...) 317 | { 318 | char buffer[TEST_MSG_MAXSIZE]; 319 | char* line_beg; 320 | char* line_end; 321 | va_list args; 322 | 323 | if(test_verbose_level__ < 2) 324 | return; 325 | 326 | /* We allow extra message only when something is already wrong in the 327 | * current test. */ 328 | if(!test_current_already_logged__ || test_current_unit__ == NULL) 329 | return; 330 | 331 | va_start(args, fmt); 332 | vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); 333 | va_end(args); 334 | buffer[TEST_MSG_MAXSIZE-1] = '\0'; 335 | 336 | line_beg = buffer; 337 | while(1) { 338 | line_end = strchr(line_beg, '\n'); 339 | if(line_end == NULL) 340 | break; 341 | printf(" %.*s\n", (int)(line_end - line_beg), line_beg); 342 | line_beg = line_end + 1; 343 | } 344 | if(line_beg[0] != '\0') 345 | printf(" %s\n", line_beg); 346 | } 347 | 348 | static void 349 | test_list_names__(void) 350 | { 351 | const struct test__* test; 352 | 353 | printf("Unit tests:\n"); 354 | for(test = &test_list__[0]; test->func != NULL; test++) 355 | printf(" %s\n", test->name); 356 | } 357 | 358 | static void 359 | test_remember__(int i) 360 | { 361 | if(test_flags__[i]) 362 | return; 363 | else 364 | test_flags__[i] = 1; 365 | 366 | tests__[test_count__] = &test_list__[i]; 367 | test_count__++; 368 | } 369 | 370 | static int 371 | test_name_contains_word__(const char* name, const char* pattern) 372 | { 373 | static const char word_delim[] = " \t-_."; 374 | const char* substr; 375 | size_t pattern_len; 376 | int starts_on_word_boundary; 377 | int ends_on_word_boundary; 378 | 379 | pattern_len = strlen(pattern); 380 | 381 | substr = strstr(name, pattern); 382 | while(substr != NULL) { 383 | starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); 384 | ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); 385 | 386 | if(starts_on_word_boundary && ends_on_word_boundary) 387 | return 1; 388 | 389 | substr = strstr(substr+1, pattern); 390 | } 391 | 392 | return 0; 393 | } 394 | 395 | static int 396 | test_lookup__(const char* pattern) 397 | { 398 | int i; 399 | int n = 0; 400 | 401 | /* Try exact match. */ 402 | for(i = 0; i < (int) test_list_size__; i++) { 403 | if(strcmp(test_list__[i].name, pattern) == 0) { 404 | test_remember__(i); 405 | n++; 406 | break; 407 | } 408 | } 409 | if(n > 0) 410 | return n; 411 | 412 | /* Try word match. */ 413 | for(i = 0; i < (int) test_list_size__; i++) { 414 | if(test_name_contains_word__(test_list__[i].name, pattern)) { 415 | test_remember__(i); 416 | n++; 417 | } 418 | } 419 | if(n > 0) 420 | return n; 421 | 422 | /* Try relaxed match. */ 423 | for(i = 0; i < (int) test_list_size__; i++) { 424 | if(strstr(test_list__[i].name, pattern) != NULL) { 425 | test_remember__(i); 426 | n++; 427 | } 428 | } 429 | 430 | return n; 431 | } 432 | 433 | /* Call directly the given test unit function. */ 434 | static int 435 | test_do_run__(const struct test__* test) 436 | { 437 | test_current_unit__ = test; 438 | test_current_failures__ = 0; 439 | test_current_already_logged__ = 0; 440 | 441 | if(test_verbose_level__ >= 3) { 442 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); 443 | test_current_already_logged__++; 444 | } else if(test_verbose_level__ >= 1) { 445 | int n; 446 | char spaces[48]; 447 | 448 | n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); 449 | memset(spaces, ' ', sizeof(spaces)); 450 | if(n < (int) sizeof(spaces)) 451 | printf("%.*s", (int) sizeof(spaces) - n, spaces); 452 | } else { 453 | test_current_already_logged__ = 1; 454 | } 455 | 456 | #ifdef __cplusplus 457 | try { 458 | #endif 459 | 460 | /* This is good to do for case the test unit e.g. crashes. */ 461 | fflush(stdout); 462 | fflush(stderr); 463 | 464 | test->func(); 465 | 466 | #ifdef __cplusplus 467 | } catch(std::exception& e) { 468 | const char* what = e.what(); 469 | if(what != NULL) 470 | test_check__(0, NULL, 0, "Threw std::exception: %s", what); 471 | else 472 | test_check__(0, NULL, 0, "Threw std::exception"); 473 | } catch(...) { 474 | test_check__(0, NULL, 0, "Threw an exception"); 475 | } 476 | #endif 477 | 478 | if(test_verbose_level__ >= 3) { 479 | switch(test_current_failures__) { 480 | case 0: test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, " All conditions have passed.\n\n"); break; 481 | case 1: test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " One condition has FAILED.\n\n"); break; 482 | default: test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " %d conditions have FAILED.\n\n", test_current_failures__); break; 483 | } 484 | } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { 485 | printf("[ "); 486 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "OK"); 487 | printf(" ]\n"); 488 | } 489 | 490 | test_current_unit__ = NULL; 491 | return (test_current_failures__ == 0) ? 0 : -1; 492 | } 493 | 494 | #if defined(ACUTEST_UNIX__) || defined(ACUTEST_WIN__) 495 | /* Called if anything goes bad in Acutest, or if the unit test ends in other 496 | * way then by normal returning from its function (e.g. exception or some 497 | * abnormal child process termination). */ 498 | static void 499 | test_error__(const char* fmt, ...) 500 | { 501 | va_list args; 502 | 503 | if(test_verbose_level__ == 0) 504 | return; 505 | 506 | if(test_verbose_level__ <= 2 && !test_current_already_logged__ && test_current_unit__ != NULL) { 507 | printf("[ "); 508 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED"); 509 | printf(" ]\n"); 510 | } 511 | 512 | if(test_verbose_level__ >= 2) { 513 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, " Error: "); 514 | va_start(args, fmt); 515 | vprintf(fmt, args); 516 | va_end(args); 517 | printf("\n"); 518 | } 519 | } 520 | #endif 521 | 522 | /* Trigger the unit test. If possible (and not suppressed) it starts a child 523 | * process who calls test_do_run__(), otherwise it calls test_do_run__() 524 | * directly. */ 525 | static void 526 | test_run__(const struct test__* test) 527 | { 528 | int failed = 1; 529 | 530 | test_current_unit__ = test; 531 | test_current_already_logged__ = 0; 532 | 533 | if(!test_no_exec__) { 534 | 535 | #if defined(ACUTEST_UNIX__) 536 | 537 | pid_t pid; 538 | int exit_code; 539 | 540 | pid = fork(); 541 | if(pid == (pid_t)-1) { 542 | test_error__("Cannot fork. %s [%d]", strerror(errno), errno); 543 | failed = 1; 544 | } else if(pid == 0) { 545 | /* Child: Do the test. */ 546 | failed = (test_do_run__(test) != 0); 547 | exit(failed ? 1 : 0); 548 | } else { 549 | /* Parent: Wait until child terminates and analyze its exit code. */ 550 | waitpid(pid, &exit_code, 0); 551 | if(WIFEXITED(exit_code)) { 552 | switch(WEXITSTATUS(exit_code)) { 553 | case 0: failed = 0; break; /* test has passed. */ 554 | case 1: /* noop */ break; /* "normal" failure. */ 555 | default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); 556 | } 557 | } else if(WIFSIGNALED(exit_code)) { 558 | char tmp[32]; 559 | const char* signame; 560 | switch(WTERMSIG(exit_code)) { 561 | case SIGINT: signame = "SIGINT"; break; 562 | case SIGHUP: signame = "SIGHUP"; break; 563 | case SIGQUIT: signame = "SIGQUIT"; break; 564 | case SIGABRT: signame = "SIGABRT"; break; 565 | case SIGKILL: signame = "SIGKILL"; break; 566 | case SIGSEGV: signame = "SIGSEGV"; break; 567 | case SIGILL: signame = "SIGILL"; break; 568 | case SIGTERM: signame = "SIGTERM"; break; 569 | default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; 570 | } 571 | test_error__("Test interrupted by %s", signame); 572 | } else { 573 | test_error__("Test ended in an unexpected way [%d]", exit_code); 574 | } 575 | } 576 | 577 | #elif defined(ACUTEST_WIN__) 578 | 579 | char buffer[512] = {0}; 580 | STARTUPINFOA startupInfo; 581 | PROCESS_INFORMATION processInfo; 582 | DWORD exitCode; 583 | 584 | /* Windows has no fork(). So we propagate all info into the child 585 | * through a command line arguments. */ 586 | _snprintf(buffer, sizeof(buffer)-1, 587 | "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"", 588 | test_argv0__, test_verbose_level__, 589 | test_colorize__ ? "always" : "never", test->name); 590 | memset(&startupInfo, 0, sizeof(startupInfo)); 591 | startupInfo.cb = sizeof(STARTUPINFO); 592 | if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { 593 | WaitForSingleObject(processInfo.hProcess, INFINITE); 594 | GetExitCodeProcess(processInfo.hProcess, &exitCode); 595 | CloseHandle(processInfo.hThread); 596 | CloseHandle(processInfo.hProcess); 597 | failed = (exitCode != 0); 598 | } else { 599 | test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); 600 | failed = 1; 601 | } 602 | 603 | #else 604 | 605 | /* A platform where we don't know how to run child process. */ 606 | failed = (test_do_run__(test) != 0); 607 | 608 | #endif 609 | 610 | } else { 611 | /* Child processes suppressed through --no-exec. */ 612 | failed = (test_do_run__(test) != 0); 613 | } 614 | 615 | test_current_unit__ = NULL; 616 | 617 | test_stat_run_units__++; 618 | if(failed) 619 | test_stat_failed_units__++; 620 | } 621 | 622 | #if defined(ACUTEST_WIN__) 623 | /* Callback for SEH events. */ 624 | static LONG CALLBACK 625 | test_exception_filter__(EXCEPTION_POINTERS *ptrs) 626 | { 627 | test_error__("Unhandled SEH exception %08lx at %p.", 628 | ptrs->ExceptionRecord->ExceptionCode, 629 | ptrs->ExceptionRecord->ExceptionAddress); 630 | fflush(stdout); 631 | fflush(stderr); 632 | return EXCEPTION_EXECUTE_HANDLER; 633 | } 634 | #endif 635 | 636 | 637 | static void 638 | test_help__(void) 639 | { 640 | printf("Usage: %s [options] [test...]\n", test_argv0__); 641 | printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); 642 | printf("tests in the suite but those listed. By default, if no tests are specified\n"); 643 | printf("on the command line, all unit tests in the suite are run.\n"); 644 | printf("\n"); 645 | printf("Options:\n"); 646 | printf(" -s, --skip Execute all unit tests but the listed ones\n"); 647 | printf(" --exec=WHEN If supported, execute unit tests as child processes\n"); 648 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 649 | printf(" -E, --no-exec Same as --exec=never\n"); 650 | printf(" --no-summary Suppress printing of test results summary\n"); 651 | printf(" -l, --list List unit tests in the suite and exit\n"); 652 | printf(" -v, --verbose Enable more verbose output\n"); 653 | printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); 654 | printf(" 0 ... Be silent\n"); 655 | printf(" 1 ... Output one line per test (and summary)\n"); 656 | printf(" 2 ... As 1 and failed conditions (this is default)\n"); 657 | printf(" 3 ... As 1 and all conditions (and extended summary)\n"); 658 | printf(" --color=WHEN Enable colorized output\n"); 659 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 660 | printf(" -h, --help Display this help and exit\n"); 661 | 662 | if(test_list_size__ < 16) { 663 | printf("\n"); 664 | test_list_names__(); 665 | } 666 | } 667 | 668 | #ifdef ACUTEST_LINUX__ 669 | static int 670 | test_is_tracer_present__(void) 671 | { 672 | char buf[256+32+1]; 673 | int tracer_present = 0; 674 | int fd; 675 | ssize_t n_read; 676 | 677 | fd = open("/proc/self/status", O_RDONLY); 678 | if(fd == -1) 679 | return 0; 680 | 681 | n_read = read(fd, buf, sizeof(buf)-1); 682 | while(n_read > 0) { 683 | static const char pattern[] = "TracerPid:"; 684 | const char* field; 685 | 686 | buf[n_read] = '\0'; 687 | field = strstr(buf, pattern); 688 | if(field != NULL && field < buf + sizeof(buf) - 32) { 689 | pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); 690 | tracer_present = (tracer_pid != 0); 691 | break; 692 | } 693 | 694 | if(n_read == sizeof(buf)-1) { 695 | memmove(buf, buf + sizeof(buf)-1 - 32, 32); 696 | n_read = read(fd, buf+32, sizeof(buf)-1-32); 697 | if(n_read > 0) 698 | n_read += 32; 699 | } 700 | } 701 | 702 | close(fd); 703 | return tracer_present; 704 | } 705 | #endif 706 | 707 | int 708 | main(int argc, char** argv) 709 | { 710 | int i; 711 | int seen_double_dash = 0; 712 | 713 | test_argv0__ = argv[0]; 714 | 715 | #if defined ACUTEST_UNIX__ 716 | test_colorize__ = isatty(STDOUT_FILENO); 717 | #elif defined ACUTEST_WIN__ 718 | #if defined __BORLANDC__ 719 | test_colorize__ = isatty(_fileno(stdout)); 720 | #else 721 | test_colorize__ = _isatty(_fileno(stdout)); 722 | #endif 723 | #else 724 | test_colorize__ = 0; 725 | #endif 726 | 727 | /* Count all test units */ 728 | test_list_size__ = 0; 729 | for(i = 0; test_list__[i].func != NULL; i++) 730 | test_list_size__++; 731 | 732 | tests__ = (const struct test__**) malloc(sizeof(const struct test__*) * test_list_size__); 733 | test_flags__ = (char*) malloc(sizeof(char) * test_list_size__); 734 | if(tests__ == NULL || test_flags__ == NULL) { 735 | fprintf(stderr, "Out of memory.\n"); 736 | exit(2); 737 | } 738 | memset((void*) test_flags__, 0, sizeof(char) * test_list_size__); 739 | 740 | /* Parse options */ 741 | for(i = 1; i < argc; i++) { 742 | if(seen_double_dash || argv[i][0] != '-') { 743 | if(test_lookup__(argv[i]) == 0) { 744 | fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0], argv[i]); 745 | fprintf(stderr, "Try '%s --list' for list of unit tests.\n", argv[0]); 746 | exit(2); 747 | } 748 | } else if(strcmp(argv[i], "--") == 0) { 749 | seen_double_dash = 1; 750 | } else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { 751 | test_help__(); 752 | exit(0); 753 | } else if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { 754 | test_verbose_level__++; 755 | } else if(strncmp(argv[i], "--verbose=", 10) == 0) { 756 | test_verbose_level__ = atoi(argv[i] + 10); 757 | } else if(strcmp(argv[i], "--color=auto") == 0) { 758 | /* noop (set from above) */ 759 | } else if(strcmp(argv[i], "--color=always") == 0 || strcmp(argv[i], "--color") == 0) { 760 | test_colorize__ = 1; 761 | } else if(strcmp(argv[i], "--color=never") == 0 || strcmp(argv[i], "--no-color") == 0) { 762 | test_colorize__ = 0; 763 | } else if(strcmp(argv[i], "--skip") == 0 || strcmp(argv[i], "-s") == 0) { 764 | test_skip_mode__ = 1; 765 | } else if(strcmp(argv[i], "--exec=auto") == 0) { 766 | /* noop (set from above) */ 767 | } else if(strcmp(argv[i], "--exec=always") == 0 || strcmp(argv[i], "--exec") == 0) { 768 | test_no_exec__ = 0; 769 | } else if(strcmp(argv[i], "--exec=never") == 0 || strcmp(argv[i], "--no-exec") == 0 || strcmp(argv[i], "-E") == 0) { 770 | test_no_exec__ = 1; 771 | } else if(strcmp(argv[i], "--no-summary") == 0) { 772 | test_no_summary__ = 1; 773 | } else if(strcmp(argv[i], "--list") == 0 || strcmp(argv[i], "-l") == 0) { 774 | test_list_names__(); 775 | exit(0); 776 | } else { 777 | fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]); 778 | fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); 779 | exit(2); 780 | } 781 | } 782 | 783 | #if defined(ACUTEST_WIN__) 784 | SetUnhandledExceptionFilter(test_exception_filter__); 785 | #endif 786 | 787 | /* By default, we want to run all tests. */ 788 | if(test_count__ == 0) { 789 | for(i = 0; test_list__[i].func != NULL; i++) 790 | tests__[i] = &test_list__[i]; 791 | test_count__ = test_list_size__; 792 | } 793 | 794 | /* Guess whether we want to run unit tests as child processes. */ 795 | if(test_no_exec__ < 0) { 796 | test_no_exec__ = 0; 797 | 798 | if(test_count__ <= 1) { 799 | test_no_exec__ = 1; 800 | } else { 801 | #ifdef ACUTEST_WIN__ 802 | if(IsDebuggerPresent()) 803 | test_no_exec__ = 1; 804 | #endif 805 | #ifdef ACUTEST_LINUX__ 806 | if(test_is_tracer_present__()) 807 | test_no_exec__ = 1; 808 | #endif 809 | } 810 | } 811 | 812 | /* Run the tests */ 813 | if(!test_skip_mode__) { 814 | /* Run the listed tests. */ 815 | for(i = 0; i < (int) test_count__; i++) 816 | test_run__(tests__[i]); 817 | } else { 818 | /* Run all tests except those listed. */ 819 | for(i = 0; test_list__[i].func != NULL; i++) { 820 | if(!test_flags__[i]) 821 | test_run__(&test_list__[i]); 822 | } 823 | } 824 | 825 | /* Write a summary */ 826 | if(!test_no_summary__ && test_verbose_level__ >= 1) { 827 | if(test_verbose_level__ >= 3) { 828 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Summary:\n"); 829 | 830 | printf(" Count of all unit tests: %4d\n", (int) test_list_size__); 831 | printf(" Count of run unit tests: %4d\n", test_stat_run_units__); 832 | printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); 833 | printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__); 834 | printf(" "); 835 | } 836 | 837 | if(test_stat_failed_units__ == 0) { 838 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS:"); 839 | printf(" All unit tests have passed.\n"); 840 | } else { 841 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:"); 842 | printf(" %d of %d unit tests have failed.\n", 843 | test_stat_failed_units__, test_stat_run_units__); 844 | } 845 | 846 | if(test_verbose_level__ >= 3) 847 | printf("\n"); 848 | } 849 | 850 | free((void*) tests__); 851 | free((void*) test_flags__); 852 | 853 | return (test_stat_failed_units__ == 0) ? 0 : 1; 854 | } 855 | 856 | 857 | #endif /* #ifndef TEST_NO_MAIN */ 858 | 859 | #ifdef __cplusplus 860 | } /* extern "C" */ 861 | #endif 862 | 863 | 864 | #endif /* #ifndef ACUTEST_H__ */ 865 | --------------------------------------------------------------------------------