params.gno
2.99 Kb · 87 lines
1package penalty
2
3import (
4 "errors"
5 "strconv"
6)
7
8const (
9 BallRadius = 110 // mm
10 PostRadius = 60 // mm
11 Distance = 11000 // penalty spot to goal line, mm
12 Gravity = 9810 // mm/s²
13
14 AimMaxX = 6000 // the aim point must lie within ±6 m of the goal centre
15 AimMaxY = 4000 // and between the grass and 4 m up
16)
17
18// ShotInput is what a taker chooses: the five numbers a player sends.
19type ShotInput struct {
20 AimX int64 // point on the goal plane the swing heads for, mm from the centre (+ = taker's right)
21 AimY int64 // its height, mm
22 ContactX int64 // where the boot meets the ball, milli-radii from its centre (+ = right)
23 ContactY int64 // milli-radii (+ = above centre)
24 Power int64 // per-mille
25}
26
27// ShotParams is a complete kick: the taker's five choices plus the 24-byte
28// mishit seed that decides how far the boot lands from the chosen contact.
29// These six, with a keeper's four, are the ten inputs of SimulateShot.
30type ShotParams struct {
31 AimX int64
32 AimY int64
33 ContactX int64
34 ContactY int64
35 Power int64
36 Mishit [24]byte
37}
38
39// KeeperPlan is the keeper's commitment: four uint16 values that DecodePlan
40// maps onto a dive using the game's Config.
41type KeeperPlan struct {
42 Direction uint16
43 Speed uint16
44 Jump uint16
45 Reaction uint16
46}
47
48var ErrShotRange = errors.New("penalty: shot parameter out of range")
49
50// Validate checks a taker's choices against the game's constants.
51func (s ShotInput) Validate(c Config) error {
52 if s.AimX < -AimMaxX || s.AimX > AimMaxX {
53 return errors.New(ErrShotRange.Error() + ": aimX must be within ±" + strconv.Itoa(AimMaxX) + " mm")
54 }
55 if s.AimY < 0 || s.AimY > AimMaxY {
56 return errors.New(ErrShotRange.Error() + ": aimY must be within 0.." + strconv.Itoa(AimMaxY) + " mm")
57 }
58 if s.ContactX*s.ContactX+s.ContactY*s.ContactY > c.MaxContact*c.MaxContact {
59 return errors.New(ErrShotRange.Error() + ": contact offset must be within " +
60 strconv.FormatInt(c.MaxContact, 10) + " milli-radii of the centre")
61 }
62 if s.Power < 0 || s.Power > 1000 {
63 return errors.New(ErrShotRange.Error() + ": power must be within 0..1000")
64 }
65 return nil
66}
67
68// WithMishit completes a taker's choices with a mishit seed.
69func (s ShotInput) WithMishit(m [24]byte) ShotParams {
70 return ShotParams{AimX: s.AimX, AimY: s.AimY, ContactX: s.ContactX, ContactY: s.ContactY, Power: s.Power, Mishit: m}
71}
72
73// Input returns the taker's five choices.
74func (s ShotParams) Input() ShotInput {
75 return ShotInput{AimX: s.AimX, AimY: s.AimY, ContactX: s.ContactX, ContactY: s.ContactY, Power: s.Power}
76}
77
78func (s ShotInput) String() string {
79 return "aim=(" + strconv.FormatInt(s.AimX, 10) + "," + strconv.FormatInt(s.AimY, 10) + ")mm contact=(" +
80 strconv.FormatInt(s.ContactX, 10) + "," + strconv.FormatInt(s.ContactY, 10) + ")mR power=" +
81 strconv.FormatInt(s.Power, 10) + "‰"
82}
83
84func (p KeeperPlan) String() string {
85 return "plan=(" + strconv.Itoa(int(p.Direction)) + "," + strconv.Itoa(int(p.Speed)) + "," +
86 strconv.Itoa(int(p.Jump)) + "," + strconv.Itoa(int(p.Reaction)) + ")"
87}