| // +build ignore |
| |
| /* |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| |
| * Neither the name of "The Computer Language Benchmarks Game" nor the |
| name of "The Computer Language Shootout Benchmarks" nor the names of |
| its contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* The Computer Language Benchmarks Game |
| * http://shootout.alioth.debian.org/ |
| * |
| * contributed by Christian Vosteen |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #define TRUE 1 |
| #define FALSE 0 |
| |
| /* The board is a 50 cell hexagonal pattern. For . . . . . |
| * maximum speed the board will be implemented as . . . . . |
| * 50 bits, which will fit into a 64 bit long long . . . . . |
| * int. . . . . . |
| * . . . . . |
| * I will represent 0's as empty cells and 1's . . . . . |
| * as full cells. . . . . . |
| * . . . . . |
| * . . . . . |
| * . . . . . |
| */ |
| |
| unsigned long long board = 0xFFFC000000000000ULL; |
| |
| /* The puzzle pieces must be specified by the path followed |
| * from one end to the other along 12 hexagonal directions. |
| * |
| * Piece 0 Piece 1 Piece 2 Piece 3 Piece 4 |
| * |
| * O O O O O O O O O O O O O O O |
| * O O O O O O O |
| * O O O |
| * |
| * Piece 5 Piece 6 Piece 7 Piece 8 Piece 9 |
| * |
| * O O O O O O O O O O O O O |
| * O O O O O O O O O |
| * O O O |
| * |
| * I had to make it 12 directions because I wanted all of the |
| * piece definitions to fit into the same size arrays. It is |
| * not possible to define piece 4 in terms of the 6 cardinal |
| * directions in 4 moves. |
| */ |
| |
| #define E 0 |
| #define ESE 1 |
| #define SE 2 |
| #define S 3 |
| #define SW 4 |
| #define WSW 5 |
| #define W 6 |
| #define WNW 7 |
| #define NW 8 |
| #define N 9 |
| #define NE 10 |
| #define ENE 11 |
| #define PIVOT 12 |
| |
| char piece_def[10][4] = { |
| { E, E, E, SE}, |
| { SE, E, NE, E}, |
| { E, E, SE, SW}, |
| { E, E, SW, SE}, |
| { SE, E, NE, S}, |
| { E, E, SW, E}, |
| { E, SE, SE, NE}, |
| { E, SE, SE, W}, |
| { E, SE, E, E}, |
| { E, E, E, SW} |
| }; |
| |
| |
| /* To minimize the amount of work done in the recursive solve function below, |
| * I'm going to allocate enough space for all legal rotations of each piece |
| * at each position on the board. That's 10 pieces x 50 board positions x |
| * 12 rotations. However, not all 12 rotations will fit on every cell, so |
| * I'll have to keep count of the actual number that do. |
| * The pieces are going to be unsigned long long ints just like the board so |
| * they can be bitwise-anded with the board to determine if they fit. |
| * I'm also going to record the next possible open cell for each piece and |
| * location to reduce the burden on the solve function. |
| */ |
| unsigned long long pieces[10][50][12]; |
| int piece_counts[10][50]; |
| char next_cell[10][50][12]; |
| |
| /* Returns the direction rotated 60 degrees clockwise */ |
| char rotate(char dir) { |
| return (dir + 2) % PIVOT; |
| } |
| |
| /* Returns the direction flipped on the horizontal axis */ |
| char flip(char dir) { |
| return (PIVOT - dir) % PIVOT; |
| } |
| |
| |
| /* Returns the new cell index from the specified cell in the |
| * specified direction. The index is only valid if the |
| * starting cell and direction have been checked by the |
| * out_of_bounds function first. |
| */ |
| char shift(char cell, char dir) { |
| switch(dir) { |
| case E: |
| return cell + 1; |
| case ESE: |
| if((cell / 5) % 2) |
| return cell + 7; |
| else |
| return cell + 6; |
| case SE: |
| if((cell / 5) % 2) |
| return cell + 6; |
| else |
| return cell + 5; |
| case S: |
| return cell + 10; |
| case SW: |
| if((cell / 5) % 2) |
| return cell + 5; |
| else |
| return cell + 4; |
| case WSW: |
| if((cell / 5) % 2) |
| return cell + 4; |
| else |
| return cell + 3; |
| case W: |
| return cell - 1; |
| case WNW: |
| if((cell / 5) % 2) |
| return cell - 6; |
| else |
| return cell - 7; |
| case NW: |
| if((cell / 5) % 2) |
| return cell - 5; |
| else |
| return cell - 6; |
| case N: |
| return cell - 10; |
| case NE: |
| if((cell / 5) % 2) |
| return cell - 4; |
| else |
| return cell - 5; |
| case ENE: |
| if((cell / 5) % 2) |
| return cell - 3; |
| else |
| return cell - 4; |
| default: |
| return cell; |
| } |
| } |
| |
| /* Returns wether the specified cell and direction will land outside |
| * of the board. Used to determine if a piece is at a legal board |
| * location or not. |
| */ |
| char out_of_bounds(char cell, char dir) { |
| char i; |
| switch(dir) { |
| case E: |
| return cell % 5 == 4; |
| case ESE: |
| i = cell % 10; |
| return i == 4 || i == 8 || i == 9 || cell >= 45; |
| case SE: |
| return cell % 10 == 9 || cell >= 45; |
| case S: |
| return cell >= 40; |
| case SW: |
| return cell % 10 == 0 || cell >= 45; |
| case WSW: |
| i = cell % 10; |
| return i == 0 || i == 1 || i == 5 || cell >= 45; |
| case W: |
| return cell % 5 == 0; |
| case WNW: |
| i = cell % 10; |
| return i == 0 || i == 1 || i == 5 || cell < 5; |
| case NW: |
| return cell % 10 == 0 || cell < 5; |
| case N: |
| return cell < 10; |
| case NE: |
| return cell % 10 == 9 || cell < 5; |
| case ENE: |
| i = cell % 10; |
| return i == 4 || i == 8 || i == 9 || cell < 5; |
| default: |
| return FALSE; |
| } |
| } |
| |
| /* Rotate a piece 60 degrees clockwise */ |
| void rotate_piece(int piece) { |
| int i; |
| for(i = 0; i < 4; i++) |
| piece_def[piece][i] = rotate(piece_def[piece][i]); |
| } |
| |
| /* Flip a piece along the horizontal axis */ |
| void flip_piece(int piece) { |
| int i; |
| for(i = 0; i < 4; i++) |
| piece_def[piece][i] = flip(piece_def[piece][i]); |
| } |
| |
| /* Convenience function to quickly calculate all of the indices for a piece */ |
| void calc_cell_indices(char *cell, int piece, char index) { |
| cell[0] = index; |
| cell[1] = shift(cell[0], piece_def[piece][0]); |
| cell[2] = shift(cell[1], piece_def[piece][1]); |
| cell[3] = shift(cell[2], piece_def[piece][2]); |
| cell[4] = shift(cell[3], piece_def[piece][3]); |
| } |
| |
| /* Convenience function to quickly calculate if a piece fits on the board */ |
| int cells_fit_on_board(char *cell, int piece) { |
| return (!out_of_bounds(cell[0], piece_def[piece][0]) && |
| !out_of_bounds(cell[1], piece_def[piece][1]) && |
| !out_of_bounds(cell[2], piece_def[piece][2]) && |
| !out_of_bounds(cell[3], piece_def[piece][3])); |
| } |
| |
| /* Returns the lowest index of the cells of a piece. |
| * I use the lowest index that a piece occupies as the index for looking up |
| * the piece in the solve function. |
| */ |
| char minimum_of_cells(char *cell) { |
| char minimum = cell[0]; |
| minimum = cell[1] < minimum ? cell[1] : minimum; |
| minimum = cell[2] < minimum ? cell[2] : minimum; |
| minimum = cell[3] < minimum ? cell[3] : minimum; |
| minimum = cell[4] < minimum ? cell[4] : minimum; |
| return minimum; |
| } |
| |
| /* Calculate the lowest possible open cell if the piece is placed on the board. |
| * Used to later reduce the amount of time searching for open cells in the |
| * solve function. |
| */ |
| char first_empty_cell(char *cell, char minimum) { |
| char first_empty = minimum; |
| while(first_empty == cell[0] || first_empty == cell[1] || |
| first_empty == cell[2] || first_empty == cell[3] || |
| first_empty == cell[4]) |
| first_empty++; |
| return first_empty; |
| } |
| |
| /* Generate the unsigned long long int that will later be anded with the |
| * board to determine if it fits. |
| */ |
| unsigned long long bitmask_from_cells(char *cell) { |
| unsigned long long piece_mask = 0ULL; |
| int i; |
| for(i = 0; i < 5; i++) |
| piece_mask |= 1ULL << cell[i]; |
| return piece_mask; |
| } |
| |
| /* Record the piece and other important information in arrays that will |
| * later be used by the solve function. |
| */ |
| void record_piece(int piece, int minimum, char first_empty, |
| unsigned long long piece_mask) { |
| pieces[piece][minimum][piece_counts[piece][minimum]] = piece_mask; |
| next_cell[piece][minimum][piece_counts[piece][minimum]] = first_empty; |
| piece_counts[piece][minimum]++; |
| } |
| |
| |
| /* Fill the entire board going cell by cell. If any cells are "trapped" |
| * they will be left alone. |
| */ |
| void fill_contiguous_space(char *board, int index) { |
| if(board[index] == 1) |
| return; |
| board[index] = 1; |
| if(!out_of_bounds(index, E)) |
| fill_contiguous_space(board, shift(index, E)); |
| if(!out_of_bounds(index, SE)) |
| fill_contiguous_space(board, shift(index, SE)); |
| if(!out_of_bounds(index, SW)) |
| fill_contiguous_space(board, shift(index, SW)); |
| if(!out_of_bounds(index, W)) |
| fill_contiguous_space(board, shift(index, W)); |
| if(!out_of_bounds(index, NW)) |
| fill_contiguous_space(board, shift(index, NW)); |
| if(!out_of_bounds(index, NE)) |
| fill_contiguous_space(board, shift(index, NE)); |
| } |
| |
| |
| /* To thin the number of pieces, I calculate if any of them trap any empty |
| * cells at the edges. There are only a handful of exceptions where the |
| * the board can be solved with the trapped cells. For example: piece 8 can |
| * trap 5 cells in the corner, but piece 3 can fit in those cells, or piece 0 |
| * can split the board in half where both halves are viable. |
| */ |
| int has_island(char *cell, int piece) { |
| char temp_board[50]; |
| char c; |
| int i; |
| for(i = 0; i < 50; i++) |
| temp_board[i] = 0; |
| for(i = 0; i < 5; i++) |
| temp_board[((int)cell[i])] = 1; |
| i = 49; |
| while(temp_board[i] == 1) |
| i--; |
| fill_contiguous_space(temp_board, i); |
| c = 0; |
| for(i = 0; i < 50; i++) |
| if(temp_board[i] == 0) |
| c++; |
| if(c == 0 || (c == 5 && piece == 8) || (c == 40 && piece == 8) || |
| (c % 5 == 0 && piece == 0)) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| |
| |
| /* Calculate all six rotations of the specified piece at the specified index. |
| * We calculate only half of piece 3's rotations. This is because any solution |
| * found has an identical solution rotated 180 degrees. Thus we can reduce the |
| * number of attempted pieces in the solve algorithm by not including the 180- |
| * degree-rotated pieces of ONE of the pieces. I chose piece 3 because it gave |
| * me the best time ;) |
| */ |
| void calc_six_rotations(char piece, char index) { |
| char rotation, cell[5]; |
| char minimum, first_empty; |
| unsigned long long piece_mask; |
| |
| for(rotation = 0; rotation < 6; rotation++) { |
| if(piece != 3 || rotation < 3) { |
| calc_cell_indices(cell, piece, index); |
| if(cells_fit_on_board(cell, piece) && !has_island(cell, piece)) { |
| minimum = minimum_of_cells(cell); |
| first_empty = first_empty_cell(cell, minimum); |
| piece_mask = bitmask_from_cells(cell); |
| record_piece(piece, minimum, first_empty, piece_mask); |
| } |
| } |
| rotate_piece(piece); |
| } |
| } |
| |
| /* Calculate every legal rotation for each piece at each board location. */ |
| void calc_pieces(void) { |
| char piece, index; |
| |
| for(piece = 0; piece < 10; piece++) { |
| for(index = 0; index < 50; index++) { |
| calc_six_rotations(piece, index); |
| flip_piece(piece); |
| calc_six_rotations(piece, index); |
| } |
| } |
| } |
| |
| |
| |
| /* Calculate all 32 possible states for a 5-bit row and all rows that will |
| * create islands that follow any of the 32 possible rows. These pre- |
| * calculated 5-bit rows will be used to find islands in a partially solved |
| * board in the solve function. |
| */ |
| #define ROW_MASK 0x1F |
| #define TRIPLE_MASK 0x7FFF |
| char all_rows[32] = {0, 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}; |
| int bad_even_rows[32][32]; |
| int bad_odd_rows[32][32]; |
| int bad_even_triple[32768]; |
| int bad_odd_triple[32768]; |
| |
| int rows_bad(char row1, char row2, int even) { |
| /* even is referring to row1 */ |
| int i, in_zeroes, group_okay; |
| char block, row2_shift; |
| /* Test for blockages at same index and shifted index */ |
| if(even) |
| row2_shift = ((row2 << 1) & ROW_MASK) | 0x01; |
| else |
| row2_shift = (row2 >> 1) | 0x10; |
| block = ((row1 ^ row2) & row2) & ((row1 ^ row2_shift) & row2_shift); |
| /* Test for groups of 0's */ |
| in_zeroes = FALSE; |
| group_okay = FALSE; |
| for(i = 0; i < 5; i++) { |
| if(row1 & (1 << i)) { |
| if(in_zeroes) { |
| if(!group_okay) |
| return TRUE; |
| in_zeroes = FALSE; |
| group_okay = FALSE; |
| } |
| } else { |
| if(!in_zeroes) |
| in_zeroes = TRUE; |
| if(!(block & (1 << i))) |
| group_okay = TRUE; |
| } |
| } |
| if(in_zeroes) |
| return !group_okay; |
| else |
| return FALSE; |
| } |
| |
| /* Check for cases where three rows checked sequentially cause a false |
| * positive. One scenario is when 5 cells may be surrounded where piece 5 |
| * or 7 can fit. The other scenario is when piece 2 creates a hook shape. |
| */ |
| int triple_is_okay(char row1, char row2, char row3, int even) { |
| if(even) { |
| /* There are four cases: |
| * row1: 00011 00001 11001 10101 |
| * row2: 01011 00101 10001 10001 |
| * row3: 011?? 00110 ????? ????? |
| */ |
| return ((row1 == 0x03) && (row2 == 0x0B) && ((row3 & 0x1C) == 0x0C)) || |
| ((row1 == 0x01) && (row2 == 0x05) && (row3 == 0x06)) || |
| ((row1 == 0x19) && (row2 == 0x11)) || |
| ((row1 == 0x15) && (row2 == 0x11)); |
| } else { |
| /* There are two cases: |
| * row1: 10011 10101 |
| * row2: 10001 10001 |
| * row3: ????? ????? |
| */ |
| return ((row1 == 0x13) && (row2 == 0x11)) || |
| ((row1 == 0x15) && (row2 == 0x11)); |
| } |
| } |
| |
| |
| void calc_rows(void) { |
| int row1, row2, row3; |
| int result1, result2; |
| for(row1 = 0; row1 < 32; row1++) { |
| for(row2 = 0; row2 < 32; row2++) { |
| bad_even_rows[row1][row2] = rows_bad(row1, row2, TRUE); |
| bad_odd_rows[row1][row2] = rows_bad(row1, row2, FALSE); |
| } |
| } |
| for(row1 = 0; row1 < 32; row1++) { |
| for(row2 = 0; row2 < 32; row2++) { |
| for(row3 = 0; row3 < 32; row3++) { |
| result1 = bad_even_rows[row1][row2]; |
| result2 = bad_odd_rows[row2][row3]; |
| if(result1 == FALSE && result2 == TRUE |
| && triple_is_okay(row1, row2, row3, TRUE)) |
| bad_even_triple[row1+(row2*32)+(row3*1024)] = FALSE; |
| else |
| bad_even_triple[row1+(row2*32)+(row3*1024)] = result1 || result2; |
| |
| result1 = bad_odd_rows[row1][row2]; |
| result2 = bad_even_rows[row2][row3]; |
| if(result1 == FALSE && result2 == TRUE |
| && triple_is_okay(row1, row2, row3, FALSE)) |
| bad_odd_triple[row1+(row2*32)+(row3*1024)] = FALSE; |
| else |
| bad_odd_triple[row1+(row2*32)+(row3*1024)] = result1 || result2; |
| } |
| } |
| } |
| } |
| |
| |
| |
| /* Calculate islands while solving the board. |
| */ |
| int boardHasIslands(char cell) { |
| /* Too low on board, don't bother checking */ |
| if(cell >= 40) |
| return FALSE; |
| int current_triple = (board >> ((cell / 5) * 5)) & TRIPLE_MASK; |
| if((cell / 5) % 2) |
| return bad_odd_triple[current_triple]; |
| else |
| return bad_even_triple[current_triple]; |
| } |
| |
| |
| /* The recursive solve algorithm. Try to place each permutation in the upper- |
| * leftmost empty cell. Mark off available pieces as it goes along. |
| * Because the board is a bit mask, the piece number and bit mask must be saved |
| * at each successful piece placement. This data is used to create a 50 char |
| * array if a solution is found. |
| */ |
| short avail = 0x03FF; |
| char sol_nums[10]; |
| unsigned long long sol_masks[10]; |
| signed char solutions[2100][50]; |
| int solution_count = 0; |
| int max_solutions = 2100; |
| |
| void record_solution(void) { |
| int sol_no, index; |
| unsigned long long sol_mask; |
| for(sol_no = 0; sol_no < 10; sol_no++) { |
| sol_mask = sol_masks[sol_no]; |
| for(index = 0; index < 50; index++) { |
| if(sol_mask & 1ULL) { |
| solutions[solution_count][index] = sol_nums[sol_no]; |
| /* Board rotated 180 degrees is a solution too! */ |
| solutions[solution_count+1][49-index] = sol_nums[sol_no]; |
| } |
| sol_mask = sol_mask >> 1; |
| } |
| } |
| solution_count += 2; |
| } |
| |
| void solve(int depth, int cell) { |
| int piece, rotation, max_rots; |
| unsigned long long *piece_mask; |
| short piece_no_mask; |
| |
| if(solution_count >= max_solutions) |
| return; |
| |
| while(board & (1ULL << cell)) |
| cell++; |
| |
| for(piece = 0; piece < 10; piece++) { |
| piece_no_mask = 1 << piece; |
| if(!(avail & piece_no_mask)) |
| continue; |
| avail ^= piece_no_mask; |
| max_rots = piece_counts[piece][cell]; |
| piece_mask = pieces[piece][cell]; |
| for(rotation = 0; rotation < max_rots; rotation++) { |
| if(!(board & *(piece_mask + rotation))) { |
| sol_nums[depth] = piece; |
| sol_masks[depth] = *(piece_mask + rotation); |
| if(depth == 9) { |
| /* Solution found!!!!!11!!ONE! */ |
| record_solution(); |
| avail ^= piece_no_mask; |
| return; |
| } |
| board |= *(piece_mask + rotation); |
| if(!boardHasIslands(next_cell[piece][cell][rotation])) |
| solve(depth + 1, next_cell[piece][cell][rotation]); |
| board ^= *(piece_mask + rotation); |
| } |
| } |
| avail ^= piece_no_mask; |
| } |
| } |
| |
| |
| /* qsort comparator - used to find first and last solutions */ |
| int solution_sort(const void *elem1, const void *elem2) { |
| signed char *char1 = (signed char *) elem1; |
| signed char *char2 = (signed char *) elem2; |
| int i = 0; |
| while(i < 50 && char1[i] == char2[i]) |
| i++; |
| return char1[i] - char2[i]; |
| } |
| |
| |
| /* pretty print a board in the specified hexagonal format */ |
| void pretty(signed char *b) { |
| int i; |
| for(i = 0; i < 50; i += 10) { |
| printf("%c %c %c %c %c \n %c %c %c %c %c \n", b[i]+'0', b[i+1]+'0', |
| b[i+2]+'0', b[i+3]+'0', b[i+4]+'0', b[i+5]+'0', b[i+6]+'0', |
| b[i+7]+'0', b[i+8]+'0', b[i+9]+'0'); |
| } |
| printf("\n"); |
| } |
| |
| int main(int argc, char **argv) { |
| if(argc > 1) |
| max_solutions = atoi(argv[1]); |
| calc_pieces(); |
| calc_rows(); |
| solve(0, 0); |
| printf("%d solutions found\n\n", solution_count); |
| qsort(solutions, solution_count, 50 * sizeof(signed char), solution_sort); |
| pretty(solutions[0]); |
| pretty(solutions[solution_count-1]); |
| return 0; |
| } |