From cd0ba6c8ee6bb11851b8a8c72757cfdc859234af Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 23 Jul 2026 05:39:31 +0700 Subject: [PATCH] Rename constructor to game.New(game.Params) per styleguide NewGame(Config) becomes New(Params), and Restore takes Params too, so the package's primary type gets the canonical New() constructor with a named-field Params struct (styleguide points 139, 159). cmd/rogue and all tests updated; ARCHITECTURE.md constructor references corrected. Pure rename, suite green. --- ARCHITECTURE.md | 22 +++++++++++----------- cmd/rogue/main.go | 16 ++++++++-------- game/effects_test.go | 4 ++-- game/fight_test.go | 2 +- game/game.go | 38 +++++++++++++++++++------------------- game/newlevel_test.go | 4 ++-- game/run_test.go | 10 +++++----- game/save.go | 8 ++++---- game/save_test.go | 4 ++-- game/tables_test.go | 6 +++--- 10 files changed, 57 insertions(+), 57 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a0f508f..0e0e90d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1076,7 +1076,7 @@ that `game` itself has no third-party imports: go.mod module git.eeqj.de/sneak/rgoue cmd/rogue/main.go package main — flag parsing, constructs RogueGame, calls Run game/ package game — the port, one .go file per .c file - game.go RogueGame struct, NewGame, Run (main.c) + game.go RogueGame struct, New, Run (main.c) rng.go seeded LCG (main.c: rnd/roll + RN macro) types.go Coord, Stats, flags, constants (rogue.h) object.go Object type (rogue.h THING _o arm) @@ -1120,7 +1120,7 @@ coherent, but everything is reachable from `g`. ```go // RogueGame is one complete game of Rogue: all state that was global in the -// C implementation, plus the terminal it plays on. Construct with NewGame, +// C implementation, plus the terminal it plays on. Construct with New, // then call Run. type RogueGame struct { // --- identity / RNG --- @@ -1193,15 +1193,15 @@ The command.c/io.c/things.c file-statics become lowercase fields of `RogueGame`, ### Entrypoints ```go -func NewGame(opts Config) *RogueGame // Config: seed, name, ROGUEOPTS string, score path, wizard -func (g *RogueGame) Run() error // main.c main()+playit(): init tables, first level, daemons, loop -func (g *RogueGame) command() // one turn (command.c) -func Restore(path string, opts Config) (*RogueGame, error) // save.c restore() +func New(params Params) *RogueGame // Params: seed, name, ROGUEOPTS string, score path, wizard +func (g *RogueGame) Run() error // main.c main()+playit(): init tables, first level, daemons, loop +func (g *RogueGame) command() // one turn (command.c) +func Restore(path string, params Params) (*RogueGame, error) // save.c restore() ``` `cmd/rogue/main.go` is a thin shell: flags (`-s` scores, `-d` death demo, -save-file arg, `SEED`/`ROGUEOPTS` env), `NewGame(...)` or `Restore(...)`, -`Run()`, exit code. +save-file arg, `SEED`/`ROGUEOPTS` env), `New(...)` or `Restore(...)`, `Run()`, +exit code. ## 4. Core types @@ -1366,7 +1366,7 @@ type Room struct { tables that are never written after init** (the only permitted package-level data — they are ROM, not state): `monsterTable [26]MonsterKind` (name, carry %, flags, stats), `initDam` weapon table, base `objInfo` tables (copied into -`RogueGame.Items` at NewGame so per-game mutation — probability resumming, +`RogueGame.Items` at New so per-game mutation — probability resumming, `oi_know`, `oi_guess` — stays instance-local), `eLevels`, `rainbow`, `stones`, `woods`, `metals`, `sylls`, trap names, help text, `lvlMons`/`wandMons` spawn strings, `hNames`/`mNames` combat messages. @@ -1634,6 +1634,6 @@ defers. - **Determinism test:** scripted input sequence against a fixed seed must produce identical final state across runs (guards hidden nondeterminism — map iteration, time dependence). -- **Save round-trip:** NewGame → play N scripted turns → save → restore → - compare full state. +- **Save round-trip:** New → play N scripted turns → save → restore → compare + full state. - `go vet` + `gofmt` clean at every commit. diff --git a/cmd/rogue/main.go b/cmd/rogue/main.go index d571e95..e202c12 100644 --- a/cmd/rogue/main.go +++ b/cmd/rogue/main.go @@ -29,10 +29,10 @@ func run() int { flag.Parse() - cfg := loadConfig() + params := loadParams() if *scores { - game.NewGame(cfg).ShowScores() + game.New(params).ShowScores() return 0 } @@ -45,13 +45,13 @@ func run() int { } defer t.Fini() - cfg.Term = t + params.Term = t var g *game.RogueGame if args := flag.Args(); len(args) == 1 && !*deathDemo { // restore a saved game - g, err = game.Restore(args[0], cfg) + g, err = game.Restore(args[0], params) if err != nil { t.Fini() fmt.Fprintln(os.Stderr, err) @@ -59,7 +59,7 @@ func run() int { return 1 } } else { - g = game.NewGame(cfg) + g = game.New(params) } if *deathDemo { @@ -81,10 +81,10 @@ func run() int { return 0 } -// loadConfig gathers the game configuration from the environment: home +// loadParams gathers the game parameters from the environment: home // directory, ROGUEOPTS, user name, wizard mode, and the dungeon seed // (main.c's startup). -func loadConfig() game.Config { +func loadParams() game.Params { home, _ := os.UserHomeDir() name := "" @@ -96,7 +96,7 @@ func loadConfig() game.Config { wizard := os.Getenv("ROGUE_WIZARD") != "" - return game.Config{ + return game.Params{ Seed: chooseSeed(wizard), Name: name, RogueOpts: os.Getenv("ROGUEOPTS"), diff --git a/game/effects_test.go b/game/effects_test.go index b432e68..3f6631f 100644 --- a/game/effects_test.go +++ b/game/effects_test.go @@ -7,7 +7,7 @@ import "testing" func mkGameInput(t *testing.T) *RogueGame { t.Helper() - g := NewGame(Config{Seed: 5, Term: &testTerm{}}) + g := New(Params{Seed: 5, Term: &testTerm{}}) g.NewLevel() g.Oldpos = g.Player.Pos g.Oldrp = g.roomIn(g.Player.Pos) @@ -183,7 +183,7 @@ func TestZapSlowMonster(t *testing.T) { } func TestParseOpts(t *testing.T) { - g := NewGame(Config{Seed: 1}) + g := New(Params{Seed: 1}) g.ParseOpts("terse,nojump,name=Conan,fruit=mango,inven=slow") if !g.Options.Terse { diff --git a/game/fight_test.go b/game/fight_test.go index 6157bf1..879cef9 100644 --- a/game/fight_test.go +++ b/game/fight_test.go @@ -7,7 +7,7 @@ import "testing" func mkGame(t *testing.T, seed int32) *RogueGame { t.Helper() - g := NewGame(Config{Seed: seed, Term: &testTerm{}}) + g := New(Params{Seed: seed, Term: &testTerm{}}) g.NewLevel() g.Oldpos = g.Player.Pos g.Oldrp = g.roomIn(g.Player.Pos) diff --git a/game/game.go b/game/game.go index a70c60c..4828916 100644 --- a/game/game.go +++ b/game/game.go @@ -31,9 +31,9 @@ type Options struct { InvType int // inven: inventory style (InvOver/InvSlow/InvClear) } -// Config carries everything needed to construct a game. -type Config struct { - Seed int32 // dungeon number; the caller derives it (time+pid or SEED env) +// Params carries everything needed to construct a game. +type Params struct { + Seed int32 // dungeon number; caller derives it (time+pid or SEED) Name string // player name (overridden by ROGUEOPTS name=) RogueOpts string // the ROGUEOPTS environment string Home string // home directory (save file default location) @@ -44,7 +44,7 @@ type Config struct { // RogueGame is one complete game of Rogue: every piece of state that was a // global (or file-scope static) in the C sources, plus the terminal it is -// played on. Construct with NewGame, then call Run. +// played on. Construct with New, then call Run. // // The struct grows with the port; fields appear in the phase that ports the // code owning them. @@ -143,22 +143,22 @@ type RogueGame struct { data *gameData } -// NewGame builds a game from cfg, seeds the RNG, and randomizes the item +// New builds a game from params, seeds the RNG, and randomizes the item // appearance tables (the front half of main.c main(); the player roll-up // and first level arrive with later porting phases). -func NewGame(cfg Config) *RogueGame { +func New(params Params) *RogueGame { g := &RogueGame{ data: newGameData(), - Rng: &Rng{Seed: cfg.Seed}, - Dnum: int(cfg.Seed), - Whoami: cfg.Name, + Rng: &Rng{Seed: params.Seed}, + Dnum: int(params.Seed), + Whoami: params.Name, Fruit: "slime-mold", - Home: cfg.Home, - Wizard: cfg.Wizard, - NoScore: cfg.Wizard, + Home: params.Home, + Wizard: params.Wizard, + NoScore: params.Wizard, Playing: true, Depth: 1, - ScorePath: cfg.ScorePath, + ScorePath: params.ScorePath, LastScore: -1, } g.Options = Options{ @@ -168,17 +168,17 @@ func NewGame(cfg Config) *RogueGame { } g.InvDescribe = true g.Msgs.SaveMsg = true - g.scr = NewScreen(cfg.Term) + g.scr = NewScreen(params.Term) g.Msgs.attach(g.scr, g.look, g.readchar) - g.FileName = cfg.Home + "/rogue.save" + g.FileName = params.Home + "/rogue.save" - g.rogueOpts = cfg.RogueOpts - if cfg.Wizard { + g.rogueOpts = params.RogueOpts + if params.Wizard { g.Player.Flags.Set(SenseMonsters) } - if cfg.RogueOpts != "" { - g.ParseOpts(cfg.RogueOpts) + if params.RogueOpts != "" { + g.ParseOpts(params.RogueOpts) } g.Monsters = g.data.monsterTable diff --git a/game/newlevel_test.go b/game/newlevel_test.go index 45f47e1..72a8387 100644 --- a/game/newlevel_test.go +++ b/game/newlevel_test.go @@ -8,7 +8,7 @@ import ( func genLevel(t *testing.T, seed int32) *RogueGame { t.Helper() - g := NewGame(Config{Seed: seed}) + g := New(Params{Seed: seed}) g.NewLevel() return g @@ -159,7 +159,7 @@ func TestNewLevelDeterministic(t *testing.T) { // mazes, dark rooms, traps, treasure rooms — as a crash/invariant sweep. func TestDeeperLevels(t *testing.T) { for _, seed := range []int32{7, 42, 1000, 31337} { - g := NewGame(Config{Seed: seed}) + g := New(Params{Seed: seed}) for depth := 1; depth <= 30; depth++ { g.Depth = depth g.NewLevel() diff --git a/game/run_test.go b/game/run_test.go index 4af9882..eaeee9d 100644 --- a/game/run_test.go +++ b/game/run_test.go @@ -10,7 +10,7 @@ import ( func TestRunScriptedSession(t *testing.T) { tt := &testTerm{input: []byte("hjkl.i Qy")} - g := NewGame(Config{Seed: 99, Term: tt}) + g := New(Params{Seed: 99, Term: tt}) err := g.Run() if err != nil { @@ -52,7 +52,7 @@ func TestRunManyTurns(t *testing.T) { script = append(script, " Q y Qy"...) tt := &testTerm{input: script} - g := NewGame(Config{Seed: 31337, Term: tt}) + g := New(Params{Seed: 31337, Term: tt}) err := g.Run() if err != nil { @@ -68,7 +68,7 @@ func TestRunManyTurns(t *testing.T) { // wizard style, then descends and keeps playing. func TestRunDownStairs(t *testing.T) { tt := &testTerm{input: []byte(">..Qy")} - g := NewGame(Config{Seed: 7, Term: tt}) + g := New(Params{Seed: 7, Term: tt}) g.NewLevel() g.Player.Pos = g.Level.Stairs // stand on the stairs g.restored = true // keep Run from regenerating the level @@ -99,7 +99,7 @@ func TestSaveCommandRoundTrip(t *testing.T) { // 'S' with no default file name goes straight to the name prompt. script := "S" + path + "\n" tt := &testTerm{input: []byte(script)} - g := NewGame(Config{Seed: 55, Term: tt}) + g := New(Params{Seed: 55, Term: tt}) g.FileName = "" // force the name prompt @@ -108,7 +108,7 @@ func TestSaveCommandRoundTrip(t *testing.T) { t.Fatalf("Run: %v", runErr) } - h, err := Restore(path, Config{Term: &testTerm{}}) + h, err := Restore(path, Params{Term: &testTerm{}}) if err != nil { t.Fatalf("Restore: %v", err) } diff --git a/game/save.go b/game/save.go index 625b07e..c8832a6 100644 --- a/game/save.go +++ b/game/save.go @@ -681,7 +681,7 @@ var ErrSaveOutOfDate = errors.New("sorry, saved game is out of date") // Restore restores a saved game from a file (save.c restore). The file is // deleted, as in C, to defeat restarting from the same save. -func Restore(path string, cfg Config) (*RogueGame, error) { +func Restore(path string, params Params) (*RogueGame, error) { f, err := os.Open(path) //nolint:gosec // G304: the save path is user-chosen by design if err != nil { return nil, err @@ -704,12 +704,12 @@ func Restore(path string, cfg Config) (*RogueGame, error) { data: newGameData(), Rng: &Rng{}, Playing: true, - ScorePath: cfg.ScorePath, + ScorePath: params.ScorePath, FileName: path, - rogueOpts: cfg.RogueOpts, + rogueOpts: params.RogueOpts, restored: true, } - g.scr = NewScreen(cfg.Term) + g.scr = NewScreen(params.Term) g.Msgs.attach(g.scr, g.look, g.readchar) g.applySnapshot(&st) diff --git a/game/save_test.go b/game/save_test.go index 34bad67..d41c7f6 100644 --- a/game/save_test.go +++ b/game/save_test.go @@ -29,7 +29,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) { t.Fatalf("saveFile: %v", saveErr) } - h, err := Restore(path, Config{Term: &testTerm{}}) + h, err := Restore(path, Params{Term: &testTerm{}}) if err != nil { t.Fatalf("Restore: %v", err) } @@ -154,7 +154,7 @@ func TestRestoreRejectsWrongVersion(t *testing.T) { t.Fatal(closeErr) } - _, restoreErr := Restore(path, Config{}) + _, restoreErr := Restore(path, Params{}) if restoreErr == nil { t.Error("restore accepted an out-of-date save") } diff --git a/game/tables_test.go b/game/tables_test.go index 313bc3f..f98ffaa 100644 --- a/game/tables_test.go +++ b/game/tables_test.go @@ -32,7 +32,7 @@ func TestProbabilitiesSumTo100(t *testing.T) { } func TestInitProbsCumulative(t *testing.T) { - g := NewGame(Config{Seed: 1}) + g := New(Params{Seed: 1}) last := g.Items.Potions[NumPotionTypes-1].Prob if last != 100 { @@ -47,14 +47,14 @@ func TestInitProbsCumulative(t *testing.T) { } func TestNewGameRandomizesAppearances(t *testing.T) { - g := NewGame(Config{Seed: 12345}) + g := New(Params{Seed: 12345}) checkPotionColors(t, g) checkScrollNames(t, g) checkWandMaterials(t, g) // Determinism: same seed, same appearances. - h := NewGame(Config{Seed: 12345}) + h := New(Params{Seed: 12345}) if h.Items != g.Items { t.Error("two games with the same seed produced different item lore") }