From ab51a27b4cb643be9d63b7e00a390de104492bcc Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 23 May 2024 15:04:08 +0200 Subject: [PATCH] added+fixed game state loader, using NewGrid() to allocate new grids --- README.md | 3 ++- TODO.md | 2 ++ game.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++------ go.mod | 1 + go.sum | 2 ++ grid.go | 31 ++++++++++++++++++++++++-- main.go | 5 ++++- 7 files changed, 99 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2c4c3fe..f0e6791 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,13 @@ While it runs, there are a couple of commands you can use: * right mouse click: set a cell to dead * space: pause or resume the game * while game is paused: press n to forward one step -* q: quit * page up: speed up * page down: slow down * Mouse wheel: zoom in or out * move mouse while middle mouse button pressed: move canvas * escape: reset to 1:1 zoom +* s: save game state to file (can be loaded with -l) +* q: quit # Report bugs diff --git a/TODO.md b/TODO.md index 84bbb86..bc194a0 100644 --- a/TODO.md +++ b/TODO.md @@ -7,3 +7,5 @@ https://github.com/sachaos/go-life/tree/master/format/rle rle files: https://catagolue.hatsya.com/object/xq2_32mmgozg0igke72z1n2q1z0qgm1z31i2bsogzggqq261z1/b3s23 https://copy.sh/life/examples/ + +- Translate mouse click co-ordinates to world co-ordinates diff --git a/game.go b/game.go index 2048e2d..5bcbac7 100644 --- a/game.go +++ b/game.go @@ -38,7 +38,8 @@ type Game struct { World *ebiten.Image // actual image we render to WheelTurned bool // when user turns wheel multiple times, zoom faster Dragging bool // middle mouse is pressed, move canvas - LastCursorPos []int + LastCursorPos []int // used to check if the user is dragging + Statefile string // load game state from it if non-nil } func (game *Game) Layout(outsideWidth, outsideHeight int) (int, int) { @@ -325,13 +326,50 @@ func (game *Game) InitPattern() { } // initialize playing field/grid -func (game *Game) InitGrid() { - grid := &Grid{Data: make([][]int, game.Height)} +func (game *Game) InitGrid(grid *Grid) { + if grid != nil { + // use pre-loaded grid + game.Grids = []*Grid{ + grid, + NewGrid(grid.Width, grid.Height), + } + + game.History = NewGrid(grid.Width, grid.Height) + + return + } + + grida := NewGrid(game.Width, game.Height) + gridb := NewGrid(game.Width, game.Height) + history := NewGrid(game.Width, game.Height) + + for y := 0; y < game.Height; y++ { + if !game.Empty { + for x := 0; x < game.Width; x++ { + if rand.Intn(game.Density) == 1 { + history.Data[y][x] = 1 + grida.Data[y][x] = 1 + } + } + } + } + + game.Grids = []*Grid{ + grida, + gridb, + } + + game.History = history +} + +func (game *Game) _InitGrid(grid *Grid) { + + grida := &Grid{Data: make([][]int, game.Height)} gridb := &Grid{Data: make([][]int, game.Height)} history := &Grid{Data: make([][]int, game.Height)} for y := 0; y < game.Height; y++ { - grid.Data[y] = make([]int, game.Width) + grida.Data[y] = make([]int, game.Width) gridb.Data[y] = make([]int, game.Width) history.Data[y] = make([]int, game.Width) @@ -339,14 +377,14 @@ func (game *Game) InitGrid() { for x := 0; x < game.Width; x++ { if rand.Intn(game.Density) == 1 { history.Data[y][x] = 1 - grid.Data[y][x] = 1 + grida.Data[y][x] = 1 } } } } game.Grids = []*Grid{ - grid, + grida, gridb, } @@ -378,6 +416,20 @@ func (game *Game) InitTiles() { func (game *Game) Init() { // setup the game + var grid *Grid + + if game.Statefile != "" { + g, err := LoadState(game.Statefile) + if err != nil { + log.Fatalf("failed to load game state: %s", err) + } + + grid = g + + game.Width = grid.Width + game.Height = grid.Height + } + game.ScreenWidth = game.Cellsize * game.Width game.ScreenHeight = game.Cellsize * game.Height @@ -390,7 +442,7 @@ func (game *Game) Init() { game.World = ebiten.NewImage(game.ScreenWidth, game.ScreenHeight) - game.InitGrid() + game.InitGrid(grid) game.InitPattern() game.InitTiles() diff --git a/go.mod b/go.mod index e9aafdd..e918b52 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( ) require ( + github.com/alecthomas/repr v0.4.0 // indirect github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect github.com/ebitengine/purego v0.7.0 // indirect diff --git a/go.sum b/go.sum index 3b0eded..0fa22ed 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= diff --git a/grid.go b/grid.go index 318785f..a63e91d 100644 --- a/grid.go +++ b/grid.go @@ -15,6 +15,31 @@ type Grid struct { Width, Height int } +// Create new empty grid and allocate Data according to provided dimensions +func NewGrid(width, height int) *Grid { + grid := &Grid{ + Height: height, + Width: width, + Data: make([][]int, height), + } + + for y := 0; y < height; y++ { + grid.Data[y] = make([]int, width) + } + + return grid +} + +func (grid *Grid) Clone() *Grid { + newgrid := &Grid{} + + newgrid.Width = grid.Width + newgrid.Height = grid.Height + newgrid.Data = grid.Data + + return newgrid +} + func GetFilename(generations int64) string { now := time.Now() return fmt.Sprintf("dump-%s-%d.gol", now.Format("20060102150405"), generations) @@ -75,16 +100,18 @@ func LoadState(filename string) (*Grid, error) { // sanity check the grid explen := 0 rows := 0 - first := false + first := true for _, row := range grid.Data { length := len(row) if first { explen = length + first = false } if explen != length { - return nil, errors.New("all rows must be in the same length") + return nil, fmt.Errorf(fmt.Sprintf("all rows must be in the same length, got: %d, expected: %d", + length, explen)) } rows++ diff --git a/main.go b/main.go index 058bb08..5faabbd 100644 --- a/main.go +++ b/main.go @@ -50,7 +50,8 @@ func main() { "game speed: the higher the slower (default: 10)") pflag.StringVarP(&rule, "rule", "r", "B3/S23", "game rule") - pflag.StringVarP(&rlefile, "rlefile", "f", "", "RLE pattern file") + pflag.StringVarP(&rlefile, "rle-file", "f", "", "RLE pattern file") + pflag.StringVarP(&game.Statefile, "load-state-file", "l", "", "game state file") pflag.BoolVarP(&showversion, "version", "v", false, "show version") pflag.BoolVarP(&game.Paused, "paused", "p", false, "do not start simulation (use space to start)") @@ -73,6 +74,8 @@ func main() { if game.RLE.Width > game.Width || game.RLE.Height > game.Height { game.Width = game.RLE.Width * 2 game.Height = game.RLE.Height * 2 + fmt.Printf("rlew: %d, rleh: %d, w: %d, h: %d\n", + game.RLE.Width, game.RLE.Height, game.Width, game.Height) } // RLE needs an empty grid