examples/2048: Introduce TileData

This commit is contained in:
Hajime Hoshi 2016-07-31 00:55:29 +09:00
parent 3ce0572a81
commit 0ec07420f4
3 changed files with 110 additions and 34 deletions

View File

@ -46,13 +46,24 @@ func (b *Board) Update(input *Input) error {
return err return err
} }
} }
for t := range b.tiles {
if err := t.Update(); err != nil {
return err
}
}
nextTiles := map[*Tile]struct{}{}
for t := range b.tiles {
if t.current.value == 0 {
continue
}
nextTiles[t] = struct{}{}
}
b.tiles = nextTiles
return nil return nil
} }
func (b *Board) Move(dir Dir) error { func (b *Board) Move(dir Dir) error {
moved := false if moved := MoveTiles(b.tiles, b.size, dir); !moved {
b.tiles, moved = MoveTiles(b.tiles, b.size, dir)
if !moved {
return nil return nil
} }
if err := addRandomTile(b.tiles, b.size); err != nil { if err := addRandomTile(b.tiles, b.size); err != nil {

View File

@ -34,6 +34,7 @@ type TileData struct {
type Tile struct { type Tile struct {
current TileData current TileData
next TileData next TileData
moving bool
} }
func NewTile(value int, x, y int) *Tile { func NewTile(value int, x, y int) *Tile {
@ -54,16 +55,43 @@ func (t *Tile) Pos() (int, int) {
return t.current.x, t.current.y return t.current.x, t.current.y
} }
func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile { func (t *Tile) NextValue() int {
for t := range tiles { return t.next.value
if t.current.x == x && t.current.y == y {
return t
}
}
return nil
} }
func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) (map[*Tile]struct{}, bool) { func (t *Tile) NextPos() (int, int) {
return t.next.x, t.next.y
}
func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
var result *Tile
for t := range tiles {
if t.current.x != x || t.current.y != y {
continue
}
if result != nil {
panic("not reach")
}
result = t
}
return result
}
func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
var result *Tile
for t := range tiles {
if t.next.x != x || t.next.y != y || t.next.value == 0 {
continue
}
if result != nil {
panic("not reach")
}
result = t
}
return result
}
func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
vx, vy := dir.Vector() vx, vy := dir.Vector()
tx := []int{} tx := []int{}
ty := []int{} ty := []int{}
@ -78,8 +106,6 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) (map[*Tile]struct{},
sort.Sort(sort.Reverse(sort.IntSlice(ty))) sort.Sort(sort.Reverse(sort.IntSlice(ty)))
} }
nextTiles := map[*Tile]struct{}{}
merged := map[*Tile]bool{}
moved := false moved := false
for _, j := range ty { for _, j := range ty {
for _, i := range tx { for _, i := range tx {
@ -95,40 +121,48 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) (map[*Tile]struct{},
if ni < 0 || ni >= size || nj < 0 || nj >= size { if ni < 0 || ni >= size || nj < 0 || nj >= size {
break break
} }
tt := tileAt(nextTiles, ni, nj) tt := nextTileAt(tiles, ni, nj)
if tt == nil { if tt == nil {
ii = ni ii = ni
jj = nj jj = nj
moved = true moved = true
continue continue
} }
if t.current.value != tt.current.value { if t.current.value != tt.next.value {
break
}
if tt.current.value != tt.next.value {
// already merged
break break
} }
if !merged[tt] {
ii = ni ii = ni
jj = nj jj = nj
moved = true moved = true
}
break break
} }
if tt := tileAt(tiles, ii, jj); tt != t && tt != nil { if tt := nextTileAt(tiles, ii, jj); tt != t && tt != nil {
t.current.value += tt.current.value t.next.value = t.current.value + tt.next.value
merged[t] = true tt.next = TileData{}
delete(nextTiles, tt) } else {
t.next.value = t.current.value
} }
t.current.x = ii t.next.x = ii
t.current.y = jj t.next.y = jj
nextTiles[t] = struct{}{} t.moving = true
} }
} }
return nextTiles, moved return moved
} }
func addRandomTile(tiles map[*Tile]struct{}, size int) error { func addRandomTile(tiles map[*Tile]struct{}, size int) error {
cells := make([]bool, size*size) cells := make([]bool, size*size)
for t := range tiles { for t := range tiles {
i := t.current.x + t.current.y*size i := 0
if t.moving {
i = t.next.x + t.next.y*size
} else {
i = t.current.x + t.current.y*size
}
cells[i] = true cells[i] = true
} }
availableCells := []int{} availableCells := []int{}
@ -153,6 +187,16 @@ func addRandomTile(tiles map[*Tile]struct{}, size int) error {
return nil return nil
} }
func (t *Tile) Update() error {
if !t.moving {
return nil
}
t.current = t.next
t.next = TileData{}
t.moving = false
return nil
}
func colorToScale(clr color.Color) (float64, float64, float64, float64) { func colorToScale(clr color.Color) (float64, float64, float64, float64) {
r, g, b, a := clr.RGBA() r, g, b, a := clr.RGBA()
rf := float64(r) / 0xffff rf := float64(r) / 0xffff
@ -170,10 +214,10 @@ func colorToScale(clr color.Color) (float64, float64, float64, float64) {
func (t *Tile) Draw(boardImage *ebiten.Image) error { func (t *Tile) Draw(boardImage *ebiten.Image) error {
i, j := t.current.x, t.current.y i, j := t.current.x, t.current.y
v := t.current.value
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
x := i*tileSize + (i+1)*tileMargin x := i*tileSize + (i+1)*tileMargin
y := j*tileSize + (j+1)*tileMargin y := j*tileSize + (j+1)*tileMargin
v := t.current.value
op.GeoM.Translate(float64(x), float64(y)) op.GeoM.Translate(float64(x), float64(y))
r, g, b, a := colorToScale(tileBackgroundColor(v)) r, g, b, a := colorToScale(tileBackgroundColor(v))
op.ColorM.Scale(r, g, b, a) op.ColorM.Scale(r, g, b, a)

View File

@ -36,13 +36,19 @@ func cellsToTiles(cells []int, size int) map[*Tile]struct{} {
return tiles return tiles
} }
func tilesToCells(tiles map[*Tile]struct{}, size int) []int { func tilesToCells(tiles map[*Tile]struct{}, size int) ([]int, []int) {
cells := make([]int, size*size) cells := make([]int, size*size)
nextCells := make([]int, size*size)
for t := range tiles { for t := range tiles {
x, y := t.Pos() x, y := t.Pos()
cells[x+y*size] = t.Value() cells[x+y*size] = t.Value()
if t.NextValue() == 0 {
continue
} }
return cells nx, ny := t.NextPos()
nextCells[nx+ny*size] = t.NextValue()
}
return cells, nextCells
} }
func TestMoveTiles(t *testing.T) { func TestMoveTiles(t *testing.T) {
@ -82,6 +88,21 @@ func TestMoveTiles(t *testing.T) {
0, 0, 0, 2, 0, 0, 0, 2,
}, },
}, },
{
Dir: DirUp,
Input: []int{
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 2, 0,
0, 0, 0, 2,
},
Want: []int{
2, 2, 2, 2,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
},
},
{ {
Dir: DirRight, Dir: DirRight,
Input: []int{ Input: []int{
@ -189,10 +210,10 @@ func TestMoveTiles(t *testing.T) {
}, },
} }
for _, test := range testCases { for _, test := range testCases {
want, _ := tilesToCells(cellsToTiles(test.Want, size), size)
tiles := cellsToTiles(test.Input, size) tiles := cellsToTiles(test.Input, size)
want := tilesToCells(cellsToTiles(test.Want, size), size) MoveTiles(tiles, size, test.Dir)
gotTiles, _ := MoveTiles(tiles, size, test.Dir) _, got := tilesToCells(tiles, size)
got := tilesToCells(gotTiles, size)
if fmt.Sprint(got) != fmt.Sprint(want) { if fmt.Sprint(got) != fmt.Sprint(want) {
t.Errorf("dir: %s, input: %v, got %v; want %v", test.Dir.String(), test.Input, got, want) t.Errorf("dir: %s, input: %v, got %v; want %v", test.Dir.String(), test.Input, got, want)
} }