├── docs ├── simgen.odt ├── iclp2013.pdf ├── tutorial.odp ├── schrijvers.pdf ├── pressuregraph.png ├── tutorial.txt ├── simgenalgorithm.txt ├── agentbased.md ├── simai_world.svg └── simai_world.graphml ├── analysis └── hwclient.ods ├── oldstuff ├── introtopddl2.pdf ├── RigSwi-TPLP11.pdf ├── clptest.pl ├── test_bt.pl ├── fabrizio.pl ├── piddle.pl ├── state_life.pl ├── life.pl ├── old_behavior_tree_parser.pl └── talespin.pl ├── examples ├── durtest.bt ├── tests.bt ├── lotsodata.bt ├── coffee_maker.bt ├── hwclient.bt ├── jittery.bt ├── ht1.bt ├── ht2.bt ├── pintest.bt ├── paralleltest.bt ├── coffee_maker2.bt ├── anontest.bt ├── hwclient2.bt └── ht3.bt ├── .gitignore ├── simgen ├── simgen.pl ├── nodes │ ├── set_guard.pl │ ├── clear_guard.pl │ ├── try_decorator.pl │ ├── fail_decorator.pl │ ├── not_decorator.pl │ ├── dur.pl │ ├── wait_guard.pl │ ├── check_guard.pl │ ├── repeat_decorator.pl │ ├── pin_decorator.pl │ ├── random_selector.pl │ ├── sequence.pl │ ├── attempt.pl │ ├── parallel.pl │ ├── paraselect.pl │ ├── random_sequence.pl │ └── pdq.pl ├── guard_manager.pl ├── bt_parser_test.pl ├── ast_transform.pl ├── clocks.pl ├── print_system.pl ├── valuator.pl ├── behavior_tree.pl ├── functions.pl ├── behavior_tree_parser.pl └── bt_impl.pl ├── test ├── tests.bt └── unit_tests.pl ├── LICENSE ├── makedata.pl ├── bt_example.pl ├── bt_example_jitter.pl └── README.md /docs/simgen.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/docs/simgen.odt -------------------------------------------------------------------------------- /docs/iclp2013.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/docs/iclp2013.pdf -------------------------------------------------------------------------------- /docs/tutorial.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/docs/tutorial.odp -------------------------------------------------------------------------------- /docs/schrijvers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/docs/schrijvers.pdf -------------------------------------------------------------------------------- /analysis/hwclient.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/analysis/hwclient.ods -------------------------------------------------------------------------------- /docs/pressuregraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/docs/pressuregraph.png -------------------------------------------------------------------------------- /oldstuff/introtopddl2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/oldstuff/introtopddl2.pdf -------------------------------------------------------------------------------- /oldstuff/RigSwi-TPLP11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simularity/SimGen/HEAD/oldstuff/RigSwi-TPLP11.pdf -------------------------------------------------------------------------------- /examples/durtest.bt: -------------------------------------------------------------------------------- 1 | test -> 2 | a, 3 | b, 4 | c. 5 | 6 | 7 | a dur 4. 8 | b dur 2. 9 | c dur 4. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.*~ 2 | *.odt# 3 | oldstuff/ 4 | *.csv 5 | *.simai 6 | docs/*.*# 7 | /**/*.*~ 8 | /**/*~ 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/tutorial.txt: -------------------------------------------------------------------------------- 1 | SimGen is a tool for creating simulated data. 2 | SimGen is all about making agents that control each other. 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/tests.bt: -------------------------------------------------------------------------------- 1 | /* 2 | Bunch of simple tests 3 | 4 | */ 5 | once ! 6 | p = 1 7 | ; 8 | p := p + 1 9 | ; 10 | p < 2. 11 | 12 | -------------------------------------------------------------------------------- /oldstuff/clptest.pl: -------------------------------------------------------------------------------- 1 | a(0). 2 | a(B) :- 3 | B > 0, 4 | NB #= B - 1, 5 | a(NB). 6 | 7 | test :- between(1,1000, _),a(10000),fail. 8 | test. 9 | 10 | -------------------------------------------------------------------------------- /simgen/simgen.pl: -------------------------------------------------------------------------------- 1 | :- module(simgen, []). 2 | /** SimGen - Simularity Inc. Simulation Generator 3 | * 4 | */ 5 | :- reexport(simgen(behavior_tree)). 6 | 7 | -------------------------------------------------------------------------------- /examples/lotsodata.bt: -------------------------------------------------------------------------------- 1 | root -> 2 | lf. 3 | 4 | lf ! 5 | p = 100, 6 | t = 20 7 | ; 8 | p := levy_flight(p, 0, 200), 9 | t := wander(t, 0, 200, 3) 10 | ; 11 | p < 5000 12 | . 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/coffee_maker.bt: -------------------------------------------------------------------------------- 1 | coffee_sim => 2 | user, 3 | coffee_maker 4 | . 5 | 6 | user -> 7 | turn_on, 8 | wait_for_coffee, 9 | wait_some, 10 | drink 11 | . 12 | 13 | turn_on -> 14 | wait_some, 15 | switch_on 16 | . 17 | 18 | wait_some dur 10. 19 | 20 | switch_on set on 21 | . 22 | 23 | wait_for_coffee wait coffee_ready 24 | . 25 | 26 | drink set drink 27 | . 28 | 29 | coffee_ready dur 10. 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/hwclient.bt: -------------------------------------------------------------------------------- 1 | root ~? 2 | type_a, 3 | type_b. 4 | 5 | type_a ! 6 | p = 100, 7 | t = 100; 8 | p := levy_flight(p, 0, 200), 9 | t := wander(t, 0, 200, 3), 10 | speed = 500 - p - t. 11 | 12 | type_b ! 13 | p = 100, 14 | t = 100; 15 | p := levy_flight(p, 0, 200), 16 | t := wander(t, 0, 200, 3), 17 | speed = 500 + clock() - p - t. 18 | 19 | % this file has syntax errors 20 | % type_a and type_b have no conditions 21 | -------------------------------------------------------------------------------- /examples/jittery.bt: -------------------------------------------------------------------------------- 1 | root => 2 | lf, 3 | jitter 4 | . 5 | 6 | lf ! 7 | p = 100, 8 | t = 20 9 | ; 10 | p := levy_flight(p, 0, 200), 11 | t := wander(t, 0, 200, 3) 12 | ; 13 | p < 5000 14 | . 15 | 16 | jitter ! 17 | a = 100, 18 | b = 100, 19 | c = 100 20 | ; 21 | c := levy_flight(c, 0, 200), 22 | b := levy_flight(c + b, 0, 200), 23 | a := wander(a, 0, 200, 3) 24 | ; 25 | a < 5000 26 | . 27 | -------------------------------------------------------------------------------- /simgen/nodes/set_guard.pl: -------------------------------------------------------------------------------- 1 | :- module(set_guard, []). 2 | /** Check a guard. Fail when it's reset 3 | * 4 | */ 5 | :- use_module(simgen(bt_impl), [emit/1]). 6 | :- use_module(simgen(guard_manager)). 7 | 8 | :- multifile bt_impl:make_cn_impl/3. 9 | 10 | bt_impl:make_cn_impl( set , C-N, _) :- 11 | bt_impl:node_(_, N, set, [GuardName], _), 12 | set_guard(C, GuardName), 13 | emit(starting(C-N)), 14 | emit(stopped(C-N, done)). 15 | 16 | 17 | -------------------------------------------------------------------------------- /simgen/nodes/clear_guard.pl: -------------------------------------------------------------------------------- 1 | :- module(clear_guard, []). 2 | /** Check a guard. Fail when it's reset 3 | * 4 | */ 5 | :- use_module(simgen(bt_impl), [emit/1]). 6 | :- use_module(simgen(guard_manager)). 7 | 8 | :- multifile bt_impl:make_cn_impl/3. 9 | 10 | bt_impl:make_cn_impl( clear , C-N, _) :- 11 | bt_impl:node_(_, N, clear, [GuardName], _), 12 | clear_guard(C, GuardName), 13 | emit(starting(C-N)), 14 | emit(stopped(C-N, done)). 15 | 16 | 17 | -------------------------------------------------------------------------------- /oldstuff/test_bt.pl: -------------------------------------------------------------------------------- 1 | :- use_module(behavior_tree). 2 | 3 | main :- repeat, 4 | tick(root), 5 | fail. 6 | main. 7 | 8 | /* 9 | % sequence 10 | root => 11 | setup, 12 | live. 13 | 14 | setup => 15 | ??gender_female ??= @@ 1/2. 16 | 17 | 18 | live => 19 | prenatal, 20 | infancy, 21 | childhood, 22 | adolescence, 23 | young_adulthood, 24 | adulthood, 25 | middle_age, 26 | retirement, 27 | end_of_life. 28 | 29 | prenatal >> 30 | */ 31 | 32 | -------------------------------------------------------------------------------- /examples/ht1.bt: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Test of everything implemented so far 4 | */ 5 | 6 | testall -> 7 | try_test_op_priority, 8 | try_test_immediate_fail_pdq, 9 | try_test_levy_flight, 10 | try_test_wander, 11 | try_test_subtract_negate, 12 | try_test_boundary, 13 | try_test_all_operators, 14 | try_test_various_assignment. 15 | 16 | try_test_op_priority try test_op_priority. 17 | test_op_priority ! 18 | x = 0 19 | ; 20 | x := x + 2 + 2 * 3 21 | ; 22 | x < 50. 23 | 24 | -------------------------------------------------------------------------------- /oldstuff/fabrizio.pl: -------------------------------------------------------------------------------- 1 | :- module(fabrizio, [test/1, life_circumstances/4]). 2 | 3 | :- use_module(library(mcintyre)). 4 | 5 | 6 | 7 | :- mc. 8 | 9 | :- begin_lpad. 10 | life_circumstances([sex(Sex), parent_econ(Econ)]) :- 11 | sex(Sex),parent_econ(Econ). 12 | 13 | sex(m):1/2 ; sex(f) : 1/2. 14 | parent_econ(poor):0.1; parent_econ(middle):0.6; parent_econ(poor):0.3. 15 | :- end_lpad. 16 | 17 | /** 18 | 19 | ?- mc_sample_arg_first(life_circumstances(L),1,L,O). 20 | 21 | */ 22 | test(O) :- mc_sample_arg_first(fabrizio:life_circumstances(L),1,L,O). 23 | -------------------------------------------------------------------------------- /simgen/guard_manager.pl: -------------------------------------------------------------------------------- 1 | :- module(guard_manager, [ 2 | guard/2, 3 | set_guard/2, 4 | clear_guard/2, 5 | reset_guards/0]). 6 | /** Support for guards 7 | * 8 | * guards are persistent conditions, checked each tick 9 | * 10 | */ 11 | 12 | :- dynamic guard_/2. 13 | 14 | guard(C, X) :- guard_(C, X). 15 | 16 | set_guard(C, X) :- asserta(guard_(C, X)). 17 | 18 | clear_guard(C, X) :- retractall(guard_(C, X)). 19 | 20 | reset_guards :- retractall(guard_(_, _)). 21 | 22 | 23 | :-listen(simulation_starting, reset). 24 | 25 | reset :- 26 | reset_guards. 27 | 28 | -------------------------------------------------------------------------------- /test/tests.bt: -------------------------------------------------------------------------------- 1 | /* 2 | Unit tests for bt language 3 | */ 4 | 5 | root -> 6 | test_pdq. 7 | 8 | test_pdq -> 9 | { try simple_add}, 10 | gap, 11 | {try complex_expr }, 12 | {dur 10}, 13 | {try functions } 14 | . 15 | 16 | gap dur 5. 17 | 18 | simple_add ! 19 | x = 0 20 | ; 21 | x := x + 1 22 | ; 23 | x < 10 24 | . 25 | 26 | complex_expr ! 27 | y = 0, 28 | z = 2 29 | ; 30 | y := (y + z) / 2 + 1, 31 | z := z % otherwise this will fail after 2nd cycle because there will be no old z value 32 | ; 33 | y < 10 34 | . 35 | 36 | 37 | functions ! 38 | x = 0 39 | ; 40 | x := levy_flight(x, -100, 100) 41 | ; 42 | x > -80, 43 | x < 80 44 | . 45 | 46 | 47 | -------------------------------------------------------------------------------- /simgen/bt_parser_test.pl: -------------------------------------------------------------------------------- 1 | :- module(bt_parser_test, [go/0]). 2 | /** test suite for behavior_tree_parser 3 | * 4 | */ 5 | 6 | :- use_module(simgen(behavior_tree_parser)). 7 | 8 | go :- 9 | content(Content), 10 | phrase(bt_dcg(define_bt(Answer)), Content), 11 | !, 12 | show_clauses(Answer). 13 | 14 | show_clauses([]). 15 | show_clauses([H|T]) :- 16 | portray_clause(H), 17 | nl, 18 | show_clauses(T). 19 | 20 | content( 21 | `wawei ~? 22 | type_a, 23 | type_b. 24 | 25 | type_a ! 26 | p = 100, 27 | t = 100; 28 | p := levy_flight(p, 0, 200), 29 | t := wander(t, 0, 200, 3), 30 | speed = 500 - p - t. 31 | 32 | type_b ! 33 | p = 100, 34 | t = 100; 35 | p := levy_flight(p, 0 200), 36 | t := wander(t, 0, 200, 3), 37 | speed = 500 + clock() - p - t.`). 38 | -------------------------------------------------------------------------------- /examples/ht2.bt: -------------------------------------------------------------------------------- 1 | 2 | testall -> 3 | try_test_op_priority, 4 | try_test_immediate_fail_pdq, 5 | try_test_levy_flight, 6 | try_taco, 7 | try_buffalo. 8 | 9 | try_test_op_priority try test_op_priority. 10 | test_op_priority ! 11 | x = 0 12 | ; 13 | x := x + 2 + 2 * 3 14 | ; 15 | x < 50. 16 | 17 | % we don't fail on first tick 18 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 19 | test_immediate_fail_pdq ! 20 | xa = 10 21 | ; 22 | xa := xa + 1 23 | ; 24 | xa < 5 25 | . 26 | 27 | try_test_levy_flight try test_levy_flight. 28 | test_levy_flight ! 29 | xb = 0 30 | ; 31 | xb := xb + 5 32 | ; 33 | xb > -80, 34 | xb < 80 35 | . 36 | 37 | 38 | try_taco try taco. 39 | taco ! 40 | taco = 0 41 | ; 42 | taco := taco + 1 43 | ; 44 | taco < 5. 45 | 46 | try_buffalo try buffalo. 47 | buffalo ! 48 | buff = 0 49 | ; 50 | buff := buff + 1 51 | ; 52 | buff < 7 53 | . 54 | 55 | -------------------------------------------------------------------------------- /examples/pintest.bt: -------------------------------------------------------------------------------- 1 | 2 | testall -> 3 | try_test_op_priority, 4 | try_test_immediate_fail_pdq, 5 | try_test_levy_flight, 6 | try_taco, 7 | try_buffalo. 8 | 9 | try_test_op_priority try test_op_priority. 10 | test_op_priority ! 11 | x = 0 12 | ; 13 | x := x + 2 + 2 * 3 14 | ; 15 | x < 50. 16 | 17 | % we don't fail on first tick 18 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 19 | test_immediate_fail_pdq ! 20 | xa = 10 21 | ; 22 | xa := xa + 1 23 | ; 24 | xa < 5 25 | . 26 | 27 | try_test_levy_flight pin test_levy_flight. 28 | test_levy_flight ! 29 | xb = 0 30 | ; 31 | xb := xb + 5 32 | ; 33 | xb > -80, 34 | xb < 80 35 | . 36 | 37 | 38 | try_taco try taco. 39 | taco ! 40 | taco = 0 41 | ; 42 | taco := taco + 1 43 | ; 44 | taco < 5. 45 | 46 | try_buffalo try buffalo. 47 | buffalo ! 48 | buff = 0 49 | ; 50 | buff := buff + 1 51 | ; 52 | buff < 7 53 | . 54 | 55 | -------------------------------------------------------------------------------- /simgen/ast_transform.pl: -------------------------------------------------------------------------------- 1 | :- module(ast_transform, 2 | [ 3 | collect_anons/2 4 | ]). 5 | /** Transform the ast 6 | * 7 | * code to simplify the ast 8 | */ 9 | 10 | %! collect_anons(+List:list, -Anons:list) is det 11 | % 12 | % extract the anonymous 13 | collect_anons([], []). 14 | collect_anons([':-'(def_node(H,O,A,C)) | T], [':-'(def_node(H,O,A,NC)) | XT]) :- 15 | anons_from_children(C, NC, Anons), 16 | append(Anons, T, NT), % note we reprocess new def_nodes 17 | collect_anons(NT, XT). 18 | collect_anons([H | T], [H | NT]) :- 19 | H \= ':-'(def_node(_,_,_,_)), 20 | collect_anons(T, NT). 21 | 22 | anons_from_children([], [], []). 23 | anons_from_children([H | T], [H |NT], Anons) :- 24 | H \= anon_node(_,_,_,_), 25 | anons_from_children(T, NT, Anons). 26 | anons_from_children([anon_node(H,O,A,C) | T], 27 | [H | NT], 28 | [':-'(def_node(H,O,A,C)) | Anons]) :- 29 | anons_from_children(T, NT, Anons). 30 | 31 | -------------------------------------------------------------------------------- /examples/paralleltest.bt: -------------------------------------------------------------------------------- 1 | 2 | testall -> 3 | try_test_fail, 4 | test_done. 5 | 6 | test_done => 7 | try_test_op_priority, 8 | try_test_immediate_fail_pdq, 9 | try_test_levy_flight, 10 | try_taco, 11 | try_buffalo. 12 | 13 | try_test_fail try test_fail. 14 | test_fail => 15 | test_op_priority, 16 | test_levy_flight, 17 | taco, 18 | buffalo. 19 | 20 | try_test_op_priority try test_op_priority. 21 | test_op_priority ! 22 | x = 0 23 | ; 24 | x := x + 2 + 2 * 3 25 | ; 26 | x < 50. 27 | 28 | % we don't fail on first tick 29 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 30 | test_immediate_fail_pdq ! 31 | xa = 10 32 | ; 33 | xa := xa + 1 34 | ; 35 | xa < 5 36 | . 37 | 38 | try_test_levy_flight pin test_levy_flight. 39 | test_levy_flight ! 40 | xb = 0 41 | ; 42 | xb := xb + 5 43 | ; 44 | xb > -80, 45 | xb < 80 46 | . 47 | 48 | 49 | try_taco try taco. 50 | taco ! 51 | taco = 0 52 | ; 53 | taco := taco + 1 54 | ; 55 | taco < 5. 56 | 57 | try_buffalo try buffalo. 58 | buffalo ! 59 | buff = 0 60 | ; 61 | buff := buff + 1 62 | ; 63 | buff < 7 64 | . 65 | 66 | -------------------------------------------------------------------------------- /simgen/nodes/try_decorator.pl: -------------------------------------------------------------------------------- 1 | :- module(try_decorator, []). 2 | 3 | :- dynamic running/1. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | 7 | :-listen(simulation_starting, reset). 8 | 9 | reset :- 10 | retractall(running(_)). 11 | 12 | :- multifile bt_impl:make_cn_impl/3. 13 | 14 | bt_impl:make_cn_impl(try , C-N, _-_) :- 15 | running(C-N), 16 | !. 17 | bt_impl:make_cn_impl(try, C-N, CParent-NParent) :- 18 | asserta(running(C-N)), 19 | listen(C-N, terminate(C-N), try_decorator:terminate(C-N)), 20 | listen(C-N, terminate_if_child(CParent-NParent), 21 | try_decorator:terminate(C-N)), 22 | emit(starting(C-N)), 23 | start_child(C-N). 24 | 25 | % start the only child of C-N, which is a try decorator 26 | start_child(C-N) :- 27 | bt_impl:node_(_M, N, try, _Args, [Kiddo]), 28 | make_cn(C-Kiddo, C-N), 29 | % stop the parent if the child stops 30 | listen(C-N, stopped(C-Kiddo, Reason), 31 | stop_me(C-N, Reason)). 32 | 33 | stop_me(C-N, How) :- 34 | member(How, [fail, done]), 35 | unlisten(C-N, _, _), 36 | retractall(running(C-N)), 37 | emit(stopped(C-N, done)). 38 | 39 | terminate(C-N) :- 40 | unlisten(C-N, _, _), 41 | emit(terminate_if_child(C-N)), 42 | retractall(running(C-N)), 43 | emit(stopped(C-N, terminated)). 44 | 45 | -------------------------------------------------------------------------------- /simgen/nodes/fail_decorator.pl: -------------------------------------------------------------------------------- 1 | :- module(fail_decorator, []). 2 | 3 | :- dynamic running/1. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | 7 | :-listen(simulation_starting, reset). 8 | 9 | reset :- 10 | retractall(running(_)). 11 | 12 | :- multifile bt_impl:make_cn_impl/3. 13 | 14 | bt_impl:make_cn_impl(fail , C-N, _-_) :- 15 | running(C-N), 16 | !. 17 | bt_impl:make_cn_impl(fail, C-N, CParent-NParent) :- 18 | asserta(running(C-N)), 19 | listen(C-N, terminate(C-N), fail_decorator:terminate(C-N)), 20 | listen(C-N, terminate_if_child(CParent-NParent), 21 | fail_decorator:terminate(C-N)), 22 | emit(starting(C-N)), 23 | start_child(C-N). 24 | 25 | % start the only child of C-N, which is a fail decorator 26 | start_child(C-N) :- 27 | bt_impl:node_(_M, N, fail, _Args, [Kiddo]), 28 | make_cn(C-Kiddo, C-N), 29 | % stop the parent if the child stops 30 | listen(C-N, stopped(C-Kiddo, Reason), 31 | stop_me(C-N, Reason)). 32 | 33 | stop_me(C-N, How) :- 34 | member(How, [fail, done]), 35 | unlisten(C-N, _, _), 36 | retractall(running(C-N)), 37 | emit(stopped(C-N, fail)). 38 | 39 | terminate(C-N) :- 40 | unlisten(C-N, _, _), 41 | emit(terminate_if_child(C-N)), 42 | retractall(running(C-N)), 43 | emit(stopped(C-N, terminated)). 44 | 45 | -------------------------------------------------------------------------------- /simgen/nodes/not_decorator.pl: -------------------------------------------------------------------------------- 1 | :- module(not_decorator, []). 2 | 3 | :- dynamic running/1. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | 7 | :-listen(simulation_starting, reset). 8 | 9 | reset :- 10 | retractall(running(_)). 11 | 12 | :- multifile bt_impl:make_cn_impl/3. 13 | 14 | bt_impl:make_cn_impl(not , C-N, _-_) :- 15 | running(C-N), 16 | !. 17 | bt_impl:make_cn_impl(not, C-N, CParent-NParent) :- 18 | asserta(running(C-N)), 19 | listen(C-N, terminate(C-N), not_decorator:terminate(C-N)), 20 | listen(C-N, terminate_if_child(CParent-NParent), 21 | not_decorator:terminate(C-N)), 22 | emit(starting(C-N)), 23 | start_child(C-N). 24 | 25 | % start the only child of C-N, which is a not decorator 26 | start_child(C-N) :- 27 | bt_impl:node_(_M, N, not, _Args, [Kiddo]), 28 | make_cn(C-Kiddo, C-N), 29 | % stop the parent if the child stops 30 | listen(C-N, stopped(C-Kiddo, Reason), 31 | stop_me(C-N, Reason)). 32 | 33 | stop_me(C-N, fail) :- 34 | unlisten(C-N, _, _), 35 | retractall(running(C-N)), 36 | emit(stopped(C-N, done)). 37 | stop_me(C-N, done) :- 38 | unlisten(C-N, _, _), 39 | retractall(running(C-N)), 40 | emit(stopped(C-N, fail)). 41 | 42 | terminate(C-N) :- 43 | unlisten(C-N, _, _), 44 | emit(terminate_if_child(C-N)), 45 | retractall(running(C-N)), 46 | emit(stopped(C-N, terminated)). 47 | 48 | -------------------------------------------------------------------------------- /simgen/nodes/dur.pl: -------------------------------------------------------------------------------- 1 | :- module(dur, []). 2 | /** Check a guard. Succeed when it's set 3 | * 4 | */ 5 | :- dynamic running/2. % C-N, DoneTime (context) 6 | 7 | :- use_module(simgen(bt_impl), [emit/1]). 8 | :- use_module(simgen(guard_manager)). 9 | :- use_module(simgen(clocks), [get_clock/2]). 10 | 11 | :-listen(simulation_starting, reset). 12 | 13 | reset :- 14 | retractall(running(_, _)). 15 | 16 | :- multifile bt_impl:make_cn_impl/3. 17 | 18 | bt_impl:make_cn_impl( dur , C-N, _-_) :- 19 | running(C-N, _), % TODO should this be evil? 20 | !. 21 | bt_impl:make_cn_impl( dur , C-N, CParent-NParent) :- 22 | \+ running(C-N, _), 23 | bt_impl:node_(_, N, dur, [Dur], _), 24 | get_clock(C, Time), 25 | EndTime is Time + Dur, 26 | asserta(running(C-N, EndTime)), 27 | listen(C-N, terminate(C-N), terminate(C-N)), 28 | listen(C-N, terminate_if_child(CParent-NParent), 29 | terminate(C-N)), 30 | listen(C-N, tick_start, tick_start(C-N)), 31 | emit(starting(C-N)). 32 | 33 | terminate(C-N) :- 34 | unlisten(C-N, _, _), 35 | retractall(running(C-N, _)), 36 | emit(stopped(C-N, terminated)). 37 | 38 | tick_start(C-N) :- 39 | running(C-N, EndTime), 40 | get_clock(C, Time), 41 | ( Time < EndTime 42 | -> true 43 | ; 44 | unlisten(C-N, _, _), 45 | retractall(running(C-N, _)), 46 | emit(stopped(C-N, done)) 47 | ). 48 | 49 | 50 | -------------------------------------------------------------------------------- /simgen/nodes/wait_guard.pl: -------------------------------------------------------------------------------- 1 | :- module(wait_guard, []). 2 | /** Check a guard. Succeed when it's set 3 | * 4 | */ 5 | :- dynamic running/1. 6 | 7 | :- use_module(simgen(bt_impl), [emit/1]). 8 | :- use_module(simgen(guard_manager)). 9 | 10 | :-listen(simulation_starting, reset). 11 | 12 | reset :- 13 | retractall(running(_)). 14 | 15 | :- multifile bt_impl:make_cn_impl/3. 16 | 17 | bt_impl:make_cn_impl( '-?' , C-N, _-_) :- 18 | running(C-N), % TODO should this be evil? 19 | !. 20 | bt_impl:make_cn_impl( '-?' , C-N, CParent-NParent) :- 21 | \+ running(C-N), 22 | bt_impl:node_(_, N, '-?', [GuardName], _), 23 | \+ guard(C, GuardName), 24 | asserta(running(C-N)), 25 | listen(C-N, terminate(C-N), terminate(C-N)), 26 | listen(C-N, terminate_if_child(CParent-NParent), 27 | terminate(C-N)), 28 | listen(C-N, tick_start, tick_start(C-N)), 29 | emit(starting(C-N)). 30 | bt_impl:make_cn_impl( '-?' , C-N, _) :- 31 | bt_impl:node_(_, N, _, [GuardName], _), 32 | guard(C, GuardName), 33 | emit(starting(C-N)), 34 | emit(stopped(C-N, done)). 35 | 36 | terminate(C-N) :- 37 | unlisten(C-N, _, _), 38 | retractall(running(C-N)), 39 | emit(stopped(C-N, terminated)). 40 | 41 | tick_start(C-N) :- 42 | bt_impl:node_(_, N, '-?', [GuardName], _), 43 | guard(C, GuardName), 44 | unlisten(C-N, _, _), 45 | retractall(running(C-N)), 46 | emit(stopped(C-N, done)). 47 | tick_start(_-_). 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/coffee_maker2.bt: -------------------------------------------------------------------------------- 1 | coffee_sim -> 2 | { clear on }, 3 | { => 4 | user, 5 | coffee_maker 6 | } 7 | . 8 | 9 | /* The User */ 10 | user -> 11 | {dur 10}, 12 | fill_coffee_maker, 13 | {set on}, 14 | {-? coffee_ready}, 15 | pour_cup, 16 | {clear on}, 17 | drink 18 | . 19 | 20 | pour_cup dur 2 21 | . 22 | 23 | drink dur 120 24 | . 25 | 26 | fill_coffee_maker ! 27 | wboil = 500, 28 | wfilter = 0, 29 | ccup = 0 30 | ; 31 | ; 32 | 0 > 1 33 | . 34 | 35 | /* The pot */ 36 | 37 | coffee_maker => 38 | filter_basket, 39 | boiler % TODO cup 40 | . 41 | 42 | /* The filter basket. 43 | repeatedly filters water into coffee as long as there's 44 | water. (we're not simulating the grounds being depleted) */ 45 | 46 | filter_basket <> 47 | { ! 48 | ; 49 | wfilter := wfilter - 4, 50 | ccup := ccup + 4 51 | ; 52 | wfilter > 0 53 | } 54 | . 55 | 56 | /* the boiler heats water as long as there's water and it's on */ 57 | 58 | boiler <> 59 | { -> 60 | {-? on}, % wait til the user turns it on 61 | heat_water, 62 | boil 63 | } 64 | . 65 | 66 | /* caraffe heater - we're not modeling it for the moment */ 67 | 68 | 69 | boil => 70 | {try boil_water}, 71 | {try filter_coffee} 72 | . 73 | 74 | boil_water ! 75 | ; 76 | wboil := wboil - 5, 77 | wfilter := wfilter + 4 78 | ; 79 | wboil > 0 80 | . 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /simgen/clocks.pl: -------------------------------------------------------------------------------- 1 | :- module(clocks, [abolish_clocks/1, 2 | new_clock/2, 3 | clock_units/2, 4 | update_clocks/0, 5 | get_clock/2]). 6 | 7 | :- use_module(simgen(bt_impl), [bad_thing_happened/0]). 8 | :- use_module(simgen(print_system)). 9 | 10 | :- dynamic clock/2, time_unit/1, tick_length/1. 11 | 12 | abolish_clocks(Name) :- 13 | retractall(clock(Name, _)). 14 | 15 | new_clock(Name, _Start) :- 16 | clock(Name, _), 17 | !, 18 | bt_debug(error(clock, duplicate_clock), 'Error, attempt to create duplicate clock ~w', [Name]), 19 | bad_thing_happened. 20 | new_clock(Name, Start) :- 21 | bt_debug(bt(clock, new_clock), 'created clock ~w ~w', [Name, Start]), 22 | asserta(clock(Name, Start)). 23 | 24 | /** Clock related bt stuff 25 | * 26 | */ 27 | clock_units(TimeUnit, TickLength) :- 28 | retractall(time_unit(_)), 29 | retractall(tick_length(_)), 30 | asserta(time_unit(TimeUnit)), 31 | asserta(tick_length(TickLength)). 32 | 33 | update_clocks :- 34 | bagof(Name, Val^clock(Name, Val), Clocks), 35 | maplist(update_clock , Clocks). 36 | 37 | update_clock(Name) :- 38 | tick_length(Len), 39 | clock(Name, Time), 40 | retractall(clock(Name, _)), 41 | NewTime is Len + Time, 42 | asserta(clock(Name, NewTime)). 43 | 44 | 45 | get_clock(Name, Time) :- 46 | clock(Name, Time). 47 | get_clock(Name, 0) :- 48 | \+ clock(Name, _), 49 | bt_debug(bt(get_clock, no_clock), 50 | '******* BAD CLOCK ~w', [Name]). 51 | -------------------------------------------------------------------------------- /simgen/nodes/check_guard.pl: -------------------------------------------------------------------------------- 1 | :- module(check_guard, []). 2 | /** Check a guard. Fail when it's reset 3 | * 4 | */ 5 | :- dynamic running/1. 6 | 7 | :- use_module(simgen(bt_impl), [emit/1]). 8 | :- use_module(simgen(guard_manager)). 9 | 10 | :-listen(simulation_starting, reset). 11 | 12 | reset :- 13 | retractall(running(_)). 14 | 15 | :- multifile bt_impl:make_cn_impl/3. 16 | 17 | bt_impl:make_cn_impl( '?' , C-N, _-_) :- 18 | running(C-N), % TODO should this be evil? 19 | !. 20 | bt_impl:make_cn_impl( '?' , C-N, CParent-NParent) :- 21 | \+ running(C-N), 22 | bt_impl:node_(_, N, '?', [GuardName], _), 23 | guard(C, GuardName), 24 | asserta(running(C-N)), 25 | listen(C-N, terminate(C-N), terminate(C-N)), 26 | listen(C-N, terminate_if_child(CParent-NParent), 27 | terminate(C-N)), 28 | listen(C-N, tick_start, tick_start(C-N)), 29 | emit(starting(C-N)). 30 | bt_impl:make_cn_impl( '?' , C-N, _) :- 31 | bt_impl:node_(_, N, _, [GuardName], _), 32 | \+ guard(C, GuardName), 33 | emit(starting(C-N)), 34 | emit(stopped(C-N, fail)). 35 | 36 | terminate(C-N) :- 37 | unlisten(C-N, _, _), 38 | retractall(running(C-N)), 39 | emit(stopped(C-N, terminated)). 40 | 41 | tick_start(C-N) :- 42 | bt_impl:node_(_, N, '?', [GuardName], _), 43 | \+ guard(C, GuardName), 44 | unlisten(C-N, _, _), 45 | retractall(running(C-N)), 46 | emit(stopped(C-N, fail)). 47 | tick_start(_-_). 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This software is copyright 3 | 4 | Copyright 2016-2019, Simularity, Inc. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /simgen/nodes/repeat_decorator.pl: -------------------------------------------------------------------------------- 1 | :- module(repeat_decorator, []). 2 | /** Handles the repeat <> operator and the persist <--> operator 3 | * 4 | */ 5 | :- dynamic running/1. 6 | 7 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 8 | 9 | :-listen(simulation_starting, reset). 10 | 11 | reset :- 12 | retractall(running(_)). 13 | 14 | :- multifile bt_impl:make_cn_impl/3. 15 | 16 | bt_impl:make_cn_impl(repeat(_) , C-N, _-_) :- 17 | running(C-N), 18 | !. 19 | bt_impl:make_cn_impl(repeat(_), C-N, CParent-NParent) :- 20 | asserta(running(C-N)), 21 | listen(C-N, terminate(C-N), repeat_decorator:terminate(C-N)), 22 | listen(C-N, terminate_if_child(CParent-NParent), 23 | repeat_decorator:terminate(C-N)), 24 | emit(starting(C-N)), 25 | start_child(C-N). 26 | 27 | % start the only child of C-N, which is a repeat decorator 28 | start_child(C-N) :- 29 | bt_impl:node_(_M, N, repeat(_), _Args, [Kiddo]), 30 | make_cn(C-Kiddo, C-N), 31 | % stop the parent if the child stops 32 | listen(C-N, stopped(C-Kiddo, Reason), 33 | stop_me(C-N, Reason)). 34 | 35 | stop_me(C-N, How) :- 36 | member(How, [fail, done]), 37 | bt_impl:node_(_M, N, repeat(How), _Args, [_Kiddo]), 38 | unlisten(C-N, _, _), 39 | retractall(running(C-N)), 40 | emit(stopped(C-N, done)). 41 | stop_me(C-N, How) :- 42 | member(How, [fail, done]), 43 | bt_impl:node_(_M, N, repeat(WhenStop), _Args, [Kiddo]), 44 | WhenStop \= How, 45 | unlisten(C-N, stopped(C-Kiddo, _)), 46 | start_child(C-N). 47 | 48 | 49 | terminate(C-N) :- 50 | unlisten(C-N, _, _), 51 | emit(terminate_if_child(C-N)), 52 | retractall(running(C-N)), 53 | emit(stopped(C-N, terminated)). 54 | 55 | -------------------------------------------------------------------------------- /simgen/nodes/pin_decorator.pl: -------------------------------------------------------------------------------- 1 | :- module(pin_decorator, []). 2 | /** pin operator. 3 | * 4 | * If the child succeeds, succeeds 5 | * 6 | * If the child fails, emits pin_drop(Context, Time, event) 7 | * and pin_drop(Context, Time, -event) 8 | * pair to mark boundaries 9 | */ 10 | 11 | :- dynamic running/2. % C-N, StartContextTime 12 | 13 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 14 | :- use_module(simgen(clocks), [get_clock/2]). 15 | 16 | :-listen(simulation_starting, reset). 17 | 18 | reset :- 19 | retractall(running(_, _)). 20 | 21 | :- multifile bt_impl:make_cn_impl/3. 22 | 23 | bt_impl:make_cn_impl(pin , C-N, _-_) :- 24 | running(C-N, _), 25 | !. 26 | bt_impl:make_cn_impl(pin, C-N, CParent-NParent) :- 27 | get_clock(C, Time), 28 | asserta(running(C-N, Time)), 29 | listen(C-N, terminate(C-N), pin_decorator:terminate(C-N)), 30 | listen(C-N, terminate_if_child(CParent-NParent), 31 | pin_decorator:terminate(C-N)), 32 | emit(starting(C-N)), 33 | start_child(C-N). 34 | 35 | % start the only child of C-N, which is a pin decorator 36 | start_child(C-N) :- 37 | bt_impl:node_(_M, N, pin, _Args, [Kiddo]), 38 | make_cn(C-Kiddo, C-N), 39 | % stop the parent if the child stops 40 | listen(C-N, stopped(C-Kiddo, Reason), 41 | stop_me(C-N, Reason)). 42 | 43 | stop_me(C-N, fail) :- 44 | unlisten(C-N, _, _), 45 | running(C-N, Time), 46 | emit(pin_drop(C, Time, event)), 47 | get_clock(C, Now), 48 | emit(pin_drop(C, Now, -event)), 49 | retractall(running(C-N, _)), 50 | emit(stopped(C-N, done)). 51 | stop_me(C-N, done) :- 52 | unlisten(C-N, _, _), 53 | retractall(running(C-N, _)), 54 | emit(stopped(C-N, done)). 55 | 56 | terminate(C-N) :- 57 | unlisten(C-N, _, _), 58 | emit(terminate_if_child(C-N)), 59 | retractall(running(C-N, _)), 60 | emit(stopped(C-N, terminated)). 61 | 62 | -------------------------------------------------------------------------------- /docs/simgenalgorithm.txt: -------------------------------------------------------------------------------- 1 | proper context 2 | proper clock 3 | 4 | Tasks: 5 | write do_cycles 6 | make start_node cooperate 7 | make rest of API work 8 | make independent clocks work 9 | make contexts work 10 | do lots of testing 11 | 12 | 13 | 14 | if a node runs children, it has to do the same. 15 | 16 | Decide on the semantics of termination. 17 | termination never happens 'during' a tick 18 | 19 | termination, ending, and start? 20 | 21 | A cycle 22 | 0. send the tick_start message 23 | 1. Remove all items to be terminated, calling terminate on each. 24 | 2. Start all items scheduled to be started. If there is a scheduled item already running, ignore the restart. 25 | for each context: 26 | 2.1, send the tick message 27 | 2.2. Run tasks 28 | 3. If there are frozen flow nodes, print an error message and halt the simulation. 29 | 4. increment the time for all clocks 30 | 31 | to run ticks: 32 | with all running context-node pairs 33 | run the node. 34 | if you get a continuation back with a ball of form keep_running(Context, Node) 35 | shove the continuation on the list 36 | if you get a continuation back with a ball of form terminate(Context, Node) 37 | eliminate this node from the run list, then recursively call the continuation. 38 | % have to be explicit about context, it could change below 39 | if you get a continuation back with set_dynamic(Context, VarName, Value) set 40 | the value, check for newly runnable flow nodes, run them, and remove from frozen flow notdes, and run the continuation. 41 | if you get a continuation back with get_dynamic(Context, VarName, Value) and 42 | you have a value, bind Value to it and call the continuation 43 | if you get a continuation back with get_dynamic and don't have the value, put it 44 | on the list of non-runnable flow nodes 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /simgen/nodes/random_selector.pl: -------------------------------------------------------------------------------- 1 | :- module(random_selector, []). 2 | 3 | :- dynamic running/1. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl(~? , C-N, _-_) :- 16 | running(C-N), 17 | !. 18 | bt_impl:make_cn_impl(~?, C-N, CParent-NParent) :- 19 | asserta(running(C-N)), 20 | listen(C-N, terminate(C-N), random_selector:terminate(C-N)), 21 | listen(C-N, terminate_if_child(CParent-NParent), 22 | random_selector:terminate(C-N)), 23 | emit(starting(C-N)), 24 | start_a_child(C-N). 25 | 26 | start_a_child(C-N) :- 27 | bt_impl:node_(_M, N, ~?, Args, Kiddos), 28 | sum_list(Args, Total), 29 | Select is random_float * Total, 30 | run_random(C-N, Select, Args, Kiddos). 31 | 32 | run_random(C-N, _Select, _, [Child]) :- 33 | start_this(C-Child, C-N). 34 | run_random(C-N, Select, [A |_], [Child | _]) :- 35 | Select < A, 36 | start_this(C-Child, C-N). 37 | run_random(C-N, Select, [A |T], [_ | Kids]) :- 38 | Select >= A, 39 | NS is Select - A, 40 | run_random(C-N, NS, T, Kids). 41 | run_random(C-N, _, [], _) :- 42 | bt_print_message(warning, bt_nonfatal_error(node_error(no_child_to_run), culprit(C-N))). 43 | 44 | start_this(C-ToStart, C-Parent) :- 45 | % start one of the children 46 | make_cn(C-ToStart, C-Parent), 47 | % stop the parent if the child stops 48 | listen(C-Parent, stopped(C-ToStart, Reason), 49 | stop_me(C-Parent, Reason)). 50 | 51 | stop_me(C-N, How) :- 52 | unlisten(C-N, _, _), 53 | retractall(running(C-N)), 54 | emit(stopped(C-N, How)). 55 | 56 | terminate(C-N) :- 57 | unlisten(C-N, _, _), 58 | emit(terminate_if_child(C-N)), 59 | retractall(running(C-N)), 60 | emit(stopped(C-N, terminated)). 61 | 62 | -------------------------------------------------------------------------------- /oldstuff/piddle.pl: -------------------------------------------------------------------------------- 1 | /* background program, defines a quasiquoter for piddle */ 2 | 3 | :- use_module(library(quasi_quotations)). 4 | :- use_module(library(dcg/basics)). 5 | 6 | :- quasi_quotation_syntax(piddle). 7 | 8 | piddle(Content, _SyntaxArgs, _VariableNames, Result) :- 9 | phrase_from_quasi_quotation(piddle_dcg(Result), Content). 10 | 11 | piddle_dcg(true) --> eos. 12 | piddle_dcg(define_piddle(Piddle)) --> piddle_(Piddle). 13 | 14 | piddle_([]) --> eos. 15 | piddle_([PiddleStatement | Piddle]) --> 16 | ws, 17 | piddle_statement(PiddleStatement), 18 | ws, 19 | piddle_(Piddle), 20 | ws. 21 | 22 | % define the exclusive, complete list 23 | % if any of the items are added, the others 24 | % in the set are removed. 25 | % If none occur, the first is added 26 | % 27 | piddle_statement(ec(TL)) --> 28 | ws, 29 | ( "exclusive" ; "ex" ), 30 | ws, 31 | ( "complete" ; "c" ), 32 | ws, 33 | term_list(TL), 34 | !. 35 | 36 | % define the exclusive list 37 | % if any of the items are added, the others 38 | % in the set are removed 39 | % 40 | piddle_statement(en(TL)) --> 41 | ws, 42 | ( "exclusive" ; "ex" ), 43 | ws, 44 | term_list(TL), 45 | !. 46 | 47 | term_list([Term | T]) --> 48 | term(Term), 49 | ws, 50 | ",", 51 | ws, 52 | term_list(T). 53 | term_list([T]) --> 54 | term(T), 55 | ws, 56 | ";". 57 | 58 | ws --> blanks. 59 | 60 | 61 | term(T) --> 62 | [X], 63 | { code_type(X, alpha) }, 64 | rest_term([X], T). 65 | 66 | rest_term(RevCodes, T) --> 67 | [X], 68 | { code_type(X, alpha); X == 0'_ ; code_type(X, digit) }, 69 | rest_term([X | RevCodes], T). 70 | rest_term(RevCodes, T) --> 71 | [], 72 | { reverse(RevCodes, Codes), 73 | atom_codes(T, Codes) 74 | }. 75 | 76 | test_piddle :- 77 | phrase(piddle_dcg(X), ` 78 | ex c foo, bar; 79 | `), 80 | writeq(X),nl. 81 | -------------------------------------------------------------------------------- /simgen/print_system.pl: -------------------------------------------------------------------------------- 1 | :- module(print_system, [ 2 | bt_print_message/2, 3 | bt_print_message/1, 4 | bt_debug/1, 5 | bt_nodebug/1, 6 | bt_debug/3 7 | ]). 8 | /** Utilities for assisting printing 9 | * 10 | */ 11 | 12 | :- use_module(simgen(bt_impl), [named_parent_of/2]). 13 | 14 | bt_debug(Sig) :- 15 | debug(Sig). 16 | 17 | bt_nodebug(Sig) :- 18 | nodebug(Sig). 19 | 20 | bt_print_message(Message) :- 21 | member(Message, 22 | [bt_nonfatal_error, bt_error]), 23 | bt_print_message(error, Message). 24 | bt_print_message(Type, Message) :- 25 | print_message(Type, Message), 26 | ( Type == error 27 | -> gtrace 28 | ; true 29 | ). 30 | 31 | bt_debug(Sig, Format, Args) :- 32 | Sig = error(_, _), 33 | print_message(error, bt_error(Sig, Format, Args)), 34 | debug(Sig, Format, Args). 35 | bt_debug(Sig, Format, Args) :- 36 | Sig \= error(_, _), 37 | debug(Sig, Format, Args). 38 | 39 | :- ensure_loaded(library(debug)). 40 | 41 | :- multifile 42 | system:goal_expansion/2. 43 | 44 | system:goal_expansion(bt_debug(Topic,_,_), true) :- 45 | ( prolog_debug:optimise_debug 46 | -> true 47 | ; prolog_debug:debug_topic(Topic), 48 | fail 49 | ). 50 | 51 | 52 | :- multifile prolog:message//1. 53 | 54 | prolog:message(bt_error(Sig, Format, Args)) --> 55 | ['BT Error: ', '~q'-[Sig], ', ', Format-Args, nl]. 56 | prolog:message(bt_nonfatal_error(node_error(no_child_to_run), culprit(_C-N))) --> 57 | ['BT Error: ', 'Node ~w has no child to run'-[N], nl]. 58 | % bt_nonfatal_error(node_error(no_child_to_run), culprit(C-N)) 59 | prolog:message(error(syntax_error(AC), context(err(AC:Line:Pos:File)))) --> 60 | ['BT File Error: ', 61 | 'Syntax error on line ~w:~w of ~w'-[Line,Pos,File], 62 | nl, 63 | AC, 64 | nl]. 65 | prolog:message(error(existance_error(procedure, Node), context(node:Head, Msg))) --> 66 | { findall(Parent, named_parent_of(Parent, Head), RawParents), 67 | sort(RawParents, Parents)}, 68 | [ 'BT File Error: ', 69 | 'Node ~w is used in ~w but is not defined'-[Node, Parents], 70 | nl, 71 | Msg, 72 | nl]. 73 | 74 | 75 | 76 | % TODO consider using term expansion on this at some point to remove the 77 | % bt_ versions 78 | -------------------------------------------------------------------------------- /simgen/nodes/sequence.pl: -------------------------------------------------------------------------------- 1 | :- module(sequence, []). 2 | 3 | :- dynamic running/2. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_, _)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl('->' , C-N, _-_) :- 16 | running(C-N, _), 17 | !, 18 | bt_debug(bt(sequence, make_cn_impl), 19 | '~w-~w already running', [C,N]). 20 | bt_impl:make_cn_impl('->', C-N, CParent-NParent) :- 21 | bt_impl:node_(_M, N, '->', _A, [FirstChild | RestOfChildren]), 22 | emit(starting(C-N)), 23 | asserta(running(C-N, RestOfChildren)), 24 | listen(C-N, terminate(C-N), sequence:terminate(C-N)), 25 | listen(C-N, terminate_if_child(CParent-NParent), 26 | sequence:terminate(C-N)), 27 | bt_debug(bt(sequence, make_cn_impl), 28 | 'Starting sequence ~w-~w and child ~w', 29 | [C,N, FirstChild]), 30 | start_a_child(C-N, FirstChild). 31 | 32 | start_a_child(C-N, Child) :- 33 | make_cn(C-Child, C-N), 34 | % stop the parent if the child stops 35 | listen(C-N, stopped(C-Child, Reason), 36 | next_child(C-N, Reason)). 37 | 38 | next_child(C-N, terminated) :- 39 | bt_debug(bt(sequence, terminated), '~w-~w terminated', [C,N]). 40 | next_child(C-N, fail) :- 41 | bt_debug(bt(sequence, next_child), 42 | '~w-~w says current child failed', 43 | [C,N]), 44 | unlisten(C-N, _, _), 45 | retractall(running(C-N, _)), 46 | emit(stopped(C-N, fail)). 47 | next_child(C-N, done) :- 48 | running(C-N, []), 49 | bt_debug(bt(sequence, next_child), 50 | '~w-~w says current child done with no more children', 51 | [C,N]), 52 | unlisten(C-N, _, _), 53 | retractall(running(C-N, _)), 54 | emit(stopped(C-N, done)). 55 | next_child(C-N, done) :- 56 | running(C-N, [H | T]), 57 | bt_debug(bt(sequence, next_child), 58 | '~w-~w says current child done, will run ~w', 59 | [C,N,H]), 60 | retractall(running(C-N, _)), 61 | asserta(running(C-N, T)), 62 | make_cn(C-H, C-N), 63 | unlisten(C-N, stopped(_, _)), 64 | listen(C-N, stopped(C-H, Reason), next_child(C-N, Reason)). 65 | 66 | terminate(C-N) :- 67 | bt_debug(bt(sequence, terminated), 68 | '~w-~w is being terminated', 69 | [C,N]), 70 | unlisten(C-N, _, _), 71 | emit(terminate_if_child(C-N)), 72 | retractall(running(C-N, _)), 73 | emit(stopped(C-N, terminated)). 74 | 75 | -------------------------------------------------------------------------------- /simgen/nodes/attempt.pl: -------------------------------------------------------------------------------- 1 | :- module(attempt, []). 2 | 3 | :- dynamic running/2. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_, _)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl(attempt , C-N, _-_) :- 16 | running(C-N, _), 17 | !, 18 | bt_debug(bt(attempt, make_cn_impl), 19 | '~w-~w already running', [C,N]). 20 | bt_impl:make_cn_impl(attempt, C-N, CParent-NParent) :- 21 | bt_impl:node_(_M, N, attempt, _A, [FirstChild | RestOfChildren]), 22 | emit(starting(C-N)), 23 | asserta(running(C-N, RestOfChildren)), 24 | listen(C-N, terminate(C-N), attempt:terminate(C-N)), 25 | listen(C-N, terminate_if_child(CParent-NParent), 26 | attempt:terminate(C-N)), 27 | bt_debug(bt(attempt, make_cn_impl), 28 | 'Starting attempt ~w-~w and child ~w', 29 | [C,N, FirstChild]), 30 | start_a_child(C-N, FirstChild). 31 | 32 | start_a_child(C-N, Child) :- 33 | make_cn(C-Child, C-N), 34 | % stop the parent if the child stops 35 | listen(C-N, stopped(C-Child, Reason), 36 | next_child(C-N, Reason)). 37 | 38 | next_child(C-N, terminated) :- 39 | bt_debug(bt(attempt, terminated), '~w-~w terminated', [C,N]). 40 | next_child(C-N, done) :- 41 | running(C-N, _), 42 | bt_debug(bt(attempt, next_child), 43 | '~w-~w says current child done, we stop', 44 | [C,N]), 45 | unlisten(C-N, _, _), 46 | retractall(running(C-N, _)), 47 | emit(stopped(C-N, done)). 48 | next_child(C-N, fail) :- 49 | running(C-N, [H | T]), 50 | bt_debug(bt(attempt, next_child), 51 | '~w-~w says current child failed, will run ~w', 52 | [C,N,H]), 53 | retractall(running(C-N, _)), 54 | asserta(running(C-N, T)), 55 | make_cn(C-H, C-N), 56 | unlisten(C-N, stopped(_, _)), 57 | listen(C-N, stopped(C-H, Reason), next_child(C-N, Reason)). 58 | next_child(C-N, fail) :- 59 | running(C-N, []), 60 | bt_debug(bt(attempt, next_child), 61 | '~w-~w says current child failed, no more to try', 62 | [C,N]), 63 | unlisten(C-N, _, _), 64 | retractall(running(C-N, _)), 65 | emit(stopped(C-N, fail)). 66 | 67 | terminate(C-N) :- 68 | bt_debug(bt(attempt, terminated), 69 | '~w-~w is being terminated', 70 | [C,N]), 71 | unlisten(C-N, _, _), 72 | emit(terminate_if_child(C-N)), 73 | retractall(running(C-N, _)), 74 | emit(stopped(C-N, terminated)). 75 | 76 | -------------------------------------------------------------------------------- /simgen/nodes/parallel.pl: -------------------------------------------------------------------------------- 1 | :- module(parallel, []). 2 | 3 | :- dynamic running/2. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_, _)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl('=>' , C-N, _-_) :- 16 | running(C-N, _), 17 | !, 18 | bt_debug(bt(parallel, make_cn_impl), 19 | '~w-~w already running', [C,N]). 20 | bt_impl:make_cn_impl('=>', C-N, CParent-NParent) :- 21 | bt_impl:node_(_M, N, '=>', _A, Children), 22 | emit(starting(C-N)), 23 | asserta(running(C-N, Children)), 24 | listen(C-N, terminate(C-N), parallel:terminate(C-N)), 25 | listen(C-N, terminate_if_child(CParent-NParent), 26 | parallel:terminate(C-N)), 27 | bt_debug(bt(parallel, make_cn_impl), 28 | 'Starting parallel ~w-~w and children ~w', 29 | [C,N, Children]), 30 | start_children(C-N, Children). 31 | 32 | start_children(_, []). 33 | start_children(C-N, [Child | Rest]) :- 34 | make_cn(C-Child, C-N), 35 | % stop the parent if the child stops 36 | listen(C-N, stopped(C-Child, fail), child_failed(C-N, C-Child)), 37 | listen(C-N, stopped(C-Child, done), child_done(C-N, C-Child)), 38 | start_children(C-N, Rest). 39 | 40 | 41 | %! child_failed(CNPair:cnpair, How:atom) is det 42 | % 43 | % respond to a child failing by terminating others and reporting 44 | % fail 45 | % 46 | child_failed(C-N, C-Child) :- 47 | emit(terminate_if_child(C-N)), 48 | bt_debug(bt(parallel, fail), '~w-~w: child ~w-~w failed, failing', 49 | [C,N,C,Child]), 50 | unlisten(C-N, _, _), 51 | retractall(running(C-N, _)), 52 | emit(stopped(C-N, fail)). 53 | 54 | child_done(C-N, C-Child) :- 55 | bt_debug(bt(parallel, child_done), 56 | '~w-~w: child ~w-~w done', 57 | [C,N,C,Child]), 58 | running(C-N, RunningChildren), 59 | select(Child, RunningChildren, NewRunningChildren), 60 | ( NewRunningChildren = [] 61 | -> 62 | unlisten(C-N, _, _), 63 | retractall(running(C-N, _)), 64 | emit(stopped(C-N, done)) 65 | ; 66 | retractall(running(C-N, _)), 67 | asserta(running(C-N, NewRunningChildren)) 68 | ). 69 | 70 | terminate(C-N) :- 71 | bt_debug(bt(sequence, terminated), 72 | '~w-~w is being terminated', 73 | [C,N]), 74 | unlisten(C-N, _, _), 75 | emit(terminate_if_child(C-N)), 76 | retractall(running(C-N, _)), 77 | emit(stopped(C-N, terminated)). 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /simgen/nodes/paraselect.pl: -------------------------------------------------------------------------------- 1 | :- module(paraselect, []). 2 | 3 | :- dynamic running/2. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_, _)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl('=?' , C-N, _-_) :- 16 | running(C-N, _), 17 | !, 18 | bt_debug(bt(paraselect, make_cn_impl), 19 | '~w-~w already running', [C,N]). 20 | bt_impl:make_cn_impl('=?', C-N, CParent-NParent) :- 21 | bt_impl:node_(_M, N, '=?', _A, Children), 22 | emit(starting(C-N)), 23 | asserta(running(C-N, Children)), 24 | listen(C-N, terminate(C-N), paraselect:terminate(C-N)), 25 | listen(C-N, terminate_if_child(CParent-NParent), 26 | paraselect:terminate(C-N)), 27 | bt_debug(bt(paraselect, make_cn_impl), 28 | 'Starting paraselect ~w-~w and children ~w', 29 | [C,N, Children]), 30 | start_children(C-N, Children). 31 | 32 | start_children(_, []). 33 | start_children(C-N, [Child | Rest]) :- 34 | make_cn(C-Child, C-N), 35 | % stop the parent if the child stops 36 | listen(C-N, stopped(C-Child, fail), child_failed(C-N, C-Child)), 37 | listen(C-N, stopped(C-Child, done), child_done(C-N, C-Child)), 38 | start_children(C-N, Rest). 39 | 40 | 41 | %! child_failed(CNPair:cnpair, How:atom) is det 42 | % 43 | % respond to a child failing by terminating others and reporting 44 | % fail 45 | % 46 | child_done(C-N, C-Child) :- 47 | emit(terminate_if_child(C-N)), 48 | bt_debug(bt(paraselect, done), '~w-~w: child ~w-~w done, succeeding', 49 | [C,N,C,Child]), 50 | unlisten(C-N, _, _), 51 | retractall(running(C-N, _)), 52 | emit(stopped(C-N, done)). 53 | 54 | child_failed(C-N, C-Child) :- 55 | bt_debug(bt(paraselect, child_done), 56 | '~w-~w: child ~w-~w failed', 57 | [C,N,C,Child]), 58 | running(C-N, RunningChildren), 59 | select(Child, RunningChildren, NewRunningChildren), 60 | ( NewRunningChildren = [] 61 | -> 62 | unlisten(C-N, _, _), 63 | retractall(running(C-N, _)), 64 | emit(stopped(C-N, fail)) 65 | ; 66 | retractall(running(C-N, _)), 67 | asserta(running(C-N, NewRunningChildren)) 68 | ). 69 | 70 | terminate(C-N) :- 71 | bt_debug(bt(sequence, terminated), 72 | '~w-~w is being terminated', 73 | [C,N]), 74 | unlisten(C-N, _, _), 75 | emit(terminate_if_child(C-N)), 76 | retractall(running(C-N, _)), 77 | emit(stopped(C-N, terminated)). 78 | 79 | -------------------------------------------------------------------------------- /simgen/nodes/random_sequence.pl: -------------------------------------------------------------------------------- 1 | :- module(random_sequence, []). 2 | 3 | :- dynamic running/2. 4 | 5 | :- use_module(simgen(bt_impl), [make_cn/2, emit/1]). 6 | :- use_module(simgen(print_system)). 7 | 8 | :-listen(simulation_starting, reset). 9 | 10 | reset :- 11 | retractall(running(_, _)). 12 | 13 | :- multifile bt_impl:make_cn_impl/3. 14 | 15 | bt_impl:make_cn_impl('~>' , C-N, _-_) :- 16 | running(C-N, _), 17 | !, 18 | bt_debug(bt(random_sequence, make_cn_impl), 19 | '~w-~w already running', [C,N]). 20 | bt_impl:make_cn_impl('~>', C-N, CParent-NParent) :- 21 | bt_impl:node_(_M, N, '~>', _A, OriginalChildren), 22 | random_permutation(OriginalChildren, [FirstChild | RestOfChildren]), 23 | emit(starting(C-N)), 24 | asserta(running(C-N, RestOfChildren)), 25 | listen(C-N, terminate(C-N), random_sequence:terminate(C-N)), 26 | listen(C-N, terminate_if_child(CParent-NParent), 27 | random_sequence:terminate(C-N)), 28 | bt_debug(bt(random_sequence, make_cn_impl), 29 | 'Starting random_sequence ~w-~w and child ~w', 30 | [C,N, FirstChild]), 31 | start_a_child(C-N, FirstChild). 32 | 33 | start_a_child(C-N, Child) :- 34 | make_cn(C-Child, C-N), 35 | % stop the parent if the child stops 36 | listen(C-N, stopped(C-Child, Reason), 37 | next_child(C-N, Reason)). 38 | 39 | next_child(C-N, terminated) :- 40 | bt_debug(bt(random_sequence, terminated), '~w-~w terminated', [C,N]). 41 | next_child(C-N, fail) :- 42 | bt_debug(bt(random_sequence, next_child), 43 | '~w-~w says current child failed', 44 | [C,N]), 45 | unlisten(C-N, _, _), 46 | retractall(running(C-N, _)), 47 | emit(stopped(C-N, fail)). 48 | next_child(C-N, done) :- 49 | running(C-N, []), 50 | bt_debug(bt(random_sequence, next_child), 51 | '~w-~w says current child done with no more children', 52 | [C,N]), 53 | unlisten(C-N, _, _), 54 | retractall(running(C-N, _)), 55 | emit(stopped(C-N, done)). 56 | next_child(C-N, done) :- 57 | running(C-N, [H | T]), 58 | bt_debug(bt(random_sequence, next_child), 59 | '~w-~w says current child done, will run ~w', 60 | [C,N,H]), 61 | retractall(running(C-N, _)), 62 | asserta(running(C-N, T)), 63 | make_cn(C-H, C-N), 64 | unlisten(C-N, stopped(_, _)), 65 | listen(C-N, stopped(C-H, Reason), next_child(C-N, Reason)). 66 | 67 | terminate(C-N) :- 68 | bt_debug(bt(random_sequence, terminated), 69 | '~w-~w is being terminated', 70 | [C,N]), 71 | unlisten(C-N, _, _), 72 | emit(terminate_if_child(C-N)), 73 | retractall(running(C-N, _)), 74 | emit(stopped(C-N, terminated)). 75 | 76 | -------------------------------------------------------------------------------- /makedata.pl: -------------------------------------------------------------------------------- 1 | :- module(bt_example, [test/2]). 2 | /** Example that runs the test simulation 3 | 4 | 5 | */ 6 | 7 | path_target. 8 | % This is required, needed for internal glue. 9 | % fix it so it points at your simgen directory 10 | % 11 | :- 12 | source_file(bt_example:path_target, Path), 13 | directory_file_path(Dir, _, Path), 14 | absolute_file_name(Dir, AbsPath), 15 | atomic_list_concat( 16 | [ 17 | AbsPath, 18 | '/simgen'], SGP), 19 | asserta(user:file_search_path(simgen, SGP)), 20 | atomic_list_concat( 21 | [ 22 | AbsPath, 23 | '/examples'], SGPE), 24 | asserta(user:file_search_path(examples, SGPE)). 25 | 26 | % Grab SimGen 27 | :- use_module(simgen(simgen)). 28 | 29 | %! test(+File:atom, +N:integer) is nondet 30 | % 31 | % run a bt file, making N contexts 32 | % with root root. File is the file 33 | % 34 | % The file name is like consult, you don't need the .bt 35 | % extension 36 | % 37 | test(File, N) :- 38 | use_bt(File), 39 | setup_call_cleanup( 40 | open('tests.simai', write, Stream), 41 | ( b_setval(test_stream, Stream), 42 | b_setval(test_root, root), 43 | !, % sanity measure, 44 | start_simulation( 45 | 0, % the start time, in 'our' units 46 | 1_000_000_000, % how long our units are in nanos 47 | 1, % how long a tick is in our units 48 | N) % our extern is just how many contexts to make 49 | ), 50 | close(Stream) 51 | ). 52 | 53 | % we register to listen for ticks 54 | % and add a new context randomly every 1 to 20 ticks 55 | % we number them going down 56 | % when we are asked to make context 0, we instead 57 | % end the simulation 58 | :- listen(tick(Extern, Tick, NewExtern), 59 | consider_adding_context(Extern, Tick, NewExtern)). 60 | 61 | consider_adding_context(Extern, 0, Extern) :- 62 | add_contexts(Extern). 63 | consider_adding_context(Extern, 3600, Extern) :- % # of ticks 64 | !, 65 | end_simulation. 66 | consider_adding_context(Extern, _, Extern). % # of ticks 67 | 68 | add_contexts(0). 69 | add_contexts(N) :- 70 | start_context(root, N, 0), 71 | succ(NN, N), 72 | add_contexts(NN). 73 | 74 | % Now we handle reading, starting, and stopped events 75 | % by writing lines to the csv file 76 | 77 | % listen for reading/5 events and write reading events to csv 78 | :- listen(reading(Time, _, Context, Type, Value), 79 | write_event(reading, Time, Context, Type, Value)). 80 | 81 | write_event(Class, Time, Context, Type, Value) :- 82 | Nanos is Time * 60_000_000_000, 83 | b_getval(test_stream, Stream), 84 | format(Stream, 'unit,~d,~d,~w,~w,~w~n', 85 | [Context, Nanos, Class, Type, Value]), 86 | flush_output(Stream). 87 | 88 | -------------------------------------------------------------------------------- /examples/anontest.bt: -------------------------------------------------------------------------------- 1 | /* 2 | Example that generates 50:50 mix of type a and b, 3 | type b steadily increase after start, and both move with p and t 4 | */ 5 | % the root node 6 | root ~? % a probabilistic selector 7 | type_a, 8 | type_b. 9 | 10 | /* 11 | An action node. 12 | Everything up to the ; is done on tick 1, 13 | everythign after on each subsequent tick 14 | */ 15 | type_a ! 16 | p = 100, /* used = because we don't have a previous tick */ 17 | t = 100 18 | ; 19 | p := levy_flight(p, 0, 200), 20 | t := wander(t, 0, 200, 3), 21 | speed = 500 - p - t 22 | ; 23 | p > 20, 24 | t > 20, 25 | p < 180, 26 | t < 180, 27 | clock() < 25. 28 | 29 | type_b ! 30 | p = 100, 31 | t = 100 32 | ; 33 | p := levy_flight(p, 0, 200), % end of line comment 34 | t := wander(t, 0, 200, /* stuff in here */ 3), 35 | speed = 500 + clock() - p - t 36 | ; 37 | p > 80, 38 | t > 80, 39 | p < 120, 40 | t < 120, 41 | clock() < 25. 42 | 43 | /* 44 | Test of everything implemented so far 45 | */ 46 | 47 | testall -> 48 | try_test_op_priority, 49 | try_test_immediate_fail_pdq, 50 | try_test_levy_flight, 51 | try_test_wander, 52 | try_test_subtract_negate, 53 | try_test_boundary, 54 | try_test_all_operators, 55 | try_test_various_assignment. 56 | 57 | try_test_op_priority try test_op_priority. 58 | test_op_priority ! 59 | x = 0 60 | ; 61 | x := x + 2 + 2 * 3 62 | ; 63 | x < 50. 64 | 65 | % we don't fail on first tick 66 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 67 | test_immediate_fail_pdq ! 68 | xa = 10 69 | ; 70 | xa := xa + 1 71 | ; 72 | xa < 5 73 | . 74 | 75 | try_test_levy_flight try test_levy_flight. 76 | test_levy_flight ! 77 | xb = 0 78 | ; 79 | xb := levy_flight(xb, -100, 100) 80 | ; 81 | xb > -80, 82 | xb < 80 83 | . 84 | 85 | try_test_wander try test_wander. 86 | test_wander ! 87 | ; 88 | xb := wander(xb, -100, 100, 5) 89 | ; 90 | xb > -50, 91 | xb < 50 92 | . 93 | 94 | try_test_subtract_negate try test_subtract_negate. 95 | test_subtract_negate ! 96 | y = 0 97 | ; 98 | y := y - -3 99 | ; 100 | y < -6, 101 | y > 9 102 | . 103 | 104 | try_test_boundary try test_boundary. 105 | test_boundary ! 106 | ; 107 | y := y + 20.3 108 | ; 109 | y < 30.7 110 | . 111 | 112 | try_test_all_operators try test_all_operators. 113 | test_all_operators ! 114 | z = 1 + 2, 115 | w = 1 * 2, 116 | x = 1 - 2, 117 | yy = 1 / 2, 118 | t = 0 119 | ; 120 | x := 1 + (1/x), 121 | t := t + 1 122 | ; 123 | x < 5, 124 | x > -5, 125 | t > 12 126 | . 127 | 128 | try_test_various_assignment try test_various_assignment. 129 | test_various_assignment ! 130 | z := z + 10, 131 | x = 5, 132 | y = clock() 133 | ; 134 | z := z - 1, 135 | x = z 136 | ; 137 | x < 5 138 | . 139 | 140 | -------------------------------------------------------------------------------- /examples/hwclient2.bt: -------------------------------------------------------------------------------- 1 | /* 2 | Example that generates 50:50 mix of type a and b, 3 | type b steadily increase after start, and both move with p and t 4 | */ 5 | % the root node 6 | root ~? % a probabilistic selector 7 | type_a, 8 | type_b. 9 | 10 | /* 11 | An action node. 12 | Everything up to the ; is done on tick 1, 13 | everythign after on each subsequent tick 14 | */ 15 | type_a ! 16 | p = 100, /* used = because we don't have a previous tick */ 17 | t = 100 18 | ; 19 | p := levy_flight(p, 0, 200), 20 | t := wander(t, 0, 200, 3), 21 | speed = 500 - p - t 22 | ; 23 | p > 20, 24 | t > 20, 25 | p < 180, 26 | t < 180, 27 | clock() < 25. 28 | 29 | type_b ! 30 | p = 100, 31 | t = 100 32 | ; 33 | p := levy_flight(p, 0, 200), % end of line comment 34 | t := wander(t, 0, 200, /* stuff in here */ 3), 35 | speed = 500 + clock() - p - t 36 | ; 37 | p > 80, 38 | t > 80, 39 | p < 120, 40 | t < 120, 41 | clock() < 25. 42 | 43 | /* 44 | Test of everything implemented so far 45 | */ 46 | 47 | testall -> 48 | try_test_op_priority, 49 | try_test_immediate_fail_pdq, 50 | try_test_levy_flight, 51 | try_test_wander, 52 | try_test_subtract_negate, 53 | try_test_boundary, 54 | try_test_all_operators, 55 | try_test_various_assignment. 56 | 57 | try_test_op_priority try test_op_priority. 58 | test_op_priority ! 59 | x = 0 60 | ; 61 | x := x + 2 + 2 * 3 62 | ; 63 | x < 50. 64 | 65 | % we don't fail on first tick 66 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 67 | test_immediate_fail_pdq ! 68 | xa = 10 69 | ; 70 | xa := xa + 1 71 | ; 72 | xa < 5 73 | . 74 | 75 | try_test_levy_flight try test_levy_flight. 76 | test_levy_flight ! 77 | xb = 0 78 | ; 79 | xb := levy_flight(xb, -100, 100) 80 | ; 81 | xb > -80, 82 | xb < 80 83 | . 84 | 85 | try_test_wander try test_wander. 86 | test_wander ! 87 | ; 88 | xb := wander(xb, -100, 100, 5) 89 | ; 90 | xb > -50, 91 | xb < 50 92 | . 93 | 94 | try_test_subtract_negate try test_subtract_negate. 95 | test_subtract_negate ! 96 | y = 0 97 | ; 98 | y := y - -3 99 | ; 100 | y < -6, 101 | y > 9 102 | . 103 | 104 | try_test_boundary try test_boundary. 105 | test_boundary ! 106 | ; 107 | y := y + 20.3 108 | ; 109 | y < 30.7 110 | . 111 | 112 | try_test_all_operators try test_all_operators. 113 | test_all_operators ! 114 | z = 1 + 2, 115 | w = 1 * 2, 116 | x = 1 - 2, 117 | yy = 1 / 2, 118 | t = 0 119 | ; 120 | x := 1 + (1/x), 121 | t := t + 1 122 | ; 123 | x < 5, 124 | x > -5, 125 | t > 12 126 | . 127 | 128 | try_test_various_assignment try test_various_assignment. 129 | test_various_assignment ! 130 | z := z + 10, 131 | x = 5, 132 | y = clock() 133 | ; 134 | z := z - 1, 135 | x = z 136 | ; 137 | x < 5 138 | . 139 | 140 | -------------------------------------------------------------------------------- /examples/ht3.bt: -------------------------------------------------------------------------------- 1 | /* 2 | Example that generates 50:50 mix of type a and b, 3 | type b steadily increase after start, and both move with p and t 4 | */ 5 | % the root node 6 | root ~? % a probabilistic selector 7 | type_a, 8 | type_b. 9 | 10 | /* 11 | An action node. 12 | Everything up to the ; is done on tick 1, 13 | everythign after on each subsequent tick 14 | */ 15 | type_a ! 16 | p = 100, /* used = because we don't have a previous tick */ 17 | t = 100 18 | ; 19 | p := levy_flight(p, 0, 200), 20 | t := wander(t, 0, 200, 3), 21 | speed = 500 - p - t 22 | ; 23 | p > 20, 24 | t > 20, 25 | p < 180, 26 | t < 180, 27 | clock() < 25. 28 | 29 | type_b ! 30 | p = 100, 31 | t = 100 32 | ; 33 | p := levy_flight(p, 0, 200), % end of line comment 34 | t := wander(t, 0, 200, /* stuff in here */ 3), 35 | speed = 500 + clock() - p - t 36 | ; 37 | p > 80, 38 | t > 80, 39 | p < 120, 40 | t < 120, 41 | clock() < 25. 42 | 43 | /* 44 | Test of everything implemented so far 45 | */ 46 | 47 | testall -> 48 | try_test_op_priority, 49 | try_test_immediate_fail_pdq, 50 | try_test_levy_flight, 51 | try_test_wander, 52 | try_test_subtract_negate, 53 | try_test_boundary, 54 | try_test_all_operators, 55 | try_test_various_assignment. 56 | 57 | try_test_op_priority try test_op_priority. 58 | test_op_priority ! 59 | x = 0 60 | ; 61 | x := x + 2 + 2 * 3 62 | ; 63 | x < 50. 64 | 65 | % we don't fail on first tick 66 | try_test_immediate_fail_pdq try test_immediate_fail_pdq. 67 | test_immediate_fail_pdq ! 68 | xa = 10 69 | ; 70 | xa := xa + 1 71 | ; 72 | xa < 5 73 | . 74 | 75 | try_test_levy_flight try test_levy_flight. 76 | test_levy_flight ! 77 | xb = 0 78 | ; 79 | xb := levy_flight(xb, -100, 100) 80 | ; 81 | xb > -80, 82 | xb < 80 83 | . 84 | 85 | try_test_wander try test_wander. 86 | test_wander ! 87 | xb = 0 88 | ; 89 | xb := wander(xb, -100, 100, 5) 90 | ; 91 | xb > -80, 92 | xb < 80 93 | . 94 | 95 | try_test_subtract_negate try test_subtract_negate. 96 | test_subtract_negate ! 97 | y = 0 98 | ; 99 | y := y - -3 100 | ; 101 | y < -6, 102 | y > 9 103 | . 104 | 105 | try_test_boundary try test_boundary. 106 | test_boundary ! 107 | ; 108 | y := y + 20.3 109 | ; 110 | y < 30.7 111 | . 112 | 113 | try_test_all_operators try test_all_operators. 114 | test_all_operators ! 115 | z = 1 + 2, 116 | w = 1 * 2, 117 | x = 1 - 2, 118 | yy = 1 / 2, 119 | t = 0 120 | ; 121 | x := 1 + (1/x), 122 | t := t + 1 123 | ; 124 | x < 5, 125 | x > -5, 126 | t > 12 127 | . 128 | 129 | try_test_various_assignment try test_various_assignment. 130 | test_various_assignment ! 131 | z := z + 10, 132 | x = 5, 133 | y = clock() 134 | ; 135 | z := z - 1, 136 | x = z 137 | ; 138 | x < 5 139 | . 140 | 141 | -------------------------------------------------------------------------------- /simgen/valuator.pl: -------------------------------------------------------------------------------- 1 | :- module(valuator, [valuator/0, 2 | cycle_values/0, 3 | setval/3, 4 | broadcast_values/0, 5 | getval/3, 6 | lastval/3, 7 | ezval/3]). 8 | 9 | :- use_module(simgen(print_system)). 10 | 11 | valuator :- 12 | debug_vals('pre valuator'), 13 | valuator(20), 14 | debug_vals('post valuator'). 15 | valuator :- 16 | bt_debug(error(valuator, fails), '***** Valuator fails',[]), 17 | debug_vals('failure'), 18 | gtrace, 19 | bt_impl:end_simulation. 20 | 21 | valuator(0) :- 22 | bt_debug(error(valuator, circular), '***** Valuator cannot resolve after 20 cycles', []), 23 | bt_impl:end_simulation. 24 | valuator(N) :- 25 | N > 0, 26 | broadcast_request(more), 27 | !, 28 | broadcast(propagate), 29 | !, 30 | NN is N - 1, 31 | valuator(NN). 32 | valuator(N) :- 33 | N > 0. 34 | 35 | :- dynamic val/3, old_val/3. 36 | 37 | debug_vals(Msg) :- 38 | findall(C-Name-Val, old_val(C, Name, Val), Old), 39 | findall(CN-NameN-ValN, val(CN, NameN, ValN), New), 40 | bt_debug(bt(valuator, debug_vals), 41 | '#### ~w~nold_val: ~q~nval: ~q', [Msg, Old, New]). 42 | cycle_values :- 43 | findall(asserta(old_val(C, N, V)), val(C,N,V), List), 44 | retractall(val(_, _, _)), 45 | retractall(old_val(_, _, _)), 46 | maplist(call , List). 47 | 48 | :- use_module(clocks, [get_clock/2]). 49 | 50 | broadcast_values :- 51 | get_clock(simgen, Time), 52 | forall( 53 | val(Context, Name, Val), 54 | ( get_clock(Context, ContextTime), 55 | broadcast(reading(Time, ContextTime, Context, Name, Val)) 56 | ) 57 | ). 58 | 59 | % note that multiple sources error succeeds. 60 | setval(Context, Name, _) :- 61 | val(Context, Name, _), 62 | !, 63 | bt_debug(error(valuator, multiple_sources), 64 | 'In context ~w value ~w has multiple sources', 65 | [Context, Name]). 66 | setval(Context, Name, Val) :- 67 | bt_debug(bt(valuator, setval), '~w: ~w <- ~w', [Context, Name, Val]), 68 | asserta(val(Context, Name, Val)). 69 | 70 | getval(Context, Name, Val) :- 71 | val(Context, Name, Val), 72 | bt_debug(bt(valuator, getval), '~w: ~w returns ~w', [Context, Name, Val]). 73 | 74 | 75 | lastval(Context, Name, Val) :- 76 | old_val(Context, Name, Val), 77 | bt_debug(bt(valuator, lastval), 78 | '~w: ~w returns lastval ~w', [Context, Name, Val]). 79 | lastval(Context, Name, 0) :- 80 | bt_debug(error(valuator, no_lastval), 81 | '**** Context ~w variable ~w has no last value so cannot be evaluated', [Context, Name]), 82 | bt_impl:bad_thing_happened. 83 | % lastval questionable design decision that this doesn't also use the 84 | % current val if the previous one isn't avail 85 | 86 | 87 | ezval(Context, Name, Val) :- 88 | val(Context, Name, Val), 89 | bt_debug(bt(valuator, lastval), 90 | '~w: ~w returns ezval ~w', [Context, Name, Val]). 91 | ezval(Context, Name, '$not_avail$') :- 92 | bt_debug(bt(valuator, lastval), 93 | '~w: ~w returns ezval ~w', [Context, Name, '$not_avail$']). 94 | 95 | 96 | :-listen(simulation_starting, reset). 97 | 98 | reset :- 99 | retractall(val(_, _, _)), 100 | retractall(old_val(_, _, _)). 101 | -------------------------------------------------------------------------------- /oldstuff/state_life.pl: -------------------------------------------------------------------------------- 1 | :- module(state_life, [its_a_life/2]). 2 | /** recursive version of life 3 | 4 | */ 5 | 6 | :- use_module(library(pita)). 7 | :- use_module(library(random)). 8 | 9 | %! its_a_life(+Born:year, -History:list, -Present:list) is nondet 10 | % 11 | % Generate a life. 12 | % 13 | % 14 | its_a_life(Present) :- 15 | life_circumstances(Circumstances), 16 | live([age(-1) | Circumstances], Present). 17 | 18 | live(State, State) :- 19 | member(dead, State). 20 | live(State, Present) :- 21 | apply_transforms(State, NewState), 22 | advance_age(NewState, AAState), 23 | live(AAState, Present). 24 | 25 | advance_age(InState, [age(NA)| OutState]) :- 26 | member(age(A), InState), 27 | subtract(InState, age(_), OutState), 28 | na(A, NA). 29 | 30 | na(pre:1, 0:0). 31 | na(pre:N, pre:NN) :- 32 | N > 1, 33 | NN is N - 1. 34 | na(Y:M, Y:NM) :- 35 | M < 11, 36 | NM is M + 1. 37 | na(Y:11, NY:0) :- 38 | NY is Y + 1. 39 | 40 | apply_transforms(State, NewState) :- 41 | transforms(T), 42 | foldl(apply_transform, T, State, NewState). 43 | 44 | transforms(T) :- 45 | bagof(transform(PC, F, A, R), transform(PC, F, A, R), T). 46 | 47 | happening(0.01/mo, [age(pre:_)], [], [dead], [], ' miscarries'). 48 | happening(0.02, [age(pre:1)], [], [dead], [], ' dies in childbirth'). 49 | happening(0.01/mo, [age(pre:_)], [], [disability], [], 50 | ' difficulty in pregnancy'). 51 | 52 | 53 | 54 | 55 | random_pick(_, _, [], _) :- !, fail. 56 | random_pick(Axis, Conditions, Possible, Outcome) :- 57 | rp(1.0, Axis, Conditions, Possible, Outcome), 58 | outcome_event(Axis, Outcome, _). 59 | 60 | rp(_Prob, _Axis, _Conditions, [Remaining], Remaining). 61 | rp(Prob, Axis, Conditions, [Outcome, _More | _YetMore], Outcome) :- 62 | LPAD =.. [Axis, Conditions, Outcome], 63 | prob(LPAD, ProbThis), 64 | ConditionalProb is ProbThis / Prob, 65 | random(Roll), 66 | Roll < ConditionalProb, 67 | !. 68 | rp(Prob, Axis, Conditions, [NotThis, More | YetMore], Outcome) :- 69 | LPAD =.. [Axis, Conditions, NotThis], 70 | prob(LPAD, ProbThis), 71 | RemProb is Prob - ProbThis, 72 | rp(RemProb, Axis, Conditions, [More | YetMore], Outcome). 73 | 74 | life_circumstances([sex(Sex), parent_econ(Econ)]) :- 75 | random_pick(sex, [], [m,f], Sex), 76 | random_pick(parent_econ, [], [rich,middle,poor], Econ). 77 | 78 | /* 79 | prob(sex(_Conditions, m), 0.5). 80 | prob(sex(_, f), 0.5). 81 | prob(parent_econ(_, rich), 0.1). 82 | prob(parent_econ(_, middle), 0.6). 83 | prob(parent_econ(_, poor), 0.3). 84 | */ 85 | 86 | outcome_event(Axis, Value, object(Axis, Value)). 87 | 88 | outcome_desc(sex, m, ' is male'). 89 | outcome_desc(sex, f, ' is female'). 90 | outcome_desc(parent_econ, rich, ' has wealthy parents'). 91 | outcome_desc(parent_econ, middle, ' is born in a middle class family'). 92 | outcome_desc(parent_econ, poor, ' is born to a poor family'). 93 | 94 | % live(YearBorn, , History, Present) :- 95 | 96 | 97 | :- pita. 98 | 99 | :- begin_lpad. 100 | 101 | sex(m):1/2 ; sex(f) : 1/2. 102 | 103 | n_heads(t, 1):1/2 ; n_heads(f, 1):1/2. 104 | n_heads(t, N):1/2 * P ; n_heads(f, N):(1 - 1/2 * P) :- 105 | NN is N - 1, 106 | n_heads(t, NN). 107 | 108 | :- end_lpad. 109 | 110 | -------------------------------------------------------------------------------- /simgen/behavior_tree.pl: -------------------------------------------------------------------------------- 1 | :- module(behavior_tree, [bt/4, 2 | use_bt/1 3 | ]). 4 | :- reexport(simgen(bt_impl)). 5 | :- reexport(simgen(clocks), [get_clock/2]). 6 | /** Behavior Trees 7 | * 8 | */ 9 | :- use_module(library(quasi_quotations)). 10 | :- use_module(simgen(print_system)). 11 | :- use_module(simgen(behavior_tree_parser)). 12 | :- use_module(simgen(ast_transform)). 13 | :- quasi_quotation_syntax(behavior_tree:bt). 14 | 15 | :- dynamic define_bt/1. % do I need this? added to deal with .bt files 16 | 17 | % for debugging this writes as well, but we have to strip the define_bt 18 | system:term_expansion(define_bt(X), X) :- write('term expands to:'),portray_clause(X),nl. 19 | 20 | /******************************* 21 | * BT file read * 22 | *******************************/ 23 | 24 | :- module_transparent use_bt/1. 25 | 26 | % TODO - the below resets nodes for behavior_tree module 27 | % I need to generally clean up where nodes are asserted and 28 | % how modules work 29 | % 30 | use_bt(Path) :- 31 | % context_module(Module), 32 | reset_nodes_for_module(behavior_tree), 33 | list_all_nodes, 34 | absolute_file_name(Path, File), 35 | atom_concat(File, '.bt', AbsPath), 36 | b_setval(bt_file_path, AbsPath), 37 | phrase_from_file(behavior_tree_syntax:bt_dcg(Result), AbsPath, []), 38 | define_bt(List) = Result, 39 | collect_anons(List, WithAnons), % [':-'(def_node(H,O,A,C))] 40 | !, 41 | call_all(WithAnons), 42 | check_nodes. % this will need to be removed to handle multiple bt files 43 | 44 | call_all([]). 45 | call_all([Unbound | Tail]) :- 46 | \+ ground(Unbound), 47 | call_all(Tail). 48 | call_all([ ':-'(Term) | Tail]) :- 49 | call(Term), 50 | call_all(Tail). 51 | 52 | list_all_nodes :- 53 | findall(node_(M,H,O,A,C), bt_impl:node_(M,H,O,A,C), List), 54 | print_term(List, []). 55 | 56 | /******************************* 57 | * Quasiquoter * 58 | *******************************/ 59 | 60 | 61 | %! bt(Content, SyntaxArgs, VariableNames, Result) is det 62 | % 63 | % Quasiquoter for the bt language 64 | % 65 | bt(Content, SyntaxArgs, VariableNames, Result) :- 66 | bt_(Content, SyntaxArgs, VariableNames, Result), 67 | writeln(result_gonna_be(Result)), 68 | !. 69 | 70 | bt_(Content, _SyntaxArgs, _VariableNames, Result) :- 71 | bt_debug(bt(quasiquoter, in), 'bt in ~w', [Content]), 72 | catch( 73 | phrase_from_quasi_quotation(bt_dcg(Result), Content), 74 | Catcher, 75 | ( bt_print_message(error, error(bt_syntax_error(parser_failed, Catcher))), 76 | Result= define_bt([':-'(true)]) 77 | ) 78 | ), 79 | write('back from parsing'),portray_clause(Result), 80 | % if we have errors we may have vars in Result 81 | % this locks up the quasiquoter, and hence swipl 82 | ground(Result), % otherwise quasiquoter locks up. 83 | bt_debug(bt(quasiquoter, out), 'bt out ~w', [Result]). 84 | bt_(_, _, _, define_bt([':-'(true)])). % make sure we never fail 85 | 86 | user:message_hook(load_file(Data), _, _) :- 87 | Data = start(_, file(_Path, FullPath)), 88 | module_property(Module, file(FullPath)), 89 | reset_nodes_for_module(Module), 90 | bt_debug(bt(quasiquoter, message_hook), 'reset ~w nodes', [Module]). 91 | 92 | /* TODO removed until I can make it work 93 | system:term_expansion(end_of_file, [:-(bt_impl:check_nodes), end_of_file]). 94 | */ 95 | 96 | -------------------------------------------------------------------------------- /bt_example.pl: -------------------------------------------------------------------------------- 1 | :- module(bt_example, [test/2, test/3]). 2 | /** Example that runs the test simulation 3 | 4 | ?- test(root, 'examples/hwclient', 3). 5 | 6 | produces file tests.simai 7 | 8 | */ 9 | 10 | path_target. 11 | % This is required, needed for internal glue. 12 | % fix it so it points at your simgen directory 13 | % 14 | :- 15 | source_file(bt_example:path_target, Path), 16 | directory_file_path(Dir, _, Path), 17 | absolute_file_name(Dir, AbsPath), 18 | atomic_list_concat( 19 | [ 20 | AbsPath, 21 | '/simgen'], SGP), 22 | asserta(user:file_search_path(simgen, SGP)), 23 | atomic_list_concat( 24 | [ 25 | AbsPath, 26 | '/examples'], SGPE), 27 | asserta(user:file_search_path(examples, SGPE)). 28 | 29 | % Grab SimGen 30 | :- use_module(simgen(simgen)). 31 | 32 | %! test(+Root:atom, +N:integer) is nondet 33 | % 34 | % run the test.bt test file, making N-1 contexts 35 | % with root Root 36 | % 37 | test(Root, N) :- 38 | test(Root, tests, N). 39 | 40 | %! test(+Root:atom, +FileBase:atom, +N:integer) is nondet 41 | % 42 | % run a bt file, making N-1 contexts 43 | % with root Root 44 | % 45 | % The file name is like consult, you don't need the .bt 46 | % extension 47 | % 48 | test(Root, File, N) :- 49 | use_bt(File), 50 | setup_call_cleanup( 51 | open('tests.simai', write, Stream), 52 | ( b_setval(test_stream, Stream), 53 | b_setval(test_root, Root), 54 | !, % sanity measure, 55 | start_simulation( 56 | 0, % the start time, in 'our' units 57 | 60_000_000_000, % how long our units are in nanos 58 | 1, % how long a tick is in our units 59 | extern{ 60 | next_context: N, 61 | add_context_on_tick: 0 62 | }) 63 | ), 64 | close(Stream) 65 | ). 66 | 67 | % we register to listen for ticks 68 | % and add a new context randomly every 1 to 20 ticks 69 | % we number them going down 70 | % when we are asked to make context 0, we instead 71 | % end the simulation 72 | :- listen(tick(Extern, Tick, NewExtern), 73 | consider_adding_context(Extern, Tick, NewExtern)). 74 | 75 | consider_adding_context(Extern, Tick, Extern) :- 76 | Extern.add_context_on_tick > Tick, 77 | !. 78 | consider_adding_context(Extern, _, Extern) :- 79 | Extern.next_context = 0, 80 | !, 81 | end_simulation. 82 | 83 | consider_adding_context(Extern, Tick, NewExtern) :- 84 | b_getval(test_root, Root), 85 | Extern.add_context_on_tick =< Tick, 86 | succ(NN, Extern.next_context), 87 | % random_between(1, 20, R), 88 | R = 300, 89 | NewTick is R + Tick, 90 | NewExtern = extern{ 91 | next_context: NN, 92 | add_context_on_tick: NewTick 93 | }, 94 | start_context(Root, Extern.next_context, 0). 95 | 96 | % Now we handle reading, starting, and stopped events 97 | % by writing lines to the csv file 98 | 99 | % listen for reading/5 events and write reading events to csv 100 | :- listen(reading(Time, _, Context, Type, Value), 101 | write_event(reading, Time, Context, Type, Value)). 102 | 103 | % write text events to csv in response to starting 104 | :- listen(starting(Context-Type), 105 | ( get_clock(simgen, Time), 106 | write_event(text, Time, Context, Type, start) 107 | ) 108 | ). 109 | 110 | % We only listen to done and fail stopping. When a node stops 111 | % it terminates all nodes under it. We aren't usually interested in 112 | % those. 113 | :- listen(stopped(Context-Type, done), 114 | ( get_clock(simgen, Time), 115 | ( ground(Type) ; gtrace), % DEBUG 116 | write_event(text, Time, Context, Type, success) 117 | ) 118 | ). 119 | 120 | :- listen(stopped(Context-Type, fail), 121 | ( get_clock(simgen, Time), 122 | write_event(text, Time, Context, Type, fail) 123 | ) 124 | ). 125 | 126 | :- listen(pin_drop(Context, Time, event), 127 | write_event(text, Time, Context, pin_drop, event)). 128 | :- listen(pin_drop(Context, Time, -event), 129 | write_event(text, Time, Context, pin_drop, '-event')). 130 | 131 | write_event(Class, Time, Context, Type, Value) :- 132 | Nanos is Time * 60_000_000_000, 133 | b_getval(test_stream, Stream), 134 | format(Stream, 'unit,~d,~d,~w,~w,~w~n', 135 | [Context, Nanos, Class, Type, Value]), 136 | flush_output(Stream). 137 | -------------------------------------------------------------------------------- /oldstuff/life.pl: -------------------------------------------------------------------------------- 1 | :- module(life, [life_transition/6]). 2 | 3 | :- include(piddle). 4 | 5 | :- dynamic life:lt/6, imply/4. 6 | 7 | :- retractall(life:imply(_, _ , _, _)), 8 | retractall(life:lt(_, _, _, _, _, _)). 9 | 10 | define_piddle(PIDDLE) :- 11 | debug(dp, 'piddle out:~w', [PIDDLE]), 12 | maplist(dp , PIDDLE). 13 | 14 | dp(ec(List)) :- 15 | maplist(def_exclusion(List), List), 16 | List = [H | _], 17 | % enforce having one selected 18 | asserta(life:lt(1, [], List, [H], [], _)). 19 | 20 | dp(en(List)) :- 21 | maplist(def_exclusion(List), List). 22 | 23 | def_exclusion(List, Item) :- 24 | debug(dp, 'def_exclusion(~w, ~w)', [List, Item]), 25 | subtract(List, [Item], Remove), 26 | asserta(life:imply([Item], [], [], Remove)). 27 | 28 | life_transition(Prob, Require, Forbid, Add, Remove, Desc) :- 29 | lt(Prob, Require, Forbid, Add, Remove, Desc). 30 | 31 | %! lt(-Probability:prob, -Requires:list, -Forbids:list, 32 | %! -Add:list, -Remove:list, -Desc:atom) is nondet 33 | % 34 | % life transitions. Things that can happen to a person during 35 | % their life 36 | 37 | :-call({|piddle|| 38 | ex c foo, bar; 39 | ex baz, mep; 40 | |}). 41 | 42 | /******************************* 43 | * Congenital Conditions * 44 | *******************************/ 45 | 46 | % can you find a way to make the next two lines symmetric? 47 | lt(0.5, [age(0,0)], [female], [male], [], ' is male'). 48 | lt(1, [age(0,0)], [male], [female], [], ' is female'). 49 | lt(1/15,[age(0,0)], [rich_parents, orphan], [poor_parents], [], ' has poor parents'). 50 | lt(1/100, [age(0,0)], [poor_parents, orphan], [rich_parents], [], ' has rich parents'). 51 | lt(1/500, [age(0,0)], [rich_parents, poor_parents], [orphan], [], ' is an orphan'). 52 | lt(1/100, [age(0,0)], [], [ambitious], [], ' is ambitious'). 53 | lt(1/100, [age(0,0)], [], [learning_disabled], [], ' is learning disabled'). 54 | lt(0.1, [age(0,0)], [], [gay], [], ' is gay'). 55 | 56 | /******************************* 57 | * parenting * 58 | *******************************/ 59 | 60 | lt(1/6, [age(0,3), orphan], [], [rich_parents], [orphan], ' is adopted'). 61 | lt(1/15, [age(4,9), orphan], [gay, learning_disabled], [rich_parents], [orphan], ' is adopted'). 62 | lt(1/45, [age(4,9), orphan], [], [rich_parents], [orphan], ' is adopted'). 63 | lt(1/100, [age(0,18)], [orphan], [orphan], [rich_parents, poor_parents], ' loses parents'). 64 | lt(1/10, [], [], [problem_parent], [], ' has a parent with a problem'). 65 | lt(1/25, [age(14,20), gay], [], [rich_parents], [unsupportive_parents], ' rejected by parents'). 66 | 67 | /******************************* 68 | * school * 69 | *******************************/ 70 | 71 | % tp is 'time passed'. It's removed when we increment the age 72 | lt(1, [age(6,18)], [dropped_out], [in_school], [], ''). % if you arent dropped out your in school 73 | lt(1, [age(6,18), dropped_out], [], [], [in_school], ''). % if you drop out you're not in school 74 | lt(0.1, [age(12,18), problem_parent], [ambitious], [dropped_out], [], ' dropped out'). 75 | lt(1/50, [age(12,18)], [problem_parent], [dropped_out], [], ' dropped out'). 76 | lt(1, [age(18,18), in_school], [], [graduated_hs, tp], [in_school], ' graduated'). 77 | lt(0.95, [graduated_hs, age(0, 26), rich_parents], [], [college_freshman], [], ' enrolled in college'). 78 | lt(0.9, [college_freshman], [tp], [college_sophmore, tp], [college_freshman], ' became a sophmore'). 79 | lt(0.9, [college_sophmore], [tp], [college_junior, tp], [college_sophmore], ' became a junior'). 80 | lt(0.9, [college_junior], [tp], [college_senior, tp], [college_junior], ' became a senior'). 81 | lt(0.9, [college_senior], [tp], [college_grad, tp], [college_senior], ' graduated'). 82 | 83 | /******************************* 84 | * illness * 85 | *******************************/ 86 | lt(1/3, [major_illness], [], [dead], [major_illness], ' die'). 87 | lt(1/50, [age(0,3)], [], [major_illness], [], ' develop a major illness'). 88 | lt(1/100, [age(4,55)], [], [major_illness], [], ' develop a major illness'). 89 | lt(1/10, [drug_use], [], [major_illness], [], ' develop a major illness because of drug use'). 90 | lt(1/25, [age(56, 70)], [], [major_illness], [], ' develop a major illness'). 91 | lt(1/5, [age(70, 999)], [], [major_illness], [], ' develop a major illness'). 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /bt_example_jitter.pl: -------------------------------------------------------------------------------- 1 | :- module(bt_example_jitter, [test/2, test/3]). 2 | /** Example that runs the test simulation 3 | 4 | 5 | */ 6 | 7 | path_target. 8 | % This is required, needed for internal glue. 9 | % fix it so it points at your simgen directory 10 | % 11 | :- 12 | source_file(bt_example_jitter:path_target, Path), 13 | directory_file_path(Dir, _, Path), 14 | absolute_file_name(Dir, AbsPath), 15 | atomic_list_concat( 16 | [ 17 | AbsPath, 18 | '/simgen'], SGP), 19 | asserta(user:file_search_path(simgen, SGP)), 20 | atomic_list_concat( 21 | [ 22 | AbsPath, 23 | '/examples'], SGPE), 24 | asserta(user:file_search_path(examples, SGPE)). 25 | 26 | % Grab SimGen 27 | :- use_module(simgen(simgen)). 28 | 29 | %! test(+Root:atom, +N:integer) is nondet 30 | % 31 | % run the test.bt test file, making N-1 contexts 32 | % with root Root 33 | % 34 | test(Root, N) :- 35 | test(Root, tests, N). 36 | 37 | %! test(+Root:atom, +FileBase:atom, +N:integer) is nondet 38 | % 39 | % run a bt file, making N-1 contexts 40 | % with root Root 41 | % 42 | % The file name is like consult, you don't need the .bt 43 | % extension 44 | % 45 | test(Root, File, N) :- 46 | use_bt(File), 47 | setup_call_cleanup( 48 | open('tests.simai', write, Stream), 49 | ( b_setval(test_stream, Stream), 50 | b_setval(test_root, Root), 51 | !, % sanity measure, 52 | start_simulation( 53 | 0, % the start time, in 'our' units 54 | 60_000_000_000, % how long our units are in nanos 55 | 1, % how long a tick is in our units 56 | extern{ 57 | next_context: N, 58 | add_context_on_tick: 0 59 | }) 60 | ), 61 | close(Stream) 62 | ). 63 | 64 | % we register to listen for ticks 65 | % and add a new context randomly every 1 to 20 ticks 66 | % we number them going down 67 | % when we are asked to make context 0, we instead 68 | % end the simulation 69 | :- listen(tick(Extern, Tick, NewExtern), 70 | consider_adding_context(Extern, Tick, NewExtern)). 71 | 72 | consider_adding_context(Extern, Tick, Extern) :- 73 | Extern.add_context_on_tick > Tick, 74 | !. 75 | consider_adding_context(Extern, _, Extern) :- 76 | Extern.next_context = 0, 77 | !, 78 | end_simulation. 79 | 80 | consider_adding_context(Extern, Tick, NewExtern) :- 81 | b_getval(test_root, Root), 82 | Extern.add_context_on_tick =< Tick, 83 | succ(NN, Extern.next_context), 84 | % random_between(1, 20, R), 85 | R = 1500, 86 | NewTick is R + Tick, 87 | NewExtern = extern{ 88 | next_context: NN, 89 | add_context_on_tick: NewTick 90 | }, 91 | start_context(Root, Extern.next_context, 0). 92 | 93 | % Now we handle reading, starting, and stopped events 94 | % by writing lines to the csv file 95 | 96 | % listen for reading/5 events and write reading events to csv 97 | :- listen(reading(Time, _, Context, Type, Value), 98 | write_event(reading, Time, Context, Type, Value)). 99 | 100 | % write text events to csv in response to starting 101 | :- listen(starting(Context-Type), 102 | ( get_clock(simgen, Time), 103 | write_event(text, Time, Context, Type, start) 104 | ) 105 | ). 106 | 107 | % We only listen to done and fail stopping. When a node stops 108 | % it terminates all nodes under it. We aren't usually interested in 109 | % those. 110 | :- listen(stopped(Context-Type, done), 111 | ( get_clock(simgen, Time), 112 | ( ground(Type) ; gtrace), % DEBUG 113 | write_event(text, Time, Context, Type, success) 114 | ) 115 | ). 116 | 117 | :- listen(stopped(Context-Type, fail), 118 | ( get_clock(simgen, Time), 119 | write_event(text, Time, Context, Type, fail) 120 | ) 121 | ). 122 | 123 | :- listen(pin_drop(Context, Time, event), 124 | write_event(text, Time, Context, pin_drop, event)). 125 | :- listen(pin_drop(Context, Time, -event), 126 | write_event(text, Time, Context, pin_drop, '-event')). 127 | 128 | :- use_module(library(clpfd)). 129 | 130 | write_event(Class, Time, Context, Type, Value) :- 131 | ( Time in 100..500 \/ 577 \/ 600..622 \/ 1125 \/ 1800, 132 | member(Type, [b,c]) 133 | -> Nanos is Time * 60_000_000_000 + random(1_000_000) 134 | ; Nanos is Time * 60_000_000_000 135 | ), 136 | b_getval(test_stream, Stream), 137 | format(Stream, 'unit,~d,~d,~w,~w,~w~n', 138 | [Context, Nanos, Class, Type, Value]), 139 | flush_output(Stream). 140 | 141 | -------------------------------------------------------------------------------- /test/unit_tests.pl: -------------------------------------------------------------------------------- 1 | :- module(unit_tests, [integration_tests/0]). 2 | /** run unit tests 3 | query 4 | 5 | ---- 6 | ?- integration_tests. 7 | ---- 8 | 9 | */ 10 | 11 | :- debug(error(_,_)). 12 | :- debug(bt(_,_)). 13 | 14 | path_target. 15 | % This is required, needed for internal glue. 16 | % fix it so it points at your simgen directory 17 | % 18 | :- 19 | source_file(unit_tests:path_target, Path), 20 | directory_file_path(Dir, _, Path), 21 | absolute_file_name(Dir, AbsPath), 22 | atomic_list_concat( 23 | [ 24 | 25 | AbsPath, 26 | '/../simgen'], SGP), 27 | asserta(user:file_search_path(simgen, SGP)), 28 | atomic_list_concat( 29 | [ 30 | AbsPath, 31 | '.'], SGPE), 32 | asserta(user:file_search_path(test, SGPE)). 33 | 34 | % Grab SimGen 35 | :- use_module(simgen(simgen)). 36 | 37 | %! integration_tests is det 38 | % 39 | % Run the unit tests 40 | integration_tests :- 41 | test(root, tests, 3). 42 | 43 | %! test(+Root:atom, +N:integer) is nondet 44 | % 45 | % run the test.bt test file, making N-1 contexts 46 | % with root Root 47 | % 48 | test(Root, N) :- 49 | test(Root, tests, N). 50 | 51 | %! test(+Root:atom, +FileBase:atom, +N:integer) is nondet 52 | % 53 | % run a bt file, making N-1 contexts 54 | % with root Root 55 | % 56 | % The file name is like consult, you don't need the .bt 57 | % extension 58 | % 59 | test(Root, File, N) :- 60 | % set_random(seed(42)), 61 | use_bt(File), 62 | setup_call_cleanup( 63 | open('tests.simai', write, Stream), 64 | ( b_setval(test_stream, Stream), 65 | b_setval(test_root, Root), 66 | !, % sanity measure, 67 | start_simulation( 68 | 0, % the start time, in 'our' units 69 | 60_000_000_000, % how long our units are in nanos 70 | 1, % how long a tick is in our units 71 | extern{ 72 | next_context: N, 73 | add_context_on_tick: 0 74 | }) 75 | ), 76 | close(Stream) 77 | ). 78 | 79 | % we register to listen for ticks 80 | % and add a new context randomly every 1 to 20 ticks 81 | % we number them going down 82 | % when we are asked to make context 0, we instead 83 | % end the simulation 84 | :- listen(tick(Extern, Tick, NewExtern), 85 | consider_adding_context(Extern, Tick, NewExtern)). 86 | 87 | consider_adding_context(Extern, Tick, Extern) :- 88 | Extern.add_context_on_tick > Tick, 89 | !. 90 | consider_adding_context(Extern, _, Extern) :- 91 | Extern.next_context = 0, 92 | !, 93 | end_simulation. 94 | 95 | consider_adding_context(Extern, Tick, NewExtern) :- 96 | b_getval(test_root, Root), 97 | Extern.add_context_on_tick =< Tick, 98 | succ(NN, Extern.next_context), 99 | % random_between(1, 20, R), 100 | R = 100, 101 | NewTick is R + Tick, 102 | NewExtern = extern{ 103 | next_context: NN, 104 | add_context_on_tick: NewTick 105 | }, 106 | start_context(Root, Extern.next_context, 0). 107 | 108 | % Now we handle reading, starting, and stopped events 109 | % by writing lines to the csv file 110 | 111 | % listen for reading/5 events and write reading events to csv 112 | :- listen(reading(Time, _, Context, Type, Value), 113 | write_event(reading, Time, Context, Type, Value)). 114 | 115 | % write text events to csv in response to starting 116 | :- listen(starting(Context-Type), 117 | ( get_clock(simgen, Time), 118 | write_event(text, Time, Context, Type, start) 119 | ) 120 | ). 121 | 122 | % We only listen to done and fail stopping. When a node stops 123 | % it terminates all nodes under it. We aren't usually interested in 124 | % those. 125 | :- listen(stopped(Context-Type, done), 126 | ( get_clock(simgen, Time), 127 | ( ground(Type) ; gtrace), % DEBUG 128 | write_event(text, Time, Context, Type, success) 129 | ) 130 | ). 131 | 132 | :- listen(stopped(Context-Type, fail), 133 | ( get_clock(simgen, Time), 134 | write_event(text, Time, Context, Type, fail) 135 | ) 136 | ). 137 | 138 | :- listen(pin_drop(Context, Time, event), 139 | write_event(text, Time, Context, pin_drop, event)). 140 | :- listen(pin_drop(Context, Time, -event), 141 | write_event(text, Time, Context, pin_drop, '-event')). 142 | 143 | write_event(Class, Time, Context, Type, Value) :- 144 | Nanos is Time * 60_000_000_000, 145 | b_getval(test_stream, Stream), 146 | format(Stream, 'unit,~d,~d,~w,~w,~w~n', 147 | [Context, Nanos, Class, Type, Value]), 148 | flush_output(Stream). 149 | -------------------------------------------------------------------------------- /simgen/functions.pl: -------------------------------------------------------------------------------- 1 | :- module(functions , [ 2 | get_functor_ok/2, 3 | do_func/4 4 | ]). 5 | 6 | 7 | 8 | :- use_module(simgen(clocks)). 9 | :- use_module(simgen(print_system)). 10 | 11 | 12 | get_functor_ok(lastval, levy_flight). 13 | get_functor_ok(lastval, wander). 14 | get_functor_ok(lastval, F) :- any_get(F). 15 | get_functor_ok(getval, F) :- any_get(F). 16 | get_functor_ok(ezval, F) :- any_get(F). 17 | 18 | % functors that are not prolog built-ins 19 | any_get(clock). 20 | any_get(pow). 21 | any_get(lshift). 22 | any_get(rshift). 23 | any_get(bitor). 24 | any_get(bitand). 25 | any_get(X) :- prolog_function(X/_). 26 | 27 | 28 | do_func(_, levy_flight, [LastVal, Lo, Hi], Val) :- 29 | map64k(LastVal, Lo, Hi, LastValMapped), % map to range [0-64k) 30 | levy_flight(LastValMapped, NewValMapped), 31 | map64k(Val, Lo, Hi, NewValMapped). 32 | do_func(_, wander, [LastVal, Lo, Hi, Dist], Val) :- 33 | bt_debug(bt(flow,wander), 'in wander ~w, ~w, ~w, ~w', 34 | [LastVal, Lo, Hi, Dist]), 35 | Bias is 2 * (LastVal - Lo) / (Hi - Lo) , 36 | random(R), 37 | Del is 2 * Dist * R - Dist * Bias, 38 | Val is min(Hi, max(Lo, LastVal + Del)), 39 | bt_debug(bt(flow,wander), 'out', []). 40 | do_func(C-N, clock, [], Val) :- 41 | bt_debug(bt(flow,clock), 'function clock(~w), ~w', [C, N]), 42 | get_clock(C-N, Val). 43 | do_func(_, pow, [Old, Exp], Val) :- Val is Old^Exp. 44 | do_func(_, lshift, [Old, Bits], Val) :- Val is Old << Bits. 45 | do_func(_, rshift, [Old, Bits], Val) :- Val is Old >> Bits. 46 | do_func(_, bitor, [Old, Bits], Val) :- Val is Old \/ Bits. 47 | do_func(_, bitand, [Old, Bits], Val) :- Val is Old /\ Bits. 48 | 49 | % those that are prolog functions 50 | do_func(_, Functor, [], Val) :- 51 | prolog_function(Functor/0), 52 | Blah =.. [Val, is , Functor], 53 | call(Blah). 54 | do_func(_, Functor, [Arg], Val) :- 55 | prolog_function(Functor/1), 56 | Blah =.. [Functor, Arg], 57 | Val is Blah. 58 | do_func(_, Functor, [L, R], Val) :- 59 | prolog_function(Functor/2), 60 | Blah =.. [Functor, L, R], 61 | Val is Blah. 62 | 63 | prolog_function(mod/2). 64 | prolog_function(rem/2). 65 | prolog_function(div/2). 66 | prolog_function(rdiv/2). 67 | prolog_function(gcd/2). 68 | prolog_function(abs/1). 69 | prolog_function(sign/1). 70 | prolog_function(copysign/2). 71 | prolog_function(max/2). 72 | prolog_function(min/2). 73 | prolog_function(random/1). 74 | prolog_function(random_float/0). 75 | prolog_function(round/1). 76 | prolog_function(integer/1). 77 | prolog_function(float/1). 78 | prolog_function(rational/1). 79 | prolog_function(rationalize/1). 80 | prolog_function(float_fractional_part/1). 81 | prolog_function(float_integer_part/1). 82 | prolog_function(truncate/1). 83 | prolog_function(floor/1). 84 | prolog_function(ceiling/1). 85 | prolog_function(xor/2). 86 | prolog_function(sqrt/1). 87 | prolog_function(sin/1). 88 | prolog_function(cos/1). 89 | prolog_function(tan/1). 90 | prolog_function(asin/1). 91 | prolog_function(acos/1). 92 | prolog_function(atan/1). 93 | prolog_function(atan2/2). 94 | prolog_function(atan/2). 95 | prolog_function(sinh/1). 96 | prolog_function(cosh/1). 97 | prolog_function(tanh/1). 98 | prolog_function(asinh/1). 99 | prolog_function(acosh/1). 100 | prolog_function(atanh/1). 101 | prolog_function(log/1). 102 | prolog_function(log10/1). 103 | prolog_function(exp/1). 104 | prolog_function(lgamma/1). 105 | prolog_function(erf/1). 106 | prolog_function(erfc/1). 107 | prolog_function(pi/0). 108 | prolog_function(e/0). 109 | prolog_function(epsilon/0). 110 | prolog_function(inf/0). 111 | prolog_function(nan/0). 112 | prolog_function(cputime/0). 113 | prolog_function(msb/1). 114 | prolog_function(lsb/1). 115 | prolog_function(popcount/1). 116 | prolog_function(getbit/2). 117 | 118 | map64k(N, Lo, Hi, Mapped) :- 119 | ground(Mapped), 120 | N is Lo + (Hi - Lo) * Mapped / 65536.0 . 121 | map64k(N, Lo, Hi, Mapped) :- 122 | ground(N), 123 | Mapped is round((N - Lo) * 65536.0 / (Hi - Lo)). 124 | 125 | % compute new mapped value from old mapped value 126 | levy_flight(LastVal, NewVal) :- 127 | random_between(0, 0xf, BitsToFlip), 128 | Bit is ((1 << BitsToFlip) >> 1), 129 | levy_flight(LastVal, NewVal, Bit). 130 | 131 | levy_flight(V, V, 0). 132 | levy_flight(LastVal, Val, Bit) :- 133 | bt_debug(bt(flow, levy_flight), 134 | 'levy_flight(~w, ~w, ~w)', 135 | [LastVal, Val, Bit]), 136 | random_between(0,1, Flip), 137 | ( Flip =:= 0 138 | -> NewVal is LastVal /\ xor(0xffff , Bit) 139 | ; NewVal is LastVal \/ Bit 140 | ), 141 | NewBit is Bit >> 1, 142 | levy_flight(NewVal, Val, NewBit). 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /simgen/nodes/pdq.pl: -------------------------------------------------------------------------------- 1 | :- module(pdq, []). 2 | 3 | :- dynamic running/1, to_do/2, first_tick_done/1. 4 | 5 | :- use_module(simgen(bt_impl), [emit/1]). 6 | :- use_module(simgen(valuator)). 7 | :- use_module(simgen(print_system)). 8 | :- use_module(simgen(functions)). 9 | 10 | :-listen(simulation_starting, reset). 11 | 12 | reset :- 13 | retractall(running(_)), 14 | retractall(to_do(_, _)), 15 | retractall(first_tick_done(_)). 16 | 17 | :- multifile bt_impl:make_cn_impl/3. 18 | 19 | bt_impl:make_cn_impl( '!' , C-N, _-_) :- 20 | running(C-N), 21 | !, 22 | bt_debug(bt(pdq, make_cn_impl), 23 | '~w-~w already running', [C,N]). 24 | bt_impl:make_cn_impl( '!' , C-N, CParent-NParent) :- 25 | asserta(running(C-N)), 26 | retractall(first_tick_done(C-N)), 27 | retractall(to_do(C-N, _)), 28 | listen(C-N, terminate(C-N), pdq:terminate(C-N)), 29 | listen(C-N, terminate_if_child(CParent-NParent), 30 | pdq:terminate(C-N)), 31 | listen(C-N, tick_start, tick_start(C-N)), 32 | listen(C-N, more, has_more(C-N)), 33 | listen(C-N, propagate, propagate(C-N)), 34 | listen(C-N, tick_end, tick_end(C-N)), 35 | bt_debug(bt(pdq, make_cn_impl), 'start ~w-~w', [C,N]), 36 | emit(starting(C-N)). 37 | 38 | stop_me(C-N, How) :- 39 | unlisten(C-N, _, _), 40 | retractall(to_do(C-N, _)), 41 | retractall(first_tick_done(C-N)), 42 | retractall(running(C-N)), 43 | bt_debug(bt(pdq, stop_me), 'stop ~w-~w', [C,N]), 44 | emit(stopped(C-N, How)). 45 | 46 | terminate(C-N) :- 47 | unlisten(C-N, _, _), 48 | retractall(running(C-N)), 49 | retractall(to_do(C-N, _)), 50 | retractall(first_tick_done(C-N)), 51 | bt_debug(bt(pdq, terminate), 'terminated ~w-~w', [C,N]), 52 | emit(stopped(C-N, terminated)). 53 | 54 | tick_start(C-N) :- 55 | bt_impl:node_(_, N, '!', [FirstTick, OtherTicks, _Conds], _), 56 | retractall(to_do(C-N, _)), 57 | ( first_tick_done(C-N) 58 | -> asserta(to_do(C-N, OtherTicks)) 59 | ; asserta(to_do(C-N, FirstTick)), 60 | asserta(first_tick_done(C-N)) 61 | ). 62 | 63 | tick_end(C-N) :- 64 | first_tick_done(C-N), 65 | !, 66 | bt_impl:node_(_, N, '!', [_, _, Conds], _), 67 | ( conds(C-N, Conds) 68 | -> true 69 | ; stop_me(C-N, fail) 70 | ). 71 | % always succeed if we haven't done the first tick by end of tick 72 | % fixes the mystery unavail values 73 | tick_end(C-N) :- 74 | \+ first_tick_done(C-N). 75 | 76 | has_more(C-N) :- 77 | to_do(C-N, ToDo), 78 | ToDo \= [], 79 | bt_debug(bt(flow,propagate), '~w-~w needs more', [C,N]). 80 | 81 | propagate(C-N) :- 82 | to_do(C-N, List), 83 | bt_debug(bt(flow,propagate), 84 | 'propagating ~w-~w tasks ~w', 85 | [C,N,List]), 86 | ( List = [] 87 | ; eval(C-N, List, NewList), 88 | retractall(to_do(C-N, _)), 89 | asserta(to_do(C-N, NewList)) 90 | ), 91 | to_do(C-N, L), 92 | bt_debug(bt(flow,propagate), 93 | 'after list is ~w', [L]). 94 | 95 | /******************************* 96 | * Continuous evaluator 97 | *******************************/ 98 | 99 | eval(_CN, [], []). 100 | eval(C-N, [H | T], Remain) :- 101 | ( eval(C-N, H) 102 | -> eval(C-N, T, Remain) 103 | ; eval(C-N, T, R), 104 | Remain = [H | R] 105 | ). 106 | 107 | eval(C-N, ':='(LVAL, RVAL)) :- 108 | eval_rval(C-N, lastval, RVAL, Value), 109 | setval(C, LVAL, Value). 110 | eval(C-N, '='(LVAL, RVAL)) :- 111 | eval_rval(C-N, getval, RVAL, Value), 112 | setval(C, LVAL, Value). 113 | 114 | conds(C-N, X) :- once(conds_(C-N, X)). 115 | conds_(_, []). 116 | conds_(C-N, [H | T]) :- 117 | H =.. [CompareOp, Left, Right], 118 | eval_rval(C-N, ezval, Left, LeftVal), 119 | eval_rval(C-N, ezval, Right, RightVal), 120 | !, 121 | Compo =.. [CompareOp, LeftVal, RightVal], 122 | call_if_avail(Compo, LeftVal, RightVal), 123 | bt_debug(bt(ticks, cond), 'cond ~w ~w ~w passed', [CompareOp, LeftVal, RightVal]), 124 | conds(C-N, T). 125 | conds_(C-N, [H | _]) :- 126 | bt_debug(bt(ticks, cond), 'context ~w cond ~w failed', [C-N, H]), 127 | fail. 128 | 129 | call_if_avail(_, '$not_avail$', _). 130 | call_if_avail(_, _, '$not_avail$'). 131 | call_if_avail(Goal, A, B) :- 132 | A \= '$not_avail$', 133 | B \= '$not_avail$', 134 | call(Goal). 135 | 136 | /* 137 | eval_rval(C-N, GetFunctor, eval(wander(V, Lo, Hi, Dist)) , Value) :- 138 | gtrace, 139 | fail. 140 | */ 141 | eval_rval(C-N, GetFunctor, RVal , Value) :- 142 | RVal =.. [F, A, B], 143 | eval_rval(C-N, GetFunctor, A, AVal), 144 | eval_rval(C-N, GetFunctor, B, BVal), 145 | e(F, AVal, BVal, Value). 146 | eval_rval(C-N, GetFunctor, -A , Value) :- 147 | eval_rval(C-N, GetFunctor, A, AVal), 148 | Value is -AVal. 149 | eval_rval(C-N, GetFunctor, eval(RVal) , Value) :- 150 | RVal =.. [F | Args], 151 | get_functor_ok(GetFunctor, F), 152 | maplist(eval_rval(C-N, GetFunctor), Args, ArgVals), 153 | do_func(C-N, F, ArgVals, Value). 154 | eval_rval(C-N, GetFunctor, eval(C, F), Value) :- 155 | atom(F), 156 | ( get_functor_ok(GetFunctor, F) 157 | -> do_func(C-N, F, [], Value) 158 | ; get_functor_ok(Legal, F), 159 | gf_name_symbol(GetFunctor, GetFunctorSymbol), 160 | gf_name_symbol(Legal, LegalSymbol), 161 | bt_debug(error(pdq, bad_get), 'Illegal to use ~w in ~w, try ~w', 162 | [GetFunctorSymbol, F, LegalSymbol]), 163 | Value = 0 164 | ). 165 | eval_rval(_, _, const(Val), Val). 166 | eval_rval(C-_N, GetFunctor, var(Name), Val) :- 167 | !, % this is nondet 168 | Func =.. [GetFunctor, C, Name, Val], 169 | call(valuator:Func). 170 | 171 | gf_name_symbol(getval, '='). 172 | gf_name_symbol(lastval, ':='). 173 | gf_name_symbol(ezval, 'in cond'). 174 | 175 | e( '+', A, B, C) :- C is A + B. 176 | e( '-', A, B, C) :- C is A - B. 177 | e( '*', A, B, C) :- C is A * B. 178 | e( '/', A, B, C) :- C is A / B. 179 | -------------------------------------------------------------------------------- /docs/agentbased.md: -------------------------------------------------------------------------------- 1 | # BTA Agent Based Behavior Tree Interpreter 2 | 3 | 4 | C-N is a context-node pair, represented as `Context-Node`. This represents a running node, a *task*. 5 | The notion of a task is now the same as a C-N. We no longer split up ticks 6 | 7 | The system is single threaded. It runs until it receives the **message queue message** `end_simulation` on the `simgen` queue. 8 | 9 | BTA depends on the SWI-Prolog library(broadcast). 10 | 11 | System is started by calling `start_simulation/4`. 12 | 13 | ## Issues 14 | 15 | * External - handling the external tick callback. 16 | * How does Prolog start/stop C-N 17 | * register listeners at CN start time. 18 | * tick listener 19 | * terminate listener 20 | * listeners needed to maintain state 21 | * Nodes use assert/retract to maintain their state information 22 | * Nodes announce 23 | * starting(C-N) 24 | * stopped(C-N, done) 25 | * stopped(C-N, fail) 26 | * stopped(C-N, terminated) 27 | * Each node op defines a clause in multifile `make_cn_impl(Op, Context-Node)` 28 | * `make_cn(Context-Node)` makes a new node 29 | * `node_/5` gets a public wrapper for access 30 | * values - There is a value listener (sep. module, valuator) that listens for broadcast_request's for lastval, getval, setval, and ezval 31 | * clocks 32 | * parallel 33 | * debug - TBD 34 | * Unlike LSL there is NO recursive message avoidance. Nodes must not broadcast messages, ubt send them to messaging queue. Hence nodes cannot broadcast_request. 35 | * thread synch - we're single threaded 8cD 36 | 37 | 38 | ## The Tick Sequence 39 | 40 | - if `end_simulation_message_exists` stop the simulation 41 | - Update all clocks 42 | - broadcast_request the tick/3 event so external Prolog can run 43 | - Get and process all message_queue messages from external prolog 44 | - send a tick_start message. 45 | - broadcast all messages off the u queue until its empty 46 | - call the valuator to do propagating values 47 | - send a tick_end message. 48 | - broadcast all messages off the u queue until its empty 49 | - display an ascii graphic debug panel listing running tasks and known values using `debug/3`. 50 | 51 | ## Reentrant Messaging 52 | 53 | Reentrant messaging is bad. 54 | 55 | Nodes must not call `broadcast/1` or `broadcast_request/1`, 56 | 57 | but must call `emit/1`. This adds the messages to messaging queue named u. 58 | 59 | `messaging_service` broadcasts these messages. 60 | 61 | Hence nodes cannot synch broadcast. 62 | 63 | 64 | ## Listener IDs 65 | 66 | Listener ID's are C-N 67 | 68 | ## Starting Tasks 69 | 70 | Tasks are started by calling `make_cn(C-N)` 71 | 72 | Every node op must implement a clause of the multifile predicate `make_cn_impl(O, C-N)` 73 | 74 | What task does on startup 75 | * set up listen for messages 76 | * record who started you, so you can respond to terminate_if_child request (thats in the listener) 77 | * emit `starting(C-N)` 78 | 79 | Note that most tasks ignore restart messages. If the task is started, do NOT emit a starting message 80 | 81 | ## Stopping Tasks 82 | 83 | Tasks are stopped by being _terminated_ or by their internal logic. 84 | 85 | * emit terminate_if_child to stop any running children (note you might not have any) 86 | * remove your listeners 87 | * abolish any state you may have 88 | * Nodes announce 89 | * stopped(C-N, done) 90 | * stopped(C-N, fail) 91 | * stopped(C-N, terminated) 92 | 93 | ## Terminating Tasks 94 | 95 | To stop another task, emit `terminate(C-N)` or `terminate_if_child(C-N)`. 96 | 97 | Every task **must** respond to these messages by terminating itself and any tasks it started. 98 | 99 | Don't forget to do the normal 'Stopping Tasks' stuff, including emitting `stopped(C-N, terminated)` 100 | 101 | Make some convenience preds 102 | 103 | ## Starting and stopping contexts 104 | 105 | A message to start or stop a context is put on the simgen queue by Prolog code via the convenience pred. 106 | 107 | At the appropriate point in the tick sequence the messages are processed, 108 | 109 | When a context starts, it does the following: 110 | 111 | * creates the context clock 112 | * calls make_cn on the root node to install listeners 113 | 114 | When a context ends, it does the following: 115 | 116 | * destroys the context clock 117 | * abolishes all context listeners 118 | 119 | Values for dead contexts are gone in a tick. 120 | 121 | ## State Info 122 | 123 | Tasks use assert/retract 124 | 125 | ## Events 126 | 127 | The valuator must emit reading events 128 | broadcast(reading(Time, ContextTime, Context, LVAL, Value)). 129 | 130 | Alter the documentation for externals to listen to the starting/stopped messages 131 | 132 | ## Values 133 | 134 | Values are stored by a valuator. 135 | 136 | lastvals are gotten by a predicate call. 137 | 138 | setvals are just predicate calls. 139 | 140 | Each tick the valuator will loop-recur: 141 | 142 | * it'll send a `broadcast(propagate)`. 143 | * Send `broadcast_request(more)`. If anyone responds, continue loop 144 | 145 | ## pdq node 146 | 147 | the pdq node will do a first tick, then remaining ticks 148 | 149 | at end of first tick, it asserts first_tick(C-N). 150 | at task stop time, we retract first_tick(C-N). 151 | 152 | at start of each tick we assert to_do(C-N, List) with stuff to eval that pass. 153 | 154 | in response to propagate we try to evaluate each expression. 155 | 156 | as we pass through the list, we weed out the ones that succeed, 157 | and update to_do with the ones that failed. 158 | 159 | in response to more, we succeed if we still have things to eval. 160 | 161 | If the valuator doesn't have a value for getval, it fails. 162 | 163 | ## Clocks 164 | 165 | Clocks are just facts in a clocks module. 166 | 167 | You get the current clock time by pred query. 168 | 169 | new_clock/2 assumes simgen as the parent clock. 170 | 171 | 172 | ## Parallel 173 | 174 | Parallel is easy with this scheme. 175 | 176 | Start all the children. 177 | 178 | Record names of all children. 179 | 180 | Register a listener for fail that terminates all children and fails. 181 | 182 | Register a listener for done that removes from list of tasks we're working on. Succeed if list is now empty. 183 | 184 | terminate as usual. 185 | 186 | 187 | ## Development Practices 188 | 189 | * Can we automate check for reentrant messages? 190 | 191 | ## external broadcast messages 192 | 193 | * `tick(External, Time, NewExternal)` - prolog code responds by grounding NewExternal and doing whatever needs done 194 | * `starting(C-N)` 195 | * `stopped(C-N, done)` 196 | * `stopped(C-N, fail)` 197 | * `stopped(C-N, terminated)` 198 | * `starting(C-N)` 199 | * `reading(Time, ContextTime, Context, Type, Value)` 200 | * `simulation_starting` 201 | 202 | ## internal message listeners 203 | 204 | Besides any of the external messages 205 | 206 | * `terminate(C-N)` 207 | * `terminate_if_child(CAncestor-NAncestor)` 208 | * `tick_start` - do whatever your node does at tick_start 209 | * `tick_end` - do whatever your node does at tick_end 210 | * `more` - if handler succeeds, it wants more iterations of flow propagation 211 | * `propagate` - attempt to propagate all remaining flows 212 | 213 | ## simgen queue messages 214 | 215 | * `new_clock(Context, Time)` - Create this clock 216 | * `start_node(Context-Root)` - start a node 217 | * `end_simulation` - kill simulation at end of tick 218 | 219 | ## u queue messages 220 | 221 | all messages on the u queue are subsequently broadcast 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /oldstuff/old_behavior_tree_parser.pl: -------------------------------------------------------------------------------- 1 | :- module(behavior_tree_parser, 2 | [ 3 | op(1200, xfx, => ), % sequence 4 | op(1200, xfx, >> ), % selector 5 | op(200, fy, ?), % variable 6 | op(200, fy, ??), % flag 7 | op(700, xfx, ??=), % set/reset flag 8 | op(200, fy, #), % action 9 | op(200, fy, @@), % probabilistic sample 10 | tick/1 11 | ]). 12 | /** behavior trees for SWI-Prolog 13 | 14 | */ 15 | :- use_module(library(dcg/basics)). 16 | 17 | :- use_module(bt_impl). 18 | 19 | tick(_Node) :- 20 | true. 21 | 22 | behavior_tree --> 23 | statement, behavior_tree. 24 | behavior_tree --> eos. 25 | 26 | statement --> 27 | head, 28 | bt_operator(ArgType), 29 | args(ArgType), 30 | ['.']. 31 | 32 | head --> anatom. 33 | 34 | anatom --> [X], {atom(X)}. 35 | 36 | args(arg_list) --> arg. 37 | args(arg_list) --> arg, 38 | [','], 39 | args(arg_list). 40 | args(atom) --> anatom. 41 | args(context_node_pair) --> 42 | integer(_Context), 43 | 44 | 45 | 46 | arg --> anatom. 47 | arg --> decoration, arg. 48 | arg --> ['{', head, bt_operator(ArgType), args(ArgType), '}']. 49 | 50 | 51 | :-discontiguous bt_operator/3. 52 | 53 | /******************************* 54 | * Selectors 55 | *******************************/ 56 | 57 | %@ priority_selector 58 | % 59 | % run tasks in order. If the first succeeds, 60 | % succeed. if the first fails, run the second. 61 | % If it succeeds, succeed. If it fails, run the 62 | % third and so on. If the last fails, fail. 63 | % 64 | bt_operator(arg_list) --> [-?]. 65 | 66 | %@ probabiliy_selector 67 | % 68 | % attached to each task is a weight. 69 | % select randomly according to the weighting 70 | % among the tasks and 71 | % run it, terminating when it terminates. 72 | % If any task has no probability, it is given weight 1.0 73 | % 74 | bt_operator(prob_arg_list) --> [~?]. 75 | 76 | %@ parallel 77 | % 78 | % Start all tasks. 79 | % When one fails, terminate all others 80 | % and fail. 81 | % If all succeed, succeed 82 | % 83 | bt_operator(arg_list) --> [=>]. 84 | 85 | /******************************* 86 | * Sequencers 87 | *******************************/ 88 | 89 | %@ sequence 90 | % 91 | % At starting, start the leftmost task. 92 | % run the leftmost task to success, then 93 | % run the second task, and so on, and then succeed 94 | % If any task fails, fail 95 | % 96 | bt_operator(arg_list) --> [>>]. 97 | 98 | %@ scrambler 99 | % 100 | % Start the tasks in L to R fashion, 101 | % as for sequence, but randomly 102 | % permute the order prior to doing so 103 | % 104 | bt_operator(arg_list) --> [~>>]. 105 | 106 | %@ parallel 107 | % 108 | % Start all tasks. When any fail, 109 | % terminate the others and fail. 110 | % When the last succeeds, succeed. 111 | bt_operator(arg_list) --> [==>]. 112 | 113 | %@ timer 114 | % 115 | % the argument list is an alternating sequence 116 | % of times and tasks 117 | % If the list starts with a task, 118 | % Start the first task immediately. 119 | % Run until it succeeds, or the next listed time. 120 | % If it fails, fail. 121 | % If it is still running at the next listed time, interrupt 122 | % Then start the next task 123 | % If the list ends with a task, run this task to completion and 124 | % return it's completion status 125 | % If the list ends with a time and the final task is interruped, 126 | % return success 127 | % 128 | bt_operator(timed_arg_list) --> [**>]. 129 | 130 | /******************************* 131 | * Actions 132 | *******************************/ 133 | bt_operator(event) --> [&*]. % emit event and succeed 134 | bt_operator(atom) --> [&>>]. % broadcast message to prolog and succeed 135 | bt_operator(atom) --> [&<<]. % runs until it gets this message from prolog 136 | bt_operator(float) --> [&~]. % immediately succeed or fail, chance of success is the float 137 | bt_operator(context_node_pair) --> [^]. % runs as long as a node in another context is running, then does what the foreign node does. fails if node not running. 138 | bt_operator(atom) --> [^]. % as above, but for this context 139 | bt_operator(float) --> [wait]. % wait for duration arg and succeed 140 | bt_operator(float) --> [until]. % wait until clock time arg and succeed 141 | bt_operator(null) --> [reset_clock]. % set the clock time to 0 and succeed 142 | bt_operator(null) --> [true]. % succeed immediately 143 | bt_operator(null) --> [fail]. % fail immediately 144 | % Need an action that tests the blackboard value on each tick, fails 145 | % when it's conditional test fails 146 | % Need an action that sets a blackboard 147 | % Should we have a blackboard, or just the messages? 148 | % Yes, have a blackboard 149 | % Have an 'end' action that reports the context ended (and terminates 150 | % all running nodes) 151 | % Need a node that establishes a constraint, a la a flow language. 152 | bt_operator(expression) --> ['!']. 153 | 154 | /******************************* 155 | * Decorations 156 | *******************************/ 157 | decoration --> [true]. % run until argument terminates, then succeed 158 | decoration --> [fail]. % run until argument terminates, then fail 159 | decoration --> [while]. % repeatedly run argument until it fails, then succeed 160 | decoration --> [at], Time, [','], [run], {number(Time)}. % run 2nd arg at time specified by first arg 161 | decoration --> [stop, at], number(_Time). % interrupt task at clock time t 162 | decoration --> [repeat, until], Time, [',']. % repeatedly run until clock time t or failure, then interrupt and succeed 163 | decoration --> [start, until], Time, [',']. % repeatedly start until clock time t or it fails, let last iteration run to completion 164 | decoration --> [clock]. % establish a new clock context 165 | decoration --> event_on_start. % insert event on start 166 | decoration --> event_on_success. % insert event on success 167 | decoration --> event_on_failure. % insert event on failure 168 | decoration --> set_context. % run the tasks below in new context. clock is inherited from caller. 169 | % need decoration to set a new blackboard 170 | % need decoration that runs as long as the thing it calls, just for the 171 | % events 172 | 173 | /******************************* 174 | * smaller stuff 175 | *******************************/ 176 | 177 | arg_list --> 178 | arg_list_, 179 | ".". 180 | 181 | 182 | /* 183 | Notes from my scroungy piece of paper 184 | 185 | the system 'ticks' all together. All contexts get ticks 186 | 187 | There is a conversion factor for the system that converts 188 | our units to nanos 189 | There is a tick advance, in our units 190 | 191 | there is a blackboard. The blackboard space is per-context or global 192 | 193 | Nodes are not restartable - 194 | the second attempt to start fails. 195 | 196 | Args are interned at (node) start time. 197 | 198 | Args can be per-context overridden with some prolog predicate 199 | 200 | When you make syntax, have a way to make a 'lambda', an unnamed node, 201 | to make coding easier. Lambdas shouldn't generate events. 202 | (they don't have a name) 203 | internally we give them a name 204 | 205 | Arguments and children are different. 206 | 207 | segment_add_triple should be hookable so we can use this in other 208 | contexts. 209 | 210 | % Clock, Context, and TickVal will be b_setval'ed 211 | % 212 | 213 | Functional nodes assert a value as a probablistic function of other 214 | values. They are active when running. they run until terminated.. 215 | 216 | There are boundary condition nodes that fail when outside some domain. 217 | 218 | */ 219 | 220 | 221 | -------------------------------------------------------------------------------- /simgen/behavior_tree_parser.pl: -------------------------------------------------------------------------------- 1 | :- module(behavior_tree_syntax, [bt_dcg//1]). 2 | /** Concrete syntax parser for behavior trees 3 | 4 | */ 5 | :- multifile license:license/3. 6 | 7 | license:license(simularity, proprietary, 8 | [ comment('this program is the confidential property of Simularity, Inc. and must not be disclosed to any person without written permission of Simularity, Inc. All rights reserved.'), 9 | url('http://thunor.simularity.com/dokuwiki/doku.php?id=components:license') 10 | ]). 11 | 12 | :- license(simularity). 13 | 14 | :- use_module(library(dcg/basics)). 15 | :- use_module(simgen(print_system)). 16 | 17 | d(Format, Args) --> {bt_debug(bt(parser, info), Format, Args)}, !, []. 18 | 19 | bt_dcg(define_bt([':-'(true)])) --> eos. 20 | bt_dcg(define_bt([':-'(set_current_bt_module) | BT])) --> 21 | d('in bt_dcg', []), 22 | bt_(BT). 23 | 24 | bt_([BTStatement | BT]) --> 25 | d('in bt_', []), 26 | ws, 27 | bt_statement(BTStatement), 28 | d('got statement ~w', [BTStatement]), 29 | ws, 30 | bt_(BT). 31 | bt_([]) --> ws, eos,!. 32 | bt_([BT]) --> 33 | d('in bt_ error', []), 34 | ws, 35 | bt_error, 36 | d('got error', []), 37 | ws, 38 | bt_(BT). 39 | 40 | bt_error --> 41 | string(OopsCodes), 42 | ( "." ; eos), 43 | lazy_list_location(file(File, Line, Pos, _)), 44 | { atom_codes(AC, OopsCodes), 45 | bt_print_message(error, error(syntax_error(AC), 46 | context(err(AC:Line:Pos:File)))) 47 | }. 48 | 49 | ws --> ws_,!. 50 | ws_ --> eos. 51 | ws_ --> blanks, comment, ws_. 52 | ws_ --> blanks. 53 | 54 | % allow prolog style comments 55 | comment --> 56 | "%", 57 | string_without("\n", Body), 58 | "\n", 59 | d('{{{%~s}}}~n', [Body]). 60 | 61 | % allow C style comments 62 | comment --> 63 | "/*", 64 | c_body(Body), 65 | d('{{{/*~s*/}}}~n', [Body]). 66 | 67 | c_body([C|T]) --> 68 | [C], 69 | { C \= 0'* }, 70 | c_body(T). 71 | c_body([]) --> 72 | "*/". 73 | c_body([ 0'*, C | T]) --> 74 | "*", 75 | [C], 76 | { C \= 0'/ }, 77 | c_body(T). 78 | 79 | ... --> []. 80 | ... --> [_], ... . 81 | 82 | ...([]) --> []. 83 | ...([X|T]) --> [X], ...(T) . 84 | 85 | bt_statement(':-'(def_node(Head, Operator, Args, Children))) --> 86 | ws, 87 | d('in bt_statement', []), 88 | head(Head), 89 | d('got head ~w', [Head]), 90 | ws, 91 | bt_operator(Operator), 92 | d('got operator ~w', [Operator]), 93 | ws, 94 | bt_args(Operator, Args, Children), 95 | ws, 96 | ".". 97 | 98 | head(Head) --> an_atom(Head). 99 | 100 | 101 | bt_operator( ~? ) --> "~?". % random selector 102 | bt_operator( '!' ) --> "!". % pdq 103 | bt_operator( '?' ) --> "?". % check condition 104 | bt_operator( '-?' ) --> "-?". % wait condition 105 | bt_operator( set ) --> "set". % set condition 106 | bt_operator( clear ) --> "clear". % clear condition 107 | bt_operator( '->' ) --> "->". % sequence 108 | bt_operator( '~>' ) --> "~>". % random sequence 109 | bt_operator( '=>' ) --> "=>". % parallel sequence 110 | bt_operator( '=?' ) --> "=?". % parallel selector 111 | bt_operator( try ) --> "try". % try (always succeed) 112 | bt_operator( fail ) --> "fail". % fail (always fail) 113 | bt_operator( not ) --> "not". % not (fail if done, done if fail) 114 | bt_operator( dur ) --> "dur". % duration 115 | bt_operator( pin ) --> "pin". % emit pin event 116 | bt_operator( repeat(fail) ) --> "<>". % loop until failure 117 | bt_operator( repeat(done) ) --> "<-->". % loop until success 118 | bt_operator( attempt ) --> "attempt". % sequence until success 119 | 120 | bt_args( repeat(_), [], [Child]) --> a_child(Child). 121 | bt_args( pin, [], [Child]) --> a_child(Child). 122 | bt_args( dur, [Dur], []) --> 123 | ws, number(Dur). 124 | bt_args( try, [], [Child]) --> a_child(Child). 125 | bt_args( fail, [], [Child]) --> a_child(Child). 126 | bt_args( not, [], [Child]) --> a_child(Child). 127 | bt_args( '->', [], Children) --> nonempty_child_list(Children). 128 | bt_args( attempt, [], Children) --> nonempty_child_list(Children). 129 | bt_args( '~>', [], Children) --> nonempty_child_list(Children). 130 | bt_args( '=>', [], Children) --> nonempty_child_list(Children). 131 | bt_args( '=?', [], Children) --> nonempty_child_list(Children). 132 | bt_args( '?' , [Cond], []) --> an_atom(Cond). 133 | bt_args( '-?' , [Cond], []) --> an_atom(Cond). 134 | bt_args( set , [Cond], []) --> an_atom(Cond). 135 | bt_args( clear , [Cond], []) --> an_atom(Cond). 136 | bt_args( ~? , Args, Children) --> prob_list(Args, Children). 137 | bt_args( '!', [FirstTicks, RestTicks, Conds], []) --> 138 | d('in bt_args', []), 139 | continuous_calc_list(FirstTicks), 140 | d('Firstticks ~w', [FirstTicks]), 141 | ws, 142 | ";", 143 | ws, 144 | d('pre RestTicks', []), 145 | continuous_calc_list(RestTicks), 146 | d('got RestTicks ~w', [RestTicks]), 147 | ws, 148 | ";", 149 | ws, 150 | d('pre conds', []), 151 | conds(Conds), 152 | d('conds ~w', [Conds]). 153 | 154 | prob_list([Prob | Probs], [Child | Children]) --> 155 | prob(Prob, Child), 156 | ws, 157 | ",", 158 | ws, 159 | prob_list(Probs, Children). 160 | prob_list([Prob], [Child]) --> 161 | prob(Prob, Child). 162 | 163 | prob(Prob, Child) --> 164 | ws, 165 | number(Prob), 166 | ws, 167 | ":", 168 | ws, 169 | a_child(Child). 170 | prob(1.0, Child) --> 171 | ws, 172 | a_child(Child), 173 | ws. 174 | 175 | continuous_calc_list([Stmt | Rest]) --> 176 | statement(Stmt), 177 | ws, 178 | ",", 179 | ws, 180 | continuous_calc_list(Rest). 181 | continuous_calc_list([Stmt]) --> 182 | statement(Stmt). 183 | continuous_calc_list([]) --> ws. 184 | 185 | statement(':='(LVal, RVal)) --> 186 | an_atom(LVal), 187 | ws, 188 | ":=", 189 | ws, 190 | rval(RVal). 191 | 192 | statement('='(LVal, RVal)) --> 193 | an_atom(LVal), 194 | ws, 195 | "=", 196 | ws, 197 | rval(RVal). 198 | 199 | conds([Cond | Rest]) --> 200 | cond(Cond), 201 | ws, 202 | ",", 203 | ws, 204 | conds(Rest). 205 | conds([Cond]) --> 206 | cond(Cond). 207 | conds([]) --> ws. 208 | 209 | cond(Cond) --> 210 | ws, 211 | rval(Left), 212 | ws, 213 | cond_op(Op), 214 | ws, 215 | rval(Right), 216 | { Cond =.. [Op, Left, Right] }. 217 | 218 | cond_op( = ) --> "=". 219 | cond_op( = ) --> "==". 220 | cond_op( < ) --> "<". 221 | cond_op( > ) --> ">". 222 | cond_op( =< ) --> "<=". 223 | cond_op( =< ) --> "=<". 224 | cond_op( >= ) --> ">=". 225 | cond_op( >= ) --> "=>". 226 | cond_op( \= ) --> "!=". 227 | cond_op( \= ) --> "\\=". 228 | 229 | bt_op(20, '+' ) --> "+". 230 | bt_op(20, '-' ) --> "-". 231 | bt_op(10, '*' ) --> "*". 232 | bt_op(10, '/' ) --> "/". 233 | 234 | rval(Rval) --> 235 | term(Term), 236 | rest_of_rval(Term, Rval). 237 | 238 | rest_of_rval(In, Out) --> 239 | ws, 240 | bt_op(20, Op), 241 | ws, 242 | term(R), 243 | { New =.. [Op, In, R] }, 244 | rest_of_rval(New, Out). 245 | rest_of_rval(X, X) --> []. 246 | 247 | term(Term) --> 248 | cofactor(CoFactor), 249 | rest_of_term(CoFactor, Term). 250 | 251 | rest_of_term(In, Out) --> 252 | ws, 253 | bt_op(10, Op), 254 | ws, 255 | cofactor(CoFactor), 256 | { Term =.. [Op, In, CoFactor] }, 257 | rest_of_term(Term, Out). 258 | rest_of_term(X, X) --> []. 259 | 260 | cofactor('-'(CF)) --> 261 | ws, 262 | "-", 263 | ws, 264 | cofactor(CF). 265 | cofactor(Lval) --> 266 | ws, 267 | "(", 268 | ws, 269 | rval(Lval), 270 | ws, 271 | ")". 272 | cofactor(eval(Term)) --> 273 | ws, 274 | an_atom(Functor), 275 | { function(Functor, _) }, 276 | ws, 277 | "(", 278 | ws, 279 | rval_args(Args), 280 | { function(Functor, Arity), 281 | length(Args, Arity), 282 | Term =.. [Functor | Args] }, 283 | ws, 284 | ")". 285 | % treat empty arg list as special case to save sanity 286 | cofactor(eval(Functor)) --> 287 | ws, 288 | an_atom(Functor), 289 | { function(Functor, _) }, 290 | ws, 291 | "(", 292 | ws, 293 | ")", 294 | { function(Functor, 0) }. 295 | 296 | cofactor(var(X)) --> 297 | ws, 298 | an_atom(X). 299 | cofactor(const(X)) --> 300 | number(X). 301 | 302 | rval_args([Arg | Args]) --> 303 | ws, 304 | rval(Arg), 305 | ws, 306 | ",", 307 | ws, 308 | rval_args_rest(Args). 309 | rval_args([Arg]) --> ws, rval(Arg). 310 | rval_args([]) --> []. 311 | 312 | rval_args_rest([Arg | Args]) --> 313 | ws, 314 | rval(Arg), 315 | ws, 316 | ",", 317 | ws, 318 | rval_args_rest(Args). 319 | rval_args_rest([Arg]) --> rval(Arg). 320 | 321 | function( levy_flight, 3). 322 | function( wander, 4). 323 | function( clock, 0). 324 | 325 | an_atom(Atom) --> 326 | [X], 327 | { ground(X), code_type(X, lower) }, 328 | atom_codes(Codes), 329 | { atom_codes(Atom, [X | Codes]) }. 330 | 331 | atom_codes([X | Rest]) --> 332 | [X], 333 | { ground(X), 334 | code_type(X, csym) }, 335 | atom_codes(Rest). 336 | atom_codes([]) --> []. 337 | 338 | 339 | nonempty_child_list([Child | MoreChildren]) --> 340 | ws, 341 | a_child(Child), 342 | ws, 343 | ",", 344 | nonempty_child_list(MoreChildren). 345 | nonempty_child_list([Child]) --> ws, a_child(Child). 346 | 347 | 348 | a_child(Child) --> 349 | an_atom(Child). 350 | a_child(anon_node(Head, Operator, Args, Children)) --> 351 | { gensym(node, Head) }, 352 | "{", 353 | ws, 354 | bt_operator(Operator), 355 | ws, 356 | bt_args(Operator, Args, Children), 357 | ws, 358 | "}". 359 | 360 | -------------------------------------------------------------------------------- /simgen/bt_impl.pl: -------------------------------------------------------------------------------- 1 | :- module(bt_impl, [ 2 | bad_thing_happened/0, 3 | reset_nodes_for_module/1, 4 | set_current_bt_module/0, 5 | def_node/4, % node(+Head, +Oper, +Args, +Children) 6 | start_context/3, % start_context(+Root, +Context, +Time) 7 | % end_context/1, % end_context(+Context), 8 | start_simulation/4, % start_simulation(+StartTime, +TimeUnit, +TickLength, +External) 9 | end_simulation/0, 10 | % bt_time/1, % get the time of the global clock 11 | % bt_context_time/2, % get the time of the context clock 12 | check_nodes/0, % check_nodes 13 | make_cn/2, 14 | emit/1, 15 | named_parent_of/2 16 | ]). 17 | /** run time support for bt 18 | * 19 | * "If you have done something twice, you are likely to do it again." 20 | * 21 | * Brian Kernighan and Bob Pike 22 | * 23 | * Agent based version 24 | */ 25 | 26 | path_target. 27 | % This is required, needed for internal glue. 28 | % fix it so it points at your simgen directory 29 | % 30 | :- 31 | source_file(bt_impl:path_target, Path), 32 | directory_file_path(Dir, _, Path), 33 | absolute_file_name(Dir, AbsPath), % /SimGen/simgen 34 | atomic_list_concat( 35 | [ 36 | AbsPath, 37 | '/nodes'], SGP), 38 | asserta(user:file_search_path(nodes, SGP)). 39 | 40 | 41 | 42 | :- dynamic nodes_path/1, examples_path/1. 43 | 44 | :- absolute_file_name(simgen('nodes/'), Path), 45 | asserta(nodes_path(Path)). 46 | :- absolute_file_name(simgen('nodes/'), Path), 47 | asserta(nodes_path(Path)). 48 | 49 | user:file_search_path(nodes, Path) :- 50 | nodes_path(Path). 51 | 52 | user:file_search_path(examples, 'examples/'). 53 | 54 | :- use_module(simgen(clocks)). 55 | :- use_module(simgen(valuator)). 56 | :- use_module(simgen(print_system)). 57 | :- use_module(nodes(random_selector)). 58 | :- use_module(nodes(pdq)). 59 | :- use_module(nodes(check_guard)). 60 | :- use_module(nodes(wait_guard)). 61 | :- use_module(nodes(set_guard)). 62 | :- use_module(nodes(clear_guard)). 63 | :- use_module(nodes(sequence)). 64 | :- use_module(nodes(random_sequence)). 65 | :- use_module(nodes(try_decorator)). 66 | :- use_module(nodes(fail_decorator)). 67 | :- use_module(nodes(not_decorator)). 68 | :- use_module(nodes(dur)). 69 | :- use_module(nodes(pin_decorator)). 70 | :- use_module(nodes(parallel)). 71 | :- use_module(nodes(paraselect)). 72 | :- use_module(nodes(repeat_decorator)). 73 | :- use_module(nodes(attempt)). 74 | 75 | /******************************* 76 | * Compilation support * 77 | *******************************/ 78 | 79 | :- dynamic node_/5. % node_(Module, Head, Operator, Args, Children) 80 | 81 | :- module_transparent set_current_bt_module/0. 82 | 83 | %! set_current_bt_module is det 84 | % 85 | % module_transparent predicate that 86 | % records the calling module. 87 | % def_node/4 uses this info 88 | % 89 | % Users usually won't call this. 90 | % It's for the compiler 91 | % 92 | set_current_bt_module :- 93 | bt_debug(bt(compile, node), 'in set_current_bt_module\n', []), 94 | context_module(Module), 95 | nb_setval(current_bt_module, Module). 96 | 97 | %! reset_nodes_for_module(+Module:atom) is det 98 | % 99 | % reset ALL nodes - for the moment we dont have modules 100 | % 101 | % Users usually won't call this, it's for the compiler 102 | % 103 | reset_nodes_for_module(_Module) :- 104 | retractall(bt_impl:node_(_, _, _, _, _)). 105 | 106 | %! def_node(+Head:atom, +Oper:atom, +Args:args, +Children:list) is det 107 | % 108 | % Define a node. 109 | % A _node_ is a cell in an behavior tree 110 | % 111 | % @arg Head the name of the node, an atom 112 | % @arg Oper the name of the operation type for the node 113 | % @arg Args a single item or list of items. Meaning depends on 114 | % Oper 115 | % @arg Children a list of 116 | % @tbd change this to a term_expansion 117 | % 118 | % Users would usually not call this directly. def_node/4 calls are 119 | % generated by use_bt 120 | % 121 | def_node(Head, _, _, _) :- 122 | node_(_, Head, _, _, _), 123 | gtrace, 124 | !, 125 | line_count(current_input, Line), 126 | bt_debug(error(compile, multiply_defined_node), 127 | '~w is multiply defined on line ~d.', [Head, Line]). 128 | def_node(Head, Oper, Args, Children) :- 129 | \+ node_(_, Head, _, _, _), 130 | bt_debug(bt(compile, node), 'node ~w ~w ~w ~w~n', 131 | [Head, Oper, Args, Children]), 132 | nb_getval(current_bt_module, Module), 133 | assertz(node_(Module, Head, Oper, Args, Children)), 134 | bt_debug(bt(compile, node), 'asserted~n', []). 135 | 136 | %! check_nodes is semidet 137 | % 138 | % Succeeds if all referenced nodes are defined 139 | % emits messages if not. 140 | % 141 | % Called at end of module compilation 142 | % 143 | check_nodes :- 144 | \+ node_(_, _, _, _, _), 145 | !. 146 | check_nodes :- 147 | setof(Node, a_used_node(Node), Used), 148 | maplist(check_def, Used). 149 | 150 | a_used_node(Node) :- 151 | node_(_, _, _, _, Children), 152 | member(Node, Children). 153 | 154 | check_def(Node) :- node_(_, Node, _, _, _). 155 | check_def(Node) :- 156 | ( setof(Head, M^O^A^(node_(M, Head, O, A, Children), member(Node, Children)), Heads) 157 | ; Heads = []), 158 | maplist(print_no_def(Node), Heads). 159 | 160 | print_no_def(Node, Head) :- 161 | format(atom(Msg), 'Node ~w is used in node ~w but is not defined', [Node, Head]), 162 | bt_print_message(error, error(existance_error(procedure, Node), 163 | context(node:Head, Msg))). 164 | 165 | %! named_parent_of(-Parent:atom, +A:atom) is nondet 166 | % 167 | % @arg Parent one of the closest enclosing parents not named nodeDDDD 168 | % @arg A is the descendent 169 | % 170 | named_parent_of(Head, A) :- 171 | atom(A), 172 | node_(_Module, Head, _Operator, _Args, Children), 173 | memberchk(A, Children), 174 | \+ atom_concat(node, _, Head). 175 | named_parent_of(Ancestor, A) :- 176 | atom(A), 177 | node_(_Module, Head, _Operator, _Args, Children), 178 | memberchk(A, Children), 179 | atom_concat(node, _, Head), 180 | named_parent_of(Ancestor, Head). 181 | 182 | 183 | /******************************* 184 | * User API * 185 | *******************************/ 186 | 187 | %! start_simulation( 188 | %! +StartTime:number, +TimeUnit:number, +TickLength:number, 189 | %! +External:term) is det 190 | % 191 | % run a new simulation 192 | % 193 | % @arg StartTime time in user units to run the first tick at 194 | % @arg TimeUnit how long is one user unit in nanos? 195 | % @arg TickLength how long is a tick in user units? 196 | % @arg external data for use by event listeners 197 | % 198 | start_simulation(StartTime, TimeUnit, TickLength, External) :- 199 | unlisten_all_cn, 200 | abolish_clocks(_), 201 | clock_units(TimeUnit, TickLength), 202 | new_clock(simgen, StartTime), 203 | empty_queues, 204 | broadcast(simulation_starting), 205 | do_ticks(External), 206 | !. % make it steadfast 207 | 208 | %! end_simulation is det 209 | % 210 | % Stop a running simulation at the 211 | % end of the tick 212 | % 213 | % Must be called from the simulation thread 214 | % (usually a message listener) 215 | % 216 | end_simulation :- 217 | thread_send_message(simgen, end_simulation). 218 | 219 | start_context(Root, Context, Time) :- 220 | new_clock(Context, Time), 221 | % direct call above 222 | % needed to cure timing bug where we broadcast values on 223 | % nonexistant clock 224 | thread_send_message(simgen, start_node(Context-Root)). 225 | 226 | 227 | /******************************* 228 | * Support for User API * 229 | *******************************/ 230 | 231 | /******************************* 232 | * Simulator * 233 | *******************************/ 234 | 235 | /* 236 | * See the document agentbased.md that should have come 237 | * with this file for information about the design of this code. 238 | */ 239 | 240 | /* 241 | * Queue to send messages into the simulator during simulation 242 | */ 243 | :- initialization ( message_queue_property(_, alias(simgen)), 244 | message_queue_destroy(simgen) 245 | ; 246 | true 247 | ), 248 | message_queue_create(_, [alias(simgen)]). 249 | 250 | /* 251 | Queue to buffer messages to make agent-like behavior 252 | */ 253 | 254 | :- initialization ( message_queue_property(_, alias(u)), 255 | message_queue_destroy(u) 256 | ; 257 | true 258 | ), 259 | message_queue_create(_, [alias(u)]). 260 | 261 | empty_queues :- 262 | thread_get_message(u, _, [timeout(0)]), 263 | empty_queues. 264 | empty_queues :- 265 | thread_get_message(simgen, _, [timeout(0)]), 266 | empty_queues. 267 | empty_queues. 268 | 269 | do_ticks(_External) :- 270 | end_simulation_message_exists. 271 | do_ticks(External) :- 272 | det(get_clock(simgen, Time)), 273 | % this is for external prolog 274 | ignore(broadcast_request(tick(External, Time, NewExtern))), 275 | % this is *from* external prolog 276 | det(empty_simgen_queue), 277 | det(cycle_values), 278 | det(do_tick_start), 279 | det(empty_u_queue), 280 | det(valuator), 281 | det(do_tick_end), 282 | det(empty_u_queue), 283 | det(broadcast_values), 284 | det(update_clocks), 285 | det(show_debug), 286 | do_ticks(NewExtern). 287 | do_ticks(_) :- 288 | bt_debug(error(simulation, simulation_failed), 289 | 'simulation failed', []), 290 | gtrace. 291 | 292 | det(Goal) :- 293 | ( call(Goal) 294 | ; gtrace 295 | ). 296 | 297 | empty_simgen_queue :- 298 | thread_get_message(simgen, new_clock(Name, Start), [timeout(0)]), 299 | !, 300 | new_clock(Name, Start), 301 | empty_simgen_queue. 302 | empty_simgen_queue :- 303 | thread_get_message(simgen, start_node(Context-Root), [timeout(0)]), 304 | !, 305 | make_cn(Context-Root, Context-'$none$'), 306 | empty_simgen_queue. 307 | empty_simgen_queue. 308 | 309 | do_tick_start :- 310 | broadcast(tick_start). 311 | 312 | do_tick_end :- 313 | broadcast(tick_end). 314 | 315 | empty_u_queue :- 316 | thread_get_message(u, Msg, [timeout(0)]), 317 | !, 318 | broadcast(Msg), 319 | empty_u_queue. 320 | empty_u_queue. 321 | 322 | end_simulation_message_exists :- 323 | thread_get_message(simgen, end_simulation, [timeout(0)]). 324 | 325 | :- multifile bt_impl:make_cn_impl/3. 326 | 327 | make_cn(C-N, CParent-NParent) :- 328 | node_(_M, N, O, _A, _C), 329 | make_cn_impl(O, C-N, CParent-NParent). 330 | 331 | emit(Msg) :- 332 | thread_send_message(u, Msg). 333 | 334 | unlisten_all_cn :- 335 | forall(listening(C-N, Templ, Goal), unlisten(C-N, Templ, Goal)). 336 | 337 | /******************************* 338 | * Dev Support 339 | *******************************/ 340 | 341 | % whenever we're in trouble we call this, so there's a convenient place 342 | % to stick a gtrace 343 | bad_thing_happened :- 344 | end_simulation. 345 | 346 | show_debug :- 347 | get_clock(simgen, Time), 348 | bt_debug(bt(ticks, tick), '@@@@@ Tick! ~w', [Time]). 349 | 350 | -------------------------------------------------------------------------------- /docs/simai_world.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | SimGen Lib 50 | 51 | 52 | 53 | 54 | 55 | 56 | prolog shell 57 | 58 | 59 | 60 | 61 | 62 | 63 | .simai file 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | BT Language 72 | File 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | segment 81 | 82 | 83 | 84 | 85 | 86 | 87 | sim_interact 88 | 89 | 90 | 91 | 92 | 93 | 94 | FI Explorer 95 | 96 | 97 | 98 | 99 | 100 | 101 | events 102 | 103 | 104 | calls 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /docs/simai_world.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | SimGen Lib 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | prolog shell 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | .simai file 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | BT Language 72 | File 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | segment 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | sim_interact 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | FI Explorer 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | calls 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | events 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /oldstuff/talespin.pl: -------------------------------------------------------------------------------- 1 | :- module(talespin, [talespin/0]). 2 | 3 | % File: talespin.pl 4 | % Author: Peter Clark 5 | % Date: Jan 1999 6 | % Purpose: Simple and highly improvised reconstruction of Meehan's TALE-SPIN 7 | % story generator, here applied to aviation incident "stories". This 8 | % reconstruction undoubtedly misses out a lot of Meehan's program, and 9 | % also adds in new parts/approaches that Meehan didn't originally use. 10 | % 11 | % AO - 11/2016 - added comments and cleaned up code, added more events 12 | % 13 | 14 | %! talespin is nondet 15 | % 16 | % prints a story on each backtracking 17 | % 18 | talespin :- 19 | repeat, 20 | talespin_. 21 | 22 | talespin_ :- 23 | initial_situation(InitialSituation), 24 | initial_goal(InitialSituation, InitialGoals), 25 | make_best_plan(InitialGoals, 26 | InitialSituation, 27 | InitialPlan), 28 | Prob = 0.3, % Probability of incident occurring at a particular step 29 | execute_plan(InitialPlan, InitialSituation, InitialGoals, 30 | Prob, StoryActions, _StorySituations), 31 | write('Once upon a time...'), nl, 32 | anglify(StoryActions, StoryText), 33 | lwrite(StoryText). 34 | 35 | % ====================================================================== 36 | % THE STRIPS-LIKE PLANNER 37 | % ====================================================================== 38 | % make_plan/3: Simple backward-chaining planner, without a protected goal list. 39 | 40 | make_best_plan(Goals, Situation, BestPlan) :- 41 | findall(Quality-Plan, 42 | ( make_plan(Goals,Situation,Plan), 43 | plan_quality(Plan,Quality) ), 44 | RankedPlans), 45 | sort(RankedPlans, OrderedPlans), 46 | last(OrderedPlans, _-BestPlan). 47 | 48 | plan_quality(Plan, Quality) :- 49 | length(Plan, Length), % lose 10 points per step 50 | ( member(evacuate(_),Plan) -> Cost = 1 ; Cost = 0 ), % lose 1 for evacuating 51 | Quality is 100 - Length*10 - Cost. 52 | 53 | % ---------- 54 | 55 | make_plan(Goals, Situation, Plan) :- 56 | make_plan(Goals, 57 | Situation, 58 | [], % state stack 59 | _FinalSituation, 60 | Plan). 61 | 62 | % DONE TO HERE 63 | % % trying to change goals to a list of goals, and 64 | % making the stack be that of situations. 65 | 66 | %! make_plan(+Goals:list, +CurrentSituation:list, +StateStack:list, 67 | %! -EndSituation:list, -Plan:list) is nondet 68 | % 69 | % Make a plan to achieve the goals in Goals 70 | % 71 | % @arg Goals list of goals 72 | % @arg CurrentSituation current situation, a list of items 73 | % @arg StateStack list of previous situations, (avoid looping) 74 | % @arg EndSituation final situation, list of items 75 | % @arg Plan list of actions 76 | make_plan(Goals, Situation, _StateStack, Situation, []) :- 77 | satisfieds(Goals, Situation). 78 | make_plan(Goals, Situation, GoalStack, NewSituation, Actions) :- 79 | member(AGoal, Goals), % cant do this 80 | \+ satisfieds(Goals, Situation), 81 | \+ member(Goals, GoalStack), % avoid looping 82 | event_definition(action, Action, PCs, Dels, Adds), 83 | achieves_some(Adds, Goals), 84 | % DONE TO HERE 85 | make_plans(PCs, Situation, [Goal|GoalStack], MidSituation, PreActions), 86 | apply_effects(Dels, Adds, MidSituation, NewSituation), 87 | append(PreActions, [Action], Actions). 88 | 89 | make_plans([], Situation, _, Situation, []). 90 | make_plans([Goal|Goals], Situation, GoalStack, NewSituation, Actions) :- 91 | make_plan(Goal, Situation, GoalStack, MidSituation, FirstActions), 92 | make_plans(Goals, MidSituation, GoalStack, NewSituation, RestActions), 93 | append(FirstActions, RestActions, Actions). 94 | 95 | achieves_some(Adds, Goals) :- 96 | member(G, Goals), 97 | achieves(_, _, Adds, G). 98 | 99 | achieves(_, _, Adds, Goal) :- % Goal = effect 100 | member(Goal, Adds). 101 | achieves(_, _, Adds, Goal) :- % Goal = ramification of effect 102 | rule(( Goal :- Facts )), 103 | subset(Facts, Adds). 104 | 105 | % ====================================================================== 106 | % THE SIMULATOR 107 | % This is similar to the planner, *except* it will also throw a spanner 108 | % in the works (ie. a happening), requiring replanning. 109 | % ====================================================================== 110 | % Final clause - we're done 111 | execute_plan([], FinalSituation, _, _, [], [FinalSituation]) :- 112 | !. 113 | 114 | % 'happening' (incident) happens 115 | execute_plan(_Actions, Sitn, Goal, P, [Happening|NextActions], [Sitn|NextSitns]) :- 116 | maybe(P), % incident happens!! Abandon old Actions and Goal... 117 | !, 118 | findall(Happening, 119 | ( event_definition(happening,Happening,PCs,_,_), 120 | satisfieds(PCs,Sitn) ), % is physically feasible 121 | Happenings), 122 | rnd_member(Happening, Happenings), % choose a random happening 123 | do_event(Happening, Sitn, NewSitn), 124 | revise_goal(NewSitn, Goal, NewGoal), 125 | make_best_plan(NewGoal, NewSitn, NewActions), 126 | execute_plan(NewActions, NewSitn, NewGoal, 0, NextActions, NextSitns). 127 | 128 | % normal event happens 129 | execute_plan([Action|Actions], Sitn, Goal, P, [Action|NextActions], [Sitn|NextSitns]) :- 130 | do_event(Action, Sitn, NewSitn), 131 | execute_plan(Actions, NewSitn, Goal, P, NextActions, NextSitns). 132 | 133 | do_event(Event, Situation, NewSituation) :- 134 | event_definition(_, Event, _PCs, Dels, Adds), 135 | apply_effects(Dels, Adds, Situation, NewSituation). 136 | 137 | % Do the deletes and adds as appropriate 138 | apply_effects(Dels, Adds, Situation, NewSituation) :- 139 | removes(Dels, Situation, MidSituation), 140 | append(Adds, MidSituation, NewSituation). 141 | 142 | % ====================================================================== 143 | % OTHER UTILITIES 144 | % ====================================================================== 145 | 146 | satisfieds([], _). 147 | satisfieds([F|Fs], S) :- 148 | satisfied(F, S), 149 | satisfieds(Fs, S). 150 | 151 | satisfied(Fact, Situation) :- 152 | member(Fact, Situation). 153 | satisfied(Fact, Situation) :- 154 | rule((Fact :- Facts)), % Fact is a ramification of the world 155 | satisfieds(Facts, Situation). 156 | 157 | % ---------- writing... 158 | 159 | lwrite([]). 160 | lwrite([X|Xs]) :- write(' '), lwrite2(X), nl, lwrite(Xs). 161 | 162 | lwrite2([]). 163 | lwrite2([BitX|BitXs]) :- !, write(BitX), lwrite2(BitXs). 164 | lwrite2(X) :- write(X). 165 | 166 | anglify([], []). 167 | anglify([Event|Events], [English|Englishs]) :- 168 | event_english(Event, English), 169 | anglify(Events, Englishs). 170 | 171 | % ====================================================================== 172 | % GENERAL UTILITIES 173 | % ====================================================================== 174 | 175 | removes([], L, L). 176 | removes([R|Rs], L, NewL) :- 177 | remove(R, L, MidL), 178 | removes(Rs, MidL, NewL). 179 | 180 | remove(A, [A|B], B). 181 | remove(A, [C|B], [C|NewB]) :- 182 | remove(A, B, NewB). 183 | 184 | subset([], _). 185 | subset([X|Xs], Ys) :- remove(X, Ys, RestYs), subset(Xs, RestYs). 186 | 187 | nmember(Elem, List, N) :- 188 | nmember(Elem, List, 1, N). 189 | 190 | nmember(Elem, [Elem|_], N, N). 191 | nmember(Elem, [_|List], NSoFar, N) :- 192 | NewN is NSoFar + 1, 193 | nmember(Elem, List, NewN, N). 194 | 195 | % ---------- Randomization utilities 196 | 197 | :- dynamic lastrnd/1. 198 | lastrnd(0). 199 | 200 | maybe(P) :- random_float < P, !. % succeed with probability P 201 | 202 | rnd_member(X, Xs) :- 203 | length(Xs, L), 204 | R is random_float, 205 | N is integer(R*L) + 1, 206 | nmember(X, Xs, N), !. 207 | 208 | % ====================================================================== 209 | % THE FLIGHT INCIDENT KNOWLEDGE BASE 210 | % ====================================================================== 211 | 212 | event_definition(Type, Event, PCs, Adds, Dels) :- ed(Type, Event, PCs, Adds, Dels, _). 213 | event_english(Event, English) :- ed(_, Event, _, _, _, English). 214 | 215 | % in Seattle, scheduled for Dallas 216 | initial_situation([ plocation(passengers1, gate(seattle)), 217 | alocation(airplane1, gate(seattle)), 218 | flight_path(seattle, chicago), 219 | flight_path(chicago, dallas), 220 | airplane(airplane1), 221 | passengers(passengers1), 222 | blocation(bags1, gate(seattle))]). 223 | 224 | initial_goal(Situation, [plocation(passengers1, gate(dallas)), blocation(bags1, gate(dallas))]) :- 225 | member(alocation(airplane1, gate(Loc)), Situation), 226 | Loc \= dallas. 227 | 228 | % ---------- Routine actions... ---------- 229 | 230 | ed(action, load_bags(Bags, Airplane), 231 | [blocation(Bags, gate(Airport)), alocation(Airplane, gate(Airport))], 232 | [blocation(Bags, gate(Airport))], 233 | [bags_on_board(Airplane, Bags)], 234 | 'The bags were loaded'). 235 | 236 | ed(action, unload_bags(Bags, Airplane), 237 | [bags_on_board(Airplane, Bags), alocation(Airplane, gate(Airport))], 238 | [bags_on_board(Airplane, Bags)], 239 | [blocation(Bags, gate(Airport))], 240 | 'The bags were unloaded'). 241 | 242 | ed(action, load(Passengers,Airplane), 243 | /*pcs*/ [plocation(Passengers,gate(Airport)),alocation(Airplane,gate(Airport))], 244 | /*del*/ [plocation(Passengers,gate(Airport))], 245 | /*add*/ [contains(Airplane,Passengers)], 246 | /*txt*/ 'The passengers boarded the plane.' ). 247 | 248 | ed(action, taxi_to_runway(Airplane), 249 | /*pcs*/ [alocation(Airplane,gate(Airport))], 250 | /*del*/ [alocation(Airplane,gate(Airport))], 251 | /*add*/ [alocation(Airplane,runway(Airport))], 252 | /*txt*/ 'The plane taxiied to the runway.' ). 253 | 254 | ed(action, take_off(Airplane,Airport), 255 | /*pcs*/ [alocation(Airplane,runway(Airport))], 256 | /*del*/ [alocation(Airplane,runway(Airport))], 257 | /*add*/ [alocation(Airplane,near(Airport))], 258 | /*txt*/ ['The plane took off from ',Airport,'.']). 259 | 260 | ed(action, cruise(Airplane,Airport1,Airport2), 261 | /*pcs*/ [flight_path(Airport1,Airport2),alocation(Airplane,near(Airport1))], 262 | /*del*/ [alocation(Airplane,near(Airport1))], 263 | /*add*/ [alocation(Airplane,near(Airport2))], 264 | /*txt*/ ['The plane cruised towards ',Airport2,'.']). 265 | 266 | ed(action, land(Airplane,Airport2), 267 | /*pcs*/ [alocation(Airplane,near(Airport2))], 268 | /*del*/ [alocation(Airplane,near(Airport2))], 269 | /*add*/ [alocation(Airplane,runway(Airport2))], 270 | /*txt*/ ['The plane landed at ',Airport2,'.']). 271 | 272 | ed(action, taxi_to_gate(Airplane), 273 | /*pcs*/ [alocation(Airplane,runway(Airport))], 274 | /*del*/ [alocation(Airplane,runway(Airport))], 275 | /*add*/ [alocation(Airplane,gate(Airport))], 276 | /*txt*/ 'The plane taxiied to the gate.' ). 277 | 278 | ed(action, unload(Passengers,Airplane), 279 | /*pcs*/ [contains(Airplane,Passengers),alocation(Airplane,gate(Airport))], 280 | /*del*/ [contains(Airplane,Passengers)], 281 | /*add*/ [plocation(Passengers,gate(Airport))], 282 | /*txt*/ 'The passengers disembarked.' ). 283 | 284 | % ---------- Emergency actions... ---------- 285 | 286 | ed(action, evacuate(Airplane), 287 | /*pcs*/ [a_on_ground(Airplane),alocation(Airplane,Loc),contains(Airplane,Passengers)], 288 | /*del*/ [contains(Airplane,Passengers)], 289 | /*add*/ [plocation(Passengers,Loc)], 290 | /*txt*/ 'The passengers were evacuated from the plane.' ). 291 | 292 | ed(action, emergency_landing(Airplane), 293 | /*pcs*/ [alocation(Airplane,near(Airport2))], 294 | /*del*/ [alocation(Airplane,near(Airport2))], 295 | /*add*/ [alocation(Airplane,on_ground_near(Airport2))], 296 | /*txt*/ ['The pilot made an emergency landing near ',Airport2,'.']). 297 | 298 | ed(action, medical_help(Passengers), 299 | /*pcs*/ [plocation(Passengers, gate(_))], % any gate 300 | /*del*/ [], 301 | /*add*/ [medical_help(Passengers)], 302 | /*txt*/ 'Medical help was provided.' ). 303 | 304 | % ---------- Possible happenings... ---------- 305 | 306 | ed(happening, fire(engine), 307 | /*pcs*/ [], % can happen anywhere 308 | /*del*/ [], 309 | /*add*/ [on_fire(engine)], 310 | /*txt*/ 'The engine caught fire.' ). 311 | 312 | % ---------- Possible happenings... ---------- 313 | 314 | ed(happening, ill_passenger, 315 | /*pcs*/ [contains(Airplane,Passengers),passengers(Passengers),airplane(Airplane)], 316 | /*del*/ [], 317 | /*add*/ [ill_passenger], 318 | /*txt*/ 'A passenger became very ill.' ). 319 | 320 | ed(happening, bag_left, 321 | /*pcs*/ [alocation(Airplane, gate(_) 322 | 323 | % Ramifications of facts about the world... 324 | rule(( a_on_ground(Airplane) :- [alocation(Airplane,gate(_))] )). 325 | rule(( a_on_ground(Airplane) :- [alocation(Airplane,runway(_))] )). 326 | rule(( a_on_ground(Airplane) :- [alocation(Airplane,on_ground_near(_))] )). 327 | rule(( p_on_ground(Passengers) :- [plocation(Passengers,gate(_))] )). 328 | rule(( p_on_ground(Passengers) :- [plocation(Passengers,runway(_))] )). 329 | rule(( p_on_ground(Passengers) :- [plocation(Passengers,on_ground_near(_))] )). 330 | 331 | % Rules for revising the goal 332 | % TODO goals are now lists 333 | revise_goal(Situation, plocation(Passengers,_), Goal) :- % If the engine's on fire, 334 | memberchk(on_fire(engine), Situation), !, % get to the ground asap! 335 | Goal = p_on_ground(Passengers). 336 | revise_goal(Situation, plocation(Passengers,_), Goal) :- % If a passenger's ill, 337 | memberchk(ill_passenger, Situation), !, % get to a gate somewhere. 338 | Goal = medical_help(Passengers). 339 | revise_goal(_Situation, Goal, Goal). 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimGen 2 | 3 | SimGen is a simulation language, originally created by Simularity, Inc. 4 | 5 | In May 2019 Simularity released SimGen under the terms of the BSD 3-clause license. 6 | 7 | # Contents 8 | 9 | 1. What is SimGen? 10 | 2. Why do this? 11 | 3. The lives of behaviors 12 | 4. The BT language 13 | 5. Events - interacting with Prolog 14 | 15 | # 1. What is SimGen? 16 | 17 | SimGen is a library for SWI-Prolog that runs _simulations_ expressed in the **BT** language. 18 | 19 | **BT** is a declarative language based on _behavior trees_. You describe how things behave by combining primitive behavior elements, nodes, into descriptions of _behavior_. 20 | 21 | So, what is behavior? 22 | 23 | A behavior is a gerund, a verbal noun. And much of human thinking is in terms of gerunds. "We'll all go eat and then see a movie, then Lisa will drive Betty home". Three behaviors (eat, see movie, drive home), in sequence. 24 | 25 | These involve some different 'objects' - the complete group of people, Lisa, Betty, the movie theater, and so on, but the core of what's happening is in the behaviors. BT is about _behaviors_.The 'things' of BT are behaviors, rather than the _objects_ of an OO language. 26 | 27 | So a play, a revolution, or an airplane flight are behaviors. A play involves the audience buying tickets, being seated, watching the actors in a series of acts, applauding, and leaving the theater. That's a play. 28 | If the audience stays home and the actors sit quietly in the theater seats for an hour while the director burns the sets, then the stage hands applaud, and the curtain opens, we don't really have a play. 29 | 30 | Behaviors are constructed from primitive behaviors we call _nodes_. BT provides a limited number of _types_ of nodes. 31 | 32 | Each type of node provides some way of combining _child_ behaviors. For example, the _sequence_ node combines its' children by running them one after the other. 33 | 34 | So if we already have `all_go_eat`, `see_movie`, and `lisa_drives_betty_home` nodes, we can make `evening_out` by using the sequence operator. 35 | 36 | ```` 37 | evening_out -> 38 | all_go_eat, 39 | see_movie, 40 | lisa_drives_betty_home. 41 | ```` 42 | 43 | We can talk about abstract behaviors that can be instantiated. "Driving" is abstract behavior, 44 | while "Bob driving his 1923 Roadster down route 22 right now" is an instantiation of that behavior. 45 | 46 | A whole play is a behavior. Seating the audience is a behavior. Since one part of a play is seating the audience, `seat_audience` is a _child_ of `perform_play`. And `seat_audience` might have behaviors for ushers helping people find their seats, a concession stand selling goodies, reminder to turn off cell phones, and so on. 47 | 48 | An abstract behavior might occur in several settings. For example, `seat_audience` could also be a sub-behavior of a concert or movie. Since several behaviors might have a similar sub-behavior, abstract behaviors form a **directed acyclic graph (DAG)**. 49 | 50 | Probably not all of that graph is happening at once. Our party isn't both eating dinner and seeing the movie at 7:42PM, though they might be eating popcorn and watching the movie at the same time. So the graph of currently active nodes is a **subgraph** of the abstract behavior DAG. 51 | 52 | When an actual, specific audience is seated, they have come to the theater expecting to see _Hamlet_, not to hear _Pink Floyd_. Even though `seat_audience` abstractly has multiple parents (a concert, a movie, a play), this audience is here for _Hamlet_. So each concrete, active node has a single parent. And hence the concrete graph of active nodes would naturally form a **tree**. 53 | 54 | However, we can externally start additional nodes from Prolog, and _contexts_ can interact. So there can be multiple root nodes running. And this makes the set of running nodes a **forest**. 55 | 56 | ## Node Life Cycle 57 | 58 | A node is started, runs, and then stops in one of 3 ways: 59 | 60 | * success - the node completed it's task normally 61 | * failure - the node wasn't able to complete it's task 62 | * termination - when a parent node stops, all running child nodes terminate 63 | 64 | When you shut off a car, a lot of things stop moving - the fuel pump stops pumping, the radio stops playing, the crankshaft stops rotating... Stopping the 'run car' action terminates all these lesser actions. 65 | 66 | SimGen keeps the running nodes a **forest** by terminating all running children under a parent when the parent stops. 67 | 68 | What happens when a second source tries to start a node that's already running? Nothing. There is _no restart_ in SimGen. When A node ends, all parents are notified. However, only the parent that actually spawned the node will terminate the node when it dies. Note that this can 'hang' a second parent. Generally, multi-parenting is a minefield. It's a good rule to only multiparent if you're confident only one will actually try to instantiate the node. 69 | 70 | ## Variables and Conditions 71 | 72 | SimGen provides _variables_, which are floats, and _conditions_, which are booleans. 73 | 74 | SimGen variables and conditions need not be declared. All are **per context** scope. 75 | 76 | In the future we want to move SimGen towards making all variables 'understand' partial differential equations, known as PDQ nodes, so that most PDQ nodes don't need to explicitly be simulated. 77 | 78 | ## Contexts 79 | 80 | All theaters operate in more or less the same way, but we might have several theaters that each have their own specific values, and interact. We call these _contexts_. 81 | 82 | Note that a context is not an object, but rather a whole copy of the 'world'. 83 | 84 | Suppose we want to model a large boiler fed by four pumps. We could have a context for the boiler itself and most other equipment, and a context for each pump. 85 | 86 | In SimGen all variables are **context scope**. 87 | 88 | Into the simulation you add contexts. For our play example, we might have multiple theaters, putting on 89 | different plays, but generally doing the same thing. When a theater actually opens, we start a context. 90 | 91 | A context is a set of _current values_, _current conditions_, a _time_ since the context started, and a _set of running nodes_. 92 | 93 | When we start the context we give the name of a single node. 94 | 95 | Inter-context interaction isn't implemented yet. When it is, it will be possible to run a node in another context. 96 | 97 | ## Using SimGen 98 | 99 | You supply a set of definitions of how some things behave (abstract behaviors), in a **BT** language file with `.bt` extension. 100 | The `.bt` file can be loaded with `use_bt/1`. 101 | 102 | You then create a _simulation_ using `start_simulation/4`. 103 | 104 | Into the simulation you add contexts with `start_context/3`. 105 | 106 | Call `end_simulation/0` to end the simulation. 107 | 108 | 109 | ## Prolog Interaction 110 | 111 | SimGen depends heavily on `library(broadcast)`. Much of the interaction that occurs in SimGen is facilitated by registering _listeners_ using `library(broadcast)`. 112 | 113 | Messages of the form `tick(Extern, Tick, NewExtern)` occur each tick. 114 | 115 | When the simulation is started, an Extern value is passed. This is external state available for Prolog. 116 | 117 | When a tick occurs, the tick listener binds the new value of Extern to NewExtern. 118 | 119 | When a node starts, a `starting(Context-Name)` message is emitted. 120 | 121 | When a node stops, a `stopped(Context-Name, Reason)` message is emitted. 122 | 123 | `Reason` will have one of the following values: 124 | 125 | * `done` the node completed successfully 126 | * `fail` the node failed 127 | * `terminated` the node was terminated by a higher node 128 | 129 | When the `pin` node is used, `pin_drop(Context, Time, event)` and `pin_drop(Context, Time, '-event')` events are broadcast. 130 | 131 | Whenever a value is changed, `reading(Time, Module, Context, Name, Value)` events are emitted. 132 | 133 | See `getval/2` and `setval/3` in module `valuator` to read/change values from Prolog. 134 | 135 | See `set_guard/2`, `clear_guard/2`, and `guard/2` in module `guard_manager` to read/change conditions from prolog. 136 | 137 | A useful idiom 138 | 139 | ```` 140 | wait_for_prolog => 141 | {clear my_condition}, 142 | {-? my_condition}. 143 | 144 | ... in prolog code .... 145 | set_guard(Context, my_condition) 146 | ```` 147 | 148 | TODO make a more elegant call node type 149 | 150 | ## Time 151 | 152 | SimGen advances in _ticks_. 153 | 154 | Internally times are represented in 'nanos', nanoseconds since the unix epoch. 155 | 156 | We can choose any time unit we like. If we want minutes, then `60_000_000_000` nanos is one unit. 157 | 158 | We can start the simulation at any time we choose. 159 | 160 | The simulation runs in discrete _ticks_, and we can choose the length of a tick. 161 | 162 | Each context also gets a clock, which starts at zero when the context starts. 163 | 164 | 165 | ## Making Contexts Interact 166 | 167 | This isn't working yet. Next day or two I get to work on SimGen it goes in. 168 | 169 | ## Choosing what to simulate 170 | 171 | When making a simulation, what you leave out is just as important as what you include. 172 | 173 | ## Some Use Cases 174 | 175 | TODO 176 | 177 | ## Some Definitions 178 | 179 | TODO update 180 | 181 | **behavior** - We humans talk about systems in terms of their behavior. Servers start, stop, handle requests, generate 404 errors, call the database, etc. Shoppers check online, then check a store, perhaps learn from a clerk that they actually want something else, realize they have the something else... **BT** describes the world in terms of a set of fundamental behaviors. 182 | 183 | **BT** - the language SimGen programs are defined in. Stored in files ending .bt 184 | 185 | A **context** - Often it's useful to have more than one 'copy' of a thing - A simulation of people might 186 | have a bunch of people modelled with identical code. The external Prolog program starts a single behavior (node) with a context. 187 | 188 | A **C-N Pair** - Context-node pair. internal name for an active node 189 | 190 | A **node** - a fundamental unit of behavior - do things in sequence. Do them in parallel. Try things until one succeeds. We're migrating away from describing nodes to talking of behaviors and sub-behaviors. 191 | 192 | A **simulation** - a collection of interacting systems that can be run forward in time. 193 | 194 | **values** - floating point variables. All SimGen values have context scope. 195 | 196 | **conditions** - boolean variables. All SimGen conditions have context scope. 197 | 198 | # The BT Language 199 | 200 | ## Syntax 201 | 202 | A BT file is a sequence of nodes, and comments. 203 | 204 | ```` 205 | % from percent sign to end of line is a comment 206 | /* multi-line C style comments are supported */ 207 | ```` 208 | 209 | Nodes have the format 210 | 211 | ```` 212 | 213 | 214 | . 215 | ```` 216 | 217 | is an atom, the name of the node. 218 | 219 | Anywere a _child_ can occur, an _anonymous node_ can be substituted. 220 | 221 | ```` 222 | { } 223 | ```` 224 | 225 | This example waits 10 seconds and then does something. It's clearer to inline the 226 | `{ dur 10 }` statement than to have a `wait_ten_seconds` node. 227 | 228 | ```` 229 | do_something_after_delay -> 230 | { dur 10 }, 231 | do_something. 232 | ```` 233 | 234 | 235 | ## The Node Types 236 | 237 | 238 | 239 | `~?` [child | float ":" child]+ randomly select one child. The probabillity that 240 | a child is selected is proportional to it's weight. 241 | the default weight is 1.0 242 | 243 | `!` partial differential equation - see PDQ section for syntax 244 | 245 | `?` condition Check guard - checks the condition every tick. If the condition is false, 246 | it fails. If true, it succeeds 247 | 248 | `-?` condition Wait guard - waits until the condition is true and succeeds 249 | 250 | `set` condition makes the condition true 251 | 252 | `clear` condition makes the condition false 253 | 254 | `->` child+ do a sequence of things. If one fails, the following are not done, and the node fails. 255 | 256 | `attempt` child+ do a sequence of things, trying one after another until one succeeds. 257 | 258 | `~>` child+ randomly order the children, and then execute as -> 259 | 260 | `=>` child+ run at same time in parallel. if any fail, fail. if they all succeed, succeed. 261 | Guard that a condition stays true. enforce coordinated action. 262 | 263 | `=?` child+ run at same time in parallel. if any fail, fail. if any succeed, succeed. 264 | 265 | `try` child run the child and succeed whether the child succeeds or fails 266 | 267 | `fail` just fail 268 | 269 | `not` child fail when child succeeds, succeed if child fails 270 | 271 | `dur` number wait this number of user time units, then succeed 272 | 273 | `pin` child emit a Simularity specific pair of 'pin' events 274 | 275 | `<>` child loop - run the child repeatedly until it fails 276 | 277 | `<-->` child retry loop - run the child repeatedly until it succeeds 278 | 279 | ## PDQ nodes 280 | 281 | The `!` node is a Partial Differential Equation(PDQ) node. 282 | 283 | ```` 284 | action_node ! 285 | x = 0 286 | ; 287 | x := x + 1 288 | ; 289 | x < 10 290 | . 291 | ```` 292 | 293 | The first section is evaluated on the first tick. The second section is evaluated on subsequent ticks. The final section is evaluated **after** the first or second section, and if false the node fails. Hence PDQ nodes always eventually fail. 294 | 295 | The operator `:=` does assignment based on the previous tick's values. The operator `=` is reactive, evaluating when all operands are available, using this tick's values. The user is responsible for assuring that all operands are available. 296 | 297 | This odd setup makes writing bias-free PDQ code easier. 298 | 299 | All operators available to the SWI-Prolog `is/2` predicate are available. 300 | 301 | Also available are a few additional functions. And some operators have been moved to prevent operator clashes with simgen operators. 302 | 303 | * `levy_flight(Prev, Lo, Hi)` which performs a Levy Flight between its low and high values. 304 | * `wander(Prev, Lo, Hi, Dist) randomly wanders a uniform 0-Dist on each step, the probabibility of 305 | wandering up or down depending on the current value (so when Prev=Lo we are guaranteed a wander up) 306 | * `clock()` returns the current context clock 307 | * `pow(Old, Exp)` - exponential 308 | * `lshift(Old, Bits)` - left shift 309 | * `rshift(Old, Bits)` - right shift 310 | * `bitor(A, B)` - bitwise OR 311 | * `bitand(A, B)` - bitwise AND 312 | 313 | TODO - Add the ability to call Prolog from expressions. 314 | 315 | 316 | 317 | --------------------------------------------------------------------------------