-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOrders.rb
855 lines (675 loc) · 16.8 KB
/
Orders.rb
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
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
class Order
attr_accessor :order, :offset
def initialize square, order, offset = nil
@square = square
@order = order
@offset = offset
@liaison = nil
end
def square
sq = @square
if @square.respond_to? :square
sq = @square.square
end
if @offset.nil?
sq
else
sq.rel @offset
end
end
def to_s
str = ""
unless @offset.nil?
str = ", offset #{ @offset }"
end
"Order #{ order }, #{ @square }#{ str }"
end
def square= v
@square = v
end
def sq_int
@square
end
def target? t
@square == t
end
def == a
sq_int.class ==a.sq_int.class and # target square can have differing classes
sq_int.row == a.sq_int.row and
sq_int.col == a.sq_int.col and
order == a.order and
( ( offset.nil? and a.offset.nil? ) or
( offset[0] == a.offset[0] and offset[1] == a.offset[1] )
)
end
def add_offset offs
if @offset.nil?
@offset = offs
else
@offset[0] += offs[0]
@offset[1] += offs[1]
end
end
def clear_liaison
@liaison = nil
end
def handle_liaison cur_sq, ai
sq = square
if @liaison
# First condition is to keep on moving to final target,
# when all liaisons are passed.
if @liaison != sq and ( @liaison == cur_sq or $region.clear_path(cur_sq, @liaison ) )
$logger.info { "Order #{ order } clear path to liaison #{ @liaison }" }
@liaison = nil
end
end
unless @liaison
liaison = $region.path_direction cur_sq, sq
if liaison.nil?
$logger.info { "WARNING: No liaison for order #{ order } to target #{ sq }" }
# We must have drifted off the path - restart search
Region.add_searches sq, [ cur_sq ], true
# Don' specify move at this moment
return nil
elsif false === liaison
$logger.info "no liaison needed - move directly"
# Note that we use the liaison member for the target move
@liaison = sq
else
@liaison = liaison
end
end
$logger.info { "current #{ cur_sq } moving to #{ @liaison }" }
ai.map[ @liaison.row][ @liaison.col ]
end
end
module Orders
attr_accessor :orders
def orders_init
@orders = []
end
private
Order_priority = [
:EVADE_GOTO,
:FORAGE,
:ATTACK,
:ASSEMBLE,
:DEFEND_HILL,
:RAZE,
:DEFEND,
:GOTO,
:HARVEST
]
def sort_orders
ai.turn.check_maxed_out
$logger.info {
str = ""
@orders.each do |o|
str << "#{o }" + ", "
end
"Sorting orders pre: " + str
}
# if none of the following orders present, then GOTO and FORAGE have equal standing
other_present = true
if false
other_present = false
[ :ATTACK, :ASSEMBLE, :DEFEND_HILL, :RAZE, :DEFEND ].each do |order|
if has_order order
other_present = true
break
end
end
end
# Nearest orders first
items = {}
@orders.each { |a|
items[a] = $pointcache.get self.pos, a.square
}
@orders.sort! do |a,b|
# Food goes before rest
if ( other_present and a.order == :FORAGE and b.order == :FORAGE ) or
( not other_present and [ :FORAGE, :GOTO].include? a.order and
[:FORAGE, :GOTO ].include? b.order )
itema = items[a]
itemb = items[b]
# valid items first
if itema.nil? and not itemb.nil?
1
elsif not itema.nil? and itemb.nil?
-1
elsif itema.nil? and itemb.nil?
0
elsif itema[3] and not itemb[3]
1
elsif not itema[3] and itemb[3]
-1
else
itema[0] <=> itemb[0]
end
else
index_a = Order_priority.index a.order
index_a = Order_priority.length if index_a.nil?
index_b = Order_priority.index b.order
index_b = Order_priority.length if index_b.nil?
index_a <=> index_b
end
end
$logger.info {
str = ""
@orders.each do |o|
str << "#{o.order }" + ", "
end
"Sorted orders: " + str
}
end
def check_order_distance n
# For a long-distance order, check if we need to insert an interim order here
if n.order != :GOTO_EVASION
target = $region.path_direction self.square, n.square
if target and target != n.square
$logger.info "Adding extra GOTO_EVASION to #{ target }"
@orders.insert 0, Order.new(target, :GOTO_EVASION )
# Note that this is the highest priority order and we are inserting
# it at front. No need to sort
end
end
end
public
#
# Returns true if order added, false otherwise
#
def set_order square, what, offset = nil, from_handle_orders = false
n = Order.new(square, what, offset)
#$logger.info { "Trying #{ n } for #{ self.to_s }" }
if collective_leader? and
what == :FORAGE and
( collective_assembled? or
(attacked? and attack_distance.in_peril? )
)
$logger.info { "Collective leader #{ self } doesn't accept order #{ what } while in conflict." }
return false
end
if not $pointcache.can_reach self.square, square
# ignore order
$logger.info "#{ self } can't reach target #{ n }"
return false
end
square = n.square
if square.water?
$logger.info { "Target #{ square.to_s } is water; determine nearest land" }
offset = nearest_non_water square
unless offset.nil?
$logger.info { "Found land at offset #{ offset }" }
n.add_offset offset
square = ai.map[n.square.row][ n.square.col ]
end
end
if square.water?
$logger.info { "ERROR: Target #{ square.to_s } is still water" }
end
@orders.each do |o|
if o.order == what
if [ :HARVEST, :DEFEND ].include? what
$logger.info "Already doing order #{ what }"
return false
end
end
if o == n
$logger.info "Order already present"
return false
end
end
if [ :EVADE_GOTO, :FORAGE, :GOTO ].include? what
clear_order :EVADE_GOTO
end
if [ :GOTO, :FORAGE ].include? what
clear_order :GOTO
#evade_reset
end
$logger.info { "Setting order #{ what } on square #{ square.to_s } for #{ self.to_s }" }
# ASSEMBLE overrides the rest of the orders
clear_orders if what == :ASSEMBLE
# Reset all liaisons of current orders, otherwise
# the ant may possibly move in a wrong direction if
# new order is cleared
@orders.each { |o| o.clear_liaison }
@orders.insert 0,n
#check_order_distance n
evade_reset
sort_orders
if ai.turn.maxed_out?
# Maxed out situation
# If at all possible, handle orders right away,
# So that we get as many orders as possible through
if from_handle_orders
$logger.info "Called from handle_orders; not recursing"
else
handle_orders
end
end
true
end
def clear_orders
p = find_order :HARVEST
if p
ai.harvesters.remove self if ai.harvesters
end
@orders.each do |o|
next unless o.order == :FORAGE
ai.food.remove_ant self, [ o.sq_int.row, o.sq_int.col ]
end
@orders = []
evade_reset
end
def clear_order order
p = find_order order
if p
if order == :HARVEST
ai.harvesters.remove self if ai.harvesters
end
if order == :FORAGE
ai.food.remove_ant self, [ p.sq_int.row, p.sq_int.col ]
end
$logger.info { "Clearing order #{ order } for #{ self.to_s }." }
@orders.delete p
evade_reset
end
end
def orders?
@orders.length > 0
end
def order_distance
return nil unless orders?
Distance.get pos, @orders[0].square
end
#
# Delete order aimed at specific square
#
def remove_target_from_order t
p = nil
if orders?
@orders.each do |o|
if o.target? t
p = o
break
end
end
unless p.nil?
ai.harvesters.remove self if ai.harvesters and p.order == :HARVEST
if p.order == :FORAGE
ai.food.remove_ant self, [ p.sq_int.row, p.sq_int.col ]
end
$logger.info { "Removing order #{ p.order } from #{ self.to_s }" }
@orders.delete p
evade_reset
end
end
# return true if target was present
!p.nil?
end
def find_order order
p = nil
if orders?
@orders.each do |o|
if o.order == order
p = o
break
end
end
end
p
end
def clear_first_order del_food = false
return unless orders?
p = @orders[0]
@orders = @orders[1..-1]
clear_next_move
evade_reset
if p.order == :FORAGE
# Note that we do not use the coord with offset here
if del_food
ai.food.remove [ p.sq_int.row, p.sq_int.col ]
else
ai.food.remove_ant self, [ p.sq_int.row, p.sq_int.col ]
end
end
end
def change_order sq, order
p = find_order order
if p
$logger.info { "Change order #{ order } to square #{ sq.to_s } for #{ self.to_s }" }
p.square = sq
p.offset = nil if order == :HARVEST
evade_reset
else
$logger.info { "#{ to_s } has no order #{ order}!" }
end
end
def clear_targets_reached
return false if !orders?
ai.turn.check_maxed_out
prev_order = @orders[0]
while orders?
n = @orders[0]
order_sq = n.square
order_order = n.order
$logger.info "Checking order #{ order_order } on #{ order_sq} for #{ self }"
move = next_move
if not @prev_move.nil? and
move == reverse( @prev_move ) and
@prev_order == n
$logger.info "Detected twitch; clearing this order."
@prev_order = nil
clear_first_order
next
end
if order_order == :ATTACK
d = Distance.get self.square, order_sq
if d.in_view?
unless !ai.map[ order_sq.row][ order_sq.col].ant.nil? and
ai.map[ order_sq.row][ order_sq.col].ant.enemy?
$logger.info { "Clearing attack target #{ order_sq.to_s}." }
clear_first_order
next
end
end
end
if false
if order_order == :GOTO
d = Distance.get self.square, order_sq
if ( self.square == order_sq ) or d.dist <= 2
$logger.info { "GOTO close enough to square #{ order_sq.to_s}." }
BorderPatrolFiber.clear_target order_sq
clear_first_order
next
end
end
end
if self.square == order_sq
# Done with this order, reached the target
if order_order == :RAZE
$logger.info { "Hit anthill at #{ self.square.to_s }" }
clear_first_order
@ai.clear_raze self.square
else
$logger.info { "#{ to_s } reached target for order #{ order_order }" }
if order_order == :GOTO
BorderPatrolFiber.clear_target order_sq
end
if order_order == :HARVEST
# Keep the order in the list, don't remove
return true
else
clear_first_order ( order_order == :FORAGE )
end
end
next
end
if false
if order_order == :GOTO
unless BorderPatrolFiber.known_region self.square
$logger.info { "#{ to_s } with order #{ order_order } outside known regions" }
clear_first_order
next
end
end
end
if order_order == :ASSEMBLE
if !collective
clear_first_order
next
end
end
# Check if food still present when in-range
if order_order == :FORAGE
sq = order_sq
result = @ai.food.active? [ sq.row, sq.col ]
if result.nil?
# food is already gone. Skip order
$logger.info { "Food #{ sq } is gone" }
clear_first_order
next
elsif result
$logger.info { "Food #{ sq } still there: yes" }
else
d = Distance.get self, sq
if d.in_view?
$logger.info { "Food #{ sq } still there: no" }
clear_first_order true
next
else
str = "can't tell, out of view"
$logger.info { "Food #{ sq } still there: #{ str}" }
end
end
end
# Check if hill still present when in-range
if order_order == :RAZE
sq = order_sq
str = ""
closest = closest_ant_view [ sq.row, sq.col], @ai
unless closest.nil?
d = Distance.get closest, sq
if d.in_view?
result = @ai.hills.active? [ sq.row, sq.col]
if result.nil?
$logger.info "Hill not there!"
elsif result == true
str << "yes"
else
# hill is dead
str << "no"
clear_first_order
@ai.clear_raze sq
next
end
else
str << "can't tell, out of view"
end
end
$logger.info { "Hill #{ sq } still there: #{ str}" }
end
break
end
first_order = @orders[0]
if not first_order.nil? and prev_order != first_order
#check_order_distance first_order
if evading?
# order changed; reset evasion
$logger.info "#{ self } evasion reset"
evade_reset
end
end
# return value
orders?
end
#
# return next direction if found
# false if no direction
# true if movement handled elsewhere
#
def determine_next_move
#$logger.debug {
# raise "#{ self } already moved" if moved?
#}
return true if collective_assembled?
if evading?
# Handle evasion elsewhere
$logger.info "#{ self } evading"
return true
end
return false if !orders?
# Following happens when razing hills.
if neighbor_enemies?(2)
$logger.info { " #{ self } right next to enemy, staying and ignoring orders." }
return :STAY
end
square.neighbors do |n|
if n.food?
$logger.info { "#{ self } right next to food #{ n }." }
return :STAY
end
end
order_sq = @orders[0].square
order_order = @orders[0].order
$logger.info "Determining direction order #{ order_order } on #{ order_sq} for #{ self }"
if order_order == :DEFEND_HILL
d = Distance.get self.square, order_sq
# Use of in_peril in order to get closer to hill than in_view
if d.in_peril?
# if on hill itself, move away
if self.square == order_sq
$logger.info { "Defender moving away from hill #{ order_sq.to_s}." }
tmp = self.default
tmp = false if tmp.nil?
tmp
else
$logger.info { "Defending hill #{ order_sq.to_s}." }
#return :STAY
return false
end
end
end
if order_order == :DEFEND
d = Distance.get self.square, order_sq
# Use of distance in order to get closer to location than in_view
if ( self.square == order_sq ) or
( d.in_attack_range? and
$pointcache.has_direct_path self.square, order_sq
)
$logger.info { "Defending square #{ order_sq.to_s}." }
#return :STAY
return false
end
end
if false
# check if harvest target is water
if order_order == :HARVEST and evading?
sq = order_sq
d = Distance.get self, sq
# Use of in_danger here is not because of attack, but because
# we want to get closer to the target square than in_view
if d.in_danger? and @ai.map[ sq.row ][sq.col].water?
$logger.info { "#{ self.to_s } harvest target #{ sq } is water. Can't get any closer" }
@orders[0].offset = [ -( sq.row - self.row ), - (sq.col - self.col ) ]
$logger.info { "Set order offset to #{ @orders[0].offset }." }
#return :STAY
return false
end
end
end
if @orders[0].order == :ASSEMBLE
# NB: method assemble removes :ASSEMBLE and other orders
# from collective members
if collective and collective.assemble
return :STAY
end
end
#
# Actual, regular movement
#
ai.turn.check_maxed_out
item = $pointcache.get self.square, order_sq
if not item.nil?
if item[3]
$logger.info "Invalid pointcache item detected" # #{ item }"
end
item[2] # is a direction
else
# This should never happen any more - but it does.....
$logger.info "WARNING: Nil item from pointcache"
false
end
end
#
#
#
def handle_orders always_move = true
if moved?
$logger.info "WARNING:#{ self } already moved."
return false
end
ai.turn.check_maxed_out
to = next_move
return false if to === true or to === false
target = nil
order = :NONE
if orders?
target = @orders[0].square
order = @orders[0].order
end
evade_reset if to == :STAY
ret = move to, target, always_move
$logger.info {
"#{ self } order #{ order } to #{ target }, dir/to #{ to }, result #{ ret }"
}
ret
end
#
# Return true if after check, we are still continuing with orders
#
def check_orders
enemies = enemies_in_view
catch :done do
enemies.each do |e|
while orders?
# If enemy is closer to order target (foraging only),
# cancel order
break unless @orders[0].order == :FORAGE
da = Distance.get( pos, @orders[0].square )
de = Distance.get( e.pos, @orders[0].square )
if da.dist > de.dist
# we lucked out - skip this order
$logger.info { "check_orders #{ to_s } skipping." }
clear_first_order
else
# We can still make it first! Even if we die...
$logger.info { "check_orders #{ to_s } we can make it!" }
throw :done
end
end
end
end
orders?
end
def find_orders what, sq = nil, first_only = false
list = {}
count = 0
@orders.each do |n|
if n.order == what
#$logger.info { "found #{ what }" }
if sq
# Search for specific target only
if sq == n.square
$logger.info { "Found order #{ what } on #{ n.square }" }
list[ sq ] = count
break
end
else
$logger.info { "Found order #{ what }" }
list[ n.square ] = count
break if first_only
end
end
count += 1
end
list
end
def has_order what, sq = nil
list = find_orders what, sq, true
list.length > 0
end
def first_order what
if @orders[0]
@orders[0].order == what
else
false
end
end
def get_first_order
@orders[0]
end
def can_raze?
not orders? or ( first_order :HARVEST and not has_order :DEFEND_HILL )
end
end