GithubHelp home page GithubHelp logo

active-logic / activelogic-cs Goto Github PK

View Code? Open in Web Editor NEW
103.0 4.0 7.0 654 KB

Behavior Trees and Case Logic for C#7

License: GNU Affero General Public License v3.0

C# 99.72% Shell 0.28%
behavior-tree behaviour-trees game-development control-flow control-systems game-ai

activelogic-cs's People

Contributors

dherault avatar eelstork avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

activelogic-cs's Issues

Retire `AL_BEST_PERF`

AL_BEST_PERF does not have a practical use; using AL with this flag turned on is not sane, and simple profiling (along with performance guidelines) will suffice.

RoR-enabled decorator may fail after ordered composite.

Considering:

status Step() => Sequence()[ 
    and ? A : 
    and ? B : repeat] && Wait(1f);

In this and related cases, even if A and B return done immediately, the ordered sequence still requires 2 frames to complete. This will then introduce a discontinuity that resets the Wait decorator, potentially causing a freeze.

NOTE: for the repeat terminal see #36

Split the quick-start guide

Covering reset management and concurrency operators may be good.
Would prefer doing this around a concrete example, perhaps from Kabuki?

Modularize time API

Currently time related decorators (Wait, Cooldown, Timeout, ...) use Unity engine time or default to System (.NET) time. This is not welcoming to other game engines.
Also does not help when a different time representation is used; in a TBS for example, a 'turn' may be the high level time unit, used alongside "game time".
Likely the overhead of an indirection here would not be significant.

Autogen Task variant(s)

Other than inheriting MonoBehaviour there is no difference between Task and UTask; imp. - perhaps adding a Howl feature?

Ordered composites do not greedily evaluate.

With a sequence A && B && C, whereas stateless evaluation will move to B without delay if A is done, the equivalent ordered sequence will only evaluate A, leaving further evaluation to the next frame.

