Skip to content

Commit a5236f8

Browse files
committed
✨ (2048): add render game tile
1 parent 7c6cf27 commit a5236f8

File tree

10 files changed

+219
-13
lines changed

10 files changed

+219
-13
lines changed

cmd/main.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ import (
44
"log"
55

66
"github.com/hajimehoshi/ebiten/v2"
7+
"github.com/leetcode-golang-classroom/2048-game/internal/game"
78
"github.com/leetcode-golang-classroom/2048-game/internal/layout"
89
)
910

1011
func main() {
1112
ebiten.SetWindowSize(layout.WinWidth, layout.WinHeight)
12-
ebiten.SetWindowTitle("2048 - Day 7 測試")
13-
game := layout.NameGameLayout()
14-
if err := ebiten.RunGame(game); err != nil {
13+
ebiten.SetWindowTitle("2048 - Day 8 測試")
14+
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)
21+
gameLayout := layout.NameGameLayout(gameInstance)
22+
if err := ebiten.RunGame(gameLayout); err != nil {
1523
log.Fatal(err)
1624
}
1725
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ 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
89
)
910

1011
require (
1112
github.com/davecgh/go-spew v1.1.1 // indirect
1213
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect
1314
github.com/ebitengine/hideconsole v1.0.0 // indirect
1415
github.com/ebitengine/purego v0.8.0 // indirect
16+
github.com/go-text/typesetting v0.2.0 // indirect
1517
github.com/jezek/xgb v1.1.1 // indirect
1618
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1719
github.com/pmezard/go-difflib v1.0.0 // indirect
1820
golang.org/x/sync v0.8.0 // indirect
1921
golang.org/x/sys v0.25.0 // indirect
22+
golang.org/x/text v0.18.0 // indirect
2023
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
2124
gopkg.in/yaml.v3 v3.0.1 // indirect
2225
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj
66
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
77
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
88
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
9+
github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho=
10+
github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I=
11+
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY=
12+
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
13+
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
14+
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
915
github.com/hajimehoshi/ebiten/v2 v2.8.8 h1:xyMxOAn52T1tQ+j3vdieZ7auDBOXmvjUprSrxaIbsi8=
1016
github.com/hajimehoshi/ebiten/v2 v2.8.8/go.mod h1:durJ05+OYnio9b8q0sEtOgaNeBEQG7Yr7lRviAciYbs=
1117
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
@@ -15,6 +21,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1521
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1622
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
1723
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
24+
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
25+
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
1826
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1927
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2028
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
@@ -25,6 +33,8 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
2533
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
2634
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
2735
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
36+
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
37+
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
2838
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2939
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
3040
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/fonts/embed.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2022 The Ebitengine Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fonts
16+
17+
import (
18+
_ "embed"
19+
)
20+
21+
var (
22+
//go:embed mplus-1p-regular.ttf
23+
MPlus1pRegular_ttf []byte
24+
25+
//go:embed pressstart2p.ttf
26+
PressStart2P_ttf []byte
27+
)

internal/fonts/license.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# License
2+
3+
## mplus-1p-regular.ttf
4+
5+
```
6+
M+ FONTS Copyright (C) 2002-2015 M+ FONTS PROJECT
7+
8+
-
9+
10+
LICENSE_E
11+
12+
13+
14+
15+
These fonts are free software.
16+
Unlimited permission is granted to use, copy, and distribute them, with
17+
or without modification, either commercially or noncommercially.
18+
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
19+
20+
21+
http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/
22+
```
23+
24+
## PressStart2P-vaV7.ttf
25+
26+
```
27+
Copyright (c) 2011, Cody "CodeMan38" Boisclair ([email protected]),
28+
with Reserved Font Name "Press Start".
29+
30+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
31+
```
1.58 MB
Binary file not shown.

internal/fonts/pressstart2p.ttf

80.5 KB
Binary file not shown.

internal/game/game.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,7 @@ func NewGame() *Game {
180180
defaultRandomFunc,
181181
}
182182
}
183+
184+
func (g *Game) Data(row int, col int) int {
185+
return g.board[row][col]
186+
}

