Skip to content

Commit

Permalink
v0.4.0 - Version Push (#13)
Browse files Browse the repository at this point in the history
## [0.4.0] - 2022-02-16
- **[Added]** `RandomPickState` which randomly pick one of the children to be executed. All children has uniform probability being picked. 
- **[Added]** added `get_status` method in `State` which return the status of the State.
- **[Changed]** name for `State` is now optional, the default is its class name.
- **[Changed]** streamlined `ParallelState` to prevent potential interrupt race issues. Enable `AtLeastOneState` to overwrite two function for its functionality.
- **[Changed]** `ParallelState` now ignores empty/None States if passed in as children.
- **[Fixed]** condition in `AtLeastOneState` where other states aren't destroyed when exiting under success condition.
  • Loading branch information
xiangzhi authored Feb 17, 2022
1 parent b403192 commit 2110c7a
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 12 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Changelog

## [Unreleased]
## [0.4.0] - 2022-02-16
- **[Added]** `RandomPickState` which randomly pick one of the children to be executed. All children has uniform probability being picked.
- **[Added]** added `get_status` method in `State` which return the status of the State.
- **[Changed]** name for `State` is now optional, the default is its class name.
- **[Changed]** streamlined `ParallelState` to prevent potential interrupt race issues. Enable `AtLeastOneState` to overwrite two function for its functionality.
- **[Changed]** `ParallelState` now ignores empty/None States if passed in as children.
- **[Fixed]** condition in `AtLeastOneState` where other states aren't destroyed when exiting under success condition.

## [0.3.5] - 2021-06-15
Expand Down
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,4 @@ m2.run()
![Example](examples/readme.png)

## Documentation
You can view more detailed documentation by following this [link](https://behavior-machine.readthedocs.io/en/latest/index.html)


## TODO Lists:
1. Implement flows for `ParallelState`. The idea would be the same flow value is duplicated (deep?) for each state
2. Rethink flows for `SelectorState`.
3. A better logging system.
You can view more detailed documentation by following this [link](https://behavior-machine.readthedocs.io/en/latest/index.html)
14 changes: 12 additions & 2 deletions behavior_machine/core/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class State():
_state_last_start_time: float
_state_last_end_time: float

def __init__(self, name):
self._name = name
def __init__(self, name: str = ""):
self._name = name if name != "" else self.__class__.__name__
self._transitions = []
self._run_thread = None
self._interupted_event = threading.Event()
Expand Down Expand Up @@ -80,6 +80,16 @@ def checkStatus(self, compare: StateStatus) -> bool:
warnings.warn("use check_status instead", DeprecationWarning)
return self.check_status(compare)

def get_status(self) -> StateStatus:
"""Get the state's status if completed.
Returns
-------
StateStatus
The status of the state.
"""
return self._status

def add_transition(self, cond: typing.Callable[['State', Board], bool], next_state: 'State') -> None:
"""Add transition to the state. Provide a checking method (cond) that when returns true, will
signal this state to transition to the state associated. Note, the transition is test in a list. If multiple
Expand Down
1 change: 1 addition & 0 deletions behavior_machine/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from .selector_state import SelectorState
from .common_state import SetFlowState, SetFlowFromBoardState, SaveFlowState, SetBoardState, GetBoardState
from .atleastone_state import AtLeastOneState
from .probability_states import RandomPickState
2 changes: 1 addition & 1 deletion behavior_machine/library/parallel_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ParallelState(NestedState):

def __init__(self, name, children: list = None):
super(ParallelState, self).__init__(name)
self._children = [] if children is None else children
self._children = [] if children is None else list(filter(None, children))
self._state_complete_event = threading.Event()
self._child_exception = False

Expand Down
40 changes: 40 additions & 0 deletions behavior_machine/library/probability_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import threading
from ..core import StateStatus, State, NestedState, Board
import typing
import random
import threading

class RandomPickState(NestedState):

_picked_state: State
_children: typing.Sequence[State]
_lock: threading.RLock

def __init__(self, children: typing.Sequence[State],name = ""):
self._children = children
self._picked_state = None
self._lock = threading.RLock()
super().__init__(name)

def execute(self, board: Board) -> StateStatus:

with self._lock:
self._picked_state = random.choice(self._children)
self._picked_state.start(board)

self._picked_state.wait()

# set the flow out and pass the status out.
self.flow_out = self._picked_state.flow_out
result_status = self._picked_state.get_status()
with self._lock:
self._picked_state = None
return result_status

def interrupt(self, timeout: float = None) -> bool:
# we have a lock here just in case it suddenly become None when interrupting.
self.signal_interrupt()
with self._lock:
if self._picked_state is not None:
return self._picked_state.interrupt(timeout)
return True
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
long_description = fh.read()

setuptools.setup(name='behavior_machine',
version='0.3.5',
version='0.4.0',
description='An implementation of a behavior tree + hierarchical state machine hybrid.',
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
29 changes: 29 additions & 0 deletions tests/library/prob_states_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from behavior_machine.core import Machine, State, StateStatus, Board
from behavior_machine.library import RandomPickState


def test_random_pick():

c1 = 0
c2 = 0
class s1(State):
def execute(self, board: Board) -> StateStatus:
nonlocal c1
c1 += 1
return StateStatus.SUCCESS
class s2(State):
def execute(self, board: Board) -> StateStatus:
nonlocal c2
c2 += 1
return StateStatus.SUCCESS

ranPick = RandomPickState(children=[
s1(),
s2()
])

for i in range(0,1000):
ranPick.start(None)
ranPick.wait()
assert 450 < c1 < 550
assert 450 < c2 < 550

0 comments on commit 2110c7a

Please sign in to comment.