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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}