diff --git a/examples/2048/2048/board.go b/examples/2048/2048/board.go index 0d64ef034..d785acba2 100644 --- a/examples/2048/2048/board.go +++ b/examples/2048/2048/board.go @@ -46,13 +46,24 @@ func (b *Board) Update(input *Input) error { 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 } func (b *Board) Move(dir Dir) error { - moved := false - b.tiles, moved = MoveTiles(b.tiles, b.size, dir) - if !moved { + if moved := MoveTiles(b.tiles, b.size, dir); !moved { return nil } if err := addRandomTile(b.tiles, b.size); err != nil { diff --git a/examples/2048/2048/tile.go b/examples/2048/2048/tile.go index e00d60f5c..9d37b9d7e 100644 --- a/examples/2048/2048/tile.go +++ b/examples/2048/2048/tile.go @@ -34,6 +34,7 @@ type TileData struct { type Tile struct { current TileData next TileData + moving bool } 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 } -func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile { - for t := range tiles { - if t.current.x == x && t.current.y == y { - return t - } - } - return nil +func (t *Tile) NextValue() int { + return t.next.value } -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() tx := []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))) } - nextTiles := map[*Tile]struct{}{} - merged := map[*Tile]bool{} moved := false for _, j := range ty { 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 { break } - tt := tileAt(nextTiles, ni, nj) + tt := nextTileAt(tiles, ni, nj) if tt == nil { ii = ni jj = nj moved = true continue } - if t.current.value != tt.current.value { + if t.current.value != tt.next.value { break } - if !merged[tt] { - ii = ni - jj = nj - moved = true + if tt.current.value != tt.next.value { + // already merged + break } + ii = ni + jj = nj + moved = true break } - if tt := tileAt(tiles, ii, jj); tt != t && tt != nil { - t.current.value += tt.current.value - merged[t] = true - delete(nextTiles, tt) + if tt := nextTileAt(tiles, ii, jj); tt != t && tt != nil { + t.next.value = t.current.value + tt.next.value + tt.next = TileData{} + } else { + t.next.value = t.current.value } - t.current.x = ii - t.current.y = jj - nextTiles[t] = struct{}{} + t.next.x = ii + t.next.y = jj + t.moving = true } } - return nextTiles, moved + return moved } func addRandomTile(tiles map[*Tile]struct{}, size int) error { cells := make([]bool, size*size) 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 } availableCells := []int{} @@ -153,6 +187,16 @@ func addRandomTile(tiles map[*Tile]struct{}, size int) error { 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) { r, g, b, a := clr.RGBA() 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 { i, j := t.current.x, t.current.y + v := t.current.value op := &ebiten.DrawImageOptions{} x := i*tileSize + (i+1)*tileMargin y := j*tileSize + (j+1)*tileMargin - v := t.current.value op.GeoM.Translate(float64(x), float64(y)) r, g, b, a := colorToScale(tileBackgroundColor(v)) op.ColorM.Scale(r, g, b, a) diff --git a/examples/2048/2048/tile_test.go b/examples/2048/2048/tile_test.go index 5a3c1c3c0..543653170 100644 --- a/examples/2048/2048/tile_test.go +++ b/examples/2048/2048/tile_test.go @@ -36,13 +36,19 @@ func cellsToTiles(cells []int, size int) map[*Tile]struct{} { 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) + nextCells := make([]int, size*size) for t := range tiles { x, y := t.Pos() cells[x+y*size] = t.Value() + if t.NextValue() == 0 { + continue + } + nx, ny := t.NextPos() + nextCells[nx+ny*size] = t.NextValue() } - return cells + return cells, nextCells } func TestMoveTiles(t *testing.T) { @@ -82,6 +88,21 @@ func TestMoveTiles(t *testing.T) { 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, Input: []int{ @@ -189,10 +210,10 @@ func TestMoveTiles(t *testing.T) { }, } for _, test := range testCases { + want, _ := tilesToCells(cellsToTiles(test.Want, size), size) tiles := cellsToTiles(test.Input, size) - want := tilesToCells(cellsToTiles(test.Want, size), size) - gotTiles, _ := MoveTiles(tiles, size, test.Dir) - got := tilesToCells(gotTiles, size) + MoveTiles(tiles, size, test.Dir) + _, got := tilesToCells(tiles, size) if fmt.Sprint(got) != fmt.Sprint(want) { t.Errorf("dir: %s, input: %v, got %v; want %v", test.Dir.String(), test.Input, got, want) }