/test/bench/meteor-contest.go
Go | 665 lines | 458 code | 51 blank | 156 comment | 198 complexity | afd143afe94e1b5166d8538950faef24 MD5 | raw file
Possible License(s): BSD-3-Clause
- /*
- 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 The Go Authors.
- * based on meteor-contest.c by Christian Vosteen
- */
- package main
- import (
- "flag"
- "fmt"
- )
- var max_solutions = flag.Int("n", 2100, "maximum number of solutions")
- func boolInt(b bool) int8 {
- if b {
- return 1
- }
- return 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. . . . . .
- * . . . . .
- * . . . . .
- * . . . . .
- */
- var board uint64 = 0xFFFC000000000000
- /* 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.
- */
- const (
- E = iota
- ESE
- SE
- S
- SW
- WSW
- W
- WNW
- NW
- N
- NE
- ENE
- PIVOT
- )
- var piece_def = [10][4]int8{
- [4]int8{E, E, E, SE},
- [4]int8{SE, E, NE, E},
- [4]int8{E, E, SE, SW},
- [4]int8{E, E, SW, SE},
- [4]int8{SE, E, NE, S},
- [4]int8{E, E, SW, E},
- [4]int8{E, SE, SE, NE},
- [4]int8{E, SE, SE, W},
- [4]int8{E, SE, E, E},
- [4]int8{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.
- */
- var (
- pieces [10][50][12]uint64
- piece_counts [10][50]int
- next_cell [10][50][12]int8
- )
- /* Returns the direction rotated 60 degrees clockwise */
- func rotate(dir int8) int8 { return (dir + 2) % PIVOT }
- /* Returns the direction flipped on the horizontal axis */
- func flip(dir int8) int8 { 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.
- */
- func shift(cell, dir int8) int8 {
- switch dir {
- case E:
- return cell + 1
- case ESE:
- if ((cell / 5) % 2) != 0 {
- return cell + 7
- } else {
- return cell + 6
- }
- case SE:
- if ((cell / 5) % 2) != 0 {
- return cell + 6
- } else {
- return cell + 5
- }
- case S:
- return cell + 10
- case SW:
- if ((cell / 5) % 2) != 0 {
- return cell + 5
- } else {
- return cell + 4
- }
- case WSW:
- if ((cell / 5) % 2) != 0 {
- return cell + 4
- } else {
- return cell + 3
- }
- case W:
- return cell - 1
- case WNW:
- if ((cell / 5) % 2) != 0 {
- return cell - 6
- } else {
- return cell - 7
- }
- case NW:
- if ((cell / 5) % 2) != 0 {
- return cell - 5
- } else {
- return cell - 6
- }
- case N:
- return cell - 10
- case NE:
- if ((cell / 5) % 2) != 0 {
- return cell - 4
- } else {
- return cell - 5
- }
- case ENE:
- if ((cell / 5) % 2) != 0 {
- return cell - 3
- } else {
- return cell - 4
- }
- }
- 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.
- */
- func out_of_bounds(cell, dir int8) bool {
- 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
- }
- return false
- }
- /* Rotate a piece 60 degrees clockwise */
- func rotate_piece(piece int) {
- for i := 0; i < 4; i++ {
- piece_def[piece][i] = rotate(piece_def[piece][i])
- }
- }
- /* Flip a piece along the horizontal axis */
- func flip_piece(piece int) {
- 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 */
- func calc_cell_indices(cell []int8, piece int, index int8) {
- cell[0] = index
- for i := 1; i < 5; i++ {
- cell[i] = shift(cell[i-1], piece_def[piece][i-1])
- }
- }
- /* Convenience function to quickly calculate if a piece fits on the board */
- func cells_fit_on_board(cell []int8, piece int) bool {
- 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.
- */
- func minimum_of_cells(cell []int8) int8 {
- minimum := cell[0]
- for i := 1; i < 5; i++ {
- if cell[i] < minimum {
- minimum = cell[i]
- }
- }
- 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.
- */
- func first_empty_cell(cell []int8, minimum int8) int8 {
- first_empty := minimum
- for 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.
- */
- func bitmask_from_cells(cell []int8) uint64 {
- var piece_mask uint64
- for i := 0; i < 5; i++ {
- piece_mask |= 1 << uint(cell[i])
- }
- return piece_mask
- }
- /* Record the piece and other important information in arrays that will
- * later be used by the solve function.
- */
- func record_piece(piece int, minimum int8, first_empty int8, piece_mask uint64) {
- 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.
- */
- func fill_contiguous_space(board []int8, index int8) {
- 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.
- */
- func has_island(cell []int8, piece int) bool {
- temp_board := make([]int8, 50)
- var i int
- for i = 0; i < 5; i++ {
- temp_board[cell[i]] = 1
- }
- i = 49
- for temp_board[i] == 1 {
- i--
- }
- fill_contiguous_space(temp_board, int8(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
- }
- 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 ;)
- */
- func calc_six_rotations(piece, index int) {
- cell := make([]int8, 5)
- for rotation := 0; rotation < 6; rotation++ {
- if piece != 3 || rotation < 3 {
- calc_cell_indices(cell, piece, int8(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. */
- func calc_pieces() {
- 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.
- */
- const (
- ROW_MASK = 0x1F
- TRIPLE_MASK = 0x7FFF
- )
- var (
- all_rows = [32]int8{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,
- }
- bad_even_rows [32][32]int8
- bad_odd_rows [32][32]int8
- bad_even_triple [32768]int8
- bad_odd_triple [32768]int8
- )
- func rows_bad(row1, row2 int8, even bool) int8 {
- /* even is referring to row1 */
- var row2_shift int8
- /* 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 := uint8(0); i < 5; i++ {
- if row1&(1<<i) != 0 {
- if in_zeroes {
- if !group_okay {
- return 1
- }
- in_zeroes = false
- group_okay = false
- }
- } else {
- if !in_zeroes {
- in_zeroes = true
- }
- if (block & (1 << i)) == 0 {
- group_okay = true
- }
- }
- }
- if in_zeroes {
- return boolInt(!group_okay)
- }
- return 0
- }
- /* 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.
- */
- func triple_is_okay(row1, row2, row3 int, even bool) bool {
- 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))
- }
- /* There are two cases:
- * row1: 10011 10101
- * row2: 10001 10001
- * row3: ????? ?????
- */
- return ((row1 == 0x13) && (row2 == 0x11)) ||
- ((row1 == 0x15) && (row2 == 0x11))
- }
- func calc_rows() {
- for row1 := int8(0); row1 < 32; row1++ {
- for row2 := int8(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 == 0 && result2 != 0 && triple_is_okay(row1, row2, row3, true) {
- bad_even_triple[row1+(row2*32)+(row3*1024)] = 0
- } else {
- bad_even_triple[row1+(row2*32)+(row3*1024)] = boolInt(result1 != 0 || result2 != 0)
- }
- result1 = bad_odd_rows[row1][row2]
- result2 = bad_even_rows[row2][row3]
- if result1 == 0 && result2 != 0 && triple_is_okay(row1, row2, row3, false) {
- bad_odd_triple[row1+(row2*32)+(row3*1024)] = 0
- } else {
- bad_odd_triple[row1+(row2*32)+(row3*1024)] = boolInt(result1 != 0 || result2 != 0)
- }
- }
- }
- }
- }
- /* Calculate islands while solving the board.
- */
- func boardHasIslands(cell int8) int8 {
- /* Too low on board, don't bother checking */
- if cell >= 40 {
- return 0
- }
- current_triple := (board >> uint((cell/5)*5)) & TRIPLE_MASK
- if (cell/5)%2 != 0 {
- return bad_odd_triple[current_triple]
- }
- 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.
- */
- var (
- avail uint16 = 0x03FF
- sol_nums [10]int8
- sol_masks [10]uint64
- solutions [2100][50]int8
- solution_count = 0
- )
- func record_solution() {
- for sol_no := 0; sol_no < 10; sol_no++ {
- sol_mask := sol_masks[sol_no]
- for index := 0; index < 50; index++ {
- if sol_mask&1 == 1 {
- 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
- }
- func solve(depth, cell int8) {
- if solution_count >= *max_solutions {
- return
- }
- for board&(1<<uint(cell)) != 0 {
- cell++
- }
- for piece := int8(0); piece < 10; piece++ {
- var piece_no_mask uint16 = 1 << uint(piece)
- if avail&piece_no_mask == 0 {
- 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] == 0 {
- 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]) == 0 {
- solve(depth+1, next_cell[piece][cell][rotation])
- }
- board ^= piece_mask[rotation]
- }
- }
- avail ^= piece_no_mask
- }
- }
- /* pretty print a board in the specified hexagonal format */
- func pretty(b *[50]int8) {
- for i := 0; i < 50; i += 10 {
- fmt.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')
- }
- fmt.Printf("\n")
- }
- /* Find smallest and largest solutions */
- func smallest_largest() (smallest, largest *[50]int8) {
- smallest = &solutions[0]
- largest = &solutions[0]
- for i := 1; i < solution_count; i++ {
- candidate := &solutions[i]
- for j, s := range *smallest {
- c := candidate[j]
- if c == s {
- continue
- }
- if c < s {
- smallest = candidate
- }
- break
- }
- for j, s := range *largest {
- c := candidate[j]
- if c == s {
- continue
- }
- if c > s {
- largest = candidate
- }
- break
- }
- }
- return
- }
- func main() {
- flag.Parse()
- calc_pieces()
- calc_rows()
- solve(0, 0)
- fmt.Printf("%d solutions found\n\n", solution_count)
- smallest, largest := smallest_largest()
- pretty(smallest)
- pretty(largest)
- }