internal/layout/font.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package layout
2+
3+
import (
4+
"bytes"
5+
"image/color"
6+
"log"
7+
"strconv"
8+
9+
"github.com/hajimehoshi/ebiten/v2/text/v2"
10+
"github.com/leetcode-golang-classroom/2048-game/internal/fonts"
11+
)
12+
13+
var (
14+
mplusFaceSource *text.GoTextFaceSource
15+
)
16+
17+
func init() {
18+
s, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
19+
if err != nil {
20+
log.Fatal(err)
21+
}
22+
mplusFaceSource = s
23+
}
24+
25+
func getTileColor(value int) color.Color {
26+
switch value {
27+
case 0:
28+
return color.RGBA{0x77, 0x6e, 0x65, 0xff}
29+
case 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536:
30+
return color.RGBA{0xf9, 0xf6, 0xf2, 0xff}
31+
}
32+
panic("not reach")
33+
}
34+
35+
func getFontSize(value int) float64 {
36+
str := strconv.Itoa(value)
37+
size := 48.0
38+
switch {
39+
case 3 < len(str):
40+
size = 24
41+
case 2 < len(str):
42+
size = 32
43+
}
44+
return size
45+
}
46+
47+
func (*GameLayout) tileBackgroundColor(value int) color.Color {
48+
switch value {
49+
case 0:
50+
return color.NRGBA{0xee, 0xe4, 0xda, 0x59}
51+
case 2:
52+
return color.RGBA{0xee, 0xe0, 0x0a, 0xff}
53+
case 4:
54+
return color.RGBA{0xee, 0xaf, 0x08, 0xff}
55+
case 8:
56+
return color.RGBA{0xf2, 0xb1, 0x79, 0xff}
57+
case 16:
58+
return color.RGBA{0xf5, 0x95, 0x63, 0xff}
59+
case 32:
60+
return color.RGBA{0xf6, 0x7c, 0x5f, 0xff}
61+
case 64:
62+
return color.RGBA{0xf6, 0x5e, 0x3b, 0xff}
63+
case 128:
64+
return color.RGBA{0xed, 0xcf, 0x72, 0xff}
65+
case 256:
66+
return color.RGBA{0xed, 0xcc, 0x61, 0xff}
67+
case 512:
68+
return color.RGBA{0xed, 0xc8, 0x50, 0xff}
69+
case 1024:
70+
return color.RGBA{0xed, 0xc5, 0x3f, 0xff}
71+
case 2048:
72+
return color.RGBA{0xed, 0xc2, 0x2e, 0xff}
73+
case 4096:
74+
return color.NRGBA{0xa3, 0x49, 0xa4, 0x7f}
75+
case 8192:
76+
return color.NRGBA{0xa3, 0x49, 0xa4, 0xb2}
77+
case 16384:
78+
return color.NRGBA{0xa3, 0x49, 0xa4, 0xcc}
79+
case 32768:
80+
return color.NRGBA{0xa3, 0x49, 0xa4, 0xe5}
81+
case 65536:
82+
return color.NRGBA{0xa3, 0x49, 0xa4, 0xff}
83+
}
84+
panic("not reach")
85+
}

internal/layout/layout.go

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package layout
22

33
import (
4+
"fmt"
45
"image/color"
56

67
"github.com/hajimehoshi/ebiten/v2"
8+
"github.com/hajimehoshi/ebiten/v2/text/v2"
9+
"github.com/leetcode-golang-classroom/2048-game/internal/game"
710
)
811

912
const (
@@ -14,21 +17,54 @@ const (
1417
WinHeight = tileSize*gridSize + padding*(gridSize+1)
1518
)
1619

17-
type GameLayout struct{}
20+
type GameLayout struct {
21+
gameInstance *game.Game
22+
}
23+
24+
// drawCell - 透過目前值來畫出目前 cell 的格子顏色
25+
func (g *GameLayout) drawCell(screen *ebiten.Image, value, row, col int) {
26+
cellXPos := padding + col*(tileSize+padding)
27+
cellYPos := padding + row*(tileSize+padding)
28+
cellColor := getTileColor(value)
29+
cell := ebiten.NewImage(tileSize, tileSize)
30+
cell.Fill(cellColor)
31+
op := &ebiten.DrawImageOptions{}
32+
op.GeoM.Translate(float64(cellXPos), float64(cellYPos))
33+
op.ColorScale.ScaleWithColor(g.tileBackgroundColor(value))
34+
screen.DrawImage(cell, op)
35+
}
36+
37+
// drawTileText - 透過目前值來畫出目前 cell 的文字顏色
38+
func (g *GameLayout) drawTileText(screen *ebiten.Image, value, row, col int) {
39+
if value > 0 {
40+
// 繪製數字 (置中)
41+
textValue := fmt.Sprintf("%d", value)
42+
textXPos := (col+1)*padding + col*tileSize + (tileSize)/2
43+
textYPos := (row+1)*padding + row*tileSize + (tileSize)/2
44+
textOpts := &text.DrawOptions{}
45+
textOpts.ColorScale.ScaleWithColor(getTileColor(value))
46+
textOpts.PrimaryAlign = text.AlignCenter
47+
textOpts.SecondaryAlign = text.AlignCenter
48+
textOpts.GeoM.Translate(float64(textXPos), float64(textYPos))
49+
text.Draw(screen, textValue, &text.GoTextFace{
50+
Source: mplusFaceSource,
51+
Size: getFontSize(value),
52+
}, textOpts)
53+
}
54+
}
1855

1956
func (g *GameLayout) Draw(screen *ebiten.Image) {
2057
// 背景色
2158
screen.Fill(color.RGBA{250, 248, 239, 255})
2259
// 畫 4x4 格子
2360
for row := 0; row < gridSize; row++ {
2461
for col := 0; col < gridSize; col++ {
25-
op := &ebiten.DrawImageOptions{}
26-
x := padding + col*(tileSize+padding)
27-
y := padding + row*(tileSize+padding)
28-
rect := ebiten.NewImage(tileSize, tileSize)
29-
rect.Fill(color.RGBA{205, 193, 180, 255})
30-
op.GeoM.Translate(float64(x), float64(y))
31-
screen.DrawImage(rect, op)
62+
// 取出值
63+
value := g.gameInstance.Data(row, col)
64+
// 畫格子背景
65+
g.drawCell(screen, value, row, col)
66+
// 畫文字
67+
g.drawTileText(screen, value, row, col)
3268
}
3369
}
3470
}
@@ -41,6 +77,8 @@ func (g *GameLayout) Update() error {
4177
return nil
4278
}
4379

44-
func NameGameLayout() *GameLayout {
45-
return &GameLayout{}
80+
func NameGameLayout(gameInstance *game.Game) *GameLayout {
81+
return &GameLayout{
82+
gameInstance: gameInstance,
83+
}
4684
}

0 commit comments

Comments
 (0)