diff --git a/egregoria/src/map/map.rs b/egregoria/src/map/map.rs index 37ab2211..1d7597f3 100644 --- a/egregoria/src/map/map.rs +++ b/egregoria/src/map/map.rs @@ -121,7 +121,10 @@ impl Map { interpoint: Option, pattern: &LanePattern, ) -> Option<(IntersectionID, RoadID)> { - if !from.kind.check_valid(self) || !to.kind.check_valid(self) { + if !from.kind.check_valid(self) + || !to.kind.check_valid(self) + || from.pos.distance(to.pos) < 1.0 + { return None; } diff --git a/egregoria/src/tests/test_iso.rs b/egregoria/src/tests/test_iso.rs index 08e11da2..87539d25 100644 --- a/egregoria/src/tests/test_iso.rs +++ b/egregoria/src/tests/test_iso.rs @@ -1,12 +1,13 @@ -use crate::engine_interaction::WorldCommand; use crate::init::init; -use crate::map::{Map, MapProject, ProjectKind}; +use crate::map::{LanePatternBuilder, Map, MapProject, ProjectKind}; use crate::utils::scheduler::SeqSchedule; use crate::utils::time::Tick; use crate::World; use crate::{Egregoria, Replay}; use common::logger::MyLog; -use common::saveload::Encoder; +use common::saveload::{Bincode, Encoder}; +use geom::vec3; +use quickcheck::{Arbitrary, Gen}; static REPLAY: &'static [u8] = include_bytes!("world_replay.json"); @@ -32,6 +33,195 @@ fn check_eq(w1: &World, w2: &World) -> bool { true } +#[derive(Debug, Copy, Clone)] +struct F3201(f32); + +impl Arbitrary for F3201 { + fn arbitrary(g: &mut Gen) -> Self { + let v = ::arbitrary(g); + F3201(v as f32 / u32::MAX as f32) + } +} + +#[derive(Debug, Copy, Clone)] +enum MapAction { + AddInter, + TwoInter, + RemoveRoad, + SplitRoad, + Serde, +} + +impl Arbitrary for MapAction { + fn arbitrary(g: &mut Gen) -> Self { + *g.choose(&[ + MapAction::AddInter, + MapAction::TwoInter, + MapAction::SplitRoad, + MapAction::Serde, + ]) + .unwrap() + } +} + +#[test] +fn quickcheck_map_ser() { + let mut q = quickcheck::QuickCheck::new(); + q.quickcheck( + (|vals: Vec<(MapAction, u32, F3201, F3201)>| -> bool { + let mut m = Map::empty(); + let mut m2 = Map::empty(); + + m.make_connection( + MapProject { + pos: vec3(0.0, 0.0, 0.0), + kind: ProjectKind::Ground, + }, + MapProject { + pos: vec3(30.0, 0.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + + m2.make_connection( + MapProject { + pos: vec3(0.0, 0.0, 0.0), + kind: ProjectKind::Ground, + }, + MapProject { + pos: vec3(30.0, 0.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + + for (action, r, x, y) in vals { + match action { + MapAction::AddInter => { + let i = m + .intersections + .iter() + .nth(r as usize % m.intersections.len()) + .unwrap() + .0; + m.make_connection( + MapProject { + pos: m.intersections[i].pos, + kind: ProjectKind::Inter(i), + }, + MapProject { + pos: vec3(x.0 * 500.0, y.0 * 500.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + m2.make_connection( + MapProject { + pos: m.intersections[i].pos, + kind: ProjectKind::Inter(i), + }, + MapProject { + pos: vec3(x.0 * 500.0, y.0 * 500.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + } + MapAction::TwoInter => { + let i1 = m + .intersections + .iter() + .nth(r as usize % m.intersections.len()) + .unwrap() + .0; + let i2 = m + .intersections + .iter() + .nth(r as usize % m.intersections.len()) + .unwrap() + .0; + if i1 != i2 { + m.make_connection( + MapProject { + pos: m.intersections[i1].pos, + kind: ProjectKind::Inter(i1), + }, + MapProject { + pos: m.intersections[i2].pos, + kind: ProjectKind::Inter(i2), + }, + None, + &LanePatternBuilder::new().build(), + ); + m2.make_connection( + MapProject { + pos: m2.intersections[i1].pos, + kind: ProjectKind::Inter(i1), + }, + MapProject { + pos: m2.intersections[i2].pos, + kind: ProjectKind::Inter(i2), + }, + None, + &LanePatternBuilder::new().build(), + ); + } + } + MapAction::RemoveRoad => { + if m.roads.len() > 3 { + // remove randomly + let r = m.roads.iter().nth(r as usize % m.roads.len()).unwrap().0; + + m.remove_road(r); + m2.remove_road(r); + } + } + MapAction::SplitRoad => { + let r = m.roads.iter().nth(r as usize % m.roads.len()).unwrap().0; + let p = m.roads[r].points.length(); + m.make_connection( + MapProject { + pos: m.roads[r].points.point_along(p * 0.5), + kind: ProjectKind::Road(r), + }, + MapProject { + pos: vec3(x.0 * 500.0, y.0 * 500.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + m2.make_connection( + MapProject { + pos: m2.roads[r].points.point_along(p * 0.5), + kind: ProjectKind::Road(r), + }, + MapProject { + pos: vec3(x.0 * 500.0, y.0 * 500.0, 0.0), + kind: ProjectKind::Ground, + }, + None, + &LanePatternBuilder::new().build(), + ); + } + MapAction::Serde => { + let v = Bincode::encode(&m).unwrap(); + let m3: Map = Bincode::decode(&v).unwrap(); + m = m3; + } + } + } + + Bincode::encode(&m).unwrap() == Bincode::encode(&m2).unwrap() + }) as fn(_) -> bool, + ); +} + //#[test] fn test_world_survives_serde() { init(); @@ -42,7 +232,7 @@ fn test_world_survives_serde() { let (mut goria2, mut loader2) = Egregoria::from_replay(replay); let mut s = SeqSchedule::default(); - let mut idx = 0; + //let mut idx = 0; while !loader.advance_tick(&mut goria, &mut s) { loader2.advance_tick(&mut goria2, &mut s);