nx_hunt_partition.nx source
↩ module page · 162 lines · 5363 B
1// nx_hunt_partition.nx -- crew role assignment for hunts.
2//
3// Per [[feedback-hunter-gatherer-meta-primitives-outward-inward-axes]]:
4// "nx_hunt_partition (crew role)." When multiple cells / peers /
5// operators are hunting in parallel, partition assigns roles so they
6// don't duplicate kills or step on each other.
7//
8// Roles:
9// SCOUT -- finds targets, hands off
10// ENGAGER -- engages scouted target with active hunt
11// KILLER -- delivers the kill (the closer)
12// ARCHIVER -- archives a target without kill (learning preserved)
13// OBSERVER -- read-only watch; no actions
14//
15// V1 ships role assignment per (cell_id, target_id) pair so the same
16// cell can have different roles for different targets. Race-prevention:
17// only one cell at a time holds each role for each target.
18
19import "nx_syscalls.nx"
20import "nx_tier.nx"
21
22// ===== Sealed enum: NxHuntRole =====================================
23
24const NX_HR_SCOUT: nx_int = 0
25const NX_HR_ENGAGER: nx_int = 1
26const NX_HR_KILLER: nx_int = 2
27const NX_HR_ARCHIVER: nx_int = 3
28const NX_HR_OBSERVER: nx_int = 4
29const NX_HR_N_ROLES: nx_int = 5
30
31const NX_HP_OK: nx_int = 0
32const NX_HP_ERR_FULL: nx_int = 1
33const NX_HP_ERR_ROLE_TAKEN: nx_int = 2
34const NX_HP_ERR_NOT_FOUND: nx_int = 3
35const NX_HP_ERR_BAD_ROLE: nx_int = 4
36
37struct NxHuntAssignment {
38 cell_id: nx_int,
39 target_id: nx_int,
40 role: nx_int,
41 assigned_us: nx_size,
42}
43
44struct NxHuntPartition {
45 assignments: *NxHuntAssignment,
46 capacity: nx_size,
47 count: nx_size,
48}
49
50const NX_HP_ASSIGN_BYTES: nx_size = 32
51
52func nx_hr_is_valid(r: nx_int) -> nx_int {
53 if r < 0 { return 0 }
54 if r >= NX_HR_N_ROLES { return 0 }
55 return 1
56}
57
58func nx_hunt_partition_new(capacity: nx_size) -> *NxHuntPartition {
59 let p: *NxHuntPartition = (sys_mmap(24)) as *NxHuntPartition
60 let bytes: nx_size = capacity * NX_HP_ASSIGN_BYTES
61 p.assignments = (sys_mmap(bytes)) as *NxHuntAssignment
62 p.capacity = capacity
63 p.count = 0
64 return p
65}
66
67func _hp_at(p: *NxHuntPartition, idx: nx_size) -> *NxHuntAssignment {
68 return (p.assignments as i64 + (idx as i64) * NX_HP_ASSIGN_BYTES) as *NxHuntAssignment
69}
70
71func _hp_find_role_for_target(p: *NxHuntPartition, target_id: nx_int, role: nx_int) -> nx_int {
72 var i: nx_size = 0
73 while i < p.count {
74 let a: *NxHuntAssignment = _hp_at(p, i)
75 if a.target_id == target_id {
76 if a.role == role { return i as i64 }
77 }
78 i = i + 1
79 }
80 return -1
81}
82
83// ===== nx_hunt_partition_claim =====================================
84//
85// Cell claims a role for a target. Returns OK or ROLE_TAKEN if
86// another cell already holds that role for that target (mutex).
87
88func nx_hunt_partition_claim(p: *NxHuntPartition,
89 cell_id: nx_int,
90 target_id: nx_int,
91 role: nx_int,
92 now_us: nx_size) -> nx_int {
93 if nx_hr_is_valid(role) == 0 { return NX_HP_ERR_BAD_ROLE }
94 if _hp_find_role_for_target(p, target_id, role) >= 0 {
95 return NX_HP_ERR_ROLE_TAKEN
96 }
97 if p.count >= p.capacity { return NX_HP_ERR_FULL }
98 let a: *NxHuntAssignment = _hp_at(p, p.count)
99 a.cell_id = cell_id
100 a.target_id = target_id
101 a.role = role
102 a.assigned_us = now_us
103 p.count = p.count + 1
104 return NX_HP_OK
105}
106
107// ===== nx_hunt_partition_release ===================================
108
109func nx_hunt_partition_release(p: *NxHuntPartition,
110 cell_id: nx_int,
111 target_id: nx_int,
112 role: nx_int) -> nx_int {
113 var i: nx_size = 0
114 while i < p.count {
115 let a: *NxHuntAssignment = _hp_at(p, i)
116 if a.cell_id == cell_id {
117 if a.target_id == target_id {
118 if a.role == role {
119 // Compact: shift remaining down
120 var j: nx_size = i
121 while j < p.count - 1 {
122 let dst: *NxHuntAssignment = _hp_at(p, j)
123 let src: *NxHuntAssignment = _hp_at(p, j + 1)
124 dst.cell_id = src.cell_id
125 dst.target_id = src.target_id
126 dst.role = src.role
127 dst.assigned_us = src.assigned_us
128 j = j + 1
129 }
130 p.count = p.count - 1
131 return NX_HP_OK
132 }
133 }
134 }
135 i = i + 1
136 }
137 return NX_HP_ERR_NOT_FOUND
138}
139
140func nx_hunt_partition_holder(p: *NxHuntPartition,
141 target_id: nx_int,
142 role: nx_int) -> nx_int {
143 let idx: nx_int = _hp_find_role_for_target(p, target_id, role)
144 if idx < 0 { return -1 }
145 let a: *NxHuntAssignment = _hp_at(p, idx as nx_size)
146 return a.cell_id
147}
148
149func nx_hunt_partition_count(p: *NxHuntPartition) -> nx_size {
150 return p.count
151}
152
153func nx_hunt_partition_count_by_role(p: *NxHuntPartition, role: nx_int) -> nx_int {
154 var hits: nx_int = 0
155 var i: nx_size = 0
156 while i < p.count {
157 let a: *NxHuntAssignment = _hp_at(p, i)
158 if a.role == role { hits = hits + 1 }
159 i = i + 1
160 }
161 return hits
162}