1
2
3
4
32
33
39
40 package main
41
42 import (
43 "flag"
44 "fmt"
45 )
46
47 var max_solutions = flag.Int("n", 2100, "maximum number of solutions")
48
49 func boolInt(b bool) int8 {
50 if b {
51 return 1
52 }
53 return 0
54 }
55
56
67
68 var board uint64 = 0xFFFC000000000000
69
70
90
91 const (
92 E = iota
93 ESE
94 SE
95 S
96 SW
97 WSW
98 W
99 WNW
100 NW
101 N
102 NE
103 ENE
104 PIVOT
105 )
106
107 var piece_def = [10][4]int8{
108 [4]int8{E, E, E, SE},
109 [4]int8{SE, E, NE, E},
110 [4]int8{E, E, SE, SW},
111 [4]int8{E, E, SW, SE},
112 [4]int8{SE, E, NE, S},
113 [4]int8{E, E, SW, E},
114 [4]int8{E, SE, SE, NE},
115 [4]int8{E, SE, SE, W},
116 [4]int8{E, SE, E, E},
117 [4]int8{E, E, E, SW},
118 }
119
120
130 var (
131 pieces [10][50][12]uint64
132 piece_counts [10][50]int
133 next_cell [10][50][12]int8
134 )
135
136
137 func rotate(dir int8) int8 { return (dir + 2) % PIVOT }
138
139
140 func flip(dir int8) int8 { return (PIVOT - dir) % PIVOT }
141
142
147 func shift(cell, dir int8) int8 {
148 switch dir {
149 case E:
150 return cell + 1
151 case ESE:
152 if ((cell / 5) % 2) != 0 {
153 return cell + 7
154 } else {
155 return cell + 6
156 }
157 case SE:
158 if ((cell / 5) % 2) != 0 {
159 return cell + 6
160 } else {
161 return cell + 5
162 }
163 case S:
164 return cell + 10
165 case SW:
166 if ((cell / 5) % 2) != 0 {
167 return cell + 5
168 } else {
169 return cell + 4
170 }
171 case WSW:
172 if ((cell / 5) % 2) != 0 {
173 return cell + 4
174 } else {
175 return cell + 3
176 }
177 case W:
178 return cell - 1
179 case WNW:
180 if ((cell / 5) % 2) != 0 {
181 return cell - 6
182 } else {
183 return cell - 7
184 }
185 case NW:
186 if ((cell / 5) % 2) != 0 {
187 return cell - 5
188 } else {
189 return cell - 6
190 }
191 case N:
192 return cell - 10
193 case NE:
194 if ((cell / 5) % 2) != 0 {
195 return cell - 4
196 } else {
197 return cell - 5
198 }
199 case ENE:
200 if ((cell / 5) % 2) != 0 {
201 return cell - 3
202 } else {
203 return cell - 4
204 }
205 }
206 return cell
207 }
208
209
213 func out_of_bounds(cell, dir int8) bool {
214 switch dir {
215 case E:
216 return cell%5 == 4
217 case ESE:
218 i := cell % 10
219 return i == 4 || i == 8 || i == 9 || cell >= 45
220 case SE:
221 return cell%10 == 9 || cell >= 45
222 case S:
223 return cell >= 40
224 case SW:
225 return cell%10 == 0 || cell >= 45
226 case WSW:
227 i := cell % 10
228 return i == 0 || i == 1 || i == 5 || cell >= 45
229 case W:
230 return cell%5 == 0
231 case WNW:
232 i := cell % 10
233 return i == 0 || i == 1 || i == 5 || cell < 5
234 case NW:
235 return cell%10 == 0 || cell < 5
236 case N:
237 return cell < 10
238 case NE:
239 return cell%10 == 9 || cell < 5
240 case ENE:
241 i := cell % 10
242 return i == 4 || i == 8 || i == 9 || cell < 5
243 }
244 return false
245 }
246
247
248 func rotate_piece(piece int) {
249 for i := 0; i < 4; i++ {
250 piece_def[piece][i] = rotate(piece_def[piece][i])
251 }
252 }
253
254
255 func flip_piece(piece int) {
256 for i := 0; i < 4; i++ {
257 piece_def[piece][i] = flip(piece_def[piece][i])
258 }
259 }
260
261
262 func calc_cell_indices(cell []int8, piece int, index int8) {
263 cell[0] = index
264 for i := 1; i < 5; i++ {
265 cell[i] = shift(cell[i-1], piece_def[piece][i-1])
266 }
267 }
268
269
270 func cells_fit_on_board(cell []int8, piece int) bool {
271 return !out_of_bounds(cell[0], piece_def[piece][0]) &&
272 !out_of_bounds(cell[1], piece_def[piece][1]) &&
273 !out_of_bounds(cell[2], piece_def[piece][2]) &&
274 !out_of_bounds(cell[3], piece_def[piece][3])
275 }
276
277
281 func minimum_of_cells(cell []int8) int8 {
282 minimum := cell[0]
283 for i := 1; i < 5; i++ {
284 if cell[i] < minimum {
285 minimum = cell[i]
286 }
287 }
288 return minimum
289 }
290
291
295 func first_empty_cell(cell []int8, minimum int8) int8 {
296 first_empty := minimum
297 for first_empty == cell[0] || first_empty == cell[1] ||
298 first_empty == cell[2] || first_empty == cell[3] ||
299 first_empty == cell[4] {
300 first_empty++
301 }
302 return first_empty
303 }
304
305
308 func bitmask_from_cells(cell []int8) uint64 {
309 var piece_mask uint64
310 for i := 0; i < 5; i++ {
311 piece_mask |= 1 << uint(cell[i])
312 }
313 return piece_mask
314 }
315
316
319 func record_piece(piece int, minimum int8, first_empty int8, piece_mask uint64) {
320 pieces[piece][minimum][piece_counts[piece][minimum]] = piece_mask
321 next_cell[piece][minimum][piece_counts[piece][minimum]] = first_empty
322 piece_counts[piece][minimum]++
323 }
324
325
328 func fill_contiguous_space(board []int8, index int8) {
329 if board[index] == 1 {
330 return
331 }
332 board[index] = 1
333 if !out_of_bounds(index, E) {
334 fill_contiguous_space(board, shift(index, E))
335 }
336 if !out_of_bounds(index, SE) {
337 fill_contiguous_space(board, shift(index, SE))
338 }
339 if !out_of_bounds(index, SW) {
340 fill_contiguous_space(board, shift(index, SW))
341 }
342 if !out_of_bounds(index, W) {
343 fill_contiguous_space(board, shift(index, W))
344 }
345 if !out_of_bounds(index, NW) {
346 fill_contiguous_space(board, shift(index, NW))
347 }
348 if !out_of_bounds(index, NE) {
349 fill_contiguous_space(board, shift(index, NE))
350 }
351 }
352
353
359 func has_island(cell []int8, piece int) bool {
360 temp_board := make([]int8, 50)
361 var i int
362 for i = 0; i < 5; i++ {
363 temp_board[cell[i]] = 1
364 }
365 i = 49
366 for temp_board[i] == 1 {
367 i--
368 }
369 fill_contiguous_space(temp_board, int8(i))
370 c := 0
371 for i = 0; i < 50; i++ {
372 if temp_board[i] == 0 {
373 c++
374 }
375 }
376 if c == 0 || (c == 5 && piece == 8) || (c == 40 && piece == 8) ||
377 (c%5 == 0 && piece == 0) {
378 return false
379 }
380 return true
381 }
382
383
390 func calc_six_rotations(piece, index int) {
391 cell := make([]int8, 5)
392 for rotation := 0; rotation < 6; rotation++ {
393 if piece != 3 || rotation < 3 {
394 calc_cell_indices(cell, piece, int8(index))
395 if cells_fit_on_board(cell, piece) && !has_island(cell, piece) {
396 minimum := minimum_of_cells(cell)
397 first_empty := first_empty_cell(cell, minimum)
398 piece_mask := bitmask_from_cells(cell)
399 record_piece(piece, minimum, first_empty, piece_mask)
400 }
401 }
402 rotate_piece(piece)
403 }
404 }
405
406
407 func calc_pieces() {
408 for piece := 0; piece < 10; piece++ {
409 for index := 0; index < 50; index++ {
410 calc_six_rotations(piece, index)
411 flip_piece(piece)
412 calc_six_rotations(piece, index)
413 }
414 }
415 }
416
417
422 const (
423 ROW_MASK = 0x1F
424 TRIPLE_MASK = 0x7FFF
425 )
426
427 var (
428 all_rows = [32]int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
429 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
430 }
431 bad_even_rows [32][32]int8
432 bad_odd_rows [32][32]int8
433 bad_even_triple [32768]int8
434 bad_odd_triple [32768]int8
435 )
436
437 func rows_bad(row1, row2 int8, even bool) int8 {
438
439 var row2_shift int8
440
441 if even {
442 row2_shift = ((row2 << 1) & ROW_MASK) | 0x01
443 } else {
444 row2_shift = (row2 >> 1) | 0x10
445 }
446 block := ((row1 ^ row2) & row2) & ((row1 ^ row2_shift) & row2_shift)
447
448 in_zeroes := false
449 group_okay := false
450 for i := uint8(0); i < 5; i++ {
451 if row1&(1<<i) != 0 {
452 if in_zeroes {
453 if !group_okay {
454 return 1
455 }
456 in_zeroes = false
457 group_okay = false
458 }
459 } else {
460 if !in_zeroes {
461 in_zeroes = true
462 }
463 if (block & (1 << i)) == 0 {
464 group_okay = true
465 }
466 }
467 }
468 if in_zeroes {
469 return boolInt(!group_okay)
470 }
471 return 0
472 }
473
474
478 func triple_is_okay(row1, row2, row3 int, even bool) bool {
479 if even {
480
485 return ((row1 == 0x03) && (row2 == 0x0B) && ((row3 & 0x1C) == 0x0C)) ||
486 ((row1 == 0x01) && (row2 == 0x05) && (row3 == 0x06)) ||
487 ((row1 == 0x19) && (row2 == 0x11)) ||
488 ((row1 == 0x15) && (row2 == 0x11))
489 }
490
495 return ((row1 == 0x13) && (row2 == 0x11)) ||
496 ((row1 == 0x15) && (row2 == 0x11))
497 }
498
499 func calc_rows() {
500 for row1 := int8(0); row1 < 32; row1++ {
501 for row2 := int8(0); row2 < 32; row2++ {
502 bad_even_rows[row1][row2] = rows_bad(row1, row2, true)
503 bad_odd_rows[row1][row2] = rows_bad(row1, row2, false)
504 }
505 }
506 for row1 := 0; row1 < 32; row1++ {
507 for row2 := 0; row2 < 32; row2++ {
508 for row3 := 0; row3 < 32; row3++ {
509 result1 := bad_even_rows[row1][row2]
510 result2 := bad_odd_rows[row2][row3]
511 if result1 == 0 && result2 != 0 && triple_is_okay(row1, row2, row3, true) {
512 bad_even_triple[row1+(row2*32)+(row3*1024)] = 0
513 } else {
514 bad_even_triple[row1+(row2*32)+(row3*1024)] = boolInt(result1 != 0 || result2 != 0)
515 }
516
517 result1 = bad_odd_rows[row1][row2]
518 result2 = bad_even_rows[row2][row3]
519 if result1 == 0 && result2 != 0 && triple_is_okay(row1, row2, row3, false) {
520 bad_odd_triple[row1+(row2*32)+(row3*1024)] = 0
521 } else {
522 bad_odd_triple[row1+(row2*32)+(row3*1024)] = boolInt(result1 != 0 || result2 != 0)
523 }
524 }
525 }
526 }
527 }
528
529
531 func boardHasIslands(cell int8) int8 {
532
533 if cell >= 40 {
534 return 0
535 }
536 current_triple := (board >> uint((cell/5)*5)) & TRIPLE_MASK
537 if (cell/5)%2 != 0 {
538 return bad_odd_triple[current_triple]
539 }
540 return bad_even_triple[current_triple]
541 }
542
543
549 var (
550 avail uint16 = 0x03FF
551 sol_nums [10]int8
552 sol_masks [10]uint64
553 solutions [2100][50]int8
554 solution_count = 0
555 )
556
557 func record_solution() {
558 for sol_no := 0; sol_no < 10; sol_no++ {
559 sol_mask := sol_masks[sol_no]
560 for index := 0; index < 50; index++ {
561 if sol_mask&1 == 1 {
562 solutions[solution_count][index] = sol_nums[sol_no]
563
564 solutions[solution_count+1][49-index] = sol_nums[sol_no]
565 }
566 sol_mask = sol_mask >> 1
567 }
568 }
569 solution_count += 2
570 }
571
572 func solve(depth, cell int8) {
573 if solution_count >= *max_solutions {
574 return
575 }
576
577 for board&(1<<uint(cell)) != 0 {
578 cell++
579 }
580
581 for piece := int8(0); piece < 10; piece++ {
582 var piece_no_mask uint16 = 1 << uint(piece)
583 if avail&piece_no_mask == 0 {
584 continue
585 }
586 avail ^= piece_no_mask
587 max_rots := piece_counts[piece][cell]
588 piece_mask := pieces[piece][cell]
589 for rotation := 0; rotation < max_rots; rotation++ {
590 if board&piece_mask[rotation] == 0 {
591 sol_nums[depth] = piece
592 sol_masks[depth] = piece_mask[rotation]
593 if depth == 9 {
594
595 record_solution()
596 avail ^= piece_no_mask
597 return
598 }
599 board |= piece_mask[rotation]
600 if boardHasIslands(next_cell[piece][cell][rotation]) == 0 {
601 solve(depth+1, next_cell[piece][cell][rotation])
602 }
603 board ^= piece_mask[rotation]
604 }
605 }
606 avail ^= piece_no_mask
607 }
608 }
609
610
611 func pretty(b *[50]int8) {
612 for i := 0; i < 50; i += 10 {
613 fmt.Printf("%c %c %c %c %c \n %c %c %c %c %c \n", b[i]+'0', b[i+1]+'0',
614 b[i+2]+'0', b[i+3]+'0', b[i+4]+'0', b[i+5]+'0', b[i+6]+'0',
615 b[i+7]+'0', b[i+8]+'0', b[i+9]+'0')
616 }
617 fmt.Printf("\n")
618 }
619
620
621 func smallest_largest() (smallest, largest *[50]int8) {
622 smallest = &solutions[0]
623 largest = &solutions[0]
624 for i := 1; i < solution_count; i++ {
625 candidate := &solutions[i]
626 for j, s := range *smallest {
627 c := candidate[j]
628 if c == s {
629 continue
630 }
631 if c < s {
632 smallest = candidate
633 }
634 break
635 }
636 for j, s := range *largest {
637 c := candidate[j]
638 if c == s {
639 continue
640 }
641 if c > s {
642 largest = candidate
643 }
644 break
645 }
646 }
647 return
648 }
649
650 func main() {
651 flag.Parse()
652 calc_pieces()
653 calc_rows()
654 solve(0, 0)
655 fmt.Printf("%d solutions found\n\n", solution_count)
656 smallest, largest := smallest_largest()
657 pretty(smallest)
658 pretty(largest)
659 }
660
View as plain text