1.2 Client
1.2.1 Connection
1.2.2 Bot
1.2.3 Action
1.2.4 Tactics
1.2.5 Wandering
1.2.6 Gathering
1.2.7 TCP Client
1.2.8 Viewer
1.2.9 TCP Viewer
8.14
1.2.4 Tactics🔗

Source code at tactics.rkt

This is a collection of functions for use in implementing an action strategy.

The direction change chance is the chance of making a random direction change when a bot is wandering around the world. This is set to a 20% chance.

(define direction-change-chance (make-parameter 0.2))

There are blocks nearby if they are in the bot’s neighbor list.

(test-case:
 "blocks nearby"
 (check-true (blocks-nearby? (bot #f #f (list (entity 101 type-block #f)))))
 (check-false (blocks-nearby? (bot #f #f (list (entity 101 type-bot #f)))))
 (check-false (blocks-nearby? (bot #f #f '()))))
(define (blocks-nearby? bot)
  (ormap (λ (entity) (= (entity-type entity) type-block))
         (bot-neighbors bot)))

The choice structure is used to return information about what a strategy has chosen for the next action.

(struct choice (type parameter direction delay))

When a strategy chooses to drop a block, the choice parameter is the direction to a free location. The next move direction is different from the drop direction, and the delay before taking another block is 5 turns.

(test-case:
 "free location found"
 (let* ([bot1 (entity 101 type-bot (location 1 1))]
        [block (entity 102 type-block (location 1 2))]
        [bot (bot bot1 #f (list block))]
        [choice (choose-drop bot)])
   (check-equal? (choice-type choice) request-drop)
   (check-equal? (choice-parameter choice) direction-east)
   (check-not-equal? (choice-direction choice) direction-east)
   (check-equal? (choice-delay choice) 5)))

The direction chosen places the block in a location with the most adjacent blocks.

(test-case:
 "best location found"
 (let* ([bot1 (entity 101 type-bot (location 1 1))]
        [block1 (entity 102 type-block (location 0 0))]
        [block2 (entity 103 type-block (location 2 0))]
        [choice (choose-drop (bot bot1 #f (list block1 block2)))])
   (check-equal? (choice-parameter choice) direction-south)))

The direction chosen doesn’t move outside the world

(test-case:
 "free location not outside world"
 (let* ([bot1 (entity 101 type-bot (location 49 49))]
        [edge1 (make-edge (location 50 49))]
        [edge2 (make-edge (location 49 50))]
        [block (entity 102 type-block (location 49 48))]
        [choice (choose-drop (bot bot1 #f (list edge1 edge2 block)))])
   (check-equal? (choice-parameter choice) direction-west)))

The chosen direction is the one to the free location with most adjacent blocks

(define (choose-drop bot)
  (define (score location)
    (if (is-free? bot location) (count-adjacent bot location) -1))
  (let* ([drop-location (argmax score (all-directions (bot-location bot)))]
         [drop-direction (direction-from (bot-location bot) drop-location)])
    (choice request-drop drop-direction
            (change-direction bot drop-direction) 5)))
(define (count-adjacent bot location)
  (define (adjacent-block? entity)
    (and (= (entity-type entity) type-block)
         (adjacent? (entity-location entity) location)))
  (count adjacent-block? (bot-neighbors bot)))

When a strategy chooses to move a bot, the delay before taking another block is reduced by one.

(test-case:
 "delay reduced"
 (let ([choice (choose-move direction-west 5)])
   (check-equal? (choice-type choice) request-move)
   (check-equal? (choice-parameter choice) direction-west)
   (check-equal? (choice-direction choice) direction-west)
   (check-equal? (choice-delay choice) 4)))
(define (choose-move direction delay)
  (choice request-move direction direction (max 0 (- delay 1))))

When a strategy chooses to take a block, the choice parameter is block id. The next move direction is the direction to the block, and the delay before taking another block is 5 turns.

(test-case:
 "choose take"
 (let ([choice (choose-take
                (bot (entity 101 type-bot (location 1 1)) #f #f)
                (entity 102 type-block (location 1 2)))])
   (check-equal? (choice-type choice) request-take)
   (check-equal? (choice-parameter choice) 102)
   (check-equal? (choice-direction choice) direction-north)
   (check-equal? (choice-delay choice) 5)))
(define (choose-take bot block)
  (let ([take-direction (direction-from (bot-location bot) (entity-location block))])
    (choice request-take (entity-id block) take-direction 5)))

When a strategy chooses to transfer a block to a base, the choice parameter is the base id. The next move direction is away from the base. The delay is not used.

(test-case:
 "choose transfer"
 (let ([choice (choose-transfer
                (bot (entity 101 type-bot (location 1 1)) #f
                     (list (entity 102 type-base (location 1 2)))))])
   (check-equal? (choice-type choice) request-transfer)
   (check-equal? (choice-parameter choice) 102)
   (check-equal? (choice-direction choice) direction-south)))
(define (choose-transfer bot)
  (let ([base (findf (λ (item) (equal? (entity-type item) type-base)) (bot-neighbors bot))])
    (choice request-transfer (entity-id base)
            (direction-from (entity-location base) (bot-location bot)) 0)))

Removeable blocks are ones that are adjacent to a bot and are not adjacent to two other blocks.

(test-case:
 "removable block found"
 (let* ([bot1 (entity 101 type-bot (location 1 1))]
        [block1 (entity 102 type-block (location 1 2))]
        [block2 (entity 102 type-block (location 2 2))]
        [bot2 (entity 103 type-bot (location 2 1))]
        [bot (bot bot1 #f (list bot2 block1 block2))]
        [removable (removable-blocks bot)])
   (check-equal? (length removable) 1)
   (check-equal? (first removable) block1)))
(test-case:
 "block not removable"
 (let* ([bot1 (entity 101 type-bot (location 1 1))]
        [block1 (entity 102 type-block (location 1 2))]
        [block2 (entity 102 type-block (location 2 2))]
        [block3 (entity 103 type-block (location 0 2))]
        [bot (bot bot1 #f (list block1 block2 block3))]
        [removable (removable-blocks bot)])
   (check-equal? (length removable) 0)))

We filter the bot’s neighbors to find removable blocks

(define (removable-blocks bot)
  (filter (λ (entity)
            (and (= (entity-type entity) type-block)
                 (adjacent? (entity-location entity) (bot-location bot))
                 (< (count-adjacent bot (entity-location entity)) 2)))
          (bot-neighbors bot)))