Merge branch 'worktree-agent-ab151ac84f067999f' into revise/edge-cases-and-sources

This commit is contained in:
Lucas Winther committed 2026-11-11 09:19:32 +01:00
commit d5d742450c
28 files changed
+293 -32

No files matched your search

@@ -5,6 +5,8 @@
//! A move (Module 8) is planned for each `YOU` object, then committed; the rules are
//! reparsed once, after all of them.
use std::collections::BTreeSet;
use super::grid::{Board, Kind, LevelDef, ObjectId};
use super::movement::{Dir, Plan, Planner, Response};
use super::parse::{parse, Rule};
@@ -86,6 +88,10 @@ impl Level {
/// move one at a time, the one furthest ahead in `dir` first (oldest first on a
/// tie); each one's move is planned in full, then committed, before the next is
/// planned. Every move uses the rules in force before the first.
///
/// An object steps at most once in a movement phase. A `YOU` object that an
/// earlier move already carried along (pushed, or moved by any other response)
/// doesn't start a move of its own, and no later move can make it step again.
pub fn move_you(&mut self, dir: Dir) {
let (dx, dy) = dir.delta();
let mut you: Vec<_> = self
@@ -95,14 +101,19 @@ impl Level {
.map(|o| (-(o.x * dx + o.y * dy), o.id))
.collect();
you.sort();
let mut stepped = BTreeSet::new();
for (_, id) in you {
if self.board.find(id).is_none() {
continue; // destroyed by an earlier move
}
let mut planner = Planner::new(self, dir);
if stepped.contains(&id) {
continue; // already moved, by an earlier move
}
let mut planner = Planner::new(self, dir, &stepped);
if planner.try_move(id) {
let plan = planner.into_plan();
self.commit(&plan, dir);
stepped.extend(plan.moving.difference(&plan.destroyed));
}
}
self.reparse();
@@ -59,18 +59,22 @@ pub struct Plan {
pub destroyed: BTreeSet<ObjectId>,
}
/// Plans one move on a board it only reads.
/// Plans one move on a board it only reads. `stepped` holds the objects that have
/// already stepped in this movement phase, in an earlier committed plan: an object
/// steps at most once per phase, so none of them can step again.
pub struct Planner<'a> {
level: &'a Level,
dir: Dir,
stepped: &'a BTreeSet<ObjectId>,
plan: Plan,
}
impl<'a> Planner<'a> {
pub fn new(level: &'a Level, dir: Dir) -> Planner<'a> {
pub fn new(level: &'a Level, dir: Dir, stepped: &'a BTreeSet<ObjectId>) -> Planner<'a> {
Planner {
level,
dir,
stepped,
plan: Plan::default(),
}
}
@@ -100,11 +104,16 @@ impl<'a> Planner<'a> {
/// Plan object `id` stepping one tile in the move's direction, with whatever
/// the responses add. Returns whether it can. If it can't, the plan is left as
/// it was before the call. An object already planned to move, or to be
/// destroyed, is out of the way: the answer is yes, and nothing is added.
/// destroyed, is out of the way: the answer is yes, and nothing is added. An
/// object that has already stepped in this movement phase can't step again: the
/// answer is no.
pub fn try_move(&mut self, id: ObjectId) -> bool {
if self.plan.moving.contains(&id) || self.plan.destroyed.contains(&id) {
return true;
}
if self.stepped.contains(&id) {
return false;
}
let level = self.level;
let o = level.board().get(id);
let (dx, dy) = self.dir.delta();