Skip to content

Commit dacad25

Browse files
committed
✨ (sudoku-game): add show remaing count
1 parent 20ec61f commit dacad25

File tree

6 files changed

+242
-32
lines changed

6 files changed

+242
-32
lines changed

internal/game/cell.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ const (
1313
// Cell 代表數獨的一個格子
1414
type Cell struct {
1515
Value int // 數字,0 表示空格
16-
Type CellType // 狀態:Empty、Preset、Input
16+
Type CellType // 狀態:Empty、Preset、Input、InputConflict
1717
}

internal/game/puzzle.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func solveCount(board *Board, limit int) int {
4242
// 找到第一個非空的格子來填
4343
for r := 0; r < BoardSize && !found; r++ {
4444
for c := 0; c < BoardSize && !found; c++ {
45-
if board.Cells[r][c].Type == Empty {
45+
if board.Cells[r][c].Type == Empty && board.Cells[r][c].Value == 0 {
4646
row, col, found = r, c, true
4747
}
4848
}
@@ -90,7 +90,7 @@ func (board *Board) presetedCount() int {
9090
count := 0
9191
for row := 0; row < BoardSize; row++ {
9292
for col := 0; col < BoardSize; col++ {
93-
if board.Cells[row][col].Type == Preset {
93+
if board.Cells[row][col].Type == Preset && board.Cells[row][col].Value != 0 {
9494
count++
9595
}
9696
}
@@ -107,17 +107,18 @@ func (board *Board) MakePuzzleFromSolution(targetClues int) {
107107
break
108108
}
109109
r, c := rc[0], rc[1]
110-
if puzzle.Cells[r][c].Type == Empty {
110+
if puzzle.Cells[r][c].Type == Empty && puzzle.Cells[r][c].Value == 0 {
111111
continue
112112
}
113113
tmp := puzzle.Cells[r][c]
114114
puzzle.Cells[r][c].Type = Empty
115115
puzzle.Cells[r][c].Value = 0
116-
if puzzle.hasUniqueSolution() {
116+
if !puzzle.hasUniqueSolution() {
117117
// 不是唯一解 → 復原
118118
puzzle.Cells[r][c].Type = tmp.Type
119119
puzzle.Cells[r][c].Value = tmp.Value
120120
}
121121
}
122+
board.TargetSolvedCount = 81 - targetClues
122123
board = &puzzle
123124
}

internal/game/sudoku.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ const (
77

88
// Board - 盤面
99
type Board struct {
10-
Cells [BoardSize][BoardSize]*Cell
11-
CursorRow int
12-
CursorCol int
10+
Cells [BoardSize][BoardSize]*Cell // 格子內容
11+
CursorRow int // Cursor row position
12+
CursorCol int // Cursor col position
13+
TargetSolvedCount int // 需要解決的格子數 = 81 - clues
14+
FilledCount int // 目前填入格子數
15+
ConflictCount int // 不符合規則的格子數
1316
}
1417

1518
// NewBoard 建立一個空的數獨盤面
@@ -62,3 +65,19 @@ func (board *Board) DecreaseCursorCol() {
6265
return
6366
}
6467
}
68+
69+
func (board *Board) IncreaseConflictCount() {
70+
board.ConflictCount++
71+
}
72+
73+
func (board *Board) DescreaseConflictCount() {
74+
board.ConflictCount--
75+
}
76+
77+
func (board *Board) IncreaseFilledCount() {
78+
board.FilledCount++
79+
}
80+
81+
func (board *Board) DecreaseFilledCount() {
82+
board.FilledCount--
83+
}

internal/layout/font.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
var (
1414
mplusFaceSource *text.GoTextFaceSource
15+
emojiFaceSource *text.GoTextFaceSource
1516
)
1617

1718
func init() {
@@ -21,6 +22,11 @@ func init() {
2122
}
2223
mplusFaceSource = s
2324

25+
s, err = text.NewGoTextFaceSource(bytes.NewReader(fonts.NotoEmojiRegular_ttf))
26+
if err != nil {
27+
log.Fatal(err)
28+
}
29+
emojiFaceSource = s
2430
}
2531

2632
func getTileColor(cellType game.CellType) color.Color {
@@ -49,3 +55,25 @@ func getTileBgColor(cellType game.CellType) color.Color {
4955
return color.RGBA{0xFF, 0xFF, 0xFF, 0xFF}
5056
}
5157
}
58+
59+
type IconType int
60+
61+
const (
62+
RemainingCount IconType = iota
63+
Playing
64+
Win
65+
Bug
66+
)
67+
68+
func getIconColor(iconType IconType) color.Color {
69+
switch iconType {
70+
case RemainingCount: // 顯示亮灰色
71+
return color.RGBA{0xCD, 0xC9, 0xC9, 0xFF}
72+
case Playing, Bug: // Green
73+
return color.RGBA{0x22, 0x8B, 0x22, 0xFF}
74+
case Win: // Gold
75+
return color.RGBA{0xFF, 0xD7, 0x00, 0xFF}
76+
default: // 其他都是預設 白色
77+
return color.RGBA{0xFF, 0xFF, 0xFF, 0xFF}
78+
}
79+
}

internal/layout/input-control.go

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,79 @@ func (gameLayout *GameLayout) DetectInput() {
1616
// 數字輸入
1717
for key := ebiten.KeyDigit1; key <= ebiten.KeyDigit9; key++ {
1818
if inpututil.IsKeyJustPressed(key) {
19-
if targetCell.Type != game.Preset {
20-
value := int(key - ebiten.KeyDigit0)
21-
// 檢查輸入的值是否為放入是否能會造成 Conflict
22-
if !board.IsSafe(targetRow, targetCol, value) {
23-
// 標示為 Conflict Input
24-
board.Cells[targetRow][targetCol].Type = game.InputConflict
25-
} else {
26-
board.Cells[targetRow][targetCol].Type = game.Input
27-
}
28-
// 更新輸入
29-
board.Cells[targetRow][targetCol].Value = value
30-
}
19+
handleKeyInput(board, targetCell, key, targetRow, targetCol)
3120
return
3221
}
3322
}
3423

3524
// 清除輸入
3625
if inpututil.IsKeyJustPressed(ebiten.Key0) || inpututil.IsKeyJustPressed(ebiten.KeyDelete) {
37-
if targetCell.Type != game.Preset {
38-
// 清空目前 Cell 的值
39-
board.Cells[targetRow][targetCol].Value = 0
40-
board.Cells[targetRow][targetCol].Type = game.Empty
41-
}
26+
handleClearInput(board, targetCell, targetRow, targetCol)
27+
return
28+
}
29+
}
30+
31+
// handleClearInput - 處理清除
32+
func handleClearInput(board *game.Board, targetCell *game.Cell,
33+
targetRow, targetCol int) {
34+
cellType := targetCell.Type
35+
// 當遇到題目時
36+
if cellType == game.Preset {
4237
return
4338
}
39+
// 原本輸入是 conflict
40+
if cellType == game.InputConflict {
41+
board.DescreaseConflictCount()
42+
}
43+
// 原本輸入非空
44+
if cellType != game.Empty {
45+
board.DecreaseFilledCount()
46+
}
47+
// 清空目前 Cell 的值
48+
board.Cells[targetRow][targetCol].Value = 0
49+
board.Cells[targetRow][targetCol].Type = game.Empty
50+
}
51+
52+
// handleKeyInput - 處理輸入時
53+
func handleKeyInput(board *game.Board, targetCell *game.Cell, key ebiten.Key,
54+
targetRow, targetCol int) {
55+
cellType := targetCell.Type
56+
// 當格子為題目時
57+
if cellType == game.Preset {
58+
return
59+
}
60+
value := int(key - ebiten.KeyDigit0)
61+
// 當輸入格為空格時
62+
if cellType == game.Empty {
63+
board.IncreaseFilledCount()
64+
}
65+
safed := board.IsSafe(targetRow, targetCol, value)
66+
if !safed {
67+
handleConflict(board, cellType, targetRow, targetCol)
68+
} else {
69+
handleNonConflict(board, cellType, targetRow, targetCol)
70+
}
71+
// 更新輸入
72+
board.Cells[targetRow][targetCol].Value = value
73+
}
74+
75+
// handleConflict - 處理 Conflict Cell
76+
func handleConflict(board *game.Board, cellType game.CellType,
77+
targetRow, targetCol int) {
78+
if cellType != game.InputConflict {
79+
board.IncreaseConflictCount()
80+
}
81+
// 標示為 Conflict Input
82+
board.Cells[targetRow][targetCol].Type = game.InputConflict
83+
}
84+
85+
// handleNonConflict - 處理 Non-Conflict Cell
86+
func handleNonConflict(board *game.Board, cellType game.CellType,
87+
targetRow, targetCol int) {
88+
// 當輸入為 Conflict 時
89+
if cellType == game.InputConflict {
90+
board.DescreaseConflictCount()
91+
}
92+
// 標示為 Input
93+
board.Cells[targetRow][targetCol].Type = game.Input
4494
}

0 commit comments

Comments
 (0)