diff --git a/board.txt b/board.txt index 6f688a7..0f62f2f 100644 --- a/board.txt +++ b/board.txt @@ -1,4 +1,5 @@ # sample board +# 1 solution, 0:0:2.312 > 0:0:0.836 > 0:0:0.819 > 0:0:0.227 4 3 # 3 rows, containing 4 elements each 1 2 3 4 6 # first row 5 6 7 8 13 diff --git a/complex.txt b/complex.txt index 4f7654d..bc31661 100644 --- a/complex.txt +++ b/complex.txt @@ -1,4 +1,5 @@ -# 115 solutions +# high complexity +# 115 solutions, 0:18:47.647 > 0:2:44.168 > 0:1:10.585 > 0:1:9.382 8 8 2 2 3 4 3 4 4 3 17 4 2 3 4 3 3 4 4 16 diff --git a/high.txt b/high.txt new file mode 100644 index 0000000..3f53e90 --- /dev/null +++ b/high.txt @@ -0,0 +1,12 @@ +# high complexity +# 435 solutions, 0:4:0.955 > 0:1:19.424 > 0:0:52.183 > 0:0:51.410 +8 8 +4 2 3 2 4 3 4 4 18 +3 4 3 3 3 2 4 2 16 +3 4 2 4 4 4 3 4 21 +3 3 3 4 3 3 2 2 17 +4 3 4 4 3 2 3 4 24 +3 4 3 4 2 4 3 4 21 +4 4 2 4 3 2 4 4 19 +3 3 3 4 4 3 2 4 19 +20 20 17 21 14 21 18 24 \ No newline at end of file diff --git a/medium.txt b/medium.txt new file mode 100644 index 0000000..6431096 --- /dev/null +++ b/medium.txt @@ -0,0 +1,12 @@ +# medium complexity +# 3 solutions, 0:1:50.036 > 0:0:25.832 > 0:0:18.215 > 0:0:15.834 +8 8 +3 4 2 4 3 3 4 4 18 +2 2 3 2 3 2 2 3 14 +2 2 4 2 2 4 3 4 17 +3 3 4 3 2 4 3 4 19 +3 2 2 4 4 4 2 4 22 +2 2 3 3 3 3 3 2 19 +3 3 2 3 4 4 3 2 14 +2 3 2 2 4 3 2 4 16 +14 16 20 21 20 19 12 17 \ No newline at end of file diff --git a/rullo.go b/rullo.go index ff21c26..b9f34eb 100644 --- a/rullo.go +++ b/rullo.go @@ -11,6 +11,72 @@ import ( "strings" ) +// Row represents a row within a board. +type Row []int + +// Sum sums all elements of a row. +func (row Row) Sum() int { + sum := 0 + for _, v := range row { + sum += v + } + return sum +} + +// Board contains a series of rows forming a board. +type Board []Row + +// Duplicate creates an exact copy of the board on a separate space. +func (board Board) Duplicate() Board { + rows := len(board) + dup := Board(make(Board, rows)) + for i := 0; i < rows; i++ { + dup[i] = Row(make([]int, len(board[i]))) + copy(dup[i], board[i]) + } + return dup +} + +// Sum sums a given column of a board. +func (board Board) Sum(col int) int { + sum := 0 + for _, r := range board { + sum += r[col] + } + return sum +} + +// Array contains an array of rows with valid solutions. +type Array []Row + +// Append adds a new plausible solution to a given row. +func (a *Array) Append(solution Row) { + *a = append(*a, solution) +} + +// Plausibles contains all rows with valid solutions. +type Plausibles []Array + +// Assemble returns a board with a plausible solution. +func (p Plausibles) Assemble(c chan Board, board *Board, rowNo int) { + if rowNo >= len(p) { // board is assembled + c <- *board + *board = (*board).Duplicate() // to avoid data contamination + } else { + for _, row := range p[rowNo] { // choose each plausible solution on this row + (*board)[rowNo] = row + p.Assemble(c, board, rowNo+1) // proceed with the following row + } + } +} + +// Iterate is a goroutine that returns all combinations of plausible solutions. +func (p Plausibles) Iterate(c chan Board) { + board := make(Board, len(p)) + p.Assemble(c, &board, 0) + close(c) +} + // Converts a string to its numeric value. func convert(line int, value string) int { n, err := strconv.Atoi(value) @@ -77,7 +143,7 @@ func load(c chan []int, s *bufio.Scanner) { } // Creates a new board with data from the input file. -func newBoard(file string) ([][]int, []int, []int) { +func newBoard(file string) (Board, []int, []int) { f, err := os.Open(file) if err != nil { panic(fmt.Sprintf("Error opening file %v", file)) @@ -87,9 +153,9 @@ func newBoard(file string) ([][]int, []int, []int) { go load(data, bufio.NewScanner(f)) row := <-data // first row contains board dimensions rows := row[1] - board := make([][]int, rows) - for i := 0; i < rows; i++ { - board[i] = <-data + board := Board(make(Board, rows)) + for i := 0; i < rows; i++ { // load rows + board[i] = Row(<-data) } horz := <-data // horizontal sums vert := <-data // vertical sums @@ -99,67 +165,46 @@ func newBoard(file string) ([][]int, []int, []int) { return board, horz, vert } -// Creates an exact copy of the board. -func duplicate(board [][]int) [][]int { - rows := len(board) - dup := make([][]int, rows) - for i := 0; i < rows; i++ { - dup[i] = make([]int, len(board[i])) - copy(dup[i], board[i]) - } - return dup -} - -// Sums all elements of a row. -func sum(row []int) int { - sum := 0 - for _, v := range row { - sum += v - } - return sum -} - -// Sums a given column of a board. -func vsum(board [][]int, col int) int { - sum := 0 - for _, r := range board { - sum += r[col] - } - return sum -} - -var solutions int - // Explores all possible solutions using brute force. -func explore(board [][]int, solution [][]int, r int, cols int, horz []int, vert []int) { - if r >= len(board) { // we may have a solution - solved := true - for i := 0; i < cols; i++ { - if vsum(solution, i) != vert[i] { - solved = false // this column failed validation - break - } - } - if solved { - solutions++ - fmt.Printf("%3v: %v\n", solutions, solution) - } - } else { - for i := 0; i < 1<>= 1 } - if sum(row) == horz[r] { // we have a plausibe solution - solution[r] = row - explore(board, solution, r+1, cols, horz, vert) // try the next row + if row.Sum() == horz[r] { // we have a plausible solution + plausibles[r].Append(row) } } } + // match all plausible solutions with one another. + n := 0 + solutions := make(chan Board, 100) + go plausibles.Iterate(solutions) + for solution := range solutions { + // see if the newly assembled board is an actual solution + solved := true + for col := range solution[0] { + if solution.Sum(col) != vert[col] { + solved = false // this column failed validation + break + } + } + if solved { + n++ + fmt.Printf("%3v: %v\n", n, solution) + } + } } func main() { @@ -176,6 +221,6 @@ func main() { } else { board, horz, vert := newBoard(os.Args[1]) fmt.Printf("board = %v\n horz = %v\n vert = %v\n", board, horz, vert) - explore(board, duplicate(board), 0, len(board[0]), horz, vert) + explore(board, horz, vert) } }