Skip to content

Instantly share code, notes, and snippets.

@mpolyak
Last active July 25, 2016 21:44

Revisions

  1. mpolyak renamed this gist Jun 13, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. mpolyak created this gist Jun 13, 2014.
    355 changes: 355 additions & 0 deletions CodeCombat_HighSea.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,355 @@
    /*
    Copyright (c) 2014 Michael Polyak. All Rights Reserved.
    [email protected]
    HighSea v2.7.6
    */

    if (this.queue === undefined)
    {
    this.queue = [];

    this.friendMinion = this.buildables.peasant ? "peasant" : "peon";
    this.friendMelee = this.buildables.soldier ? "soldier" : "munchkin";
    this.friendRange = this.buildables.librarian ? "librarian" : "shaman";
    this.enemyMinion = this.buildables.peasant ? "peon" : "peasant";

    this.units =
    {
    "soldier": {melee: true, power: 45.0},
    "munchkin": {melee: true, power: 45.0},
    "knight": {melee: true, power: 51.2},
    "ogre": {melee: true, power: 51.2},
    "librarian": {range: true, power: 75.0},
    "shaman": {range: true, power: 75.0},
    "griffin-rider":{range: true, power: 150.0},
    "fangrider": {range: true, power: 150.0},
    "captain": {melee: true, power: 32.4},
    "brawler": {melee: true, power: 32.4}
    };
    }

    var FRIEND_MINION = this.friendMinion;
    var FRIEND_MELEE = this.friendMelee;
    var FRIEND_RANGE = this.friendRange;
    var ENEMY_MINION = this.enemyMinion;
    var BASE = "base";
    var UNITS = this.units;
    var WIDTH = 85;
    var HEIGHT = 70;
    var MAX_MINIONS = 6;
    var GRAB_DISTANCE = 20;
    var MAX_HEADINGS = 8;
    var MIN_HEADINGS = 4;
    var DEFEND_DISTANCE = 50;
    var ATTACK_DISTANCE = 30;
    var ATTACK_TIME = 120;
    var MELEE_RANGE_RATIO = 3 / 2;

    var base = this;

    this.lootGrid = function (index, sectors, width, height)
    {
    var rows = Math.floor(Math.sqrt(sectors)); var cols = Math.floor(sectors / rows);

    var hsize = width / cols;
    var vsize = height / rows;

    if (index >= rows * cols)
    {
    return {left: (width / 2) - hsize, right: (width / 2) + hsize,
    top: (height / 2) + vsize, bottom: (height / 2) - vsize};
    }

    var row = Math.floor(index / cols); var col = index % cols;

    return {left: hsize * col, right: hsize * (col + 1),
    top: vsize * (row + 1), bottom: vsize * row};
    };

    this.lootItem = function (minion, grid, items, taken, headings)
    {
    var compass = [];

    var i;

    for (i = 0; i < headings; i ++)
    compass.push(null);

    var point = -1; var wealth = 0;

    for (i = 0; i < items.length; i ++)
    {
    var item = items[i];

    if (taken && taken.indexOf(item.id) !== -1)
    continue;

    var direction = Vector.subtract(item.pos, minion.pos); var distance = direction.magnitude();

    if (grid && distance > GRAB_DISTANCE)
    {
    if (item.pos.x < grid.left || item.pos.x > grid.right || item.pos.y < grid.bottom || item.pos.y > grid.top)
    continue;
    }

    var heading = Math.round(headings * Vector.normalize(direction).heading() / (2 * Math.PI) + headings) % headings;

    if (!compass[heading])
    compass[heading] = {wealth: 0, worth: 0, distance: 0, item: null};

    var worth = item.bountyGold / distance;

    if (compass[heading].worth < worth) {
    compass[heading].worth = worth; compass[heading].distance = distance; compass[heading].item = item;
    }

    compass[heading].wealth += item.bountyGold / (distance * distance);

    if (compass[heading].wealth > wealth) {
    point = heading; wealth = compass[heading].wealth;
    }
    }

    return point !== -1 ? {distance: compass[point].distance, item: compass[point].item} : null;
    };

    this.lootGold = function (items, minions, enemies)
    {
    if (!minions.length)
    return;

    var headings = Math.max(MIN_HEADINGS, Math.min(MAX_HEADINGS,
    Math.floor((items.length / (minions.length + enemies.length)) / 2) * 2));

    var grid;
    var loot;

    var i;

    var looted = []; var taken = [];

    for (i = 0; i < minions.length; i ++)
    {
    grid = base.lootGrid(i, minions.length, WIDTH, HEIGHT);

    loot = base.lootItem(minions[i], grid, items, null, headings);

    if (loot)
    taken.push(loot.item.id);

    looted.push({grid: grid, loot: loot});
    }

    if (looted.length > 1)
    {
    var index;

    var reloot = [];

    for (i = 0; i < looted.length - 1; i ++)
    {
    if (!looted[i].loot)
    continue;

    for (var j = i + 1; j < looted.length; j ++)
    {
    if (!looted[j].loot || looted[j].loot.item.id !== looted[i].loot.item.id)
    continue;

    index = looted[j].loot.distance < looted[i].loot.distance ? i : j;

    if (reloot.indexOf(index) === -1)
    reloot.push(index);

    if (index === i)
    break;
    }
    }

    for (i = 0; i < reloot.length; i ++)
    {
    index = reloot[i];

    looted[index].loot = base.lootItem(minions[index], looted[index].grid, items, taken, headings);
    }
    }

    for (i = 0; i < looted.length; i ++)
    {
    var minion = minions[i];

    grid = looted[i].grid;
    loot = looted[i].loot;

    if (loot) {
    base.command(minion, "move", loot.item.pos);
    }
    else if (grid)
    base.command(minion, "move", {x: (grid.left + grid.right) / 2, y: (grid.top + grid.bottom) / 2});
    }
    };

    this.balanceOfPower = function (friends, enemies)
    {
    var target = null;

    var melee = 0;
    var range = 0;

    var friendsDistance = -1;
    var enemiesDistance = -1;

    var distance;

    var type;
    var unit;

    var i;

    for (i = 0; i < enemies.length; i ++)
    {
    type = enemies[i].type;

    if (type === BASE) {
    target = enemies[i];
    }
    else if (type !== ENEMY_MINION)
    {
    unit = UNITS[type];

    if (unit.melee) {
    melee -= unit.power;
    }
    else
    range -= unit.power;

    distance = base.distance(enemies[i].pos);

    if (enemiesDistance === -1 || distance < enemiesDistance)
    enemiesDistance = distance;
    }
    }

    for (i = 0; i < friends.length; i ++)
    {
    type = friends[i].type;

    if (type !== BASE && type !== FRIEND_MINION)
    {
    unit = UNITS[type];

    if (unit.melee) {
    melee += unit.power;
    }
    else
    range += unit.power;

    if (target)
    {
    distance = friends[i].distance(target.pos);

    if (friendsDistance === -1 || distance < friendsDistance)
    friendsDistance = distance;
    }
    }
    }

    return {target: target, melee: melee, range: range, friendsDistance: friendsDistance, enemiesDistance: enemiesDistance};
    };

    this.timeToAttack = function (bop)
    {
    if (base.now() > ATTACK_TIME)
    {
    if (bop.enemiesDistance === -1 && bop.target && bop.target.gold < base.gold)
    return true;
    }
    else
    {
    if (bop.enemiesDistance === -1 && bop.target && bop.target.gold < base.gold / 2 &&
    base.gold > ((base.buildables[FRIEND_MELEE].goldCost * MELEE_RANGE_RATIO) + base.buildables[FRIEND_RANGE].goldCost))
    {
    return true;
    }
    }

    return false;
    };

    this.buildUnit = function (type, count)
    {
    for (var i = 0; i < count; i ++)
    base.queue.push(type);
    };

    this.buildBattleArmy = function (bop)
    {
    if (!bop.target)
    return false;

    var melee = bop.melee < 0 ? Math.ceil(Math.abs(bop.melee) / UNITS[FRIEND_MELEE].power) : 0;
    var range = bop.range < 0 ? Math.ceil(Math.abs(bop.range) / UNITS[FRIEND_RANGE].power) : 0;

    var meleeCost = melee * base.buildables[FRIEND_MELEE].goldCost;
    var rangeCost = range * base.buildables[FRIEND_RANGE].goldCost;

    if (meleeCost + rangeCost > base.gold || meleeCost + rangeCost > bop.target.gold)
    return false;

    if (melee)
    base.buildUnit(FRIEND_MELEE, melee);

    if (range)
    base.buildUnit(FRIEND_RANGE, range);

    return true;
    };

    this.buildAttackArmy = function ()
    {
    var units = Math.floor(base.gold / ((base.buildables[FRIEND_MELEE].goldCost * MELEE_RANGE_RATIO) + base.buildables[FRIEND_RANGE].goldCost));

    if (units)
    {
    base.buildUnit(FRIEND_MELEE, units * MELEE_RANGE_RATIO);
    base.buildUnit(FRIEND_RANGE, units);
    }
    };

    this.buildDefenceArmy = function ()
    {
    base.buildUnit(FRIEND_MELEE, base.gold / base.buildables[FRIEND_MELEE].goldCost);
    };

    var friendsMinions = this.getByType(FRIEND_MINION);
    var enemiesMinions = this.getByType(ENEMY_MINION);

    this.lootGold(this.getItems(), friendsMinions, enemiesMinions);

    if (!this.queue.length)
    {
    var bop = this.balanceOfPower(this.getFriends(), this.getEnemies());

    if ((bop.friendsDistance !== -1 && bop.friendsDistance < ATTACK_DISTANCE) ||
    (bop.enemiesDistance !== -1 && bop.enemiesDistance < DEFEND_DISTANCE) || this.timeToAttack(bop))
    {
    if (bop.enemiesDistance !== -1 && bop.enemiesDistance < ATTACK_DISTANCE) {
    this.buildDefenceArmy();
    }
    else
    {
    if (bop.enemiesDistance === -1 || !this.buildBattleArmy(bop))
    this.buildAttackArmy();
    }
    }
    else if (bop.melee >= 0 && bop.range >= 0 && friendsMinions.length < enemiesMinions.length + 1 && friendsMinions.length < MAX_MINIONS)
    {
    if (this.gold >= this.buildables[FRIEND_MINION].goldCost)
    this.queue.push(FRIEND_MINION);
    }
    }

    if (this.queue.length && this.gold >= this.buildables[this.queue[0]].goldCost)
    this.build(this.queue.shift());