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.

victory.gno

4.73 Kb · 151 lines
  1package engine
  2
  3// Victory point awards (rule 15), for showdowns where each character is a
  4// side of its own: there are no allies (no "Ally killed"), and no special
  5// rule captures or kills anyone.
  6const (
  7	VPSurvive   = "survive"         // +2: conscious and unsurrendered at the end (15.62)
  8	VPHoldField = "hold_field"      // +2: the only side with survivors (15.63)
  9	VPSurrender = "surrender"       // -2 (15.41)
 10	VPCaptured  = "captured"        // -4: passed out or surrendered when the enemy holds the field (15.63, 15.7)
 11	VPKilled    = "killed"          // -8 (15.2)
 12	VPKill      = "kill_enemy"      // +2: the enemy who killed him (15.2)
 13	VPKnockOut  = "knock_out_enemy" // +1: the enemy who crossed off most of his endurance boxes (15.3, 15.41)
 14	VPRunAway   = "run_away"        // -2: moved off the map (15.5); no end-of-showdown points (15.6)
 15)
 16
 17// VPPoints is what each award is worth (the VICTORY POINTS schedule, 15.11).
 18// Passing out is worth 0 and makes no award.
 19var VPPoints = map[string]int{
 20	VPSurvive:   2,
 21	VPHoldField: 2,
 22	VPSurrender: -2,
 23	VPCaptured:  -4,
 24	VPKilled:    -8,
 25	VPKill:      2,
 26	VPKnockOut:  1,
 27	VPRunAway:   -2,
 28}
 29
 30// VPSharedKill is what each enemy gets when several KILLs land in the same
 31// segment: 15.2 shares the "Kill enemy" points, kept whole here.
 32const VPSharedKill = 1
 33
 34// Award is victory points a character earns (Points > 0) or loses.
 35type Award struct {
 36	Player int
 37	Reason string // one of the VP* names
 38	Points int
 39}
 40
 41// TurnVictoryPoints scores what a turn did, from the characters before and
 42// after it: a character killed loses the "Killed" points and the enemy
 43// whose hit killed it gets "Kill enemy" (15.2, Character.Killers), or,
 44// when several enemies share the kill, 1 point each (kept whole); one
 45// who passed out gets nothing and the enemy who crossed off most of its
 46// endurance boxes "Knock out enemy" (15.3, Character.Hurt). A character
 47// who is not up cannot be hit, so a knocked-out one is never killed
 48// afterwards.
 49func TurnVictoryPoints(before, after State) []Award {
 50	var out []Award
 51	for p, c := range after.Chars {
 52		if p >= len(before.Chars) || before.Chars[p].Status != Alive {
 53			continue
 54		}
 55		switch c.Status {
 56		case Killed:
 57			out = append(out, award(p, VPKilled))
 58			var killers []int
 59			for q, k := range c.Killers {
 60				if k && q != p && q < len(after.Chars) {
 61					killers = append(killers, q)
 62				}
 63			}
 64			for _, q := range killers {
 65				if len(killers) == 1 {
 66					out = append(out, award(q, VPKill))
 67				} else {
 68					out = append(out, Award{Player: q, Reason: VPKill, Points: VPSharedKill})
 69				}
 70			}
 71		case PassedOut:
 72			if k := knockedOutBy(c); k >= 0 {
 73				out = append(out, award(k, VPKnockOut))
 74			}
 75		case RanAway:
 76			out = append(out, award(p, VPRunAway))
 77		}
 78	}
 79	return out
 80}
 81
 82// knockedOutBy is the enemy who crossed off most of the character's
 83// endurance boxes (15.3): the first in seat order when two did as much,
 84// NoSeat when nobody did.
 85func knockedOutBy(c Character) int {
 86	best, most := NoSeat, 0
 87	for q, n := range c.Hurt {
 88		if n > most {
 89			best, most = q, n
 90		}
 91	}
 92	return best
 93}
 94
 95// EndVictoryPoints scores the end of the showdown (15.6): a character still
 96// up survives, and holds the field when it is the only one (15.62, 15.63);
 97// Out stands for a surrendered character, who loses the "Surrender" points
 98// and, like passing out, gives "Knock out enemy" to the enemy who crossed
 99// off most of its endurance boxes (15.41); passed out or surrendered, a
100// character is captured when an enemy holds the field (15.63, 15.7).
101func EndVictoryPoints(s State) []Award {
102	var out []Award
103	survivors := AliveCount(s)
104	for p, c := range s.Chars {
105		holds := survivors == 1 && c.Status != Alive // someone else holds the field
106		switch c.Status {
107		case Alive:
108			out = append(out, award(p, VPSurvive))
109			if survivors == 1 {
110				out = append(out, award(p, VPHoldField))
111			}
112		case Out:
113			out = append(out, award(p, VPSurrender))
114			if k := knockedOutBy(c); k >= 0 {
115				out = append(out, award(k, VPKnockOut))
116			}
117			if holds {
118				out = append(out, award(p, VPCaptured))
119			}
120		case PassedOut:
121			if holds {
122				out = append(out, award(p, VPCaptured))
123			}
124		}
125	}
126	return out
127}
128
129func award(p int, reason string) Award {
130	return Award{Player: p, Reason: reason, Points: VPPoints[reason]}
131}
132
133// VPEvents are the awards as end-of-turn events of the turn's log.
134func VPEvents(awards []Award) []Event {
135	evs := make([]Event, 0, len(awards))
136	for _, a := range awards {
137		evs = append(evs, Event{Seg: EndOfTurn, Player: a.Player, Kind: EvVP, N: a.Points, Reason: a.Reason})
138	}
139	return evs
140}
141
142// VPTotals adds the awards up per seat, for n seats.
143func VPTotals(awards []Award, n int) []int {
144	t := make([]int, n)
145	for _, a := range awards {
146		if a.Player >= 0 && a.Player < n {
147			t[a.Player] += a.Points
148		}
149	}
150	return t
151}