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.

plan.gno

2.97 Kb · 126 lines
  1package penalty
  2
  3// Mode is what kind of move a keeper plan describes.
  4type Mode uint8
  5
  6const (
  7	ModeStar Mode = iota + 1 // spread into a star jump
  8	ModeRead                 // hold and react to the shot
  9	ModeDive                 // commit to a dive
 10)
 11
 12func (m Mode) String() string {
 13	switch m {
 14	case ModeStar:
 15		return "star jump"
 16	case ModeRead:
 17		return "wait and read"
 18	case ModeDive:
 19		return "committed dive"
 20	}
 21	return "unknown"
 22}
 23
 24// Plan is a KeeperPlan decoded with a game's constants.
 25type Plan struct {
 26	Mode     Mode
 27	Heading  int64 // deci-degrees: 0 = along the line to the taker's right, 1800 = to the left, 900 = straight out
 28	Speed    int64 // mm/s along the ground (0 for a read)
 29	Jump     int64 // mm/s upwards (negative = collapse low)
 30	Reaction int64 // ms after contact before moving
 31}
 32
 33// Dive headings stay within ForwardMax of the line, except for the middle
 34// CentreShare of the direction range, which sweeps the headings in between.
 35const (
 36	ForwardMax  = 220 // deci-degrees
 37	CentreShare = 60  // per-mille of the direction range
 38	StarMaxSpd  = 1150
 39)
 40
 41// DecodePlan maps the four uint16 of a keeper plan onto a move:
 42//
 43//	direction  monotonic 0..65535 -> heading 0°..180°
 44//	speed      lowest StarShare -> star jump, next ReadShare -> wait and read,
 45//	           the rest -> dive at DiveMin..DiveMax
 46//	jump       JumpMin..JumpMax
 47//	reaction   ReactionMin..ReactionMax
 48//
 49// For a read plan the direction and jump are unused: the keeper decides them
 50// from the ball.
 51func DecodePlan(p KeeperPlan, c Config) Plan {
 52	out := Plan{
 53		Heading:  heading(p.Direction),
 54		Jump:     span(p.Jump, c.JumpMin, c.JumpMax),
 55		Reaction: span(p.Reaction, c.ReactionMin, c.ReactionMax),
 56	}
 57	u := int64(p.Speed)
 58	starEdge := 65535 * c.StarShare / 1000
 59	readEdge := 65535 * (c.StarShare + c.ReadShare) / 1000
 60	switch {
 61	case u < starEdge:
 62		out.Mode = ModeStar
 63		out.Speed = StarMaxSpd * u / starEdge
 64	case u < readEdge:
 65		out.Mode = ModeRead
 66	default:
 67		out.Mode = ModeDive
 68		out.Speed = c.DiveMin + (c.DiveMax-c.DiveMin)*(u-readEdge)/maxI(1, 65535-readEdge)
 69	}
 70	return out
 71}
 72
 73func heading(d uint16) int64 {
 74	s := 2*int64(d) - 65535 // -65535 (right) .. 65535 (left)
 75	c := int64(65535 * CentreShare / 1000)
 76	switch {
 77	case s <= -c:
 78		return ForwardMax * (65535 + s) / (65535 - c)
 79	case s >= c:
 80		return 1800 - ForwardMax*(65535-s)/(65535-c)
 81	}
 82	return 900 + (900-ForwardMax)*s/c
 83}
 84
 85// cosPermille approximates cos(heading) × 1000 (Bhaskara I), heading in
 86// deci-degrees within 0..1800. Integer-only, accurate to about 0.2%.
 87func cosPermille(h int64) int64 {
 88	sign := int64(1)
 89	if h > 900 {
 90		h = 1800 - h
 91		sign = -1
 92	}
 93	d2 := h * h // (deci-degrees)²
 94	return sign * 1000 * (3240000 - 4*d2) / (3240000 + d2)
 95}
 96
 97func maxI(a, b int64) int64 {
 98	if a > b {
 99		return a
100	}
101	return b
102}
103
104func minI(a, b int64) int64 {
105	if a < b {
106		return a
107	}
108	return b
109}
110
111func absI(a int64) int64 {
112	if a < 0 {
113		return -a
114	}
115	return a
116}
117
118func clampI(x, lo, hi int64) int64 {
119	if x < lo {
120		return lo
121	}
122	if x > hi {
123		return hi
124	}
125	return x
126}