forked from screepers/screeps-snippets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpushdownAutomataStateMachine.js
100 lines (91 loc) · 2.78 KB
/
pushdownAutomataStateMachine.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* Posted 18 October 2017 by @warinternal
*
* Pushdown automata state machine
*/
/** Call this to run the current state */
RoomObject.prototype.invokeState = function() {
if(!this.memory.stack || !this.memory.stack.length)
return false;
var [[state,scope]] = this.memory.stack;
var method = `run${state}`;
if(!this[method])
return false;
this[method](scope);
return true;
};
/**
* @param {string} [defaultState] - Fallback state if none defined.
*/
RoomObject.prototype.getState = function (defaultState = 'I') {
if(!this.memory.stack)
return defaultState;
return this.memory.stack[0][0] || defaultState;
};
/**
* @param {string} state - Name of state to switch to.
* @param {*} scope - Any data you want to supply to the state.
*/
RoomObject.prototype.setState = function (state, scope) {
if (state == null)
throw new TypeError('State can not be null');
if (!this.memory.stack)
this.memory.stack = [[]];
this.memory.stack[0] = [state, scope];
return state;
};
/**
* @param {string} state - Name of state to push
* @param {*} scope - Any data you want to supply to the state.
*/
RoomObject.prototype.pushState = function (state, scope={}) {
if (!this.memory.stack)
this.memory.stack = [];
var method = `run${state}`;
if (this[method] == null)
throw new Error(`No such state or action ${method}`);
if (this.memory.stack.length >= 100)
throw new Error('Automata stack limit exceeded');
this.memory.stack.unshift([state, scope]);
return state;
};
/** Pop the current state off the stack */
RoomObject.prototype.popState = function () {
if (!this.memory.stack || !this.memory.stack.length)
return;
const [state] = this.memory.stack.shift();
if (!this.memory.stack.length)
this.memory.stack = undefined;
};
/** Clear the stack */
RoomObject.prototype.clearState = function() {
this.memory.stack = undefined;
};
/** Example state - goto location */
Creep.prototype.runGoto = function (scope) {
var { pos, range = 1 } = scope;
var roomPos = _.create(RoomPosition.prototype, pos);
if (this.moveTo(roomPos, { range: range }) === ERR_NO_PATH)
this.popState();
};
/** Example state - goto room */
Creep.prototype.runGotoRoom = function (scope) {
if (this.moveToRoom(scope) === ERR_NO_PATH)
this.popState();
};
/**
* Example - Heal self and flee
* Push this state when you start to get hurt. Creep will heal
* and go back to what it was doing. If it keeps getting hurt, it
* will leave the room, and try to go back to healing.
*/
Creep.prototype.runHealSelf = function (scope) {
this.heal(this);
// take this opportunity to flee
if (this.hits >= this.hitsMax) { // If we're back to full, we're done
return this.popState();
} else if(this.hits / this.hitsMax < 0.5) { // Otherwise run away
// Find neighboring room
this.pushState('GotoRoom', /* neighbor */);
}
};