Advertisement
Draco18s

Vampire Mk 7.1b

Apr 5th, 2018
245
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // == Shared low-level helpers for all solutions ==
  2. var QUEEN = 5;
  3.  
  4. var WHITE = 1;
  5. var COL_MIN = WHITE;
  6. var COL_LIM = 9;
  7.  
  8. var CENTRE = 4;
  9.  
  10. var NOP = {cell: CENTRE};
  11.  
  12. var DIR_FORWARDS = false;
  13. var DIR_REVERSE = true;
  14. var SIDE_RIGHT = true;
  15. var SIDE_LEFT = false;
  16. //if(view[4].ant.type >= 5) console.log(JSON.stringify(view))
  17. function sanity_check(movement) {
  18.     var me = view[CENTRE].ant;
  19.     if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
  20.         return false;
  21.     }
  22.     if(movement.type) {
  23.         if(movement.color) {
  24.             return false;
  25.         }
  26.         if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
  27.             return false;
  28.         }
  29.         if(view[movement.cell].ant || view[movement.cell].food) {
  30.             return false;
  31.         }
  32.         if(me.type !== QUEEN || me.food < 1) {
  33.             return false;
  34.         }
  35.         return true;
  36.     }
  37.     if(movement.color) {
  38.         if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
  39.             return false;
  40.         }
  41.         if(view[movement.cell].color === movement.color) {
  42.             return false;
  43.         }
  44.         return true;
  45.     }
  46.     if(view[movement.cell].ant && movement.cell != 4) {
  47.         return false;
  48.     }
  49.     if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
  50.         return false;
  51.     }
  52.     return true;
  53. }
  54.  
  55. function as_array(o) {
  56.     if(Array.isArray(o)) {
  57.         return o;
  58.     }
  59.     return [o];
  60. }
  61.  
  62. function best_of(movements) {
  63.     var m;
  64.     for(var i = 0; i < movements.length; ++ i) {
  65.         if(typeof(movements[i]) === 'function') {
  66.             m = movements[i]();
  67.         } else {
  68.             m = movements[i];
  69.         }
  70.         if(sanity_check(m)) {
  71.             return m;
  72.         }
  73.     }
  74.     return null;
  75. }
  76.  
  77. function play_safe(movement) {
  78.     // Avoid disqualification: no-op if moves are invalid
  79.     return best_of(as_array(movement)) || NOP;
  80. }
  81.  
  82. var RAND_SEED = (() => {
  83.     var s = 0;
  84.     for(var i = 0; i < 9; ++ i) {
  85.         s += view[i].color * (i + 1);
  86.         s += view[i].ant ? i * i : 0;
  87.         s += view[i].food ? i * i * i : 0;
  88.     }
  89.     return s % 29;
  90. })();
  91.  
  92. var ROTATIONS = [
  93.     [0, 1, 2, 3, 4, 5, 6, 7, 8],
  94.     [6, 3, 0, 7, 4, 1, 8, 5, 2],
  95.     [8, 7, 6, 5, 4, 3, 2, 1, 0],
  96.     [2, 5, 8, 1, 4, 7, 0, 3, 6],
  97. ];
  98.  
  99. function areAdjacent(A, B) {
  100.     if(A == 4 || B == 4 || A == B) return true;
  101.     if(A % 2 == 0 && B % 2 == 0) return false;
  102.     if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A);
  103.     if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A);
  104.     if(A == 0 && (B == 1 || B == 3)) return true;
  105.     if(A == 2 && (B == 1 || B == 5)) return true;
  106.     if(A == 6 && (B == 3 || B == 7)) return true;
  107.     if(A == 8 && (B == 5 || B == 7)) return true;
  108.     return false;
  109. }
  110.  
  111. function try_all(fns, limit, wrapperFn, checkFn) {
  112.     var m;
  113.     fns = as_array(fns);
  114.     for(var i = 0; i < fns.length; ++ i) {
  115.         if(typeof(fns[i]) !== 'function') {
  116.             if(checkFn(m = fns[i])) {
  117.                 return m;
  118.             }
  119.             continue;
  120.         }
  121.         for(var j = 0; j < limit; ++ j) {
  122.             if(checkFn(m = wrapperFn(fns[i], j))) {
  123.                 return m;
  124.             }
  125.         }
  126.     }
  127.     return null;
  128. }
  129.  
  130. function identify_rotation(testFns) {
  131.     // testFns MUST be functions, not constants
  132.     return try_all(
  133.         testFns,
  134.         4,
  135.         (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
  136.         (r) => r
  137.     );
  138. }
  139.  
  140. function near(a, b) {
  141.     return (
  142.         Math.abs(a % 3 - b % 3) < 2 &&
  143.         Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  144.     );
  145. }
  146.  
  147. function try_all_angles(solverFns) {
  148.     return try_all(
  149.         solverFns,
  150.         4,
  151.         (fn, r) => fn(ROTATIONS[r]),
  152.         sanity_check
  153.     );
  154. }
  155.  
  156. function try_all_cells(solverFns, skipCentre) {
  157.     return try_all(
  158.         solverFns,
  159.         9,
  160.         (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
  161.         sanity_check
  162.     );
  163. }
  164.  
  165. function try_all_cells_near(p, solverFns) {
  166.     return try_all(
  167.         solverFns,
  168.         9,
  169.         (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
  170.         sanity_check
  171.     );
  172. }
  173.  
  174. function ant_type_at(i, friend) {
  175.     return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
  176. }
  177.  
  178. function friend_at(i) {
  179.     return ant_type_at(i, true);
  180. }
  181.  
  182. function foe_at(i) {
  183.     return ant_type_at(i, false);
  184. }
  185.  
  186. function foe_near() {
  187.     for(var i = 0; i < 9; ++ i) {
  188.         if(i !== 4 && view[i].ant && !view[i].ant.friend) {
  189.             return true;
  190.         }
  191.     }
  192.     return false;
  193. }
  194.  
  195. function ant_type_near(p, friend) {
  196.     for(var i = 0; i < 9; ++ i) {
  197.         if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
  198.             return true;
  199.         }
  200.     }
  201.     return false;
  202. }
  203.  
  204. function move_agent(agents) {
  205.     var me = view[CENTRE].ant;
  206.     var buddies = [0, 0, 0, 0, 0, 0];
  207.     for(var i = 0; i < 9; ++ i) {
  208.         ++ buddies[friend_at(i)];
  209.     }
  210.  
  211.     for(var i = 0; i < agents.length; i += 2) {
  212.         if(agents[i] === me.type) {
  213.             return agents[i+1](me, buddies);
  214.         }
  215.     }
  216.     return null;
  217. }
  218.  
  219. function grab_nearby_food() {
  220.     return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
  221. }
  222.  
  223. function go_anywhere() {
  224.     return try_all_cells((i) => ({cell: i}), true);
  225. }
  226.  
  227. function colours_excluding(cols) {
  228.     var r = [];
  229.     for(var i = COL_MIN; i < COL_LIM; ++ i) {
  230.         if(cols.indexOf(i) === -1) {
  231.             r.push(i);
  232.         }
  233.     }
  234.     return r;
  235. }
  236.  
  237. function generate_band(start, width) {
  238.     var r = [];
  239.     for(var i = 0; i < width; ++ i) {
  240.         r.push(start + i);
  241.     }
  242.     return r;
  243. }
  244.  
  245. function colour_band(colours) {
  246.     return {
  247.         contains: function(c) {
  248.             return colours.indexOf(c) !== -1;
  249.         },
  250.         next: function(c) {
  251.             return colours[(colours.indexOf(c) + 1) % colours.length];
  252.         },
  253.         prev: function(c) {
  254.             return colours[(colours.indexOf(c) + colours.length - 1) % colours.length];
  255.         }
  256.     };
  257. }
  258.  
  259. function random_colour_band(colours) {
  260.     return {
  261.         contains: function(c) {
  262.             return colours.indexOf(c) !== -1;
  263.         },
  264.         next: function() {
  265.             return colours[RAND_SEED % colours.length];
  266.         }
  267.     };
  268. }
  269.  
  270. function fast_diagonal(colourBand) {
  271.     var m = try_all_angles([
  272.         // Avoid nearby checked areas
  273.         (rot) => {
  274.             if(
  275.                 !colourBand.contains(view[rot[0]].color) &&
  276.                 colourBand.contains(view[rot[5]].color) &&
  277.                 colourBand.contains(view[rot[7]].color)
  278.             ) {
  279.                 return {cell: rot[0]};
  280.             }
  281.         },
  282.  
  283.         // Go in a straight diagonal line if possible
  284.         (rot) => {
  285.             if(
  286.                 !colourBand.contains(view[rot[0]].color) &&
  287.                 colourBand.contains(view[rot[8]].color)
  288.             ) {
  289.                 return {cell: rot[0]};
  290.             }
  291.         },
  292.  
  293.         // When in doubt, pick randomly but avoid doubling-back
  294.         (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),
  295.  
  296.         // Double-back when absolutely necessary
  297.         (rot) => ({cell: rot[0]})
  298.     ]);
  299.  
  300.     // Lay a colour track so that we can avoid doubling-back
  301.     // (and mess up our foes as much as possible)
  302.     if(!colourBand.contains(view[CENTRE].color)) {
  303.         var prevCol = m ? view[8-m.cell].color : WHITE;
  304.  
  305.         var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  306.         for(var i = 0; i < 9; ++ i) {
  307.             ++ colours[view[i].color];
  308.         }
  309.  
  310.         return {cell: CENTRE, color: colourBand.next(prevCol)};
  311.     }
  312.  
  313.     return m;
  314. }
  315.  
  316. function checkAllNearEnvirons(colours, buddies) {
  317.         var nearMoves = [victims.length];
  318.         for(var e = 0; e < victims.length; e++) {
  319.                 var env = victims[e];
  320.                 nearMoves[e] = null;
  321.                 if(env.near_nest(colours)) {
  322.                         nearMoves[e] = env.near_nest_move(colours, buddies);
  323.                 }
  324.         }
  325.         return best_of(nearMoves);
  326. }
  327.  
  328. function follow_edge(obstacleFn, side) {
  329.     // Since we don't know which direction we came from, this can cause us to get
  330.     // stuck on islands, but the random orientation helps to ensure we don't get
  331.     // stuck forever.
  332.  
  333.     var order = ((side === SIDE_LEFT)
  334.         ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
  335.         : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  336.     );
  337.     return try_all(
  338.         [obstacleFn],
  339.         order.length - 1,
  340.         (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
  341.         sanity_check
  342.     );
  343. }
  344.  
  345. function start_dotted_path(colourBand, side, protectedCols) {
  346.     var right = (side === SIDE_RIGHT);
  347.     return try_all_angles([
  348.         (rot) => ((
  349.             !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
  350.             !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
  351.             !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
  352.             !colourBand.contains(view[rot[1]].color)
  353.         )
  354.             ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
  355.             : null)
  356.     ]);
  357. }
  358.  
  359. function lay_dotted_path(colourBand, side, protectedCols) {
  360.     var right = (side === SIDE_RIGHT);
  361.     return try_all_angles([
  362.         (rot) => {
  363.             var ahead = rot[right ? 2 : 0];
  364.             var behind = rot[right ? 8 : 6];
  365.             if(
  366.                 colourBand.contains(view[behind].color) &&
  367.                 !protectedCols.contains(view[ahead].color) &&
  368.                 !colourBand.contains(view[ahead].color) &&
  369.                 !colourBand.contains(view[rot[right ? 6 : 8]].color)
  370.             ) {
  371.                 return {cell: ahead, color: colourBand.next(view[behind].color)};
  372.             }
  373.         }
  374.     ]);
  375. }
  376.  
  377. function follow_dotted_path(colourBand, side, direction) {
  378.     var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  379.     var right = (side === SIDE_RIGHT);
  380.  
  381.     return try_all_angles([
  382.         // Cell on our side? advance
  383.         (rot) => {
  384.             if(
  385.                 colourBand.contains(view[rot[right ? 5 : 3]].color) &&
  386.                 // Prevent sticking / trickery
  387.                 !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
  388.                 !colourBand.contains(view[rot[0]].color) &&
  389.                 !colourBand.contains(view[rot[2]].color)
  390.             ) {
  391.                 return {cell: rot[forwards]};
  392.             }
  393.         },
  394.  
  395.         // Cell ahead and behind? advance
  396.         (rot) => {
  397.             var passedCol = view[rot[right ? 8 : 6]].color;
  398.             var nextCol = view[rot[right ? 2 : 0]].color;
  399.             if(
  400.                 colourBand.contains(passedCol) &&
  401.                 nextCol === colourBand.next(passedCol) &&
  402.  
  403.                 // Prevent sticking / trickery
  404.                 !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
  405.                 !colourBand.contains(view[rot[right ? 0 : 2]].color)
  406.             ) {
  407.                 return {cell: rot[forwards]};
  408.             }
  409.         }
  410.     ]);
  411. }
  412.  
  413. function escape_dotted_path(colourBand, side, newColourBand) {
  414.     var right = (side === SIDE_RIGHT);
  415.     if(!newColourBand) {
  416.         newColourBand = colourBand;
  417.     }
  418.  
  419.     return try_all_angles([
  420.         // Escape from beside the line
  421.         (rot) => {
  422.             var approachingCol = view[rot[right ? 2 : 0]].color;
  423.             if(
  424.                 !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
  425.                 !colourBand.contains(approachingCol) ||
  426.                 colourBand.contains(view[rot[7]].color) ||
  427.                 colourBand.contains(view[rot[right ? 6 : 8]].color)
  428.             ) {
  429.                 // not oriented, or in a corner
  430.                 return null;
  431.             }
  432.             return best_of([
  433.                 {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
  434.                 {cell: rot[right ? 3 : 5]},
  435.                 {cell: rot[right ? 0 : 2]},
  436.                 {cell: rot[right ? 6 : 8]},
  437.                 {cell: rot[right ? 2 : 0]},
  438.                 {cell: rot[right ? 8 : 6]},
  439.                 {cell: rot[right ? 5 : 3]}
  440.             ]);
  441.         },
  442.  
  443.         // Escape from inside the line
  444.         (rot) => {
  445.             if(
  446.                 !colourBand.contains(view[rot[7]].color) ||
  447.                 !colourBand.contains(view[rot[1]].color) ||
  448.                 colourBand.contains(view[CENTRE].color)
  449.             ) {
  450.                 return null;
  451.             }
  452.             return best_of([
  453.                 {cell: rot[3]},
  454.                 {cell: rot[5]},
  455.                 {cell: rot[0]},
  456.                 {cell: rot[2]},
  457.                 {cell: rot[6]},
  458.                 {cell: rot[8]}
  459.             ]);
  460.         }
  461.     ]);
  462. }
  463.  
  464. function latch_to_dotted_path(colourBand, side) {
  465.     var right = (side === SIDE_RIGHT);
  466.  
  467.     return try_all_angles([
  468.         (rot) => {
  469.             var approachingCol = view[rot[right ? 2 : 0]].color;
  470.             if(
  471.                 colourBand.contains(approachingCol) &&
  472.                 view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
  473.                 !colourBand.contains(view[rot[right ? 5 : 3]].color)
  474.             ) {
  475.                 // We're on the wrong side; go inside the line
  476.                 return {cell: rot[right ? 5 : 3]};
  477.             }
  478.         },
  479.  
  480.         // Inside the line? pick a side
  481.         (rot) => {
  482.             var passedCol = view[rot[7]].color;
  483.             var approachingCol = view[rot[1]].color;
  484.             if(
  485.                 !colourBand.contains(passedCol) ||
  486.                 !colourBand.contains(approachingCol) ||
  487.                 colourBand.contains(view[CENTRE].color)
  488.             ) {
  489.                 return null;
  490.             }
  491.             if((approachingCol === colourBand.next(passedCol)) === right) {
  492.                 return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
  493.             } else {
  494.                 return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
  495.             }
  496.         }
  497.     ]);
  498. }
  499.  
  500.  
  501. // == High-level logic begins here ==
  502.  
  503.  
  504. var TARGET_COLOURS_ZIG = colour_band([4, 5, 7, 8]);
  505. var TARGET_COLOURS_FIREFLY = colour_band([2, 5, 8]);
  506. var GROUND_COLOURS_BH = colour_band([2, 7, 8]);
  507. var SAFE_COLOURS = random_colour_band([8]);
  508.  
  509. var THIEF = 1;
  510. var BOUNCER = 2;
  511. var LANCE = 3;
  512. var LANCE_TIP = 4;
  513.  
  514. var INITIAL_GATHER = 12;
  515.  
  516. function colour_band_prev(band, base) {
  517.     if(!band.contains(base)) {
  518.         return band.next(WHITE);
  519.     }
  520.     var cur = band.next(base);
  521.     var c;
  522.     while((c = band.next(cur)) !== base) {
  523.         cur = c;
  524.     }
  525.     return cur;
  526. }
  527.  
  528. function white_near(p) {
  529.     for(var i = 0; i < 9; ++ i) {
  530.         if(near(i, p) && view[i].color === WHITE) {
  531.             return true;
  532.         }
  533.     }
  534.     return false;
  535. }
  536.  
  537. function white_near(p, min) {
  538.     var c = 0;
  539.     for(var i = 0; i < 9; ++ i) {
  540.         if(near(i, p) && view[i].color === WHITE) {
  541.             if(++c >= min) return true;
  542.         }
  543.     }
  544.     return false;
  545. }
  546.  
  547. var TARGET_ARRANGEMENT_RAIL = [
  548.     [8,4,5,8,5,2,4,2,6],
  549.     [8,5,2,4,2,6,6,4,5],
  550.     [4,2,6,6,4,5,8,4,5],
  551.     [6,4,5,8,4,5,8,5,2]
  552. ];
  553. var TARGET_NEAR_RAIL = [
  554.     [5,8,0,4,8,0,6,4,0],
  555.     [2,4,0,5,8,0,4,8,0],
  556.     [4,6,0,2,4,0,5,8,0],
  557.     [4,8,0,4,6,0,2,4,0],
  558.     [4,5,0,5,2,0,2,6,0],
  559.     [4,5,0,4,5,0,5,2,0],
  560.     [2,6,0,4,5,0,4,5,0],
  561.     [5,2,0,2,6,0,4,5,0]
  562. ];
  563. var TARGET_COLOURS_RAIL = colour_band([4,2,5,4]);
  564. var rail_miners = {
  565.     name:function() { return "rail_miners"; },
  566.     near_nest: function(colours) {
  567.         var bestScore = 0;
  568.         var enemyQueen = false;
  569.         // check every rotation for each 3x3 rail possibility
  570.         TARGET_NEAR_RAIL.forEach(function (arrangement) {
  571.             ROTATIONS.forEach(function (rot){
  572.                 var score = 0;
  573.                 for(var i = 0; i < 9; i++) {
  574.                     score += arrangement[i] == view[rot[i]].color?1:0;
  575.                     enemyQueen |= view[i].ant && view[i].ant.type == QUEEN && !view[i].ant.friend;
  576.                 }
  577.                 if(score > bestScore) {
  578.                     bestScore = score;
  579.                 }
  580.             });
  581.         });
  582.         if(bestScore >= (5 - (enemyQueen && view[4].ant.type == 1?1:0))) {
  583.             if(highway.likely_nest(colours)) return false;
  584.             return true;
  585.         }
  586.     },
  587.     worth_leeching: function(myFood, buddies) {
  588.         if(buddies[LANCE_TIP]) return true;
  589.         if(buddies[THIEF]) return myFood > 5-buddies[THIEF] && ((myFood < 800) || (myFood % 3 == 0));
  590.         return myFood > 10 && ((myFood < 500) || (myFood % 3 == 0));
  591.     },
  592.     near_nest_move: function(colours, buddies) {
  593.         if(view[4].ant == QUEEN && !this.worth_leeching(view[4].ant.food, buddies)) return null;
  594.         //if there's an ant we're dodging
  595.         var victim_pos = -1;
  596.         var enemyCount = 0;
  597.         for(var a=0; a<9; a++) {
  598.             if(view[a].ant != null && !view[a].ant.friend) {
  599.                 if(view[a].ant.type == 5) {
  600.                     victim_pos = a;
  601.                 }
  602.                 else {
  603.                     enemyCount++;
  604.                 }
  605.             }
  606.         }
  607.         if(victim_pos >= 0 && enemyCount >= buddies[THIEF]) {
  608.             //if next to the queen and we're outnumbered, move back to the center of the rail.
  609.             var target = TARGET_COLOURS_RAIL.prev(view[victim_pos].color);
  610.             var best = best_of([
  611.                 try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,victim_pos)) ? {cell: i} : null)),
  612.             ]);
  613.             if(best != null) return best;
  614.         }
  615.         for(var a=0; a<9; a++) {
  616.             if(a != 4 && view[a].ant == null) {
  617.                 var target = TARGET_COLOURS_RAIL.prev(view[a].color);
  618.                 var ret = best_of([
  619.                     try_all_cells((i) => ((i != a && view[i].color == target && i % 2 == 0 && areAdjacent(i,a)) ? {cell: i} : null)),
  620.                     NOP
  621.                 ]);
  622.                 if(ret.cell != 4) {
  623.                     return ret;
  624.                 }
  625.             }
  626.         }
  627.         if(!buddies[THIEF]) {
  628.             //if there is not and we are alone
  629.             for(var a=0; a<9; a++) {
  630.                 if(a != 4) {
  631.                     var target = TARGET_COLOURS_RAIL.prev(view[a].color);
  632.                     var best = best_of([
  633.                         try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null)),
  634.                     ]);
  635.                     if(best != null) return best;
  636.                 }
  637.             }
  638.         }
  639.         return null;
  640.     },
  641.     likely_nest: function(colours) {
  642.         var bestScore = 0;
  643.         // check every rotation for each 3x3 rail possibility
  644.         var q = 0;
  645.         var near4 = -1;
  646.         TARGET_ARRANGEMENT_RAIL.forEach(function (arrangement) {
  647.             var j = 0;
  648.             ROTATIONS.forEach(function (rot){
  649.                 var score = 0;
  650.                 for(var i = 0; i < 9; i++) {
  651.                     score += arrangement[i] == view[rot[i]].color?1:0;
  652.                     //score -= view[rot[i]].color == 7?2:0;//fake rail detection
  653.                     if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == LANCE_TIP) near4 = i;
  654.                 }
  655.                 if(score > bestScore) {
  656.                     bestScore = score;
  657.                 }
  658.                 j++;
  659.             });
  660.             q++;
  661.         });
  662.         if(rail_miners.near_nest(colours) && near4 >= 0) return true;
  663.         if(view[4].ant.type == THIEF && view[4].color == 4 && bestScore >= 3 && view[1].color != view[7].color && view[3].color != view[5].color) return true;
  664.         if(bestScore >= 8) {
  665.             if(highway.likely_nest(colours)) return false;
  666.             return true;
  667.         }
  668.  
  669.         return false;
  670.     },
  671.  
  672.     likely_victim: function(victim_pos) {
  673.         return true;
  674.     },
  675.  
  676.     follow_victim: function(me, buddies, colours, victim_pos) {
  677.         if(me.type === THIEF && !buddies[QUEEN]) {
  678.             // Queen may be on other side of victim; try orbiting to find her
  679.             return try_all_angles([
  680.                 (rot) => ((victim_pos === rot[1]) ? {cell: rot[0]} : null),
  681.                 (rot) => ((victim_pos === rot[0]) ? {cell: rot[3]} : null)
  682.             ]);
  683.         }
  684.         var current = view[CENTRE].color;
  685.         var target = TARGET_COLOURS_RAIL.next(current);
  686.         var antitarget = TARGET_COLOURS_RAIL.prev(current);
  687.         var forwardCell = -1
  688.         for(var i = 0; i < 9; i++) {
  689.             if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
  690.                 forwardCell = i;
  691.             }
  692.         }
  693.         if(forwardCell < 0 && current == 4) {
  694.             current = 4
  695.             target = 4
  696.             antitarget = 5
  697.             for(var i = 0; i < 9; i++) {
  698.                 if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
  699.                     forwardCell = i;
  700.                 }
  701.             }
  702.         }
  703.  
  704.         if(me.type === QUEEN && !buddies[THIEF] && me.food > 0 && !buddies[LANCE_TIP]) {
  705.             // Make a buddy to help us steal
  706.             return best_of([
  707.                 (victim_pos % 2 == 1 && view[deRotate(victim_pos,2)].ant == null && deRotate(victim_pos,2) != forwardCell && !(rail_miners.near_nest(colours) && buddies[LANCE_TIP])) ? {cell: deRotate(victim_pos,2), type: THIEF} : null,
  708.                 try_all_cells((i) => (near(i, victim_pos) && i != forwardCell ? {cell: i, type: THIEF} : null), true)
  709.             ]);
  710.         }
  711.  
  712.         return best_of([
  713.             (view[victim_pos].ant.food > 30 && view[deRotate(victim_pos,1)].ant != null && !view[deRotate(victim_pos,1)].ant.friend && view[deRotate(victim_pos,1)].ant.type == 4) &&
  714.                 (victim_pos % 2 == 1 && view[deRotate(victim_pos,-1)].ant == null) ? {cell: deRotate(victim_pos,-1), type: LANCE_TIP} : null,
  715.             (view[victim_pos].ant.food > 30 && buddies[LANCE_TIP] == 3) &&
  716.                 try_all_cells((i) => (near(i, victim_pos) && view[8-i].ant && view[8-i].ant.friend && view[8-i].ant.type == LANCE_TIP ? {cell: i, type: LANCE_TIP} : null), true),
  717.             (view[victim_pos].ant.food > 30) &&
  718.                 try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
  719.  
  720.            
  721.  
  722.             // Continue to steal and get fat
  723.             NOP
  724.         ]);
  725.     },
  726.     find_victim: function(me, buddies, colours) {
  727.         var current = view[CENTRE].color;
  728.         if(me.type === QUEEN && current == WHITE && buddies[LANCE_TIP]) {
  729.             //TODO: better bounce-out
  730.             //if(buddies[LANCE_TIP] == 2 && me.food > 150) {
  731.             //    return best_of([
  732.             //        try_all_cells((i) => (view[i].ant == null ? {cell: i, type: BOUNCER} : null), true),
  733.             //        NOP
  734.             //    ]);
  735.             //}
  736.             return NOP;
  737.         }
  738.         // Easier to navigate alone, and thief reaching queen first would be a
  739.         // disaster, so the thief just gives up.
  740.         if(me.type === THIEF) {
  741.             for(var i = 0; i < 9; ++ i) {
  742.                 if(foe_at(i) === QUEEN) {
  743.                     return NOP;
  744.                 }
  745.                 if(friend_at(i) === LANCE_TIP) {
  746.                     return NOP;
  747.                 }
  748.                 if(foe_at(i) === 4 && view[deRotate(i,1)].ant != null && view[deRotate(i,1)].ant.type == QUEEN) {
  749.                     //TODO: better bounce-out
  750.                     //if(view[8-deRotate(i,1)].ant != null && view[8-deRotate(i,1)].ant.type != QUEEN && i == 0) {
  751.                     //    return {cell:deRotate(i,1),color:WHITE}
  752.                     //}
  753.                     return NOP;
  754.                 }
  755.             }
  756.             return go_anywhere;
  757.         }
  758.         var target = TARGET_COLOURS_RAIL.next(current);
  759.         var antitarget = TARGET_COLOURS_RAIL.prev(current);
  760.         var ret = best_of([
  761.             try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget) ? {cell: i} : null)),
  762.             //try_all_cells((i) => ((view[i].color == current) ? {cell: i} : null)),
  763.  
  764.             // We actually don't want to move if we're on the rails and there's no open next spot
  765.             NOP
  766.         ]);
  767.         if((ret == null || ret.cell == 4) && current == 4) {
  768.             current = 4
  769.             target = 4
  770.             antitarget = 5
  771.             ret = best_of([
  772.                 try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget) ? {cell: i} : null)),
  773.                 //try_all_cells((i) => ((view[i].color == current) ? {cell: i} : null)),
  774.  
  775.                 // We actually don't want to move if we're on the rails and there's no open next spot
  776.                 NOP
  777.             ]);
  778.         }
  779.         // if there wasn't an obvious "forward" spot...
  780.         if(ret == null || ret.cell == 4) {
  781.             if(buddies[LANCE_TIP]) {
  782.                 return best_of([
  783.                     try_all_cells((i) => (view[deRotate(i,2)].ant != null && view[deRotate(i,2)].ant.friend && view[deRotate(i,2)].ant.type == LANCE_TIP && view[deRotate(i,1)].ant != null && !view[deRotate(i,1)].ant.friend && view[deRotate(i,1)].ant.type == 4) ? {cell: i, type:THIEF} : null),
  784.                     try_all_cells((i) => (view[deRotate(i,-2)].ant != null && view[deRotate(i,-2)].ant.friend && view[deRotate(i,-2)].ant.type == LANCE_TIP && view[deRotate(i,-1)].ant != null && !view[deRotate(i,-1)].ant.friend && view[deRotate(i,-1)].ant.type == 4) ? {cell: i, type:THIEF} : null),
  785.  
  786.                     // We actually don't want to move if we're on the rails and there's no open next spot
  787.                     NOP
  788.                 ]);
  789.             }
  790.             var numAnts = 0;
  791.             var enemyBehind = false;
  792.             for(var na = 0; na < 9; na++) {
  793.                 if(view[na].ant != null && !view[na].ant.friend) numAnts++;
  794.                 if(view[na].ant != null && view[na].color == antitarget) enemyBehind = true;
  795.             }
  796.             if(numAnts == 0 || (enemyBehind && numAnts == 1)) {
  797.                 // if we're the only ant we can see (that is: unable to follow the rail)...
  798.                 if(rail_repair.likely_nest(colours)) {
  799.                     //try repairing the rail
  800.                     ret = rail_repair.find_victim(me, buddies, colours);
  801.                 }
  802.                 else {
  803.                     // otherwise go anywhere
  804.                     ret = go_anywhere;
  805.                 }
  806.             }
  807.             else {
  808.                 var likelyType4 = -1;
  809.                 for(var t = 0; t < 9; t++) {
  810.                     if(view[t].ant && !view[t].ant.friend && view[t].ant.type == 4) {
  811.                         likelyType4 = t;
  812.                     }
  813.                 }
  814.                 if(likelyType4 >= 0 && view[likelyType4].ant.food == 0 && me.food > 0) {
  815.                     ret = best_of([
  816.                         try_all_cells((i) => (buddies[THIEF] < 2 && i % 2 == 0 && view[i].ant == null && near(i,likelyType4)) ? {cell: i,type:THIEF} : null),
  817.                         NOP
  818.                         ]);
  819.                 }
  820.                 else {
  821.                     ret = go_anywhere;
  822.                 }
  823.             }
  824.         }
  825.         else {
  826.             ret = best_of([
  827.                 try_all_cells((i) => (i % 2 == 1 && view[i].food > 0) ? {cell: i} : null),
  828.                 ret
  829.             ]);
  830.         }
  831.         if(!ret.color && !ret.type && (view[deRotate(ret.cell,1)].color == WHITE || buddies[LANCE_TIP] > 0)) {
  832.             if(view[deRotate(ret.cell,2)].ant == null) {
  833.                 ret = {cell:deRotate(ret.cell,2),type: LANCE_TIP};
  834.             }
  835.             else if(view[deRotate(ret.cell,-2)].ant == null) {
  836.                 ret = {cell:deRotate(ret.cell,-2),type: LANCE_TIP};
  837.             }
  838.             else if(buddies[LANCE_TIP] < 4){
  839.                 var moveOrSpawn = false;
  840.                 for(var i=0;i<9;i++) {
  841.                     if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
  842.                         moveOrSpawn = i % 2;
  843.                     }
  844.                 }
  845.                 if(moveOrSpawn) {
  846.                     ret = {cell:8-ret.cell};
  847.                 }
  848.                 else {
  849.                     ret = {cell:ret.cell,type: THIEF};
  850.                 }
  851.             }
  852.             else if(buddies[LANCE_TIP] >= 4){
  853.                 return NOP;
  854.             }
  855.         }
  856.         else if(!ret.color && !ret.type && buddies[LANCE_TIP] > 0) {
  857.             return NOP;
  858.         }
  859.         return ret;
  860.     }
  861. };
  862.  
  863. var TARGET_RAIL_REPAIRATION = [
  864.     [0,0,0,8,0,0,4,2,6],
  865.     [0,0,0,4,0,0,6,4,5],
  866.     [0,0,0,6,0,0,8,4,5],
  867.     [0,0,0,8,0,0,8,5,2]
  868. ];
  869. var knownRotation = -1;
  870. var knownNest = -1;
  871. var rail_repair = {
  872.     name:function() { return "rail_repair"; },
  873.     near_nest: function(colours) { return false; },
  874.     near_nest_move: function(colours, buddies) { return null; },
  875.     likely_nest: function(colours) {
  876.         var bestScore = 0;
  877.         // check every rotation for each 3x3 rail possibility
  878.         TARGET_RAIL_REPAIRATION.forEach(function (arrangement) {
  879.             ROTATIONS.forEach(function (rot){
  880.                 var score = 0;
  881.                 for(var i = 0; i < 9; i++) {
  882.                     score += arrangement[i] == view[rot[i]].color?1:0;
  883.                     //score -= view[rot[i]].color == 7?1:0;//fake rail
  884.                     score -= i != 4 && view[rot[i]].ant != null && view[rot[i]].ant.friend ? 4 : 0;
  885.                 }
  886.                 if(score > bestScore) {
  887.                     bestScore = score;
  888.                     knownRotation = ROTATIONS.indexOf(rot);
  889.                     knownNest = TARGET_RAIL_REPAIRATION.indexOf(arrangement);
  890.                 }
  891.             });
  892.         });
  893.         if(bestScore >= 4) {
  894.             if(highway.likely_nest(colours)) return false;
  895.             return true;
  896.         }
  897.  
  898.         return false;
  899.     },
  900.     worth_leeching: function(myFood, buddies) {
  901.         return myFood > 4 && ((myFood < 80) || (myFood % 3 == 0));
  902.     },
  903.     likely_victim: function(victim_pos) {
  904.         if(view[victim_pos].color === WHITE) {
  905.             return false;
  906.         }
  907.         for(var i = 0; i < 9; ++ i) {
  908.             if(i !== victim_pos && near(i, victim_pos)) {
  909.                 return true;
  910.             }
  911.         }
  912.         return false;
  913.     },
  914.  
  915.     follow_victim: function(me, buddies, colours, victim_pos) {
  916.         if(me.type === THIEF && !buddies[QUEEN]) {
  917.             // Queen may be on other side of victim; try orbiting to find her
  918.             return try_all_angles([
  919.                 (rot) => ((victim_pos === rot[1]) ? {cell: rot[0]} : null),
  920.                 (rot) => ((victim_pos === rot[0]) ? {cell: rot[3]} : null)
  921.             ]);
  922.         }
  923.  
  924.         if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {
  925.             // Make a buddy to help us steal
  926.             return best_of([
  927.                 try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
  928.                 try_all_cells((i) => ({cell: i, type: THIEF}), true)
  929.             ]);
  930.         }
  931.  
  932.         return best_of([
  933.             // Ziggurat will try to drown us in workers; make sure we have plenty
  934.             // of our own (but only if it's worth it)
  935.             // Rail Miners doesn't currently spam workers, but this code doesn't hurt
  936.             (view[victim_pos].ant.food > 4 && buddies[THIEF] < 3) &&
  937.                 try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
  938.  
  939.             // Continue to steal and get fat
  940.             NOP
  941.         ]);
  942.     },
  943.  
  944.     find_victim: function(me, buddies, colours) {
  945.         var current = view[CENTRE].color;
  946.  
  947.         // Easier to navigate alone, and thief reaching queen first would be a
  948.         // disaster, so the thief just gives up.
  949.         if(me.type === THIEF) {
  950.             return go_anywhere;
  951.         }
  952.  
  953.         //find first bad cell to repaint
  954.         var painter = -1;
  955.         var PAINT_ORDER = [0, 2, 3, 5, 6, 7, 8, 1];
  956.         for(var o = 0; o < 8; o++) {
  957.             var i = PAINT_ORDER[o];
  958.             if(TARGET_ARRANGEMENT_RAIL[knownNest][i] != view[ROTATIONS[knownRotation][i]].color) {
  959.                 return {cell:ROTATIONS[knownRotation][i],color:TARGET_ARRANGEMENT_RAIL[knownNest][i]}
  960.             }
  961.             if(view[i].ant && !view[i].ant.friend) {
  962.                 painter = i;
  963.             }
  964.         }
  965.         if(painter < 0)
  966.             return {cell:ROTATIONS[knownRotation][1]};
  967.         /*var cls = [2,4,5];
  968.         if(!view[deRotate(painter,1)].ant && cls.indexOf(view[deRotate(painter,1)].color) < 0)
  969.             return {cell:deRotate(painter,1), type: 1};
  970.         if(!view[deRotate(painter,-1)].ant && cls.indexOf(view[deRotate(painter,-1)].color) < 0)
  971.             return {cell:deRotate(painter,-1), type: 1};
  972.         if(!view[deRotate(painter,-1)].ant && view[deRotate(painter,-1)].color == 4)
  973.             return {cell:deRotate(painter,-1), type: 1};
  974.         if(!view[deRotate(painter,1)].ant && view[deRotate(painter,1)].color == 5)
  975.             return {cell:deRotate(painter,1), type: 1};*/
  976.         return NOP;
  977.     }
  978. };
  979.  
  980. var TARGET_ARRANGEMENT_WIND = [
  981.         [5,4,0,7,6,0,6,4,0],
  982.         [7,6,0,6,4,0,5,4,0],
  983.         [6,4,0,5,4,0,7,6,0]
  984. ];
  985. var TARGET_ARRANGEMENT_WINDCENTER = [
  986.         [2,7,6,2,6,4,6,5,4],
  987.         [2,6,4,6,5,4,2,7,6],
  988.         [6,5,4,2,7,6,2,6,4]
  989. ];
  990. var WIND_BAND = colour_band([5,6,7]);
  991. var windmill = {
  992.     name:function() { return "windmill"; },
  993.     near_nest: function(colours) { return false; },
  994.     near_nest_move: function(colours, buddies) { return null; },
  995.     likely_nest: function(colours) { // Main nest detection
  996.         var bestScore = 0;
  997.         // check every rotation for each 3x3 rail possibility
  998.         TARGET_ARRANGEMENT_WIND.forEach(function (arrangement) {
  999.             ROTATIONS.forEach(function (rot){
  1000.                 var score = 0;
  1001.                 for(var i = 0; i < 9; i++) {
  1002.                     score += arrangement[i] == view[rot[i]].color?1:0;
  1003.                 }
  1004.                 if(score > bestScore) {
  1005.                     bestScore = score;
  1006.                 }
  1007.             });
  1008.         });
  1009.         if(bestScore >= 5) {
  1010.             return true;
  1011.         }
  1012.  
  1013.         var bestScore = 0;
  1014.         // check every rotation for each 3x3 rail possibility
  1015.         TARGET_ARRANGEMENT_WINDCENTER.forEach(function (arrangement) {
  1016.             ROTATIONS.forEach(function (rot){
  1017.                 var score = 0;
  1018.                 for(var i = 0; i < 9; i++) {
  1019.                     score += arrangement[i] == view[rot[i]].color?1:0;
  1020.                 }
  1021.                 if(score > bestScore) {
  1022.                     bestScore = score;
  1023.                 }
  1024.             });
  1025.         });
  1026.         if(bestScore >= 8) {
  1027.             return true;
  1028.         }
  1029.         var buddies = [0, 0, 0, 0, 0, 0];
  1030.         for(var i = 0; i < 9; ++ i) {
  1031.             ++ buddies[friend_at(i)];
  1032.         }
  1033.         return buddies[LANCE] || buddies[LANCE_TIP];
  1034.     },
  1035.     worth_leeching: function(myFood, buddies) {
  1036.         return myFood > 5 || (myFood > 1 && buddies[LANCE]);
  1037.     },
  1038.     likely_victim: function(victim_pos) {
  1039.         return false;
  1040.     },
  1041.  
  1042.     follow_victim: function(me, buddies, colours, victim_pos) {
  1043.         // nest is chaotic and varies by direction of approach
  1044.         // We'll let the Find Victim logic handle this
  1045.         return NOP;
  1046.     },
  1047.  
  1048.     find_victim: function(me, buddies, colours) {
  1049.         if(buddies[LANCE_TIP]) {
  1050.             var lancePos = -1;
  1051.             for(var i=0;i<9;i++) {
  1052.                 if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
  1053.                     lancePos = i;
  1054.                 }
  1055.             }
  1056.             if(buddies[LANCE]) {
  1057.                 if(view[8-lancePos].ant != null && view[8-lancePos].ant.friend && view[8-lancePos].ant.type == 3) {
  1058.                     return NOP;
  1059.                 }
  1060.                 if(view[deRotate(lancePos,3)].ant != null && view[deRotate(lancePos,3)].ant.friend && view[deRotate(lancePos,3)].ant.type == 3) {
  1061.                     if(view[lancePos].color == 2 && view[deRotate(lancePos,1)].color == 5) {
  1062.                         return {cell:deRotate(lancePos,1)};
  1063.                     }
  1064.                     return NOP;
  1065.                 }
  1066.                 if(view[4].color == 6 && view[lancePos].color == 6 && view[deRotate(lancePos,1)].ant != null && view[deRotate(lancePos,1)].ant.type == LANCE && view[deRotate(lancePos,1)].ant.friend) {
  1067.                     if(view[deRotate(lancePos,2)].ant != null && !view[deRotate(lancePos,2)].ant.friend) {
  1068.                         return {cell:8-deRotate(lancePos,2)};
  1069.                     }
  1070.                     return NOP;
  1071.                 }
  1072.                 if(view[lancePos].color == 2 && view[deRotate(lancePos,-3)].color == 5 && view[deRotate(lancePos,-3)].ant != null && view[deRotate(lancePos,-3)].ant.friend && view[deRotate(lancePos,-3)].ant.type == LANCE) {
  1073.                     return NOP;
  1074.                 }
  1075.                 return {cell:deRotate(lancePos,-1)};
  1076.             }
  1077.             if(view[deRotate(lancePos,-1)].ant != null && view[deRotate(lancePos,-1)].ant.type == 5) {
  1078.                 return {cell:deRotate(lancePos,2)};
  1079.             }
  1080.             if(view[lancePos].color == 5 && view[deRotate(lancePos,1)].color == 7) {
  1081.                 return {cell:deRotate(lancePos,1)};
  1082.             }
  1083.             return {cell:deRotate(lancePos,-1)};
  1084.         }
  1085.         else if(buddies[LANCE]) {
  1086.             var lancePos = -1;
  1087.             for(var i=0;i<9;i++) {
  1088.                 if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE) {
  1089.                     lancePos = i;
  1090.                 }
  1091.             }
  1092.             if(view[4].color == 3 && lancePos % 2 == 1) return NOP;
  1093.             //if(view[lancePos].ant.food > 0) return NOP;
  1094.             var moveNext = lancePos % 2 == 1 ? {cell:deRotate(lancePos,2)} : {cell:deRotate(lancePos,1)};
  1095.             if(view[moveNext.cell].ant != null && !view[moveNext.cell].ant.friend) {
  1096.                 moveNext = {cell:deRotate(lancePos,1),type:LANCE_TIP};
  1097.             }
  1098.             if(view[lancePos].ant.food > 0) {
  1099.                 return {cell:deRotate(lancePos,4),type:LANCE_TIP};
  1100.             }
  1101.  
  1102.             /*if(view[deRotate(lancePos,1)].ant != null && !view[deRotate(lancePos,1)].ant.friend && view[deRotate(lancePos,2)].ant != null && !view[deRotate(lancePos,2)].ant.friend) {
  1103.                 return {cell:deRotate(lancePos,-2), type: BOUNCER};
  1104.             }*/
  1105.  
  1106.             if(view[lancePos].color == 6 && view[moveNext.cell].color == 8 && view[deRotate(lancePos,2)].color == 5) {
  1107.                 return {cell:moveNext.cell,type:LANCE_TIP};
  1108.             }
  1109.  
  1110.             return moveNext;
  1111.         }
  1112.         else {
  1113.             var current = view[CENTRE].color;
  1114.             var standOn = WIND_BAND.next(WIND_BAND.next(WIND_BAND.next(current)));
  1115.             var target = WIND_BAND.next(current);
  1116.             var antitarget = WIND_BAND.next(target);
  1117.             if(current != standOn) return wilderness.find_victim(me, buddies, colours);
  1118.  
  1119.             var ret = best_of([
  1120.                 try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && !(view[deRotate(i,1)].color == 2 && view[deRotate(i,-1)].color == 2)) ? {cell: i, type: LANCE} : null), true),
  1121.                 NOP
  1122.             ]);
  1123.             if(ret.cell == 4) {
  1124.                 return wilderness.find_victim(me, buddies, colours);
  1125.             }
  1126.             return ret;
  1127.         }
  1128.         return NOP;
  1129.     }
  1130. };
  1131.  
  1132. var TARGET_ARRANGEMENT_HIGHWAY = [
  1133.     [2,3,7,6,8,2,3,7,6],
  1134.     [2,3,7,7,6,4,4,2,3],
  1135.     [2,4,6,7,3,2,4,6,7],
  1136.     [3,2,4,4,6,7,7,3,2],
  1137.     [3,4,7,7,2,6,6,3,4],
  1138.     [3,4,7,2,6,3,4,7,2],
  1139.     [3,6,2,2,7,4,4,3,6],
  1140.     [4,7,2,2,5,6,3,4,7],
  1141.     [4,6,7,2,6,3,3,4,7],
  1142.     [4,6,7,7,3,2,2,4,6],
  1143.     [6,4,2,3,7,6,4,2,3],
  1144.     [7,3,2,2,4,6,6,7,3],
  1145.     [7,4,3,6,2,7,4,3,5]
  1146. ];
  1147. var HIGHWAY_BAND = colour_band([2,7,4,3,6]);
  1148. var HIGHWAY_BAND2 = colour_band([2,3,7,6,4]);
  1149.  
  1150. var highway = {
  1151.     name:function() { return "highway"; },                                       // For debugging
  1152.     near_nest: function(colours) { return false; },                   // For dodging enemy workers without getting lost
  1153.     near_nest_move: function(colours, buddies) { return null; }, // How to move when near_nest is true
  1154.     likely_nest: function(colours) { // Main nest detection
  1155.         var bestScore = 0;
  1156.         // check every rotation for each 3x3 rail possibility
  1157.         TARGET_ARRANGEMENT_HIGHWAY.forEach(function (arrangement) {
  1158.             ROTATIONS.forEach(function (rot){
  1159.                 var score = 0;
  1160.                 for(var i = 0; i < 9; i++) {
  1161.                     score += arrangement[i] == view[rot[i]].color?1:0;
  1162.                 }
  1163.                 if(score > bestScore) {
  1164.                     bestScore = score;
  1165.                 }
  1166.             });
  1167.         });
  1168.         if(bestScore >= 7) {
  1169.             return true;
  1170.         }
  1171.         if(this.isCenter(colours)) return true;
  1172.  
  1173.         return false;
  1174.     },           // Main nest detection
  1175.     isCenter: function(colours) {
  1176.         var bestScore = 0;
  1177.         ROTATIONS.forEach(function (rot){
  1178.             var score = 0;
  1179.             for(var i = 0; i < 9; i++) {
  1180.                 if(i >= 3 && i <= 5 && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 4 || view[rot[i]].color != view[rot[8-i]].color)) {
  1181.                     if(i != 4) {
  1182.                         score++;
  1183.                     }
  1184.                     else {
  1185.                         if(view[rot[3]].color != view[rot[5]].color && view[rot[1]].color == view[rot[7]].color && (view[rot[4]].color != view[rot[1]].color && view[rot[4]].color != view[rot[3]].color && view[rot[4]].color != view[rot[5]].color && view[rot[4]].color != view[rot[7]].color)) {
  1186.                             score++;
  1187.                         }
  1188.                     }
  1189.                 }
  1190.                 else if(i >= 6) {
  1191.                     if(view[rot[i]].color == view[rot[i-6]].color && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 7 || view[rot[i]].color != view[rot[8-i]].color) && view[rot[i]].color != view[4].color) {
  1192.                         score += 2;
  1193.                     }
  1194.                 }
  1195.             }
  1196.             if(score > bestScore) {
  1197.                 bestScore = score;
  1198.             }
  1199.         });
  1200.         if(bestScore >= 7) {
  1201.             return true;
  1202.         }
  1203.         return false;
  1204.     },
  1205.     worth_leeching:function(myFood, buddies){ return myFood > 80 && myFood < 500; }, // Is this nest worth leeching?
  1206.     likely_victim: function(victim_pos) {
  1207.         return true;
  1208.     },   // Identifying the target queen
  1209.     follow_victim: function(me, buddies, colours, victim_pos) {
  1210.         if(me.type == QUEEN && buddies[THIEF] < 3) {
  1211.             return best_of([
  1212.                 try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
  1213.                 try_all_cells((i) => ({cell: i, type: THIEF}), true)
  1214.             ]);
  1215.         }
  1216.         if(me.type == THIEF && buddies[QUEEN])
  1217.             return NOP;
  1218.         return go_anywhere;
  1219.     },   // How to handle what happens when the enemy queen is found
  1220.     find_victim: function(me, buddies, colours) {
  1221.         if(me.type == THIEF && !buddies[QUEEN]) {
  1222.             for(var i=0;i<9;i++) {
  1223.                 if(foe_at(i)) return NOP;
  1224.             }
  1225.             var target = HIGHWAY_BAND.prev(view[4].color);
  1226.             var followRail = best_of([
  1227.                 try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
  1228.                 NOP
  1229.             ]);
  1230.         }
  1231.         else {
  1232.             var target = HIGHWAY_BAND.next(view[4].color);
  1233.             var followRail = best_of([
  1234.                 try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
  1235.                 NOP
  1236.             ]);
  1237.         }
  1238.         return followRail;
  1239.     }                 // How to follow the nest
  1240. }
  1241.  
  1242. var wilderness = {
  1243.     name:function() { return "wilderness"; },
  1244.     near_nest: function(colours) { return false; },
  1245.     near_nest_move: function(colours, buddies) { return null; },
  1246.     likely_nest: function(colours) {
  1247.         return true;
  1248.     },
  1249.     worth_leeching: function(myFood, buddies) {
  1250.         return true;
  1251.     },
  1252.     likely_victim: function(victim_pos) {
  1253.         return true;
  1254.     },
  1255.  
  1256.     follow_victim: function(me, buddies, colours, victim_pos) {
  1257.         // We stumbled across a random queen; make the most of it
  1258.         // TODO
  1259.  
  1260.         // avoids blocking off the rail miner queen from her workers
  1261.         // (we'd like to leech her again)
  1262.         if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {
  1263.             // Make a buddy to help us steal
  1264.             return best_of([
  1265.                 try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
  1266.                 try_all_cells((i) => ({cell: i, type: THIEF}), true)
  1267.             ]);
  1268.         }
  1269.         else if(me.type === QUEEN){
  1270.             var enemyCount = 0;
  1271.             var allyPos = -1;
  1272.             for(var a=0; a<9; a++) {
  1273.                 if(a != 4 && view[a].ant != null) {
  1274.                     if(view[a].ant.friend) {
  1275.                         if(near(a,victim_pos)){
  1276.                             allyPos = a;
  1277.                         }
  1278.                     }
  1279.                     else if(view[a].ant.type != 5) {
  1280.                         enemyCount++;
  1281.                     }
  1282.                 }
  1283.             }
  1284.             if(enemyCount >= buddies[THIEF] && allyPos >= 0) {
  1285.                 //if next to the queen and we're outnumbered, move back to the center of the rail.
  1286.                 var target = TARGET_COLOURS_RAIL.prev(view[allyPos].color);
  1287.                 var best = best_of([
  1288.                     try_all_cells((i) => (near(i, victim_pos) && i % 2 == 0 ? {cell: i, type: THIEF} : null), true),
  1289.                     try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true)
  1290.                 ]);
  1291.                 if(best != null) return best;
  1292.  
  1293.                 best_of([
  1294.                     try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null))
  1295.                 ]);
  1296.                 if(best != null) return best;
  1297.  
  1298.                 return best_of([
  1299.                     {cell:deRotate(allyPos,1)},
  1300.                     {cell:deRotate(allyPos,-1)}
  1301.                 ]);
  1302.             }
  1303.         }
  1304.  
  1305.         return NOP;
  1306.     },
  1307.     find_victim: function(me, buddies, colours) {
  1308.         if(me.type === QUEEN) {
  1309.             var in_void = true;
  1310.             for(var i = 0; i < 9; ++ i) {
  1311.                 if(view[i].color !== WHITE && !SAFE_COLOURS.contains(view[i].color)) {
  1312.                     in_void = false;
  1313.                     break;
  1314.                 }
  1315.             }
  1316.             if(!in_void) {
  1317.                 // because of avoiding returning Miner on a Rail workers
  1318.                 // we dodge sideways and this takes us back onto track
  1319.                 var nearMove = checkAllNearEnvirons(colours, buddies)
  1320.                 if(nearMove) return nearMove;
  1321.                 if(rail_miners.likely_nest(colours)) {
  1322.                     var r = try_all_angles.bind(null, [
  1323.                         (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[8], type: BOUNCER} : null)
  1324.                     ]);
  1325.                     var m = r();
  1326.                     if(sanity_check(m)) {
  1327.                         return m;
  1328.                     }
  1329.                     var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true);
  1330.                     if(m) {
  1331.                         return m;
  1332.                     }
  1333.                 }
  1334.             }
  1335.             return best_of([
  1336.                 // Make a buddy once we have a reasonable stash of food so we can
  1337.                 // search the board faster
  1338.                 // (but avoid making buddies when there's a potential nest nearby;
  1339.                 // better to wait until we find their queen)
  1340.                 (!buddies[THIEF] && me.food >= INITIAL_GATHER && in_void) &&
  1341.                     try_all_cells((i) => ({cell: i, type: THIEF}), true),
  1342.  
  1343.                 // Follow buddy in search of victims
  1344.                 buddies[THIEF] && try_all_angles.bind(null, [
  1345.                     (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
  1346.                     (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
  1347.                 ]),
  1348.                 buddies[THIEF] && NOP, // Don't lose our buddy!
  1349.  
  1350.                 // Random walk until we can make a buddy or find the victim
  1351.                 grab_nearby_food,
  1352.                 foe_near() ? go_anywhere : fast_diagonal.bind(null, SAFE_COLOURS),
  1353.                 go_anywhere
  1354.             ]);
  1355.         } else if(me.type === THIEF) {
  1356.             return best_of([
  1357.                 // Lost the queen! Random walk because we have nothing better to do.
  1358.                 // (don't leave lines; they could disrupt the pattern)
  1359.                 !buddies[QUEEN] && go_anywhere,
  1360.                 buddies[BOUNCER] && go_anywhere,
  1361.  
  1362.                 // Follow queen in search of victims
  1363.                 try_all_angles.bind(null, [
  1364.                     (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
  1365.                     (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
  1366.                 ]),
  1367.                 NOP // Don't lose our buddy!
  1368.             ]);
  1369.         }
  1370.     }
  1371. };
  1372.  
  1373. var victims = [highway, rail_miners, rail_repair, windmill];
  1374.  
  1375. function guess_environment(colours, buddies) {
  1376.     var food = view[4].ant.food;
  1377.     if(view[4].ant.type !== QUEEN) {
  1378.         for(var i = 0; i < 9; i++) {
  1379.             if(i != 4 && view[i].ant && view[i].ant.friend && view[i].ant.type === QUEEN) {
  1380.                 food = view[i].ant.food;
  1381.             }
  1382.         }
  1383.     }
  1384.     for(var i = 0; i < victims.length; ++ i) {
  1385.         if(victims[i].likely_nest(colours) && victims[i].worth_leeching(food, buddies)) {
  1386.             return victims[i];
  1387.         }
  1388.     }
  1389.  
  1390.     return wilderness;
  1391. }
  1392.  
  1393. function is_safe(i) {
  1394.     var nearThief = false;
  1395.     var nearOfficer = false;
  1396.     for(var j = 0; j < 9; ++ j) {
  1397.         if(friend_at(j) === THIEF) {
  1398.             nearThief = true;
  1399.         }
  1400.         if(foe_at(j) && foe_at(j) !== QUEEN) {
  1401.             nearOfficer = true;
  1402.         }
  1403.     }
  1404.     return nearThief && !nearOfficer;
  1405. }
  1406.  
  1407. function move(me, buddies) {
  1408.     var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  1409.     for(var i = 0; i < 9; ++ i) {
  1410.         ++ colours[view[i].color];
  1411.     }
  1412.     var env = guess_environment(colours,buddies);
  1413.     var victim_pos = -1;
  1414.     for(var i = 0; i < 9; ++ i) {
  1415.         if(foe_at(i) === QUEEN && env.likely_victim(i) && view[i].ant.food > 0) {
  1416.             victim_pos = i;
  1417.             if(view[i].ant.food > 0) {
  1418.                 break;
  1419.             }
  1420.         }
  1421.     }
  1422.  
  1423.     var in_void = true;
  1424.     for(var i = 0; i < 9; ++ i) {
  1425.         if(view[i].color !== WHITE || (i != 4 && me.type === BOUNCER && friend_at(i) === BOUNCER)) {
  1426.             in_void = false;
  1427.             break;
  1428.         }
  1429.     }
  1430.     if(me.type === BOUNCER) {
  1431.         if(env === wilderness && in_void) {
  1432.             // Our work is done; leave queen and wander at random
  1433.             if(buddies[QUEEN]) {
  1434.                 return best_of([
  1435.                     try_all_cells((i) => (ant_type_near(i, true) ? null : {cell: i}), true),
  1436.                     go_anywhere
  1437.                 ]);
  1438.             }
  1439.             return NOP;
  1440.         }
  1441.         else if(env === rail_miners) {
  1442.             // Our work is done; leave queen and wander at random
  1443.             if(buddies[QUEEN]) {
  1444.                 var allAngles = try_all_angles.bind(null, [
  1445.                     (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
  1446.                     (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
  1447.                     NOP
  1448.                 ]);
  1449.                 return best_of([
  1450.                     //if next to an enemy queen, move out of the way
  1451.                     try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
  1452.                     try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
  1453.                     allAngles
  1454.                 ]);
  1455.             }
  1456.             return NOP;
  1457.         } else if(buddies[QUEEN]) {
  1458.             // Escort queen out of nest
  1459.             var allAngles = try_all_angles.bind(null, [
  1460.                 (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
  1461.                 (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
  1462.                 go_anywhere
  1463.             ]);
  1464.  
  1465.             return best_of([
  1466.                 //if next to an enemy queen, move out of the way
  1467.                 try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
  1468.                 try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
  1469.                 allAngles
  1470.             ]);
  1471.         }
  1472.         else {
  1473.             return go_anywhere;
  1474.         }
  1475.     } else if(buddies[BOUNCER]) {
  1476.         if(me.type === QUEEN) {
  1477.             // Be escorted out of nest
  1478.             return try_all_angles.bind(null, [
  1479.                 (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[2]} : null),
  1480.                 (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[1]} : null),
  1481.                 go_anywhere,
  1482.                 NOP
  1483.             ]);
  1484.         } else {
  1485.             // Get out of the way
  1486.             return try_all_angles.bind(null, [
  1487.                 (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[7]} : null),
  1488.                 (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[8]} : null),
  1489.                 (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[7]} : null),
  1490.                 (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[8]} : null),
  1491.                 go_anywhere
  1492.             ]);
  1493.         }
  1494.     }
  1495.     if(victim_pos !== -1) {
  1496.         // abandon the queen if she's dry.
  1497.         // abandon rail miner's queen so she has at least 10 food (otherwise she produces workers 3:4 food she aquires)
  1498.         // value is higher than 10 because there's two to three rounds of theft (at 4 ants each) before the queen gets out of range
  1499.         // this can still leave the rail miner's queen lower than 10, but unlikely
  1500.         // other queens are abandoned if they have less than 5 food, due to the "max 4 ants stealing" and at 0 food, she's not a target.
  1501.         if(view[victim_pos].ant.food < 5 || ((env == rail_miners || env == rail_repair) && view[victim_pos].ant.food < 28)) {
  1502.             if(me.type == THIEF) {
  1503.                 if(rail_miners.near_nest(colours)) {
  1504.                     // we'd rather reuse the workers
  1505.                     return NOP;
  1506.                 }
  1507.             }
  1508.             // Victim is out of food; bounce out of nest
  1509.             if(env == rail_miners || env == rail_repair) {
  1510.                 // murder SlM
  1511.                 return NOP;
  1512.             }
  1513.             var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true);
  1514.             if(m) {
  1515.                 return m;
  1516.             }
  1517.         }
  1518.         if(me.type === QUEEN && buddies[THIEF] && !is_safe(CENTRE)) {
  1519.             // Try to avoid getting food stolen back from us
  1520.             var m = try_all_cells((i) => (is_safe(i) ? {cell: i} : null), true);
  1521.             if(m) {
  1522.                 return m;
  1523.             }
  1524.         }
  1525.         return env.follow_victim(me, buddies, colours, victim_pos);
  1526.     } else {
  1527.         if(me.type == THIEF && buddies[QUEEN] == 0) {
  1528.             if(rail_miners.near_nest(colours)) {
  1529.                 // we'd rather reuse the workers
  1530.                 // with SlM they won't be next to the queen, but they may have food
  1531.                 // and if our queen comes by, she'll pick it up.
  1532.                 for(var i=0;i<9;i++) {
  1533.                     if(view[i].ant && !view[i].ant.friend && view[i].ant.food == 0) {
  1534.                         return rail_miners.near_nest_move(colours, buddies);
  1535.                     }
  1536.                 }
  1537.                 return NOP;
  1538.             }
  1539.         }
  1540.         return env.find_victim(me, buddies, colours);
  1541.     }
  1542. }
  1543.  
  1544. // LANCE is only used by windmill targetting, easier to break this out as its own method
  1545. function moveLance(me, buddies) {
  1546.     var queenPos = -1;
  1547.     var tipPos = -1;
  1548.     if(buddies[BOUNCER]) {
  1549.         for(var i=0;i<9;i++) {
  1550.             if(view[i].ant && view[i].ant.friend && view[i].ant.type == BOUNCER) {
  1551.                 return {cell:8-i};
  1552.             }
  1553.         }
  1554.     }
  1555.     if(!buddies[QUEEN]) {
  1556.         for(var i=0;i<9;i++) {
  1557.             if(i % 2 == 0 && view[i].ant && !view[i].ant.friend && view[i].ant.type == QUEEN) {
  1558.                 if(view[deRotate(i,3)].ant != null && view[deRotate(i,3)].ant.friend && view[deRotate(i,3)].ant.type == LANCE_TIP) return NOP;
  1559.                 return {cell:deRotate(i,1)};
  1560.             }
  1561.         }
  1562.         return NOP;
  1563.     }
  1564.     for(var i=0;i<9;i++) {
  1565.         if(view[i].ant && view[i].ant.friend && view[i].ant.type == QUEEN) {
  1566.             queenPos = i;
  1567.         }
  1568.         if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
  1569.             tipPos = i;
  1570.         }
  1571.     }
  1572.     if(buddies[LANCE_TIP]) {
  1573.         if(deRotate(queenPos,-1) == tipPos && view[tipPos].color == 8) return {cell:8-tipPos};
  1574.         if(deRotate(queenPos,-1) == tipPos) return try_all_cells((i) => (areAdjacent(i,tipPos) && view[i].color == 5 ? {cell:i} : null));
  1575.         if(view[8-tipPos].ant != null && !view[8-tipPos].ant.friend && view[8-tipPos].ant.type == 5) return {cell:8-tipPos,color:6};
  1576.         return try_all_cells((i) => (!areAdjacent(i,queenPos) && !areAdjacent(i,tipPos) ? {cell:i} : null));
  1577.     }
  1578.     if(view[4].color != 4 && view[4].color != 6) {
  1579.         if(view[8-queenPos].ant != null && !view[8-queenPos].ant.friend && view[8-queenPos].ant.type == 5) return NOP;
  1580.         return best_of([
  1581.             try_all_cells((i) => (i % 2 == 1 && (view[i].color == 4 || view[i].color == 6) && view[deRotate(i,1)].color != 2 && view[deRotate(i,-1)].color != 2 && areAdjacent(i,queenPos) ? {cell: i} : null), true),
  1582.             NOP
  1583.         ]);
  1584.     }
  1585.     else {
  1586.         var queenOn = view[8-queenPos].color;
  1587.         var target = WIND_BAND.next(queenOn);
  1588.         var prior = WIND_BAND.next(target);
  1589.         var followRail = best_of([
  1590.             try_all_cells((i) => (view[deRotate(i,-3)].color == prior && view[deRotate(i,-1)].color == target && areAdjacent(i,queenPos) && (view[i].color == 4 || view[i].color == 6) ? {cell: i} : null), true),
  1591.             queenPos % 2 == 1 ? (view[queenPos].color == 4 || view[4].color == 4 ? NOP : {cell:deRotate(queenPos,-2)}) : (view[queenPos].color == 4 || view[queenPos].color == 6 ? {cell:deRotate(queenPos,-1)} : NOP)
  1592.         ]);
  1593.  
  1594.         if(view[deRotate(queenPos,-1)].ant != null) {
  1595.             if(!view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant != null && !view[deRotate(queenPos,-2)].ant.friend) {
  1596.                 return {cell:deRotate(queenPos,1)};
  1597.             }
  1598.             return NOP;
  1599.         }
  1600.         if(me.food > 0 && queenPos % 2 == 0) {
  1601.             return {cell:deRotate(queenPos,-1)};
  1602.         }
  1603.         if(view[deRotate(queenPos,-3)].ant != null && !view[deRotate(queenPos,-3)].ant.friend && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
  1604.             return {cell:queenPos,color:3};
  1605.         }
  1606.         if(view[deRotate(queenPos,-4)].ant != null && !view[deRotate(queenPos,-4)].ant.friend && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
  1607.             return {cell:queenPos,color:3};
  1608.         }
  1609.         return followRail;
  1610.     }
  1611.     return NOP;
  1612. }
  1613.  
  1614. // LANCE_TIP never needs to move
  1615. // Unfortunately, reusing an existing worker type for this purpose is not easily possible.
  1616. // Used against Sliding Miners as a stationary blocker to prevent the queen slipping past.
  1617. function moveTip(me, buddies) {
  1618.     var queenPos = -1;
  1619.     var in_void = true;
  1620.     for(var i=0;i<9;i++) {
  1621.         if(view[i].ant && view[i].ant.friend && view[i].ant.type == QUEEN) {
  1622.             queenPos = i;
  1623.         }
  1624.         if(view[i].color != WHITE) {
  1625.             in_void = false;
  1626.         }
  1627.     }
  1628.     var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  1629.     for(var i = 0; i < 9; ++ i) {
  1630.         ++ colours[view[i].color];
  1631.     }
  1632.     var onRails = rail_miners.near_nest(colours)
  1633.  
  1634.     if(buddies[QUEEN] && !buddies[LANCE]) {
  1635.         if(onRails) return NOP;
  1636.         if(view[8-queenPos].ant != null && !view[8-queenPos].ant.friend && view[8-queenPos].ant.type == 4) {
  1637.             return {cell:deRotate(queenPos,2)};
  1638.         }
  1639.         if(in_void) return {cell:deRotate(queenPos,4)};
  1640.     }
  1641.     if(buddies[QUEEN] && buddies[LANCE]) {
  1642.         return {cell:deRotate(queenPos,2)};
  1643.     }
  1644.     if(buddies[QUEEN] && buddies[BOUNCER]) {
  1645.         // rail miners bounce out
  1646.         return go_anywhere;
  1647.     }
  1648.     return NOP;
  1649. }
  1650.  
  1651. function deRotate(m, amt) {
  1652.     var rotationsCW = [1,2,5,8,7,6,3,0];
  1653.     var rotationsCCW = [3,6,7,8,5,2,1,0];
  1654.     if(m == 4 || m < 0 || m > 8 || amt == 0) return m;
  1655.     if(amt > 0)
  1656.         return rotationsCW[(rotationsCW.indexOf(m)+amt)%8];
  1657.     amt = -amt;
  1658.     return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8];
  1659. }
  1660.  
  1661. return play_safe(move_agent([
  1662.     THIEF, move,
  1663.     QUEEN, move,
  1664.     BOUNCER, move,
  1665.     LANCE, moveLance,
  1666.     LANCE_TIP, moveTip
  1667. ]));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement