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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/lsp/diagnostics.rb +1 -1
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -13
- data/std/blackboard.mt +100 -0
- data/std/cmd.mt +132 -0
- data/std/color.mt +245 -0
- data/std/graph.mt +237 -0
- data/std/htn.mt +254 -0
- data/std/input.mt +132 -0
- data/std/noise.mt +201 -0
- data/std/pool.mt +82 -0
- data/std/signal.mt +66 -0
- data/std/steering.mt +132 -0
- data/std/timer.mt +81 -0
- data/std/tween.mt +374 -0
- data/std/utility.mt +208 -0
- metadata +14 -2
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()
|