mt-lang 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/std/graph.mt CHANGED
@@ -1,6 +1,10 @@
1
1
  import std.vec as vec
2
2
  import std.deque as deque
3
3
  import std.mem.heap as heap
4
+ import std.binary_heap as heap_mod
5
+
6
+ const INF: float = 3.4028235e38
7
+ const NIL_NODE: ptr_uint = ~(ptr_uint<-0)
4
8
 
5
9
  public struct Edge:
6
10
  from: ptr_uint
@@ -20,6 +24,27 @@ public struct DenseGraph[T]:
20
24
  is_directed: bool
21
25
 
22
26
 
27
+ public struct HeapEntry:
28
+ node: ptr_uint
29
+ dist: float
30
+
31
+ extending HeapEntry:
32
+ public static function order(a: const_ptr[HeapEntry], b: const_ptr[HeapEntry]) -> int:
33
+ let da = unsafe: read(a).dist
34
+ let db = unsafe: read(b).dist
35
+ if da < db:
36
+ return 1
37
+ else if da > db:
38
+ return -1
39
+ else:
40
+ return 0
41
+
42
+
43
+ public struct ShortestPaths:
44
+ dist: vec.Vec[float]
45
+ prev: vec.Vec[ptr_uint]
46
+
47
+
23
48
  extending Graph[T]:
24
49
  public static function create() -> Graph[T]:
