1 #ifndef EAGINE_SUDOKU_HPP
9 #define EAGINE_SUDOKU_HPP
26 class basic_sudoku_glyph;
27 template <
unsigned S,
bool Tight = false>
28 class basic_sudoku_board;
30 class basic_sudoku_board_generator;
33 class basic_sudoku_board_traits {
35 static constexpr
const unsigned rank = S;
36 static constexpr
const unsigned glyph_count = S * S;
38 using This = basic_sudoku_board_traits;
39 using generator = basic_sudoku_board_generator<S>;
40 using board_type = basic_sudoku_board<S>;
42 basic_sudoku_board_traits() noexcept = default;
44 basic_sudoku_board_traits(
45 std::array<std::
string, glyph_count> glyphs,
46 std::array<std::
string, S> multi_glyphs,
47 std::
string empty_glyph) noexcept
48 : _glyphs{std::move(glyphs)}
49 , _multi_glyphs{std::move(multi_glyphs)}
50 , _empty_glyph{std::move(empty_glyph)} {}
52 auto make_diagonal() const -> board_type;
53 auto make_generator() const -> generator;
55 auto print(std::ostream&, const basic_sudoku_glyph<S>& glyph) const
58 auto print_empty(std::ostream& out) const -> std::ostream& {
59 return out << _empty_glyph;
62 auto print(std::ostream&,
const basic_sudoku_board<S, false>& board)
const
65 auto print(std::ostream&,
const basic_sudoku_board<S, true>& board)
const
69 std::array<std::string, glyph_count> _glyphs{};
70 std::array<std::string, S> _multi_glyphs{};
71 std::string _empty_glyph{};
75 class default_sudoku_board_traits;
78 class default_sudoku_board_traits<2> :
public basic_sudoku_board_traits<2> {
81 default_sudoku_board_traits() noexcept
82 : basic_sudoku_board_traits<2>{
89 class default_sudoku_board_traits<3> :
public basic_sudoku_board_traits<3> {
92 default_sudoku_board_traits() noexcept
93 : basic_sudoku_board_traits<3>{
94 {
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"},
95 {
"?",
"?",
"?"},
"."} {}
100 class default_sudoku_board_traits<4> :
public basic_sudoku_board_traits<4> {
103 default_sudoku_board_traits() noexcept
104 : basic_sudoku_board_traits<4>{
105 {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"A",
"B",
"C",
"D",
"E",
"F"},
106 {
"?",
"?",
"?",
"?"},
"."} {}
111 class default_sudoku_board_traits<5> :
public basic_sudoku_board_traits<5> {
114 default_sudoku_board_traits() noexcept
115 : basic_sudoku_board_traits<5>{
116 {
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M"
117 ,
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y"},
118 {
"?",
"?",
"?",
"?",
"?"},
"."} {}
123 class default_sudoku_board_traits<6> :
public basic_sudoku_board_traits<6> {
126 default_sudoku_board_traits() noexcept
127 : basic_sudoku_board_traits<6>{
128 {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"A",
"B"
129 ,
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N"
130 ,
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"},
131 {
"?",
"?",
"?",
"?",
"?",
"?"},
"."} {}
135 template <
unsigned S>
136 class block_sudoku_board_traits;
139 class block_sudoku_board_traits<2> :
public basic_sudoku_board_traits<2> {
142 block_sudoku_board_traits() noexcept
143 : basic_sudoku_board_traits<2>{
150 class block_sudoku_board_traits<3> :
public basic_sudoku_board_traits<3> {
153 block_sudoku_board_traits() noexcept
154 : basic_sudoku_board_traits<3>{
155 {
" ",
"▀",
"▄",
"▖",
"▗",
"▘",
"▝",
"▚",
"▞"},
156 {
"?",
"?",
"?"},
"."} {}
161 class block_sudoku_board_traits<4> :
public basic_sudoku_board_traits<4> {
164 block_sudoku_board_traits() noexcept
165 : basic_sudoku_board_traits<4>{
166 {
" ",
"▀",
"▄",
"█",
"▌",
"▐",
"▖",
"▗",
"▘",
"▙",
"▚",
"▛",
"▜",
"▝",
"▞",
"▟"},
167 {
"?",
"?",
"?",
"?"},
"."} {}
172 class block_sudoku_board_traits<5> :
public basic_sudoku_board_traits<5> {
175 block_sudoku_board_traits() noexcept
176 : basic_sudoku_board_traits<5>{
177 {
"▁",
"▂",
"▃",
"▄",
"▅",
"▆",
"▇",
"▏",
"▎",
"▍",
"▌",
"▋",
"▊"
178 ,
"▉",
"▐",
"▖",
"▗",
"▘",
"▙",
"▚",
"▛",
"▜",
"▝",
"▞",
"▟"},
179 {
"?",
"?",
"?",
"?",
"?"},
"."} {}
183 template <
unsigned S>
184 class basic_sudoku_glyph {
186 using board_traits = basic_sudoku_board_traits<S>;
187 using cell_type = std::conditional_t<
188 (board_traits::glyph_count > 32U),
191 (board_traits::glyph_count > 16),
194 (board_traits::glyph_count > 8),
198 static constexpr
const unsigned glyph_count = board_traits::glyph_count;
200 static constexpr
auto to_cell_type(
unsigned index) noexcept {
202 return cell_type(1U << index);
205 constexpr basic_sudoku_glyph() noexcept = default;
206 constexpr basic_sudoku_glyph(
unsigned index) noexcept
207 : _cel_val{to_cell_type(index)} {}
209 constexpr
auto is_empty() const noexcept {
210 return _cel_val == 0U;
213 constexpr
auto is_single() const noexcept {
214 return _is_pot(_cel_val);
217 constexpr
auto is_multiple() const noexcept {
218 return !is_empty() && !is_single();
221 auto cell_value() const noexcept -> cell_type {
225 auto get_index() const noexcept ->
unsigned {
226 unsigned result = 0U;
227 while(result < glyph_count) {
228 if(_cel_val == to_cell_type(result)) {
233 EAGINE_ASSERT(result < glyph_count);
237 constexpr
auto set(
unsigned index) noexcept ->
auto& {
238 EAGINE_ASSERT(index < glyph_count);
239 _cel_val = to_cell_type(index);
243 constexpr
auto add(
unsigned index) noexcept ->
auto& {
244 EAGINE_ASSERT(index < glyph_count);
245 _cel_val |= to_cell_type(index);
249 template <
typename Function>
250 void for_each_alternative(Function func)
const noexcept {
253 const auto mask = to_cell_type(index);
254 if((_cel_val & mask) == mask) {
260 auto alternative_count() const noexcept ->
unsigned {
262 cell_type bits = _cel_val;
271 friend class basic_sudoku_board<S, false>;
272 friend class basic_sudoku_board<S, true>;
274 struct from_cell_value_tag {};
275 constexpr basic_sudoku_glyph(cell_type cel_val, from_cell_value_tag) noexcept
276 : _cel_val{cel_val} {}
278 static constexpr
auto _is_pot(cell_type v) noexcept {
279 return (v != 0U) && ((v & (v - 1U)) == 0U);
282 cell_type _cel_val{0U};
285 template <
unsigned S,
bool Tight>
286 class basic_sudoku_board {
288 using This = basic_sudoku_board;
289 using board_traits = basic_sudoku_board_traits<S>;
290 using glyph_type = basic_sudoku_glyph<S>;
291 using cell_type =
typename glyph_type::cell_type;
292 using coord_type = std::array<unsigned, 4>;
293 static constexpr
const unsigned glyph_count = board_traits::glyph_count;
295 static constexpr
auto invalid_coord() noexcept -> coord_type {
296 return {{S, S, S, S}};
299 template <
typename Function>
300 void for_each_coord(Function func)
const noexcept {
305 if(!func(coord_type{{bx, by, cx, cy}})) {
314 basic_sudoku_board(
const board_traits& type) noexcept
316 for_each_coord([&](
const auto& coord) {
317 set(coord, glyph_type());
322 basic_sudoku_board(
const basic_sudoku_board<S, !Tight>& that) noexcept
324 , _blocks{that._blocks} {}
326 auto type() const noexcept -> auto& {
330 friend auto operator<<(std::ostream& out,
const This& that)
332 return that._type.get().print(out, that);
335 auto is_possible(
const coord_type& coord, glyph_type value)
const noexcept {
336 const auto [cbx, cby, ccx, ccy] = coord;
337 const auto cel_val = value.cell_value();
341 if((cx != ccx) || (cy != ccy)) {
342 if(_cell_val({cbx, cby, cx, cy}) == cel_val) {
351 if((bx != cbx) || (cx != ccx)) {
352 if(_cell_val({bx, cby, cx, ccy}) == cel_val) {
361 if((by != cby) || (cy != ccy)) {
362 if(_cell_val({cbx, by, ccx, cy}) == cel_val) {
371 auto is_solved() noexcept {
373 for_each_coord([&](
const auto& coord) {
374 const auto value = get(coord);
375 if(!value.is_single()) {
384 auto has_empty() noexcept {
386 for_each_coord([&](
const auto& coord) {
387 if(get(coord).is_empty()) {
396 auto get(
const coord_type& coord)
const noexcept -> glyph_type {
397 return {_cell_val(coord),
typename glyph_type::from_cell_value_tag{}};
400 auto set(
const coord_type& coord, glyph_type value) noexcept ->
auto& {
401 _cell_ref(coord) = value.cell_value();
405 auto set_available_alternatives(
const coord_type& coord) noexcept ->
auto& {
406 glyph_type alternatives;
408 if(is_possible(coord, value)) {
409 alternatives.add(value);
412 return set(coord, alternatives);
415 auto calculate_alternatives() noexcept -> auto& {
416 for_each_coord([&](
const auto& coord) {
417 if(!get(coord).is_single()) {
418 set_available_alternatives(coord);
425 auto find_unsolved() const noexcept -> coord_type {
426 auto result = invalid_coord();
427 auto min_alt = glyph_count + 1;
428 for_each_coord([&](
const auto& coord) {
429 if(
const auto num_alt{get(coord).alternative_count()}) {
430 if((num_alt > 1) && (min_alt > num_alt)) {
440 template <
typename Function>
441 void for_each_alternative(
const coord_type& coord, Function func)
const {
442 if(coord != invalid_coord()) {
443 get(coord).for_each_alternative([&](glyph_type alt) {
444 auto candidate = This(*this).set(coord, alt);
445 if(candidate.is_possible(coord, alt)) {
446 if(!candidate.calculate_alternatives().has_empty()) {
454 auto alternative_count() const noexcept ->
unsigned {
456 for_each_coord([&](
const auto& coord) {
457 const auto& cell = get(coord);
458 if(!cell.is_single()) {
459 count += cell.alternative_count();
466 using block_type = std::array<cell_type, glyph_count>;
467 using blocks_type = std::array<block_type, glyph_count>;
469 auto get_block(
unsigned bx,
unsigned by)
const noexcept
470 ->
const block_type& {
471 return _block(_blocks, bx, by);
474 auto set_block(
unsigned bx,
unsigned by,
const block_type&
block) noexcept
476 _block(_blocks, bx, by) =
block;
480 auto tight() const noexcept -> basic_sudoku_board<S, true> {
485 auto _cell_val(
const coord_type& coord)
const noexcept {
486 const auto [bx, by, cx, cy] = coord;
487 return _cell(_block(_blocks, bx, by), cx, cy);
490 auto _cell_ref(
const coord_type& coord) noexcept ->
auto& {
491 const auto [bx, by, cx, cy] = coord;
492 return _cell(_block(_blocks, bx, by), cx, cy);
495 template <
typename B>
496 static auto _cell(B&
block,
unsigned x,
unsigned y) noexcept ->
auto& {
497 return block[y * S + x];
500 template <
typename B>
501 static auto _block(B& blocks,
unsigned x,
unsigned y) noexcept ->
auto& {
502 return blocks[y * S + x];
505 friend class basic_sudoku_board<S, !Tight>;
506 friend struct serializer<basic_sudoku_board<S, Tight>>;
507 friend struct deserializer<basic_sudoku_board<S, Tight>>;
509 std::reference_wrapper<const board_traits> _type;
510 blocks_type _blocks{};
513 template <
unsigned S>
514 class basic_sudoku_board_generator {
516 using board_traits = basic_sudoku_board_traits<S>;
517 using board_type = basic_sudoku_board<S>;
519 basic_sudoku_board_generator(
const board_traits& traits)
522 auto generate(std::size_t count) -> board_type {
523 board_type result{_traits};
524 result.calculate_alternatives();
527 const typename board_type::coord_type coord{
533 if(!result.get(coord).is_single()) {
535 const auto value{_glyph_dist(_rd)};
536 if(result.is_possible(coord, value)) {
537 result.set(coord, value).calculate_alternatives();
548 auto generate_one() {
552 auto generate_few() {
556 auto generate_medium() {
560 auto generate_many() {
561 return generate(S * S * S + S + S + S);
565 std::reference_wrapper<const board_traits> _traits;
566 std::random_device _rd;
567 std::uniform_int_distribution<unsigned> _coord_dist{0, S - 1U};
568 std::uniform_int_distribution<unsigned> _glyph_dist{0, S* S - 1U};
571 template <
unsigned S>
572 auto basic_sudoku_board_traits<S>::make_diagonal() const -> board_type {
573 board_type result{*
this};
577 result.set({{b, b, c, c}}, g++);
580 return result.calculate_alternatives();
583 template <
unsigned S>
584 auto basic_sudoku_board_traits<S>::make_generator() const -> generator {
588 template <
unsigned S>
589 auto basic_sudoku_board_traits<S>::print(
591 const basic_sudoku_glyph<S>& glyph)
const -> std::ostream& {
592 if(glyph.is_single()) {
593 out << _glyphs[glyph.get_index()];
594 }
else if(glyph.is_empty()) {
597 out << _multi_glyphs[0U];
602 template <
unsigned S>
603 auto basic_sudoku_board_traits<S>::print(
605 const basic_sudoku_board<S, false>& board)
const -> std::ostream& {
610 print(out <<
' ', board.get({{bx, by, cx, cy}}));
621 if(s1 == 0 && s2 == 0) {
637 template <
unsigned S>
638 auto basic_sudoku_board_traits<S>::print(
640 const basic_sudoku_board<S, true>& board)
const -> std::ostream& {
645 print(out, board.get({{bx, by, cx, cy}}));
654 template <
unsigned S>
655 class basic_sudoku_solver {
657 using board_type = basic_sudoku_board<S>;
659 auto solve(board_type board) -> board_type {
660 std::stack<board_type> solutions;
661 solutions.push(board);
664 while(!(solutions.empty() || done)) {
665 board = solutions.top();
668 board.for_each_alternative(
669 board.find_unsolved(), [&](
auto candidate) {
670 if(candidate.is_solved()) {
674 solutions.push(candidate);
682 template <
unsigned S>
683 class basic_sudoku_tiling;
685 template <
unsigned S>
686 class basic_sudoku_tile_patch {
687 static_assert(S > 2U);
688 static constexpr
const span_size_t M = S * (S - 2);
692 : _width{w % M ? (1 + w / M) * M : w}
693 , _height{h % M ? (1 + w / M) * M : h} {
694 EAGINE_ASSERT(_width > 0);
695 EAGINE_ASSERT(_height > 0);
696 _cells.resize(
std_size(_width * _height));
708 EAGINE_ASSERT((x >= 0) && (x < width()));
709 EAGINE_ASSERT((y >= 0) && (y < height()));
710 return _cells[
std_size(y * _width + x)];
714 operator<<(std::ostream& out,
const basic_sudoku_tile_patch& that)
719 EAGINE_MAYBE_UNUSED(x);
720 EAGINE_MAYBE_UNUSED(y);
721 EAGINE_ASSERT(k < that._cells.size());
722 out << std::setw(3) << unsigned(that._cells[k++]);
730 friend class basic_sudoku_tiling<S>;
734 std::vector<std::uint8_t> _cells;
737 template <
unsigned S>
738 class basic_sudoku_tiling : basic_sudoku_solver<S> {
739 static_assert(S > 2U);
742 using board_traits = basic_sudoku_board_traits<S>;
743 using board_type = basic_sudoku_board<S>;
745 basic_sudoku_tiling(
const board_traits& traits)
748 std::make_tuple(0, 0), this->solve(traits.make_diagonal()));
751 basic_sudoku_tiling(
const board_traits& traits, board_type board)
753 _boards.emplace(std::make_tuple(0, 0), this->solve(std::move(board)));
756 auto generate(
int xmin,
int ymin,
int xmax,
int ymax) ->
auto& {
765 auto fill(
int xmin,
int ymin, basic_sudoku_tile_patch<S>& patch)
766 -> basic_sudoku_tile_patch<S>& {
767 const int ymax = ymin + patch.height() / (S * (S - 2));
768 const int xmax = xmin + patch.width() / (S * (S - 2));
770 for(
int y = ymax - 1; y >= ymin; --y) {
774 auto& board = _get(x, y);
777 EAGINE_ASSERT(k < patch._cells.size());
779 board.get({bx, by, cx, cy}).get_index();
789 auto print(std::ostream& out,
int xmin,
int ymin,
int xmax,
int ymax)
795 auto& board = _get(x, y);
799 out, board.get({bx, by, cx, cy}));
811 auto _get(
int x,
int y) ->
const board_type& {
812 auto pos = _boards.find({x, y});
813 if(pos == _boards.end()) {
814 board_type added{_traits};
817 auto& left = _get(x - 1, y);
819 added.set_block(0U, by, left.get_block(S - 1U, by));
821 auto& down = _get(x, y - 1);
823 added.set_block(bx, S - 1U, down.get_block(bx, 0U));
825 pos = _emplace(x, y, added);
827 auto& right = _get(x + 1, y);
829 added.set_block(S - 1U, by, right.get_block(0U, by));
831 auto& down = _get(x, y - 1);
833 added.set_block(bx, S - 1U, down.get_block(bx, 0U));
835 pos = _emplace(x, y, added);
837 auto& down = _get(x, y - 1);
839 added.set_block(bx, S - 1U, down.get_block(bx, 0U));
841 pos = _emplace(x, y, added);
845 auto& left = _get(x - 1, y);
847 added.set_block(0U, by, left.get_block(S - 1U, by));
849 auto& up = _get(x, y + 1);
851 added.set_block(bx, 0U, up.get_block(bx, S - 1U));
853 pos = _emplace(x, y, added);
855 auto& right = _get(x + 1, y);
857 added.set_block(S - 1U, by, right.get_block(0U, by));
859 auto& up = _get(x, y + 1);
861 added.set_block(bx, 0U, up.get_block(bx, S - 1U));
863 pos = _emplace(x, y, added);
865 auto& up = _get(x, y + 1);
867 added.set_block(bx, 0U, up.get_block(bx, S - 1U));
869 pos = _emplace(x, y, added);
873 auto& left = _get(x - 1, y);
875 added.set_block(0U, by, left.get_block(S - 1U, by));
877 pos = _emplace(x, y, added);
879 auto& right = _get(x + 1, y);
881 added.set_block(S - 1U, by, right.get_block(0U, by));
883 pos = _emplace(x, y, added);
889 auto _emplace(
int x,
int y, board_type board) {
892 std::make_tuple(x, y), this->solve(board.calculate_alternatives()))
896 std::reference_wrapper<const board_traits> _traits;
897 flat_map<std::tuple<int, int>, basic_sudoku_board<S>> _boards;
902 #endif // EAGINE_SUDOKU_HPP