Skip to content

Commit 3375dda

Browse files
committed
✨ (2048): add game struct with init
0 parents  commit 3375dda

File tree

7 files changed

+271
-0
lines changed

7 files changed

+271
-0
lines changed

.github/workflow/go.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Go
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
jobs:
11+
build_and_test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
- name: Setup Go
17+
uses: actions/setup-go@v5
18+
with:
19+
go-version: 'stable'
20+
check-latest: true
21+
- name: Setup dependency
22+
run: go mod tidy
23+
- name: Test
24+
run: go test -v ./...

.gitignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Code coverage profiles and other test artifacts
15+
*.out
16+
coverage.*
17+
*.coverprofile
18+
profile.cov
19+
20+
# Dependency directories (remove the comment below to include it)
21+
# vendor/
22+
23+
# Go workspace file
24+
go.work
25+
go.work.sum
26+
27+
# env file
28+
.env
29+
30+
# Editor/IDE
31+
# .idea/
32+
# .vscode/

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 2048-game
2+
3+
這個 repository 主要用來 demo 使用 golang 來實做 2048 遊戲的功能
4+
5+
目標會採用 ebiten 來作畫面展現的遊戲引擎
6+
7+
預期會先從核心功能開始寫,最後再加上 ebiten 來作畫面渲染
8+
9+
## 2048 遊戲介紹
10+
11+
2048 是一款單人益智遊戲,玩家透過滑動方格中的數字進行合併,
12+
最終目標是在盤面上生成一個 數字 2048 的方塊。
13+
遊戲盤面為 4x4,每格可以放置一個數字(通常是 2 的冪次)。
14+
15+
## 遊戲規則說明
16+
17+
遊戲基本迴圈如下:
18+
19+
1. 初始化:隨機在兩個位置生成 2 或 4。
20+
2. 玩家滑動(上下左右四個方向)。
21+
3. 壓縮數字:將所有數字往滑動方向靠攏。
22+
4. 合併數字:相鄰且數值相同的數字會合併,數值加倍。
23+
5. 再壓縮:合併後的空格再次填補。
24+
6. 新增數字:在隨機空格生成 2 或 4。
25+
7. 檢查終止條件(勝利 / 失敗)。
26+
8. 重複步驟 2~7。
27+
28+
## 滑動與合併規則
29+
30+
這是 2048 最重要的邏輯,先用向左滑為例:
31+
32+
33+
|操作前|壓縮非零數字|合併相同數字|再壓縮|
34+
|:-:|:-:|:-:|:-:|
35+
|2 0 2 4|2 2 4 0|4 0 4 0|4 4 0 0|
36+
|4 4 4 4|4 4 4 4|8 0 8 0|8 8 0 0|
37+
38+
* 壓縮(Compress):將所有非零數字往滑動方向移動。
39+
* 合併(Merge):相鄰且相同的數字合併成一個,數值翻倍。
40+
* 再次壓縮:把合併後出現的空格補齊。
41+
42+
## 終止條件
43+
44+
遊戲會在以下情況結束:
45+
46+
* 勝利條件
47+
48+
盤面上出現數字 2048(或玩家自訂目標,例如 4096)。
49+
50+
* 失敗條件
51+
52+
盤面已無空格,且 四個方向都無法再合併。

go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/leetcode-golang-classroom/2048-game
2+
3+
go 1.24.0
4+
5+
require github.com/stretchr/testify v1.10.0
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/game.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package internal
2+
3+
// sideSize - 預設 sideSize
4+
const sideSize = 4
5+
6+
// Game - 紀錄當下遊戲處理狀態
7+
//
8+
// board [][]int - 紀錄盤面狀態
9+
type Game struct {
10+
board [][]int
11+
}
12+
13+
// Init - 初始化
14+
func (g *Game) Init(data [][]int) {
15+
// 建立棋盤
16+
g.board = make([][]int, sideSize)
17+
for index := range g.board {
18+
g.board[index] = make([]int, sideSize)
19+
}
20+
// checkout input value
21+
if len(data) != sideSize || len(data[0]) != sideSize {
22+
return
23+
}
24+
// setup data
25+
for r := range sideSize {
26+
for c := range sideSize {
27+
if data[r][c] != 0 {
28+
g.board[r][c] = data[r][c]
29+
}
30+
}
31+
}
32+
}
33+
34+
func NewGame() *Game {
35+
return &Game{}
36+
}

internal/game_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package internal
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestGameInit(t *testing.T) {
10+
type field struct {
11+
board [][]int
12+
}
13+
tests := []struct {
14+
name string
15+
input field
16+
want [][]int
17+
}{
18+
{
19+
name: "Empty Input",
20+
input: field{
21+
board: nil,
22+
},
23+
want: [][]int{
24+
{0, 0, 0, 0},
25+
{0, 0, 0, 0},
26+
{0, 0, 0, 0},
27+
{0, 0, 0, 0},
28+
},
29+
},
30+
{
31+
name: "Case1",
32+
input: field{
33+
board: [][]int{
34+
{2, 4, 0, 0},
35+
{8, 0, 0, 0},
36+
{16, 0, 4, 0},
37+
{0, 0, 0, 0},
38+
},
39+
},
40+
want: [][]int{
41+
{2, 4, 0, 0},
42+
{8, 0, 0, 0},
43+
{16, 0, 4, 0},
44+
{0, 0, 0, 0},
45+
},
46+
},
47+
{
48+
name: "Case2",
49+
input: field{
50+
board: [][]int{
51+
{128, 64, 32, 16},
52+
{8, 0, 0, 2},
53+
{4, 0, 0, 4},
54+
{2, 4, 8, 16},
55+
},
56+
},
57+
want: [][]int{
58+
{128, 64, 32, 16},
59+
{8, 0, 0, 2},
60+
{4, 0, 0, 4},
61+
{2, 4, 8, 16},
62+
},
63+
},
64+
{
65+
name: "Case3",
66+
input: field{
67+
board: [][]int{
68+
{0, 0, 0, 0},
69+
{0, 2048, 0, 0},
70+
{0, 0, 0, 0},
71+
{0, 0, 0, 0},
72+
},
73+
},
74+
want: [][]int{
75+
{0, 0, 0, 0},
76+
{0, 2048, 0, 0},
77+
{0, 0, 0, 0},
78+
{0, 0, 0, 0},
79+
},
80+
},
81+
{
82+
name: "Case4",
83+
input: field{
84+
board: [][]int{
85+
{16, 8, 4, 2},
86+
{32, 16, 8, 4},
87+
{64, 32, 16, 8},
88+
{128, 64, 32, 16},
89+
},
90+
},
91+
want: [][]int{
92+
{16, 8, 4, 2},
93+
{32, 16, 8, 4},
94+
{64, 32, 16, 8},
95+
{128, 64, 32, 16},
96+
},
97+
},
98+
}
99+
for _, tt := range tests {
100+
t.Run(tt.name, func(t *testing.T) {
101+
game := NewGame()
102+
game.Init(tt.input.board)
103+
assert.Equal(t, tt.want, game.board)
104+
})
105+
}
106+
}

0 commit comments

Comments
 (0)