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:
1 parent
010a6a8a12
commit
773be21455
5 files changed
+161
-37
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user