Marks and dig selection in each master's colour; imps dig only their master's marks; AI marks near its ground, acts calmer, re-summons imps; allies never claim each other's ground

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-06 09:21:42 +01:00
1 parent 010a6a8a12
commit 773be21455
5 files changed
+161 -37

No files matched your search

+60 -14
View File
@@ -10,11 +10,12 @@ use crate::game::*;
use crate::map::*;
// per difficulty (easy, medium, hard)
const THINK: [f32; 3] = [4.0, 2.5, 1.5]; // seconds between two thoughts
const THINK: [f32; 3] = [5.2, 3.25, 1.95]; // seconds between two thoughts (a calm pace)
const MAX_RADIUS: [i32; 3] = [11, 15, 19]; // how far the dungeon grows around the heart
const MAX_IMPS: [usize; 3] = [6, 9, 12];
const FIRST_ATTACK: [f32; 3] = [720.0, 480.0, 330.0]; // no attack before (seconds)
const MAX_IMPS: [usize; 3] = [5, 7, 9];
const FIRST_ATTACK: [f32; 3] = [940.0, 625.0, 430.0]; // no attack before (seconds)
const BOLDNESS: [f32; 3] = [1.5, 1.15, 0.9]; // own army / enemy army needed to attack
const MARK_REACH: i32 = 5; // an AI master marks only this close to its own ground
const MAX_TOWERS: [usize; 3] = [1, 2, 4];
const START_GOLD: [f32; 3] = [1500.0, 2500.0, 3500.0];
@@ -55,11 +56,56 @@ impl Game {
self.ai_fight(k, d, side, cx, cy);
}
// the tiles an AI master may mark: within MARK_REACH tiles of its own ground
fn ai_reach_mask(&self) -> Vec<bool> {
let me = self.me;
let mut dist = vec![u8::MAX; self.map.t.len()];
let mut queue = std::collections::VecDeque::new();
for (i, t) in self.map.t.iter().enumerate() {
if t.owner == me {
dist[i] = 0;
queue.push_back(i);
}
}
while let Some(i) = queue.pop_front() {
let d = dist[i];
if d as i32 >= MARK_REACH {
continue;
}
let (x, y) = xy(i);
for &(dx, dy) in DIR8.iter() {
if inb(x + dx, y + dy) {
let j = idx(x + dx, y + dy);
if dist[j] > d + 1 {
dist[j] = d + 1;
queue.push_back(j);
}
}
}
}
dist.into_iter().map(|d| d as i32 <= MARK_REACH).collect()
}
// marks a tile for this master's imps (only near its own ground)
fn ai_mark(&mut self, i: usize, mask: &[bool]) -> bool {
if !mask.get(i).copied().unwrap_or(false) {
return false;
}
let side = self.me.side().min(7);
let t = &mut self.map.t[i];
if t.marked && t.mark_by != side {
return false; // another master's plan
}
t.marked = true;
t.mark_by = side;
true
}
// enough imps for the dungeon's size
fn ai_imps(&mut self, d: usize, cx: i32, cy: i32) {
let claimed = self.map.t.iter().filter(|t| t.owner == self.me).count();
let want = (4 + claimed / 30).min(MAX_IMPS[d]);
if self.worker_count() < want && self.mana >= self.summon_cost() + 20.0 {
if self.worker_count() < want && self.mana >= self.summon_cost() {
self.summon(cx as f32 + 0.5, cy as f32 + 2.5);
}
}
@@ -67,12 +113,13 @@ impl Game {
// dig: rings of earth around the heart, veins first; the ring grows when it is dug out
fn ai_dig(&mut self, k: usize, d: usize, cx: i32, cy: i32) {
let me = self.me;
let side = me.side().min(7);
let r = self.rivals[k].radius.max(5);
let marked_mine = |g: &Game| {
let mut n = 0;
for y in cy - r - 4..=cy + r + 4 {
for x in cx - r - 4..=cx + r + 4 {
if g.map.get(x, y).map_or(false, |t| t.marked) {
if g.map.get(x, y).map_or(false, |t| t.marked && t.mark_by == side) {
n += 1;
}
}
@@ -84,13 +131,14 @@ impl Game {
return; // the imps have enough to do
}
let mut added = 0;
let mask = self.ai_reach_mask();
let diggable = |t: &Tile| t.diggable() && !t.plant() && !t.reinforced && t.kind != Kind::Steel && (t.owner == Owner::None || t.owner == me);
// veins a little beyond the ring, then the ring itself
for pass in 0..2 {
let reach = if pass == 0 { r + 3 } else { r };
for y in cy - reach..=cy + reach {
for x in cx - reach..=cx + reach {
if added >= 16 || !inb(x, y) || x < 2 || y < 2 || x >= mw() as i32 - 2 || y >= mh() as i32 - 2 {
if added >= 11 || !inb(x, y) || x < 2 || y < 2 || x >= mw() as i32 - 2 || y >= mh() as i32 - 2 {
continue;
}
let i = idx(x, y);
@@ -98,9 +146,9 @@ impl Game {
if t.marked || !diggable(&t) || (pass == 0 && t.vein().is_none()) {
continue;
}
self.map.t[i].marked = true;
self.map.t[i].mark_by = self.me.side().min(7);
added += 1;
if self.ai_mark(i, &mask) {
added += 1;
}
}
}
}
@@ -146,12 +194,11 @@ impl Game {
};
let Some(path) = found else { return false };
let me = self.me;
let mask = self.ai_reach_mask();
let mut cluster = Vec::new();
for &i in &path {
let t = self.map.t[i];
if t.diggable() && !t.plant() && t.kind != Kind::Steel && (t.owner == Owner::None || t.owner == me || t.reinforced) {
self.map.t[i].marked = true;
self.map.t[i].mark_by = self.me.side().min(7);
if t.diggable() && !t.plant() && t.kind != Kind::Steel && (t.owner == Owner::None || t.owner == me || t.reinforced) && self.ai_mark(i, &mask) {
if t.vein().is_some() {
cluster.push(i);
}
@@ -163,8 +210,7 @@ impl Game {
for &(dx, dy) in DIR8.iter() {
if let Some(t) = self.map.get(x + dx, y + dy) {
if t.vein().is_some() && t.owner == Owner::None {
self.map.t[idx(x + dx, y + dy)].marked = true;
self.map.t[idx(x + dx, y + dy)].mark_by = self.me.side().min(7);
self.ai_mark(idx(x + dx, y + dy), &mask);
}
}
}