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.

shootout.gno

2.48 Kb · 104 lines
  1package penalty
  2
  3// Side is who takes a kick.
  4type Side uint8
  5
  6const (
  7	SidePlayer Side = iota + 1
  8	SideChain
  9)
 10
 11func (s Side) String() string {
 12	switch s {
 13	case SidePlayer:
 14		return "player"
 15	case SideChain:
 16		return "chain"
 17	}
 18	return "none"
 19}
 20
 21// Other returns the opposing side.
 22func (s Side) Other() Side {
 23	if s == SidePlayer {
 24		return SideChain
 25	}
 26	return SidePlayer
 27}
 28
 29// RegulationKicks is how many kicks each side takes before sudden death.
 30const RegulationKicks = 5
 31
 32// Shootout is the score of a penalty shootout in which the player always
 33// kicks first in each round:
 34//
 35//   - each side takes RegulationKicks kicks, alternating;
 36//   - it ends early as soon as one side cannot be caught with the kicks the
 37//     other has left;
 38//   - level after regulation, it goes to sudden death: rounds of one kick
 39//     each, decided when one side scores and the other does not.
 40type Shootout struct {
 41	PlayerKicks int
 42	PlayerGoals int
 43	ChainKicks  int
 44	ChainGoals  int
 45}
 46
 47// Next returns who kicks next (SidePlayer at the start of every round).
 48func (s Shootout) Next() Side {
 49	if s.PlayerKicks == s.ChainKicks {
 50		return SidePlayer
 51	}
 52	return SideChain
 53}
 54
 55// Round is the 1-based number of the round in progress.
 56func (s Shootout) Round() int { return s.ChainKicks + 1 }
 57
 58// SuddenDeath reports whether regulation is over.
 59func (s Shootout) SuddenDeath() bool { return s.ChainKicks >= RegulationKicks }
 60
 61// Record adds a kick for side. It panics if side is out of turn or the
 62// shootout is already decided.
 63func (s *Shootout) Record(side Side, scored bool) {
 64	if s.Winner() != 0 {
 65		panic("penalty: the shootout is already decided")
 66	}
 67	if side != s.Next() {
 68		panic("penalty: " + side.String() + " is kicking out of turn")
 69	}
 70	g := 0
 71	if scored {
 72		g = 1
 73	}
 74	if side == SidePlayer {
 75		s.PlayerKicks++
 76		s.PlayerGoals += g
 77	} else {
 78		s.ChainKicks++
 79		s.ChainGoals += g
 80	}
 81}
 82
 83// Winner returns the side that has won, or 0 while undecided.
 84func (s Shootout) Winner() Side {
 85	if s.PlayerKicks <= RegulationKicks && s.ChainKicks <= RegulationKicks {
 86		playerLeft := RegulationKicks - s.PlayerKicks
 87		chainLeft := RegulationKicks - s.ChainKicks
 88		if s.PlayerGoals > s.ChainGoals+chainLeft {
 89			return SidePlayer
 90		}
 91		if s.ChainGoals > s.PlayerGoals+playerLeft {
 92			return SideChain
 93		}
 94		return 0
 95	}
 96	// Sudden death: decided only once both have kicked in the round.
 97	if s.PlayerKicks == s.ChainKicks && s.PlayerGoals != s.ChainGoals {
 98		if s.PlayerGoals > s.ChainGoals {
 99			return SidePlayer
100		}
101		return SideChain
102	}
103	return 0
104}