package game import ( "fmt" "os" "strings" "testing" ) // dumpItemTables formats a game's per-seed item appearance tables in the // same layout the instrumented C reference prints: the potion colors, // scroll names, ring stones, and wand/staff materials, each generated by // consuming the RNG in a fixed order during New(). func dumpItemTables(seed int32, g *RogueGame) string { var b strings.Builder fmt.Fprintf(&b, "SEED %d\n", seed) fmt.Fprintln(&b, "POTIONS") for _, c := range g.Items.PotColors { fmt.Fprintln(&b, c) } fmt.Fprintln(&b, "SCROLLS") for _, s := range g.Items.ScrNames { fmt.Fprintln(&b, s) } fmt.Fprintln(&b, "RINGS") for _, s := range g.Items.RingStones { fmt.Fprintln(&b, s) } fmt.Fprintln(&b, "STICKS") for i := range g.Items.WandType { fmt.Fprintf(&b, "%s %s\n", g.Items.WandType[i], g.Items.WandMade[i]) } return b.String() } // TestSeedCompatItemTables proves the port's seed-compatibility claim: for // the same seed, the Go game generates the exact per-seed item appearance // tables as the C reference on modern-rogue. That requires the LCG and its // consumption order through the whole init sequence (init_probs → // init_player → init_names → init_colors → init_stones → init_materials) to // match C byte for byte. The golden is captured from an instrumented build // of the C game (testdata/README.md). func TestSeedCompatItemTables(t *testing.T) { golden, err := os.ReadFile("testdata/item_tables.golden") if err != nil { t.Fatalf("read golden: %v", err) } // These must match the seeds the golden was generated from // (testdata/README.md). seeds := []int32{1, 42, 12345, 99999} var got strings.Builder for _, seed := range seeds { g := New(Params{Seed: seed, Wizard: true}) got.WriteString(dumpItemTables(seed, g)) } if got.String() != string(golden) { t.Errorf("Go item tables diverge from the C reference at %s", firstDiff(string(golden), got.String())) } } // firstDiff returns a description of the first line where want and got // differ, for a readable failure. func firstDiff(want, got string) string { wl := strings.Split(want, "\n") gl := strings.Split(got, "\n") for i := 0; i < len(wl) || i < len(gl); i++ { w, g := "", "" if i < len(wl) { w = wl[i] } if i < len(gl) { g = gl[i] } if w != g { return fmt.Sprintf("line %d: C=%q Go=%q", i+1, w, g) } } return "no line difference (trailing content?)" }