403 lines
12 KiB
Lua
403 lines
12 KiB
Lua
local floor, ceil = math.floor, math.ceil
|
|
local min, max = math.min, math.max
|
|
|
|
local common = require("layouts.common")
|
|
local simple = require("layouts.simple")
|
|
local mpp_util = require("mpp_util")
|
|
local EAST, NORTH, SOUTH, WEST = mpp_util.directions()
|
|
|
|
---@class CompactLayout : SimpleLayout
|
|
local layout = table.deepcopy(simple)
|
|
|
|
layout.name = "compact"
|
|
layout.translation = {"mpp.settings_layout_choice_compact"}
|
|
|
|
layout.restrictions.miner_near_radius = {1, 1}
|
|
layout.restrictions.miner_far_radius = {1, 10e3}
|
|
layout.restrictions.uses_underground_belts = true
|
|
layout.restrictions.pole_omittable = true
|
|
layout.restrictions.pole_width = {1, 1}
|
|
layout.restrictions.pole_length = {7.5, 10e3}
|
|
layout.restrictions.pole_supply_area = {2.5, 10e3}
|
|
layout.restrictions.coverage_tuning = true
|
|
layout.restrictions.lamp_available = true
|
|
layout.restrictions.module_available = true
|
|
layout.restrictions.pipe_available = true
|
|
|
|
---@param state SimpleState
|
|
---@return PlacementAttempt
|
|
function layout:_placement_attempt(state, shift_x, shift_y)
|
|
local grid = state.grid
|
|
local size, near, far = state.miner.size, state.miner.near, state.miner.far
|
|
local fullsize = state.miner.full_size
|
|
local neighbor_sum = 0
|
|
local far_neighbor_sum = 0
|
|
local simple_density = 0
|
|
local real_density = 0
|
|
local miners, postponed = {}, {}
|
|
local leech_sum = 0
|
|
local empty_space = 0
|
|
local lane_layout = {}
|
|
|
|
local heuristic = self:_get_miner_placement_heuristic(state)
|
|
|
|
local row_index = 1
|
|
for ry = 1 + shift_y, state.coords.th + near, size + 0.5 do
|
|
local y = ceil(ry)
|
|
local column_index = 1
|
|
lane_layout[#lane_layout+1] = {y = y+near, row_index = row_index}
|
|
for x = 1 + shift_x, state.coords.tw, size do
|
|
local tile = grid:get_tile(x, y)
|
|
local center = grid:get_tile(x+near, y+near) --[[@as GridTile]]
|
|
local miner = {
|
|
tile = tile,
|
|
line = row_index,
|
|
column = column_index,
|
|
center = center,
|
|
}
|
|
if heuristic(center) then
|
|
miners[#miners+1] = miner
|
|
neighbor_sum = neighbor_sum + center.neighbor_count
|
|
far_neighbor_sum = far_neighbor_sum + center.far_neighbor_count
|
|
empty_space = empty_space + (size^2) - center.neighbor_count
|
|
real_density = real_density + center.far_neighbor_count / (fullsize ^ 2)
|
|
simple_density = simple_density + center.neighbor_count / (size ^ 2)
|
|
leech_sum = leech_sum + max(0, center.far_neighbor_count - center.neighbor_count)
|
|
elseif center.far_neighbor_count > 0 then
|
|
postponed[#postponed+1] = miner
|
|
end
|
|
column_index = column_index + 1
|
|
end
|
|
row_index = row_index + 1
|
|
end
|
|
|
|
local result = {
|
|
sx=shift_x, sy=shift_y,
|
|
miners=miners,
|
|
miner_count=#miners,
|
|
lane_layout=lane_layout,
|
|
postponed=postponed,
|
|
neighbor_sum=neighbor_sum,
|
|
far_neighbor_sum=far_neighbor_sum,
|
|
leech_sum=leech_sum,
|
|
simple_density=simple_density,
|
|
real_density=real_density,
|
|
empty_space=empty_space,
|
|
unconsumed_count=0,
|
|
postponed_count=0,
|
|
}
|
|
|
|
common.process_postponed(state, result, miners, postponed)
|
|
|
|
return result
|
|
end
|
|
|
|
---@param self CompactLayout
|
|
---@param state SimpleState
|
|
function layout:prepare_belt_layout(state)
|
|
local pole_proto = game.entity_prototypes[state.pole_choice] or {supply_area_distance=3, max_wire_distance=9}
|
|
local supply_area, wire_reach = 3.5, 9
|
|
if pole_proto then
|
|
supply_area, wire_reach = pole_proto.supply_area_distance, pole_proto.max_wire_distance
|
|
end
|
|
|
|
local belts = {}
|
|
state.belts = belts
|
|
state.builder_belts = {}
|
|
|
|
if supply_area < 3 or wire_reach < 9 then
|
|
state.pole_step = 6
|
|
self:_placement_belts_small(state)
|
|
else
|
|
state.pole_step = 9
|
|
self:_placement_belts_large(state)
|
|
end
|
|
|
|
return "prepare_pole_layout"
|
|
end
|
|
|
|
local function create_entity_que(belts)
|
|
return function(t) belts[#belts+1] = t end
|
|
end
|
|
|
|
---@param self CompactLayout
|
|
---@param state SimpleState
|
|
function layout:_placement_belts_small(state)
|
|
local m = state.miner
|
|
local attempt = state.best_attempt
|
|
local belt_choice = state.belt_choice
|
|
local underground_belt = game.entity_prototypes[belt_choice].related_underground_belt.name
|
|
|
|
local power_poles = {}
|
|
state.builder_power_poles = power_poles
|
|
|
|
---@type table<number, MinerPlacement[]>
|
|
local miner_lanes = {}
|
|
local miner_lane_count = 0 -- highest index of a lane, because using # won't do the job if a lane is missing
|
|
|
|
local belts = state.belts
|
|
|
|
for _, miner in ipairs(attempt.miners) do
|
|
local index = miner.line
|
|
miner_lane_count = max(miner_lane_count, index)
|
|
if not miner_lanes[index] then miner_lanes[index] = {} end
|
|
local line = miner_lanes[index]
|
|
line[#line+1] = miner
|
|
end
|
|
|
|
for _, lane in ipairs(miner_lanes) do
|
|
table.sort(lane, function(a, b) return a.center.x < b.center.x end)
|
|
end
|
|
|
|
---@param lane MinerPlacement[]
|
|
local function get_lane_length(lane) if lane then return lane[#lane].center.x end return 0 end
|
|
---@param lane MinerPlacement[]
|
|
local function get_lane_column(lane) if lane and #lane > 0 then return lane[#lane].column end return 0 end
|
|
|
|
state.belt_count = 0
|
|
local que_entity = create_entity_que(state.builder_belts)
|
|
|
|
local function belts_filled(x1, y, w)
|
|
for x = x1, x1 + w do
|
|
que_entity{name=belt_choice, direction=WEST, grid_x=x, grid_y=y, thing="belt"}
|
|
end
|
|
end
|
|
|
|
local pipe_adjust = state.place_pipes and -1 or 0
|
|
for i = 1, miner_lane_count, 2 do
|
|
local lane1 = miner_lanes[i]
|
|
local lane2 = miner_lanes[i+1]
|
|
|
|
local y = attempt.sy + m.size * i + ceil(i/2)
|
|
local x0 = attempt.sx + 1
|
|
|
|
local column_count = max(get_lane_column(lane1), get_lane_column(lane2))
|
|
if column_count == 0 then goto continue_lane end
|
|
|
|
state.belt_count = state.belt_count + 1
|
|
|
|
local indices = {}
|
|
if lane1 then for _, v in ipairs(lane1) do indices[v.column] = v end end
|
|
if lane2 then for _, v in ipairs(lane2) do indices[v.column] = v end end
|
|
|
|
if state.place_pipes then
|
|
que_entity{
|
|
name=state.belt_choice,
|
|
thing="belt",
|
|
grid_x = x0 + pipe_adjust,
|
|
grid_y = y,
|
|
direction=WEST,
|
|
}
|
|
end
|
|
|
|
belts[#belts+1] = {
|
|
x1 = x0 + pipe_adjust, x2 = x0 + column_count * m.size,
|
|
y = y, lane1 = lane1, lane2 = lane2,
|
|
}
|
|
|
|
for j = 1, column_count do
|
|
local x1 = x0 + (j-1) * m.size
|
|
if j % 2 == 1 then -- part one
|
|
if indices[j] or indices[j+1] then
|
|
que_entity{
|
|
name=state.belt_choice, thing="belt", grid_x=x1, grid_y=y, direction=WEST,
|
|
}
|
|
local stopper = (j+1 > column_count) and state.belt_choice or underground_belt
|
|
que_entity{
|
|
name=stopper, thing="belt", grid_x=x1+1, grid_y=y, direction=WEST, type="output",
|
|
}
|
|
-- power_poles[#power_poles+1] = {
|
|
-- x=x1+3, y=y,
|
|
-- ix=1+floor(i/2), iy=1+floor(j/2),
|
|
-- built = true,
|
|
-- }
|
|
power_poles[#power_poles+1] = {
|
|
name=state.pole_choice,
|
|
thing="pole",
|
|
grid_x = x1+3,
|
|
grid_y = y,
|
|
ix=1+floor(i/2), iy=1+floor(j/2),
|
|
}
|
|
else -- just a passthrough belt
|
|
belts_filled(x1, y, m.size - 1)
|
|
end
|
|
elseif j % 2 == 0 then -- part two
|
|
if indices[j-1] or indices[j] then
|
|
que_entity{
|
|
name=belt_choice, thing="belt", grid_x=x1+2, grid_y=y, direction=WEST,
|
|
}
|
|
que_entity{
|
|
name=underground_belt, thing="belt", grid_x=x1+1, grid_y=y, direction=WEST,
|
|
}
|
|
else -- just a passthrough belt
|
|
belts_filled(x1, y, m.size - 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
::continue_lane::
|
|
end
|
|
|
|
end
|
|
|
|
|
|
---@param self CompactLayout
|
|
function layout:_placement_belts_large(state)
|
|
local m = state.miner
|
|
local attempt = state.best_attempt
|
|
local belt_choice = state.belt_choice
|
|
local underground_belt = game.entity_prototypes[belt_choice].related_underground_belt.name
|
|
|
|
local power_poles = {}
|
|
state.builder_power_poles = power_poles
|
|
|
|
local belts = state.belts
|
|
|
|
---@type table<number, MinerPlacement[]>
|
|
local miner_lanes = {{}}
|
|
local miner_lane_count = 0 -- highest index of a lane, because using # won't do the job if a lane is missing
|
|
|
|
for _, miner in ipairs(attempt.miners) do
|
|
local index = miner.line
|
|
miner_lane_count = max(miner_lane_count, index)
|
|
if not miner_lanes[index] then miner_lanes[index] = {} end
|
|
local line = miner_lanes[index]
|
|
line[#line+1] = miner
|
|
end
|
|
|
|
state.miner_lane_count = miner_lane_count
|
|
|
|
local que_entity = create_entity_que(state.builder_belts)
|
|
|
|
for _, lane in pairs(miner_lanes) do
|
|
table.sort(lane, function(a, b) return a.center.x < b.center.x end)
|
|
end
|
|
|
|
---@param lane MinerPlacement[]
|
|
local function get_lane_length(lane) if lane and #lane > 0 then return lane[#lane].center.x or 0 end return 0 end
|
|
---@param lane MinerPlacement[]
|
|
local function get_lane_column(lane) if lane and #lane > 0 then return lane[#lane].column or 0 end return 0 end
|
|
|
|
local function belts_filled(x1, y, w)
|
|
for x = x1, x1 + w do
|
|
que_entity{name=belt_choice, direction=WEST, grid_x=x, grid_y=y, thing="belt"}
|
|
end
|
|
end
|
|
|
|
local pipe_adjust = state.place_pipes and -1 or 0
|
|
for i = 1, miner_lane_count, 2 do
|
|
local lane1 = miner_lanes[i]
|
|
local lane2 = miner_lanes[i+1]
|
|
|
|
local y = attempt.sy + m.size * i + ceil(i/2)
|
|
local x0 = attempt.sx + 1
|
|
|
|
local column_count = max(get_lane_column(lane1), get_lane_column(lane2))
|
|
if column_count == 0 then goto continue_lane end
|
|
|
|
local indices = {}
|
|
if lane1 then for _, v in ipairs(lane1) do indices[v.column] = v end end
|
|
if lane2 then for _, v in ipairs(lane2) do indices[v.column] = v end end
|
|
|
|
if state.place_pipes then
|
|
que_entity{
|
|
name=state.belt_choice,
|
|
thing="belt",
|
|
grid_x = x0 + pipe_adjust,
|
|
grid_y = y,
|
|
direction=WEST,
|
|
}
|
|
end
|
|
|
|
belts[#belts+1] = {
|
|
x1 = x0 + pipe_adjust, x2 = x0 + column_count * m.size,
|
|
y = y, lane1 = lane1, lane2 = lane2,
|
|
}
|
|
|
|
for j = 1, column_count do
|
|
local x1 = x0 + (j-1) * m.size
|
|
if j % 3 == 1 then -- part one
|
|
if indices[j] or indices[j+1] or indices[j+2] then
|
|
que_entity{
|
|
name=belt_choice, grid_x=x1, grid_y=y, thing="belt", direction=WEST,
|
|
}
|
|
|
|
local stopper = (j+1 > column_count) and state.belt_choice or underground_belt
|
|
que_entity{
|
|
name=stopper, grid_x=x1+1, grid_y=y, thing="belt", direction=WEST,
|
|
type="output",
|
|
}
|
|
-- power_poles[#power_poles+1] = {
|
|
-- x=x1+3, y=y,
|
|
-- ix=1+floor(i/2), iy=1+floor(j/2),
|
|
-- built = true,
|
|
-- }
|
|
power_poles[#power_poles+1] = {
|
|
name=state.pole_choice,
|
|
thing="pole",
|
|
grid_x = x1+3,
|
|
grid_y = y,
|
|
ix=1+floor(i/2), iy=1+floor(j/2),
|
|
}
|
|
else -- just a passthrough belt
|
|
belts_filled(x1, y, m.size - 1)
|
|
end
|
|
elseif j % 3 == 2 then -- part two
|
|
if indices[j-1] or indices[j] or indices[j+1] then
|
|
que_entity{
|
|
name=underground_belt, grid_x=x1+1, grid_y=y, thing="belt", direction=WEST,
|
|
type="input",
|
|
}
|
|
que_entity{
|
|
name=belt_choice, grid_x=x1+2, grid_y=y, thing="belt", direction=WEST,
|
|
}
|
|
else -- just a passthrough belt
|
|
belts_filled(x1, y, m.size - 1)
|
|
end
|
|
elseif j % 3 == 0 then
|
|
belts_filled(x1, y, m.size - 1)
|
|
end
|
|
end
|
|
|
|
::continue_lane::
|
|
end
|
|
end
|
|
|
|
---@param self CompactLayout
|
|
---@param state SimpleState
|
|
function layout:prepare_lamp_layout(state)
|
|
local lamps = {}
|
|
state.builder_lamps = lamps
|
|
|
|
local grid = state.grid
|
|
|
|
local sx, sy = -1, 0
|
|
local lamp_spacing = true
|
|
if state.pole_step > 7 then lamp_spacing = false end
|
|
|
|
for _, pole in ipairs(state.builder_power_poles) do
|
|
local x, y = pole.grid_x, pole.grid_y
|
|
local ix, iy = pole.ix, pole.iy
|
|
local tile = grid:get_tile(x+sx, y+sy)
|
|
local skippable_lamp = iy % 2 == 1 and ix % 2 == 1
|
|
if tile and (not lamp_spacing or skippable_lamp) then
|
|
lamps[#lamps+1] = {
|
|
name="small-lamp",
|
|
thing="lamp",
|
|
grid_x = x+sx,
|
|
grid_y = y+sy,
|
|
}
|
|
end
|
|
end
|
|
|
|
return "expensive_deconstruct"
|
|
end
|
|
|
|
---@param self CompactLayout
|
|
---@param state SimpleState
|
|
function layout:prepare_pole_layout(state)
|
|
return "prepare_lamp_layout"
|
|
end
|
|
|
|
return layout
|