Explorar el Código

ft: 初始化提交

master
SisMaker hace 4 años
commit
658adce1a9
Se han modificado 55 ficheros con 3917 adiciones y 0 borrados
  1. +29
    -0
      .gitignore
  2. +21
    -0
      LICENSE
  3. +78
    -0
      README.md
  4. +21
    -0
      examples/action/action_add_rage.erl
  5. +20
    -0
      examples/action/action_become_attacking.erl
  6. +20
    -0
      examples/action/action_become_idle.erl
  7. +20
    -0
      examples/action/action_become_patrolling.erl
  8. +20
    -0
      examples/action/action_become_recovering.erl
  9. +60
    -0
      examples/action/action_collect_dest_path.erl
  10. +45
    -0
      examples/action/action_collect_path.erl
  11. +26
    -0
      examples/action/action_collect_patrol.erl
  12. +22
    -0
      examples/action/action_cost_power.erl
  13. +21
    -0
      examples/action/action_cost_rage.erl
  14. +20
    -0
      examples/action/action_del_dead_target.erl
  15. +21
    -0
      examples/action/action_died.erl
  16. +21
    -0
      examples/action/action_finish.erl
  17. +21
    -0
      examples/action/action_move_grid.erl
  18. +22
    -0
      examples/action/action_recover_power.erl
  19. +44
    -0
      examples/action/action_skill_attack.erl
  20. +24
    -0
      examples/condition/cond_is_attacking.erl
  21. +23
    -0
      examples/condition/cond_is_dest.erl
  22. +23
    -0
      examples/condition/cond_is_died.erl
  23. +24
    -0
      examples/condition/cond_is_idle.erl
  24. +24
    -0
      examples/condition/cond_is_patrolling.erl
  25. +24
    -0
      examples/condition/cond_is_power_full.erl
  26. +24
    -0
      examples/condition/cond_is_rage_full.erl
  27. +24
    -0
      examples/condition/cond_is_recovering.erl
  28. +28
    -0
      examples/condition/cond_is_target_died.erl
  29. +24
    -0
      examples/condition/cond_power_lt.erl
  30. +32
    -0
      examples/example.hrl
  31. +1786
    -0
      examples/example.json
  32. +59
    -0
      examples/game_dict.erl
  33. +127
    -0
      examples/game_process.erl
  34. +64
    -0
      include/behavior3.hrl
  35. +15
    -0
      rebar.config
  36. +22
    -0
      src/action/error.erl
  37. +22
    -0
      src/action/failer.erl
  38. +22
    -0
      src/action/runner.erl
  39. +22
    -0
      src/action/succeeder.erl
  40. +36
    -0
      src/action/wait.erl
  41. +43
    -0
      src/composite/mem_priority.erl
  42. +43
    -0
      src/composite/mem_sequence.erl
  43. +31
    -0
      src/composite/priority.erl
  44. +31
    -0
      src/composite/sequence.erl
  45. +137
    -0
      src/core/base_node.erl
  46. +215
    -0
      src/core/behavior_tree.erl
  47. +157
    -0
      src/core/blackboard.erl
  48. +31
    -0
      src/decorator/inverter.erl
  49. +43
    -0
      src/decorator/limiter.erl
  50. +39
    -0
      src/decorator/max_time.erl
  51. +44
    -0
      src/decorator/repeat_until_failure.erl
  52. +45
    -0
      src/decorator/repeat_until_success.erl
  53. +44
    -0
      src/decorator/repeater.erl
  54. +10
    -0
      src/eBhv3.app.src
  55. +3
    -0
      src/eBhv3.erl

+ 29
- 0
.gitignore Ver fichero

