Skip to content

Commit 037782f

Browse files
committed
✨ (random): add random tile function
1 parent ec2b3e6 commit 037782f

File tree

3 files changed

+202
-4
lines changed

3 files changed

+202
-4
lines changed

internal/game.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,26 @@ package internal
33
// sideSize - 預設 sideSize
44
const sideSize = 4
55

6+
// randomPositioner - 根據給訂的 TotalSize 隨機產生一個位置
7+
type randomPositoner func(TotalSize int) int
8+
9+
// randomGenerator - 隨機給個 0 - 1 之間的機率數
10+
type randomGenerator func() float64
11+
612
// Game - 紀錄當下遊戲處理狀態
713
//
814
// board [][]int - 紀錄盤面狀態
915
type Game struct {
10-
board [][]int
16+
board [][]int
17+
randomPositonerFunc randomPositoner
18+
randomFunc randomGenerator
1119
}
1220

1321
// Init - 初始化
14-
func (g *Game) Init(data [][]int) {
22+
func (g *Game) Init(data [][]int, randomPosFunc randomPositoner, randomFunc randomGenerator) {
23+
// setup random functions
24+
g.randomPositonerFunc = randomPosFunc
25+
g.randomFunc = randomFunc
1526
// 建立棋盤
1627
g.board = make([][]int, sideSize)
1728
for index := range g.board {
@@ -31,6 +42,35 @@ func (g *Game) Init(data [][]int) {
3142
}
3243
}
3344

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++ {
50+
for c := 0; c < sideSize; c++ {
51+
if g.board[r][c] == 0 {
52+
emptyTiles = append(emptyTiles, [2]int{r, c})
53+
}
54+
}
55+
}
56+
// 如果所有格子都滿了
57+
if len(emptyTiles) == 0 {
58+
return
59+
}
60+
// 選出要填入的位置
61+
position := emptyTiles[g.randomPositonerFunc(len(emptyTiles))]
62+
// 90% 機率是 2 , 10% 機率則為 4
63+
value := 2
64+
if g.randomFunc() < 0.1 {
65+
value = 4
66+
}
67+
g.board[position[0]][position[1]] = value
68+
}
69+
3470
func NewGame() *Game {
35-
return &Game{}
71+
return &Game{
72+
nil,
73+
defaultRandomPositioner,
74+
defaultRandomFunc,
75+
}
3676
}

internal/game_test.go

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,147 @@ func TestGameInit(t *testing.T) {
9999
for _, tt := range tests {
100100
t.Run(tt.name, func(t *testing.T) {
101101
game := NewGame()
102-
game.Init(tt.input.board)
102+
game.Init(tt.input.board, nil, nil)
103103
assert.Equal(t, tt.want, game.board)
104104
})
105105
}
106106
}
107+
108+
func TestGameRandTile(t *testing.T) {
109+
type field struct {
110+
board [][]int
111+
randomPosFunc randomPositoner
112+
randomFunc randomGenerator
113+
}
114+
tests := []struct {
115+
name string
116+
input field
117+
want [][]int
118+
}{
119+
{
120+
name: "Case1",
121+
input: field{
122+
board: [][]int{
123+
{0, 0, 0, 0},
124+
{0, 2, 0, 0},
125+
{0, 0, 0, 0},
126+
{0, 0, 0, 4},
127+
},
128+
randomPosFunc: func(TotalSize int) int {
129+
return 1
130+
},
131+
randomFunc: func() float64 {
132+
return 0.2
133+
},
134+
},
135+
want: [][]int{
136+
{0, 2, 0, 0},
137+
{0, 2, 0, 0},
138+
{0, 0, 0, 0},
139+
{0, 0, 0, 4},
140+
},
141+
},
142+
{
143+
name: "Case2",
144+
input: field{
145+
board: [][]int{
146+
{2, 4, 8, 16},
147+
{32, 64, 128, 256},
148+
{512, 1024, 2048, 0},
149+
{2, 4, 8, 16},
150+
},
151+
randomPosFunc: func(TotalSize int) int {
152+
return 0
153+
},
154+
randomFunc: func() float64 {
155+
return 0.09
156+
},
157+
},
158+
want: [][]int{
159+
{2, 4, 8, 16},
160+
{32, 64, 128, 256},
161+
{512, 1024, 2048, 4},
162+
{2, 4, 8, 16},
163+
},
164+
},
165+
{
166+
name: "Case3",
167+
input: field{
168+
board: [][]int{
169+
{2, 4, 2, 4},
170+
{4, 2, 4, 2},
171+
{2, 4, 2, 4},
172+
{4, 2, 4, 0},
173+
},
174+
randomPosFunc: func(TotalSize int) int {
175+
return 0
176+
},
177+
randomFunc: func() float64 {
178+
return 0.5
179+
},
180+
},
181+
want: [][]int{
182+
{2, 4, 2, 4},
183+
{4, 2, 4, 2},
184+
{2, 4, 2, 4},
185+
{4, 2, 4, 2},
186+
},
187+
},
188+
{
189+
name: "Case4",
190+
input: field{
191+
board: [][]int{
192+
{8, 16, 32, 2},
193+
{4, 2, 8, 4},
194+
{16, 32, 4, 8},
195+
{2, 4, 16, 0},
196+
},
197+
randomPosFunc: func(TotalSize int) int {
198+
return 0
199+
},
200+
randomFunc: func() float64 {
201+
return 0.5
202+
},
203+
},
204+
want: [][]int{
205+
{8, 16, 32, 2},
206+
{4, 2, 8, 4},
207+
{16, 32, 4, 8},
208+
{2, 4, 16, 2},
209+
},
210+
},
211+
{
212+
name: "Case5 - full of tile no change",
213+
input: field{
214+
board: [][]int{
215+
{2, 4, 8, 16},
216+
{32, 64, 128, 256},
217+
{512, 1024, 2048, 2},
218+
{4, 8, 16, 32},
219+
},
220+
randomPosFunc: func(TotalSize int) int {
221+
return 0
222+
},
223+
randomFunc: func() float64 {
224+
return 0.5
225+
},
226+
},
227+
want: [][]int{
228+
{2, 4, 8, 16},
229+
{32, 64, 128, 256},
230+
{512, 1024, 2048, 2},
231+
{4, 8, 16, 32},
232+
},
233+
},
234+
}
235+
for _, tt := range tests {
236+
t.Run(tt.name, func(t *testing.T) {
237+
game := NewGame()
238+
game.Init(tt.input.board, tt.input.randomPosFunc, tt.input.randomFunc)
239+
// 模擬隨機產生 tile
240+
game.addRandomTile()
241+
assert.Equal(t, tt.want, game.board)
242+
})
243+
}
244+
245+
}

internal/random.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package internal
2+
3+
import (
4+
"math/rand"
5+
"time"
6+
)
7+
8+
var defaultRandomPositioner randomPositoner = func(totalSize int) int {
9+
if totalSize == 1 {
10+
return 0
11+
}
12+
rand.New(rand.NewSource(time.Now().UnixNano()))
13+
return rand.Intn(totalSize)
14+
}
15+
16+
var defaultRandomFunc randomGenerator = func() float64 {
17+
rand.New(rand.NewSource(time.Now().UnixNano()))
18+
return rand.Float64()
19+
}

0 commit comments

Comments
 (0)