Last active
July 25, 2016 21:44
-
-
Save mpolyak/8af627cbdf596b5e294a to your computer and use it in GitHub Desktop.
3rd place in the CodeCombat Greed tournament on the Human side http://blog.codecombat.com/a-31-trillion-390-billion-statement-programming-war-between-545-wizards
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment