Skip to content

Instantly share code, notes, and snippets.

@FanaHOVA
Created February 21, 2022 00:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FanaHOVA/a051a4674cdd9f3465637846760f31c2 to your computer and use it in GitHub Desktop.
Save FanaHOVA/a051a4674cdd9f3465637846760f31c2 to your computer and use it in GitHub Desktop.
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