@@ 0,0 1,30 @@
+package main
+
+// HUD geometry constants. All values are in virtual pixels (640×360 grid).
+// HudY is the top edge of the HUD strip; the five power columns begin at HudX
+// and are each HudColW pixels wide. The reactor-readout band occupies the
+// horizontal range [0, HudX).
+const (
+ HudY = 272
+ HudX = 200
+ HudColW = 40
+ HudColCount = 5
+)
+
+// hudHitTest maps a virtual-pixel coordinate to the RoomRole whose power column
+// was clicked. Returns ok=false for any coordinate outside the column strip:
+// - py < HudY (above the HUD)
+// - px < HudX (reactor-readout band to the left)
+// - px >= HudX + HudColCount*HudColW (right of last column)
+//
+// Columns are flush (no gutters). Column i covers [HudX+i*HudColW, HudX+(i+1)*HudColW).
+func hudHitTest(px, py int) (RoomRole, bool) {
+ if py < HudY {
+ return 0, false
+ }
+ if px < HudX || px >= HudX+HudColCount*HudColW {
+ return 0, false
+ }
+ idx := (px - HudX) / HudColW
+ return RoomRole(idx), true
+}
@@ 0,0 1,83 @@
+package main
+
+import "testing"
+
+// TestHudHitTest_aboveHud verifies that a pixel above the HUD strip returns ok=false.
+func TestHudHitTest_aboveHud(t *testing.T) {
+ _, ok := hudHitTest(HudX+HudColW/2, HudY-1)
+ if ok {
+ t.Errorf("expected ok=false for py=%d (above HudY=%d)", HudY-1, HudY)
+ }
+}
+
+// TestHudHitTest_leftOfColumns verifies that a pixel in the reactor-readout band
+// to the left of the column strip returns ok=false.
+func TestHudHitTest_leftOfColumns(t *testing.T) {
+ _, ok := hudHitTest(HudX-1, HudY)
+ if ok {
+ t.Errorf("expected ok=false for px=%d (left of HudX=%d)", HudX-1, HudX)
+ }
+}
+
+// TestHudHitTest_rightOfColumns verifies that a pixel at or beyond the right edge
+// of the column strip returns ok=false.
+func TestHudHitTest_rightOfColumns(t *testing.T) {
+ px := HudX + HudColCount*HudColW
+ _, ok := hudHitTest(px, HudY)
+ if ok {
+ t.Errorf("expected ok=false for px=%d (right edge, HudX+5*HudColW=%d)", px, HudX+HudColCount*HudColW)
+ }
+}
+
+// TestHudHitTest_eachColumnCenter verifies that the pixel at the horizontal centre
+// of column i maps to RoomRole(i) with ok=true.
+func TestHudHitTest_eachColumnCenter(t *testing.T) {
+ roles := []RoomRole{RolePilot, RoleWeapons, RoleShields, RoleMedBay, RoleEngines}
+ for i, want := range roles {
+ px := HudX + i*HudColW + HudColW/2
+ py := HudY
+ got, ok := hudHitTest(px, py)
+ if !ok {
+ t.Errorf("column %d centre px=%d: expected ok=true", i, px)
+ continue
+ }
+ if got != want {
+ t.Errorf("column %d centre px=%d: got role %d, want %d", i, px, got, want)
+ }
+ }
+}
+
+// TestHudHitTest_columnEdges verifies inclusive left-edge and exclusive right-edge
+// semantics for each column.
+func TestHudHitTest_columnEdges(t *testing.T) {
+ py := HudY
+
+ for i := 0; i < HudColCount; i++ {
+ // Left edge of column i — inclusive, must return RoomRole(i).
+ leftPx := HudX + i*HudColW
+ got, ok := hudHitTest(leftPx, py)
+ if !ok {
+ t.Errorf("column %d left edge px=%d: expected ok=true", i, leftPx)
+ } else if got != RoomRole(i) {
+ t.Errorf("column %d left edge px=%d: got role %d, want %d", i, leftPx, got, RoomRole(i))
+ }
+
+ // Right edge of column i (first pixel of next column) — exclusive.
+ rightPx := HudX + (i+1)*HudColW
+ if i < HudColCount-1 {
+ // Should belong to column i+1.
+ got, ok = hudHitTest(rightPx, py)
+ if !ok {
+ t.Errorf("column %d right-exclusive px=%d: expected ok=true (belongs to col %d)", i, rightPx, i+1)
+ } else if got != RoomRole(i+1) {
+ t.Errorf("column %d right-exclusive px=%d: got role %d, want %d", i, rightPx, got, RoomRole(i+1))
+ }
+ } else {
+ // Last column: right edge is out of bounds.
+ _, ok = hudHitTest(rightPx, py)
+ if ok {
+ t.Errorf("last column right-exclusive px=%d: expected ok=false", rightPx)
+ }
+ }
+ }
+}