@ -0,0 +1,29 @@
.eunit
*.o
*.beam
*.plt
erl_crash.dump
.concrete/DEV_MODE
# rebar 2.x
.rebar
rel/example_project
ebin/*
deps
# rebar 3
.rebar3
_build/
_checkouts/
rebar.lock
# idea
.idea
*.iml
cmake-build*
CMakeLists.txt
# nif compile temp file
*.pdb
*.d
compile_commands.json

+ 21
- 0
LICENSE Ver fichero

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2019-2020 alisdair sullivan <alisdairsullivan@yahoo.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 78
- 0
README.md Ver fichero

@ -0,0 +1,78 @@
中文
=====
Behavior3的erlang支持库
快速开始
----
添加如下内容到**rebar.config**
{deps, [
...
{behavior3erl, "1.0.0"}
]}.
编译
----
$ rebar3 compile
使用
----
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("example.json"),
{ok, RootID} = behavior_tree:init_btree_by_title(<<"example_ai"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{_BTStatus, _BTState1} = behavior_tree:execute(RootID, BTState = #{}).
更多
----
[behavior3editor](https://github.com/behavior3/behavior3editor)
[behavior3go](https://github.com/magicsea/behavior3go)
#### Behavior3使用系列文章:
[(一)行为树应用之行为树简介](http://note.youdao.com/s/77bGugj9)
[(二)行为树应用之组合节点](http://note.youdao.com/s/XiKlHPIr)
[(三)行为树应用之装饰节点](http://note.youdao.com/s/9Z6zI3YE)
[(四)行为树应用之自定义节点](http://note.youdao.com/s/AcRrY8ig)
[(五)行为树应用之加载行为树](http://note.youdao.com/s/DiqLf0ES)
[(六)行为树应用之节点执行](http://note.youdao.com/s/PI3Wic5D)
[(七)行为树应用之设计巡逻兵AI](http://note.youdao.com/s/HTCGTgAm)
[(八)行为树应用之设计丧尸AI](http://note.youdao.com/s/3wKFxcTw)
English
=====
Behavior3 by erlang library
Quickstart
----
add to **rebar.config**
{deps, [
...
{behavior3erl, "1.0.0"}
]}.
Build
----
$ rebar3 compile
Usage
----
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("example.json"),
{ok, RootID} = behavior_tree:init_btree_by_title(<<"example_ai"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{_BTStatus, _BTState1} = behavior_tree:execute(RootID, BTState = #{}).
More
----
[behavior3editor](https://github.com/behavior3/behavior3editor)
[behavior3go](https://github.com/magicsea/behavior3go)

+ 21
- 0
examples/action/action_add_rage.erl Ver fichero

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% add_rage增加相应怒气100
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_add_rage).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{add_rage := AddRage}} = _BTree, #{cur_rage := CurRage} = State) ->
State1 = State#{cur_rage := min(CurRage + AddRage, ?MAX_RAGE)},
?INFO("~ts:怒气值增加~w点 总怒气~w", [maps:get(uid, State1), AddRage, maps:get(cur_rage, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_attacking.erl Ver fichero

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_attacking).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_ATTACKING)},
?INFO("~ts:变为攻击状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_idle.erl Ver fichero

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_idle).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_IDLE)},
?INFO("~ts:变为空闲状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_patrolling.erl Ver fichero

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_patrolling).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_PATROLLING)},
?INFO("~ts:变为巡逻状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_recovering.erl Ver fichero

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_recovering).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_RECOVERING)},
?INFO("~ts:变为恢复状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 60
- 0
examples/action/action_collect_dest_path.erl Ver fichero

@ -0,0 +1,60 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_dest_path).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_grid := CurGrid, grid_list := GirdList} = State) ->
case GirdList of
[] ->
EndGrid = get_end_grid(CurGrid),
State1 = State#{grid_list := get_grid_path(CurGrid, EndGrid)},
?INFO("~ts:找到穿传说中的“大蒜”的位置,要尽快到达那里!", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1};
_ ->
?INFO("~ts:天要黑了,要尽快到达那里!", [maps:get(uid, State)]),
{?BT_SUCCESS, State}
end.
get_end_grid(CurGrid) ->
EndGridList = [{0, 0}, {0, ?MAX_X}, {?MAX_Y, ?MAX_X}, {?MAX_Y, 0}],
{_, EndGrid} = hd(lists:sort([{calc_distance(CurGrid, Grid), Grid} || Grid <- EndGridList])),
EndGrid.
calc_distance({X1, Y1}, {X2, Y2}) ->
math:sqrt(math:pow(X1 - X2, 2) + math:pow(Y1 - Y2, 2)).
get_grid_path(CurGrid, EndGrid) ->
case EndGrid of
{0, 0} ->
get_grid_path(CurGrid, [EndGrid], 1, 1);
{0, ?MAX_X} ->
get_grid_path(CurGrid, [EndGrid], 1, -1);
{?MAX_Y, ?MAX_X} ->
get_grid_path(CurGrid, [EndGrid], -1, -1);
{?MAX_Y, 0} ->
get_grid_path(CurGrid, [EndGrid], -1, 1)
end.
get_grid_path(CurGrid, GridPath, XInc, YInc) ->
case GridPath of
[CurGrid | _] ->
GridPath;
[{X, Y} | _] ->
case Y + YInc of
Y1 when Y1 == 0 orelse Y1 == ?MAX_X ->
get_grid_path(CurGrid, [{X + XInc, Y1}, {X, Y1} | GridPath], XInc, bnot YInc + 1);
Y1 ->
get_grid_path(CurGrid, [{X, Y1} | GridPath], XInc, YInc)
end
end.

+ 45
- 0
examples/action/action_collect_path.erl Ver fichero

@ -0,0 +1,45 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_path).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2, get_grid_path/1]).
tick(_BTree, #{cur_grid := CurGrid} = State) ->
State1 = State#{grid_list := get_grid_path(CurGrid)},
{?BT_SUCCESS, State1}.
get_grid_path(CurGrid) ->
List = [{1, 0}, {-1, 0}, {0, 1}, {0, -1}],
Max = length(List),
List1 = [E || {_, E} <- lists:sort([{?RAND(1, Max), E} || E <- List])],
case get_grid_path_1(List1, CurGrid, []) of
[] ->
get_grid_path(CurGrid);
Result ->
Result
end.
get_grid_path_1([{X, Y} | T], {CurX, CurY}, Result) ->
X1 = CurX + X,
Y1 = CurY + Y,
Grid = {X1, Y1},
case X1 >= 0 andalso X1 =< ?MAX_X andalso Y1 >= 0 andalso Y1 =< ?MAX_Y of
true ->
get_grid_path_1(T, Grid, [Grid | Result]);
false ->
[]
end;
get_grid_path_1([], _, Result) ->
Result.

+ 26
- 0
examples/action/action_collect_patrol.erl Ver fichero

@ -0,0 +1,26 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_patrol).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_grid := CurGrid, rival_list := RivalList} = State) ->
Fun = fun(UID) -> maps:get(type, game_dict:get_role_state(UID)) == ?HUMAN end,
case lists:filter(Fun, game_dict:get_map_data(CurGrid)) of
UIDList when UIDList /= [] ->
?INFO("~ts:嗅到了人类的味道,将要对[~ts]发起攻击", [maps:get(uid, State), lists:join(",", UIDList)]),
{?BT_SUCCESS, State#{rival_list := UIDList ++ RivalList}};
[] ->
{?BT_FAILURE, State}
end.

+ 22
- 0
examples/action/action_cost_power.erl Ver fichero

@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% Misc结构中的atk_dmg字段的值
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_cost_power).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
#{cur_power := CurPower, misc := #{atk_dmg := {AtkType, Dmg}} = Misc} = State,
State1 = State#{cur_power := max(CurPower - Dmg, 0), misc := maps:remove(atk_dmg, Misc)},
?INFO("~ts:受到~ts攻击,体力值减少~w点 剩余体力~w", [maps:get(uid, State1), AtkType, Dmg,maps:get(cur_power, State1)]),
{?BT_SUCCESS, State1}.

+ 21
- 0
examples/action/action_cost_rage.erl Ver fichero

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% cost_rage扣除相应怒气0
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_cost_rage).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{cost_rage := CostRage}} = _BTree, #{cur_rage := CurRage} = State) ->
State1 = State#{cur_rage := max(CurRage - CostRage, 0)},
?INFO("~ts:怒气值减少~w", [maps:get(uid, State1), CurRage - maps:get(cur_rage, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_del_dead_target.erl Ver fichero

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% rival_list中第一个目标
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_del_dead_target).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{rival_list := [_ | T]} = State) ->
State1 = State#{rival_list := T},
{?BT_SUCCESS, State1}.

+ 21
- 0
examples/action/action_died.erl Ver fichero

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_died).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{uid := UID} = State) ->
self() ! {died, UID},
?INFO("~ts 被消灭!", [UID]),
{?BT_SUCCESS, State#{cur_state := ?SET_STATE(?STATE_TYPE_DEAD)}}.

+ 21
- 0
examples/action/action_finish.erl Ver fichero

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%% is_win属性打印结果
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_finish).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{is_win := IsWin}} = _BTree, #{uid := UID} = State) ->
self() ! finish,
?INFO("游戏结束,~ts~ts!", [UID, IsWin]),
{?BT_SUCCESS, State}.

+ 21
- 0
examples/action/action_move_grid.erl Ver fichero

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_move_grid).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{uid := UID, cur_grid := OldGrid, grid_list := [Grid | T]} = State) ->
game_dict:update_map_date(OldGrid, Grid, UID),
State1 = State#{grid_list := T, cur_grid := Grid},
?INFO("~ts:移动到了~w格子", [UID, Grid]),
{?BT_SUCCESS, State1}.

+ 22
- 0
examples/action/action_recover_power.erl Ver fichero

@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% Max - Min之间随机恢复几点体力
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_recover_power).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{max_power := MaxPower, min_power := MinPower}} = _BTree, #{cur_power := CurPower} = State) ->
RecoverPower = ?RAND(MinPower, MaxPower),
State1 = State#{cur_power := min(CurPower + RecoverPower, ?MAX_POWER)},
?INFO("~ts:体力值增加了~w点 当前体力:~w", [maps:get(uid, State1), RecoverPower, maps:get(cur_power, State1)]),
{?BT_SUCCESS, State1}.

+ 44
- 0
examples/action/action_skill_attack.erl Ver fichero

@ -0,0 +1,44 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% RAND(min_power ,max_power) * RAND(min_rate, max_rate)
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_skill_attack).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := Prop} = _BTree, #{uid := UID, rival_list := [RivalUID | _]} = State) ->
BTreeID = game_dict:get_passivity_btree_id(RivalUID),
RivalState = get_be_attacker_rival_state(Prop, UID, RivalUID),
{_, RivalState1} = behavior_tree:execute_sub_tree(BTreeID, RivalState),
game_dict:put_role_state(RivalUID, RivalState1),
{?BT_SUCCESS, State}.
get_be_attacker_rival_state(Prop, UID, RivalUID) ->
#{
skill_type := SkillType,
min_power := MinPower, max_power := MaxPower,
min_rate := MinRate, max_rate := MaxRate
} = Prop,
#{rival_list := RivalList} = RivalState = game_dict:get_role_state(RivalUID),
case lists:member(UID, RivalList) of
true ->
RivalList1 = RivalList;
false ->
RivalList1 = RivalList ++ [UID]
end,
RivalState1 = RivalState#{
rival_list := RivalList1,
misc := #{atk_dmg => {SkillType, ?RAND(MinPower, MaxPower) * ?RAND(MinRate, MaxRate)}}
},
game_dict:put_role_state(RivalUID, RivalState1),
?INFO("~ts:发动了~ts攻击", [UID, SkillType]),
RivalState1.

+ 24
- 0
examples/condition/cond_is_attacking.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_attacking).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_ATTACKING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 23
- 0
examples/condition/cond_is_dest.erl Ver fichero

@ -0,0 +1,23 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:18
%%%-------------------------------------------------------------------
-module(cond_is_dest).
-include("behavior3.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
case State of
#{grid_list := []} ->
{?BT_SUCCESS, State};
#{} ->
{?BT_FAILURE, State}
end.

+ 23
- 0
examples/condition/cond_is_died.erl Ver fichero

@ -0,0 +1,23 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:26
%%%-------------------------------------------------------------------
-module(cond_is_died).
-include("behavior3.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_power := CurPower} = State) ->
case CurPower =< 0 of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_idle.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_idle).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_IDLE, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_patrolling.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_patrolling).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_PATROLLING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_power_full.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_power_full).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_power := CurPower} = State) ->
case CurPower >= ?MAX_POWER of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_rage_full.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:18
%%%-------------------------------------------------------------------
-module(cond_is_rage_full).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_rage := CurRage} = State) ->
case CurRage >= ?MAX_RAGE of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_recovering.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_recovering).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_RECOVERING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 28
- 0
examples/condition/cond_is_target_died.erl Ver fichero

@ -0,0 +1,28 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:20
%%%-------------------------------------------------------------------
-module(cond_is_target_died).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
case State of
#{rival_list := [RivalUID | _]} ->
case game_dict:get_role_state(RivalUID) of
#{cur_state := CurState} when ?IS_STATE(?STATE_TYPE_DEAD, CurState) ->
{?BT_SUCCESS, State};
#{} ->
{?BT_FAILURE, State}
end;
#{} ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_power_lt.erl Ver fichero

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:20
%%%-------------------------------------------------------------------
-module(cond_power_lt).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{power := LTPower}} = _BTree, #{cur_power := CurPower} = State) ->
case CurPower < LTPower of
true ->
?INFO("~ts:当前体力低于~w点,必须要尽快休息恢复体力!", [maps:get(uid, State), LTPower]),
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 32
- 0
examples/example.hrl Ver fichero

@ -0,0 +1,32 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 07. 10 2020 15:16
%%%-------------------------------------------------------------------
-type uid() :: binary().
-type grid() :: {X :: non_neg_integer(), Y :: non_neg_integer()}.
-define(INFO(Format, Args), io:format(Format ++ "~n", Args)).
-define(MAX_X, 5).
-define(MAX_Y, 5).
-define(HUMAN, 1).
-define(ZOMBIE, 2).
-define(RAND(Min, Max), (rand:uniform(Max - (Min - 1)) + (Min - 1))).
-define(STATE_TYPE_IDLE, 1).
-define(STATE_TYPE_ATTACKING, 2).
-define(STATE_TYPE_PATROLLING, 3).
-define(STATE_TYPE_RECOVERING, 4).
-define(STATE_TYPE_DEAD, 5).
-define(IS_STATE(StateType, CurState), (CurState band (1 bsl (StateType - 1)) > 0)).
-define(SET_STATE(StateType), (1 bsl (StateType - 1))).
-define(MAX_POWER, 100).
-define(MAX_RAGE, 100).

+ 1786
- 0
examples/example.json
La diferencia del archivo ha sido suprimido porque es demasiado grande
Ver fichero


+ 59
- 0
examples/game_dict.erl Ver fichero

@ -0,0 +1,59 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 18. 10 2020 20:53
%%%-------------------------------------------------------------------
-module(game_dict).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([
get_map_data/1, update_map_date/3,
get_initiative_btree_id/1, put_initiative_btree_id/2, erase_initiative_btree_id/1,
get_passivity_btree_id/1, put_passivity_btree_id/2, erase_passivity_btree_id/1,
get_role_state/1, put_role_state/2, erase_role_state/1
]).
-spec get_map_data(grid()) -> [uid()].
get_map_data(Grid) ->
case erlang:get(Grid) of
undefined ->
[];
L ->
L
end.
-spec update_map_date(grid(), grid(), uid()) -> ok.
update_map_date(OldGrid, NewGrid, UID) ->
erlang:put(OldGrid, lists:delete(UID, get_map_data(OldGrid))),
erlang:put(NewGrid, [UID | get_map_data(NewGrid)]),
ok.
-spec get_initiative_btree_id(uid()) -> bt_uid().
get_initiative_btree_id(UID) ->
erlang:get({initiative_btree_id, UID}).
put_initiative_btree_id(UID, BTreeID) ->
erlang:put({initiative_btree_id, UID}, BTreeID).
erase_initiative_btree_id(UID) ->
erlang:erase({initiative_btree_id, UID}).
-spec get_passivity_btree_id(uid()) -> bt_uid().
get_passivity_btree_id(UID) ->
erlang:get({passivity_btree_id, UID}).
put_passivity_btree_id(UID, BTreeID) ->
erlang:put({passivity_btree_id, UID}, BTreeID).
erase_passivity_btree_id(UID) ->
erlang:erase({passivity_btree_id, UID}).
-spec get_role_state(uid()) -> map().
get_role_state(UID) ->
erlang:get({role_state, UID}).
put_role_state(UID, State) ->
erlang:put({role_state, UID}, State).
erase_role_state(UID) ->
erlang:erase({role_state, UID}).

+ 127
- 0
examples/game_process.erl Ver fichero

@ -0,0 +1,127 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(game_process).
-behaviour(gen_server).
-include("example.hrl").
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% Spawning and gen_server implementation
%%%===================================================================
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("examples/example.json"),
ZombieList = [
%% <<"HUNTER"/utf8>>,
%% <<"BOMMER"/utf8>>,
%% <<"JOCKEY"/utf8>>,
%% <<"SOMKER"/utf8>>,
%% <<"SPITTER"/utf8>>,
%% <<"CHARGER"/utf8>>,
%% <<"TANK"/utf8>>,
<<"WITCH"/utf8>>
],
init_zombie(ZombieList, TitleMaps, TreeMaps, TreeNodeMaps),
UID = <<"凯恩"/utf8>>,
init_patrol(UID, TitleMaps, TreeMaps, TreeNodeMaps),
erlang:put(uid_list, ZombieList ++ [UID]),
self() ! run,
{ok, #{frame => 1}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(run, #{frame := Frame} = State) ->
try
?INFO("~w分钟", [Frame]),
Fun = fun(UID) -> run(UID) end,
lists:foreach(Fun, get(uid_list)),
erlang:send_after(1000, self(), run),
{noreply, State#{frame := Frame + 1}}
catch
Error:Reason:StackTrace ->
?INFO("Error:~w Reason:~w ~n StackTrace:~p", [Error, Reason, StackTrace]),
{stop, normal, State}
end;
handle_info(finish, State) ->
{stop, normal, State};
handle_info({died, UID}, State) ->
erlang:put(uid_list, lists:delete(UID, erlang:get(uid_list))),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
new_state(UID, Type, CurGrid) ->
case Type of
?HUMAN ->
Power = ?MAX_POWER;
?ZOMBIE ->
Power = ?RAND(?MAX_POWER div 2, ?MAX_POWER)
end,
State = #{
uid => UID,
type => Type,
cur_state => ?STATE_TYPE_IDLE,
cur_power => Power,
cur_rage => 0,
grid_list => [],
cur_grid => CurGrid,
rival_list => [],
misc => #{}
},
?INFO("生成单位:~tp", [State]),
State.
init_zombie([UID | T], TitleMaps, TreeMaps, TreeNodeMaps) ->
{ok, InitiativeBTreeID} = behavior_tree:init_btree_by_title(<<"丧尸主动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{ok, PassivityBTreeID} = behavior_tree:init_btree_by_title(<<"丧尸被动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
game_dict:put_initiative_btree_id(UID, InitiativeBTreeID),
game_dict:put_passivity_btree_id(UID, PassivityBTreeID),
Grid = {?RAND(0, ?MAX_X), ?RAND(0, ?MAX_Y)},
game_dict:put_role_state(UID, new_state(UID, ?ZOMBIE, Grid)),
init_zombie(T, TitleMaps, TreeMaps, TreeNodeMaps);
init_zombie([], _TitleMaps, _TreeMaps, _TreeNodeMaps) ->
ok.
init_patrol(UID, TitleMaps, TreeMaps, TreeNodeMaps) ->
{ok, InitiativeBTreeID} = behavior_tree:init_btree_by_title(<<"巡逻兵主动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{ok, PassivityBTreeID} = behavior_tree:init_btree_by_title(<<"巡逻兵被动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
game_dict:put_initiative_btree_id(UID, InitiativeBTreeID),
game_dict:put_passivity_btree_id(UID, PassivityBTreeID),
Grid = {?RAND(0, ?MAX_X), ?RAND(0, ?MAX_Y)},
game_dict:put_role_state(UID, new_state(UID, ?HUMAN, Grid)).
run(UID) ->
BTreeID = game_dict:get_initiative_btree_id(UID),
#{cur_state := CurState} = State = game_dict:get_role_state(UID),
case ?IS_STATE(?STATE_TYPE_DEAD, CurState) of
true ->
ok;
false ->
{_, State1} = behavior_tree:execute(BTreeID, State),
game_dict:put_role_state(UID, State1)
end.

+ 64
- 0
include/behavior3.hrl Ver fichero

@ -0,0 +1,64 @@
-ifndef(behavior3_h).
-define(behavior3_h, true).
-export_type([bt_uid/0, bt_status/0, bt_node_id/0, bt_state/0, properties/0, uninit_bt_node/0, tree_nodes/0, btree/0, bt_node/0]).
-type bt_uid() :: reference().
-define(BT_SUCCESS, 1).
-define(BT_FAILURE, 2).
-define(BT_RUNNING, 3).
-define(BT_ERROR, 4).
-type bt_status() :: ?BT_SUCCESS|?BT_FAILURE|?BT_RUNNING|?BT_ERROR.
-type bt_node_id() :: string().
-type bt_state() :: #{
'$global_maps' => map(),
'$local_maps' => map(),
term() => term()
}.
-type properties() :: #{atom() => term()}.
%%
-type uninit_bt_node() :: #{
id => bt_node_id(),
name => atom() | bt_node_id(),
category => atom(),
properties => properties(),
children => [bt_node_id()]
}.
-type tree_nodes() :: #{bt_node_id() => uninit_bt_node()}.
%%
-type btree() :: #{
id => bt_node_id(),
root => bt_node_id(),
properties => properties(),
tree_nodes => tree_nodes()
}.
%%
-type bt_node() :: #{
id := bt_uid(),
bt_node_id := bt_node_id(),
parent_id := bt_uid(),
name := atom(),
category := atom(),
properties := properties(),
children := [bt_uid()]
}.
-define(BI_MOD_LIST, [
error, failer, runner, succeeder, wait,
mem_priority, mem_sequence, priority, sequence,
inverter, limiter, max_time, repeat_until_failure, repeat_until_success, repeater
]).
-define(BT_LOG(Format), io:format("~w:~w:~w [debug] ~p: " ++ Format ++ "~n", tuple_to_list(time()) ++ [?MODULE])).
-define(BT_LOG(Format, Args), io:format("~w:~w:~w [debug] ~p: " ++ Format ++ "~n", tuple_to_list(time()) ++ [?MODULE | Args])).
-endif.

+ 15
- 0
rebar.config Ver fichero

@ -0,0 +1,15 @@
{erl_opts, [debug_info, {i, "include"}
%% {d, show_bt_log}, %% log_switch
]}.
{deps, [
jsx
]}.
{xref_checks, [
undefined_function_calls
, undefined_functions
, locals_not_used
, deprecated_function_calls
, deprecated_functions
]}.

+ 22
- 0
src/action/error.erl Ver fichero

@ -0,0 +1,22 @@
-module(error).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/failer.erl Ver fichero

@ -0,0 +1,22 @@
-module(failer).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_FAILURE, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/runner.erl Ver fichero

@ -0,0 +1,22 @@
-module(runner).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_RUNNING, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/succeeder.erl Ver fichero

@ -0,0 +1,22 @@
-module(succeeder).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_SUCCESS, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 36
- 0
src/action/wait.erl Ver fichero

@ -0,0 +1,36 @@
-module(wait).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID}, BTState) ->
blackboard:set(start_time, erlang:system_time(millisecond), ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, properties := #{milliseconds := EndTime}} = _BTNode, BTState) ->
StartTime = blackboard:get(start_time, ID, BTState),
case erlang:system_time(millisecond) - StartTime > EndTime of
true ->
{?BT_SUCCESS, BTState};
false ->
{?BT_RUNNING, BTState}
end.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(start_time, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 43
- 0
src/composite/mem_priority.erl Ver fichero

@ -0,0 +1,43 @@
-module(mem_priority).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID, children := Children} = _BTNode, BTState) ->
blackboard:set(running_children, Children, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID} = _BTNode, BTState) ->
RunningChildren = blackboard:get(running_children, ID, BTState),
tick_1(RunningChildren, ID, BTState).
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, State) ->
blackboard:remove(running_children, ID, State).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T] = Children, ID, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTState1} ->
tick_1(T, ID, BTState1);
{?BT_RUNNING, BTState1} ->
BTState2 = blackboard:set(running_children, Children, ID, BTState1),
{?BT_RUNNING, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], _ID, BTState) ->
{?BT_FAILURE, BTState}.

+ 43
- 0
src/composite/mem_sequence.erl Ver fichero

@ -0,0 +1,43 @@
-module(mem_sequence).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID, children := Children} = _BTNode, BTState) ->
blackboard:set(running_children, Children, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID} = _BTNode, BTState) ->
RunningChildren = blackboard:get(running_children, ID, BTState),
tick_1(RunningChildren, ID, BTState).
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(running_children, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T] = Children, ID, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(T, ID, BTState1);
{?BT_RUNNING, BTState1} ->
BTState2 = blackboard:set(running_children, Children, ID, BTState1),
{?BT_RUNNING, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], _ID, BTState) ->
{?BT_SUCCESS, BTState}.

+ 31
- 0
src/composite/priority.erl Ver fichero

@ -0,0 +1,31 @@
-module(priority).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := Children} = _BTNode, BTState) ->
tick_1(Children, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T], BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTState1} ->
tick_1(T, BTState1);
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], BTState) ->
{?BT_FAILURE, BTState}.

+ 31
- 0
src/composite/sequence.erl Ver fichero

@ -0,0 +1,31 @@
-module(sequence).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := Children} = _BTNode, BTState) ->
tick_1(Children, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T], BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(T, BTState1);
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], BTState) ->
{?BT_SUCCESS, BTState}.

+ 137
- 0
src/core/base_node.erl Ver fichero

@ -0,0 +1,137 @@
-module(base_node).
-compile([inline, {inline_size, 100}]).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([execute/2]).
%% Internal API
-export([do_init/4, do_open/2, do_tick/2, do_close/2]).
%%
-callback(init(bt_node()) -> bt_node()).
%%
-callback(open(bt_node(), bt_state()) -> bt_state()).
%%
-callback(tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}).
%%
-callback(close(bt_node(), bt_state()) -> bt_state()).
-optional_callbacks([init/1, open/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc
-spec execute(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute(NodeID, BTState) ->
BTNode = blackboard:get_btree_node(NodeID),
BTState1 = do_open(BTNode, BTState),
{BTStatus, BTState2} = do_tick(BTNode, BTState1),
case BTStatus of
?BT_RUNNING ->
{?BT_RUNNING, BTState2};
BTStatus ->
BTState3 = do_close(BTNode, BTState2),
{BTStatus, BTState3}
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%
-spec do_init(bt_uid()|undefined, bt_node_id(), #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
do_init(ParentNodeID, BTNodeID, TreeMaps, TreeNodeMaps) ->
case behavior_tree:get_btree_node(BTNodeID, TreeNodeMaps) of
#{name := Name, category := tree} ->
#{root := Root} = behavior_tree:get_btree(Name, TreeMaps),
do_init(ParentNodeID, Root, TreeMaps, TreeNodeMaps);
#{name := Name, children := Children} = BTNode ->
case code:ensure_loaded(Name) of
{module, Mod} ->
case erlang:function_exported(Mod, init, 1) of
true ->
BTNode1 = Mod:init(BTNode);
false ->
BTNode1 = BTNode
end,
ID = make_ref(),
BTNode2 = BTNode1#{
id => ID,
bt_node_id => BTNodeID,
parent_id => ParentNodeID,
children => [do_init(ID, ChildBTNodeID, TreeMaps, TreeNodeMaps) || ChildBTNodeID <- Children]
},
blackboard:set_btree_node(BTNode2),
{ok, ID};
_ ->
{error, {btree_node_not_implement, Name}}
end
end.
%% open/2open函数
%%
-spec do_open(bt_node(), bt_state()) -> bt_state().
do_open(#{id := ID, parent_id := ParentID, name := Mod} = BTNode, BTState) ->
case blackboard:get('$is_open', ID, false, BTState) of
true ->
BTState;
false ->
case erlang:function_exported(Mod, open, 2) of
true ->
BTState1 = Mod:open(BTNode, BTState);
false ->
BTState1 = BTState
end,
BTState2 = blackboard:set('$is_open', true, ID, BTState1),
case is_reference(ParentID) of
true ->
Children = blackboard:get('$children', ParentID, [], BTState2),
blackboard:set('$children', [ID | Children], ParentID, BTState2);
false ->
BTState2
end
end.
%% tick函数
-spec do_tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
do_tick(#{name := Mod} = BTNode, BTState) ->
Mod:tick(BTNode, BTState).
%% @doc close/2close函数
-spec do_close(bt_node(), bt_state()) -> bt_state().
do_close(#{id := ID, name := Mod} = BTNode, BTState) ->
Children = blackboard:get('$children', ID, [], BTState),
BTState1 = do_close_1(Children, BTState),
case erlang:function_exported(Mod, close, 2) of
true ->
BTState2 = Mod:close(BTNode, BTState1);
false ->
BTState2 = BTState1
end,
BTState3 = blackboard:set('$is_open', false, ID, BTState2),
BTState4 = blackboard:set('$children', [], ID, BTState3),
blackboard:erase_node_local(ID, BTState4).
do_close_1([NodeID | T], BTState) ->
BTState1 = do_close_1(T, BTState),
case blackboard:get('$is_open', NodeID, false, BTState1) of
true ->
BTNode = blackboard:get_btree_node(NodeID),
do_close(BTNode, BTState1);
false ->
BTState1
end;
do_close_1([], BTState) ->
BTState.

+ 215
- 0
src/core/behavior_tree.erl Ver fichero

@ -0,0 +1,215 @@
-module(behavior_tree).
-compile([inline, {inline_size, 100}]).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([
load_tree_file/1,
init_btree_by_title/4, init_btree/3,
execute/2, execute_child/2, close_btree_node/2, unload_btree/1
]).
%% Internal API
-export([get_btree_id_by_title/2, get_btree/2, get_btree_node/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc
%%
-spec load_tree_file(file:name_all()) -> {#{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()}|{error, term()}.
load_tree_file(JsonConfig) ->
case file:read_file(JsonConfig) of
{ok, Json} ->
JsonTerm = jsx:decode(Json, [return_maps]),
parse_btree(JsonTerm);
{error, Reason} ->
?BT_LOG("Error:~w Reason:~w JsonConfig:~w", [error, Reason, JsonConfig]),
{error, Reason}
end.
%% @doc
%% id
-spec get_btree_id_by_title(string(), #{string() => bt_node_id()}) -> bt_node_id()|undefined.
get_btree_id_by_title(Title, TitleMaps) ->
case TitleMaps of
#{Title := ID} ->
ID;
#{} ->
undefined
end.
%% @doc
%%
-spec get_btree(bt_node_id(), #{bt_node_id() => btree()}) -> btree()|undefined.
get_btree(BTNodeID, TreeMaps) ->
case TreeMaps of
#{BTNodeID := BTree} ->
BTree;
#{} ->
undefined
end.
%% @doc
%%
-spec get_btree_node(bt_node_id(), tree_nodes()) -> uninit_bt_node()|undefined.
get_btree_node(BTNodeID, TreeNodeMaps) ->
case TreeNodeMaps of
#{BTNodeID := BTNode} ->
BTNode;
#{} ->
undefined
end.
%% @doc
%%
-spec init_btree_by_title(string(), #{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
init_btree_by_title(Title, TitleMaps, TreeMaps, TreeNodeMaps) ->
BTNodeID = get_btree_id_by_title(Title, TitleMaps),
init_btree(BTNodeID, TreeMaps, TreeNodeMaps).
%% @doc
%%
-spec init_btree(bt_node_id(), #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
init_btree(BTNodeID, TreeMaps, TreeNodeMaps) ->
#{root := Root} = behavior_tree:get_btree(BTNodeID, TreeMaps),
#{id := NodeID} = behavior_tree:get_btree_node(Root, TreeNodeMaps),
base_node:do_init(undefined, NodeID, TreeMaps, TreeNodeMaps).
%% @doc
%%
-spec execute(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute(NodeID, BTState) ->
BTState1 = blackboard:init_maps_keys(BTState),
case base_node:execute(NodeID, BTState1) of
{?BT_RUNNING, BTState2} ->
{?BT_RUNNING, BTState2};
{BTStatus, BTState2} ->
BTState3 = blackboard:erase_btree(BTState2),
{BTStatus, BTState3}
end.
%% @doc
%%
-spec execute_child(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute_child(NodeID, BTState) ->
base_node:execute(NodeID, BTState).
%% @doc
%%
-spec close_btree_node(bt_uid(), bt_state()) -> bt_state().
close_btree_node(NodeID, BTState) ->
BTState1 = do_close_btree_node(NodeID, BTState),
blackboard:erase_btree(BTState1).
%% @doc
%%
-spec unload_btree(bt_uid()) -> ok.
unload_btree(NodeID) ->
do_unload_btree_node(NodeID).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%
-spec parse_btree(map()) -> {#{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()}.
parse_btree(#{<<"trees">> := Trees}) ->
parse_btree_1(Trees, #{}, #{}, #{}).
parse_btree_1([Node | T], TitleMaps, TreeMaps, TreeNodeMaps) ->
#{<<"id">> := ID, <<"title">> := Title, <<"root">> := Root, <<"nodes">> := Nodes} = Node,
Iterator = maps:iterator(Nodes),
{TreeNodes, TreeNodeMaps1} = parse_btree_node(maps:next(Iterator), #{}, TreeNodeMaps),
BTree = #{
id => ID,
root => Root,
properties => parse_properties(Node),
tree_nodes => TreeNodes
},
parse_btree_1(T, TitleMaps#{Title => ID}, TreeMaps#{ID => BTree}, TreeNodeMaps1);
parse_btree_1([], TitleMaps, TreeMaps, TreeNodeMaps) ->
{TitleMaps, TreeMaps, TreeNodeMaps}.
%%
-spec parse_btree_node({bt_node_id(), map(), maps:iterator()}|none, tree_nodes(), tree_nodes()) -> {tree_nodes(), tree_nodes()}.
parse_btree_node({ID, Node, NextIterator}, TreeNodes, TreeNodeMaps) ->
#{<<"name">> := Name, <<"category">> := Category} = Node,
case Category of
<<"tree">> ->
Name1 = Name;
_ ->
Name1 = name_convert(Name)
end,
TreeNode = #{
id => ID,
name => Name1,
category => binary_to_atom(Category, utf8),
properties => parse_properties(Node),
children => parse_children(Node)
},
parse_btree_node(maps:next(NextIterator), TreeNodes#{ID => TreeNode}, TreeNodeMaps#{ID => TreeNode});
parse_btree_node(none, TreeNodes, TreeNodeMaps) ->
{TreeNodes, TreeNodeMaps}.
%%
%% key会转为下划线分割小写atomvalue保持不变
-spec parse_properties(map()) -> properties().
parse_properties(#{<<"properties">> := Properties} = _Node) ->
Fun = fun(K, V, Map) -> Map#{name_convert(K) => V} end,
maps:fold(Fun, #{}, Properties).
%%
-spec parse_children(map()) -> [bt_node_id()].
parse_children(#{<<"child">> := Child} = _Node) ->
[Child];
parse_children(#{<<"children">> := Children} = _Node) ->
Children;
parse_children(_Node) ->
[].
%%
%% PlayerID -> player_id Name -> name
-spec name_convert(unicode:chardata()) -> atom().
name_convert(Name) ->
case name_convert_1(Name) of
<<"_"/utf8, NewName/binary>> ->
binary_to_atom(NewName, utf8);
NewName ->
binary_to_atom(NewName, utf8)
end.
name_convert_1(<<C/utf8, Other/binary>>) when $A =< C, C =< $Z ->
Other1 = name_convert_1(Other),
<<"_"/utf8, (C + 32)/utf8, Other1/binary>>;
name_convert_1(<<C/utf8, Other/binary>>) ->
Other1 = name_convert_1(Other),
<<C/utf8, Other1/binary>>;
name_convert_1(<<>>) ->
<<>>.
%%
-spec do_close_btree_node(bt_uid(), bt_state()) -> bt_state().
do_close_btree_node(NodeID, BTState) ->
BTNode = blackboard:get_btree_node(NodeID),
base_node:do_close(BTNode, BTState).
%%
-spec do_unload_btree_node(bt_uid()) -> ok.
do_unload_btree_node(NodeID) ->
#{children := Children} = blackboard:get_btree_node(NodeID),
do_unload_btree_node_1(Children),
blackboard:erase_btree_node(NodeID),
ok.
do_unload_btree_node_1([NodeID | T]) ->
do_unload_btree_node(NodeID),
do_unload_btree_node_1(T);
do_unload_btree_node_1([]) ->
ok.

+ 157
- 0
src/core/blackboard.erl Ver fichero

@ -0,0 +1,157 @@
-module(blackboard).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([
get_btree_node/1, set_btree_node/1, erase_btree_node/1,
set_global/3, set/4,
get_global/2, get_global/3, get/3, get/4,
remove/2, remove/3
]).
%% Internal API
-export([init_maps_keys/1, erase_node_local/2, erase_btree/1]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc
%%
-spec get_btree_node(bt_uid()) -> bt_node()|undefined.
get_btree_node(NodeID) ->
get({btree_node, NodeID}).
%% @doc
%%
-spec set_btree_node(bt_node()) -> ok.
set_btree_node(#{id := NodeID} = BTNode) ->
put({btree_node, NodeID}, BTNode),
ok.
%% @doc
%%
-spec erase_btree_node(bt_uid()) -> bt_node().
erase_btree_node(NodeID) ->
erase({btree_node, NodeID}).
%% @doc
%%
-spec set_global(term(), term(), bt_state()) -> bt_state().
set_global(Key, Value, #{'$global_maps' := GlobalMaps} = BTState) ->
BTState#{'$global_maps' := GlobalMaps#{Key => Value}}.
%% @doc
%%
-spec set(term(), term(), bt_uid(), bt_state()) -> bt_state().
set(Key, Value, NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
case LocalMaps of
#{NodeID := ValueMaps} ->
LocalMaps1 = LocalMaps#{NodeID := ValueMaps#{Key => Value}};
#{} ->
LocalMaps1 = LocalMaps#{NodeID => #{Key => Value}}
end,
BTState#{'$local_maps' := LocalMaps1}.
%% @doc
%%
-spec get_global(term(), bt_state()) -> term()|undefined.
get_global(Key, BTState) ->
case BTState of
#{'$global_maps' := #{Key := Value}} ->
Value;
#{} ->
undefined
end.
%% @doc
%% Default
-spec get_global(term(), term(), bt_state()) -> term().
get_global(Key, Default, BTState) ->
case BTState of
#{'$global_maps' := #{Key := Value}} ->
Value;
#{} ->
Default
end.
%% @doc
%%
-spec get(term(), bt_uid(), bt_state()) -> term()|undefined.
get(Key, NodeID, BTState) ->
case BTState of
#{'$local_maps' := #{NodeID := #{Key := Value}}} ->
Value;
#{} ->
undefined
end.
%% @doc
%% Default
-spec get(term(), bt_uid(), term(), bt_state()) -> term().
get(Key, NodeID, Default, BTState) ->
case BTState of
#{'$local_maps' := #{NodeID := #{Key := Value}}} ->
Value;
#{} ->
Default
end.
%% @doc
%%
-spec remove(term(), bt_state()) -> bt_state().
remove(Key, #{'$global_maps' := GlobalMaps} = BTState) ->
BTState#{'$global_maps' := maps:remove(Key, GlobalMaps)}.
%% @doc
%%
-spec remove(term(), bt_uid(), bt_state()) -> bt_state().
remove(Key, NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
case LocalMaps of
#{NodeID := ValueMap} ->
LocalMaps1 = LocalMaps#{NodeID := maps:remove(Key, ValueMap)},
BTState#{'$local_maps' := LocalMaps1};
#{} ->
BTState
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%
-spec init_maps_keys(bt_state()) -> bt_state().
init_maps_keys(BTState) ->
BTState1 = init_global(BTState),
init_local(BTState1).
init_global(BTState) ->
case maps:is_key('$global_maps', BTState) of
true ->
BTState;
false ->
BTState#{'$global_maps' => #{}}
end.
init_local(BTState) ->
case maps:is_key('$local_maps', BTState) of
true ->
BTState;
false ->
BTState#{'$local_maps' => #{}}
end.
%%
-spec erase_node_local(bt_uid(), bt_state()) -> bt_state().
erase_node_local(NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
BTState#{'$local_maps' := maps:remove(NodeID, LocalMaps)}.
%%
-spec erase_btree(bt_state()) -> bt_state().
erase_btree(BTState) ->
BTState1 = maps:remove('$global_maps', BTState),
maps:remove('$local_maps', BTState1).

+ 31
- 0
src/decorator/inverter.erl Ver fichero

@ -0,0 +1,31 @@
-module(inverter).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := [ChildID]} = _BTNode, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
{?BT_FAILURE, BTState1};
{?BT_FAILURE, BTState1} ->
{?BT_SUCCESS, BTState1};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 43
- 0
src/decorator/limiter.erl Ver fichero

@ -0,0 +1,43 @@
-module(limiter).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
case blackboard:get(i, ID, BTState) of
I when I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{BTStatus, BTState1} when BTStatus =:= ?BT_SUCCESS orelse BTStatus =:= ?BT_FAILURE ->
BTState2 = blackboard:set(i, I + 1, ID, BTState1),
{BTStatus, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
_I ->
{?BT_FAILURE, BTState}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 39
- 0
src/decorator/max_time.erl Ver fichero

@ -0,0 +1,39 @@
-module(max_time).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(start_time, erlang:system_time(millisecond), ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID] , properties := #{max_time := MaxTime}} = _BTNode, BTState) ->
StartTime = blackboard:get(start_time, ID, BTState),
{BTStatus, BTState1} = base_node:execute(ChildID, BTState),
case MaxTime > erlang:system_time(millisecond) - StartTime of
true ->
{BTStatus, BTState1};
false ->
{?BT_FAILURE, BTState}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(start_time, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 44
- 0
src/decorator/repeat_until_failure.erl Ver fichero

@ -0,0 +1,44 @@
-module(repeat_until_failure).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_ERROR, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(MaxLoop, I + 1, ChildID, ?BT_SUCCESS, BTState1);
{BTStatus, BTState1} ->
{I, BTStatus, BTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 45
- 0
src/decorator/repeat_until_success.erl Ver fichero

@ -0,0 +1,45 @@
-module(repeat_until_success).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_ERROR, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTBTState1} ->
tick_1(MaxLoop, I + 1, ChildID, ?BT_FAILURE, BTBTState1);
{BTStatus, BTBTState1} ->
{I, BTStatus, BTBTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 44
- 0
src/decorator/repeater.erl Ver fichero

@ -0,0 +1,44 @@
-module(repeater).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_SUCCESS, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{BTStatus, BTState1} when BTStatus =:= ?BT_SUCCESS; BTStatus =:= ?BT_FAILURE ->
tick_1(MaxLoop, I + 1, ChildID, BTStatus, BTState1);
{BTStatus, BTState1} ->
{I, BTStatus, BTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 10
- 0
src/eBhv3.app.src Ver fichero

@ -0,0 +1,10 @@
{application, eBhv3,
[{description, "An OTP library"},
{vsn, "0.1.0"},
{registered, []},
{applications, [kernel, stdlib]},
{env,[]},
{modules, []},
{licenses, ["MIT"]},
{links, []}
]}.

+ 3
- 0
src/eBhv3.erl Ver fichero

@ -0,0 +1,3 @@
-module(eBhv3).
-export([]).

Cargando…
Cancelar
Guardar