-
Notifications
You must be signed in to change notification settings - Fork 1
/
creep.js
650 lines (595 loc) · 17.2 KB
/
creep.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
/**
* The creep library : how to manage creeps with basic features that you can override
*/
let basic_work = require('./work.basic');
let body = require('./body');
let constants = require('./constants');
let messages = require('./messages');
let names = require('./names');
let objects = require('./objects');
let path = require('./path');
let rooms = require('./rooms');
let rooms_work = require('./work.rooms');
let shorter_path = require('./shorter_path');
/**
* Use sources() / targets() to find its initial source / target
*
* @type string
*/
module.exports.AUTO = 'AUTO';
/**
* @type boolean|string If string : creep name
*/
module.exports.DEBUG = false;
/**
* This value for spawn() enables the automatic available source terrain finder
*
* @type string
*/
module.exports.ENERGY = 'ENERGY';
/**
* This is returned by targetWork() when job is done
*
* @type string
*/
module.exports.JOB_DONE = 'JOB_DONE';
/**
* @type number
*/
module.exports.NEXT_STEP = 5;
/**
* @type number
*/
module.exports.NO_SOURCE = 1;
/**
* @type number
*/
module.exports.NO_TARGET = 2;
/**
* @type number
*/
module.exports.WAIT = 3;
/**
* Body parts for a starter creep
* CARRY, MOVE, WORK
* - consume 200 energy units
* - harvests 2 energy units per tick
* - carrie 25 energy units per tick
*
* @type string[]
*/
module.exports.body_parts = [CARRY, MOVE, WORK];
/**
* When true, find a new target each time the creep finishes filling in
*
* @type boolean
*/
module.exports.find_next_target = false;
/**
* The default role when this creep is spawned
*
* @type string
*/
module.exports.role = 'creep';
/**
* @type boolean If true, do not find another source when source job is done
*/
module.exports.single_source = false;
/**
* @type boolean If true, do not find another target when target job is done
*/
module.exports.single_target = false;
/**
* If false, disable source work :
* sourceJobDone always returns true, targetJobDone always returns false, sources always returns []
*
* @type boolean
*/
module.exports.source_work = true;
/**
* If false, disable target work :
* sourceJobDone always returns false, targetJobDone always returns true, targets always returns []
*
* @type boolean
*/
module.exports.target_work = true;
/**
* Returns true if the creep can continue its source work, without any consideration about its source state
*
* Default behaviour : the creep is not full of energy
*
* @param creep
*/
module.exports.canWorkSource = function(creep)
{
return !objects.energyFull(creep);
};
/**
* Returns true if the creep can continue its target work, without any consideration about its target state
*
* Default behaviour : the creep has energy
*
* @param creep
*/
module.exports.canWorkTarget = function(creep)
{
return objects.energy(creep) > 0;
};
/**
* Set the source of the creep and return the source.
* If no available source, delete the source from memory and returns null.
*
* @param context RoomObject
* @return object source
*/
module.exports.findSource = function(context)
{
if (context instanceof Creep) {
delete context.memory.source_duration;
if (context.memory.sources) {
context.memory.source = context.memory.sources.shift();
if (!context.memory.sources.length) delete context.memory.sources;
return context.memory.source;
}
context.say('source');
}
else {
this.log(context, 'source');
}
let sources = shorter_path.sort(context, this.sources(context));
if (sources.length) {
let source = sources.shift();
if (context instanceof Creep) {
context.memory.source = source.id;
if (sources.length) context.memory.sources = objects.toIds(sources);
else delete context.memory.sources;
}
return source;
}
if (context instanceof Creep) delete context.memory.source;
return null;
};
/**
* Set the source of the creep and returns the source id.
* If no available source, delete the source from memory and returns null.
*
* @param creep Creep
* @return string|null
*/
module.exports.findSourceId = function(creep)
{
let source = this.findSource(creep);
return source ? source.id : null;
};
/**
* Set the target of the creep and returns the target.
* If no available target, delete the target from memory and return null.
*
* @param context RoomObject
* @return object target
*/
module.exports.findTarget = function(context)
{
if (context instanceof Creep) {
delete context.memory.target_duration;
if (context.memory.targets) {
context.memory.target = context.memory.targets.shift();
if (!context.memory.targets.length) delete context.memory.targets;
return context.memory.target;
}
context.say('target');
}
else {
this.log(context, 'target');
}
let targets = shorter_path.sort(context, this.targets(context));
if (targets.length) {
let target = targets.shift();
if (context instanceof Creep) {
context.memory.target = target.id;
if (targets.length) context.memory.targets = objects.toIds(targets);
else delete context.memory.targets;
}
return target;
}
if (context instanceof Creep) delete context.memory.target;
return null;
};
/**
* Set the target of the creep and returns the target id.
* If no available target, delete the target from memory and return null.
*
* @param creep Creep
* @return string|null
*/
module.exports.findTargetId = function(creep)
{
let target = this.findTarget(creep);
return target ? target.id : null;
};
/**
* @param context RoomObject
* @param args string ...
*/
module.exports.log = function(context, args)
{
if (this.DEBUG !== false) {
args = Array.prototype.slice.call(arguments);
context = args.shift();
if ((this.DEBUG === true) || ((context instanceof Creep) && (this.DEBUG === context.name))) {
console.log.apply(console, args);
}
}
};
/**
* Switch to the next step
*
* @param creep Creep
* @param step string
*/
module.exports.nextStep = function(creep, step)
{
creep.memory.step = step;
if (
(step === 'sourceWork')
&& (creep.memory.source_duration !== undefined)
&& !creep.memory.source_duration--
) {
delete creep.memory.source;
}
if (
(step === 'targetWork')
&& (creep.memory.target_duration !== undefined)
&& !creep.memory.target_duration--
) {
delete creep.memory.target;
}
};
/**
* Sets source duration.
* Call it from sources() in order to change source after an amount source.next switches
* If duration is not set, stays unlimited (remove source_duration from memory)
*
* @param creep Creep
* @param [duration] number
*/
module.exports.setSourceDuration = function(creep, duration)
{
if (!this.single_source && (creep instanceof Creep)) {
if (duration === undefined) delete creep.memory.source_duration;
else creep.memory.source_duration = duration;
}
};
/**
* Sets target duration.
* Call it from targets() in order to change target after an amount of target.next switches
* If duration is not set, stays unlimited (remove source_duration from memory)
*
* @param creep Creep
* @param [duration] number
*/
module.exports.setTargetDuration = function(creep, duration)
{
if (!this.single_target && (creep instanceof Creep)) {
if (duration === undefined) delete creep.memory.target_duration;
else creep.memory.target_duration = duration;
}
};
/**
* Returns true if creep has only one source
*
* @param creep
* @returns boolean
*/
module.exports.singleSource = function(creep)
{
return (creep.memory['single_source'] !== undefined) ? creep.memory.single_source : this.single_source;
};
/**
* Returns true if creep has only one target
*
* @param creep
* @returns boolean
*/
module.exports.singleTarget = function(creep)
{
return (creep.memory['single_target'] !== undefined) ? creep.memory.single_target : this.single_target;
};
/**
* Gets the creep source ready for work
* - if the current one still exists and source job is not done, returns it
* - if find next source, returns it
* - if the current one does not exist anymore and no other source was found, returns null
*
* @param creep Creep
* @returns RoomObject|null
*/
module.exports.source = function(creep)
{
let source = objects.get(creep, creep.memory.source);
if (!source || this.sourceJobDone(creep)) {
source = this.singleSource(creep) ? null : this.findSource(creep);
}
return source;
};
/**
* Count number of creeps into the source
* If context is a creep, don't count this creep as creeps affected to the source
*
* @param source RoomObject
* @param [context] RoomObject If is a creep, does not count this creep
* @returns number
*/
module.exports.sourceCount = function(source, context)
{
let count = 0;
for (let creep_name in Memory.creeps) if (Memory.creeps.hasOwnProperty(creep_name)) {
let creep = Memory.creeps[creep_name];
if ((creep.source === source.id) && (!context || !context.name || (context.name !== creep_name))) {
count ++;
}
}
return count;
};
/**
* The work the creep must do at its source
* Or how it gets its energy from source
*
* @param creep Creep
* @return number 0 if no error, error code if error during the job
*/
module.exports.sourceJob = function(creep)
{
let source = objects.get(creep, creep.memory.source);
this.log(creep, 's: source =', source);
let result = objects.getEnergy(creep, source);
this.log(creep, 's: result =', messages.error(result));
if (
!this.target_work
&& (result === OK)
&& (creep.memory.source_duration !== undefined)
&& !--creep.memory.source_duration
) {
delete creep.memory.source;
}
return result;
};
/**
* Return true if the creep is full of energy
* Store the full information into its memory
*
* @param creep Creep
* @return boolean
*/
module.exports.sourceJobDone = function(creep)
{
let source = objects.get(creep, creep.memory.source);
this.log(creep, 's: source =', source);
let result = !objects.energy(source);
this.log(creep, result ? 's: source job done (source energy empty)' : 's: source job continue (need more energy)');
return result;
};
/**
* A simple sources selector :
* Default is energy sources : dropped energy, and if not container / storage energy
*
* @param context RoomObject
* @return string[] Sources id
*/
module.exports.sources = function(context)
{
if (!this.source_work) return [];
let sources = context.room.find(FIND_DROPPED_ENERGY);
if (sources.length) return sources;
sources = context.room.find(FIND_STRUCTURES, { filter: structure =>
(
(structure.structureType === STRUCTURE_CONTAINER)
|| (structure.structureType === STRUCTURE_LINK)
|| (structure.structureType === STRUCTURE_STORAGE)
)
&& (objects.energy(structure) >= 50)
});
if (sources.length) { this.setSourceDuration(context, 1); return sources; }
if (objects.can(context, WORK)) {
sources = context.room.find(FIND_SOURCES_ACTIVE);
if (sources.length) { this.setSourceDuration(context, 1); return sources; }
}
return [];
};
/**
* The common source work algorithm
*
* @param creep Creep
* @returns number OK if worked well, NEXT_STEP, or an ERR_ constant
*/
module.exports.sourceWork = function(creep)
{
if (!this.source_work) return this.NEXT_STEP;
if (!this.canWorkSource(creep)) {
if (this.target_work && this.canWorkTarget(creep) && this.target(creep)) return this.NEXT_STEP;
return this.WAIT;
}
if (!this.source(creep)) {
if (this.target_work && this.canWorkTarget(creep) && this.target(creep)) return this.NEXT_STEP;
return this.NO_SOURCE;
}
return this.sourceJob(creep);
};
/**
* Spawn a creep, giving it a role, source and target (optionals)
*
* @param [opts] {{ target: RoomObject|RoomPosition|string, source: RoomObject|RoomPosition|string, role: string,
* name: string }}
* @return Creep|null
*/
module.exports.spawn = function(opts)
{
if (!opts) opts = {};
// spawn
if (!opts.spawn && opts.source) opts.spawn = rooms.get(rooms.nameOf(opts.source), 'spawn');
if (!opts.spawn && opts.target) opts.spawn = rooms.get(rooms.nameOf(opts.target), 'spawn');
// body parts
let body_parts = opts.body_parts ? opts.body_parts : this.body_parts;
if (opts.accept_little && opts.spawn.canCreateCreep(body_parts)) {
body_parts = body.parts(body_parts, opts.spawn.room.energyAvailable);
}
// create creep
if (body_parts && !opts.spawn.canCreateCreep(body_parts)) {
if (!opts.name) opts.name = names.chooseName();
if (!opts.role) opts.role = this.role;
if (!opts.source) opts.source = this.findSource(opts.spawn);
if (!opts.target) opts.target = this.findTarget(opts.spawn);
// source / target id
if (opts.source && (opts.source instanceof RoomObject)) opts.source = opts.source.id;
if (opts.target && (opts.target instanceof RoomObject)) opts.target = opts.target.id;
// prepare creep memory
let memory = { role: opts.role };
if (opts.source) memory.source = opts.source;
if (opts.target) memory.target = opts.target;
// spawn a new creep
let creep_name = opts.spawn.createCreep(body_parts, opts.name, memory);
this.log(null, 'spawns ' + opts.role + ' ' + creep_name);
return Game.creeps[creep_name];
}
return null;
};
/**
* Gets the creep target ready for work
* - if the current one still exists and target job is not done, returns it
* - if find next target, returns it
* - if the current one does not exist anymore and no other target was found, returns null
*
* @param creep Creep
* @returns RoomObject|null
*/
module.exports.target = function(creep)
{
let target = objects.get(creep, creep.memory.target);
if (!target || this.targetJobDone(creep)) {
target = this.singleTarget(creep) ? null : this.findTarget(creep);
}
return target;
};
/**
* Count number of creeps into the target
* If context is a creep, don't count this creep as creeps affected to the target
*
* @param target RoomObject
* @param [context] RoomObject If is a creep, does not count this creep
* @returns number
*/
module.exports.targetCount = function(target, context)
{
let count = 0;
for (let creep_name in Memory.creeps) if (Memory.creeps.hasOwnProperty(creep_name)) {
let creep = Memory.creeps[creep_name];
if ((creep.target === target.id) && (!context || !context.name || (context.name !== creep_name))) {
count ++;
}
}
return count;
};
/**
* The work the creep must do at its target
* The default is to transfer its energy to the target
*
* @param creep Creep
* @return number
*/
module.exports.targetJob = function(creep)
{
let target = objects.get(creep, creep.memory.target);
this.log(creep, 't: target =', target);
let result = objects.putEnergy(creep, target);
this.log(creep, 't: result =', messages.error(result));
if (
!this.source_work
&& (result === OK)
&& (creep.memory.target_duration !== undefined)
&& !--creep.memory.target_duration
) {
delete creep.memory.target;
}
return result;
};
/**
* Job is done when the target is filled with energy
*
* @param creep Creep
* @return boolean
*/
module.exports.targetJobDone = function(creep)
{
let target = objects.get(creep, creep.memory.target);
this.log(creep, 't: target =', target);
let result = objects.energyFull(target);
this.log(
creep, result ? 't: target job done (target energy full)' : 't: target job continue (target energy not full)'
);
return result;
};
/**
* Find an available target for the harvester : the first not filled-in spawn
*
* @param context RoomObject
* @return StructureSpawn[]
**/
module.exports.targets = function(context)
{
// the nearest extension without energy into the current room
let targets = context.room.find(FIND_STRUCTURES, { filter: structure =>
(structure.structureType === STRUCTURE_EXTENSION) && !objects.energyFull(structure)
});
if (targets.length) return targets;
// the nearest spawn without energy into the current room
targets = context.room.find(FIND_STRUCTURES, { filter: structure =>
(structure.structureType === STRUCTURE_SPAWN) && !objects.energyFull(structure)
});
if (targets.length) return targets;
// the nearest container or storage
targets = context.room.find(FIND_STRUCTURES, { filter: structure =>
((structure.structureType === STRUCTURE_CONTAINER) || (structure.structureType === STRUCTURE_STORAGE))
&& !objects.energyFull(structure)
});
if (targets.length) { this.setTargetDuration(context, 1); return targets; }
return [];
};
/**
* The common target work algorithm
*
* @param creep Creep
* @returns number OK if worked well, else an ERR_ constant
*/
module.exports.targetWork = function(creep)
{
if (!this.target_work) return this.NEXT_STEP;
if (!this.canWorkTarget(creep)) {
if (this.source_work && this.canWorkSource(creep) && this.source(creep)) return this.NEXT_STEP;
return this.WAIT;
}
if (!this.target(creep)) {
if (this.source_work && this.canWorkSource(creep) && this.source(creep)) return this.NEXT_STEP;
return this.NO_TARGET;
}
return this.targetJob(creep);
};
/**
* Let's work ! work depends on creep role and work mode
*
* @param creep Creep
**/
module.exports.work = function(creep)
{
this.log(
creep,
'WORK',
creep.name,
creep.memory.role,
creep.memory.room_role ? creep.memory.room_role : 'basic',
creep.memory.step ? creep.memory.step : 'no-step'
);
this.log(creep, 'w: single source =', this.singleSource(creep), ', single target =', this.singleTarget(creep));
this.log(creep, 'w: source work =', this.source_work, ', target work =', this.target_work);
if (creep.memory.room_role) rooms_work.work(this, creep);
else basic_work.work(this, creep);
};