#include #include #include #include "bt.h" #include "map.h" #define QUOTE(...) #__VA_ARGS__ static void ppmapget(Node*, char*, Node*); static void ppmapset(Node*, char*, Node*); static void ppmapgc(Pointer*); static Metatable mapmeta = { .index = ppmapget, .newindex = ppmapset, .gc = ppmapgc, }; static void ppmapget(Node *n, char *nam, Node *res) { int val; Pointer *p; Map *m; p = n->store.pval; assert(p->meta == &mapmeta); m = p->v; val = (int)mapget(m, nam); res->op = OCONST; res->store.type = TINT; res->store.ival = val; } static void ppmapset(Node *n, char *nam, Node *res) { Pointer *p; Map *m; p = n->store.pval; assert(p->meta == &mapmeta); m = p->v; if(res->store.type != TINT) pperror("map expects a int value"); mapset(m, nam, (void*)res->store.ival); } static void ppmapgc(Pointer *p) { Map *m; assert(p->meta = &mapmeta); m = p->v; // woops //mapfree(m); } static void ppmap(Node *r, Node **av, int na) { Map *map; USED(av); USED(na); map = mapnew(nil); r->op = OCONST; r->store.type = TPOINTER; r->store.pval = pppointernode(map, &mapmeta); } void ppnrand(Node *r, Node **av, int na) { Node res; if(na != 1) pperror("nrand: not enough arguments"); ppexpr(av[0], &res); if(res.store.type != TINT) pperror("nrand: expected int"); r->op = OCONST; r->store.type = TINT; r->store.ival = nrand(res.store.ival); } static void ppleafgc(Pointer *p) { BehaviorNode *node; node = p->v; USED(node); //fprint(2, "shoulda free'd behavior node"); } static Metatable btmeta = { .index = nil, .newindex = nil, .gc = ppleafgc, }; int btthunk(void *ctx, void *a) { char *fn; Node *agent; Node res; fn = ctx; agent = a; ppcall(fn, agent, &res); if(res.op != OCONST || res.store.type != TINT) pperror("expected integer return from leaf"); return res.store.ival; } static void ppleaf(Node *r, Node **av, int na) { Node name, fun; BehaviorNode *leaf; if(na != 2) pperror("leaf: wrong # arguments"); ppexpr(av[0], &name); ppexpr(av[1], &fun); if(name.store.type != TSTRING) pperror("leaf: expected string"); if(fun.store.type != TCODE) pperror("leaf: expected function"); leaf = btleaf(name.store.string->string, btthunk, av[1]->sym->name); r->op = OCONST; r->store.type = TPOINTER; r->store.pval = pppointernode(leaf, &btmeta); } static void ppsequence(Node *r, Node **av, int na) { int i; Node res; BehaviorNode *seq; if(na < 2) pperror("sequence: expected 2+ arguments"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("sequence: expected string name"); seq = btsequence(res.store.string->string, nil); assert(seq != nil); for(i = 1; i < na; i++){ ppexpr(av[i], &res); if(res.store.type != TPOINTER || res.store.pval->meta != &btmeta){ btfreenode(seq); pperror("expected BehaviorNode"); } assert(btaddbranch(seq, res.store.pval->v) == 0); } r->op = OCONST; r->store.type = TPOINTER; r->store.pval = pppointernode(seq, &btmeta); } static void pppriority(Node *r, Node **av, int na) { int i; Node res; BehaviorNode *prio; if(na < 2) pperror("priority: expected 2+ arguments"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("priority: expected string name"); prio = btpriority(res.store.string->string, nil); assert(prio != nil); for(i = 1; i < na; i++){ ppexpr(av[i], &res); if(res.store.type != TPOINTER || res.store.pval->meta != &btmeta){ btfreenode(prio); pperror("priority: expected BehaviorNode"); } assert(btaddbranch(prio, res.store.pval->v) == 0); } r->op = OCONST; r->store.type = TPOINTER; r->store.pval = pppointernode(prio, &btmeta); } void test_bt(BehaviorNode *root) { int i; Map *agent; Behavior *b; BehaviorState *bs; agent = mapnew(nil); mapset(agent, "hunger", (void*)10); mapset(agent, "food", (void*)0); b = btroot(root); bs = btstatenew(b); while((int)mapget(agent, "hunger") > 0){ bttick(b, bs, pppointer(agent, &mapmeta)); print("hunger=%d food=%d\n", (int)mapget(agent, "hunger"), (int)mapget(agent, "food")); //sleep(10); gc(1); } ppcall("interpret", ppstring("_thiscmd=nil;"), nil); /* XXX: agent currently not used for statefree / cancellation */ btstatefree(bs, nil); btfree(b); mapfree(agent); } static void usage(void) { fprint(2, "usage: %s\n", argv0); exits("usage"); } static char ppscript[]; void main(int argc, char *argv[]) { int i; Node res; ARGBEGIN{ default: usage(); }ARGEND srand(nsec()&0xFFFF); for(i = 0; i < 5; i++){ ppsetup(); ppbind("map", ppmap); ppbind("nrand", ppnrand); ppbind("leaf", ppleaf); ppbind("sequence", ppsequence); ppbind("priority", pppriority); if(ppcall("interpret", ppstring(ppscript), nil) < 0) sysfatal("error"); if(ppcall("mkbehavior", nil, &res) < 0) sysfatal("error"); test_bt(res.store.pval->v); ppdestroy(); } exits(nil); } static char ppscript[] = QUOTE( FRESH = 0; RUNNING = 1; FAIL = 2; SUCCESS = 3; tasknames = { "fresh", "running", "fail", "success", "max" }; defn pstate(state) { print(tasknames[state], "\n"); return state; } defn rstate() { local state; state = nrand(SUCCESS)+1; return pstate(state); } defn findfood(agent) { local r; r = nrand(5); print("finding food\n"); if r == 0 then return pstate(SUCCESS); else if r == 1 then return pstate(FAIL); return pstate(RUNNING); } defn movetofood(agent) { print("moving to food\n"); return rstate(); } defn pickupfood(agent) { print("picking up food\n"); agent.food = agent.food + nrand(3)+1; return pstate(SUCCESS); } defn goout(agent) { print("going outside\n"); return rstate(); } defn eatfood(agent) { print("eating food\n"); if agent.food > 0 then { agent.food = agent.food - 1; agent.hunger = agent.hunger - 1; if agent.food == 0 then return pstate(SUCCESS); return pstate(RUNNING); } return pstate(FAIL); } defn mkbehavior() { return sequence("root", priority("get", sequence("nearby food", leaf("find food", findfood), leaf("move to food", movetofood), leaf("pick up food", pickupfood) ), leaf("go outside", goout) ), leaf("eat food", eatfood), ); } );