OGLplus  (0.59.0) a C++ wrapper for rendering APIs

sudoku.hpp
Go to the documentation of this file.
1 #ifndef EAGINE_SUDOKU_HPP
9 #define EAGINE_SUDOKU_HPP
10 
11 #include "assert.hpp"
12 #include "flat_map.hpp"
13 #include "integer_range.hpp"
14 #include "serialize/fwd.hpp"
15 #include <array>
16 #include <cstdint>
17 #include <functional>
18 #include <iomanip>
19 #include <ostream>
20 #include <random>
21 #include <stack>
22 
23 namespace eagine {
24 //------------------------------------------------------------------------------
25 template <unsigned S>
26 class basic_sudoku_glyph;
27 template <unsigned S, bool Tight = false>
28 class basic_sudoku_board;
29 template <unsigned S>
30 class basic_sudoku_board_generator;
31 //------------------------------------------------------------------------------
32 template <unsigned S>
33 class basic_sudoku_board_traits {
34 public:
35  static constexpr const unsigned rank = S;
36  static constexpr const unsigned glyph_count = S * S;
37 
38  using This = basic_sudoku_board_traits;
39  using generator = basic_sudoku_board_generator<S>;
40  using board_type = basic_sudoku_board<S>;
41 
42  basic_sudoku_board_traits() noexcept = default;
43 
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)} {}
51 
52  auto make_diagonal() const -> board_type;
53  auto make_generator() const -> generator;
54 
55  auto print(std::ostream&, const basic_sudoku_glyph<S>& glyph) const
56  -> std::ostream&;
57 
58  auto print_empty(std::ostream& out) const -> std::ostream& {
59  return out << _empty_glyph;
60  }
61 
62  auto print(std::ostream&, const basic_sudoku_board<S, false>& board) const
63  -> std::ostream&;
64 
65  auto print(std::ostream&, const basic_sudoku_board<S, true>& board) const
66  -> std::ostream&;
67 
68 private:
69  std::array<std::string, glyph_count> _glyphs{};
70  std::array<std::string, S> _multi_glyphs{};
71  std::string _empty_glyph{};
72 };
73 //------------------------------------------------------------------------------
74 template <unsigned S>
75 class default_sudoku_board_traits;
76 
77 template <>
78 class default_sudoku_board_traits<2> : public basic_sudoku_board_traits<2> {
79 public:
80  // clang-format off
81  default_sudoku_board_traits() noexcept
82  : basic_sudoku_board_traits<2>{
83  {"1","2","3","4"},
84  { "?", "?"}, "."} {}
85  // clang-format on
86 };
87 
88 template <>
89 class default_sudoku_board_traits<3> : public basic_sudoku_board_traits<3> {
90 public:
91  // clang-format off
92  default_sudoku_board_traits() noexcept
93  : basic_sudoku_board_traits<3>{
94  {"1","2","3","4","5","6","7","8","9"},
95  {"?", "?", "?"}, "."} {}
96  // clang-format on
97 };
98 
99 template <>
100 class default_sudoku_board_traits<4> : public basic_sudoku_board_traits<4> {
101 public:
102  // clang-format off
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  {"?", "?", "?", "?"}, "."} {}
107  // clang-format on
108 };
109 
110 template <>
111 class default_sudoku_board_traits<5> : public basic_sudoku_board_traits<5> {
112 public:
113  // clang-format off
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  {"?", "?", "?", "?", "?"}, "."} {}
119  // clang-format on
120 };
121 
122 template <>
123 class default_sudoku_board_traits<6> : public basic_sudoku_board_traits<6> {
124 public:
125  // clang-format off
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  {"?", "?", "?", "?", "?", "?"}, "."} {}
132  // clang-format on
133 };
134 //------------------------------------------------------------------------------
135 template <unsigned S>
136 class block_sudoku_board_traits;
137 
138 template <>
139 class block_sudoku_board_traits<2> : public basic_sudoku_board_traits<2> {
140 public:
141  // clang-format off
142  block_sudoku_board_traits() noexcept
143  : basic_sudoku_board_traits<2>{
144  {"▖","▗","▘","▝"},
145  { "?", "?"}, "."} {}
146  // clang-format on
147 };
148 
149 template <>
150 class block_sudoku_board_traits<3> : public basic_sudoku_board_traits<3> {
151 public:
152  // clang-format off
153  block_sudoku_board_traits() noexcept
154  : basic_sudoku_board_traits<3>{
155  {" ","▀","▄","▖","▗","▘","▝","▚","▞"},
156  {"?","?","?"}, "."} {}
157  // clang-format on
158 };
159 
160 template <>
161 class block_sudoku_board_traits<4> : public basic_sudoku_board_traits<4> {
162 public:
163  // clang-format off
164  block_sudoku_board_traits() noexcept
165  : basic_sudoku_board_traits<4>{
166  {" ","▀","▄","█","▌","▐","▖","▗","▘","▙","▚","▛","▜","▝","▞","▟"},
167  {"?","?","?","?"}, "."} {}
168  // clang-format on
169 };
170 
171 template <>
172 class block_sudoku_board_traits<5> : public basic_sudoku_board_traits<5> {
173 public:
174  // clang-format off
175  block_sudoku_board_traits() noexcept
176  : basic_sudoku_board_traits<5>{
177  {"▁","▂","▃","▄","▅","▆","▇","▏","▎","▍","▌","▋","▊"
178  ,"▉","▐","▖","▗","▘","▙","▚","▛","▜","▝","▞","▟"},
179  {"?", "?", "?", "?", "?"}, "."} {}
180  // clang-format on
181 };
182 //------------------------------------------------------------------------------
183 template <unsigned S>
184 class basic_sudoku_glyph {
185 public:
186  using board_traits = basic_sudoku_board_traits<S>;
187  using cell_type = std::conditional_t<
188  (board_traits::glyph_count > 32U),
189  std::uint64_t,
190  std::conditional_t<
191  (board_traits::glyph_count > 16),
192  std::uint32_t,
193  std::conditional_t<
194  (board_traits::glyph_count > 8),
195  std::uint16_t,
196  std::uint8_t>>>;
197 
198  static constexpr const unsigned glyph_count = board_traits::glyph_count;
199 
200  static constexpr auto to_cell_type(unsigned index) noexcept {
201  // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
202  return cell_type(1U << index);
203  }
204 
205  constexpr basic_sudoku_glyph() noexcept = default;
206  constexpr basic_sudoku_glyph(unsigned index) noexcept
207  : _cel_val{to_cell_type(index)} {}
208 
209  constexpr auto is_empty() const noexcept {
210  return _cel_val == 0U;
211  }
212 
213  constexpr auto is_single() const noexcept {
214  return _is_pot(_cel_val);
215  }
216 
217  constexpr auto is_multiple() const noexcept {
218  return !is_empty() && !is_single();
219  }
220 
221  auto cell_value() const noexcept -> cell_type {
222  return _cel_val;
223  }
224 
225  auto get_index() const noexcept -> unsigned {
226  unsigned result = 0U;
227  while(result < glyph_count) {
228  if(_cel_val == to_cell_type(result)) {
229  break;
230  }
231  ++result;
232  }
233  EAGINE_ASSERT(result < glyph_count);
234  return result;
235  }
236 
237  constexpr auto set(unsigned index) noexcept -> auto& {
238  EAGINE_ASSERT(index < glyph_count);
239  _cel_val = to_cell_type(index);
240  return *this;
241  }
242 
243  constexpr auto add(unsigned index) noexcept -> auto& {
244  EAGINE_ASSERT(index < glyph_count);
245  _cel_val |= to_cell_type(index);
246  return *this;
247  }
248 
249  template <typename Function>
250  void for_each_alternative(Function func) const noexcept {
251 
252  for(auto index : integer_range(glyph_count)) {
253  const auto mask = to_cell_type(index);
254  if((_cel_val & mask) == mask) {
255  func(index);
256  }
257  }
258  }
259 
260  auto alternative_count() const noexcept -> unsigned {
261  unsigned count = 0U;
262  cell_type bits = _cel_val;
263  while(bits) {
264  bits &= (bits - 1U);
265  ++count;
266  }
267  return count;
268  }
269 
270 private:
271  friend class basic_sudoku_board<S, false>;
272  friend class basic_sudoku_board<S, true>;
273 
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} {}
277 
278  static constexpr auto _is_pot(cell_type v) noexcept {
279  return (v != 0U) && ((v & (v - 1U)) == 0U);
280  }
281 
282  cell_type _cel_val{0U};
283 };
284 //------------------------------------------------------------------------------
285 template <unsigned S, bool Tight>
286 class basic_sudoku_board {
287 public:
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;
294 
295  static constexpr auto invalid_coord() noexcept -> coord_type {
296  return {{S, S, S, S}};
297  }
298 
299  template <typename Function>
300  void for_each_coord(Function func) const noexcept {
301  for(auto by : integer_range(S)) {
302  for(auto bx : integer_range(S)) {
303  for(auto cy : integer_range(S)) {
304  for(auto cx : integer_range(S)) {
305  if(!func(coord_type{{bx, by, cx, cy}})) {
306  return;
307  }
308  }
309  }
310  }
311  }
312  }
313 
314  basic_sudoku_board(const board_traits& type) noexcept
315  : _type{type} {
316  for_each_coord([&](const auto& coord) {
317  set(coord, glyph_type());
318  return true;
319  });
320  }
321 
322  basic_sudoku_board(const basic_sudoku_board<S, !Tight>& that) noexcept
323  : _type{that._type}
324  , _blocks{that._blocks} {}
325 
326  auto type() const noexcept -> auto& {
327  return _type;
328  }
329 
330  friend auto operator<<(std::ostream& out, const This& that)
331  -> std::ostream& {
332  return that._type.get().print(out, that);
333  }
334 
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();
338 
339  for(auto cy : integer_range(S)) {
340  for(auto cx : integer_range(S)) {
341  if((cx != ccx) || (cy != ccy)) {
342  if(_cell_val({cbx, cby, cx, cy}) == cel_val) {
343  return false;
344  }
345  }
346  }
347  }
348 
349  for(auto bx : integer_range(S)) {
350  for(auto cx : integer_range(S)) {
351  if((bx != cbx) || (cx != ccx)) {
352  if(_cell_val({bx, cby, cx, ccy}) == cel_val) {
353  return false;
354  }
355  }
356  }
357  }
358 
359  for(auto by : integer_range(S)) {
360  for(auto cy : integer_range(S)) {
361  if((by != cby) || (cy != ccy)) {
362  if(_cell_val({cbx, by, ccx, cy}) == cel_val) {
363  return false;
364  }
365  }
366  }
367  }
368  return true;
369  }
370 
371  auto is_solved() noexcept {
372  bool result = true;
373  for_each_coord([&](const auto& coord) {
374  const auto value = get(coord);
375  if(!value.is_single()) {
376  result = false;
377  return false;
378  }
379  return true;
380  });
381  return result;
382  }
383 
384  auto has_empty() noexcept {
385  bool result = false;
386  for_each_coord([&](const auto& coord) {
387  if(get(coord).is_empty()) {
388  result = true;
389  return false;
390  }
391  return true;
392  });
393  return result;
394  }
395 
396  auto get(const coord_type& coord) const noexcept -> glyph_type {
397  return {_cell_val(coord), typename glyph_type::from_cell_value_tag{}};
398  }
399 
400  auto set(const coord_type& coord, glyph_type value) noexcept -> auto& {
401  _cell_ref(coord) = value.cell_value();
402  return *this;
403  }
404 
405  auto set_available_alternatives(const coord_type& coord) noexcept -> auto& {
406  glyph_type alternatives;
407  for(auto value : integer_range(glyph_count)) {
408  if(is_possible(coord, value)) {
409  alternatives.add(value);
410  }
411  }
412  return set(coord, alternatives);
413  }
414 
415  auto calculate_alternatives() noexcept -> auto& {
416  for_each_coord([&](const auto& coord) {
417  if(!get(coord).is_single()) {
418  set_available_alternatives(coord);
419  }
420  return true;
421  });
422  return *this;
423  }
424 
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)) {
431  min_alt = num_alt;
432  result = coord;
433  }
434  }
435  return true;
436  });
437  return result;
438  }
439 
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()) {
447  func(candidate);
448  }
449  }
450  });
451  }
452  }
453 
454  auto alternative_count() const noexcept -> unsigned {
455  unsigned count = 0U;
456  for_each_coord([&](const auto& coord) {
457  const auto& cell = get(coord);
458  if(!cell.is_single()) {
459  count += cell.alternative_count();
460  }
461  return true;
462  });
463  return count;
464  }
465 
466  using block_type = std::array<cell_type, glyph_count>;
467  using blocks_type = std::array<block_type, glyph_count>;
468 
469  auto get_block(unsigned bx, unsigned by) const noexcept
470  -> const block_type& {
471  return _block(_blocks, bx, by);
472  }
473 
474  auto set_block(unsigned bx, unsigned by, const block_type& block) noexcept
475  -> auto& {
476  _block(_blocks, bx, by) = block;
477  return *this;
478  }
479 
480  auto tight() const noexcept -> basic_sudoku_board<S, true> {
481  return {*this};
482  }
483 
484 private:
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);
488  }
489 
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);
493  }
494 
495  template <typename B>
496  static auto _cell(B& block, unsigned x, unsigned y) noexcept -> auto& {
497  return block[y * S + x];
498  }
499 
500  template <typename B>
501  static auto _block(B& blocks, unsigned x, unsigned y) noexcept -> auto& {
502  return blocks[y * S + x];
503  }
504 
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>>;
508 
509  std::reference_wrapper<const board_traits> _type;
510  blocks_type _blocks{};
511 };
512 //------------------------------------------------------------------------------
513 template <unsigned S>
514 class basic_sudoku_board_generator {
515 public:
516  using board_traits = basic_sudoku_board_traits<S>;
517  using board_type = basic_sudoku_board<S>;
518 
519  basic_sudoku_board_generator(const board_traits& traits)
520  : _traits{traits} {}
521 
522  auto generate(std::size_t count) -> board_type {
523  board_type result{_traits};
524  result.calculate_alternatives();
525 
526  while(count) {
527  const typename board_type::coord_type coord{
528  _coord_dist(_rd),
529  _coord_dist(_rd),
530  _coord_dist(_rd),
531  _coord_dist(_rd)};
532 
533  if(!result.get(coord).is_single()) {
534  while(true) {
535  const auto value{_glyph_dist(_rd)};
536  if(result.is_possible(coord, value)) {
537  result.set(coord, value).calculate_alternatives();
538  --count;
539  break;
540  }
541  }
542  }
543  }
544 
545  return result;
546  }
547 
548  auto generate_one() {
549  return generate(1);
550  }
551 
552  auto generate_few() {
553  return generate(S + S);
554  }
555 
556  auto generate_medium() {
557  return generate(S * S + S + S);
558  }
559 
560  auto generate_many() {
561  return generate(S * S * S + S + S + S);
562  }
563 
564 private:
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};
569 };
570 //------------------------------------------------------------------------------
571 template <unsigned S>
572 auto basic_sudoku_board_traits<S>::make_diagonal() const -> board_type {
573  board_type result{*this};
574  unsigned g = 0;
575  for(auto b : integer_range(S)) {
576  for(auto c : integer_range(S)) {
577  result.set({{b, b, c, c}}, g++);
578  }
579  }
580  return result.calculate_alternatives();
581 }
582 //------------------------------------------------------------------------------
583 template <unsigned S>
584 auto basic_sudoku_board_traits<S>::make_generator() const -> generator {
585  return {*this};
586 }
587 //------------------------------------------------------------------------------
588 template <unsigned S>
589 auto basic_sudoku_board_traits<S>::print(
590  std::ostream& out,
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()) {
595  out << _empty_glyph;
596  } else {
597  out << _multi_glyphs[0U];
598  }
599  return out;
600 }
601 //------------------------------------------------------------------------------
602 template <unsigned S>
603 auto basic_sudoku_board_traits<S>::print(
604  std::ostream& out,
605  const basic_sudoku_board<S, false>& board) const -> std::ostream& {
606  for(auto by : integer_range(S)) {
607  for(auto cy : integer_range(S)) {
608  for(auto bx : integer_range(S)) {
609  for(auto cx : integer_range(S)) {
610  print(out << ' ', board.get({{bx, by, cx, cy}}));
611  }
612  if(bx + 1 < S) {
613  out << " |";
614  }
615  }
616  out << '\n';
617  }
618  if(by + 1 < S) {
619  for(auto s1 : integer_range(S)) {
620  for(auto s2 : integer_range(S)) {
621  if(s1 == 0 && s2 == 0) {
622  out << " -";
623  } else {
624  out << "--";
625  }
626  }
627  if(s1 + 1 < S) {
628  out << "-+";
629  }
630  }
631  out << '\n';
632  }
633  }
634  return out;
635 }
636 //------------------------------------------------------------------------------
637 template <unsigned S>
638 auto basic_sudoku_board_traits<S>::print(
639  std::ostream& out,
640  const basic_sudoku_board<S, true>& board) const -> std::ostream& {
641  for(auto by : integer_range(S)) {
642  for(auto cy : integer_range(S)) {
643  for(auto bx : integer_range(S)) {
644  for(auto cx : integer_range(S)) {
645  print(out, board.get({{bx, by, cx, cy}}));
646  }
647  }
648  out << '\n';
649  }
650  }
651  return out;
652 }
653 //------------------------------------------------------------------------------
654 template <unsigned S>
655 class basic_sudoku_solver {
656 public:
657  using board_type = basic_sudoku_board<S>;
658 
659  auto solve(board_type board) -> board_type {
660  std::stack<board_type> solutions;
661  solutions.push(board);
662 
663  bool done = false;
664  while(!(solutions.empty() || done)) {
665  board = solutions.top();
666  solutions.pop();
667 
668  board.for_each_alternative(
669  board.find_unsolved(), [&](auto candidate) {
670  if(candidate.is_solved()) {
671  board = candidate;
672  done = true;
673  } else {
674  solutions.push(candidate);
675  }
676  });
677  }
678  return board;
679  }
680 };
681 //------------------------------------------------------------------------------
682 template <unsigned S>
683 class basic_sudoku_tiling;
684 //------------------------------------------------------------------------------
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);
689 
690 public:
691  basic_sudoku_tile_patch(span_size_t w, span_size_t h)
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));
697  }
698 
699  auto width() const noexcept -> span_size_t {
700  return _width;
701  }
702 
703  auto height() const noexcept -> span_size_t {
704  return _height;
705  }
706 
707  auto get(span_size_t x, span_size_t y) const noexcept -> unsigned {
708  EAGINE_ASSERT((x >= 0) && (x < width()));
709  EAGINE_ASSERT((y >= 0) && (y < height()));
710  return _cells[std_size(y * _width + x)];
711  }
712 
713  friend auto
714  operator<<(std::ostream& out, const basic_sudoku_tile_patch& that)
715  -> std::ostream& {
716  std::size_t k = 0;
717  for(auto y : integer_range(that._height)) {
718  for(auto x : integer_range(that._width)) {
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++]);
723  }
724  out << '\n';
725  }
726  return out;
727  }
728 
729 private:
730  friend class basic_sudoku_tiling<S>;
731 
732  span_size_t _width{0};
733  span_size_t _height{0};
734  std::vector<std::uint8_t> _cells;
735 };
736 //------------------------------------------------------------------------------
737 template <unsigned S>
738 class basic_sudoku_tiling : basic_sudoku_solver<S> {
739  static_assert(S > 2U);
740 
741 public:
742  using board_traits = basic_sudoku_board_traits<S>;
743  using board_type = basic_sudoku_board<S>;
744 
745  basic_sudoku_tiling(const board_traits& traits)
746  : _traits{traits} {
747  _boards.emplace(
748  std::make_tuple(0, 0), this->solve(traits.make_diagonal()));
749  }
750 
751  basic_sudoku_tiling(const board_traits& traits, board_type board)
752  : _traits{traits} {
753  _boards.emplace(std::make_tuple(0, 0), this->solve(std::move(board)));
754  }
755 
756  auto generate(int xmin, int ymin, int xmax, int ymax) -> auto& {
757  for(auto y : integer_range(ymin, ymax + 1)) {
758  for(auto x : integer_range(xmin, xmax + 1)) {
759  _get(x, y);
760  }
761  }
762  return *this;
763  }
764 
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));
769  std::size_t k = 0;
770  for(int y = ymax - 1; y >= ymin; --y) {
771  for(auto by : integer_range(1U, S - 1U)) {
772  for(auto cy : integer_range(S)) {
773  for(auto x : integer_range(xmin, xmax)) {
774  auto& board = _get(x, y);
775  for(auto bx : integer_range(1U, S - 1U)) {
776  for(auto cx : integer_range(S)) {
777  EAGINE_ASSERT(k < patch._cells.size());
778  patch._cells[k++] =
779  board.get({bx, by, cx, cy}).get_index();
780  }
781  }
782  }
783  }
784  }
785  }
786  return patch;
787  }
788 
789  auto print(std::ostream& out, int xmin, int ymin, int xmax, int ymax)
790  -> std::ostream& {
791  for(auto y : integer_range(ymin, ymax + 1)) {
792  for(auto by : integer_range(1U, S - 1U)) {
793  for(auto cy : integer_range(S)) {
794  for(auto x : integer_range(xmin, xmax + 1)) {
795  auto& board = _get(x, y);
796  for(auto bx : integer_range(1U, S - 1U)) {
797  for(auto cx : integer_range(S)) {
798  _traits.get().print(
799  out, board.get({bx, by, cx, cy}));
800  }
801  }
802  }
803  out << '\n';
804  }
805  }
806  }
807  return out;
808  }
809 
810 private:
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};
815  if(y > 0) {
816  if(x > 0) {
817  auto& left = _get(x - 1, y);
818  for(auto by : integer_range(S - 1U)) {
819  added.set_block(0U, by, left.get_block(S - 1U, by));
820  }
821  auto& down = _get(x, y - 1);
822  for(auto bx : integer_range(1U, S)) {
823  added.set_block(bx, S - 1U, down.get_block(bx, 0U));
824  }
825  pos = _emplace(x, y, added);
826  } else if(x < 0) {
827  auto& right = _get(x + 1, y);
828  for(auto by : integer_range(S - 1U)) {
829  added.set_block(S - 1U, by, right.get_block(0U, by));
830  }
831  auto& down = _get(x, y - 1);
832  for(auto bx : integer_range(S - 1U)) {
833  added.set_block(bx, S - 1U, down.get_block(bx, 0U));
834  }
835  pos = _emplace(x, y, added);
836  } else {
837  auto& down = _get(x, y - 1);
838  for(auto bx : integer_range(S)) {
839  added.set_block(bx, S - 1U, down.get_block(bx, 0U));
840  }
841  pos = _emplace(x, y, added);
842  }
843  } else if(y < 0) {
844  if(x > 0) {
845  auto& left = _get(x - 1, y);
846  for(auto by : integer_range(1U, S)) {
847  added.set_block(0U, by, left.get_block(S - 1U, by));
848  }
849  auto& up = _get(x, y + 1);
850  for(auto bx : integer_range(1U, S)) {
851  added.set_block(bx, 0U, up.get_block(bx, S - 1U));
852  }
853  pos = _emplace(x, y, added);
854  } else if(x < 0) {
855  auto& right = _get(x + 1, y);
856  for(auto by : integer_range(1U, S)) {
857  added.set_block(S - 1U, by, right.get_block(0U, by));
858  }
859  auto& up = _get(x, y + 1);
860  for(auto bx : integer_range(S - 1U)) {
861  added.set_block(bx, 0U, up.get_block(bx, S - 1U));
862  }
863  pos = _emplace(x, y, added);
864  } else {
865  auto& up = _get(x, y + 1);
866  for(auto bx : integer_range(S)) {
867  added.set_block(bx, 0U, up.get_block(bx, S - 1U));
868  }
869  pos = _emplace(x, y, added);
870  }
871  } else {
872  if(x > 0) {
873  auto& left = _get(x - 1, y);
874  for(auto by : integer_range(S)) {
875  added.set_block(0U, by, left.get_block(S - 1U, by));
876  }
877  pos = _emplace(x, y, added);
878  } else if(x < 0) {
879  auto& right = _get(x + 1, y);
880  for(auto by : integer_range(S)) {
881  added.set_block(S - 1U, by, right.get_block(0U, by));
882  }
883  pos = _emplace(x, y, added);
884  }
885  }
886  }
887  return pos->second;
888  }
889  auto _emplace(int x, int y, board_type board) {
890  return _boards
891  .emplace(
892  std::make_tuple(x, y), this->solve(board.calculate_alternatives()))
893  .first;
894  }
895 
896  std::reference_wrapper<const board_traits> _traits;
897  flat_map<std::tuple<int, int>, basic_sudoku_board<S>> _boards;
898 };
899 //------------------------------------------------------------------------------
900 } // namespace eagine
901 
902 #endif // EAGINE_SUDOKU_HPP
std::ptrdiff_t span_size_t
Signed span size type used by eagine.
Definition: types.hpp:36
Common code is placed in this namespace.
Definition: eagine.hpp:21
basic_block< false > block
Alias for non-const byte memory span.
Definition: block.hpp:27
static constexpr auto std_size(T v) noexcept
Converts argument to std size type.
Definition: types.hpp:52
static auto fill(basic_span< T, P, S > spn, const V &v) -> basic_span< T, P, S >
Fills a span with copies of the specified value.
Definition: span_algo.hpp:536
static auto generate(basic_span< T, P, S > spn, Generator gen) -> basic_span< T, P, S >
Fills a span with elements generated by a generator callable.
Definition: span_algo.hpp:584
integer_range(B, E) -> integer_range< std::common_type_t< B, E >>
Deduction guide for integer_range.
static auto operator<<(std::ostream &out, const identifier_name< M > &n) -> std::ostream &
Operator for writing identifier_name into output streams.
Definition: identifier.hpp:159

Copyright © 2015-2021 Matúš Chochlík.
<chochlik -at -gmail.com>
Documentation generated on Tue Apr 13 2021 by Doxygen (version 1.8.17).