今天我們要完成 2048 遊戲的核心動作之一——滑動合併,並先以「左滑」為例實作。
我們會先在 CLI 環境驗證邏輯,等確定正確後,再整合到 Ebiten 畫面更新邏輯。
在 2048 中,「左滑」時每一行數字會:
範例:
初始行: [2, 0, 2, 4]
去掉空格: [2, 2, 4, 0]
合併後: [4, 0, 4, 0]
再去空格: [4, 4, 0, 0]
我們的盤面在 Day 3 已經用 二維整數陣列 表示:
var board = [4][4]int{
{2, 0, 2, 4},
{0, 4, 4, 0},
{2, 2, 0, 2},
{0, 0, 0, 2},
}
我們先實作一個可以處理「單行左滑」的函式,然後讓它去處理整個盤面的每一行。
// slideAndMergeLeft - 向左滑動並且合併
func (g *Game) slideAndMergeLeft(row []int) []int {
// 1 去掉空格
filtered := make([]int, 0, len(row))
for _, v := range row {
if v != 0 {
filtered = append(filtered, v)
}
}
// 假設沒有空格
if len(filtered) == 0 {
return row
}
// 2 合併相鄰相同數字
for i := 0; i < len(filtered)-1; i++ {
if filtered[i] == filtered[i+1] {
filtered[i] *= 2
filtered[i+1] = 0
i++ // 跳過剛合併的數字
}
}
// 3 再次去掉空格
result := make([]int, 0, len(row))
for _, v := range filtered {
if v != 0 {
result = append(result, v)
}
}
// 4 補充剩下的空格為 0
for len(result) < len(row) {
result = append(result, 0)
}
return result
}
// moveLeft - 整個 board 同時左移
func (g *Game) moveLeft() {
for r := 0; r < sideSize; r++ {
g.board[r] = g.slideAndMergeLeft(g.board[r][:])
}
}
func TestGameMoveLeft(t *testing.T) {
type field struct {
board [][]int
}
tests := []struct {
name string
input field
want [][]int
}{
{
name: "case1: 單行多次合併",
input: field{
board: [][]int{
{4, 4, 4, 4},
{2, 2, 0, 0},
{2, 0, 2, 0},
{8, 0, 0, 8},
},
},
want: [][]int{
{8, 8, 0, 0},
{4, 0, 0, 0},
{4, 0, 0, 0},
{16, 0, 0, 0},
},
},
{
name: "case2: 新生成的數字不參與當回合合併",
input: field{
board: [][]int{
{2, 2, 4, 8},
{0, 0, 0, 0},
{4, 4, 8, 8},
{2, 2, 2, 2},
},
},
want: [][]int{
{4, 4, 8, 0},
{0, 0, 0, 0},
{8, 16, 0, 0},
{4, 4, 0, 0},
},
},
{
name: "case3: 無合併,只有移動",
input: field{
board: [][]int{
{2, 4, 8, 16},
{0, 2, 0, 4},
{8, 0, 4, 0},
{2, 4, 2, 4},
},
},
want: [][]int{
{2, 4, 8, 16},
{2, 4, 0, 0},
{8, 4, 0, 0},
{2, 4, 2, 4},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
game := NewGame()
game.Init(tt.input.board, nil, nil)
// 模擬左移
game.moveLeft()
assert.Equal(t, tt.want, game.board)
})
}
}
gemini -p "你是一位 Golang 與 2048 遊戲邏輯驗證專家。
現在我提供一個 4x4 遊戲盤面,請你模擬 2048 的『左滑』規則,並驗證我程式輸出的結果是否正確。
【2048 左滑規則】:
1. 所有數字向左靠,移除空格(0)。
2. 相鄰且相同的數字會合併成它們的和,並將該格右邊的數字設為 0。
3. 合併後再次移除空格(0),並在右側補 0 至長度一致。
4. 同一回合中,每個數字最多只能被合併一次。
【輸入範例】
初始盤面:
[2 0 2 4]
[0 4 4 0]
[2 2 0 2]
[0 0 0 2]
我的程式輸出:
[4 4 0 0]
[8 0 0 0]
[4 2 0 0]
[2 0 0 0]
請你依規則驗證我的輸出是否正確。
如果正確,請回覆『驗證通過』並簡述理由。
如果錯誤,請指出錯誤的行與正確的期望結果,並解釋錯誤原因。
請同時產生三組額外的測資(初始盤面與正確輸出),方便我繼續測試程式。"
三組測資,涵蓋了一些常見的邊界情況,供您繼續測試。
測資 1:測試單行多次合併
[4 4 4 4]
[2 2 0 0]
[2 0 2 0]
[8 0 0 8]
[8 8 0 0]
[4 0 0 0]
[4 0 0 0]
[16 0 0 0]
測資 2:測試新生成的數字不參與當回合合併
[2 2 4 8]
[0 0 0 0]
[4 4 8 8]
[2 2 2 2]
[4 4 8 0]
[0 0 0 0]
[8 16 0 0]
[4 4 0 0]
測資 3:測試無合併,僅移動
[2 4 8 16]
[0 2 0 4]
[8 0 4 0]
[2 4 2 4]
[2 4 8 16]
[2 4 0 0]
[8 4 0 0]
[2 4 2 4]
https://github.com/leetcode-golang-classroom/2048-game/actions/runs/17046494123/job/48323566098
明天我們會 將左滑邏輯延伸到四個方向(上、右、下),並探討如何透過矩陣轉置與反轉,讓同一個函式處理所有方向的滑動,避免重複寫邏輯。