Implements plugin: channel-safely v0.4

develop
Josh Cooper 2022-11-06 13:16:27 -08:00
parent a8dcfeead9
commit c2d346fc84
6 changed files with 105 additions and 54 deletions

@ -55,8 +55,7 @@ Settings
-------- --------
:refresh-freq: The rate at which full refreshes are performed. :refresh-freq: The rate at which full refreshes are performed.
This can be expensive if you're undertaking many mega projects. (default:600, twice a day) This can be expensive if you're undertaking many mega projects. (default:600, twice a day)
:monitor-freq: The rate at which active jobs are monitored. :monitor-freq: The rate at which active jobs are monitored. (default:1)
todo: this can have a massive impact? (default:10)
:ignore-threshold: Sets the priority threshold below which designations are processed. You can set to 1 or 0 to :ignore-threshold: Sets the priority threshold below which designations are processed. You can set to 1 or 0 to
effectively disable the scanning. (default: 7) effectively disable the scanning. (default: 5)
:fall-threshold: Sets the fall threshold beyond which is considered unsafe. (default: 1) :fall-threshold: Sets the fall threshold beyond which is considered unsafe. (default: 1)

@ -5,6 +5,22 @@
#include <random> #include <random>
template<class Ctr1, class Ctr2, class Ctr3>
void set_difference(const Ctr1 &c1, const Ctr2 &c2, Ctr3 &c3) {
for (const auto &a : c1) {
bool matched = false;
for (const auto &b : c2) {
if (a == b) {
matched = true;
break;
}
}
if (!matched) {
c3.emplace(a);
}
}
}
// adds map_pos to a group if an adjacent one exists, or creates one if none exist... if multiple exist they're merged into the first found // adds map_pos to a group if an adjacent one exists, or creates one if none exist... if multiple exist they're merged into the first found
void ChannelGroups::add(const df::coord &map_pos) { void ChannelGroups::add(const df::coord &map_pos) {
// if we've already added this, we don't need to do it again // if we've already added this, we don't need to do it again
@ -111,12 +127,24 @@ void ChannelGroups::scan_one(const df::coord &map_pos) {
// builds groupings of adjacent channel designations // builds groupings of adjacent channel designations
void ChannelGroups::scan() { void ChannelGroups::scan() {
// iterate over each job, finding channel jobs // save current jobs, then clear and load the current jobs
std::set<df::coord> last_jobs;
for (auto &pos : jobs) {
last_jobs.emplace(pos);
}
jobs.load_channel_jobs(); jobs.load_channel_jobs();
// transpose channel jobs to // transpose channel jobs to
for (auto &pos : jobs) { std::set<df::coord> new_jobs;
std::set<df::coord> gone_jobs;
set_difference(last_jobs, jobs, gone_jobs);
set_difference(jobs, last_jobs, new_jobs);
for (auto &pos : new_jobs) {
add(pos); add(pos);
} }
for (auto &pos : gone_jobs){
remove(pos);
}
DEBUG(groups).print(" scan()\n"); DEBUG(groups).print(" scan()\n");
// foreach block // foreach block
for (int32_t z = mapz - 1; z >= 0; --z) { for (int32_t z = mapz - 1; z >= 0; --z) {

@ -62,9 +62,18 @@ bool ChannelManager::manage_one(const Group &group, const df::coord &map_pos, bo
// do we already know whether to set marker mode? // do we already know whether to set marker mode?
if (set_marker_mode) { if (set_marker_mode) {
DEBUG(manager).print(" -> marker_mode\n"); DEBUG(manager).print(" -> marker_mode\n");
tile_occupancy.bits.dig_marked = marker_mode; // if enabling marker mode, just do it
jobs.erase(map_pos); if (marker_mode) {
return true; tile_occupancy.bits.dig_marked = marker_mode;
return true;
}
// if activating designation, check if it is safe to dig or not a channel designation
if (!is_channel_designation(block->designation[Coord(local)]) || is_safe_to_dig_down(map_pos)) {
tile_occupancy.bits.dig_marked = marker_mode;
return marker_mode;
}
return false;
} else { } else {
// next search for the designation priority // next search for the designation priority
for (df::block_square_event* event: block->block_events) { for (df::block_square_event* event: block->block_events) {
@ -73,7 +82,7 @@ bool ChannelManager::manage_one(const Group &group, const df::coord &map_pos, bo
if (evT->priority[Coord(local)] < 1000 * config.ignore_threshold) { if (evT->priority[Coord(local)] < 1000 * config.ignore_threshold) {
DEBUG(manager).print(" if(has_groups_above())\n"); DEBUG(manager).print(" if(has_groups_above())\n");
// check that the group has no incomplete groups directly above it // check that the group has no incomplete groups directly above it
if (has_group_above(groups, map_pos)) { if (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos)) {
DEBUG(manager).print(" has_groups_above: setting marker mode\n"); DEBUG(manager).print(" has_groups_above: setting marker mode\n");
tile_occupancy.bits.dig_marked = true; tile_occupancy.bits.dig_marked = true;
jobs.erase(map_pos); jobs.erase(map_pos);
@ -95,5 +104,4 @@ bool ChannelManager::manage_one(const Group &group, const df::coord &map_pos, bo
void ChannelManager::mark_done(const df::coord &map_pos) { void ChannelManager::mark_done(const df::coord &map_pos) {
groups.remove(map_pos); groups.remove(map_pos);
jobs.erase(map_pos); //redundant (repopulated on each build)
} }

@ -1,7 +1,7 @@
/* Prevent channeling down into known open space. /* Prevent channeling down into known open space.
Author: Josh Cooper Author: Josh Cooper
Created: Aug. 4 2020 Created: Aug. 4 2020
Updated: Nov. 1 2022 Updated: Nov. 6 2022
Enable plugin: Enable plugin:
-> build groups -> build groups
@ -13,9 +13,9 @@ Updated: Nov. 1 2022
Manage Designation(s): Manage Designation(s):
-> for each group in groups: -> for each group in groups:
-> for each designation in this group: -> does any tile in group have a group above
-> -> Yes: set entire group to marker mode
-> No: activate entire group (still checks is_safe_to_dig_down before activating each designation)
Job started event: Job started event:
-> validate job type (channel) -> validate job type (channel)
@ -150,7 +150,7 @@ namespace CSP {
INFO(monitor).print("JobStartedEvent()\n"); INFO(monitor).print("JobStartedEvent()\n");
auto job = (df::job*) p; auto job = (df::job*) p;
// validate job type // validate job type
if (is_dig_job(job)) { if (is_channel_job(job)) {
DEBUG(monitor).print(" valid channel job:\n"); DEBUG(monitor).print(" valid channel job:\n");
df::unit* worker = Job::getWorker(job); df::unit* worker = Job::getWorker(job);
// there is a valid worker (living citizen) on the job? right.. // there is a valid worker (living citizen) on the job? right..
@ -160,7 +160,7 @@ namespace CSP {
local.x = local.x % 16; local.x = local.x % 16;
local.y = local.y % 16; local.y = local.y % 16;
// check pathing exists to job // check pathing exists to job
if (Maps::canWalkBetween(worker->pos, job->pos)) { if (can_reach_designation(worker->pos, job->pos)) {
DEBUG(monitor).print(" can path from (" COORD ") to (" COORD ")\n", DEBUG(monitor).print(" can path from (" COORD ") to (" COORD ")\n",
COORDARGS(worker->pos), COORDARGS(job->pos)); COORDARGS(worker->pos), COORDARGS(job->pos));
// track workers on jobs // track workers on jobs
@ -246,6 +246,17 @@ namespace CSP {
last_refresh_tick = tick; last_refresh_tick = tick;
TRACE(monitor).print("OnUpdate()\n"); TRACE(monitor).print("OnUpdate()\n");
UnpauseEvent(); UnpauseEvent();
TRACE(monitor).print(" -> evaluate dignow queue\n");
for (const df::coord &pos: dignow_queue) {
if (!has_unit(Maps::getTileOccupancy(pos))) {
dig_now(out, pos);
} else {
// todo: teleport?
//Units::teleport()
}
}
TRACE(monitor).print("OnUpdate() exits\n");
} }
if (config.monitor_active && tick - last_monitor_tick >= config.monitor_freq) { if (config.monitor_active && tick - last_monitor_tick >= config.monitor_freq) {
last_monitor_tick = tick; last_monitor_tick = tick;
@ -299,16 +310,6 @@ namespace CSP {
} }
} }
} }
TRACE(monitor).print(" -> evaluate dignow queue\n");
for (const df::coord &pos: dignow_queue) {
if (!has_unit(Maps::getTileOccupancy(pos))) {
dig_now(out, pos);
} else {
// todo: teleport?
//Units::teleport()
}
}
TRACE(monitor).print("OnUpdate() exits\n");
} }
} }
} }

@ -17,16 +17,39 @@ namespace CSP {
extern std::unordered_set<df::coord> dignow_queue; extern std::unordered_set<df::coord> dignow_queue;
} }
inline void get_neighbours(const df::coord &map_pos, df::coord(&neighbours)[8]) {
neighbours[0] = map_pos;
neighbours[1] = map_pos;
neighbours[2] = map_pos;
neighbours[3] = map_pos;
neighbours[4] = map_pos;
neighbours[5] = map_pos;
neighbours[6] = map_pos;
neighbours[7] = map_pos;
neighbours[0].x--; neighbours[0].y--;
neighbours[1].y--;
neighbours[2].x++; neighbours[2].y--;
neighbours[3].x--;
neighbours[4].x++;
neighbours[5].x--; neighbours[5].y++;
neighbours[6].y++;
neighbours[7].x++; neighbours[7].y++;
}
inline bool is_dig_job(const df::job* job) { inline bool is_dig_job(const df::job* job) {
return job->job_type == df::job_type::Dig || job->job_type == df::job_type::DigChannel; return job->job_type == df::job_type::Dig || job->job_type == df::job_type::DigChannel;
} }
inline bool is_channel_job(const df::job* job) {
return job->job_type == df::job_type::DigChannel;
}
inline bool is_dig_designation(const df::tile_designation &designation) { inline bool is_dig_designation(const df::tile_designation &designation) {
return designation.bits.dig != df::tile_dig_designation::No; return designation.bits.dig != df::tile_dig_designation::No;
} }
inline bool has_unit(const df::tile_occupancy* occupancy) { inline bool is_channel_designation(const df::tile_designation &designation) {
return occupancy->bits.unit || occupancy->bits.unit_grounded; return designation.bits.dig != df::tile_dig_designation::Channel;
} }
inline bool is_safe_fall(const df::coord &map_pos) { inline bool is_safe_fall(const df::coord &map_pos) {
@ -65,11 +88,22 @@ inline bool is_safe_to_dig_down(const df::coord &map_pos) {
return false; return false;
} }
inline bool is_group_occupied(const ChannelGroups &groups, const Group &group) { inline bool can_reach_designation(const df::coord &start, const df::coord &end) {
// return true if any tile in the group is occupied by a unit if (!Maps::canWalkBetween(start,end)) {
return std::any_of(group.begin(), group.end(), [](const Group::key_type &pos){ df::coord neighbours[8];
return has_unit(Maps::getTileOccupancy(pos)); get_neighbours(end, neighbours);
}); for (auto &pos : neighbours) {
if (Maps::canWalkBetween(start, pos)) {
return true;
}
}
return false;
}
return true;
}
inline bool has_unit(const df::tile_occupancy* occupancy) {
return occupancy->bits.unit || occupancy->bits.unit_grounded;
} }
inline bool has_group_above(const ChannelGroups &groups, const df::coord &map_pos) { inline bool has_group_above(const ChannelGroups &groups, const df::coord &map_pos) {
@ -128,22 +162,3 @@ inline void cancel_job(df::job* job) {
Job::removeJob(job); Job::removeJob(job);
} }
} }
inline void get_neighbours(const df::coord &map_pos, df::coord(&neighbours)[8]) {
neighbours[0] = map_pos;
neighbours[1] = map_pos;
neighbours[2] = map_pos;
neighbours[3] = map_pos;
neighbours[4] = map_pos;
neighbours[5] = map_pos;
neighbours[6] = map_pos;
neighbours[7] = map_pos;
neighbours[0].x--; neighbours[0].y--;
neighbours[1].y--;
neighbours[2].x++; neighbours[2].y--;
neighbours[3].x--;
neighbours[4].x++;
neighbours[5].x--; neighbours[5].y++;
neighbours[6].y++;
neighbours[7].x++; neighbours[7].y++;
}

@ -14,8 +14,8 @@ struct Configuration {
bool require_vision = true; bool require_vision = true;
bool insta_dig = false; bool insta_dig = false;
int32_t refresh_freq = 600; int32_t refresh_freq = 600;
int32_t monitor_freq = 10; int32_t monitor_freq = 1;
uint8_t ignore_threshold = 7; uint8_t ignore_threshold = 5;
uint8_t fall_threshold = 1; uint8_t fall_threshold = 1;
}; };