Created
February 21, 2022 00:00
-
-
Save FanaHOVA/a051a4674cdd9f3465637846760f31c2 to your computer and use it in GitHub Desktop.
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
const { | |
html, | |
render, | |
useState, | |
useEffect, | |
useLayoutEffect | |
} = await import('https://unpkg.com/htm/preact/standalone.module.js'); | |
const { | |
eachLimit | |
} = await import('https://cdn.skypack.dev/async-es'); | |
const { | |
hasArtifact, | |
} = await import('https://plugins.zkga.me/utils/utils.js'); | |
let { | |
move, | |
} = await import('https://plugins.zkga.me/utils/queued-move.js'); | |
const whitelist = [ | |
"0x3755f7bc5f88894db84534acba8ac333442cb94d", // Agent K | |
"0x5f3d286f20cb174570957868a96f4fe252ae6b02", // bitcoin louis | |
"0x82d94dc968e69706728ba2b5996b1eb048e825d4", // Some unlucky noob that spawned near me | |
"0x67d715ba08d84a030105150bbee57d6cffb147cc", // lightsout | |
]; | |
// Planets that should be taken in revenge. | |
const revengePlanets = [ | |
// Agent K sent attacks from these planets. | |
"000017700009dba4d45193ee0bba6e9b062c55b31a33835614ae9b68fded8019", | |
"000039900021d769455bde90b7b326b367d66fa6696f352d212f8b9e18c58ca9", | |
] | |
function beep() { | |
var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU="); | |
snd.play(); | |
} | |
function distanceSort(a, b) { | |
return a[1] - b[1]; | |
} | |
function attackEfficiencySort(a, b) { | |
return a.forces - b.forces; | |
} | |
// Highest level first. | |
function levelSort(a, b) { | |
return b.planetLevel - a.planetLevel; | |
} | |
function lowestEnergySort(a, b) { | |
return a.energy - b.energy; | |
} | |
function highestEnergySort(a, b) { | |
return b.energy - a.energy; | |
} | |
function highestEnergyCapSort(a, b) { | |
return b.energyCap - a.energyCap; | |
} | |
function distance(fromLoc, toLoc) { | |
return Math.sqrt( | |
(fromLoc.coords.x - toLoc.coords.x) ** 2 + | |
(fromLoc.coords.y - toLoc.coords.y) ** 2 | |
); | |
} | |
function planetPower(planet) { | |
return (planet.energy * planet.defense) / 100; | |
} | |
function planetPercentEnergy(planet, percentCap = 25) { | |
const unconfirmedDepartures = planet.unconfirmedDepartures.reduce( | |
(acc, dep) => { | |
return acc + dep.forces; | |
}, | |
0 | |
); | |
const FUZZY_ENERGY = Math.floor(planet.energy - unconfirmedDepartures); | |
return (FUZZY_ENERGY * percentCap) / 100; | |
} | |
function getEnergyArrival(fromId, toId, forces) { | |
const sync = df.getPlanetWithId(toId); | |
return ( | |
df.getEnergyArrivingForMove(fromId, toId, forces) / (sync.defense / 100) | |
); | |
} | |
const concurrentAttackLimit = 6; | |
function isAttackingAtCapacity(attacking, target) { | |
return attacking[target.locationId] !== undefined && (attacking[target.locationId].count >= concurrentAttackLimit || attacking[target.locationId].energy > planetPower(target)); | |
} | |
let VerticalSpacing = { | |
marginBottom: "12px", | |
}; | |
function App() { | |
return html` | |
<h1 style=${VerticalSpacing}> | |
Want to conquer everything? | |
<button | |
style=${{ float: "right" }} | |
onClick=${() => { | |
conquer(); | |
}} | |
> | |
Yes - Conquer! | |
</button> | |
</h1> | |
`; | |
} | |
function conquer(wait) { | |
if (wait) { | |
if (df.getUnconfirmedMoves().length > 0) { | |
console.log("There are unconfirmed moves. Not doing anything.") | |
return | |
} | |
} | |
const planetsUnderAttack = df.getAllVoyages().filter(v => v.player != df.account).map(v => df.getPlanetWithId(v.toPlanet)).filter(p => p.owner == df.account).map(p => p.locationId); | |
let planets = df.getMyPlanets() | |
.filter((p) => p.planetLevel > 1 && p.silverGrowth == 0 && (p.energy-p.unconfirmedDepartures.reduce((acc, curr) => acc += curr.forces, 0)) / p.energyCap > 0.75 && !planetsUnderAttack.includes(p.locationId)) | |
.sort(highestEnergySort); | |
let attacking = df.getAllVoyages() | |
.filter((v) => v.player == df.account) | |
.reduce((acc, v) => { | |
if (!acc[v.toPlanet]) { | |
acc[v.toPlanet] = {count:0, energy:0}; | |
} | |
acc[v.toPlanet].count++; | |
acc[v.toPlanet].energy += v.energyArriving; | |
return acc; | |
}, {}); | |
// let moves = []; | |
let moves = df.getUnconfirmedMoves().length; | |
planets.forEach(planet => { | |
const fromId = planet.locationId; | |
const candidates = df | |
.getPlanetsInRange(fromId, 50) | |
.filter((p) => p.owner !== df.account && (p.planetLevel > 2 || hasArtifact(p))) // Ignore level 1 planets, waste of time, except to grief/protect from other players, also get artifacts. | |
.sort(highestEnergyCapSort).map(p => [p]); // map to be the same as the distance thing. | |
// .map((to) => { | |
// const fromLoc = df.getLocationOfPlanet(fromId); | |
// const toLoc = df.getLocationOfPlanet(to.locationId); | |
// return [to, distance(fromLoc, toLoc)]; | |
// }).sort(distanceSort); | |
let attacked = false; | |
for(const candidate of candidates) { | |
if (moves >= 100) { // Limit unconfirmed moves to 100 to prevent OOM. | |
return; | |
} | |
const target = candidate[0]; | |
if (isAttackingAtCapacity(attacking, target) || (target.owner === "0x0000000000000000000000000000000000000000" && target.planetLevel < 2) || whitelist.includes(target.owner)) { | |
continue; | |
} | |
const minEnergy = df.getEnergyNeededForMove( | |
fromId, | |
target.locationId, | |
1 | |
); | |
let forces = Math.floor( | |
df.getEnergyNeededForMove( | |
fromId, | |
target.locationId, | |
planetPower(target) + target.energyCap*0.1 // Take planet and give it 10% energy. | |
)); | |
// If this planet can attack, even a little bit, then do it. | |
if (forces >= planet.energy) { | |
if (minEnergy < planet.energyCap*0.85 && planet.energy >= planet.energyCap*0.9 && target.owner === "0x0000000000000000000000000000000000000000" && target.planetLevel > 4) { | |
forces = Math.floor(planet.energyCap*0.85) | |
} else { | |
continue; | |
} | |
} | |
if (attacking[target.locationId] === undefined) { | |
attacking[target.locationId] = {count:0, energy:0}; | |
} | |
attacking[target.locationId].count++; | |
attacking[target.locationId].energy += getEnergyArrival(fromId, target.locationId, forces); | |
try { | |
move(fromId, target.locationId, forces, 0); | |
moves++; | |
} catch (e) { | |
console.error(e); | |
break; | |
} | |
attacked = true; | |
break; | |
} | |
// If there is nothing to attack and we are at max cap, give the energy to another planet. | |
if (!attacked && planet.energy >= planet.energyCap*0.99 && planet.unconfirmedDepartures.length === 0) { // Disabled for now. | |
const cs = df.getPlanetsInRange(fromId, 50) | |
.filter((p) => p.owner === df.account && p.energy < p.energyCap-10000 && p.planetLevel > planet.planetLevel) | |
.map((to) => { | |
const fromLoc = df.getLocationOfPlanet(fromId); | |
const toLoc = df.getLocationOfPlanet(to.locationId); | |
return [to, distance(fromLoc, toLoc)]; | |
}).sort(distanceSort) | |
.map((p) => p[0]); | |
// Fill up other planets with at least 1k, to reduce these small moves. | |
for (const target of cs) { | |
let forces = Math.floor( | |
df.getEnergyNeededForMove( | |
fromId, | |
target.locationId, | |
1000, | |
)); | |
if (forces > planet.energy*0.95) { | |
continue; | |
} | |
forces = Math.floor(planet.energy*0.95); | |
if (forces < 1000) { | |
continue; | |
} | |
try { | |
move(fromId, target.locationId, forces, 0); | |
moves++; | |
} catch (e) { | |
console.error(e); | |
} | |
break; | |
} | |
} | |
}); | |
// moves.sort(attackEfficiencySort) | |
} | |
class AutoConquer { | |
constructor() { | |
this.root = null; | |
this.container = null; | |
} | |
/** | |
* Called when plugin is launched with the "run" button. | |
*/ | |
async render(container) { | |
this.container = container; | |
container.style.width = "450px"; | |
this.root = render(html`<${App} />`, container); | |
} | |
} | |
/** | |
* And don't forget to register it! | |
*/ | |
plugin.register(new AutoConquer()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment