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.

deck.gno

5.34 Kb · 190 lines
  1package engine
  2
  3import (
  4	"crypto/sha256"
  5	"encoding/binary"
  6	"math/rand"
  7	"strings"
  8
  9	"gno.land/p/g1th0masp67zfs9lg6d34qhghldhg6kjcqjuv7c2/gnoslinger/hex/v0"
 10)
 11
 12// Result is one line of a result card: a number of points, or a named
 13// penalty (rules 14.1, 14.21). Points are 0 or more; penalties are negative.
 14type Result int
 15
 16const (
 17	// LoseAim: the character loses its aim (no aim is tracked yet).
 18	LoseAim Result = -1
 19	// WildShot: every cocked gun goes off without hitting anything, is
 20	// uncocked and loses a shell; the aim is lost.
 21	WildShot Result = -2
 22	// Drop: an upright character turns DOWN, draws 3 delay cards and loses
 23	// its aim; no effect when already DOWN.
 24	Drop Result = -3
 25)
 26
 27// IsPoints reports whether the line is a number of points.
 28func (r Result) IsPoints() bool { return r >= 0 }
 29
 30// String returns the penalty name used in events and JSON, or "" for points.
 31func (r Result) String() string {
 32	switch r {
 33	case LoseAim:
 34		return "lose_aim"
 35	case WildShot:
 36		return "wild_shot"
 37	case Drop:
 38		return "drop"
 39	}
 40	return ""
 41}
 42
 43// ResultCard is one of the 108 result cards (rule 2.4): N is its number on
 44// the card. Delay and Wound are the DELAY and WOUND lines and Hex the HEX
 45// line (direction cards, 14.3: a relative direction, hex.Same or hex.Long).
 46// The upper block is either a FIRE hit chart with its Target Status letters
 47// (cards 1-100, see fire.gno) or a MALFUNCTION block (cards 101-108). The
 48// TAC line (optional rule 9) is not used by this game.
 49type ResultCard struct {
 50	N      int
 51	Delay  Result
 52	Wound  Result
 53	Hex    hex.Rel
 54	Fire   *Chart       // the hit chart of a FIRE card, nil on a MALFUNCTION
 55	Status string       // Target Status letters of a FIRE card (StatusMove...)
 56	Malf   *Malfunction // the block of a MALFUNCTION card, nil on a FIRE card
 57}
 58
 59// IsFire reports whether the card is a FIRE card (13.2).
 60func (c ResultCard) IsFire() bool { return c.Malf == nil }
 61
 62// DeckSize is the number of result cards.
 63const DeckSize = 108
 64
 65// Cards is the result deck in card order, index 0 = card 1.
 66var Cards = buildCards()
 67
 68func buildCards() (cards [DeckSize]ResultCard) {
 69	// Each row is a run of consecutive cards sharing the same three lines,
 70	// transcribed from the card images (assets/result_card_NNN.png).
 71	runs := []struct {
 72		from, to     int
 73		delay, wound Result
 74		hex          hex.Rel
 75	}{
 76		{1, 18, 0, 0, hex.Same},
 77		{19, 36, 0, 1, hex.Long},
 78		{37, 54, 1, 2, hex.Long},
 79		{55, 60, 1, 3, hex.AheadLeft},
 80		{61, 69, 2, 3, hex.Same},
 81		{70, 78, 2, 4, hex.Long},
 82		{79, 84, 2, 5, hex.AheadRight},
 83		{85, 87, 2, 6, hex.Ahead},
 84		{88, 93, LoseAim, 1, hex.BackRight},
 85		{94, 99, LoseAim, WildShot, hex.BackLeft},
 86		{100, 100, WildShot, Drop, hex.Back},
 87		{101, 103, WildShot, Drop, hex.Back},
 88		{104, 106, 1, Drop, hex.Ahead},
 89		{107, 108, Drop, Drop, hex.Back},
 90	}
 91	n := 0
 92	for _, r := range runs {
 93		for i := r.from; i <= r.to; i++ {
 94			c := ResultCard{N: i, Delay: r.delay, Wound: r.wound, Hex: r.hex}
 95			if i <= len(fireCards) {
 96				c.Fire = parseChart(fireCards[i-1].chart)
 97				c.Status = fireCards[i-1].status
 98			} else {
 99				c.Malf = &malfunctions[i-1-len(fireCards)]
100			}
101			cards[i-1] = c
102			n++
103		}
104	}
105	if n != DeckSize {
106		panic("result deck: wrong card count")
107	}
108	return cards
109}
110
111// Card returns result card n (1 to 108).
112func Card(n int) ResultCard { return Cards[n-1] }
113
114// Drawer hands out result cards to Resolve.
115type Drawer interface {
116	// Draw returns the next result card.
117	Draw() ResultCard
118}
119
120// Seed hashes the given parts into shuffle seed material.
121func Seed(parts ...string) [32]byte {
122	return sha256.Sum256([]byte(strings.Join(parts, "|")))
123}
124
125// Deck is the result deck shuffled once from a seed and dealt in order
126// (rule 5.7: the deck is reshuffled when exhausted). The same seed always
127// deals the same cards, so a turn can be replayed from its stored seed.
128type Deck struct {
129	r     *rand.Rand
130	order [DeckSize]int // card numbers in deal order
131	next  int
132	drawn []int // every card dealt so far, in order
133}
134
135// NewDeck shuffles the 108 cards with the seed.
136func NewDeck(seed [32]byte) *Deck {
137	s1 := binary.BigEndian.Uint64(seed[0:8])
138	s2 := binary.BigEndian.Uint64(seed[8:16])
139	d := &Deck{r: rand.New(rand.NewPCG(s1, s2))}
140	d.shuffle()
141	return d
142}
143
144func (d *Deck) shuffle() {
145	for i := range d.order {
146		d.order[i] = i + 1
147	}
148	d.r.Shuffle(DeckSize, func(i, j int) { d.order[i], d.order[j] = d.order[j], d.order[i] })
149	d.next = 0
150}
151
152// Draw implements Drawer: the next card of the shuffle, reshuffling the
153// whole deck once every card has been dealt.
154func (d *Deck) Draw() ResultCard {
155	if d.next >= DeckSize {
156		d.shuffle()
157	}
158	c := Cards[d.order[d.next]-1]
159	d.next++
160	d.drawn = append(d.drawn, c.N)
161	return c
162}
163
164// Order returns the card numbers in the order the deck deals them.
165func (d *Deck) Order() [DeckSize]int { return d.order }
166
167// Drawn returns the numbers of every card dealt so far, in order: the trace
168// of a turn's draws.
169func (d *Deck) Drawn() []int { return d.drawn }
170
171// Fixed is a Drawer that always deals the same card; tests use Fixed(37)
172// (DELAY 1) to match the BGA reference engine's stub.
173type Fixed int
174
175// Draw implements Drawer.
176func (f Fixed) Draw() ResultCard { return Card(int(f)) }
177
178// Sequence is a Drawer dealing the given card numbers in order, then the
179// last one forever; for tests.
180type Sequence []int
181
182// Draw implements Drawer.
183func (s *Sequence) Draw() ResultCard {
184	q := *s
185	n := q[0]
186	if len(q) > 1 {
187		*s = q[1:]
188	}
189	return Card(n)
190}