-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcrossword_generation.hpp
2035 lines (1860 loc) · 72.9 KB
/
crossword_generation.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// (Japanese, UTF-8)
#pragma once
#define CROSSWORD_GENERATION 25 // crossword_generation version
#define _GNU_SOURCE
#include <cstdio> // 標準入出力。
#include <cstdint> // 標準整数。
#include <ctime> // 時間。
#include <cassert> // assertion。
#include <vector> // std::vector
#include <unordered_set> // std::unordered_set
#include <unordered_map> // std::unordered_map
#include <queue> // 待ち行列(std::queue)
#include <thread> // std::thread
#include <mutex> // std::mutex
#include <algorithm> // std::shuffle
#include <utility> // ????
#include <random> // 新しい乱数生成。
#ifdef _WIN32
#include <windows.h> // Windowsヘッダ。
#else
// Linux/Mac用の定義。
#include <unistd.h>
#include <sys/types.h>
inline uint64_t GetTickCount64(void) {
using namespace std;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t tick = ts.tv_nsec / 1000000;
tick += ts.tv_sec * 1000;
return tick;
}
inline unsigned GetCurrentThreadId(void) {
using namespace std;
return gettid();
}
#endif
// マスの位置を定義する。
namespace crossword_generation {
struct pos_t {
int m_x, m_y;
constexpr pos_t(int x, int y) noexcept : m_x(x), m_y(y) { }
constexpr bool operator==(const pos_t& pos) const noexcept {
return m_x == pos.m_x && m_y == pos.m_y;
}
};
} // namespace crossword_generation
namespace std {
template <>
struct hash<crossword_generation::pos_t> {
size_t operator()(const crossword_generation::pos_t& pos) const noexcept {
return static_cast<uint16_t>(pos.m_x) | (static_cast<uint16_t>(pos.m_y) << 16);
}
};
} // namespace std
// クロスワード生成用の名前空間。現状は「単語群から自動生成」のみ実装。
// 将来的にはその他の生成方法もこのようなモダンな方向に移行する。
namespace crossword_generation {
inline static bool s_generated = false; // 生成済みか?
inline static bool s_canceled = false; // キャンセルされたか?
inline static std::mutex s_mutex; // ミューテックス(排他処理用)。
// ルールを表すフラグ群。
struct RULES {
enum {
DONTDOUBLEBLACK = (1 << 0), // 連黒禁。
DONTCORNERBLACK = (1 << 1), // 四隅黒禁。
DONTTRIDIRECTIONS = (1 << 2), // 三方黒禁。
DONTDIVIDE = (1 << 3), // 分断禁。
DONTFOURDIAGONALS = (1 << 4), // 黒斜四連禁。
POINTSYMMETRY = (1 << 5), // 黒マス点対称。
DONTTHREEDIAGONALS = (1 << 6), // 黒斜三連禁。
LINESYMMETRYV = (1 << 7), // 黒マス上下対称。
LINESYMMETRYH = (1 << 8), // 黒マス左右対称。
};
};
// 文字マスか?
template <typename t_char>
inline bool is_letter(t_char ch) noexcept {
return (ch != '#' && ch != '?');
}
// プロセッサの数を返す関数。
inline uint32_t get_num_processors(void) noexcept {
#ifdef XWORDGIVER
return xg_dwThreadCount;
#elif defined(_WIN32)
SYSTEM_INFO info;
::GetSystemInfo(&info);
return info.dwNumberOfProcessors;
#else
return std::thread::hardware_concurrency();
#endif
}
// replacement of std::random_shuffle
// 従来型の乱数生成(std::rand、std::random_shuffle)は推奨されない。
template <typename t_elem>
inline void random_shuffle(const t_elem& begin, const t_elem& end) {
#ifndef NO_RANDOM
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(begin, end, g);
#endif
}
// 生成前に初期化。
inline void reset() noexcept {
s_generated = s_canceled = false;
#ifdef XWORDGIVER
for (auto& info : xg_aThreadInfo) {
info.m_count = 0;
}
#endif
}
// 連結性を判定。
template <typename t_string>
inline bool
check_connectivity(const std::unordered_set<t_string>& words,
t_string& nonconnected)
{
if (words.size() <= 1)
return true;
std::vector<t_string> vec_words(words.begin(), words.end()); // 単語群。
std::queue<size_t> queue; // 待ち行列。
std::unordered_set<size_t> indexes;
queue.emplace(0); // 待ち行列に初期の種を添える。。
while (!queue.empty()) {
const size_t index0 = queue.front();
indexes.insert(index0);
queue.pop(); // 種を取り除く。
auto& w0 = vec_words[index0];
for (size_t index1 = 0; index1 < vec_words.size(); ++index1) {
if (index0 == index1)
continue;
auto& w1 = vec_words[index1];
for (auto ch0 : w0) {
for (auto ch1 : w1) {
if (ch0 == ch1) {
if (indexes.count(index1) == 0) {
queue.emplace(index1);
goto skip;
}
}
}
}
skip:;
}
}
for (size_t i = 0; i < vec_words.size(); ++i) {
if (indexes.count(i) == 0) {
nonconnected = vec_words[i];
return false;
}
}
return true;
}
// 候補。位置情報と単語と縦横の向きの情報を持つ。
template <typename t_string>
struct candidate_t {
int m_x, m_y;
t_string m_word;
bool m_vertical;
};
// 交差候補。
template <typename t_string>
struct cross_candidate_t {
candidate_t<t_string> m_cand_x, m_cand_y;
cross_candidate_t() {
m_cand_x.m_vertical = false;
m_cand_y.m_vertical = true;
}
};
// 盤面データ。中身は文字列。配置方法はサブクラス board_t によって決まる。
template <typename t_string>
struct board_data_t {
using t_char = typename t_string::value_type;
t_string m_data;
// コンストラクタによる初期化。文字で埋める。
board_data_t(int cx = 1, int cy = 1, t_char ch = ' ') {
resize(cx, cy, ch);
}
// マス数。
int size() const noexcept {
return static_cast<int>(m_data.size());
}
// マス数の増減。
void resize(int cx, int cy, t_char ch = ' ') {
m_data.assign(cx * cy, ch);
}
// 特定の文字で埋める。
void fill(t_char ch = ' ') {
std::fill(m_data.begin(), m_data.end(), ch);
}
// 文字を置換により置き換える。
void replace(t_char chOld, t_char chNew) {
std::replace(m_data.begin(), m_data.end(), chOld, chNew);
}
// 特定の文字の個数を数える。
int count(t_char ch) const noexcept {
int ret = 0;
for (int xy = 0; xy < size(); ++xy) {
if (m_data[xy] == ch)
++ret;
}
return ret;
}
// 空か?
bool is_empty() const noexcept {
return count('?') == size();
}
// 未知のマスがないか?
bool is_full() const noexcept {
return count('?') == 0;
}
// 文字マスがあるか。
bool has_letter() const noexcept {
for (int xy = 0; xy < size(); ++xy) {
if (is_letter(m_data[xy]))
return true;
}
return false;
}
};
// board_data_t のサブクラス。盤面データ。継承されて文字の配置が規定されている。
template <typename t_string, bool t_fixed>
struct board_t : board_data_t<t_string> {
using t_char = typename t_string::value_type;
using self_type = board_t<t_string, t_fixed>;
using super_type = board_data_t<t_string>;
int m_cx, m_cy; // サイズ。
int m_rules; // ルール群。
int m_x0, m_y0;
// board_tのコンストラクタ。
board_t(int cx = 1, int cy = 1, t_char ch = ' ', int rules = 0, int x0 = 0, int y0 = 0) noexcept
: super_type(cx, cy, ch), m_cx(cx), m_cy(cy)
, m_rules(rules), m_x0(x0), m_y0(y0)
{
}
board_t(const self_type& b) = default;
self_type& operator=(const self_type& b) = default;
// インデックス位置の文字を返す。
t_char get(int xy) const {
if (0 <= xy && xy < this->size())
return super_type::m_data[xy];
return t_fixed ? '#' : '?';
}
// インデックス位置に文字をセット。
void set(int xy, t_char ch) {
if (0 <= xy && xy < this->size())
super_type::m_data[xy] = ch;
}
// マス(x, y)は盤面の範囲内か?
// x, y: absolute coordinate
bool in_range(int x, int y) const noexcept {
return (0 <= x && x < m_cx && 0 <= y && y < m_cy);
}
// マス(x, y)を取得する。範囲チェックあり。
// x, y: absolute coordinate
t_char real_get_at(int x, int y) const noexcept {
if (in_range(x, y))
return super_type::m_data[y * m_cx + x];
return ' ';
}
// マス(x, y)を取得する。範囲チェックあり。範囲外なら'#'か'?'を返す。
// x, y: absolute coordinate
t_char get_at(int x, int y) const noexcept {
if (in_range(x, y))
return super_type::m_data[y * m_cx + x];
return t_fixed ? '#' : '?';
}
// マス(x, y)をセットする。範囲チェックあり。範囲外なら無視。
// x, y: absolute coordinate
void set_at(int x, int y, t_char ch) noexcept {
if (in_range(x, y))
super_type::m_data[y * m_cx + x] = ch;
}
// マス(x, y)をセットする。ただしルールにより反射する。範囲チェックあり。範囲外なら無視。
// x, y: absolute coordinate
void mirror_set_black_at(int x, int y) {
if (!in_range(x, y))
return;
set_at(x, y, '#');
if (m_rules == 0) {
return;
}
if (m_rules & RULES::POINTSYMMETRY) {
set_at(m_cx - (x + 1), m_cy - (y + 1), '#');
} else if (m_rules & RULES::LINESYMMETRYV) {
set_at(m_cx - (x + 1), y, '#');
} else if (m_rules & RULES::LINESYMMETRYH) {
set_at(x, m_cy - (y + 1), '#');
}
}
// ルールにより黒マスを反射する。
void do_mirror() {
if (m_rules == 0)
return;
if (m_rules & RULES::POINTSYMMETRY) {
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx; ++x) {
if (get_at(x, y) == '#') {
set_at(m_cx - (x + 1), m_cy - (y + 1), '#');
}
}
}
} else if (m_rules & RULES::LINESYMMETRYV) {
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx; ++x) {
if (get_at(x, y) == '#') {
set_at(m_cx - (x + 1), y, '#');
}
}
}
} else if (m_rules & RULES::LINESYMMETRYH) {
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx; ++x) {
if (get_at(x, y) == '#') {
set_at(x, m_cy - (y + 1), '#');
}
}
}
}
}
// マス(x, y)から横向きパターンを取得する。
// x, y: absolute coordinate
t_string get_pat_x(int x, int y, int *px0 = nullptr) const {
t_string pat;
if (!in_range(x, y) || get_at(x, y) == '#')
return pat;
int x0, x1;
x0 = x1 = x;
while (get_at(x0 - 1, y) != '#') {
--x0;
}
while (get_at(x1 + 1, y) != '#') {
++x1;
}
for (int x2 = x0; x2 <= x1; ++x2) {
pat += get_at(x2, y);
}
if (px0)
*px0 = x0;
return pat;
}
// マス(x, y)から縦向きパターンを取得する。
// x, y: absolute coordinate
t_string get_pat_y(int x, int y, int *py0 = nullptr) const {
t_string pat;
if (!in_range(x, y) || get_at(x, y) == '#')
return pat;
int y0, y1;
y0 = y1 = y;
while (get_at(x, y0 - 1) != '#') {
--y0;
}
while (get_at(x, y1 + 1) != '#') {
++y1;
}
for (int y2 = y0; y2 <= y1; ++y2) {
pat += get_at(x, y2);
}
if (py0)
*py0 = y0;
return pat;
}
// マス(x, y)は、コーナー(四隅)か?
bool is_corner(int x, int y) const {
if (y == 0 || y == m_cy - 1) {
if (x == 0 || x == m_cx - 1)
return true;
}
return false;
}
// マス(x, y)に黒マスを置くと、連黒禁に抵触するか?
bool can_make_double_black(int x, int y) const {
return (real_get_at(x - 1, y) == '#' || real_get_at(x + 1, y) == '#' ||
real_get_at(x, y - 1) == '#' || real_get_at(x, y + 1) == '#');
}
bool can_make_tri_direction(int x, int y) {
auto ch = get_at(x, y);
set_at(x, y, '#');
bool ret = false;
for (int i = y - 4; i <= y + 4; ++y) {
for (int j = x - 4; i <= x + 4; ++i) {
int sum = 0;
sum += (real_get_at(j, i - 1) == '#');
sum += (real_get_at(j, i + 1) == '#');
sum += (real_get_at(j - 1, i) == '#');
sum += (real_get_at(j + 1, i) == '#');
if (sum >= 3) {
ret = true;
goto skip;
}
}
}
skip:;
set_at(x, y, ch);
return ret;
}
// マス(x, y)に黒マスがあるとき、黒斜三連禁に抵触するか?
bool can_make_three_diagonals(int x, int y) const {
// center (right down)
if (real_get_at(x - 1, y - 1) == '#' || real_get_at(x + 1, y + 1) == '#') {
return true;
}
// center (right up)
if (real_get_at(x + 1, y - 1) == '#' || real_get_at(x - 1, y + 1) == '#') {
return true;
}
// upper left
if (real_get_at(x - 2, y - 2) == '#' || real_get_at(x - 1, y - 1) == '#') {
return true;
}
// lower right
if (real_get_at(x + 1, y + 1) == '#' || real_get_at(x + 2, y + 2) == '#') {
return true;
}
// upper right
if (real_get_at(x + 2, y - 2) == '#' || real_get_at(x + 1, y - 1) == '#') {
return true;
}
// lower left
if (real_get_at(x - 2, y + 1) == '#' || real_get_at(x - 1, y + 2) == '#') {
return true;
}
return false;
}
// マス(x, y)に黒マスがあるとき、黒斜四連禁に抵触するか?
bool can_make_four_diagonals(int x, int y) {
auto ch = get_at(x, y);
set_at(x, y, '#'); // 一時的にセット。後で戻す。
bool ret = false;
for (int i = y - 3; i <= y + 3; ++y) {
for (int j = x - 3; i <= x + 3; ++i) {
if (real_get_at(j, i) != '#')
continue;
if (real_get_at(j + 1, i + 1) != '#')
continue;
if (real_get_at(j + 2, i + 2) != '#')
continue;
if (real_get_at(j + 3, i + 3) != '#')
continue;
ret = true;
goto skip;
}
}
for (int i = y - 3; i <= y + 3; ++y) {
for (int j = x - 3; i <= x + 3; ++i) {
if (real_get_at(j, i) != '#')
continue;
if (real_get_at(j - 1, i + 1) != '#')
continue;
if (real_get_at(j - 2, i + 2) != '#')
continue;
if (real_get_at(j - 3, i + 3) != '#')
continue;
ret = true;
goto skip;
}
}
skip:;
set_at(x, y, ch); // 元に戻す。
return ret;
}
// マス(x, y)に黒マスをセットできるかどうか?
// NOTE: This method doesn't check divided_by_black.
bool can_set_black_at(int x, int y) {
if (get_at(x, y) == '#')
return true;
if (!in_range(x, y) || get_at(x, y) != '?')
return false;
if (m_rules == 0) {
return true;
}
if (m_rules & RULES::DONTCORNERBLACK) {
if (is_corner(x, y))
return false;
}
if (m_rules & RULES::DONTDOUBLEBLACK) {
if (m_rules & RULES::POINTSYMMETRY) {
if (can_make_double_black(x, y) ||
can_make_double_black(m_cx - (x + 1), m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYV) {
if (can_make_double_black(x, y) ||
can_make_double_black(x, m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYH) {
if (can_make_double_black(x, y) ||
can_make_double_black(m_cx - (x + 1), y))
{
return false;
}
} else {
if (can_make_double_black(x, y))
return false;
}
}
if (m_rules & RULES::DONTTRIDIRECTIONS) {
if (m_rules & RULES::POINTSYMMETRY) {
if (can_make_tri_direction(x, y) ||
can_make_tri_direction(m_cx - (x + 1), m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYV) {
if (can_make_tri_direction(x, y) ||
can_make_tri_direction(x, m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYH) {
if (can_make_tri_direction(x, y) ||
can_make_tri_direction(m_cx - (x + 1), y))
{
return false;
}
} else {
if (can_make_tri_direction(x, y))
return false;
}
}
if (m_rules & RULES::DONTTHREEDIAGONALS) {
if (m_rules & RULES::POINTSYMMETRY) {
if (can_make_three_diagonals(x, y) ||
can_make_three_diagonals(m_cx - (x + 1), m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYV) {
if (can_make_three_diagonals(x, y) ||
can_make_three_diagonals(x, m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYH) {
if (can_make_three_diagonals(x, y) ||
can_make_three_diagonals(m_cx - (x + 1), y))
{
return false;
}
} else {
if (can_make_three_diagonals(x, y))
return false;
}
} else if (m_rules & RULES::DONTFOURDIAGONALS) {
if (m_rules & RULES::POINTSYMMETRY) {
if (can_make_four_diagonals(x, y) ||
can_make_four_diagonals(m_cx - (x + 1), m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYV) {
if (can_make_four_diagonals(x, y) ||
can_make_four_diagonals(x, m_cy - (y + 1)))
{
return false;
}
} else if (m_rules & RULES::LINESYMMETRYH) {
if (can_make_four_diagonals(x, y) ||
can_make_four_diagonals(m_cx - (x + 1), y))
{
return false;
}
} else {
if (can_make_four_diagonals(x, y))
return false;
}
}
if (m_rules & RULES::POINTSYMMETRY) {
auto ch = real_get_at(m_cx - (x + 1), m_cy - (y + 1));
if (is_letter(ch))
return false;
} else if (m_rules & RULES::LINESYMMETRYV) {
auto ch = real_get_at(x, m_cy - (y + 1));
if (is_letter(ch))
return false;
} else if (m_rules & RULES::LINESYMMETRYH) {
auto ch = real_get_at(m_cx - (x + 1), y);
if (is_letter(ch))
return false;
} else {
;
}
return true;
}
// 盤面が固定サイズならサイズに収まるかどうか判定し、
// 盤面が固定サイズでなければ、収まるように拡張する。
// x: relative coordinate
bool ensure_x(int x) {
if (t_fixed) {
return (0 <= x && x < m_cx);
} else {
if (x < m_x0) {
grow_x0(m_x0 - x, '?');
} else if (m_cx + m_x0 <= x) {
grow_x1(x - (m_cx + m_x0) + 1, '?');
}
return true;
}
}
// 盤面が固定サイズならサイズに収まるかどうか判定し、
// 盤面が固定サイズでなければ、収まるように拡張する。
// y: relative coordinate
bool ensure_y(int y) {
if (t_fixed) {
return (0 <= y && y < m_cy);
} else {
if (y < m_y0) {
grow_y0(m_y0 - y, '?');
} else if (m_cy + m_y0 <= y) {
grow_y1(y - (m_cy + m_y0) + 1, '?');
}
return true;
}
}
// 盤面が固定サイズならサイズに収まるかどうか判定し、
// 盤面が固定サイズでなければ、収まるように拡張する。
// x, y: relative coordinate
bool ensure(int x, int y) {
if (t_fixed) {
return in_range(x, y);
} else {
ensure_x(x);
ensure_y(y);
return true;
}
}
// マス(x, y)の文字を取得する。リリース時の範囲チェックなし。
// x, y: relative coordinate
t_char get_on(int x, int y) const noexcept {
assert(m_x0 <= 0);
assert(m_y0 <= 0);
return get_at(x - m_x0, y - m_y0);
}
// マス(x, y)の文字をセットする。リリース時の範囲チェックなし。
// x, y: relative coordinate
void set_on(int x, int y, t_char ch) noexcept {
assert(m_x0 <= 0);
assert(m_y0 <= 0);
set_at(x - m_x0, y - m_y0, ch);
}
// 列を挿入する。指定された文字と幅で。
// x0: absolute coordinate
void insert_x(int x0, int cx = 1, t_char ch = ' ') {
assert(0 <= x0 && x0 <= m_cx);
board_t<t_string, t_fixed> data(m_cx + cx, m_cy, ch);
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx; ++x) {
auto ch0 = get_at(x, y);
if (x < x0)
data.set_at(x, y, ch0);
else
data.set_at(x + cx, y, ch0);
}
}
this->m_data = std::move(data.m_data);
m_cx += cx;
}
// 行を挿入する。指定された文字と高さで。
// y0: absolute coordinate
void insert_y(int y0, int cy = 1, t_char ch = ' ') {
assert(0 <= y0 && y0 <= m_cy);
board_t<t_string, t_fixed> data(m_cx, m_cy + cy, ch);
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx; ++x) {
auto ch0 = get_at(x, y);
if (y < y0)
data.set_at(x, y, ch0);
else
data.set_at(x, y + cy, ch0);
}
}
this->m_data = std::move(data.m_data);
m_cy += cy;
}
// 列を削除する。
// x0: absolute coordinate
void delete_x(int x0) {
assert(0 <= x0 && x0 < m_cx);
board_t<t_string, t_fixed> data(m_cx - 1, m_cy, ' ');
for (int y = 0; y < m_cy; ++y) {
for (int x = 0; x < m_cx - 1; ++x) {
if (x < x0)
data.set_at(x, y, get_at(x, y));
else
data.set_at(x, y, get_at(x + 1, y));
}
}
this->m_data = std::move(data.m_data);
--m_cx;
}
// 行を削除する。
// y0: absolute coordinate
void delete_y(int y0) {
assert(0 <= y0 && y0 < m_cy);
board_t<t_string, t_fixed> data(m_cx, m_cy - 1, ' ');
for (int y = 0; y < m_cy - 1; ++y) {
for (int x = 0; x < m_cx; ++x) {
if (y < y0)
data.set_at(x, y, get_at(x, y));
else
data.set_at(x, y, get_at(x, y + 1));
}
}
this->m_data = std::move(data.m_data);
--m_cy;
}
// 左側に列を挿入する。
void grow_x0(int cx, t_char ch = ' ') {
assert(cx > 0);
insert_x(0, cx, ch);
m_x0 -= cx;
}
// 右側に列を挿入する。
void grow_x1(int cx, t_char ch = ' ') {
assert(cx > 0);
insert_x(m_cx, cx, ch);
}
// 上側に行を挿入する。
void grow_y0(int cy, t_char ch = ' ') {
assert(cy > 0);
insert_y(0, cy, ch);
m_y0 -= cy;
}
// 下側に行を挿入する。
void grow_y1(int cy, t_char ch = ' ') {
assert(cy > 0);
insert_y(m_cy, cy, ch);
}
// 文字マスが見つからない列を左右端からカットする。
void trim_x() {
bool found;
int x, y;
while (m_cx > 0) {
found = false;
x = 0;
for (y = 0; y < m_cy; ++y) {
t_char ch = get_at(x, y);
if (is_letter(ch)) {
found = true;
break;
}
}
if (!found) {
delete_x(0);
++m_x0;
} else {
break;
}
}
while (m_cx > 0) {
found = false;
x = m_cx - 1;
for (y = 0; y < m_cy; ++y) {
t_char ch = get_at(x, y);
if (is_letter(ch)) {
found = true;
break;
}
}
if (!found) {
delete_x(m_cx - 1);
} else {
break;
}
}
m_x0 = 0;
}
// 文字マスが見つからない行を上下端からカットする。
void trim_y() {
bool found;
int x, y;
while (m_cy > 0) {
found = false;
y = 0;
for (x = 0; x < m_cx; ++x) {
t_char ch = get_at(x, y);
if (is_letter(ch)) {
found = true;
break;
}
}
if (!found) {
delete_y(0);
} else {
break;
}
}
while (m_cy > 0) {
found = false;
y = m_cy - 1;
for (x = 0; x < m_cx; ++x) {
t_char ch = get_at(x, y);
if (is_letter(ch)) {
found = true;
break;
}
}
if (!found) {
delete_y(m_cy - 1);
} else {
break;
}
}
m_y0 = 0;
}
// 文字マスが見つからない行と列を端からカットする。
void trim() {
trim_y();
trim_x();
}
// 文字列として標準出力。デバッグ用。
void print() const {
std::printf("dx:%d, dy:%d, cx:%d, cy:%d\n", m_x0, m_y0, m_cx, m_cy);
for (int y = m_y0; y < m_y0 + m_cy; ++y) {
std::printf("%3d: ", y);
for (int x = m_x0; x < m_x0 + m_cx; ++x) {
auto ch = get_on(x, y);
std::putchar(ch);
}
std::printf("\n");
}
std::fflush(stdout);
}
// マス(x, y)は横向き交差可能か?
bool is_crossable_x(int x, int y) const noexcept {
assert(is_letter(get_on(x, y)));
t_char ch1, ch2;
ch1 = get_on(x - 1, y);
ch2 = get_on(x + 1, y);
return (ch1 == '?' || ch2 == '?');
}
// マス(x, y)は縦向き交差可能か?
bool is_crossable_y(int x, int y) const noexcept {
assert(is_letter(get_on(x, y)));
t_char ch1, ch2;
ch1 = get_on(x, y - 1);
ch2 = get_on(x, y + 1);
return (ch1 == '?' || ch2 == '?');
}
bool must_be_cross(int x, int y) const noexcept {
assert(is_letter(get_on(x, y)));
t_char ch1, ch2;
ch1 = get_on(x - 1, y);
ch2 = get_on(x + 1, y);
const bool flag1 = (is_letter(ch1) || is_letter(ch2));
ch1 = get_on(x, y - 1);
ch2 = get_on(x, y + 1);
const bool flag2 = (is_letter(ch1) || is_letter(ch2));
return flag1 && flag2;
}
// 候補に対して十分なサイズを確保する。
void apply_size(const candidate_t<t_string>& cand) {
auto& word = cand.m_word;
const int x = cand.m_x, y = cand.m_y;
if (cand.m_vertical) {
ensure(x, y - 1);
ensure(x, y + static_cast<int>(word.size()));
}
else {
ensure(x - 1, y);
ensure(x + static_cast<int>(word.size()), y);
}
}
// ルールに適合しているか?
bool rules_ok() const {
if (m_rules == 0)
return true;
if ((m_rules & RULES::DONTDOUBLEBLACK) && double_black())
return false;
if ((m_rules & RULES::DONTCORNERBLACK) && corner_black())
return false;
if ((m_rules & RULES::DONTTRIDIRECTIONS) && tri_black_around())
return false;
if ((m_rules & RULES::DONTTHREEDIAGONALS) && three_diagonals())
return false;
else if ((m_rules & RULES::DONTFOURDIAGONALS) && four_diagonals())
return false;
if ((m_rules & RULES::POINTSYMMETRY) && !is_point_symmetry()) {
return false;
} else {
if ((m_rules & RULES::LINESYMMETRYH) && !is_line_symmetry_h())
return false;
if ((m_rules & RULES::LINESYMMETRYV) && !is_line_symmetry_v())
return false;
}
if ((m_rules & RULES::DONTDIVIDE) && divided_by_black())
return false;
return true;
}
// 四隅黒禁。
bool corner_black() const noexcept {
return get_at(0, 0) == '#' ||
get_at(m_cx - 1, 0) == '#' ||
get_at(m_cx - 1, m_cy - 1) == '#' ||
get_at(0, m_cy - 1) == '#';
}
// 連黒禁。
bool double_black() const noexcept {
const int n1 = m_cx - 1;
const int n2 = m_cy - 1;
int i = m_cy;
for (--i; i >= 0; --i) {
for (int j = 0; j < n1; j++) {
if (get_at(j, i) == '#' && get_at(j + 1, i) == '#')
return true;
}
}
int j = m_cx;
for (--j; j >= 0; --j) {
for (int i0 = 0; i0 < n2; i0++) {