#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
North,
East,
South,
West,
}
impl Direction {
pub fn turn_right(&self) -> Direction {
match self {
Direction::North => Direction::East,
Direction::East => Direction::South,
Direction::South => Direction::West,
Direction::West => Direction::North,
}
}
pub fn turn_left(&self) -> Direction {
match self {
Direction::North => Direction::West,
Direction::West => Direction::South,
Direction::South => Direction::East,
Direction::East => Direction::North,
}
}
pub fn delta(&self) -> (isize, isize) {
match self {
Direction::North => (0, -1),
Direction::East => (1, 0),
Direction::South => (0, 1),
Direction::West => (-1, 0),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ant {
pub x: isize,
pub y: isize,
pub direction: Direction,
}
impl Ant {
pub fn new(x: isize, y: isize) -> Self {
Self {
x,
y,
direction: Direction::North,
}
}
pub fn with_direction(x: isize, y: isize, direction: Direction) -> Self {
Self { x, y, direction }
}
pub fn move_forward(&mut self) {
let (dx, dy) = self.direction.delta();
self.x += dx;
self.y += dy;
}
}
#[derive(Debug, Clone)]
pub struct LangtonsAnt {
pub width: usize,
pub height: usize,
pub grid: Vec<u8>,
pub ant: Ant,
steps: usize,
pub num_colors: u8,
pub turn_rules: Vec<bool>,
}
impl LangtonsAnt {
pub fn new(width: usize, height: usize) -> Self {
let cx = (width / 2) as isize;
let cy = (height / 2) as isize;
Self {
width,
height,
grid: vec![0; width * height],
ant: Ant::new(cx, cy),
steps: 0,
num_colors: 2,
turn_rules: vec![true, false], }
}
pub fn new_with_rules(width: usize, height: usize, turn_rules: &str) -> Self {
let cx = (width / 2) as isize;
let cy = (height / 2) as isize;
let rules: Vec<bool> = turn_rules
.chars()
.map(|c| c == 'R' || c == 'r')
.collect();
Self {
width,
height,
grid: vec![0; width * height],
ant: Ant::new(cx, cy),
steps: 0,
num_colors: rules.len() as u8,
turn_rules: rules,
}
}
pub fn get(&self, x: isize, y: isize) -> u8 {
let x = ((x % self.width as isize) + self.width as isize) as usize % self.width;
let y = ((y % self.height as isize) + self.height as isize) as usize % self.height;
self.grid[y * self.width + x]
}
pub fn set(&mut self, x: isize, y: isize, state: u8) {
let x = ((x % self.width as isize) + self.width as isize) as usize % self.width;
let y = ((y % self.height as isize) + self.height as isize) as usize % self.height;
self.grid[y * self.width + x] = state;
}
pub fn step(&mut self) {
let color = self.get(self.ant.x, self.ant.y);
let rule_idx = color as usize % self.turn_rules.len();
if self.turn_rules[rule_idx] {
self.ant.direction = self.ant.direction.turn_right();
} else {
self.ant.direction = self.ant.direction.turn_left();
}
let new_color = (color + 1) % self.num_colors;
self.set(self.ant.x, self.ant.y, new_color);
self.ant.move_forward();
self.steps += 1;
}
pub fn step_n(&mut self, n: usize) {
for _ in 0..n {
self.step();
}
}
pub fn step_count(&self) -> usize {
self.steps
}
pub fn count_state(&self, state: u8) -> usize {
self.grid.iter().filter(|&&s| s == state).count()
}
pub fn to_string_visual(&self) -> String {
let mut s = String::new();
for y in 0..self.height {
for x in 0..self.width {
let ch = match self.grid[y * self.width + x] {
0 => '·',
1 => '█',
n => (b'0' + n - 2) as char,
};
s.push(ch);
}
if y + 1 < self.height {
s.push('\n');
}
}
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_direction_turns() {
assert_eq!(Direction::North.turn_right(), Direction::East);
assert_eq!(Direction::East.turn_right(), Direction::South);
assert_eq!(Direction::South.turn_right(), Direction::West);
assert_eq!(Direction::West.turn_right(), Direction::North);
assert_eq!(Direction::North.turn_left(), Direction::West);
assert_eq!(Direction::West.turn_left(), Direction::South);
assert_eq!(Direction::South.turn_left(), Direction::East);
assert_eq!(Direction::East.turn_left(), Direction::North);
}
#[test]
fn test_direction_full_rotation() {
let d = Direction::North;
let d = d.turn_right().turn_right().turn_right().turn_right();
assert_eq!(d, Direction::North);
}
#[test]
fn test_direction_deltas() {
assert_eq!(Direction::North.delta(), (0, -1));
assert_eq!(Direction::East.delta(), (1, 0));
assert_eq!(Direction::South.delta(), (0, 1));
assert_eq!(Direction::West.delta(), (-1, 0));
}
#[test]
fn test_ant_new() {
let ant = Ant::new(5, 5);
assert_eq!(ant.x, 5);
assert_eq!(ant.y, 5);
assert_eq!(ant.direction, Direction::North);
}
#[test]
fn test_ant_move_forward() {
let mut ant = Ant::new(5, 5);
ant.move_forward(); assert_eq!(ant.x, 5);
assert_eq!(ant.y, 4);
}
#[test]
fn test_ant_turn_and_move() {
let mut ant = Ant::new(5, 5);
ant.direction = ant.direction.turn_right(); ant.move_forward();
assert_eq!(ant.x, 6);
assert_eq!(ant.y, 5);
}
#[test]
fn test_langton_initial_state() {
let ant = LangtonsAnt::new(10, 10);
assert_eq!(ant.step_count(), 0);
assert_eq!(ant.ant.x, 5);
assert_eq!(ant.ant.y, 5);
assert!(ant.grid.iter().all(|&s| s == 0));
}
#[test]
fn test_langton_single_step() {
let mut la = LangtonsAnt::new(10, 10);
la.step();
assert_eq!(la.step_count(), 1);
assert_eq!(la.ant.direction, Direction::East);
assert_eq!(la.ant.x, 6);
assert_eq!(la.ant.y, 5);
assert_eq!(la.get(5, 5), 1);
}
#[test]
fn test_langton_two_steps() {
let mut la = LangtonsAnt::new(10, 10);
la.step(); la.step(); assert_eq!(la.ant.direction, Direction::South);
assert_eq!(la.ant.x, 6);
assert_eq!(la.ant.y, 6);
assert_eq!(la.get(5, 5), 1);
assert_eq!(la.get(6, 5), 1);
}
#[test]
fn test_langton_highway_emerges() {
let mut la = LangtonsAnt::new(100, 100);
la.step_n(11000);
let dx = (la.ant.x - 50).abs();
let dy = (la.ant.y - 50).abs();
assert!(dx > 5 || dy > 5, "Ant should have moved significantly after highway formation");
}
#[test]
fn test_langton_step_n() {
let mut la1 = LangtonsAnt::new(10, 10);
la1.step_n(5);
let mut la2 = LangtonsAnt::new(10, 10);
for _ in 0..5 {
la2.step();
}
assert_eq!(la1.grid, la2.grid);
assert_eq!(la1.ant.x, la2.ant.x);
assert_eq!(la1.ant.y, la2.ant.y);
}
#[test]
fn test_multi_color_variant() {
let mut la = LangtonsAnt::new_with_rules(20, 20, "RLR");
assert_eq!(la.num_colors, 3);
assert_eq!(la.turn_rules.len(), 3);
la.step_n(100);
assert!(la.count_state(0) < la.width * la.height);
}
#[test]
fn test_periodic_wrapping() {
let mut la = LangtonsAnt::new(3, 3);
la.ant.x = 0;
la.ant.y = 0;
la.ant.direction = Direction::West;
la.step(); assert_eq!(la.ant.y, -1);
assert_eq!(la.ant.direction, Direction::North);
}
#[test]
fn test_count_state() {
let mut la = LangtonsAnt::new(5, 5);
la.step_n(10);
assert_eq!(la.count_state(0) + la.count_state(1), 25);
}
#[test]
fn test_visual_string() {
let la = LangtonsAnt::new(3, 3);
let v = la.to_string_visual();
assert!(v.contains('·'));
}
}