@rbxts/htn-shop 1.0.1

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.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @rbxts/shop
2
+
3
+ a simple HTN, SHOP-like planner written in js, based on [Pyhop](https://bitbucket.org/dananau/pyhop)
4
+
5
+ shop was easy to implement (less than 50 lines of code), and if you understand the basic ideas of HTN planning ([this presentation](http://www.cs.umd.edu/~nau/papers/nau2013game.pdf) contains a quick summary), shop should be easy to understand.
6
+
7
+ shop's planning algorithm is like the one in SHOP, but with several differences that should make it easier to integrate it with ordinary computer programs:
8
+
9
+ shop represents states of the world using ordinary variable bindings, not logical propositions. A state is just a js object. For example, you might write s.loc['v'] = 'd' to say that vehicle v is at location d in state s.
10
+
11
+ To write HTN operators and methods forjshop, you don't need to learn a specialized planning language. Instead, you write them as ordinary js functions. The current state (e.g., s in the above example) is passed to them as an argument.
12
+
13
+ ## Module Usage
14
+
15
+ ```
16
+ npm install @rbxts/shop
17
+ ```
18
+
19
+ see the unit.test.ts file for an example usage. more to follow.
20
+
21
+ ## Further reading
22
+
23
+ - Designing games mechanics with a HTN Planner - [PDF](http://www.gameaipro.com/GameAIPro/GameAIPro_Chapter12_Exploring_HTN_Planners_through_Example.pdf)
24
+
25
+ - Use in video games - [Youtube](https://youtu.be/kXm467TFTcY)
26
+
27
+ ## License
28
+
29
+ MIT
package/out/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Planner, State } from "./types";
2
+ export declare function create<S extends State>(): Planner<S>;
package/out/init.luau ADDED
@@ -0,0 +1,87 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = require(game:GetService("ReplicatedStorage"):WaitForChild("rbxts_include"):WaitForChild("RuntimeLib"))
3
+ local _util = TS.import(script, game:GetService("ReplicatedStorage"), "shop", "util")
4
+ local extend = _util.extend
5
+ local deepClone = _util.deepClone
6
+ local tail = _util.tail
7
+ local addOperators, setMethods, solve
8
+ local function create()
9
+ local operators = {}
10
+ local taskMethods = {}
11
+ return {
12
+ operators = function(toAdd)
13
+ return addOperators(operators, toAdd)
14
+ end,
15
+ setMethods = function(taskName, methods)
16
+ return setMethods(taskMethods, taskName, methods)
17
+ end,
18
+ solve = function(state, tasks)
19
+ return solve(operators, taskMethods, state, tasks)
20
+ end,
21
+ }
22
+ end
23
+ function addOperators(currentOperators, toAdd)
24
+ extend(currentOperators, toAdd)
25
+ end
26
+ function setMethods(currentTaskMethods, taskName, toAdd)
27
+ currentTaskMethods[taskName] = toAdd
28
+ end
29
+ local seekPlan
30
+ function solve(operators, taskMethods, state, tasks)
31
+ return seekPlan(operators, taskMethods, state, tasks, {}, 0)
32
+ end
33
+ function seekPlan(operators, taskMethods, state, tasks, plan, depth)
34
+ if #tasks == 0 then
35
+ return plan
36
+ end
37
+ local task1 = tasks[1]
38
+ local taskName = task1[1]
39
+ if operators[taskName] ~= nil then
40
+ local operator = operators[taskName]
41
+ local args = tail(task1)
42
+ local newstate = operator(deepClone(state), unpack(args))
43
+ if newstate then
44
+ local _exp = operators
45
+ local _exp_1 = taskMethods
46
+ local _exp_2 = tail(tasks)
47
+ local _array = {}
48
+ local _length = #_array
49
+ local _planLength = #plan
50
+ table.move(plan, 1, _planLength, _length + 1, _array)
51
+ _length += _planLength
52
+ _array[_length + 1] = task1
53
+ local solution = seekPlan(_exp, _exp_1, newstate, _exp_2, _array, depth + 1)
54
+ if solution ~= nil then
55
+ return solution
56
+ end
57
+ end
58
+ end
59
+ if taskMethods[taskName] ~= nil then
60
+ local solution = nil
61
+ for _, method in taskMethods[taskName] do
62
+ local args = tail(task1)
63
+ local subtasks = method(state, unpack(args))
64
+ if subtasks ~= nil then
65
+ local _exp = operators
66
+ local _exp_1 = taskMethods
67
+ local _exp_2 = state
68
+ local _array = {}
69
+ local _length = #_array
70
+ local _subtasksLength = #subtasks
71
+ table.move(subtasks, 1, _subtasksLength, _length + 1, _array)
72
+ _length += _subtasksLength
73
+ local _array_1 = tail(tasks)
74
+ table.move(_array_1, 1, #_array_1, _length + 1, _array)
75
+ solution = seekPlan(_exp, _exp_1, _exp_2, _array, plan, depth + 1)
76
+ if solution ~= nil then
77
+ break
78
+ end
79
+ end
80
+ end
81
+ return solution
82
+ end
83
+ return nil
84
+ end
85
+ return {
86
+ create = create,
87
+ }
package/out/types.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /** A task is represented as an array where the first element is the task name and the rest are arguments */
2
+ export type Task = [string, ...defined[]];
3
+ /** State can be any object with string keys */
4
+ export type State = Record<string, defined>;
5
+ /** An operator takes a state and task arguments, returns a new state or undefined if not applicable */
6
+ export type Operator<S extends State> = (state: S, ...args: defined[]) => S | undefined;
7
+ /** A method takes a state and task arguments, returns subtasks or undefined if not applicable */
8
+ export type Method<S extends State> = (state: S, ...args: defined[]) => Task[] | undefined;
9
+ /** Collection of operators keyed by task name */
10
+ export type Operators<S extends State> = Record<string, Operator<S>>;
11
+ /** Collection of methods for a task (multiple methods can apply to the same task) */
12
+ export type TaskMethods<S extends State> = Record<string, Method<S>[]>;
13
+ export interface Planner<S extends State> {
14
+ operators: (toAdd: Operators<S>) => void;
15
+ setMethods: (taskName: string, methods: Method<S>[]) => void;
16
+ solve: (state: S, tasks: Task[]) => Task[] | undefined;
17
+ }
package/out/types.luau ADDED
@@ -0,0 +1,8 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --* A task is represented as an array where the first element is the task name and the rest are arguments
3
+ --* State can be any object with string keys
4
+ --* An operator takes a state and task arguments, returns a new state or undefined if not applicable
5
+ --* A method takes a state and task arguments, returns subtasks or undefined if not applicable
6
+ --* Collection of operators keyed by task name
7
+ --* Collection of methods for a task (multiple methods can apply to the same task)
8
+ return nil
package/out/util.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function deepClone<T>(obj: T): T;
2
+ export declare function tail<T extends defined>(arr: T[]): T[];
3
+ export declare function extend<T extends object>(target: T, source: T): T;
package/out/util.luau ADDED
@@ -0,0 +1,31 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local function deepClone(obj)
3
+ local _obj = obj
4
+ if typeof(_obj) ~= "table" then
5
+ return obj
6
+ end
7
+ local result = {}
8
+ for key, value in pairs(obj) do
9
+ result[key] = deepClone(value)
10
+ end
11
+ return result
12
+ end
13
+ local function tail(arr)
14
+ local result = {}
15
+ for i = 1, #arr - 1 do
16
+ local _arg0 = arr[i + 1]
17
+ table.insert(result, _arg0)
18
+ end
19
+ return result
20
+ end
21
+ local function extend(target, source)
22
+ for key, value in pairs(source) do
23
+ target[key] = value
24
+ end
25
+ return target
26
+ end
27
+ return {
28
+ deepClone = deepClone,
29
+ tail = tail,
30
+ extend = extend,
31
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@rbxts/htn-shop",
3
+ "version": "1.0.1",
4
+ "description": "a simple HTN SHOP-like planner",
5
+ "author": "shrjrd",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "planning",
9
+ "planner",
10
+ "shop",
11
+ "HTN"
12
+ ],
13
+ "main": "out/init.lua",
14
+ "types": "out/index.d.ts",
15
+ "files": [
16
+ "out",
17
+ "!**/*.tsbuildinfo",
18
+ "!out/*.test.*",
19
+ "!out/jest.*"
20
+ ],
21
+ "devDependencies": {
22
+ "@rbxts/compiler-types": "^3.0.0-types.0",
23
+ "@rbxts/jest": "^3.13.3-alpha.2",
24
+ "@rbxts/jest-globals": "^3.13.3-alpha.2",
25
+ "@rbxts/types": "^1.0.894",
26
+ "@typescript-eslint/eslint-plugin": "^8.48.0",
27
+ "@typescript-eslint/parser": "^8.48.0",
28
+ "eslint": "^9.39.1",
29
+ "eslint-config-prettier": "^10.1.8",
30
+ "eslint-plugin-prettier": "^5.5.4",
31
+ "eslint-plugin-roblox-ts": "^1.3.0",
32
+ "prettier": "^3.7.0",
33
+ "roblox-ts": "^3.0.0",
34
+ "rojo": "^1.0.3-rojo7.6.1",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "scripts": {
38
+ "build": "rbxtsc",
39
+ "build:dev": "rbxtsc --type game --rojo default.project.json",
40
+ "build:rbxl": "rojo sourcemap -o sourcemap.json && rojo build -o place.rbxl",
41
+ "watch": "npm run build:dev && npx concurrently \"npm run build:dev -- -w\" \"rojo serve\""
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/shrjrd/rbxts-htn-shop.git"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }