Skip to content
This repository has been archived by the owner on Nov 21, 2017. It is now read-only.

Commit

Permalink
major ai planning
Browse files Browse the repository at this point in the history
  • Loading branch information
Afforess committed Mar 27, 2016
1 parent 1bf5c94 commit 56f5302
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 123 deletions.
146 changes: 146 additions & 0 deletions libs/ai/attack_plan.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
require 'libs/pathfinder'

attack_plan = {}
attack_plan.__index = attack_plan

function attack_plan.find_best_player_target(attack_data, max_chunks)
local best_value = nil
local best_position = nil
local surface = game.surfaces[attack_data.surface_name]
local region_data = region.lookup_region_from_position(surface, attack_data.position)

for dx = -(max_chunks), max_chunks do
for dy = -(max_chunks), max_chunks do
local tile_x = dx * 32 + attack_data.position.x
local tile_y = dx * 32 + attack_data.position.y

local value = region.get_player_target_value_at(region_data, {x = tile_x, y = tile_y})
value = value / (1 + ((dx * dx) + (dy * dy)))
if value > 0 and (best_value == nil or value > best_value)then
best_value = value
best_position = { x = tile_x, y = tile_y }
end
end
end

return best_position
end

function attack_plan.tick(attack_data)
if attack_data.completed then
return
end
local expansion_phase = BiterExpansion.get_expansion_phase(global.expansion_index)

if not attack_data.target_position then
local chunk_search = expansion_phase.min_biter_attack_chunk_distance
local attack_target = attack_plan.find_best_player_target(attack_data, chunk_search)
if attack_target == nil then
Logger.log("Failed to find an attack target within " .. chunk_search .. " chunks of " .. serpent.line(attack_data.position))
attack_data.completed = true
return
else
Logger.log("Best attack target within " .. chunk_search .. " chunks of " .. serpent.line(attack_data.position) .. " is " .. serpent.line(attack_target))
attack_data.target_position = attack_target
end
end

if attack_data.unit_group and not attack_data.wait_for_attack then
if attack_data.unit_group.valid then
attack_data.attack_in_progress = attack_data.attack_in_progress + 1
else
attack_data.completed = true
Logger.log("Took " .. attack_data.attack_in_progress .. " ticks for the unit group to become invalid (attack complete)")
end
-- 1 minute
if attack_data.attack_in_progress > (60 * 60 * 60 * 1) then
Logger.log("Attack unit group never become invalid! Data: {" .. serpent.line(attack_data) .. "}")
attack_data.completed = true
end
return
else
attack_plan.coordinate_biters(attack_data)
return
end

if not attack_data.pathfinder_data then
local surface = game.surfaces[attack_data.surface_name]

attack_data.pathfinder_data = pathfinder.partial_a_star(surface, attack_data.position, attack_data.target_position, 10)
elseif not attack_data.pathfinder_data.completed then
attack_data.pathfinder_data = pathfinder.resume_a_star(attack_data.pathfinder_data, 1)
if attack_data.pathfinder_data.iterations > 1000 then
Logger.log("Pathfinding between " .. serpent.line(attack_data.position) .. " and " .. serpent.line(attack_data.target_position) .. " took more than 1000 iterations")
attack_data.completed = true
return
end
else
local path = attack_data.pathfinder_data.path
if not path then
Logger.log("Failed to find a path between " .. serpent.line(attack_data.position) .. " and " .. serpent.line(attack_data.target_position))
attack_data.completed = true
else
attack_plan.attack_target(attack_data)
attack_data.completed = true
end
end
end

function attack_plan.coordinate_biters(attack_data)
local expansion_phase = BiterExpansion.get_expansion_phase(global.expansion_index)
local surface = game.surfaces[attack_data.surface_name]
local pos = attack_data.position
local range = math.min(35, 15 + (3 * attack_data.biter_base.count))
local area = {left_top = {x = pos.x - range, y = pos.y - range}, right_bottom = {x = pos.x + range, y = pos.y + range}}
local enemy_units = surface.find_entities_filtered({area = area, type = "unit", force = game.forces.enemy})

if #enemy_units == 0 then
Logger.log("Failed to find any enemy units at " .. serpent.line(pos, {comment = false}))
attack_data.completed = true
return
end

Logger.log("Found " .. #enemy_units .. " enemy units at " .. serpent.line(pos, {comment = false}))
local total_x = 0
local total_y = 0
for _, entity in pairs(enemy_units) do
local entity_pos = entity.position
total_x = total_x + entity_pos.x
total_y = total_y + entity_pos.y
end
local avg_pos = {x = total_x / #enemy_units, y = total_y / #enemy_units}
Logger.log("Average biter position for attack position (" .. serpent.line(pos, {comment = false}) .. "): " .. serpent.line(avg_pos, {comment = false}))

local safe_pos = surface.find_non_colliding_position("behemoth-spitter", avg_pos, 16, 0.5)
Logger.log("Safe position for grouping: " .. serpent.line(safe_pos, {comment = false}))
if not safe_pos then
attack_data.completed = true
return
end

local unit_group = surface.create_unit_group({position = safe_pos, force = game.forces.enemy})
for _, entity in pairs(enemy_units) do
unit_group.add_member(entity)
end
attack_data.unit_group = unit_group
attack_data.attack_in_progress = 0
local command = {type = defines.command.attack_area, destination = attack_data.target_position, radius = 16, distraction = defines.distraction.by_damage}

if game.evolution_factor > 0.5 and #enemy_units < 20 then
local target_pos = attack_data.target_position
local half_way_pos = { x = (safe_pos.x + target_pos.x) / 2, y = (safe_pos.y + target_pos.y) / 2}
local safe_base_pos = surface.find_non_colliding_position("behemoth-spitter", half_way_pos, 16, 0.5)
Logger.log("Safe position for building a new base: " .. serpent.line(safe_base_pos, {comment = false}))
if safe_base_pos then
command = {type = defines.command.build_base, destination = safe_base_pos, ignore_planner = 1, distraction = defines.distraction.by_damage}
end
end

Logger.log("Command: " .. serpent.line(command, {comment=false}))
unit_group.set_command(command)
unit_group.start_moving()
end

function attack_plan.new(surface_name, position, biter_base)
return { position = position, surface_name = surface_name, biter_base = biter_base, completed = false }
end
13 changes: 8 additions & 5 deletions libs/biter_expansion.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function BiterExpansion.new()
if expansion_phase.tick then
expansion_phase:tick()
end

-- apparently this is really slow
if game.tick % 600 == 0 then
self:update_expansion_factors(expansion_phase)
Expand Down Expand Up @@ -97,6 +97,9 @@ function BiterExpansion.new()

self:reset_unit_group()

game.map_settings.steering.moving.separation_force = 0.005
game.map_settings.steering.moving.separation_factor = 1

-- cause pollution to spread farther
game.map_settings.pollution.diffusion_ratio = 0.05
game.map_settings.pollution.min_to_diffuse = 10
Expand All @@ -116,10 +119,10 @@ function BiterExpansion.new()
end

-- At 12 hours, the time factor will be at 0.000004 (vanilla value).
-- after 108 hours of game play, max value of 0.00002 will be reached
local time_factor = math.min(0.00002, 0.000002 + 0.00000000000077160494 * ticks_played)
-- after 64 hours of gameplay, max value of 0.000025 will be reached
local pollution_factor = math.max(0.000025, 0.000005 + 0.0000000000014467593 * ticks_played)
-- after 108 hours of game play, max value of 0.00008 will be reached
local time_factor = math.min(0.00008, 0.000002 + 0.0000000000030864198 * ticks_played)
-- after 64 hours of gameplay, max value of 0.00005 will be reached
local pollution_factor = math.max(0.00005, 0.000005 + 0.0000000000028935186 * ticks_played)

if global.harpa_list and global.idle_harpa_list then
if #global.harpa_list > 0 or #global.idle_harpa_list > 0 then
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/aggressive.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Aggressive Expansion",
evo_modifier = 0.96,
minimum_attack_value = 0,
min_biter_attack_group = 50,
min_biter_attack_chunk_distance = 12,
min_biter_search_distance = 96}

function expansion:update_expansion_state()
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/assault.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Assault",
evo_modifier = 0.90,
minimum_attack_value = 0,
min_biter_attack_group = 75,
min_biter_attack_chunk_distance = 16,
min_biter_search_distance = 128}

function expansion:update_expansion_state()
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/beachhead.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Beachhead",
evo_modifier = 0.85,
minimum_attack_value = 0,
min_biter_attack_group = 50,
min_biter_attack_chunk_distance = 20,
min_biter_search_distance = 172}

function expansion:update_expansion_state()
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/normal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Normal",
evo_modifier = 1,
minimum_attack_value = 1000,
min_biter_attack_group = 10,
min_biter_attack_chunk_distance = 8,
min_biter_search_distance = 32}

function expansion:update_expansion_state()
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/passive.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Passive Expansion",
evo_modifier = 0.975,
minimum_attack_value = 100,
min_biter_attack_group = 32,
min_biter_attack_chunk_distance = 10,
min_biter_search_distance = 64}

function expansion:update_expansion_state()
Expand Down
1 change: 1 addition & 0 deletions libs/expansion/peaceful.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local expansion = { name = "Peaceful",
evo_modifier = 1,
minimum_attack_value = 10000,
min_biter_attack_group = 10,
min_biter_attack_chunk_distance = 1,
min_biter_search_distance = 16}

function expansion:update_expansion_state()
Expand Down
2 changes: 1 addition & 1 deletion libs/logger.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "defines"

Logger = {prefix='misanthrope', name = 'main', log_buffer = {}, last_write_tick = 0, last_write_size = 0, ever_written = false, debug = true}
Logger = {prefix='misanthrope', name = 'main', log_buffer = {}, last_write_tick = 0, last_write_size = 0, ever_written = false, debug = false}

function Logger.log(str)
local run_time_s = 0
Expand Down
Loading

0 comments on commit 56f5302

Please sign in to comment.