iT邦幫忙

2025 iThome 鐵人賽

DAY 29
1

主題

在前幾天,我們已經完成了數獨盤面的初始化、玩家輸入檢查、勝利判定與結束畫面。
今天的重點是進一步提升遊戲性:

  • 加入「關卡難度選擇」功能,讓玩家能挑戰不同難度的 Sudoku 題目。
  • 顯示遊戲執行時間,提供挑戰性與成就感,玩家能夠嘗試打破自己的紀錄。

🎯 功能目標

  • 提供三種難度選擇(簡單 / 中等 / 困難),影響預設題目的空格數。
  • 在遊戲畫面上方顯示計時器,隨著遊戲進行即時更新。
  • 遊戲結束時,顯示本局所花費的時間,並可與不同難度做比較。

🧩 關卡難度設計

通常,Sudoku 的難度取決於「保留的初始數字數量」:

  • 簡單 (Easy):大約保留 36–40 格 → 玩家較容易完成。
  • 中等 (Medium):保留 30–34 格 → 需要進行推理與觀察。
  • 困難 (Hard):只保留 24–28 格 → 高度挑戰性,需要多層邏輯。

程式實做

type Difficulty int

const (
	Easy   Difficulty = 36
	Medium Difficulty = 32
	Hard   Difficulty = 28
)

// handleToggleLevelDifficultButton
func (gameLayout *GameLayout) handleToggleLevelDifficultButton() {
	if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
		xPos, yPos := ebiten.CursorPosition()
		if (xPos >= 4*cellSize && xPos <= 5*cellSize) &&
			(yPos >= cellSize && yPos <= 2*cellSize) {
			gameLayout.difficultyLevel = (gameLayout.difficultyLevel + 1) % len(difficultyOptions)
			gameLayout.ResetGameWithLevel()
		}
	}
	gameLayout.isPlayerWin = gameLayout.checkIfPlayerWin()
}

toggleButton 繪製

var difficultyOptions = [3]game.Difficulty{
	game.Easy,
	game.Medium,
	game.Hard,
}
var difficultyIcons = map[game.Difficulty]string{
	game.Easy:   "🐣", // 小雞
	game.Medium: "🦊", // 狐狸
	game.Hard:   "🦁", // 獅子
}

func getLevelIconColor(difficulty game.Difficulty) color.Color {
	switch difficulty {
	case game.Medium: // dark Red
		return color.RGBA{0xdc, 0x14, 0x3c, 0xff}
	case game.Hard, game.Easy: // Gold
		return color.RGBA{0xFF, 0xD7, 0x00, 0xFF}
	default: // 其他都是預設 白色
		return color.RGBA{0xFF, 0xFF, 0xFF, 0xFF}
	}
}

func (gameLayout *GameLayout) drawLevelButtonWithIcon(screen *ebiten.Image) {
	vector.DrawFilledCircle(screen, float32(ScreenWidth/2), cellSize+cellSize/2, 25,
		getIconColor(DarkButton),
		true,
	)
	gameDifficulty := difficultyOptions[gameLayout.difficultyLevel]
	levelIcon := difficultyIcons[gameDifficulty]
	emojiValue := levelIcon
	emojiXPos := ScreenWidth / 2
	emojiYPos := cellSize + cellSize/2
	emojiOpts := &text.DrawOptions{}
	emojiOpts.ColorScale.ScaleWithColor(getLevelIconColor(gameDifficulty))
	emojiOpts.PrimaryAlign = text.AlignCenter
	emojiOpts.SecondaryAlign = text.AlignCenter
	emojiOpts.GeoM.Translate(float64(emojiXPos), float64(emojiYPos))
	text.Draw(screen, emojiValue, &text.GoTextFace{
		Source: emojiFaceSource,
		Size:   30,
	}, emojiOpts)
}

⏱️ 顯示執行時間

我們需要一個簡單的「計時器」來追蹤遊戲時間。
思路如下:

  1. 在遊戲開始時,記錄 startTime。
  2. 每次更新畫面時計算 elapsed = time.Since(startTime)。
  3. 轉換為 分:秒 格式,繪製在畫面上。

程式邏輯

func (game *Game) GetElaspedTime() int {
	return int(time.Since(game.StartTime).Seconds())
}

畫面實做

func (gameLayout *GameLayout) drawTimeLayout(screen *ebiten.Image) {
	elapsedSeconds := gameLayout.elapsedSeconds
	secondPart := elapsedSeconds % 60
	minutePart := (elapsedSeconds / 60) % 60
	textValue := fmt.Sprintf("%02d:%02d", minutePart, secondPart)
	textXPos := ScreenWidth - cellSize/4 + len(textValue)
	textYPos := cellSize / 2
	textOpts := &text.DrawOptions{}
	textOpts.ColorScale.ScaleWithColor(color.Black)
	textOpts.PrimaryAlign = text.AlignEnd
	textOpts.SecondaryAlign = text.AlignCenter
	textOpts.GeoM.Translate(float64(textXPos), float64(textYPos))
	text.Draw(screen, textValue, &text.GoTextFace{
		Source: mplusFaceSource,
		Size:   30,
	}, textOpts)
	emojiValue := "⏰"
	emojiXPos := ScreenWidth - 3*cellSize + len(emojiValue)
	emojiYPos := cellSize / 2
	emojiOpts := &text.DrawOptions{}
	emojiOpts.ColorScale.ScaleWithColor(getIconColor(IsClock))
	emojiOpts.PrimaryAlign = text.AlignStart
	emojiOpts.SecondaryAlign = text.AlignCenter
	emojiOpts.GeoM.Translate(float64(emojiXPos), float64(emojiYPos))
	text.Draw(screen, emojiValue, &text.GoTextFace{
		Source: emojiFaceSource,
		Size:   30,
	}, emojiOpts)
}

執行效果

https://ithelp.ithome.com.tw/upload/images/20250910/20111580C38ZCclSKF.pnghttps://ithelp.ithome.com.tw/upload/images/20250910/20111580UoxCyvIv1G.png
https://ithelp.ithome.com.tw/upload/images/20250910/20111580OOO3iviwxV.png

github action 測試結果

https://github.com/leetcode-golang-classroom/sudoku-game/actions/runs/17606836599/job/50019346536

📺 整合效果

  • 遊戲開始時 → 玩家選擇難度(例如透過按鍵、按鈕或下拉選單)。
  • 題目載入完成後 → 開始計時並顯示在畫面上方。
  • 遊戲結束 → 顯示「恭喜完成!」以及「本局花費時間」。

這樣玩家就能挑戰更高難度,並追求更快的完成速度。

✅ 今日收穫

  • 學會了如何在 Sudoku 中設計 難度機制(透過保留格數控制)。
  • 實作了 計時功能,能夠在遊戲中顯示時間並作為挑戰依據。

🔮 明日預計

明天的內容將進一步改善 遊戲的互動體驗:
我們將加入「數字快捷選擇面板」與「高亮提示」,讓玩家能更直覺輸入與觀察數字,提升遊戲流暢度。


上一篇
Sudoku 遊戲:遊戲勝利檢查與結束畫面設計
下一篇
Sudoku 遊戲: 遊戲的互動體驗
系列文
在 ai 時代 gopher 遊戲開發者的 30 天自我養成30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言