Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

bullscows_test.gno

3.69 Kb · 141 lines
  1package bullscows
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nt/testutils/v0"
  9)
 10
 11func TestParseGuess(t *testing.T) {
 12	cases := []struct {
 13		in string
 14		ok bool
 15	}{
 16		{"1972", true},
 17		{"0123", true},
 18		{"11 2", false},
 19		{"123", false},
 20		{"12345", false},
 21		{"1123", false}, // repeated digit
 22		{"12a3", false},
 23	}
 24	for _, c := range cases {
 25		if _, ok := parseGuess(c.in); ok != c.ok {
 26			t.Fatalf("parseGuess(%q) ok = %v, want %v", c.in, ok, c.ok)
 27		}
 28	}
 29}
 30
 31func TestScore(t *testing.T) {
 32	target := [4]int{1, 9, 7, 2}
 33
 34	if b, c := score(target, [4]int{1, 9, 7, 2}); b != 4 || c != 0 {
 35		t.Fatalf("exact match: got bulls=%d cows=%d, want 4/0", b, c)
 36	}
 37	if b, c := score(target, [4]int{2, 7, 9, 1}); b != 0 || c != 4 {
 38		t.Fatalf("full rotation: got bulls=%d cows=%d, want 0/4", b, c)
 39	}
 40	if b, c := score(target, [4]int{1, 3, 4, 5}); b != 1 || c != 0 {
 41		t.Fatalf("one bull: got bulls=%d cows=%d, want 1/0", b, c)
 42	}
 43	if b, c := score(target, [4]int{5, 6, 7, 8}); b != 1 || c != 0 {
 44		t.Fatalf("no overlap but position 7: got bulls=%d cows=%d, want 1/0", b, c)
 45	}
 46}
 47
 48func TestNewSecretDistinctDigits(t *testing.T) {
 49	for seed := int64(0); seed < 50; seed++ {
 50		d := newSecret(seed)
 51		var seen [10]bool
 52		for _, v := range d {
 53			if v < 0 || v > 9 {
 54				t.Fatalf("digit out of range: %d", v)
 55			}
 56			if seen[v] {
 57				t.Fatalf("newSecret(%d) = %v has repeated digit", seed, d)
 58			}
 59			seen[v] = true
 60		}
 61	}
 62}
 63
 64func TestAddRecordKeepsFastestSorted(t *testing.T) {
 65	records = nil
 66	addRecord(record{player: "a", attempts: 10, round: 1})
 67	addRecord(record{player: "b", attempts: 3, round: 2})
 68	addRecord(record{player: "c", attempts: 7, round: 3})
 69	addRecord(record{player: "d", attempts: 1, round: 4})
 70	addRecord(record{player: "e", attempts: 9, round: 5})
 71	addRecord(record{player: "f", attempts: 2, round: 6}) // pushes out the worst
 72
 73	if len(records) != maxRecords {
 74		t.Fatalf("len(records) = %d, want %d", len(records), maxRecords)
 75	}
 76	want := []int{1, 2, 3, 7, 9}
 77	for i, w := range want {
 78		if records[i].attempts != w {
 79			t.Fatalf("records[%d].attempts = %d, want %d", i, records[i].attempts, w)
 80		}
 81	}
 82}
 83
 84func TestAddHistoryCaps(t *testing.T) {
 85	history = nil
 86	for i := 0; i < maxHistory+5; i++ {
 87		addHistory(guessLog{player: "p", guess: strconv.Itoa(i), bulls: 0, cows: 0})
 88	}
 89	if len(history) != maxHistory {
 90		t.Fatalf("len(history) = %d, want %d", len(history), maxHistory)
 91	}
 92	if history[len(history)-1].guess != strconv.Itoa(maxHistory+4) {
 93		t.Fatalf("history did not keep the most recent entries")
 94	}
 95}
 96
 97// Crossing: Guess mutates realm state, needs cur realm + cross(cur).
 98func TestGuessRoundtrip(cur realm, t *testing.T) {
 99	alice := testutils.TestAddress("alice")
100	testing.SetRealm(testing.NewUserRealm(alice))
101
102	round = 1
103	attempts = 0
104	history = nil
105	records = nil
106	secret = [4]int{1, 9, 7, 2}
107
108	out := Guess(cross(cur), "1234")
109	if !strings.Contains(out, "bulls") {
110		t.Fatalf("Guess() = %q, want bulls/cows feedback", out)
111	}
112	if attempts != 1 {
113		t.Fatalf("attempts = %d, want 1", attempts)
114	}
115
116	testing.SetRealm(testing.NewUserRealm(alice))
117	out = Guess(cross(cur), "1972")
118	if !strings.Contains(out, "SOLVED") {
119		t.Fatalf("Guess() = %q, want a solved message", out)
120	}
121	if round != 2 {
122		t.Fatalf("round = %d, want 2 (new round should start on solve)", round)
123	}
124	if len(records) != 1 || records[0].attempts != 2 {
125		t.Fatalf("records = %v, want one entry with attempts=2", records)
126	}
127}
128
129func TestGiveUpStartsNewRound(cur realm, t *testing.T) {
130	round = 1
131	attempts = 3
132	secret = [4]int{4, 5, 6, 7}
133
134	out := GiveUp(cross(cur))
135	if !strings.Contains(out, "4567") {
136		t.Fatalf("GiveUp() = %q, want it to reveal 4567", out)
137	}
138	if round != 2 || attempts != 0 {
139		t.Fatalf("round=%d attempts=%d, want round=2 attempts=0", round, attempts)
140	}
141}