implement heat sink validation
This commit is contained in:
parent
2b542e5f34
commit
fdd4fde699
|
|
@ -146,7 +146,7 @@
|
||||||
Roadmap:
|
Roadmap:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Reactor verification and stats</li>
|
<li>Reactor stats</li>
|
||||||
<li>Casing improvements</li>
|
<li>Casing improvements</li>
|
||||||
<li>Keyboard shortcuts</li>
|
<li>Keyboard shortcuts</li>
|
||||||
<li>MSR support</li>
|
<li>MSR support</li>
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ registerLoadCallback(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
awaitEvent("config:end").then(() => {
|
awaitEvent("config:end").then(() => {
|
||||||
void loadExampleFile("neutron test");
|
void loadExampleFile("Pandora's Fission Reactor");
|
||||||
});
|
});
|
||||||
|
|
||||||
queueMicrotask(() => emitEvent("dev-mode:end"));
|
queueMicrotask(() => emitEvent("dev-mode:end"));
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ export async function saveFile(name = undefined) {
|
||||||
if (!fullBlock || !fullBlock.modules || !("ncpf:block_recipes" in fullBlock.modules)) return;
|
if (!fullBlock || !fullBlock.modules || !("ncpf:block_recipes" in fullBlock.modules)) return;
|
||||||
|
|
||||||
const designIndex = designBlocksAtlasMap[Number(designAsCellData[x][y][z].tile)].designIndex;
|
const designIndex = designBlocksAtlasMap[Number(designAsCellData[x][y][z].tile)].designIndex;
|
||||||
const solverMetadata = getMetadata(designAsCellData[x][y][z].solverMetadata);
|
const solverMetadata = getMetadata(designAsCellData[x][y][z].metadata);
|
||||||
const minimizedBlock = minimizedBlocks[designIndex];
|
const minimizedBlock = minimizedBlocks[designIndex];
|
||||||
let recipeIndex = -1;
|
let recipeIndex = -1;
|
||||||
|
|
||||||
|
|
@ -156,6 +156,8 @@ export async function saveFile(name = undefined) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(recipeIndex)
|
||||||
|
|
||||||
return recipeIndex;
|
return recipeIndex;
|
||||||
})
|
})
|
||||||
.filter(b => b != null)
|
.filter(b => b != null)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,19 @@ export function processCellOverlays(node, key = null, added = null) {
|
||||||
overlayNode.classList.add("planner--overlay");
|
overlayNode.classList.add("planner--overlay");
|
||||||
node.appendChild(overlayNode);
|
node.appendChild(overlayNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key === "active" && metadata) {
|
||||||
|
const overlayNode = makeAtlasImage(-1);
|
||||||
|
overlayNode.dataset.overlay = key;
|
||||||
|
overlayNode.classList.add("planner--overlay");
|
||||||
|
node.appendChild(overlayNode);
|
||||||
|
}
|
||||||
|
if (key === "active" && !metadata) {
|
||||||
|
const overlayNode = makeAtlasImage(-1);
|
||||||
|
overlayNode.dataset.overlay = "inactive";
|
||||||
|
overlayNode.classList.add("planner--overlay");
|
||||||
|
node.appendChild(overlayNode);
|
||||||
|
}
|
||||||
// FIXME: handle remove
|
// FIXME: handle remove
|
||||||
}
|
}
|
||||||
// TODO: handle generic update
|
// TODO: handle generic update
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,22 @@ export function* getCellTooltip(cell) {
|
||||||
const moderator = data.modules["nuclearcraft:overhaul_sfr:moderator"];
|
const moderator = data.modules["nuclearcraft:overhaul_sfr:moderator"];
|
||||||
yield `Flux factor: ${moderator.flux}`;
|
yield `Flux factor: ${moderator.flux}`;
|
||||||
yield `Efficiency: ${moderator.efficiency}`;
|
yield `Efficiency: ${moderator.efficiency}`;
|
||||||
|
yield {
|
||||||
|
type: "compound",
|
||||||
|
items: [
|
||||||
|
"Moderator ",
|
||||||
|
{
|
||||||
|
type: "bold",
|
||||||
|
text: solverMetadata?.active ? "active" : "inactive",
|
||||||
|
},
|
||||||
|
" for heat sinks",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
if (solverMetadata?.flux) {
|
||||||
|
for (const [axis, flux] of Object.entries(solverMetadata.flux)) {
|
||||||
|
yield `Flux on ${axis}: ${flux}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("nuclearcraft:overhaul_sfr:reflector" in data.modules) {
|
if ("nuclearcraft:overhaul_sfr:reflector" in data.modules) {
|
||||||
|
|
@ -394,13 +410,19 @@ export function* getCellTooltip(cell) {
|
||||||
|
|
||||||
if ("nuclearcraft:overhaul_sfr:heat_sink" in data.modules) {
|
if ("nuclearcraft:overhaul_sfr:heat_sink" in data.modules) {
|
||||||
const heatSink = data.modules["nuclearcraft:overhaul_sfr:heat_sink"];
|
const heatSink = data.modules["nuclearcraft:overhaul_sfr:heat_sink"];
|
||||||
if (heatSink.rules) {
|
if (heatSink.rules && !solverMetadata.active) {
|
||||||
for (const rule of heatSink.rules) {
|
for (const rule of heatSink.rules) {
|
||||||
yield "Rule not met: " + parseRuleAsString(rule, { configurationHint: metadata.configuration });
|
yield "Rule not met: " + parseRuleAsString(rule, { configurationHint: metadata.configuration });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("nuclearcraft:overhaul_sfr:irradiator" in data.modules) {
|
||||||
|
if (solverMetadata?.flux) {
|
||||||
|
yield `Flux: ${solverMetadata.flux}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
type: "italics",
|
type: "italics",
|
||||||
text: metadata.configurationAddon.name,
|
text: metadata.configurationAddon.name,
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,18 @@ function *travelUntil(grid, { x, y, z }, {x: dirX = 0, y: dirY = 0, z: dirZ = 0}
|
||||||
yield grid[x][y][z];
|
yield grid[x][y][z];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function neighbor(grid, { x, y, z }, {x: dirX = 0, y: dirY = 0, z: dirZ = 0}) {
|
||||||
|
x = Number(x);
|
||||||
|
y = Number(y);
|
||||||
|
z = Number(z);
|
||||||
|
if (x > 0 && y > 0 && z > 0
|
||||||
|
&& x < grid.length - 1 && y < grid[0].length - 1 && z < grid[0][0].length - 1) {
|
||||||
|
x += dirX;
|
||||||
|
y += dirY;
|
||||||
|
z += dirZ;
|
||||||
|
return grid[x][y][z];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function debugBlockSolver(data) {
|
async function debugBlockSolver(data) {
|
||||||
const planner = getPlannerDom();
|
const planner = getPlannerDom();
|
||||||
|
|
@ -107,13 +119,15 @@ export async function runSolver() {
|
||||||
// const reflectors = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:reflector"]);
|
// const reflectors = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:reflector"]);
|
||||||
// const shields = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:neutron_shield"]);
|
// const shields = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:neutron_shield"]);
|
||||||
// const moderators = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:moderator"]);
|
// const moderators = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:moderator"]);
|
||||||
// const coolers = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:heat_sink"]);
|
const heatSinks = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:heat_sink"]);
|
||||||
// const irradiators = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:irradiator"]);
|
// const irradiators = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:irradiator"]);
|
||||||
// const conductors = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:conductor"]);
|
// const conductors = plannerBlocks.filter(b => b.tile.modules && b.tile.modules["nuclearcraft:overhaul_sfr:conductor"]);
|
||||||
|
|
||||||
fuelCells.forEach(cell => {
|
fuelCells.forEach(cell => {
|
||||||
const recipeList = cell.tile.modules["ncpf:block_recipes"].recipes;
|
const recipeList = cell.tile.modules["ncpf:block_recipes"].recipes;
|
||||||
cell.recipe = recipeList.find(r => checkEquivalence(r, cell.metadata.block_recipe));
|
cell.recipe = recipeList.find(r => checkEquivalence(r, cell.metadata?.block_recipe));
|
||||||
|
// TODO: handle blank recipe
|
||||||
|
if (!cell.recipe) return; // FIXME
|
||||||
cell.axes = Object.values(sixAxes).map(dir => [...travelUntil(
|
cell.axes = Object.values(sixAxes).map(dir => [...travelUntil(
|
||||||
plannerBlocksGrid,
|
plannerBlocksGrid,
|
||||||
cell,
|
cell,
|
||||||
|
|
@ -136,36 +150,172 @@ export async function runSolver() {
|
||||||
)]);
|
)]);
|
||||||
if (cell.recipe.modules["nuclearcraft:overhaul_sfr:fuel_stats"].self_priming) {
|
if (cell.recipe.modules["nuclearcraft:overhaul_sfr:fuel_stats"].self_priming) {
|
||||||
cell.primer = cell;
|
cell.primer = cell;
|
||||||
|
cell.active = true;
|
||||||
} else if (cell.axes.some(a => (a[a.length - 1]?.tile?.modules ?? {})["nuclearcraft:overhaul_sfr:neutron_source"])) {
|
} else if (cell.axes.some(a => (a[a.length - 1]?.tile?.modules ?? {})["nuclearcraft:overhaul_sfr:neutron_source"])) {
|
||||||
const primerAxis = cell.axes.find(a => (a[a.length - 1]?.tile?.modules ?? {})["nuclearcraft:overhaul_sfr:neutron_source"]);
|
const primerAxis = cell.axes.find(a => (a[a.length - 1]?.tile?.modules ?? {})["nuclearcraft:overhaul_sfr:neutron_source"]);
|
||||||
cell.primer = primerAxis[primerAxis.length - 1];
|
cell.primer = primerAxis[primerAxis.length - 1];
|
||||||
|
cell.active = true;
|
||||||
}
|
}
|
||||||
setCellMetadata(cell.dom, "primer", cell.primer);
|
setCellMetadata(cell.dom, "primer", cell.primer);
|
||||||
});
|
});
|
||||||
|
|
||||||
await debugBlockSolver("primed");
|
await debugBlockSolver("primed");
|
||||||
|
|
||||||
fuelCells.forEach(cell => {
|
const seenModerators = new Set();
|
||||||
|
const activeCells = fuelCells.filter(c => c.active);
|
||||||
|
let activeCellIndex = 0;
|
||||||
|
while (activeCellIndex < activeCells.length) {
|
||||||
|
const cell = activeCells[activeCellIndex];
|
||||||
|
if (!cell.axes) return; // ignore bad cells
|
||||||
Object.keys(sixAxes).map((name, i) => {
|
Object.keys(sixAxes).map((name, i) => {
|
||||||
console.log(name, cell.axes[i]);
|
|
||||||
let moderatorSum = 0;
|
let moderatorSum = 0;
|
||||||
|
let moderatorPath = [];
|
||||||
for (const block of cell.axes[i]) {
|
for (const block of cell.axes[i]) {
|
||||||
|
if (!block) break;
|
||||||
|
|
||||||
if (block.tile.modules["nuclearcraft:overhaul_sfr:moderator"]) {
|
if (block.tile.modules["nuclearcraft:overhaul_sfr:moderator"]) {
|
||||||
moderatorSum += block.tile.modules["nuclearcraft:overhaul_sfr:moderator"].flux;
|
moderatorSum += block.tile.modules["nuclearcraft:overhaul_sfr:moderator"].flux;
|
||||||
|
moderatorPath.push(block);
|
||||||
|
seenModerators.add(block);
|
||||||
|
if (!block.flux) {
|
||||||
|
block.flux = {};
|
||||||
|
}
|
||||||
|
block.flux[name] = moderatorSum;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log(block);
|
|
||||||
if (block.tile.modules["nuclearcraft:overhaul_sfr:fuel_cell"] || block.tile.modules["nuclearcraft:overhaul_sfr:irradiator"]) {
|
if (block.tile.modules["nuclearcraft:overhaul_sfr:fuel_cell"] || block.tile.modules["nuclearcraft:overhaul_sfr:irradiator"]) {
|
||||||
block.flux = (block.flux ?? 0) + moderatorSum;
|
block.flux = (block.flux ?? 0) + moderatorSum;
|
||||||
setCellMetadata(block.dom, "flux", block.flux);
|
|
||||||
}
|
}
|
||||||
if (block.tile.modules["nuclearcraft:overhaul_sfr:reflector"]) {
|
if (block.tile.modules["nuclearcraft:overhaul_sfr:reflector"]) {
|
||||||
cell.flux = (cell.flux ?? 0) + moderatorSum * 2;
|
const backAxis = name.replace(/^[+-]/, s => s === "+" ? "-" : "+");
|
||||||
setCellMetadata(cell.dom, "flux", cell.flux);
|
for (const blockBack of moderatorPath.toReversed()) {
|
||||||
|
moderatorSum += blockBack.tile.modules["nuclearcraft:overhaul_sfr:moderator"].flux;
|
||||||
|
moderatorPath.push(blockBack);
|
||||||
|
blockBack.flux[backAxis] = moderatorSum;
|
||||||
|
}
|
||||||
|
cell.flux = (cell.flux ?? 0) + moderatorSum;
|
||||||
|
}
|
||||||
|
if (block.tile.modules["nuclearcraft:overhaul_sfr:fuel_cell"] && !block.active) {
|
||||||
|
if (!block.recipe) break; // FIXME
|
||||||
|
const fuelStats = block.recipe.modules["nuclearcraft:overhaul_sfr:fuel_stats"];
|
||||||
|
if (fuelStats.criticality <= block.flux) {
|
||||||
|
block.active = true;
|
||||||
|
activeCells.push(block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (moderatorPath.length > 0) {
|
||||||
|
moderatorPath[0].active = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
activeCellIndex++;
|
||||||
|
}
|
||||||
|
seenModerators.forEach(moderator => {
|
||||||
|
setCellMetadata(moderator.dom, "flux", moderator.flux);
|
||||||
|
if (moderator.active) {
|
||||||
|
setCellMetadata(moderator.dom, "active", moderator.active);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
fuelCells.forEach(cell => {
|
||||||
|
if (cell.flux) {
|
||||||
|
setCellMetadata(cell.dom, "flux", cell.flux);
|
||||||
|
}
|
||||||
|
setCellMetadata(cell.dom, "active", cell.active ?? false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await debugBlockSolver("moderators");
|
||||||
|
|
||||||
|
heatSinks.forEach(hs => {
|
||||||
|
hs.neighbors = {};
|
||||||
|
Object.entries(sixAxes).forEach(([name, dir]) => {
|
||||||
|
hs.neighbors[name] = neighbor(plannerBlocksGrid, hs, dir);
|
||||||
|
});
|
||||||
|
hs.rules = hs.tile.modules["nuclearcraft:overhaul_sfr:heat_sink"].rules;
|
||||||
|
});
|
||||||
|
let heatSinkProcessed = false;
|
||||||
|
const unprocessedHeatSinks = new Set(heatSinks);
|
||||||
|
do {
|
||||||
|
heatSinkProcessed = false;
|
||||||
|
unprocessedHeatSinks.forEach(hs => {
|
||||||
|
if (hs.active) {
|
||||||
|
console.log("active hs in queue", hs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const activeNeighbors = Object.entries(hs.neighbors).filter(([_, n]) => {
|
||||||
|
if (!n) return false;
|
||||||
|
if (n.active) return true;
|
||||||
|
if (n.tile.modules) {
|
||||||
|
return n.tile.modules["nuclearcraft:overhaul_sfr:reflector"]
|
||||||
|
|| n.tile.modules["nuclearcraft:overhaul_sfr:irradiator"]
|
||||||
|
|| n.tile.modules["nuclearcraft:overhaul_sfr:casing"];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (activeNeighbors.length === 0) return;
|
||||||
|
|
||||||
|
let valid = false;
|
||||||
|
for (const rule of hs.rules) {
|
||||||
|
if (checkHeatSinkRule(hs, rule, activeNeighbors)) {
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
heatSinkProcessed = true;
|
||||||
|
unprocessedHeatSinks.delete(hs);
|
||||||
|
hs.active = true;
|
||||||
|
setCellMetadata(hs.dom, "active", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} while (heatSinkProcessed);
|
||||||
|
unprocessedHeatSinks.forEach(hs => {
|
||||||
|
setCellMetadata(hs.dom, "active", false);
|
||||||
|
});
|
||||||
|
console.log(unprocessedHeatSinks);
|
||||||
|
|
||||||
|
await debugBlockSolver("heatsinks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkHeatSinkRule(hs, rule, activeNeighbors) {
|
||||||
|
switch (rule.type) {
|
||||||
|
case "and":
|
||||||
|
return rule.rules.every(r => checkHeatSinkRule(hs, r, activeNeighbors));
|
||||||
|
|
||||||
|
case "or":
|
||||||
|
return rule.rules.some(r => checkHeatSinkRule(hs, r, activeNeighbors));
|
||||||
|
|
||||||
|
case "between": {
|
||||||
|
const blocks = activeNeighbors
|
||||||
|
.map(([,b]) => b.tile)
|
||||||
|
.filter(b => {
|
||||||
|
if (rule.block.type === "module") {
|
||||||
|
return b.modules && rule.block.name in b.modules;
|
||||||
|
}
|
||||||
|
return checkEquivalence(rule.block, b);
|
||||||
|
});
|
||||||
|
return rule.min <= blocks.length && blocks.length <= rule.max;
|
||||||
|
}
|
||||||
|
case "axial": {
|
||||||
|
const axialPairs = activeNeighbors
|
||||||
|
.filter(([,b]) => {
|
||||||
|
if (rule.block.type === "module") {
|
||||||
|
return b.tile.modules && rule.block.name in b.tile.modules;
|
||||||
|
}
|
||||||
|
return checkEquivalence(rule.block, b.tile);
|
||||||
|
})
|
||||||
|
.reduce((acc, [dir, block]) => {
|
||||||
|
acc[dir[1]] = acc[dir[1]] ?? [];
|
||||||
|
acc[dir[1]].push(block);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const count = Object.values(axialPairs).filter(p => p.length === 2).length;
|
||||||
|
return rule.min <= count && count <= rule.max;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.warn("unknown rule: %o", rule);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,14 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
||||||
.planner--cell > .planner--overlay[data-overlay="block_recipe"] {
|
.planner--cell > .planner--overlay[data-overlay="block_recipe"] {
|
||||||
transform: scale(var(--recipe-scale-factor, 0.8));
|
transform: scale(var(--recipe-scale-factor, 0.8));
|
||||||
}
|
}
|
||||||
|
#planner .planner--cell > .planner--overlay[data-overlay="active"] {
|
||||||
|
outline-offset: calc(var(--scale-factor) * -1px);
|
||||||
|
outline: calc(var(--scale-factor) * 1px) solid var(--cell-active-outline, #0f08);
|
||||||
|
}
|
||||||
|
#planner .planner--cell > .planner--overlay[data-overlay="inactive"] {
|
||||||
|
outline-offset: calc(var(--scale-factor) * -1px);
|
||||||
|
outline: calc(var(--scale-factor) * 1px) solid var(--cell-inactive-outline, #f008);
|
||||||
|
}
|
||||||
|
|
||||||
.planner--resize {
|
.planner--resize {
|
||||||
--resize-width: var(--width);
|
--resize-width: var(--width);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue