hex.gno
3.35 Kb · 109 lines
1// Package hex holds the pure hex geometry of the game: absolute directions,
2// directions relative to a character's facing, and board adjacency tables.
3package hex
4
5// Dir is an absolute direction on a flat-top hex grid laid out in vertical
6// columns: 0=N 1=NE 2=SE 3=S 4=SW 5=NW.
7type Dir int
8
9const (
10 N Dir = iota
11 NE
12 SE
13 S
14 SW
15 NW
16)
17
18var dirNames = [6]string{"N", "NE", "SE", "S", "SW", "NW"}
19
20// Valid reports whether d is one of the six directions.
21func (d Dir) Valid() bool { return d >= 0 && d < 6 }
22
23// String returns the compass name of d.
24func (d Dir) String() string {
25 if !d.Valid() {
26 return "?"
27 }
28 return dirNames[d]
29}
30
31// Opposite returns the direction pointing back at d.
32func (d Dir) Opposite() Dir { return (d + 3) % 6 }
33
34// Rel is a direction relative to a character's facing, as chosen on a move
35// or turn card. The names are the ones the client and the plan string use.
36// Same and Long only appear on the HEX line of result cards (direction
37// cards, rule 14.3, relative to the target's body counter): they are not
38// movement choices, ParseRel rejects them and Offset panics on them.
39type Rel string
40
41const (
42 AheadLeft Rel = "ahead_left"
43 Ahead Rel = "ahead"
44 AheadRight Rel = "ahead_right"
45 BackLeft Rel = "back_left"
46 Back Rel = "back"
47 BackRight Rel = "back_right"
48 Same Rel = "same" // the target's own hex (14.33)
49 Long Rel = "long" // the hex beyond the target on the line from the shooter's hex through the target's (14.32)
50)
51
52// AheadRels are the three forward directions a move_ahead/turn_ahead card offers.
53var AheadRels = [3]Rel{AheadLeft, Ahead, AheadRight}
54
55// BackRels are the three backward directions a move_back/turn_back card offers.
56var BackRels = [3]Rel{BackLeft, Back, BackRight}
57
58// ParseRel converts a plan string token to a Rel.
59func ParseRel(s string) (Rel, bool) {
60 switch r := Rel(s); r {
61 case AheadLeft, Ahead, AheadRight, BackLeft, Back, BackRight:
62 return r, true
63 }
64 return "", false
65}
66
67// IsAhead reports whether r is one of the three forward directions.
68func (r Rel) IsAhead() bool { return r == AheadLeft || r == Ahead || r == AheadRight }
69
70// IsBack reports whether r is one of the three backward directions.
71func (r Rel) IsBack() bool { return r == BackLeft || r == Back || r == BackRight }
72
73// Offset is the number of 60° clockwise steps from the facing to r
74// (ahead_left=5 ahead=0 ahead_right=1 back_right=2 back=3 back_left=4).
75// Left and right are the character's own sides while it keeps facing
76// forward: facing N, back_right is SE and back_left is SW.
77func (r Rel) Offset() int {
78 switch r {
79 case Ahead:
80 return 0
81 case AheadRight:
82 return 1
83 case BackRight:
84 return 2
85 case Back:
86 return 3
87 case BackLeft:
88 return 4
89 case AheadLeft:
90 return 5
91 }
92 panic("hex: invalid relative direction " + string(r))
93}
94
95// Resolve turns a relative direction into an absolute one for a facing.
96func Resolve(facing Dir, r Rel) Dir {
97 return Dir((int(facing) + r.Offset()) % 6)
98}
99
100// AheadDirs returns the absolute directions of ahead_left, ahead and
101// ahead_right for a facing; these are also the aim-zone spokes (rule 8.32).
102func AheadDirs(f Dir) [3]Dir {
103 return [3]Dir{Resolve(f, AheadLeft), Resolve(f, Ahead), Resolve(f, AheadRight)}
104}
105
106// BackDirs returns the absolute directions of back_left, back and back_right.
107func BackDirs(f Dir) [3]Dir {
108 return [3]Dir{Resolve(f, BackLeft), Resolve(f, Back), Resolve(f, BackRight)}
109}