25
50
  return Graph[T](
@@ -233,6 +258,15 @@ extending Graph[T]:
233
258
  var targets = vec.Vec[ptr_uint].with_capacity(m)
234
259
  var weights = vec.Vec[float].with_capacity(m)
235
260
 
261
+ if n == 0:
262
+ return DenseGraph[T](
263
+ nodes = vec.Vec[T].create(),
264
+ offsets = offsets,
265
+ targets = targets,
266
+ weights = weights,
267
+ is_directed = this.is_directed
268
+ )
269
+
236
270
  var counts = heap.must_alloc[ptr_uint](n)
237
271
  var pos = heap.must_alloc[ptr_uint](n)
238
272
  var i: ptr_uint = 0
@@ -514,8 +548,211 @@ extending DenseGraph[T]:
514
548
  return order
515
549
 
516
550
 
551
+ public function dijkstra(source: ptr_uint) -> ShortestPaths:
552
+ var dist = vec.Vec[float].with_capacity(this.node_count())
553
+ var prev = vec.Vec[ptr_uint].with_capacity(this.node_count())
554
+ let n = this.node_count()
555
+
556
+ var i: ptr_uint = 0
557
+ while i < n:
558
+ dist.push(INF)
559
+ prev.push(NIL_NODE)
560
+ i += 1
561
+
562
+ if n == 0 or source >= n:
563
+ return ShortestPaths(dist = dist, prev = prev)
564
+
565
+ let src_dist_ptr = dist.get(source) else:
566
+ return ShortestPaths(dist = dist, prev = prev)
567
+ unsafe:
568
+ read(src_dist_ptr) = 0.0
569
+
570
+ var pq = heap_mod.BinaryHeap[HeapEntry].create()
571
+ pq.push(HeapEntry(node = source, dist = 0.0))
572
+
573
+ while not pq.is_empty():
574
+ let entry = pq.pop() else:
575
+ break
576
+ let u = entry.node
577
+ let d = entry.dist
578
+
579
+ let cur_dist_ptr = dist.get(u) else:
580
+ break
581
+ if d > unsafe: read(cur_dist_ptr):
582
+ continue
583
+
584
+ let start_ptr = this.offsets.get(u) else:
585
+ break
586
+ let end_ptr = this.offsets.get(u + 1) else:
587
+ break
588
+ let start = unsafe: read(start_ptr)
589
+ let end = unsafe: read(end_ptr)
590
+ var j = start
591
+ while j < end:
592
+ let t_ptr = this.targets.get(j) else:
593
+ break
594
+ let w_ptr = this.weights.get(j) else:
595
+ break
596
+ let v = unsafe: read(t_ptr)
597
+ let weight = unsafe: read(w_ptr)
598
+ let new_dist = d + weight
599
+
600
+ let v_dist_ptr = dist.get(v) else:
601
+ continue
602
+ if new_dist < unsafe: read(v_dist_ptr):
603
+ unsafe:
604
+ read(v_dist_ptr) = new_dist
605
+ let v_prev_ptr = prev.get(v) else:
606
+ continue
607
+ unsafe:
608
+ read(v_prev_ptr) = u
609
+ pq.push(HeapEntry(node = v, dist = new_dist))
610
+ j += 1
611
+
612
+ pq.release()
613
+ return ShortestPaths(dist = dist, prev = prev)
614
+
615
+
616
+ public function astar(source: ptr_uint, target: ptr_uint, heuristic: fn(node: ptr_uint) -> float) -> vec.Vec[ptr_uint]:
617
+ var path = vec.Vec[ptr_uint].create()
618
+ let n = this.node_count()
619
+ if n == 0 or source >= n or target >= n:
620
+ return path
621
+
622
+ var g_score = heap.must_alloc[float](n)
623
+ var came_from = heap.must_alloc[ptr_uint](n)
624
+ var i: ptr_uint = 0
625
+ while i < n:
626
+ unsafe:
627
+ g_score[i] = INF
628
+ came_from[i] = NIL_NODE
629
+ i += 1
630
+ unsafe:
631
+ g_score[source] = 0.0
632
+
633
+ var pq = heap_mod.BinaryHeap[HeapEntry].create()
634
+ pq.push(HeapEntry(node = source, dist = heuristic(source)))
635
+
636
+ while not pq.is_empty():
637
+ let entry = pq.pop() else:
638
+ break
639
+ let u = entry.node
640
+
641
+ if u == target:
642
+ var cur = target
643
+ while cur != source:
644
+ path.push(cur)
645
+ unsafe:
646
+ cur = came_from[cur]
647
+ path.push(source)
648
+ var li: ptr_uint = 0
649
+ var lj = path.len()
650
+ if lj > 0:
651
+ lj -= 1
652
+ while li < lj:
653
+ let a_ptr = path.get(li) else:
654
+ break
655
+ let b_ptr = path.get(lj) else:
656
+ break
657
+ let tmp = unsafe: read(a_ptr)
658
+ unsafe:
659
+ read(a_ptr) = unsafe: read(b_ptr)
660
+ read(b_ptr) = tmp
661
+ li += 1
662
+ lj -= 1
663
+ break
664
+
665
+ let cur_g = unsafe: g_score[u]
666
+
667
+ let start_ptr = this.offsets.get(u) else:
668
+ break
669
+ let end_ptr = this.offsets.get(u + 1) else:
670
+ break
671
+ let start = unsafe: read(start_ptr)
672
+ let end = unsafe: read(end_ptr)
673
+ var j = start
674
+ while j < end:
675
+ let t_ptr = this.targets.get(j) else:
676
+ break
677
+ let w_ptr = this.weights.get(j) else:
678
+ break
679
+ let v = unsafe: read(t_ptr)
680
+ let weight = unsafe: read(w_ptr)
681
+ let tentative_g = cur_g + weight
682
+
683
+ if tentative_g < unsafe: g_score[v]:
684
+ unsafe:
685
+ g_score[v] = tentative_g
686
+ came_from[v] = u
687
+ pq.push(HeapEntry(node = v, dist = tentative_g + heuristic(v)))
688
+ j += 1
689
+
690
+ pq.release()
691
+ heap.release(g_score)
692
+ heap.release(came_from)
693
+ return path
694
+
695
+
517
696
  public editable function release():
518
697
  this.nodes.release()
519
698
  this.offsets.release()
520
699
  this.targets.release()
521
700
  this.weights.release()
701
+
702
+
703
+ extending ShortestPaths:
704
+ public function distance_to(node: ptr_uint) -> float:
705
+ let d_ptr = this.dist.get(node) else:
706
+ return INF
707
+ return unsafe: read(d_ptr)
708
+
709
+
710
+ public function has_path_to(node: ptr_uint) -> bool:
711
+ let d_ptr = this.dist.get(node) else:
712
+ return false
713
+ return unsafe: read(d_ptr) < INF
714
+
715
+
716
+ public function path_to(target: ptr_uint) -> vec.Vec[ptr_uint]:
717
+ var path = vec.Vec[ptr_uint].create()
718
+ if target >= this.dist.len():
719
+ return path
720
+
721
+ let d_ptr = this.dist.get(target) else:
722
+ return path
723
+ if unsafe: read(d_ptr) >= INF:
724
+ return path
725
+
726
+ var cur = target
727
+ path.push(cur)
728
+ let nil = NIL_NODE
729
+ while cur != nil:
730
+ let p_ptr = this.prev.get(cur) else:
731
+ break
732
+ cur = unsafe: read(p_ptr)
733
+ if cur == nil:
734
+ break
735
+ path.push(cur)
736
+
737
+ var i: ptr_uint = 0
738
+ var j = path.len()
739
+ if j > 0:
740
+ j -= 1
741
+ while i < j:
742
+ let a_ptr = path.get(i) else:
743
+ break
744
+ let b_ptr = path.get(j) else:
745
+ break
746
+ let tmp = unsafe: read(a_ptr)
747
+ unsafe:
748
+ read(a_ptr) = unsafe: read(b_ptr)
749
+ read(b_ptr) = tmp
750
+ i += 1
751
+ j -= 1
752
+
753
+ return path
754
+
755
+
756
+ public editable function release():
757
+ this.dist.release()
758
+ this.prev.release()
data/std/htn.mt ADDED
@@ -0,0 +1,254 @@
1
+ ## Hierarchical Task Network (HTN) planner.
2
+ ##
3
+ ## Total-order forward decomposition. Define a domain of operators
4
+ ## (primitive tasks) and methods (compound task decompositions),
5
+ ## then plan from a root task with an initial world state.
6
+ ##
7
+ ## import std.htn as htn
8
+ ## var planner = htn.HtnPlanner[World, Context].create()
9
+ ## planner.add_operator(...)
10
+ ## planner.add_method(...)
11
+ ## var result = planner.plan(ref_of(ctx), world, "root_task")
12
+ ## match result.plan:
13
+ ## Option.some as p: execute(p.value)
14
+ ## Option.none: handle failure
15
+
16
+ import std.vec as vec
17
+
18
+
19
+ public enum HtnStatus: ubyte
20
+ success = 0
21
+ failure = 1
22
+ max_depth = 2
23
+ max_iterations = 3
24
+
25
+
26
+ public struct HtnOperator[World, Context]:
27
+ name: str
28
+ precondition: fn(context: ptr[Context], world: World) -> bool
29
+ effect: fn(context: ptr[Context], world: World) -> World
30
+
31
+
32
+ public struct HtnMethod[World, Context]:
33
+ name: str
34
+ task: str
35
+ precondition: fn(context: ptr[Context], world: World) -> bool
36
+ subtasks: vec.Vec[str]
37
+
38
+
39
+ public struct HtnDomain[World, Context]:
40
+ operators: vec.Vec[HtnOperator[World, Context]]
41
+ methods: vec.Vec[HtnMethod[World, Context]]
42
+
43
+
44
+ public struct HtnPlanStep:
45
+ task_name: str
46
+
47
+
48
+ public struct HtnPlan[World]:
49
+ steps: vec.Vec[HtnPlanStep]
50
+ final_world: World
51
+
52
+
53
+ public struct HtnResult[World]:
54
+ status: HtnStatus
55
+ plan: Option[HtnPlan[World]]
56
+ iterations: ptr_uint
57
+ depth: ptr_uint
58
+
59
+
60
+ public struct HtnPlanner[World, Context]:
61
+ domain: HtnDomain[World, Context]
62
+ max_depth: ptr_uint
63
+ max_iterations: ptr_uint
64
+
65
+
66
+ # ── planner entry point ──
67
+
68
+ function plan_task[World, Context](
69
+ planner: ref[HtnPlanner[World, Context]],
70
+ context: ptr[Context],
71
+ world: World,
72
+ task: str,
73
+ depth: ptr_uint,
74
+ iterations: ptr[ptr_uint],
75
+ reason: ptr[HtnStatus]
76
+ ) -> Option[HtnPlan[World]]:
77
+ unsafe:
78
+ read(iterations) += 1
79
+ if depth >= planner.max_depth:
80
+ unsafe:
81
+ if read(reason) != HtnStatus.max_iterations:
82
+ read(reason) = HtnStatus.max_depth
83
+ return Option[HtnPlan[World]].none
84
+ if unsafe: read(iterations) >= planner.max_iterations:
85
+ unsafe:
86
+ read(reason) = HtnStatus.max_iterations
87
+ return Option[HtnPlan[World]].none
88
+
89
+ # Try operators (primitive tasks)
90
+ for entry in planner.domain.operators:
91
+ unsafe:
92
+ let op = read(entry)
93
+ if op.name == task:
94
+ if op.precondition(context, world):
95
+ var steps = vec.Vec[HtnPlanStep].create()
96
+ steps.push(HtnPlanStep(task_name = task))
97
+ let result_world = op.effect(context, world)
98
+ return Option[HtnPlan[World]].some(value =
99
+ HtnPlan[World](steps = steps, final_world = result_world)
100
+ )
101
+
102
+ # Try methods (compound task decompositions)
103
+ for entry in planner.domain.methods:
104
+ unsafe:
105
+ let method = read(entry)
106
+ if method.task == task and method.precondition(context, world):
107
+ var current_world = world
108
+ var all_steps = vec.Vec[HtnPlanStep].create()
109
+ var success = true
110
+
111
+ for subtask_name_ptr in method.subtasks:
112
+ let subtask_name = unsafe: read(subtask_name_ptr)
113
+ let sub_plan = plan_task(planner, context, current_world, subtask_name, depth + 1, iterations, reason)
114
+ match sub_plan:
115
+ Option.none:
116
+ success = false
117
+ break
118
+ Option.some as sub_payload:
119
+ for step_ptr in sub_payload.value.steps:
120
+ unsafe:
121
+ all_steps.push(read(step_ptr))
122
+ current_world = sub_payload.value.final_world
123
+ sub_payload.value.steps.release()
124
+
125
+ if success:
126
+ return Option[HtnPlan[World]].some(value =
127
+ HtnPlan[World](steps = all_steps, final_world = current_world)
128
+ )
129
+
130
+ all_steps.release()
131
+
132
+ return Option[HtnPlan[World]].none
133
+
134
+
135
+ # ── public types: extensions ──
136
+
137
+ extending HtnOperator[World, Context]:
138
+ public static function create(
139
+ name: str,
140
+ precondition: fn(context: ptr[Context], world: World) -> bool,
141
+ effect: fn(context: ptr[Context], world: World) -> World
142
+ ) -> HtnOperator[World, Context]:
143
+ return HtnOperator[World, Context](
144
+ name = name,
145
+ precondition = precondition,
146
+ effect = effect
147
+ )
148
+
149
+
150
+ extending HtnMethod[World, Context]:
151
+ public static function create(
152
+ name: str,
153
+ task: str,
154
+ precondition: fn(context: ptr[Context], world: World) -> bool
155
+ ) -> HtnMethod[World, Context]:
156
+ return HtnMethod[World, Context](
157
+ name = name,
158
+ task = task,
159
+ precondition = precondition,
160
+ subtasks = vec.Vec[str].create()
161
+ )
162
+
163
+
164
+ public editable function add_subtask(task_name: str) -> void:
165
+ this.subtasks.push(task_name)
166
+
167
+
168
+ extending HtnPlan[World]:
169
+ public editable function release() -> void:
170
+ this.steps.release()
171
+
172
+
173
+ public function step_count() -> ptr_uint:
174
+ return this.steps.len()
175
+
176
+
177
+ public function step(index: ptr_uint) -> ptr[HtnPlanStep]?:
178
+ return this.steps.get(index)
179
+
180
+
181
+ extending HtnResult[World]:
182
+ public editable function release() -> void:
183
+ match this.plan:
184
+ Option.none:
185
+ pass
186
+ Option.some as payload:
187
+ payload.value.release()
188
+ this.plan = Option[HtnPlan[World]].none
189
+
190
+
191
+ public function has_plan() -> bool:
192
+ match this.plan:
193
+ Option.none:
194
+ return false
195
+ Option.some:
196
+ return true
197
+
198
+
199
+ extending HtnPlanner[World, Context]:
200
+ public static function create() -> HtnPlanner[World, Context]:
201
+ return HtnPlanner[World, Context](
202
+ domain = HtnDomain[World, Context](
203
+ operators = vec.Vec[HtnOperator[World, Context]].create(),
204
+ methods = vec.Vec[HtnMethod[World, Context]].create()
205
+ ),
206
+ max_depth = 32,
207
+ max_iterations = 4096
208
+ )
209
+
210
+
211
+ public editable function release() -> void:
212
+ this.domain.operators.release()
213
+ for entry in this.domain.methods:
214
+ unsafe:
215
+ read(entry).subtasks.release()
216
+ this.domain.methods.release()
217
+
218
+
219
+ public editable function add_operator(op: HtnOperator[World, Context]) -> void:
220
+ this.domain.operators.push(op)
221
+
222
+
223
+ public editable function add_method(method: HtnMethod[World, Context]) -> void:
224
+ this.domain.methods.push(method)
225
+
226
+
227
+ public editable function set_max_depth(limit: ptr_uint) -> void:
228
+ this.max_depth = limit
229
+
230
+
231
+ public editable function set_max_iterations(limit: ptr_uint) -> void:
232
+ this.max_iterations = limit
233
+
234
+
235
+ public editable function plan(context: ptr[Context], world: World, root_task: str) -> HtnResult[World]:
236
+ var iterations: ptr_uint = 0
237
+ var reason: HtnStatus = HtnStatus.failure
238
+ let res = plan_task(ref_of(this), context, world, root_task, 0, ptr_of(iterations), ptr_of(reason))
239
+
240
+ match res:
241
+ Option.none:
242
+ return HtnResult[World](
243
+ status = reason,
244
+ plan = Option[HtnPlan[World]].none,
245
+ iterations = iterations,
246
+ depth = 0
247
+ )
248
+ Option.some as payload:
249
+ return HtnResult[World](
250
+ status = HtnStatus.success,
251
+ plan = Option[HtnPlan[World]].some(value = payload.value),
252
+ iterations = iterations,
253
+ depth = 0
254
+ )
data/std/input.mt ADDED
@@ -0,0 +1,132 @@
1
+ ## Input action mapping — decouple logical actions from physical inputs.
2
+ ##
3
+ ## Define actions, bind keyboard/mouse/gamepad inputs, and
4
+ ## query the map each frame using your platform's input API.
5
+ ##
6
+ ## import std.input as inp
7
+ ## const ACTION_JUMP: ptr_uint = 0
8
+ ## var map = inp.InputMap.create()
9
+ ## map.bind_key(ACTION_JUMP, KEY_SPACE)
10
+ ## map.bind_gamepad(ACTION_JUMP, 0, GAMEPAD_BUTTON_A)
11
+ ## ...
12
+ ## if map.check_digital(ACTION_JUMP, key_down, mouse_down, gamepad_down):
13
+ ## do_jump()
14
+
15
+ import std.vec as vec
16
+
17
+
18
+ public enum InputKind: ubyte
19
+ key = 0
20
+ mouse_button = 1
21
+ gamepad_button = 2
22
+
23
+
24
+ public struct InputBinding:
25
+ kind: InputKind
26
+ code: int
27
+ device_id: int
28
+
29
+
30
+ public struct InputAction:
31
+ name: str
32
+ bindings: vec.Vec[InputBinding]
33
+
34
+
35
+ public struct InputMap:
36
+ actions: vec.Vec[InputAction]
37
+
38
+
39
+ # ── public API ──
40
+
41
+ extending InputMap:
42
+ public static function create() -> InputMap:
43
+ return InputMap(actions = vec.Vec[InputAction].create())
44
+
45
+
46
+ public editable function add_action(name: str) -> ptr_uint:
47
+ let id = this.actions.len()
48
+ var action = InputAction(name = name, bindings = vec.Vec[InputBinding].create())
49
+ this.actions.push(action)
50
+ return id
51
+
52
+
53
+ public editable function bind_key(action_id: ptr_uint, keycode: int) -> void:
54
+ let action_ptr = this.actions.get(action_id) else:
55
+ return
56
+ unsafe:
57
+ read(action_ptr).bindings.push(
58
+ InputBinding(kind = InputKind.key, code = keycode, device_id = 0)
59
+ )
60
+
61
+
62
+ public editable function bind_mouse(action_id: ptr_uint, button: int) -> void:
63
+ let action_ptr = this.actions.get(action_id) else:
64
+ return
65
+ unsafe:
66
+ read(action_ptr).bindings.push(
67
+ InputBinding(kind = InputKind.mouse_button, code = button, device_id = 0)
68
+ )
69
+
70
+
71
+ public editable function bind_gamepad(action_id: ptr_uint, gamepad_id: int, button: int) -> void:
72
+ let action_ptr = this.actions.get(action_id) else:
73
+ return
74
+ unsafe:
75
+ read(action_ptr).bindings.push(
76
+ InputBinding(kind = InputKind.gamepad_button, code = button, device_id = gamepad_id)
77
+ )
78
+
79
+
80
+ public function action_count() -> ptr_uint:
81
+ return this.actions.len()
82
+
83
+
84
+ public function action_name(action_id: ptr_uint) -> str:
85
+ let action_ptr = this.actions.get(action_id) else:
86
+ return ""
87
+ unsafe:
88
+ return read(action_ptr).name
89
+
90
+
91
+ public function check_digital(
92
+ action_id: ptr_uint,
93
+ key_down: fn(code: int) -> bool,
94
+ mouse_down: fn(button: int) -> bool,
95
+ gamepad_down: fn(device: int, button: int) -> bool
96
+ ) -> bool:
97
+ let action_ptr = this.actions.get(action_id) else:
98
+ return false
99
+ unsafe:
100
+ let action = read(action_ptr)
101
+ for entry in action.bindings:
102
+ let binding = read(entry)
103
+ if binding.kind == InputKind.key and key_down(binding.code):
104
+ return true
105
+ if binding.kind == InputKind.mouse_button and mouse_down(binding.code):
106
+ return true
107
+ if binding.kind == InputKind.gamepad_button and gamepad_down(binding.device_id, binding.code):
108
+ return true
109
+ return false
110
+
111
+
112
+ public function binding_count(action_id: ptr_uint) -> ptr_uint:
113
+ let action_ptr = this.actions.get(action_id) else:
114
+ return 0
115
+ unsafe:
116
+ return read(action_ptr).bindings.len()
117
+
118
+
119
+ public function binding_at(action_id: ptr_uint, index: ptr_uint) -> Option[InputBinding]:
120
+ let action_ptr = this.actions.get(action_id) else:
121
+ return Option[InputBinding].none
122
+ unsafe:
123
+ let b_ptr = read(action_ptr).bindings.get(index) else:
124
+ return Option[InputBinding].none
125
+ return Option[InputBinding].some(value = read(b_ptr))
126
+
127
+
128
+ public editable function release() -> void:
129
+ for entry in this.actions:
130
+ unsafe:
131
+ read(entry).bindings.release()
132
+ this.actions.release()