Skip to content

Commit 677ee1b

Browse files
committed
✨ (2048): add move with random tile
1 parent a5236f8 commit 677ee1b

File tree

7 files changed

+142
-40
lines changed

7 files changed

+142
-40
lines changed

cmd/main.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ import (
1010

1111
func main() {
1212
ebiten.SetWindowSize(layout.WinWidth, layout.WinHeight)
13-
ebiten.SetWindowTitle("2048 - Day 8 測試")
13+
ebiten.SetWindowTitle("2048 - Day 9 測試")
1414
gameInstance := game.NewGame()
15-
gameInstance.Init([][]int{
16-
{2, 4, 8, 16},
17-
{32, 64, 128, 256},
18-
{512, 1024, 2048, 4096},
19-
{0, 0, 0, 8192},
20-
}, nil, nil)
15+
gameInstance.AddRandomTile(game.Default)
16+
gameInstance.AddRandomTile(game.Default)
2117
gameLayout := layout.NameGameLayout(gameInstance)
2218
if err := ebiten.RunGame(gameLayout); err != nil {
2319
log.Fatal(err)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ go 1.24.0
55
require (
66
github.com/hajimehoshi/ebiten/v2 v2.8.8
77
github.com/stretchr/testify v1.10.0
8-
golang.org/x/image v0.20.0
98
)
109

1110
require (
@@ -17,6 +16,7 @@ require (
1716
github.com/jezek/xgb v1.1.1 // indirect
1817
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1918
github.com/pmezard/go-difflib v1.0.0 // indirect
19+
golang.org/x/image v0.20.0 // indirect
2020
golang.org/x/sync v0.8.0 // indirect
2121
golang.org/x/sys v0.25.0 // indirect
2222
golang.org/x/text v0.18.0 // indirect

internal/game/game.go

Lines changed: 103 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,25 @@ type Game struct {
1818
randomFunc randomGenerator
1919
}
2020

21+
type RandomType int
22+
23+
const (
24+
Default RandomType = iota
25+
DirectionUp
26+
DirectionDown
27+
DirectionLeft
28+
DirectionRight
29+
)
30+
2131
// Init - 初始化
2232
func (g *Game) Init(data [][]int, randomPosFunc randomPositoner, randomFunc randomGenerator) {
2333
// setup random functions
24-
g.randomPositonerFunc = randomPosFunc
25-
g.randomFunc = randomFunc
34+
if randomFunc != nil {
35+
g.randomPositonerFunc = randomPosFunc
36+
}
37+
if randomPosFunc != nil {
38+
g.randomFunc = randomFunc
39+
}
2640
// 建立棋盤
2741
g.board = make([][]int, sideSize)
2842
for index := range g.board {
@@ -42,17 +56,80 @@ func (g *Game) Init(data [][]int, randomPosFunc randomPositoner, randomFunc rand
4256
}
4357
}
4458

45-
// addRandomTile - 新增隨機的 2 或是 4 到一個空的 tile 內
46-
func (g *Game) addRandomTile() {
47-
// 蒐集所有空的 tile
48-
emptyTiles := make([][2]int, 0, sideSize*sideSize)
49-
for r := 0; r < sideSize; r++ {
59+
var collectStrategyMap map[RandomType]func(g *Game) [][2]int = map[RandomType]func(g *Game) [][2]int{
60+
Default: func(g *Game) [][2]int {
61+
emptyTiles := make([][2]int, 0, sideSize*sideSize)
62+
for r := 0; r < sideSize; r++ {
63+
for c := 0; c < sideSize; c++ {
64+
if g.board[r][c] == 0 {
65+
emptyTiles = append(emptyTiles, [2]int{r, c})
66+
}
67+
}
68+
}
69+
return emptyTiles
70+
},
71+
DirectionDown: func(g *Game) [][2]int {
72+
emptyTiles := make([][2]int, 0, sideSize*sideSize)
73+
for r := 0; r < sideSize; r++ {
74+
for c := 0; c < sideSize; c++ {
75+
if g.board[r][c] == 0 {
76+
if len(emptyTiles) >= 4 {
77+
break
78+
}
79+
emptyTiles = append(emptyTiles, [2]int{r, c})
80+
}
81+
}
82+
}
83+
return emptyTiles
84+
},
85+
DirectionUp: func(g *Game) [][2]int {
86+
emptyTiles := make([][2]int, 0, sideSize*sideSize)
87+
for r := sideSize - 1; r > 0; r-- {
88+
for c := 0; c < sideSize; c++ {
89+
if g.board[r][c] == 0 {
90+
if len(emptyTiles) >= 4 {
91+
break
92+
}
93+
emptyTiles = append(emptyTiles, [2]int{r, c})
94+
}
95+
}
96+
}
97+
return emptyTiles
98+
},
99+
DirectionLeft: func(g *Game) [][2]int {
100+
emptyTiles := make([][2]int, 0, sideSize*sideSize)
101+
for c := sideSize - 1; c > 0; c-- {
102+
for r := 0; r < sideSize; r++ {
103+
if g.board[r][c] == 0 {
104+
if len(emptyTiles) >= 4 {
105+
break
106+
}
107+
emptyTiles = append(emptyTiles, [2]int{r, c})
108+
}
109+
}
110+
}
111+
return emptyTiles
112+
},
113+
DirectionRight: func(g *Game) [][2]int {
114+
emptyTiles := make([][2]int, 0, sideSize*sideSize)
50115
for c := 0; c < sideSize; c++ {
51-
if g.board[r][c] == 0 {
52-
emptyTiles = append(emptyTiles, [2]int{r, c})
116+
for r := 0; r < sideSize; r++ {
117+
if g.board[r][c] == 0 {
118+
if len(emptyTiles) >= 4 {
119+
break
120+
}
121+
emptyTiles = append(emptyTiles, [2]int{r, c})
122+
}
53123
}
54124
}
55-
}
125+
return emptyTiles
126+
},
127+
}
128+
129+
// AddRandomTile - 新增隨機的 2 或是 4 到一個空的 tile 內
130+
func (g *Game) AddRandomTile(randomType RandomType) {
131+
// 蒐集所有空的 tile
132+
emptyTiles := collectStrategyMap[randomType](g)
56133

57134
// 如果所有格子都滿了
58135
if len(emptyTiles) == 0 {
@@ -110,8 +187,8 @@ func (g *Game) slideAndMergeLeft(row []int) []int {
110187
return result
111188
}
112189

113-
// moveLeft - 整個 board 同時左移
114-
func (g *Game) moveLeft() {
190+
// MoveLeft - 整個 board 同時左移
191+
func (g *Game) MoveLeft() {
115192
for r := 0; r < sideSize; r++ {
116193
g.board[r] = g.slideAndMergeLeft(g.board[r][:])
117194
}
@@ -139,43 +216,47 @@ func (g *Game) reverseRow(row []int) []int {
139216
return reversedRow
140217
}
141218

142-
// moveRight - 整個 board 同時往右
143-
func (g *Game) moveRight() {
219+
// MoveRight - 整個 board 同時往右
220+
func (g *Game) MoveRight() {
144221
// 先把整個 board 作 reverse
145222
for i := range g.board {
146223
g.board[i] = g.reverseRow(g.board[i])
147224
}
148225
// 把整個 board 往左移動
149-
g.moveLeft()
226+
g.MoveLeft()
150227
// 再整個 board 作 reverse 回來
151228
for i := range g.board {
152229
g.board[i] = g.reverseRow(g.board[i])
153230
}
154231
}
155232

156-
// moveUp - 整個 board 同時往上
157-
func (g *Game) moveUp() {
233+
// MoveUp - 整個 board 同時往上
234+
func (g *Game) MoveUp() {
158235
// 先把 board 作轉置
159236
g.board = g.transpose()
160237
// 再把 board 同時往左
161-
g.moveLeft()
238+
g.MoveLeft()
162239
// 再把 board 作轉置
163240
g.board = g.transpose()
164241
}
165242

166-
// moveDown - 把整個 board 往下移動
167-
func (g *Game) moveDown() {
243+
// MoveDown - 把整個 board 往下移動
244+
func (g *Game) MoveDown() {
168245
// 先把整個 board 轉置
169246
g.board = g.transpose()
170247
// 再把整個 board 往右滑
171-
g.moveRight()
248+
g.MoveRight()
172249
// 再把整個 board 轉置
173250
g.board = g.transpose()
174251
}
175252

176253
func NewGame() *Game {
254+
board := make([][]int, sideSize)
255+
for idx := range board {
256+
board[idx] = make([]int, sideSize)
257+
}
177258
return &Game{
178-
nil,
259+
board,
179260
defaultRandomPositioner,
180261
defaultRandomFunc,
181262
}

internal/game/game_move_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestGameMoveLeft(t *testing.T) {
7373
game := NewGame()
7474
game.Init(tt.input.board, nil, nil)
7575
// 模擬左移
76-
game.moveLeft()
76+
game.MoveLeft()
7777
assert.Equal(t, tt.want, game.board)
7878
})
7979
}
@@ -212,28 +212,28 @@ func TestGameMove(t *testing.T) {
212212
game := NewGame()
213213
game.Init(tt.input.board, nil, nil)
214214
// 模擬左移
215-
game.moveLeft()
215+
game.MoveLeft()
216216
assert.Equal(t, tt.want.left, game.board)
217217
})
218218
t.Run(fmt.Sprintf("%s right", tt.name), func(t *testing.T) {
219219
game := NewGame()
220220
game.Init(tt.input.board, nil, nil)
221221
// 模擬右移
222-
game.moveRight()
222+
game.MoveRight()
223223
assert.Equal(t, tt.want.right, game.board)
224224
})
225225
t.Run(fmt.Sprintf("%s up", tt.name), func(t *testing.T) {
226226
game := NewGame()
227227
game.Init(tt.input.board, nil, nil)
228228
// 模擬上移
229-
game.moveUp()
229+
game.MoveUp()
230230
assert.Equal(t, tt.want.up, game.board)
231231
})
232232
t.Run(fmt.Sprintf("%s down", tt.name), func(t *testing.T) {
233233
game := NewGame()
234234
game.Init(tt.input.board, nil, nil)
235235
// 模擬下移
236-
game.moveDown()
236+
game.MoveDown()
237237
assert.Equal(t, tt.want.down, game.board)
238238
})
239239
}

internal/game/game_random_tile_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func TestGameRandTile(t *testing.T) {
138138
game := NewGame()
139139
game.Init(tt.input.board, tt.input.randomPosFunc, tt.input.randomFunc)
140140
// 模擬隨機產生 tile
141-
game.addRandomTile()
141+
game.AddRandomTile(Default)
142142
assert.Equal(t, tt.want, game.board)
143143
})
144144
}

internal/layout/action_handler.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package layout
2+
3+
import (
4+
"github.com/hajimehoshi/ebiten/v2"
5+
"github.com/hajimehoshi/ebiten/v2/inpututil"
6+
"github.com/leetcode-golang-classroom/2048-game/internal/game"
7+
)
8+
9+
// Update - 用來處理畫面偵測,與使用者互動,並且觸發狀態變更
10+
func (g *GameLayout) Update() error {
11+
if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) {
12+
g.gameInstance.MoveUp()
13+
g.gameInstance.AddRandomTile(game.DirectionUp)
14+
}
15+
if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) {
16+
g.gameInstance.MoveDown()
17+
g.gameInstance.AddRandomTile(game.DirectionDown)
18+
}
19+
if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) {
20+
g.gameInstance.MoveLeft()
21+
g.gameInstance.AddRandomTile(game.DirectionLeft)
22+
}
23+
if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) {
24+
g.gameInstance.MoveRight()
25+
g.gameInstance.AddRandomTile(game.DirectionRight)
26+
}
27+
28+
return nil
29+
}

internal/layout/layout.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ func (g *GameLayout) Layout(outsideWidth, outsideHeight int) (int, int) {
7373
return WinWidth, WinHeight
7474
}
7575

76-
func (g *GameLayout) Update() error {
77-
return nil
78-
}
79-
8076
func NameGameLayout(gameInstance *game.Game) *GameLayout {
8177
return &GameLayout{
8278
gameInstance: gameInstance,

0 commit comments

Comments
 (0)