Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // == Shared low-level helpers for all solutions ==
- var QUEEN = 5;
- var WHITE = 1;
- var COL_MIN = WHITE;
- var COL_LIM = 9;
- var CENTRE = 4;
- var NOP = {cell: CENTRE};
- var DIR_FORWARDS = false;
- var DIR_REVERSE = true;
- var SIDE_RIGHT = true;
- var SIDE_LEFT = false;
- //if(view[4].ant.type >= 5) console.log(JSON.stringify(view))
- function sanity_check(movement) {
- var me = view[CENTRE].ant;
- if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
- return false;
- }
- if(movement.type) {
- if(movement.color) {
- return false;
- }
- if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
- return false;
- }
- if(view[movement.cell].ant || view[movement.cell].food) {
- return false;
- }
- if(me.type !== QUEEN || me.food < 1) {
- return false;
- }
- return true;
- }
- if(movement.color) {
- if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
- return false;
- }
- if(view[movement.cell].color === movement.color) {
- return false;
- }
- return true;
- }
- if(view[movement.cell].ant && movement.cell != 4) {
- return false;
- }
- if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
- return false;
- }
- return true;
- }
- function as_array(o) {
- if(Array.isArray(o)) {
- return o;
- }
- return [o];
- }
- function best_of(movements) {
- var m;
- for(var i = 0; i < movements.length; ++ i) {
- if(typeof(movements[i]) === 'function') {
- m = movements[i]();
- } else {
- m = movements[i];
- }
- if(sanity_check(m)) {
- return m;
- }
- }
- return null;
- }
- function play_safe(movement) {
- // Avoid disqualification: no-op if moves are invalid
- return best_of(as_array(movement)) || NOP;
- }
- var RAND_SEED = (() => {
- var s = 0;
- for(var i = 0; i < 9; ++ i) {
- s += view[i].color * (i + 1);
- s += view[i].ant ? i * i : 0;
- s += view[i].food ? i * i * i : 0;
- }
- return s % 29;
- })();
- var ROTATIONS = [
- [0, 1, 2, 3, 4, 5, 6, 7, 8],
- [6, 3, 0, 7, 4, 1, 8, 5, 2],
- [8, 7, 6, 5, 4, 3, 2, 1, 0],
- [2, 5, 8, 1, 4, 7, 0, 3, 6],
- ];
- function areAdjacent(A, B) {
- if(A == 4 || B == 4 || A == B) return true;
- if(A % 2 == 0 && B % 2 == 0) return false;
- if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A);
- if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A);
- if(A == 0 && (B == 1 || B == 3)) return true;
- if(A == 2 && (B == 1 || B == 5)) return true;
- if(A == 6 && (B == 3 || B == 7)) return true;
- if(A == 8 && (B == 5 || B == 7)) return true;
- return false;
- }
- function try_all(fns, limit, wrapperFn, checkFn) {
- var m;
- fns = as_array(fns);
- for(var i = 0; i < fns.length; ++ i) {
- if(typeof(fns[i]) !== 'function') {
- if(checkFn(m = fns[i])) {
- return m;
- }
- continue;
- }
- for(var j = 0; j < limit; ++ j) {
- if(checkFn(m = wrapperFn(fns[i], j))) {
- return m;
- }
- }
- }
- return null;
- }
- function identify_rotation(testFns) {
- // testFns MUST be functions, not constants
- return try_all(
- testFns,
- 4,
- (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
- (r) => r
- );
- }
- function near(a, b) {
- return (
- Math.abs(a % 3 - b % 3) < 2 &&
- Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
- );
- }
- function try_all_angles(solverFns) {
- return try_all(
- solverFns,
- 4,
- (fn, r) => fn(ROTATIONS[r]),
- sanity_check
- );
- }
- function try_all_cells(solverFns, skipCentre) {
- return try_all(
- solverFns,
- 9,
- (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
- sanity_check
- );
- }
- function try_all_cells_near(p, solverFns) {
- return try_all(
- solverFns,
- 9,
- (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
- sanity_check
- );
- }
- function ant_type_at(i, friend) {
- return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
- }
- function friend_at(i) {
- return ant_type_at(i, true);
- }
- function foe_at(i) {
- return ant_type_at(i, false);
- }
- function foe_near() {
- for(var i = 0; i < 9; ++ i) {
- if(i !== 4 && view[i].ant && !view[i].ant.friend) {
- return true;
- }
- }
- return false;
- }
- function ant_type_near(p, friend) {
- for(var i = 0; i < 9; ++ i) {
- if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
- return true;
- }
- }
- return false;
- }
- function move_agent(agents) {
- var me = view[CENTRE].ant;
- var buddies = [0, 0, 0, 0, 0, 0];
- for(var i = 0; i < 9; ++ i) {
- ++ buddies[friend_at(i)];
- }
- for(var i = 0; i < agents.length; i += 2) {
- if(agents[i] === me.type) {
- return agents[i+1](me, buddies);
- }
- }
- return null;
- }
- function grab_nearby_food() {
- return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
- }
- function go_anywhere() {
- return try_all_cells((i) => ({cell: i}), true);
- }
- function colours_excluding(cols) {
- var r = [];
- for(var i = COL_MIN; i < COL_LIM; ++ i) {
- if(cols.indexOf(i) === -1) {
- r.push(i);
- }
- }
- return r;
- }
- function generate_band(start, width) {
- var r = [];
- for(var i = 0; i < width; ++ i) {
- r.push(start + i);
- }
- return r;
- }
- function colour_band(colours) {
- return {
- contains: function(c) {
- return colours.indexOf(c) !== -1;
- },
- next: function(c) {
- return colours[(colours.indexOf(c) + 1) % colours.length];
- },
- prev: function(c) {
- return colours[(colours.indexOf(c) + colours.length - 1) % colours.length];
- }
- };
- }
- function random_colour_band(colours) {
- return {
- contains: function(c) {
- return colours.indexOf(c) !== -1;
- },
- next: function() {
- return colours[RAND_SEED % colours.length];
- }
- };
- }
- function fast_diagonal(colourBand) {
- var m = try_all_angles([
- // Avoid nearby checked areas
- (rot) => {
- if(
- !colourBand.contains(view[rot[0]].color) &&
- colourBand.contains(view[rot[5]].color) &&
- colourBand.contains(view[rot[7]].color)
- ) {
- return {cell: rot[0]};
- }
- },
- // Go in a straight diagonal line if possible
- (rot) => {
- if(
- !colourBand.contains(view[rot[0]].color) &&
- colourBand.contains(view[rot[8]].color)
- ) {
- return {cell: rot[0]};
- }
- },
- // When in doubt, pick randomly but avoid doubling-back
- (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),
- // Double-back when absolutely necessary
- (rot) => ({cell: rot[0]})
- ]);
- // Lay a colour track so that we can avoid doubling-back
- // (and mess up our foes as much as possible)
- if(!colourBand.contains(view[CENTRE].color)) {
- var prevCol = m ? view[8-m.cell].color : WHITE;
- var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
- for(var i = 0; i < 9; ++ i) {
- ++ colours[view[i].color];
- }
- return {cell: CENTRE, color: colourBand.next(prevCol)};
- }
- return m;
- }
- function checkAllNearEnvirons(colours, buddies) {
- var nearMoves = [victims.length];
- for(var e = 0; e < victims.length; e++) {
- var env = victims[e];
- nearMoves[e] = null;
- if(env.near_nest(colours)) {
- nearMoves[e] = env.near_nest_move(colours, buddies);
- }
- }
- return best_of(nearMoves);
- }
- function follow_edge(obstacleFn, side) {
- // Since we don't know which direction we came from, this can cause us to get
- // stuck on islands, but the random orientation helps to ensure we don't get
- // stuck forever.
- var order = ((side === SIDE_LEFT)
- ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
- : [0, 1, 2, 5, 8, 7, 6, 3, 0]
- );
- return try_all(
- [obstacleFn],
- order.length - 1,
- (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
- sanity_check
- );
- }
- function start_dotted_path(colourBand, side, protectedCols) {
- var right = (side === SIDE_RIGHT);
- return try_all_angles([
- (rot) => ((
- !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
- !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
- !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
- !colourBand.contains(view[rot[1]].color)
- )
- ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
- : null)
- ]);
- }
- function lay_dotted_path(colourBand, side, protectedCols) {
- var right = (side === SIDE_RIGHT);
- return try_all_angles([
- (rot) => {
- var ahead = rot[right ? 2 : 0];
- var behind = rot[right ? 8 : 6];
- if(
- colourBand.contains(view[behind].color) &&
- !protectedCols.contains(view[ahead].color) &&
- !colourBand.contains(view[ahead].color) &&
- !colourBand.contains(view[rot[right ? 6 : 8]].color)
- ) {
- return {cell: ahead, color: colourBand.next(view[behind].color)};
- }
- }
- ]);
- }
- function follow_dotted_path(colourBand, side, direction) {
- var forwards = (direction === DIR_REVERSE) ? 7 : 1;
- var right = (side === SIDE_RIGHT);
- return try_all_angles([
- // Cell on our side? advance
- (rot) => {
- if(
- colourBand.contains(view[rot[right ? 5 : 3]].color) &&
- // Prevent sticking / trickery
- !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
- !colourBand.contains(view[rot[0]].color) &&
- !colourBand.contains(view[rot[2]].color)
- ) {
- return {cell: rot[forwards]};
- }
- },
- // Cell ahead and behind? advance
- (rot) => {
- var passedCol = view[rot[right ? 8 : 6]].color;
- var nextCol = view[rot[right ? 2 : 0]].color;
- if(
- colourBand.contains(passedCol) &&
- nextCol === colourBand.next(passedCol) &&
- // Prevent sticking / trickery
- !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
- !colourBand.contains(view[rot[right ? 0 : 2]].color)
- ) {
- return {cell: rot[forwards]};
- }
- }
- ]);
- }
- function escape_dotted_path(colourBand, side, newColourBand) {
- var right = (side === SIDE_RIGHT);
- if(!newColourBand) {
- newColourBand = colourBand;
- }
- return try_all_angles([
- // Escape from beside the line
- (rot) => {
- var approachingCol = view[rot[right ? 2 : 0]].color;
- if(
- !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
- !colourBand.contains(approachingCol) ||
- colourBand.contains(view[rot[7]].color) ||
- colourBand.contains(view[rot[right ? 6 : 8]].color)
- ) {
- // not oriented, or in a corner
- return null;
- }
- return best_of([
- {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
- {cell: rot[right ? 3 : 5]},
- {cell: rot[right ? 0 : 2]},
- {cell: rot[right ? 6 : 8]},
- {cell: rot[right ? 2 : 0]},
- {cell: rot[right ? 8 : 6]},
- {cell: rot[right ? 5 : 3]}
- ]);
- },
- // Escape from inside the line
- (rot) => {
- if(
- !colourBand.contains(view[rot[7]].color) ||
- !colourBand.contains(view[rot[1]].color) ||
- colourBand.contains(view[CENTRE].color)
- ) {
- return null;
- }
- return best_of([
- {cell: rot[3]},
- {cell: rot[5]},
- {cell: rot[0]},
- {cell: rot[2]},
- {cell: rot[6]},
- {cell: rot[8]}
- ]);
- }
- ]);
- }
- function latch_to_dotted_path(colourBand, side) {
- var right = (side === SIDE_RIGHT);
- return try_all_angles([
- (rot) => {
- var approachingCol = view[rot[right ? 2 : 0]].color;
- if(
- colourBand.contains(approachingCol) &&
- view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
- !colourBand.contains(view[rot[right ? 5 : 3]].color)
- ) {
- // We're on the wrong side; go inside the line
- return {cell: rot[right ? 5 : 3]};
- }
- },
- // Inside the line? pick a side
- (rot) => {
- var passedCol = view[rot[7]].color;
- var approachingCol = view[rot[1]].color;
- if(
- !colourBand.contains(passedCol) ||
- !colourBand.contains(approachingCol) ||
- colourBand.contains(view[CENTRE].color)
- ) {
- return null;
- }
- if((approachingCol === colourBand.next(passedCol)) === right) {
- return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
- } else {
- return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
- }
- }
- ]);
- }
- // == High-level logic begins here ==
- var TARGET_COLOURS_ZIG = colour_band([4, 5, 7, 8]);
- var TARGET_COLOURS_FIREFLY = colour_band([2, 5, 8]);
- var GROUND_COLOURS_BH = colour_band([2, 7, 8]);
- var SAFE_COLOURS = random_colour_band([8]);
- var THIEF = 1;
- var BOUNCER = 2;
- var LANCE = 3;
- var LANCE_TIP = 4;
- var INITIAL_GATHER = 12;
- function colour_band_prev(band, base) {
- if(!band.contains(base)) {
- return band.next(WHITE);
- }
- var cur = band.next(base);
- var c;
- while((c = band.next(cur)) !== base) {
- cur = c;
- }
- return cur;
- }
- function white_near(p) {
- for(var i = 0; i < 9; ++ i) {
- if(near(i, p) && view[i].color === WHITE) {
- return true;
- }
- }
- return false;
- }
- function white_near(p, min) {
- var c = 0;
- for(var i = 0; i < 9; ++ i) {
- if(near(i, p) && view[i].color === WHITE) {
- if(++c >= min) return true;
- }
- }
- return false;
- }
- var TARGET_ARRANGEMENT_RAIL = [
- [8,4,5,8,5,2,4,2,6],
- [8,5,2,4,2,6,6,4,5],
- [4,2,6,6,4,5,8,4,5],
- [6,4,5,8,4,5,8,5,2]
- ];
- var TARGET_NEAR_RAIL = [
- [5,8,0,4,8,0,6,4,0],
- [2,4,0,5,8,0,4,8,0],
- [4,6,0,2,4,0,5,8,0],
- [4,8,0,4,6,0,2,4,0],
- [4,5,0,5,2,0,2,6,0],
- [4,5,0,4,5,0,5,2,0],
- [2,6,0,4,5,0,4,5,0],
- [5,2,0,2,6,0,4,5,0]
- ];
- var TARGET_COLOURS_RAIL = colour_band([4,2,5,4]);
- var rail_miners = {
- name:function() { return "rail_miners"; },
- near_nest: function(colours) {
- var bestScore = 0;
- var enemyQueen = false;
- // check every rotation for each 3x3 rail possibility
- TARGET_NEAR_RAIL.forEach(function (arrangement) {
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- enemyQueen |= view[i].ant && view[i].ant.type == QUEEN && !view[i].ant.friend;
- }
- if(score > bestScore) {
- bestScore = score;
- }
- });
- });
- if(bestScore >= (5 - (enemyQueen && view[4].ant.type == 1?1:0))) {
- if(highway.likely_nest(colours)) return false;
- return true;
- }
- },
- worth_leeching: function(myFood, buddies) {
- if(buddies[LANCE_TIP]) return true;
- if(buddies[THIEF]) return myFood > 5-buddies[THIEF] && ((myFood < 800) || (myFood % 3 == 0));
- return myFood > 10 && ((myFood < 500) || (myFood % 3 == 0));
- },
- near_nest_move: function(colours, buddies) {
- if(view[4].ant == QUEEN && !this.worth_leeching(view[4].ant.food, buddies)) return null;
- //if there's an ant we're dodging
- var victim_pos = -1;
- var enemyCount = 0;
- for(var a=0; a<9; a++) {
- if(view[a].ant != null && !view[a].ant.friend) {
- if(view[a].ant.type == 5) {
- victim_pos = a;
- }
- else {
- enemyCount++;
- }
- }
- }
- if(victim_pos >= 0 && enemyCount >= buddies[THIEF]) {
- //if next to the queen and we're outnumbered, move back to the center of the rail.
- var target = TARGET_COLOURS_RAIL.prev(view[victim_pos].color);
- var best = best_of([
- try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,victim_pos)) ? {cell: i} : null)),
- ]);
- if(best != null) return best;
- }
- for(var a=0; a<9; a++) {
- if(a != 4 && view[a].ant == null) {
- var target = TARGET_COLOURS_RAIL.prev(view[a].color);
- var ret = best_of([
- try_all_cells((i) => ((i != a && view[i].color == target && i % 2 == 0 && areAdjacent(i,a)) ? {cell: i} : null)),
- NOP
- ]);
- if(ret.cell != 4) {
- return ret;
- }
- }
- }
- if(!buddies[THIEF]) {
- //if there is not and we are alone
- for(var a=0; a<9; a++) {
- if(a != 4) {
- var target = TARGET_COLOURS_RAIL.prev(view[a].color);
- var best = best_of([
- try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null)),
- ]);
- if(best != null) return best;
- }
- }
- }
- return null;
- },
- likely_nest: function(colours) {
- var bestScore = 0;
- // check every rotation for each 3x3 rail possibility
- var q = 0;
- var near4 = -1;
- TARGET_ARRANGEMENT_RAIL.forEach(function (arrangement) {
- var j = 0;
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- //score -= view[rot[i]].color == 7?2:0;//fake rail detection
- if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == LANCE_TIP) near4 = i;
- }
- if(score > bestScore) {
- bestScore = score;
- }
- j++;
- });
- q++;
- });
- if(rail_miners.near_nest(colours) && near4 >= 0) return true;
- 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;
- if(bestScore >= 8) {
- if(highway.likely_nest(colours)) return false;
- return true;
- }
- return false;
- },
- likely_victim: function(victim_pos) {
- return true;
- },
- follow_victim: function(me, buddies, colours, victim_pos) {
- if(me.type === THIEF && !buddies[QUEEN]) {
- // Queen may be on other side of victim; try orbiting to find her
- return try_all_angles([
- (rot) => ((victim_pos === rot[1]) ? {cell: rot[0]} : null),
- (rot) => ((victim_pos === rot[0]) ? {cell: rot[3]} : null)
- ]);
- }
- var current = view[CENTRE].color;
- var target = TARGET_COLOURS_RAIL.next(current);
- var antitarget = TARGET_COLOURS_RAIL.prev(current);
- var forwardCell = -1
- for(var i = 0; i < 9; i++) {
- if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
- forwardCell = i;
- }
- }
- if(forwardCell < 0 && current == 4) {
- current = 4
- target = 4
- antitarget = 5
- for(var i = 0; i < 9; i++) {
- if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
- forwardCell = i;
- }
- }
- }
- if(me.type === QUEEN && !buddies[THIEF] && me.food > 0 && !buddies[LANCE_TIP]) {
- // Make a buddy to help us steal
- return best_of([
- (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,
- try_all_cells((i) => (near(i, victim_pos) && i != forwardCell ? {cell: i, type: THIEF} : null), true)
- ]);
- }
- return best_of([
- (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) &&
- (victim_pos % 2 == 1 && view[deRotate(victim_pos,-1)].ant == null) ? {cell: deRotate(victim_pos,-1), type: LANCE_TIP} : null,
- (view[victim_pos].ant.food > 30 && buddies[LANCE_TIP] == 3) &&
- 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),
- (view[victim_pos].ant.food > 30) &&
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
- // Continue to steal and get fat
- NOP
- ]);
- },
- find_victim: function(me, buddies, colours) {
- var current = view[CENTRE].color;
- if(me.type === QUEEN && current == WHITE && buddies[LANCE_TIP]) {
- //TODO: better bounce-out
- //if(buddies[LANCE_TIP] == 2 && me.food > 150) {
- // return best_of([
- // try_all_cells((i) => (view[i].ant == null ? {cell: i, type: BOUNCER} : null), true),
- // NOP
- // ]);
- //}
- return NOP;
- }
- // Easier to navigate alone, and thief reaching queen first would be a
- // disaster, so the thief just gives up.
- if(me.type === THIEF) {
- for(var i = 0; i < 9; ++ i) {
- if(foe_at(i) === QUEEN) {
- return NOP;
- }
- if(friend_at(i) === LANCE_TIP) {
- return NOP;
- }
- if(foe_at(i) === 4 && view[deRotate(i,1)].ant != null && view[deRotate(i,1)].ant.type == QUEEN) {
- //TODO: better bounce-out
- //if(view[8-deRotate(i,1)].ant != null && view[8-deRotate(i,1)].ant.type != QUEEN && i == 0) {
- // return {cell:deRotate(i,1),color:WHITE}
- //}
- return NOP;
- }
- }
- return go_anywhere;
- }
- var target = TARGET_COLOURS_RAIL.next(current);
- var antitarget = TARGET_COLOURS_RAIL.prev(current);
- var ret = best_of([
- try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget) ? {cell: i} : null)),
- //try_all_cells((i) => ((view[i].color == current) ? {cell: i} : null)),
- // We actually don't want to move if we're on the rails and there's no open next spot
- NOP
- ]);
- if((ret == null || ret.cell == 4) && current == 4) {
- current = 4
- target = 4
- antitarget = 5
- ret = best_of([
- try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget) ? {cell: i} : null)),
- //try_all_cells((i) => ((view[i].color == current) ? {cell: i} : null)),
- // We actually don't want to move if we're on the rails and there's no open next spot
- NOP
- ]);
- }
- // if there wasn't an obvious "forward" spot...
- if(ret == null || ret.cell == 4) {
- if(buddies[LANCE_TIP]) {
- return best_of([
- 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),
- 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),
- // We actually don't want to move if we're on the rails and there's no open next spot
- NOP
- ]);
- }
- var numAnts = 0;
- var enemyBehind = false;
- for(var na = 0; na < 9; na++) {
- if(view[na].ant != null && !view[na].ant.friend) numAnts++;
- if(view[na].ant != null && view[na].color == antitarget) enemyBehind = true;
- }
- if(numAnts == 0 || (enemyBehind && numAnts == 1)) {
- // if we're the only ant we can see (that is: unable to follow the rail)...
- if(rail_repair.likely_nest(colours)) {
- //try repairing the rail
- ret = rail_repair.find_victim(me, buddies, colours);
- }
- else {
- // otherwise go anywhere
- ret = go_anywhere;
- }
- }
- else {
- var likelyType4 = -1;
- for(var t = 0; t < 9; t++) {
- if(view[t].ant && !view[t].ant.friend && view[t].ant.type == 4) {
- likelyType4 = t;
- }
- }
- if(likelyType4 >= 0 && view[likelyType4].ant.food == 0 && me.food > 0) {
- ret = best_of([
- try_all_cells((i) => (buddies[THIEF] < 2 && i % 2 == 0 && view[i].ant == null && near(i,likelyType4)) ? {cell: i,type:THIEF} : null),
- NOP
- ]);
- }
- else {
- ret = go_anywhere;
- }
- }
- }
- else {
- ret = best_of([
- try_all_cells((i) => (i % 2 == 1 && view[i].food > 0) ? {cell: i} : null),
- ret
- ]);
- }
- if(!ret.color && !ret.type && (view[deRotate(ret.cell,1)].color == WHITE || buddies[LANCE_TIP] > 0)) {
- if(view[deRotate(ret.cell,2)].ant == null) {
- ret = {cell:deRotate(ret.cell,2),type: LANCE_TIP};
- }
- else if(view[deRotate(ret.cell,-2)].ant == null) {
- ret = {cell:deRotate(ret.cell,-2),type: LANCE_TIP};
- }
- else if(buddies[LANCE_TIP] < 4){
- var moveOrSpawn = false;
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
- moveOrSpawn = i % 2;
- }
- }
- if(moveOrSpawn) {
- ret = {cell:8-ret.cell};
- }
- else {
- ret = {cell:ret.cell,type: THIEF};
- }
- }
- else if(buddies[LANCE_TIP] >= 4){
- return NOP;
- }
- }
- else if(!ret.color && !ret.type && buddies[LANCE_TIP] > 0) {
- return NOP;
- }
- return ret;
- }
- };
- var TARGET_RAIL_REPAIRATION = [
- [0,0,0,8,0,0,4,2,6],
- [0,0,0,4,0,0,6,4,5],
- [0,0,0,6,0,0,8,4,5],
- [0,0,0,8,0,0,8,5,2]
- ];
- var knownRotation = -1;
- var knownNest = -1;
- var rail_repair = {
- name:function() { return "rail_repair"; },
- near_nest: function(colours) { return false; },
- near_nest_move: function(colours, buddies) { return null; },
- likely_nest: function(colours) {
- var bestScore = 0;
- // check every rotation for each 3x3 rail possibility
- TARGET_RAIL_REPAIRATION.forEach(function (arrangement) {
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- //score -= view[rot[i]].color == 7?1:0;//fake rail
- score -= i != 4 && view[rot[i]].ant != null && view[rot[i]].ant.friend ? 4 : 0;
- }
- if(score > bestScore) {
- bestScore = score;
- knownRotation = ROTATIONS.indexOf(rot);
- knownNest = TARGET_RAIL_REPAIRATION.indexOf(arrangement);
- }
- });
- });
- if(bestScore >= 4) {
- if(highway.likely_nest(colours)) return false;
- return true;
- }
- return false;
- },
- worth_leeching: function(myFood, buddies) {
- return myFood > 4 && ((myFood < 80) || (myFood % 3 == 0));
- },
- likely_victim: function(victim_pos) {
- if(view[victim_pos].color === WHITE) {
- return false;
- }
- for(var i = 0; i < 9; ++ i) {
- if(i !== victim_pos && near(i, victim_pos)) {
- return true;
- }
- }
- return false;
- },
- follow_victim: function(me, buddies, colours, victim_pos) {
- if(me.type === THIEF && !buddies[QUEEN]) {
- // Queen may be on other side of victim; try orbiting to find her
- return try_all_angles([
- (rot) => ((victim_pos === rot[1]) ? {cell: rot[0]} : null),
- (rot) => ((victim_pos === rot[0]) ? {cell: rot[3]} : null)
- ]);
- }
- if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {
- // Make a buddy to help us steal
- return best_of([
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
- try_all_cells((i) => ({cell: i, type: THIEF}), true)
- ]);
- }
- return best_of([
- // Ziggurat will try to drown us in workers; make sure we have plenty
- // of our own (but only if it's worth it)
- // Rail Miners doesn't currently spam workers, but this code doesn't hurt
- (view[victim_pos].ant.food > 4 && buddies[THIEF] < 3) &&
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
- // Continue to steal and get fat
- NOP
- ]);
- },
- find_victim: function(me, buddies, colours) {
- var current = view[CENTRE].color;
- // Easier to navigate alone, and thief reaching queen first would be a
- // disaster, so the thief just gives up.
- if(me.type === THIEF) {
- return go_anywhere;
- }
- //find first bad cell to repaint
- var painter = -1;
- var PAINT_ORDER = [0, 2, 3, 5, 6, 7, 8, 1];
- for(var o = 0; o < 8; o++) {
- var i = PAINT_ORDER[o];
- if(TARGET_ARRANGEMENT_RAIL[knownNest][i] != view[ROTATIONS[knownRotation][i]].color) {
- return {cell:ROTATIONS[knownRotation][i],color:TARGET_ARRANGEMENT_RAIL[knownNest][i]}
- }
- if(view[i].ant && !view[i].ant.friend) {
- painter = i;
- }
- }
- if(painter < 0)
- return {cell:ROTATIONS[knownRotation][1]};
- /*var cls = [2,4,5];
- if(!view[deRotate(painter,1)].ant && cls.indexOf(view[deRotate(painter,1)].color) < 0)
- return {cell:deRotate(painter,1), type: 1};
- if(!view[deRotate(painter,-1)].ant && cls.indexOf(view[deRotate(painter,-1)].color) < 0)
- return {cell:deRotate(painter,-1), type: 1};
- if(!view[deRotate(painter,-1)].ant && view[deRotate(painter,-1)].color == 4)
- return {cell:deRotate(painter,-1), type: 1};
- if(!view[deRotate(painter,1)].ant && view[deRotate(painter,1)].color == 5)
- return {cell:deRotate(painter,1), type: 1};*/
- return NOP;
- }
- };
- var TARGET_ARRANGEMENT_WIND = [
- [5,4,0,7,6,0,6,4,0],
- [7,6,0,6,4,0,5,4,0],
- [6,4,0,5,4,0,7,6,0]
- ];
- var TARGET_ARRANGEMENT_WINDCENTER = [
- [2,7,6,2,6,4,6,5,4],
- [2,6,4,6,5,4,2,7,6],
- [6,5,4,2,7,6,2,6,4]
- ];
- var WIND_BAND = colour_band([5,6,7]);
- var windmill = {
- name:function() { return "windmill"; },
- near_nest: function(colours) { return false; },
- near_nest_move: function(colours, buddies) { return null; },
- likely_nest: function(colours) { // Main nest detection
- var bestScore = 0;
- // check every rotation for each 3x3 rail possibility
- TARGET_ARRANGEMENT_WIND.forEach(function (arrangement) {
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- }
- if(score > bestScore) {
- bestScore = score;
- }
- });
- });
- if(bestScore >= 5) {
- return true;
- }
- var bestScore = 0;
- // check every rotation for each 3x3 rail possibility
- TARGET_ARRANGEMENT_WINDCENTER.forEach(function (arrangement) {
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- }
- if(score > bestScore) {
- bestScore = score;
- }
- });
- });
- if(bestScore >= 8) {
- return true;
- }
- var buddies = [0, 0, 0, 0, 0, 0];
- for(var i = 0; i < 9; ++ i) {
- ++ buddies[friend_at(i)];
- }
- return buddies[LANCE] || buddies[LANCE_TIP];
- },
- worth_leeching: function(myFood, buddies) {
- return myFood > 5 || (myFood > 1 && buddies[LANCE]);
- },
- likely_victim: function(victim_pos) {
- return false;
- },
- follow_victim: function(me, buddies, colours, victim_pos) {
- // nest is chaotic and varies by direction of approach
- // We'll let the Find Victim logic handle this
- return NOP;
- },
- find_victim: function(me, buddies, colours) {
- if(buddies[LANCE_TIP]) {
- var lancePos = -1;
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
- lancePos = i;
- }
- }
- if(buddies[LANCE]) {
- if(view[8-lancePos].ant != null && view[8-lancePos].ant.friend && view[8-lancePos].ant.type == 3) {
- return NOP;
- }
- if(view[deRotate(lancePos,3)].ant != null && view[deRotate(lancePos,3)].ant.friend && view[deRotate(lancePos,3)].ant.type == 3) {
- if(view[lancePos].color == 2 && view[deRotate(lancePos,1)].color == 5) {
- return {cell:deRotate(lancePos,1)};
- }
- return NOP;
- }
- 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) {
- if(view[deRotate(lancePos,2)].ant != null && !view[deRotate(lancePos,2)].ant.friend) {
- return {cell:8-deRotate(lancePos,2)};
- }
- return NOP;
- }
- 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) {
- return NOP;
- }
- return {cell:deRotate(lancePos,-1)};
- }
- if(view[deRotate(lancePos,-1)].ant != null && view[deRotate(lancePos,-1)].ant.type == 5) {
- return {cell:deRotate(lancePos,2)};
- }
- if(view[lancePos].color == 5 && view[deRotate(lancePos,1)].color == 7) {
- return {cell:deRotate(lancePos,1)};
- }
- return {cell:deRotate(lancePos,-1)};
- }
- else if(buddies[LANCE]) {
- var lancePos = -1;
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE) {
- lancePos = i;
- }
- }
- if(view[4].color == 3 && lancePos % 2 == 1) return NOP;
- //if(view[lancePos].ant.food > 0) return NOP;
- var moveNext = lancePos % 2 == 1 ? {cell:deRotate(lancePos,2)} : {cell:deRotate(lancePos,1)};
- if(view[moveNext.cell].ant != null && !view[moveNext.cell].ant.friend) {
- moveNext = {cell:deRotate(lancePos,1),type:LANCE_TIP};
- }
- if(view[lancePos].ant.food > 0) {
- return {cell:deRotate(lancePos,4),type:LANCE_TIP};
- }
- /*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) {
- return {cell:deRotate(lancePos,-2), type: BOUNCER};
- }*/
- if(view[lancePos].color == 6 && view[moveNext.cell].color == 8 && view[deRotate(lancePos,2)].color == 5) {
- return {cell:moveNext.cell,type:LANCE_TIP};
- }
- return moveNext;
- }
- else {
- var current = view[CENTRE].color;
- var standOn = WIND_BAND.next(WIND_BAND.next(WIND_BAND.next(current)));
- var target = WIND_BAND.next(current);
- var antitarget = WIND_BAND.next(target);
- if(current != standOn) return wilderness.find_victim(me, buddies, colours);
- var ret = best_of([
- 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),
- NOP
- ]);
- if(ret.cell == 4) {
- return wilderness.find_victim(me, buddies, colours);
- }
- return ret;
- }
- return NOP;
- }
- };
- var TARGET_ARRANGEMENT_HIGHWAY = [
- [2,3,7,6,8,2,3,7,6],
- [2,3,7,7,6,4,4,2,3],
- [2,4,6,7,3,2,4,6,7],
- [3,2,4,4,6,7,7,3,2],
- [3,4,7,7,2,6,6,3,4],
- [3,4,7,2,6,3,4,7,2],
- [3,6,2,2,7,4,4,3,6],
- [4,7,2,2,5,6,3,4,7],
- [4,6,7,2,6,3,3,4,7],
- [4,6,7,7,3,2,2,4,6],
- [6,4,2,3,7,6,4,2,3],
- [7,3,2,2,4,6,6,7,3],
- [7,4,3,6,2,7,4,3,5]
- ];
- var HIGHWAY_BAND = colour_band([2,7,4,3,6]);
- var HIGHWAY_BAND2 = colour_band([2,3,7,6,4]);
- var highway = {
- name:function() { return "highway"; }, // For debugging
- near_nest: function(colours) { return false; }, // For dodging enemy workers without getting lost
- near_nest_move: function(colours, buddies) { return null; }, // How to move when near_nest is true
- likely_nest: function(colours) { // Main nest detection
- var bestScore = 0;
- // check every rotation for each 3x3 rail possibility
- TARGET_ARRANGEMENT_HIGHWAY.forEach(function (arrangement) {
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- score += arrangement[i] == view[rot[i]].color?1:0;
- }
- if(score > bestScore) {
- bestScore = score;
- }
- });
- });
- if(bestScore >= 7) {
- return true;
- }
- if(this.isCenter(colours)) return true;
- return false;
- }, // Main nest detection
- isCenter: function(colours) {
- var bestScore = 0;
- ROTATIONS.forEach(function (rot){
- var score = 0;
- for(var i = 0; i < 9; i++) {
- 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)) {
- if(i != 4) {
- score++;
- }
- else {
- 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)) {
- score++;
- }
- }
- }
- else if(i >= 6) {
- 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) {
- score += 2;
- }
- }
- }
- if(score > bestScore) {
- bestScore = score;
- }
- });
- if(bestScore >= 7) {
- return true;
- }
- return false;
- },
- worth_leeching:function(myFood, buddies){ return myFood > 80 && myFood < 500; }, // Is this nest worth leeching?
- likely_victim: function(victim_pos) {
- return true;
- }, // Identifying the target queen
- follow_victim: function(me, buddies, colours, victim_pos) {
- if(me.type == QUEEN && buddies[THIEF] < 3) {
- return best_of([
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
- try_all_cells((i) => ({cell: i, type: THIEF}), true)
- ]);
- }
- if(me.type == THIEF && buddies[QUEEN])
- return NOP;
- return go_anywhere;
- }, // How to handle what happens when the enemy queen is found
- find_victim: function(me, buddies, colours) {
- if(me.type == THIEF && !buddies[QUEEN]) {
- for(var i=0;i<9;i++) {
- if(foe_at(i)) return NOP;
- }
- var target = HIGHWAY_BAND.prev(view[4].color);
- var followRail = best_of([
- try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
- NOP
- ]);
- }
- else {
- var target = HIGHWAY_BAND.next(view[4].color);
- var followRail = best_of([
- try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
- NOP
- ]);
- }
- return followRail;
- } // How to follow the nest
- }
- var wilderness = {
- name:function() { return "wilderness"; },
- near_nest: function(colours) { return false; },
- near_nest_move: function(colours, buddies) { return null; },
- likely_nest: function(colours) {
- return true;
- },
- worth_leeching: function(myFood, buddies) {
- return true;
- },
- likely_victim: function(victim_pos) {
- return true;
- },
- follow_victim: function(me, buddies, colours, victim_pos) {
- // We stumbled across a random queen; make the most of it
- // TODO
- // avoids blocking off the rail miner queen from her workers
- // (we'd like to leech her again)
- if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {
- // Make a buddy to help us steal
- return best_of([
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
- try_all_cells((i) => ({cell: i, type: THIEF}), true)
- ]);
- }
- else if(me.type === QUEEN){
- var enemyCount = 0;
- var allyPos = -1;
- for(var a=0; a<9; a++) {
- if(a != 4 && view[a].ant != null) {
- if(view[a].ant.friend) {
- if(near(a,victim_pos)){
- allyPos = a;
- }
- }
- else if(view[a].ant.type != 5) {
- enemyCount++;
- }
- }
- }
- if(enemyCount >= buddies[THIEF] && allyPos >= 0) {
- //if next to the queen and we're outnumbered, move back to the center of the rail.
- var target = TARGET_COLOURS_RAIL.prev(view[allyPos].color);
- var best = best_of([
- try_all_cells((i) => (near(i, victim_pos) && i % 2 == 0 ? {cell: i, type: THIEF} : null), true),
- try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true)
- ]);
- if(best != null) return best;
- best_of([
- try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null))
- ]);
- if(best != null) return best;
- return best_of([
- {cell:deRotate(allyPos,1)},
- {cell:deRotate(allyPos,-1)}
- ]);
- }
- }
- return NOP;
- },
- find_victim: function(me, buddies, colours) {
- if(me.type === QUEEN) {
- var in_void = true;
- for(var i = 0; i < 9; ++ i) {
- if(view[i].color !== WHITE && !SAFE_COLOURS.contains(view[i].color)) {
- in_void = false;
- break;
- }
- }
- if(!in_void) {
- // because of avoiding returning Miner on a Rail workers
- // we dodge sideways and this takes us back onto track
- var nearMove = checkAllNearEnvirons(colours, buddies)
- if(nearMove) return nearMove;
- if(rail_miners.likely_nest(colours)) {
- var r = try_all_angles.bind(null, [
- (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[8], type: BOUNCER} : null)
- ]);
- var m = r();
- if(sanity_check(m)) {
- return m;
- }
- var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true);
- if(m) {
- return m;
- }
- }
- }
- return best_of([
- // Make a buddy once we have a reasonable stash of food so we can
- // search the board faster
- // (but avoid making buddies when there's a potential nest nearby;
- // better to wait until we find their queen)
- (!buddies[THIEF] && me.food >= INITIAL_GATHER && in_void) &&
- try_all_cells((i) => ({cell: i, type: THIEF}), true),
- // Follow buddy in search of victims
- buddies[THIEF] && try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
- (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
- ]),
- buddies[THIEF] && NOP, // Don't lose our buddy!
- // Random walk until we can make a buddy or find the victim
- grab_nearby_food,
- foe_near() ? go_anywhere : fast_diagonal.bind(null, SAFE_COLOURS),
- go_anywhere
- ]);
- } else if(me.type === THIEF) {
- return best_of([
- // Lost the queen! Random walk because we have nothing better to do.
- // (don't leave lines; they could disrupt the pattern)
- !buddies[QUEEN] && go_anywhere,
- buddies[BOUNCER] && go_anywhere,
- // Follow queen in search of victims
- try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
- (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
- ]),
- NOP // Don't lose our buddy!
- ]);
- }
- }
- };
- var victims = [highway, rail_miners, rail_repair, windmill];
- function guess_environment(colours, buddies) {
- var food = view[4].ant.food;
- if(view[4].ant.type !== QUEEN) {
- for(var i = 0; i < 9; i++) {
- if(i != 4 && view[i].ant && view[i].ant.friend && view[i].ant.type === QUEEN) {
- food = view[i].ant.food;
- }
- }
- }
- for(var i = 0; i < victims.length; ++ i) {
- if(victims[i].likely_nest(colours) && victims[i].worth_leeching(food, buddies)) {
- return victims[i];
- }
- }
- return wilderness;
- }
- function is_safe(i) {
- var nearThief = false;
- var nearOfficer = false;
- for(var j = 0; j < 9; ++ j) {
- if(friend_at(j) === THIEF) {
- nearThief = true;
- }
- if(foe_at(j) && foe_at(j) !== QUEEN) {
- nearOfficer = true;
- }
- }
- return nearThief && !nearOfficer;
- }
- function move(me, buddies) {
- var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
- for(var i = 0; i < 9; ++ i) {
- ++ colours[view[i].color];
- }
- var env = guess_environment(colours,buddies);
- var victim_pos = -1;
- for(var i = 0; i < 9; ++ i) {
- if(foe_at(i) === QUEEN && env.likely_victim(i) && view[i].ant.food > 0) {
- victim_pos = i;
- if(view[i].ant.food > 0) {
- break;
- }
- }
- }
- var in_void = true;
- for(var i = 0; i < 9; ++ i) {
- if(view[i].color !== WHITE || (i != 4 && me.type === BOUNCER && friend_at(i) === BOUNCER)) {
- in_void = false;
- break;
- }
- }
- if(me.type === BOUNCER) {
- if(env === wilderness && in_void) {
- // Our work is done; leave queen and wander at random
- if(buddies[QUEEN]) {
- return best_of([
- try_all_cells((i) => (ant_type_near(i, true) ? null : {cell: i}), true),
- go_anywhere
- ]);
- }
- return NOP;
- }
- else if(env === rail_miners) {
- // Our work is done; leave queen and wander at random
- if(buddies[QUEEN]) {
- var allAngles = try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
- (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
- NOP
- ]);
- return best_of([
- //if next to an enemy queen, move out of the way
- try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
- try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
- allAngles
- ]);
- }
- return NOP;
- } else if(buddies[QUEEN]) {
- // Escort queen out of nest
- var allAngles = try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
- (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
- go_anywhere
- ]);
- return best_of([
- //if next to an enemy queen, move out of the way
- try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
- try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
- allAngles
- ]);
- }
- else {
- return go_anywhere;
- }
- } else if(buddies[BOUNCER]) {
- if(me.type === QUEEN) {
- // Be escorted out of nest
- return try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[2]} : null),
- (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[1]} : null),
- go_anywhere,
- NOP
- ]);
- } else {
- // Get out of the way
- return try_all_angles.bind(null, [
- (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[7]} : null),
- (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[8]} : null),
- (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[7]} : null),
- (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[8]} : null),
- go_anywhere
- ]);
- }
- }
- if(victim_pos !== -1) {
- // abandon the queen if she's dry.
- // abandon rail miner's queen so she has at least 10 food (otherwise she produces workers 3:4 food she aquires)
- // 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
- // this can still leave the rail miner's queen lower than 10, but unlikely
- // 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.
- if(view[victim_pos].ant.food < 5 || ((env == rail_miners || env == rail_repair) && view[victim_pos].ant.food < 28)) {
- if(me.type == THIEF) {
- if(rail_miners.near_nest(colours)) {
- // we'd rather reuse the workers
- return NOP;
- }
- }
- // Victim is out of food; bounce out of nest
- if(env == rail_miners || env == rail_repair) {
- // murder SlM
- return NOP;
- }
- var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true);
- if(m) {
- return m;
- }
- }
- if(me.type === QUEEN && buddies[THIEF] && !is_safe(CENTRE)) {
- // Try to avoid getting food stolen back from us
- var m = try_all_cells((i) => (is_safe(i) ? {cell: i} : null), true);
- if(m) {
- return m;
- }
- }
- return env.follow_victim(me, buddies, colours, victim_pos);
- } else {
- if(me.type == THIEF && buddies[QUEEN] == 0) {
- if(rail_miners.near_nest(colours)) {
- // we'd rather reuse the workers
- // with SlM they won't be next to the queen, but they may have food
- // and if our queen comes by, she'll pick it up.
- for(var i=0;i<9;i++) {
- if(view[i].ant && !view[i].ant.friend && view[i].ant.food == 0) {
- return rail_miners.near_nest_move(colours, buddies);
- }
- }
- return NOP;
- }
- }
- return env.find_victim(me, buddies, colours);
- }
- }
- // LANCE is only used by windmill targetting, easier to break this out as its own method
- function moveLance(me, buddies) {
- var queenPos = -1;
- var tipPos = -1;
- if(buddies[BOUNCER]) {
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == BOUNCER) {
- return {cell:8-i};
- }
- }
- }
- if(!buddies[QUEEN]) {
- for(var i=0;i<9;i++) {
- if(i % 2 == 0 && view[i].ant && !view[i].ant.friend && view[i].ant.type == QUEEN) {
- if(view[deRotate(i,3)].ant != null && view[deRotate(i,3)].ant.friend && view[deRotate(i,3)].ant.type == LANCE_TIP) return NOP;
- return {cell:deRotate(i,1)};
- }
- }
- return NOP;
- }
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == QUEEN) {
- queenPos = i;
- }
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE_TIP) {
- tipPos = i;
- }
- }
- if(buddies[LANCE_TIP]) {
- if(deRotate(queenPos,-1) == tipPos && view[tipPos].color == 8) return {cell:8-tipPos};
- if(deRotate(queenPos,-1) == tipPos) return try_all_cells((i) => (areAdjacent(i,tipPos) && view[i].color == 5 ? {cell:i} : null));
- if(view[8-tipPos].ant != null && !view[8-tipPos].ant.friend && view[8-tipPos].ant.type == 5) return {cell:8-tipPos,color:6};
- return try_all_cells((i) => (!areAdjacent(i,queenPos) && !areAdjacent(i,tipPos) ? {cell:i} : null));
- }
- if(view[4].color != 4 && view[4].color != 6) {
- if(view[8-queenPos].ant != null && !view[8-queenPos].ant.friend && view[8-queenPos].ant.type == 5) return NOP;
- return best_of([
- 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),
- NOP
- ]);
- }
- else {
- var queenOn = view[8-queenPos].color;
- var target = WIND_BAND.next(queenOn);
- var prior = WIND_BAND.next(target);
- var followRail = best_of([
- 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),
- 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)
- ]);
- if(view[deRotate(queenPos,-1)].ant != null) {
- if(!view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant != null && !view[deRotate(queenPos,-2)].ant.friend) {
- return {cell:deRotate(queenPos,1)};
- }
- return NOP;
- }
- if(me.food > 0 && queenPos % 2 == 0) {
- return {cell:deRotate(queenPos,-1)};
- }
- 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)) {
- return {cell:queenPos,color:3};
- }
- 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)) {
- return {cell:queenPos,color:3};
- }
- return followRail;
- }
- return NOP;
- }
- // LANCE_TIP never needs to move
- // Unfortunately, reusing an existing worker type for this purpose is not easily possible.
- // Used against Sliding Miners as a stationary blocker to prevent the queen slipping past.
- function moveTip(me, buddies) {
- var queenPos = -1;
- var in_void = true;
- for(var i=0;i<9;i++) {
- if(view[i].ant && view[i].ant.friend && view[i].ant.type == QUEEN) {
- queenPos = i;
- }
- if(view[i].color != WHITE) {
- in_void = false;
- }
- }
- var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0];
- for(var i = 0; i < 9; ++ i) {
- ++ colours[view[i].color];
- }
- var onRails = rail_miners.near_nest(colours)
- if(buddies[QUEEN] && !buddies[LANCE]) {
- if(onRails) return NOP;
- if(view[8-queenPos].ant != null && !view[8-queenPos].ant.friend && view[8-queenPos].ant.type == 4) {
- return {cell:deRotate(queenPos,2)};
- }
- if(in_void) return {cell:deRotate(queenPos,4)};
- }
- if(buddies[QUEEN] && buddies[LANCE]) {
- return {cell:deRotate(queenPos,2)};
- }
- if(buddies[QUEEN] && buddies[BOUNCER]) {
- // rail miners bounce out
- return go_anywhere;
- }
- return NOP;
- }
- function deRotate(m, amt) {
- var rotationsCW = [1,2,5,8,7,6,3,0];
- var rotationsCCW = [3,6,7,8,5,2,1,0];
- if(m == 4 || m < 0 || m > 8 || amt == 0) return m;
- if(amt > 0)
- return rotationsCW[(rotationsCW.indexOf(m)+amt)%8];
- amt = -amt;
- return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8];
- }
- return play_safe(move_agent([
- THIEF, move,
- QUEEN, move,
- BOUNCER, move,
- LANCE, moveLance,
- LANCE_TIP, moveTip
- ]));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement