mirror of
https://codeberg.org/scip/golsky.git
synced 2025-12-16 20:20:57 +01:00
Compare commits
6 Commits
v0.0.8
...
menu2subsc
| Author | SHA1 | Date | |
|---|---|---|---|
| 11fc54399c | |||
| 80797a90fb | |||
| 0bd7c51bfd | |||
| 6afdf3fa62 | |||
| f2289238df | |||
| 37fb0d637f |
27
TODO.md
27
TODO.md
@@ -1,16 +1,17 @@
|
||||
- add all other options like size etc
|
||||
|
||||
- Clear screen problem:
|
||||
- it works when hitting the K key, immediately
|
||||
- its being turned off correctly when entering menu and on when leaving it
|
||||
- but regardless of the setting, after turning it off, the engine
|
||||
seems to run a couple of ticks with the old setting before switching
|
||||
scenes
|
||||
- looks like a race condition
|
||||
- obviously with K there are more loops before actually switching
|
||||
scenes, which doesn't happen with ESC
|
||||
|
||||
|
||||
- if grid lines is disabled, they appear anyway in the first frame (then disappear)
|
||||
|
||||
- changing options mid-game has no effect in most cases, even after a restart
|
||||
|
||||
- Statefile loading does not work correclty anymore. With larger grids
|
||||
everything is empty. With square grids part of the grid is cut
|
||||
off. Smaller grids load though
|
||||
|
||||
- Also when loading a state file, centering doesn't work anymore, I
|
||||
think the geom calculation is overthrown by the parser func. So, put
|
||||
this calc into its own func and always call. Or - as stated below -
|
||||
put it onto camera.go and call from Init().
|
||||
|
||||
- Zoom 0 on reset only works when world<screen. otherwise zoom would
|
||||
be negative So, on Init() memoize centered camera position or add a
|
||||
Center() function to camera.go. Then on reset calculate the zoom
|
||||
level so that the world fits into the screen.
|
||||
|
||||
68
config.go
68
config.go
@@ -3,12 +3,12 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/repr"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/tlinden/golsky/rle"
|
||||
)
|
||||
@@ -30,6 +30,8 @@ type Config struct {
|
||||
Restart, RestartGrid, RestartCache bool
|
||||
StartWithMenu bool
|
||||
Zoomfactor int
|
||||
InitialCamPos []float64
|
||||
DelayedStart bool // if true game, we wait. like pause but program induced
|
||||
|
||||
// for internal profiling
|
||||
ProfileFile string
|
||||
@@ -38,26 +40,19 @@ type Config struct {
|
||||
}
|
||||
|
||||
const (
|
||||
VERSION = "v0.0.7"
|
||||
VERSION = "v0.0.8"
|
||||
Alive = 1
|
||||
Dead = 0
|
||||
|
||||
DEFAULT_WIDTH = 600
|
||||
DEFAULT_HEIGHT = 400
|
||||
DEFAULT_CELLSIZE = 4
|
||||
DEFAULT_ZOOMFACTOR = 150 // FIXME, doesn't work?
|
||||
DEFAULT_GEOM = "640x384"
|
||||
DEFAULT_GRID_WIDTH = 600
|
||||
DEFAULT_GRID_HEIGHT = 400
|
||||
DEFAULT_CELLSIZE = 4
|
||||
DEFAULT_ZOOMFACTOR = 150
|
||||
DEFAULT_GEOM = "640x384"
|
||||
)
|
||||
|
||||
// parse given window geometry and adjust game settings according to it
|
||||
func (config *Config) ParseGeom(geom string) error {
|
||||
if geom == "" {
|
||||
config.ScreenWidth = config.Cellsize * config.Width
|
||||
config.ScreenHeight = config.Cellsize * config.Height
|
||||
config.Zoomfactor = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// force a geom
|
||||
geometry := strings.Split(geom, "x")
|
||||
if len(geometry) != 2 {
|
||||
@@ -74,25 +69,27 @@ func (config *Config) ParseGeom(geom string) error {
|
||||
return errors.New("failed to parse height, expecting integer")
|
||||
}
|
||||
|
||||
/*
|
||||
// adjust dimensions, account for grid width+height so that cells
|
||||
// fit into window
|
||||
config.ScreenWidth = width - (width % config.Width)
|
||||
config.ScreenHeight = height - (height % config.Height)
|
||||
|
||||
if config.ScreenWidth == 0 || config.ScreenHeight == 0 {
|
||||
return errors.New("the number of requested cells don't fit into the requested window size")
|
||||
}
|
||||
*/
|
||||
|
||||
config.ScreenWidth = width
|
||||
config.ScreenHeight = height
|
||||
|
||||
//config.Cellsize = config.ScreenWidth / config.Width
|
||||
config.Cellsize = DEFAULT_CELLSIZE
|
||||
config.Zoomfactor = DEFAULT_ZOOMFACTOR
|
||||
|
||||
repr.Println(config)
|
||||
// calculate the initial cam pos. It is negative if the total grid
|
||||
// size is smaller than the screen in a centered position, but
|
||||
// it's zero if it's equal or larger than the screen.
|
||||
config.InitialCamPos = make([]float64, 2)
|
||||
|
||||
config.InitialCamPos[0] = float64(((config.ScreenWidth - (config.Width * config.Cellsize)) / 2) * -1)
|
||||
if config.Width*config.Cellsize >= config.ScreenWidth {
|
||||
// must be positive if world wider than screen
|
||||
config.InitialCamPos[0] = math.Abs(config.InitialCamPos[0])
|
||||
}
|
||||
|
||||
if config.Height*config.Cellsize > config.ScreenHeight {
|
||||
config.InitialCamPos[1] = math.Abs(float64(((config.ScreenHeight - (config.Height * config.Cellsize)) / 2)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,6 +118,9 @@ func (config *Config) ParseRLE(rlefile string) error {
|
||||
config.Cellsize = config.ScreenWidth / config.Width
|
||||
}
|
||||
|
||||
fmt.Printf("width: %d, screenwidth: %d, rlewidth: %d, cellsize: %d\n",
|
||||
config.Width, config.ScreenWidth, config.RLE.Width, config.Cellsize)
|
||||
|
||||
// RLE needs an empty grid
|
||||
config.Empty = true
|
||||
|
||||
@@ -133,7 +133,7 @@ func (config *Config) ParseRLE(rlefile string) error {
|
||||
}
|
||||
|
||||
// parse a state file, if given, and adjust game settings accordingly
|
||||
func (config *Config) ParseStatefile(statefile string) error {
|
||||
func (config *Config) ParseStatefile() error {
|
||||
if config.Statefile == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -175,8 +175,8 @@ func ParseCommandline() (*Config, error) {
|
||||
)
|
||||
|
||||
// commandline params, most configure directly config flags
|
||||
pflag.IntVarP(&config.Width, "width", "W", DEFAULT_WIDTH, "grid width in cells")
|
||||
pflag.IntVarP(&config.Height, "height", "H", DEFAULT_HEIGHT, "grid height in cells")
|
||||
pflag.IntVarP(&config.Width, "width", "W", DEFAULT_GRID_WIDTH, "grid width in cells")
|
||||
pflag.IntVarP(&config.Height, "height", "H", DEFAULT_GRID_HEIGHT, "grid height in cells")
|
||||
pflag.IntVarP(&config.Cellsize, "cellsize", "c", 8, "cell size in pixels")
|
||||
pflag.StringVarP(&geom, "geom", "G", DEFAULT_GEOM, "window geometry in WxH in pixels, overturns -c")
|
||||
|
||||
@@ -191,7 +191,7 @@ func ParseCommandline() (*Config, error) {
|
||||
pflag.BoolVarP(&config.ShowVersion, "version", "v", false, "show version")
|
||||
pflag.BoolVarP(&config.Paused, "paused", "p", false, "do not start simulation (use space to start)")
|
||||
pflag.BoolVarP(&config.Debug, "debug", "d", false, "show debug info")
|
||||
pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", true, "draw grid lines")
|
||||
pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", false, "draw grid lines")
|
||||
pflag.BoolVarP(&config.Empty, "empty", "e", false, "start with an empty screen")
|
||||
pflag.BoolVarP(&config.Invert, "invert", "i", false, "invert colors (dead cell: black)")
|
||||
pflag.BoolVarP(&config.ShowEvolution, "show-evolution", "s", false, "show evolution traces")
|
||||
@@ -214,12 +214,18 @@ func ParseCommandline() (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = config.ParseStatefile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load rule from commandline when no rule came from RLE file,
|
||||
// default is B3/S23, aka conways game of life
|
||||
if config.Rule == nil {
|
||||
config.Rule = ParseGameRule(rule)
|
||||
}
|
||||
|
||||
//repr.Println(config)
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
|
||||
35
game.go
35
game.go
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
@@ -53,26 +51,29 @@ func (game *Game) Update() error {
|
||||
scene := game.GetCurrentScene()
|
||||
scene.Update()
|
||||
|
||||
fmt.Printf("Clear Screen: %t\n", ebiten.IsScreenClearedEveryFrame())
|
||||
next := scene.GetNext()
|
||||
if next != game.CurrentScene {
|
||||
scene.ResetNext()
|
||||
game.CurrentScene = next
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (game *Game) Draw(screen *ebiten.Image) {
|
||||
var nextscene Scene
|
||||
// first draw primary scene[s], although there are only 1
|
||||
for current, scene := range game.Scenes {
|
||||
if scene.IsPrimary() {
|
||||
// primary scenes always draw
|
||||
scene.Draw(screen)
|
||||
|
||||
if current == game.CurrentScene {
|
||||
// avoid to redraw it in the next step
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene := game.GetCurrentScene()
|
||||
|
||||
next := scene.GetNext()
|
||||
if next != game.CurrentScene {
|
||||
scene.ResetNext()
|
||||
game.CurrentScene = next
|
||||
nextscene = game.GetCurrentScene()
|
||||
ebiten.SetScreenClearedEveryFrame(nextscene.Clearscreen())
|
||||
}
|
||||
|
||||
scene.Draw(screen)
|
||||
|
||||
if nextscene != nil {
|
||||
nextscene.Draw(screen)
|
||||
}
|
||||
}
|
||||
|
||||
15
grid.go
15
grid.go
@@ -78,6 +78,19 @@ func (grid *Grid) FillRandom() {
|
||||
}
|
||||
}
|
||||
|
||||
func (grid *Grid) Dump() {
|
||||
for y := 0; y < grid.Height; y++ {
|
||||
for x := 0; x < grid.Width; x++ {
|
||||
if grid.Data[y][x] == 1 {
|
||||
fmt.Print("XX")
|
||||
} else {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// initialize using a given RLE pattern
|
||||
func (grid *Grid) LoadRLE(pattern *rle.RLE) {
|
||||
if pattern != nil {
|
||||
@@ -95,6 +108,8 @@ func (grid *Grid) LoadRLE(pattern *rle.RLE) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//grid.Dump()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
main.go
8
main.go
@@ -12,11 +12,12 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
dau := true
|
||||
var directstart bool
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
dau = false
|
||||
directstart = true
|
||||
}
|
||||
|
||||
config, err := ParseCommandline()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -28,8 +29,9 @@ func main() {
|
||||
}
|
||||
|
||||
start := Play
|
||||
if dau {
|
||||
if !directstart {
|
||||
start = Menu
|
||||
config.DelayedStart = true
|
||||
}
|
||||
game := NewGame(config, SceneName(start))
|
||||
|
||||
|
||||
@@ -46,14 +46,11 @@ func (scene *SceneMenu) SetNext(next SceneName) {
|
||||
scene.Next = next
|
||||
}
|
||||
|
||||
func (scene *SceneMenu) Clearscreen() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (scene *SceneMenu) Update() error {
|
||||
scene.Ui.Update()
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyEscape) || inpututil.IsKeyJustPressed(ebiten.KeyQ) {
|
||||
scene.Config.DelayedStart = false
|
||||
scene.Leave()
|
||||
}
|
||||
|
||||
@@ -61,6 +58,10 @@ func (scene *SceneMenu) Update() error {
|
||||
|
||||
}
|
||||
|
||||
func (scene *SceneMenu) IsPrimary() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (scene *SceneMenu) Draw(screen *ebiten.Image) {
|
||||
scene.Ui.Draw(screen)
|
||||
}
|
||||
@@ -102,7 +103,7 @@ func (scene *SceneMenu) Init() {
|
||||
separator2 := NewSeparator()
|
||||
separator3 := NewSeparator()
|
||||
|
||||
cancel := NewMenuButton("Close Window",
|
||||
cancel := NewMenuButton("Back",
|
||||
func(args *widget.ButtonClickedEventArgs) {
|
||||
scene.Leave()
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@ func (scene *SceneOptions) SetNext(next SceneName) {
|
||||
scene.Next = next
|
||||
}
|
||||
|
||||
func (scene *SceneOptions) Clearscreen() bool {
|
||||
func (scene *SceneOptions) IsPrimary() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ type Images struct {
|
||||
Black, White, Age1, Age2, Age3, Age4, Old *ebiten.Image
|
||||
}
|
||||
|
||||
const (
|
||||
DEBUG_FORMAT = "FPS: %0.2f, TPG: %d, M: %0.2fMB, Generations: %d\nScale: %.02f, Zoom: %d, Cam: %.02f,%.02f Cursor: %d,%d %s"
|
||||
)
|
||||
|
||||
type ScenePlay struct {
|
||||
Game *Game
|
||||
Config *Config
|
||||
@@ -61,6 +65,10 @@ func NewPlayScene(game *Game, config *Config) Scene {
|
||||
return scene
|
||||
}
|
||||
|
||||
func (scene *ScenePlay) IsPrimary() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (scene *ScenePlay) GetNext() SceneName {
|
||||
return scene.Next
|
||||
}
|
||||
@@ -73,10 +81,6 @@ func (scene *ScenePlay) SetNext(next SceneName) {
|
||||
scene.Next = next
|
||||
}
|
||||
|
||||
func (scene *ScenePlay) Clearscreen() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (scene *ScenePlay) CheckRule(state int64, neighbors int64) int64 {
|
||||
var nextstate int64
|
||||
|
||||
@@ -366,7 +370,8 @@ func (scene *ScenePlay) SaveRectRLE() {
|
||||
func (scene *ScenePlay) Update() error {
|
||||
if scene.Config.Restart {
|
||||
scene.Config.Restart = false
|
||||
scene.Init()
|
||||
scene.InitGrid(nil)
|
||||
scene.InitCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -395,7 +400,7 @@ func (scene *ScenePlay) ToggleCellOnCursorPos(alive int64) {
|
||||
x := int(worldX) / scene.Config.Cellsize
|
||||
y := int(worldY) / scene.Config.Cellsize
|
||||
|
||||
if x > -1 && y > -1 {
|
||||
if x > -1 && y > -1 && x < scene.Config.Width && y < scene.Config.Height {
|
||||
scene.Grids[scene.Index].Data[y][x] = alive
|
||||
scene.History.Data[y][x] = 1
|
||||
}
|
||||
@@ -484,10 +489,14 @@ func (scene *ScenePlay) DrawDebug(screen *ebiten.Image) {
|
||||
paused = "-- paused --"
|
||||
}
|
||||
|
||||
x, y := ebiten.CursorPosition()
|
||||
debug := fmt.Sprintf(
|
||||
"FPS: %0.2f, TPG: %d, Mem: %0.2fMB, Gen: %d, Scale: %.02f, Z: %d, Clear: %t %s",
|
||||
DEBUG_FORMAT,
|
||||
ebiten.ActualTPS(), scene.TPG, GetMem(), scene.Generations,
|
||||
scene.Game.Scale, scene.Camera.ZoomFactor, ebiten.IsScreenClearedEveryFrame(), paused)
|
||||
scene.Game.Scale, scene.Camera.ZoomFactor,
|
||||
scene.Camera.Position[0], scene.Camera.Position[1],
|
||||
x, y,
|
||||
paused)
|
||||
|
||||
FontRenderer.Renderer.SetSizePx(10 + int(scene.Game.Scale*10))
|
||||
FontRenderer.Renderer.SetTarget(screen)
|
||||
@@ -500,6 +509,7 @@ func (scene *ScenePlay) DrawDebug(screen *ebiten.Image) {
|
||||
|
||||
fmt.Println(debug)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// load a pre-computed pattern from RLE file
|
||||
@@ -549,6 +559,7 @@ func (scene *ScenePlay) InitGrid(grid *Grid) {
|
||||
gridb := NewGrid(scene.Config.Width, scene.Config.Height, scene.Config.Density, scene.Config.Empty)
|
||||
history := NewGrid(scene.Config.Width, scene.Config.Height, scene.Config.Density, scene.Config.Empty)
|
||||
|
||||
// startup is delayed until user has selected options
|
||||
grida.FillRandom()
|
||||
grida.Copy(history)
|
||||
|
||||
@@ -607,7 +618,6 @@ func (scene *ScenePlay) Init() {
|
||||
|
||||
if scene.Config.StateGrid != nil {
|
||||
grid = scene.Config.StateGrid
|
||||
|
||||
}
|
||||
|
||||
scene.Camera = Camera{
|
||||
@@ -617,12 +627,27 @@ func (scene *ScenePlay) Init() {
|
||||
},
|
||||
}
|
||||
|
||||
scene.World = ebiten.NewImage(scene.Config.ScreenWidth, scene.Config.ScreenHeight)
|
||||
scene.Cache = ebiten.NewImage(scene.Config.ScreenWidth, scene.Config.ScreenHeight)
|
||||
scene.World = ebiten.NewImage(
|
||||
scene.Config.Width*scene.Config.Cellsize,
|
||||
scene.Config.Height*scene.Config.Cellsize,
|
||||
)
|
||||
|
||||
scene.Cache = ebiten.NewImage(
|
||||
scene.Config.Width*scene.Config.Cellsize,
|
||||
scene.Config.Height*scene.Config.Cellsize,
|
||||
)
|
||||
|
||||
scene.InitTiles()
|
||||
scene.InitCache()
|
||||
scene.InitGrid(grid)
|
||||
|
||||
if scene.Config.DelayedStart && !scene.Config.Empty {
|
||||
scene.Config.Empty = true
|
||||
scene.InitGrid(grid)
|
||||
scene.Config.Empty = false
|
||||
} else {
|
||||
scene.InitGrid(grid)
|
||||
}
|
||||
|
||||
scene.InitPattern()
|
||||
|
||||
scene.Index = 0
|
||||
@@ -633,6 +658,9 @@ func (scene *ScenePlay) Init() {
|
||||
if scene.Config.Zoomfactor < 0 || scene.Config.Zoomfactor > 0 {
|
||||
scene.Camera.ZoomFactor = scene.Config.Zoomfactor
|
||||
}
|
||||
|
||||
scene.Camera.Position[0] = scene.Config.InitialCamPos[0]
|
||||
scene.Camera.Position[1] = scene.Config.InitialCamPos[1]
|
||||
}
|
||||
|
||||
// count the living neighbors of a cell
|
||||
|
||||
2
scene.go
2
scene.go
@@ -14,9 +14,9 @@ type Scene interface {
|
||||
SetNext(SceneName)
|
||||
GetNext() SceneName
|
||||
ResetNext()
|
||||
Clearscreen() bool
|
||||
Update() error
|
||||
Draw(screen *ebiten.Image)
|
||||
IsPrimary() bool // if true, this scene will be always drawn
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
17
widgets.go
17
widgets.go
@@ -69,23 +69,12 @@ func NewSeparator() widget.PreferredSizeLocateableWidget {
|
||||
widget.ContainerOpts.Layout(widget.NewRowLayout(
|
||||
widget.RowLayoutOpts.Direction(widget.DirectionVertical),
|
||||
widget.RowLayoutOpts.Padding(widget.Insets{
|
||||
Top: 20,
|
||||
Bottom: 20,
|
||||
Top: 3,
|
||||
Bottom: 0,
|
||||
}))),
|
||||
widget.ContainerOpts.WidgetOpts(
|
||||
widget.WidgetOpts.LayoutData(
|
||||
widget.RowLayoutData{Stretch: true})))
|
||||
|
||||
c.AddChild(widget.NewGraphic(
|
||||
widget.GraphicOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.RowLayoutData{
|
||||
Stretch: true,
|
||||
MaxHeight: 2,
|
||||
})),
|
||||
widget.GraphicOpts.ImageNineSlice(
|
||||
image.NewNineSliceColor(
|
||||
color.NRGBA{0xdf, 0xf4, 0xff, 0xff})),
|
||||
))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -125,7 +114,7 @@ func NewRowContainer(title string) *RowContainer {
|
||||
),
|
||||
widget.ContainerOpts.Layout(widget.NewRowLayout(
|
||||
widget.RowLayoutOpts.Direction(widget.DirectionVertical),
|
||||
widget.RowLayoutOpts.Padding(widget.NewInsetsSimple(20)),
|
||||
widget.RowLayoutOpts.Padding(widget.NewInsetsSimple(8)),
|
||||
widget.RowLayoutOpts.Spacing(0),
|
||||
)),
|
||||
widget.ContainerOpts.BackgroundImage(buttonImageHover),
|
||||
|
||||
Reference in New Issue
Block a user