golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/shootout/meteor-contest.go (about) 1 //go:build ignore 2 // +build ignore 3 4 /* 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are met: 7 8 * Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 11 * Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 15 * Neither the name of "The Computer Language Benchmarks Game" nor the 16 name of "The Computer Language Shootout Benchmarks" nor the names of 17 its contributors may be used to endorse or promote products derived 18 from this software without specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* The Computer Language Benchmarks Game 34 * http://shootout.alioth.debian.org/ 35 * 36 * contributed by The Go Authors. 37 * based on meteor-contest.c by Christian Vosteen 38 */ 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 /* The board is a 50 cell hexagonal pattern. For . . . . . 57 * maximum speed the board will be implemented as . . . . . 58 * 50 bits, which will fit into a 64 bit long long . . . . . 59 * int. . . . . . 60 * . . . . . 61 * I will represent 0's as empty cells and 1's . . . . . 62 * as full cells. . . . . . 63 * . . . . . 64 * . . . . . 65 * . . . . . 66 */ 67 68 var board uint64 = 0xFFFC000000000000 69 70 /* The puzzle pieces must be specified by the path followed 71 * from one end to the other along 12 hexagonal directions. 72 * 73 * Piece 0 Piece 1 Piece 2 Piece 3 Piece 4 74 * 75 * O O O O O O O O O O O O O O O 76 * O O O O O O O 77 * O O O 78 * 79 * Piece 5 Piece 6 Piece 7 Piece 8 Piece 9 80 * 81 * O O O O O O O O O O O O O 82 * O O O O O O O O O 83 * O O O 84 * 85 * I had to make it 12 directions because I wanted all of the 86 * piece definitions to fit into the same size arrays. It is 87 * not possible to define piece 4 in terms of the 6 cardinal 88 * directions in 4 moves. 89 */ 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 /* To minimize the amount of work done in the recursive solve function below, 121 * I'm going to allocate enough space for all legal rotations of each piece 122 * at each position on the board. That's 10 pieces x 50 board positions x 123 * 12 rotations. However, not all 12 rotations will fit on every cell, so 124 * I'll have to keep count of the actual number that do. 125 * The pieces are going to be unsigned long long ints just like the board so 126 * they can be bitwise-anded with the board to determine if they fit. 127 * I'm also going to record the next possible open cell for each piece and 128 * location to reduce the burden on the solve function. 129 */ 130 var ( 131 pieces [10][50][12]uint64 132 piece_counts [10][50]int 133 next_cell [10][50][12]int8 134 ) 135 136 /* Returns the direction rotated 60 degrees clockwise */ 137 func rotate(dir int8) int8 { return (dir + 2) % PIVOT } 138 139 /* Returns the direction flipped on the horizontal axis */ 140 func flip(dir int8) int8 { return (PIVOT - dir) % PIVOT } 141 142 /* Returns the new cell index from the specified cell in the 143 * specified direction. The index is only valid if the 144 * starting cell and direction have been checked by the 145 * out_of_bounds function first. 146 */ 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 /* Returns wether the specified cell and direction will land outside 210 * of the board. Used to determine if a piece is at a legal board 211 * location or not. 212 */ 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 /* Rotate a piece 60 degrees clockwise */ 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 /* Flip a piece along the horizontal axis */ 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 /* Convenience function to quickly calculate all of the indices for a piece */ 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 /* Convenience function to quickly calculate if a piece fits on the board */ 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 /* Returns the lowest index of the cells of a piece. 278 * I use the lowest index that a piece occupies as the index for looking up 279 * the piece in the solve function. 280 */ 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 /* Calculate the lowest possible open cell if the piece is placed on the board. 292 * Used to later reduce the amount of time searching for open cells in the 293 * solve function. 294 */ 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 /* Generate the unsigned long long int that will later be anded with the 306 * board to determine if it fits. 307 */ 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 /* Record the piece and other important information in arrays that will 317 * later be used by the solve function. 318 */ 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 /* Fill the entire board going cell by cell. If any cells are "trapped" 326 * they will be left alone. 327 */ 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 /* To thin the number of pieces, I calculate if any of them trap any empty 354 * cells at the edges. There are only a handful of exceptions where the 355 * the board can be solved with the trapped cells. For example: piece 8 can 356 * trap 5 cells in the corner, but piece 3 can fit in those cells, or piece 0 357 * can split the board in half where both halves are viable. 358 */ 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 /* Calculate all six rotations of the specified piece at the specified index. 384 * We calculate only half of piece 3's rotations. This is because any solution 385 * found has an identical solution rotated 180 degrees. Thus we can reduce the 386 * number of attempted pieces in the solve algorithm by not including the 180- 387 * degree-rotated pieces of ONE of the pieces. I chose piece 3 because it gave 388 * me the best time ;) 389 */ 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 /* Calculate every legal rotation for each piece at each board location. */ 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 /* Calculate all 32 possible states for a 5-bit row and all rows that will 418 * create islands that follow any of the 32 possible rows. These pre- 419 * calculated 5-bit rows will be used to find islands in a partially solved 420 * board in the solve function. 421 */ 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 /* even is referring to row1 */ 439 var row2_shift int8 440 /* Test for blockages at same index and shifted index */ 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 /* Test for groups of 0's */ 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 /* Check for cases where three rows checked sequentially cause a false 475 * positive. One scenario is when 5 cells may be surrounded where piece 5 476 * or 7 can fit. The other scenario is when piece 2 creates a hook shape. 477 */ 478 func triple_is_okay(row1, row2, row3 int, even bool) bool { 479 if even { 480 /* There are four cases: 481 * row1: 00011 00001 11001 10101 482 * row2: 01011 00101 10001 10001 483 * row3: 011?? 00110 ????? ????? 484 */ 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 /* There are two cases: 491 * row1: 10011 10101 492 * row2: 10001 10001 493 * row3: ????? ????? 494 */ 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 /* Calculate islands while solving the board. 530 */ 531 func boardHasIslands(cell int8) int8 { 532 /* Too low on board, don't bother checking */ 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 /* The recursive solve algorithm. Try to place each permutation in the upper- 544 * leftmost empty cell. Mark off available pieces as it goes along. 545 * Because the board is a bit mask, the piece number and bit mask must be saved 546 * at each successful piece placement. This data is used to create a 50 char 547 * array if a solution is found. 548 */ 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 /* Board rotated 180 degrees is a solution too! */ 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 /* Solution found!!!!!11!!ONE! */ 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 /* pretty print a board in the specified hexagonal format */ 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 /* Find smallest and largest solutions */ 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 }