|
| 1 | +package game |
| 2 | + |
| 3 | +// presetBoard - 填滿格子 |
| 4 | +func (board *Board) presetBoard() bool { |
| 5 | + row, col, foundEmpty := -1, -1, false |
| 6 | + // 找到第一個非空的格子來填 |
| 7 | + for r := 0; r < BoardSize && !foundEmpty; r++ { |
| 8 | + for c := 0; c < BoardSize && !foundEmpty; c++ { |
| 9 | + if board.Cells[r][c].Value == 0 && board.Cells[r][c].Type != Preset { |
| 10 | + row, col, foundEmpty = r, c, true |
| 11 | + } |
| 12 | + } |
| 13 | + } |
| 14 | + |
| 15 | + // 當所有都填滿了回傳 true |
| 16 | + if !foundEmpty { |
| 17 | + return true |
| 18 | + } |
| 19 | + |
| 20 | + // 隨機取值出來填寫 |
| 21 | + for _, digit := range digitsShuffled() { |
| 22 | + // 確認 digit 是否可以填入 row, col |
| 23 | + if board.isSafe(row, col, digit) { |
| 24 | + // 先填入 row, col 為 digit |
| 25 | + board.Cells[row][col].Type = Preset |
| 26 | + board.Cells[row][col].Value = digit |
| 27 | + // 如果格子填滿則回傳 true |
| 28 | + if board.presetBoard() { |
| 29 | + return true |
| 30 | + } |
| 31 | + // 否則把 row, col 回朔 |
| 32 | + board.Cells[row][col].Type = Empty |
| 33 | + board.Cells[row][col].Value = 0 |
| 34 | + } |
| 35 | + } |
| 36 | + return false |
| 37 | +} |
| 38 | + |
| 39 | +// solveCount - 計算一共有多少解答 |
| 40 | +func solveCount(board *Board, limit int) int { |
| 41 | + row, col, found := -1, -1, false |
| 42 | + // 找到第一個非空的格子來填 |
| 43 | + for r := 0; r < BoardSize && !found; r++ { |
| 44 | + for c := 0; c < BoardSize && !found; c++ { |
| 45 | + if board.Cells[r][c].Type == Empty { |
| 46 | + row, col, found = r, c, true |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + // 全部非空解答找到了 |
| 51 | + if !found { |
| 52 | + return 1 |
| 53 | + } |
| 54 | + // 開始試著填入值找到解答 |
| 55 | + count := 0 |
| 56 | + for _, digit := range digitsShuffled() { |
| 57 | + if board.isSafe(row, col, digit) { |
| 58 | + // 先填入 row, col 為 digit |
| 59 | + board.Cells[row][col].Type = Preset |
| 60 | + board.Cells[row][col].Value = digit |
| 61 | + // 累加 |
| 62 | + count += solveCount(board, limit-count) |
| 63 | + // 把 row, col 回朔 |
| 64 | + board.Cells[row][col].Type = Empty |
| 65 | + board.Cells[row][col].Value = 0 |
| 66 | + if count >= limit { |
| 67 | + return count |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + return count |
| 73 | +} |
| 74 | + |
| 75 | +// hasUniqueSolution - 是否具有唯一解 |
| 76 | +func (board *Board) hasUniqueSolution() bool { |
| 77 | + copyBoard := *board |
| 78 | + count := solveCount(©Board, 2) |
| 79 | + return count == 1 |
| 80 | +} |
| 81 | + |
| 82 | +// GenerateSolution - 產生解法 |
| 83 | +func (board *Board) GenerateSolution() { |
| 84 | + // 填入解法 |
| 85 | + board.presetBoard() |
| 86 | +} |
| 87 | + |
| 88 | +// presetedCount - 計算被先填入的 count |
| 89 | +func (board *Board) presetedCount() int { |
| 90 | + count := 0 |
| 91 | + for row := 0; row < BoardSize; row++ { |
| 92 | + for col := 0; col < BoardSize; col++ { |
| 93 | + if board.Cells[row][col].Type == Preset { |
| 94 | + count++ |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | + return count |
| 99 | +} |
| 100 | + |
| 101 | +// MakePuzzleFromSolution - 建立題目 |
| 102 | +func (board *Board) MakePuzzleFromSolution(targetClues int) { |
| 103 | + puzzle := board.Clone() |
| 104 | + order := coordsShuffled() |
| 105 | + for _, rc := range order { |
| 106 | + if puzzle.presetedCount() <= targetClues { |
| 107 | + break |
| 108 | + } |
| 109 | + r, c := rc[0], rc[1] |
| 110 | + if puzzle.Cells[r][c].Type == Empty { |
| 111 | + continue |
| 112 | + } |
| 113 | + tmp := puzzle.Cells[r][c] |
| 114 | + puzzle.Cells[r][c].Type = Empty |
| 115 | + puzzle.Cells[r][c].Value = 0 |
| 116 | + if puzzle.hasUniqueSolution() { |
| 117 | + // 不是唯一解 → 復原 |
| 118 | + puzzle.Cells[r][c].Type = tmp.Type |
| 119 | + puzzle.Cells[r][c].Value = tmp.Value |
| 120 | + } |
| 121 | + } |
| 122 | + board = &puzzle |
| 123 | +} |
0 commit comments