~bigbes/game-prototype-ftl

1d4896f1f22a88c49ef116887efbe9a40a105457 — Eugene Blikh 30 days ago ef6e0d5
systems-power: HUD geometry + hudHitTest with tests

systems-power
2 files changed, 113 insertions(+), 0 deletions(-)

A hud.go
A hud_test.go
A hud.go => hud.go +30 -0
@@ 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
}

A hud_test.go => hud_test.go +83 -0
@@ 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)
			}
		}
	}
}