@rbxts/insitux
v23.7.23
Published
Extensible s-expression scripting language enabling players to safely mod Roblox games themselves.
Downloads
16
Maintainers
Readme
- Main Github repository - learn everything else about Insitux here
- Roblox-TS NPM package and its Github repository.
Usage
Include https://github.com/insitux/rbxts-Insitux into your Roblox-TS project: npm i @rbxts/insitux
Exposed are invoke()
and invokeFunction()
. Both require a Ctx
instance, along with a generated source ID.
Further explanation can be found in the docstring of most functions and types like invoke
, Ctx
, symbols
, etc.
export type Ctx = {
set: (key: string, val: Val) => undefined | string;
get: (key: string) => ValOrErr;
exe: (name: string, args: Val[]) => ValOrErr;
functions: ExternalFunction[];
print: (str: string, withNewline: boolean) => void;
env: Env;
loopBudget: number;
rangeBudget: number;
callBudget: number;
recurBudget: number;
};
set
andget
should be used to directly write/read values in your game.functions
lets you define your own Insitux functions, with the same arity and type-checking as internal operations!exe
is used to handle function calls any time Insitux fails to dereference an expression head as something internal or previously defined throughCtx.functions[]
. For example(abc 123)
, unlessabc
is an already defined let/var/function it will callexe
withabc
asname
, and[{t: "num" v: 123}]
asargs
.print
is called whenprint
orprint-str
is used within Insituxenv
persists user defined variables and functions.- The budgets set a limit on looping, range creation, function calls and
explicit recurs. This will vary between games so start with a safe
1000
and increase steadily.
If anybody could improve this guide please make a PR!
Various examples
; Test if 2D point is inside 2D area
(function inside-2d? X Y areaX areaY areaW areaH
(and (<= areaX X (+ areaX areaW))
(<= areaY Y (+ areaY areaH))))
(inside-2d? 50 50 0 0 100 100) → true
(inside-2d? 50 150 0 0 100 100) → false
; Recursive Fibonacci solver
(function fib n
(if (< n 2) n
(+ (fib (dec n))
(fib (- n 2)))))
(fib 13) → 233
; and iterative
(function fib n
(when (zero? n) (return 0))
(let a 1 b 0)
(loop n i
(let t (+ a b) a b b t)))
(fib 35) → 9227465
; FizzBuzz with match syntax
(function fizzbuzz n
(match (map (rem n) [3 5])
[0 0] "FizzBuzz"
[0 _] "Fizz"
[_ 0] "Buzz"
n))
(map fizzbuzz (range 10 16))
→ ["Buzz" 11 "Fizz" 13 14 "FizzBuzz"]
; Filter for vectors and strings above a certain length
(filter 2 [[1] [:a :b :c] "hello" "hi"])
→ [[:a :b :c] "hello"]
; Flatten a vector one level deep
(.. .. vec [[0 1] 2 3 [4 5]])
→ [0 1 2 3 4 5]
; Triple every vector item, four different ways
(for * [0 1 2 3 4] [3])
(map #(* 3 %) [0 1 2 3 4])
(map @(* 3) [0 1 2 3 4])
(map (* 3) [0 1 2 3 4])
→ [0 3 6 9 12]
; Primes calculator
(reduce
(fn primes num
(if (find zero? (map (rem num) primes))
primes
(append num primes)))
[2]
(range 3 1000))
; Generate random strong password
(-> #(map rand-int [97 65 48 33] [123 91 58 48])
(times 4)
flatten
shuffle
(map char-code)
(.. str))
→ "d$W1iP*tO9'V9(y8"
; Palindrome checker
;Note: returning non-false or non-null is truthy in Insitux
(function palindrome? x
(.. and (map = x (reverse x))))
;or
(function palindrome? x
(= x (reverse x))) ;Works even for vectors due to deep equality checks
(palindrome? "aabbxbbaa") → true
(palindrome? "abcd") → false
(palindrome? [0 1 2]) → false
(palindrome? [2 1 2]) → true
; Matrix addition, subtraction, transposition
(var A [[3 8] [4 6]]
B [[4 0] [1 -9]])
(map (map +) A B)
→ [[7 8] [5 -3]]
(var M [[2 -4] [7 10]])
(map (map -) M)
→ [[-2 4] [-7 -10]]
(var M [[0 1 2] [3 4 5]])
(@(.. map vec) M)
→ [[0 3] [1 4] [2 5]]
; Find first repeated letter
(function find-two-in-row text
(-> (map (.. ==) text (skip 1 text))
(find val)))
(find-two-in-row "Hello") → "l"
; Add thousands separator
(var thousands (comp str reverse (partition 3) reverse (join ",")))
(thousands 1234567890) → "1,432,765,098"
; Clojure's comp
(function comp f
(let funcs (sect args))
#(do (let 1st (... f args))
(reduce #(%1 %) 1st funcs)))
(map (comp + inc) [0 1 2 3 4] [0 1 2 3 4])
→ [1 3 5 7 9]
; Time a function call
(function measure
(let [start result end] [(time) (... . args) (time)])
(str result " took " (- end start) "ms"))
(measure fib 35)
→ "9227465 took 22914ms"
;Insitux quine
(#(join(char-code 34)[% %(char-code 41)])"(#(join(char-code 34)[% %(char-code 41)])")
→ (#(join(char-code 34)[% %(char-code 41)])"(#(join(char-code 34)[% %(char-code 41)])")
; Display the Mandelbrot fractal as ASCII
(function mandelbrot width height depth
(.. str (for #(do
(let c_re (/ (* (- % (/ width 2)) 4) width)
c_im (/ (* (- %1 (/ height 2)) 4) width))
(let x 0 y 0 i 0)
(while (and (<= (+ (** x) (** y)) 4)
(< i depth))
(let x2 (+ c_re (- (** x) (** y)))
y (+ c_im (* 2 x y))
x x2
i (inc i)))
(str ((zero? %) "\n" "") (i "ABCDEFGHIJ ")))
(range width) (range height))))
(mandelbrot 56 32 10)
; Convert nested arrays and dictionaries into HTML
(function vec->html v
(if! (vec? v) (return v))
(let [tag attr] v
from-key @((key? %) (-> % str sect))
has-attr (dict? attr)
make-attr (fn [k v] (str " " (from-key k) "=\"" v "\""))
attr (if has-attr (map make-attr attr) "")
tag (from-key tag)
body (sect v (has-attr 2 1))
body (map vec->html body))
(if (["link" "meta" "input" "img"] tag)
(.. str "<" tag attr " />")
(.. str "<" tag attr ">" body "</" tag ">")))
(vec->html
[:div
[:h2 "Hello"]
[:p "PI is " [:b (round 2 PI)] "."]
[:p "Find out about Insitux on "
[:a {:href "https://insitux.github.io"}
"Github"]]])
→ "<div><h2>Hello</h2><p>PI is <b>3.14</b>.</p><p>Find out about Insitux on <a href=\"https://insitux.github.io\">Github</a></p></div>"
; Neural network for genetic algorithms with two hidden layers
(function sigmoid (/ 1 (inc (** E (neg %)))))
(function m (< .8 (rand)))
(function make-brain num-in num-out num-hid
(let make-neuron #{:bias 0 :weights (repeat 1 %)})
[(repeat (make-neuron num-in) num-hid)
(repeat (make-neuron num-hid) num-hid)
(repeat (make-neuron num-hid) num-out)])
(function mutate brain
(let mutate-neuron
#{:bias ((m) (rand -2 2) (:bias %))
:weights (map @((m) (rand -1 1)) (:weights %))})
(map (map mutate-neuron) brain))
(function neuron-think inputs neuron
(let weighted (map * (:weights neuron) inputs)
avg (average weighted))
(sigmoid (+ avg (:bias neuron))))
(function think brain inputs
(reduce (fn in layer (map @(neuron-think in) layer))
inputs brain))
(var brain (mutate (make-brain 5 5 5)))
(-> (repeat #(rand-int) 5)
@(think brain)
(map @(round 2)))
→ [0.23 0.41 0.63 0.64 0.57]