This conflicts with the RoR feature (#35); RoR leniency patches the issue, however this is not ideal.

Fixing this requires changing the ordered composite syntax.

Plain status constants

For logging purposes status constants (done, cont, fail) are postfixed with a () which may optionally include logging information:

return done(log && "All caught up");

Without disregarding #28, test-oriented workflows are less dependent on logging and, overall, an option to use plain status constants would be welcome.

Still unclear whether both plain and annotated status constants may coexist in the same class (it only looks impossible). That isn't really a problem though.

Also, marginal performance gains with raw constants.

Simplify certainties API

Semantics for certainties are too complex.

  • Status constants and other members may follow the status naming convention - done, cont, fail
  • Although it may seem unsafe (and technically is), when returning certainties an implicit cast from status may be harmless. Type safety would be decreased but we can still catch this at runtime.

Reset-on-Exit/Reset-on-Crit

Reset-on-Resume (RoR) is a relatively new feature in Active Logic. Experiments show that Reset-on-Resume is not dependable enough, since it may cause issues when repeating tasks seamlessly (see #37)
Reset-on-Exit is safer, and may be implemented as follows:

roe DoSomething() => `cx() && Wait(0.5) && ... ;  

Or

roe DoSomething(){
    // some preliminary work
    return cx() && Once()?[ task ] ... ;   
}

Above syntax is primarily attractive because it does not require delimiters; other syntaxes are possible; this may come with a few desirable features:

  • RoE decorators fail if no RoE context is provided (runtime check)
  • Stateless control does not require a context, avoiding both syntax and performance overheads.
  • cx() and roe are interdependent, so one cannot accidentally be used without another (compile check)

`Do`, `Cont`, `Fail` and `Try`

In places, AL has object-context functions looking like:

action Do(object arg){ return @void(); }

These functions serve no purpose other than integrating (mostly assignment calls) with expression bodied members, however they are still useful. As part of updating certainties, it would be nice to look into this.

Considerations:

  • These functions do not need an object context. Would be better placed under the status static context, making them widely available (DRY).
  • Although Do is correctly formulated as an action, design-wise likely as not we want done or cont.
  • Certainties generate syntactic overheads. Intentional yes, but they may not be wanted here.
  • Do and Run as separate functions may be good since it clarifies the return type better than prefixing a unary op.
  • With an object or boolean assignment, there is a straightforward conversion from the argument to a status value. We do not always need a resulting status. When we do, a Try function may be nice.

Support legacy animation (Unity)

A while back drafted a utility to help with Mecanim integration; want a common interface covering legacy and Mecanim, for simple animation needs

Setup a dev branch?

After the next Asset Store release a dev branch will be needed. This is because APIs are still evolving, along with documentation; on master, doc should reflect what is available in the Asset Store.

Reset stateful constructs on resume

Stateful constructs: most decorators; ordered composites, mutable composites
Reset on resume: if control has not entered a branch for one or more frames, upon re-entry the stateful construct should reset.

Benefit
With many stateful constructs the expectation when an agent dropped the associate task for a while, is that state related to said task should revert to its default state. Invoking Reset() manually is possible, however manually managing decorator state is not desirable.

`Wait` task

Now that RoR is in place, a simple wait statement should be possible

Fix `undef` or remove it

After updating certainties undef() does not support convert to pending/impending anymore. If this is really useful, fix it; otherwise, remove.

Avoid duplicating `Gate`

Does not impact functionality, but we have 2-3 implementations of gate and status ref which are very closely related.

Right now it is not obvious whether this can be done or not, because null-conditional decorators rely on typing, and how far generification can help with this is unclear.

Generic gate:

namespace Active.Core{
public readonly struct Gate<T> where T : AbstractDecorator{

    readonly T owner; readonly LogData logData;

    internal Gate(T owner, LogData logData)
    { this.owner = owner; this.logData = logData; }

    public StatusRef this[status s]{ get{
        #if !AL_OPTIMIZE
        owner.target = s.targetScope;
        #endif
        owner.OnStatus(s);
        return new StatusRef(s, logData);
    }}

}}

Works when moving target and a couple other things to AbstractDecorator, however type dependency on StatusRef, which may be touchier.

Automate Logging

This is to track current work towards automating log-traces.
Found some success setting up AspectInjector but this is not easy to integrate with Unity. Having a look at Mono.Cecil (which AspectInjector at least partially relies on) as it's likely a faster way to investigate related issues, and may suffice for the purpose.

`Once` should return fail/complete status of decorated task, not `fail`

Originally Once was implemented as a (thread safe) conditional. As such it returns:

  • cont until the decorated task succeeds or fails (good)
  • done or fail upon task completion (good)
  • fail throughout later iterations, and until reset (bad)

In the third case, Once should retain and return the final status of the decorated task.

There isn't a solution here that's going to be compatible with thread pooling but, thread safety is on a best effort basis, not a requirement across the board.

Rarely used the Once decorator until recently. Seems a good alternative to ordered composites in a typical workflow, where stateless by default works well, and stateful nodes are ~1/10.

Make certainties type safe with sequences and selectors

In the near future, all certainties (loop, pending, impending, ...) will be implicitly convertible to status as we take a more integer approach to the certainties API.

  • Experimentally demonstrated that (with significant limitations) AL calculus can be made type safe. For example we can disallow loop && status (since a loop will never allow the right hand to evaluate) at compile time.
  • While this cannot be done promptly, fail-fast policies (runtime) will catch bugs where compile-time safety is lacking.
  • File upstream issues to csharplang.

Current approach, where a certainty is convertible to status via labeling (loop.forever && status) is a wooden shield. It does not actively prevent errors, only expose contradiction semantically. With that, conversions still occur, so the referred limitations already apply.

Pedantic & forms (such as the current action & status in lieu of action && status) will be dropped. Although logically correct this creates a situation where a single &, used accidentally and causing unwanted pseudo-parallelism, does not really stand out.

Lean jargon at the top of introduction

Could be just me but scanning the readme I see "explicit data structures" and "visual programming" which is pretty much the opposite of what this library does.

tie / @while operator

Commonly known composites effect the next task if the prior task has succeeded (sequence) or failed (selector); although I initially did not see a use for this the alternative effects the next task while the prior task is running.
For example:

1 - play an animation (walk) @while moving forward.
2 - keep a light on while there is power

So, in some cases the result of A ⨀ B is independent of B, in others, such as when B represents a needed resource, failing B will also fail A.

Ideally this would implementing using shorting operators. With C# shorting operators are exhausted (there are 2); decorators may be used.

Implicitly convert status from bool

Implicit conversion from bool has the disadvantage that little logging info is provided in this case (other than "it came from bool").
Still, explicit conversion from bool is too much hassle.

Cycling stateful tasks

RoR decorators reset on re-entry. That is, an RoR-enabled decorator will reset if control skips ticking the decorator for one frame or more.
This is somewhat different from the conventional reset on exit behavior seen in some BT libraries.
With cycling tasks, RoR may cause unexpected results:

status Step() => Once()?[ A ] && B

Repeating invocations of Step do not cycle A and B. This is because the Once decorator is ticked at every frame.

AL does not enforce a "parent node" concept; investigating.

@for and @forall decorators

Currently there isn't a decorator for applying an action to a list of target objects.
For example, if we had an IEnumerable list of waypoints, we may wish to visit all waypoints in turn; maybe something like:

@for(waypoints)?[ Reach ]  // Reach: Func<T, status>

@forall would do the same assuming parallelism.

`repeat` terminal

Ordered composites have the end and loop terminals.

  • end finalizes the state of a composite, in that an ended sequence will always return done, whereas an ended selector will always return fail, until the underlying ordered composite is reset.
  • Upon executing the last subtask, looping composites return cont, also resetting the underlying task iterator.

Currently we lack a repeat mode allowing a composite to reset the task iterator on success/failure. This is inconvenient because many tasks are repeatable.

Coverage

Considering Coverlet, likely integrated with Coveralls

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.