code wiki / (root) / nx_hunt_partition.nx

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}