Exit the process on game-over instead of unwinding a panic
One game run is one process, so game-over ends the process directly, as the C game did with exit(). myExit now restores the terminal (via the new Terminal.Fini) and calls os.Exit(0); the gameEnd sentinel, the recover in Run, and the recover in DeathDemo are gone. Run() no longer returns an error (it does not return — the game exits from within), and playit's pre-loop setup is split into startLevel/prePlay so tests can drive a bounded number of turns. Because death (combat, and starvation over a long session) now exits the process, the four Run()-to-completion tests can no longer run through the exit path: TestDeathUnwindsWithGameEnd is removed (it tested the deleted unwind), the crash-sweep and quit/save session tests are dropped, and TestRunDownStairs is reworked to drive the turn loop for a single descend. Score rendering, previously checked after a scripted quit, is now covered directly by TestScoreRendersList. Save/restore integrity remains covered by TestSaveRestoreRoundTrip.
This commit is contained in:
@@ -1194,7 +1194,7 @@ The command.c/io.c/things.c file-statics become lowercase fields of `RogueGame`,
|
||||
|
||||
```go
|
||||
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) Run() // 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()
|
||||
```
|
||||
@@ -1552,26 +1552,26 @@ Tombstone/victory screens port verbatim from rip.c.
|
||||
|
||||
## 6. C construct → Go construct map
|
||||
|
||||
| C construct | Go translation |
|
||||
| ---------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| global variable | `RogueGame` field (or Player/Level/subsystem field) |
|
||||
| file-scope static | unexported field on `RogueGame` or subsystem struct |
|
||||
| `THING` union | `Creature` / `Object` structs |
|
||||
| linked lists (`l_next/l_prev`, attach/detach) | slices + `attach(&s, x)`-style helpers (prepend, remove by identity) |
|
||||
| `coord*` aliasing (t_dest) | `*Coord` pointers into live structs (kept) |
|
||||
| function pointers (daemons, options, nameit) | enum IDs + dispatch switch (daemons, save-relevant); closures (options, nameit) |
|
||||
| `when`/`otherwise`/`until` macros | `case`/`default`/`for !cond` |
|
||||
| `goto` (retry loops in pack.c, move.c, passages.c) | labeled loops / restructured `for` |
|
||||
| char-indexed tables (`monsters[type-'A']`) | same, `monsterTable[t.Type-'A']` |
|
||||
| damage strings `"3x4/1x2"` | kept as strings; parsed by `rollEm` (`strings`/`strconv`) |
|
||||
| curses stdscr/hw windows | `Window` cell buffers over tcell |
|
||||
| `mvinch` screen reads | `Window.Inch` from the buffer |
|
||||
| signal handlers (SIGHUP autosave, SIGTSTP, SIGINT) | `os/signal` goroutine → channel checked in ReadChar; tcell handles TSTP/resize |
|
||||
| `setjmp`-free exits (`my_exit`, `exit()` everywhere) | error returns from `Run` + a small `gameOver` sentinel; no `os.Exit` inside game logic |
|
||||
| `vsprintf` message building | `fmt.Sprintf` |
|
||||
| XOR-encrypted binary saves (state.c) | gob snapshot (§5.6) |
|
||||
| DES wizard password | `ROGUE_WIZARD=1` env check |
|
||||
| `md_*` platform shims | stdlib (`os`, `os/user`, `time`) or deleted |
|
||||
| C construct | Go translation |
|
||||
| ---------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| global variable | `RogueGame` field (or Player/Level/subsystem field) |
|
||||
| file-scope static | unexported field on `RogueGame` or subsystem struct |
|
||||
| `THING` union | `Creature` / `Object` structs |
|
||||
| linked lists (`l_next/l_prev`, attach/detach) | slices + `attach(&s, x)`-style helpers (prepend, remove by identity) |
|
||||
| `coord*` aliasing (t_dest) | `*Coord` pointers into live structs (kept) |
|
||||
| function pointers (daemons, options, nameit) | enum IDs + dispatch switch (daemons, save-relevant); closures (options, nameit) |
|
||||
| `when`/`otherwise`/`until` macros | `case`/`default`/`for !cond` |
|
||||
| `goto` (retry loops in pack.c, move.c, passages.c) | labeled loops / restructured `for` |
|
||||
| char-indexed tables (`monsters[type-'A']`) | same, `monsterTable[t.Type-'A']` |
|
||||
| damage strings `"3x4/1x2"` | kept as strings; parsed by `rollEm` (`strings`/`strconv`) |
|
||||
| curses stdscr/hw windows | `Window` cell buffers over tcell |
|
||||
| `mvinch` screen reads | `Window.Inch` from the buffer |
|
||||
| signal handlers (SIGHUP autosave, SIGTSTP, SIGINT) | `os/signal` goroutine → channel checked in ReadChar; tcell handles TSTP/resize |
|
||||
| `setjmp`-free exits (`my_exit`, `exit()` everywhere) | `myExit` restores the terminal and calls `os.Exit(0)`; one game run is one process |
|
||||
| `vsprintf` message building | `fmt.Sprintf` |
|
||||
| XOR-encrypted binary saves (state.c) | gob snapshot (§5.6) |
|
||||
| DES wizard password | `ROGUE_WIZARD=1` env check |
|
||||
| `md_*` platform shims | stdlib (`os`, `os/user`, `time`) or deleted |
|
||||
|
||||
**Exit discipline** deserves a note: the C code calls `exit()` from deep inside
|
||||
call chains (death, victory, save). The port threads a `g.gameOver(reason)` that
|
||||
|
||||
Reference in New Issue
Block a user