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.

encode.gno

6.75 Kb · 236 lines
  1package engine
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/g1th0masp67zfs9lg6d34qhghldhg6kjcqjuv7c2/gnoslinger/cards/v0"
  8	"gno.land/p/g1th0masp67zfs9lg6d34qhghldhg6kjcqjuv7c2/gnoslinger/hex/v0"
  9)
 10
 11// Compact text encodings of the engine's values, for what a realm keeps
 12// per turn: a struct stored on chain costs every field its type and
 13// value, so a turn's events as structs weigh over ten kilobytes, as one
 14// string a tenth of that. Fields are joined with '|', records with ';',
 15// gun slots with ',' and gun fields with ':'; none of the values (codes,
 16// hex ids, numbers) contain those characters. Decoding is lenient: a
 17// malformed number reads as 0, so a damaged record never breaks a view.
 18
 19// EncodeEvents packs events into one string; DecodeEvents unpacks it.
 20func EncodeEvents(evs []Event) string {
 21	var b strings.Builder
 22	for i, e := range evs {
 23		if i > 0 {
 24			b.WriteByte(';')
 25		}
 26		b.WriteString(strings.Join([]string{
 27			strconv.Itoa(e.Seg), strconv.Itoa(e.Player), e.Kind, string(e.Action), e.From, e.To,
 28			strconv.Itoa(int(e.Facing)), boolStr(e.Down), strconv.Itoa(e.N), strconv.Itoa(e.Delay),
 29			e.Reason, strconv.Itoa(e.Card), e.Result, strconv.Itoa(e.Endurance), strconv.Itoa(e.Target),
 30			e.Hit, strconv.Itoa(e.Range), string(e.Gun), strconv.Itoa(e.GunID), strconv.Itoa(e.Step),
 31			e.Chart, e.Status,
 32		}, "|"))
 33	}
 34	return b.String()
 35}
 36
 37// DecodeEvents unpacks what EncodeEvents produced.
 38func DecodeEvents(s string) []Event {
 39	if s == "" {
 40		return nil
 41	}
 42	recs := strings.Split(s, ";")
 43	evs := make([]Event, 0, len(recs))
 44	for _, r := range recs {
 45		f := strings.Split(r, "|")
 46		if len(f) != 22 {
 47			continue
 48		}
 49		evs = append(evs, Event{
 50			Seg: atoi(f[0]), Player: atoi(f[1]), Kind: f[2], Action: cards.Code(f[3]), From: f[4], To: f[5],
 51			Facing: hex.Dir(atoi(f[6])), Down: f[7] == "1", N: atoi(f[8]), Delay: atoi(f[9]),
 52			Reason: f[10], Card: atoi(f[11]), Result: f[12], Endurance: atoi(f[13]), Target: atoi(f[14]),
 53			Hit: f[15], Range: atoi(f[16]), Gun: GunType(f[17]), GunID: atoi(f[18]), Step: atoi(f[19]),
 54			Chart: f[20], Status: f[21],
 55		})
 56	}
 57	return evs
 58}
 59
 60// EncodeCharacter packs a character, guns included; DecodeCharacter unpacks it.
 61func EncodeCharacter(c Character) string {
 62	guns := make([]string, 0, MaxGuns)
 63	for _, g := range c.Guns {
 64		if g.Type != NoGun {
 65			guns = append(guns, encodeGun(g))
 66		}
 67	}
 68	var killers [MaxPlayers]int
 69	for q, k := range c.Killers {
 70		if k {
 71			killers[q] = 1
 72		}
 73	}
 74	return strings.Join([]string{
 75		c.Hex, strconv.Itoa(int(c.Facing)), boolStr(c.Down), strconv.Itoa(c.Delay), strconv.Itoa(int(c.Status)),
 76		boolStr(c.PlayedFoot), boolStr(c.PlayedRun), boolStr(c.RanLastTurn), boolStr(c.Dropped),
 77		strconv.Itoa(c.Aim), c.AimHex, strconv.Itoa(c.Endurance), strconv.Itoa(c.Exhausted),
 78		strconv.Itoa(c.Serious), strconv.Itoa(c.GunArm), strconv.Itoa(c.OtherArm), strconv.Itoa(c.Leg),
 79		strings.Join(guns, ","),
 80		strconv.Itoa(c.AimAt), perSeat(killers), perSeat(c.SeriousFrom), perSeat(c.LegFrom),
 81		perSeat(c.Hurt),
 82	}, "|")
 83}
 84
 85// perSeat packs one number per seat, "/"-separated.
 86func perSeat(v [MaxPlayers]int) string {
 87	s := make([]string, len(v))
 88	for i, n := range v {
 89		s[i] = strconv.Itoa(n)
 90	}
 91	return strings.Join(s, "/")
 92}
 93
 94// seatValues unpacks what perSeat packed.
 95func seatValues(f string) (out [MaxPlayers]int) {
 96	if f == "" {
 97		return out
 98	}
 99	for i, n := range strings.Split(f, "/") {
100		if i < MaxPlayers {
101			out[i] = atoi(n)
102		}
103	}
104	return out
105}
106
107// DecodeCharacter unpacks what EncodeCharacter produced.
108func DecodeCharacter(s string) Character {
109	f := strings.Split(s, "|")
110	if len(f) != 23 {
111		return Character{}
112	}
113	c := Character{
114		Hex: f[0], Facing: hex.Dir(atoi(f[1])), Down: f[2] == "1", Delay: atoi(f[3]), Status: Status(atoi(f[4])),
115		PlayedFoot: f[5] == "1", PlayedRun: f[6] == "1", RanLastTurn: f[7] == "1", Dropped: f[8] == "1",
116		Aim: atoi(f[9]), AimHex: f[10], Endurance: atoi(f[11]), Exhausted: atoi(f[12]),
117		Serious: atoi(f[13]), GunArm: atoi(f[14]), OtherArm: atoi(f[15]), Leg: atoi(f[16]),
118		AimAt: atoi(f[18]),
119	}
120	for q, k := range seatValues(f[19]) {
121		c.Killers[q] = k != 0
122	}
123	c.SeriousFrom = seatValues(f[20])
124	c.LegFrom = seatValues(f[21])
125	c.Hurt = seatValues(f[22])
126	if f[17] != "" {
127		for i, rec := range strings.Split(f[17], ",") {
128			g, ok := decodeGun(rec)
129			if i >= MaxGuns || !ok {
130				break
131			}
132			c.Guns[i] = g
133		}
134	}
135	return c
136}
137
138// EncodeChars packs the characters of a state (a turn's starting
139// snapshot), seats in order; DecodeChars unpacks them.
140func EncodeChars(cs []Character) string {
141	parts := make([]string, len(cs))
142	for i, c := range cs {
143		parts[i] = EncodeCharacter(c)
144	}
145	return strings.Join(parts, ";")
146}
147
148// DecodeChars unpacks what EncodeChars produced.
149func DecodeChars(s string) []Character {
150	if s == "" {
151		return nil
152	}
153	parts := strings.Split(s, ";")
154	cs := make([]Character, len(parts))
155	for i, part := range parts {
156		cs[i] = DecodeCharacter(part)
157	}
158	return cs
159}
160
161// encodeGun packs a gun's fields, ':'-separated; decodeGun unpacks them.
162func encodeGun(g Gun) string {
163	return strings.Join([]string{
164		strconv.Itoa(g.ID), string(g.Type), strconv.Itoa(int(g.Location)), boolStr(g.Cocked),
165		strconv.Itoa(g.Shells), boolStr(g.Jammed), boolStr(g.Exploded),
166	}, ":")
167}
168
169func decodeGun(s string) (Gun, bool) {
170	g := strings.Split(s, ":")
171	if len(g) != 7 {
172		return Gun{}, false
173	}
174	return Gun{
175		ID: atoi(g[0]), Type: GunType(g[1]), Location: GunLocation(atoi(g[2])), Cocked: g[3] == "1",
176		Shells: atoi(g[4]), Jammed: g[5] == "1", Exploded: g[6] == "1",
177	}, true
178}
179
180// EncodeGround packs the weapons lying on the board (a turn's ground at its
181// start), ';'-separated records of id, hex and gun ("1|A-G6|1:colt45:0:0:6:0:0");
182// DecodeGround unpacks them, skipping a malformed record.
183func EncodeGround(ground []GroundGun) string {
184	recs := make([]string, len(ground))
185	for i, gg := range ground {
186		recs[i] = strconv.Itoa(gg.ID) + "|" + gg.Hex + "|" + encodeGun(gg.Gun)
187	}
188	return strings.Join(recs, ";")
189}
190
191// DecodeGround unpacks what EncodeGround produced.
192func DecodeGround(s string) []GroundGun {
193	if s == "" {
194		return nil
195	}
196	var out []GroundGun
197	for _, rec := range strings.Split(s, ";") {
198		f := strings.SplitN(rec, "|", 3)
199		if len(f) != 3 {
200			continue
201		}
202		if g, ok := decodeGun(f[2]); ok {
203			out = append(out, GroundGun{ID: atoi(f[0]), Hex: f[1], Gun: g})
204		}
205	}
206	return out
207}
208
209// EncodeInts packs numbers (the cards drawn in a turn); DecodeInts unpacks them.
210func EncodeInts(ns []int) string {
211	parts := make([]string, len(ns))
212	for i, n := range ns {
213		parts[i] = strconv.Itoa(n)
214	}
215	return strings.Join(parts, ",")
216}
217
218// DecodeInts unpacks what EncodeInts produced.
219func DecodeInts(s string) []int {
220	if s == "" {
221		return nil
222	}
223	parts := strings.Split(s, ",")
224	ns := make([]int, len(parts))
225	for i, p := range parts {
226		ns[i] = atoi(p)
227	}
228	return ns
229}
230
231func boolStr(b bool) string {
232	if b {
233		return "1"
234	}
235	return "0"
236}