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.

ruleset.gno

5.92 Kb · 151 lines
  1package engine
  2
  3// Clan is a named pool of special cards.
  4type Clan struct {
  5	Name string
  6	Pool uint64 // bit i set: Ruleset.Cards[i] is in the pool
  7}
  8
  9// Ruleset holds every card and number the rules use.
 10type Ruleset struct {
 11	Cards         []Card
 12	Clans         []Clan
 13	HP            int // starting HP of both gnomes
 14	BlockCap      int
 15	CounterCap    int
 16	VulnerableCap int
 17	StrengthCap   int
 18	// VulnerableDoublesOneHit: each hit against a Vulnerable gnome deals double and uses a
 19	// token. Otherwise the next card to hit deals double on every hit and uses one token.
 20	VulnerableDoublesOneHit bool
 21	// StrengthLastsTheRound: Strength ends with the round, like Block and Counter. Otherwise
 22	// it lasts the match.
 23	StrengthLastsTheRound bool
 24	// VulnerableLastsTheRound: unused Vulnerable ends with the round. Otherwise it lasts
 25	// until hits use it.
 26	VulnerableLastsTheRound bool
 27}
 28
 29// Gnome is one side's state between rounds; Block and Counter end with each round.
 30type Gnome struct {
 31	HP, Vulnerable, Strength int
 32	Spent                    uint64 // special cards played, by CardID bit
 33}
 34
 35// State is the match between rounds. Round is the round about to be played, 1 to 3.
 36type State struct {
 37	Round  int
 38	Gnomes [2]Gnome
 39}
 40
 41// Start is the state before round 1.
 42func (rs *Ruleset) Start() State {
 43	return State{Round: 1, Gnomes: [2]Gnome{{HP: rs.HP}, {HP: rs.HP}}}
 44}
 45
 46func (rs *Ruleset) CardByName(name string) (CardID, bool) {
 47	for i := range rs.Cards {
 48		if rs.Cards[i].Name == name {
 49			return CardID(i), true
 50		}
 51	}
 52	return 0, false
 53}
 54
 55// PlanOf looks each card up by name. It reports false when a name matches no card.
 56func (rs *Ruleset) PlanOf(names ...string) (Plan, bool) {
 57	plan := make(Plan, 0, len(names))
 58	for _, n := range names {
 59		id, ok := rs.CardByName(n)
 60		if !ok {
 61			return nil, false
 62		}
 63		plan = append(plan, id)
 64	}
 65	return plan, true
 66}
 67
 68func (rs *Ruleset) ClanByName(name string) (Clan, bool) {
 69	for _, c := range rs.Clans {
 70		if c.Name == name {
 71			return c, true
 72		}
 73	}
 74	return Clan{}, false
 75}
 76
 77func hits(n, damage int) []Effect {
 78	out := make([]Effect, n)
 79	for i := range out {
 80		out[i] = Effect{Hit, damage}
 81	}
 82	return out
 83}
 84
 85// ProofOfConcept is the proof-of-concept ruleset: the two basics and the Pickers, Ironhide
 86// and Delvers packs.
 87func ProofOfConcept() *Ruleset {
 88	cards := []Card{
 89		{Name: "Pick", Cost: 1, Tier: Normal, Damage: hits(1, 1)},
 90		{Name: "Brace", Cost: 1, Tier: Normal, Block: []Effect{{GainBlock, 1}}},
 91
 92		{Name: "Pickaxe Flurry", Cost: 1, Tier: Quick, Special: true, Damage: hits(2, 1)},
 93		{Name: "Kneecap", Cost: 1, Tier: Quick, Special: true, Interrupt: true, Damage: hits(1, 1)},
 94		{Name: "Sling Stone", Cost: 1, Tier: Normal, Special: true, Damage: hits(1, 2)},
 95		{Name: "Dodge Roll", Cost: 2, Tier: Normal, Special: true, Parry: 1},
 96		{Name: "Chalk Dust", Cost: 1, Tier: Quick, Special: true, Damage: []Effect{{ApplyVulnerable, 1}}},
 97		{Name: "Double Tap", Cost: 2, Tier: Normal, Special: true, Damage: hits(3, 1)},
 98		{Name: "Momentum", Cost: 1, Tier: Normal, Special: true, Damage: []Effect{{GainStrength, 1}}},
 99		{Name: "Undermine", Cost: 1, Tier: Quick, Special: true, Rare: true, Interrupt: true, Damage: []Effect{{VulnerableIfCancelled, 2}}},
100		{Name: "Hunker Down", Cost: 3, Tier: Normal, Special: true, Parry: 2},
101		{Name: "Boot and Heel", Cost: 2, Tier: Slow, Special: true, Damage: hits(2, 2)},
102
103		{Name: "Stone Guard", Cost: 3, Tier: Normal, Special: true, Parry: 2},
104		{Name: "Bulwark", Cost: 2, Tier: Normal, Special: true, Block: []Effect{{GainBlock, 5}}},
105		{Name: "Hammer Blow", Cost: 2, Tier: Slow, Special: true, Damage: hits(1, 5)},
106		{Name: "Cobble Throw", Cost: 1, Tier: Slow, Special: true, Damage: hits(1, 2)},
107		{Name: "Spiked Shield", Cost: 1, Tier: Normal, Special: true, Block: []Effect{{GainBlock, 1}, {GainCounter, 1}}},
108		{Name: "Anvil Stomp", Cost: 2, Tier: Slow, Special: true, Damage: []Effect{{Hit, 2}, {ApplyVulnerable, 1}}},
109		{Name: "Feint", Cost: 2, Tier: Normal, Special: true, Damage: hits(1, 4), OnCancel: []Effect{{GainBlock, 4}}},
110		{Name: "Avalanche", Cost: 1, Tier: Slow, Special: true, Rare: true, Damage: []Effect{{HitBlock, 0}}},
111		{Name: "Shield Catch", Cost: 2, Tier: Normal, Special: true, Parry: 1},
112		{Name: "Loose Timbers", Cost: 2, Tier: Normal, Special: true, Block: []Effect{{GainBlock, 3}}, CancelDamage: hits(1, 4)},
113
114		{Name: "Kickback", Cost: 1, Tier: Slow, Special: true, Rare: true, Damage: []Effect{{HitHardest, 5}}},
115		{Name: "Overdig", Cost: 2, Tier: Normal, Special: true, Damage: []Effect{{Hit, 6}, {GainVulnerable, 1}}},
116		{Name: "Ambush", Cost: 1, Tier: Normal, Special: true, Damage: []Effect{{GainVulnerable, 1}, {HitIfNotAttacked, 6}}},
117		{Name: "Open Guard", Cost: 1, Tier: Normal, Special: true, Block: []Effect{{GainBlock, 4}}, Damage: []Effect{{GainVulnerable, 1}}},
118		{Name: "Chisel Jab", Cost: 1, Tier: Normal, Special: true, Damage: hits(1, 2)},
119		{Name: "Rockfall", Cost: 2, Tier: Slow, Special: true, Damage: hits(1, 5)},
120		{Name: "Cribbing", Cost: 1, Tier: Normal, Special: true, Block: []Effect{{BlockPerVulnerable, 1}}},
121		{Name: "Deadfall", Cost: 2, Tier: Normal, Special: true, Parry: 1, Damage: []Effect{{VulnerableIfCancelled, 1}}},
122		{Name: "Blowback", Cost: 2, Tier: Normal, Special: true, Block: []Effect{{MoveVulnerable, 0}}},
123		{Name: "Cave-In", Cost: 3, Tier: Slow, Special: true, Damage: []Effect{{StripBlock, 0}, {Hit, 5}}},
124	}
125	const firstPickers, firstIronhide, firstDelvers, end = 2, 12, 22, 32
126	return &Ruleset{
127		Cards: cards,
128		Clans: []Clan{
129			{Name: "Pickers", Pool: bitRange(firstPickers, firstIronhide)},
130			{Name: "Ironhide", Pool: bitRange(firstIronhide, firstDelvers)},
131			{Name: "Delvers", Pool: bitRange(firstDelvers, end)},
132		},
133		HP:            15,
134		BlockCap:      20,
135		CounterCap:    3,
136		VulnerableCap: 3,
137		StrengthCap:   8,
138
139		VulnerableDoublesOneHit: true,
140		StrengthLastsTheRound:   true,
141	}
142}
143
144// bitRange sets bits from, from+1, ..., to-1.
145func bitRange(from, to int) uint64 {
146	var bits uint64
147	for i := from; i < to; i++ {
148		bits |= uint64(1) << uint(i)
149	}
150	return bits
151}