-
-
Save nazariyv/93050513c9fe2ddec39b069d112ed6fa 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()); |
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
/** | |
* Silver rebalancer | |
* @prestonvanloon | |
* | |
* Work in progress... | |
*/ | |
const { | |
html, | |
render, | |
} = await import('https://unpkg.com/htm/preact/standalone.module.js'); | |
let { | |
move, | |
} = await import('https://plugins.zkga.me/utils/queued-move.js'); | |
function distanceSort(a, b) { | |
return a[1] - b[1]; | |
} | |
// Decending by how much silver is needed. | |
function silverCapSort(a, b) { | |
return (b.silverCap-b.silver)-(a.silverCap-a.silver); | |
} | |
//Descending by how much silver the planet has. | |
function silverSort(a, b) { | |
return b.silver - a.silver; | |
} | |
// Decending by points (highest first). | |
function pointsSort(a, b) { | |
return (b.energyCap*b.planetLevel)-(a.energyCap*b.planetLevel); | |
} | |
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; | |
} | |
let VerticalSpacing = { | |
marginBottom: "12px", | |
}; | |
function App() { | |
return html` | |
<h1 style=${VerticalSpacing}> | |
Redistribute full(ish) silver mines? | |
<button | |
style=${{ float: "right" }} | |
onClick=${() => { | |
rebalance(); | |
}} | |
> | |
Yes - Distribute! | |
</button> | |
</h1> | |
`; | |
} | |
function rebalance(wait) { | |
if (wait) { | |
if (df.getUnconfirmedMoves().length > 0) { | |
console.log("There are unconfirmed moves. Not doing anything.") | |
return | |
} | |
} | |
let planets = df.getMyPlanets() | |
.filter((p) => p.silverGrowth > 0 && p.silver >= 1000 && p.silver >= p.silverCap*0.8) | |
.sort(silverSort); | |
let attacking = df.getAllVoyages() | |
.filter((v) => v.player == df.account && v.silverMoved > 0) | |
.reduce((acc, curr) => { | |
const ID = curr.toPlanet; | |
if (!acc[ID]) { | |
acc[ID] = 0; | |
} | |
acc[ID] += curr.silverMoved; | |
return acc; | |
}, {}); | |
planets.forEach(planet => { | |
const fromId = planet.locationId; | |
const candidates = df | |
.getPlanetsInRange(fromId, 50) | |
.filter((p) => p.owner == df.account && p.planetLevel > 2 && p.silverCap > 0 && p.silver != p.silverCap && p.silverGrowth === 0) | |
.sort(silverCapSort); | |
for(const candidate of candidates) { | |
const target = candidate; | |
const FORCES = Math.floor( | |
df.getEnergyNeededForMove( | |
fromId, | |
target.locationId, | |
1, | |
)); | |
if (FORCES >= planet.energy) { | |
continue; | |
} | |
let inFlight = attacking[target.locationId]; | |
if (inFlight > 0) { //temporary disable this inflight stuff... It keeps sending too much silver. | |
continue; | |
} else { | |
inFlight = 0; | |
} | |
const silver = Math.floor(Math.min(planet.silver, target.silverCap-(target.silver+inFlight))); | |
if (silver < 100) { | |
continue; | |
} | |
move(fromId, target.locationId, FORCES, silver); | |
if (!attacking[target.locationId]) { | |
attacking[target.locationId] = 0; | |
} | |
attacking[target.locationId] += silver; | |
break; | |
} | |
}); | |
} | |
class AutoSilver { | |
/** | |
* 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 AutoSilver()); |
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, | |
useLayoutEffect, | |
} = await import('https://unpkg.com/htm/preact/standalone.module.js'); | |
const { | |
Defense, | |
Range, | |
Speed, | |
} = await import('https://plugins.zkga.me/game/Icons.js'); | |
const { | |
UpgradeBranchName, | |
SpaceType, | |
canStatUpgrade, | |
canPlanetUpgrade, | |
} = await import('https://plugins.zkga.me/utils/utils.js') | |
function doUpgrade(planet, branch) { | |
if (planet && canPlanetUpgrade(planet) && canStatUpgrade(planet, branch)) { | |
df.upgrade(planet.locationId, branch) | |
} | |
} | |
function upgrade() { | |
let planets = df.getMyPlanets().filter(canPlanetUpgrade).filter(p => p.planetLevel > 3); | |
for (const planet of planets) { | |
if (planet.unconfirmedUpgrades.length > 0) { | |
continue; | |
} | |
if (canStatUpgrade(planet, UpgradeBranchName.Defense)) { | |
doUpgrade(planet, UpgradeBranchName.Defense); | |
} else if (canStatUpgrade(planet, UpgradeBranchName.Range)) { | |
doUpgrade(planet, UpgradeBranchName.Range); | |
} else if (canStatUpgrade(planet, UpgradeBranchName.Speed)) { | |
doUpgrade(planet, UpgradeBranchName.Speed); | |
} | |
} | |
} | |
let VerticalSpacing = { | |
marginBottom: "12px", | |
}; | |
function App() { | |
return html` | |
<h1 style=${VerticalSpacing}> | |
Upgrade planets? | |
<button | |
style=${{ float: "right" }} | |
onClick=${() => { | |
upgrade(); | |
}} | |
> | |
Yes - Upgrade! | |
</button> | |
</h1> | |
`; | |
} | |
class AutoUpgrade { | |
/** | |
* 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 AutoUpgrade()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment