Переглянути джерело

WIP new coordinate system

Christoph Stelz 1 рік тому
батько
коміт
a67ebf0aad
2 змінених файлів з 295 додано та 352 видалено
  1. 283 273
      src/lib.rs
  2. 12 79
      src/main.rs

+ 283 - 273
src/lib.rs

@@ -1,158 +1,119 @@
 use std::collections::HashMap;
 
 
-/* 
- * The basic element is a single triangle.
- * It can be with a wide base (upright) or on its tip:
- *
- *      /\     +--------+
- *     /  \     \      /
- *    /    \     \    /
- *   /      \     \  /
- *  +--------+     \/
- *
- *  We number its edges accordingly:
- *                                       1
- *              /\                   +--------+
- *          3  /  \  2                \      /
- *            /    \                   \    /
- *           /      \               3   \  /  2
- *          +--------+                   \/
- *              1
- *
- *      counter-clockwise            clockwise
- *
- */
-
-#[derive(Debug,PartialEq,Copy,Clone)]
-pub enum Edge {
-    BOTTOMTOP, // 1
-    RIGHT,     // 2
-    LEFT       // 3
+#[derive(Clone,Debug,PartialEq)]
+pub struct Coord {
+    pub a: i8,
+    pub b: i8,
+    pub c: i8,
 }
 
+impl Coord {
 
-/* Numbers 1..7 are used to identify a given part */
-pub type PartID = u8;
+    fn from(a : i8, b: i8, c: i8) -> Coord {
+        Coord {
+            a: a,
+            b: b,
+            c: c
+        }
+    }
 
-/* Parts are described by a given upright triangle and a sequence of edges that are extended */
-pub type Part = Vec<Edge>;
+    /**
+     * Rotates coordinate around the origin by 120°
+     */
+    fn rotate(&self, rotations: u8) -> Coord {
+        match rotations % 3 {
+            0 => self.clone(),
+            1 => Coord::from(self.b, self.c, self.a),
+            _ => Coord::from(self.c, self.a, self.b),
+        }
+    }
 
 
 
-/* For example, the brown part (1) could be described by starting at the lower right triangle and
- * traversing edges in the following order:
- * 3, 3, 1, 1, 2, 1, 3
- *      ____
- *      \  /\
- *       \/__\
- *       /\  /\
- *      /__\/__\ <--
- *      \  /
- *       \/
- *
- *         (1)
- *
- * Rotations look as follows:
- *
- * 120 degrees.
- * Our edges are now 1, 2, 2, 3, 3, 3, 1
- *
- *
- *   --> /\
- *      /__\___
- *     /\  /\  /
- *    /__\/__\/
- *    \  /
- *     \/
- *
- * 240 degrees.
- *
- * 2, 1, 3, 2, 1, 2, 2
- *      ____
- *      \  /\
- *       \/__\____
- *       /\  /\  /
- *  --> /__\/__\/
- *
- *
- *  We observe the following principle:
- *
- *          0 deg      120deg       240deg
- *
- *        3 /\ 2      2 /\ 1       1 /\ 3
- *         /__\        /__\         /__\
- *          1           3            2
- *
- *          1            2           3
- *        __v_         __v_        __v_
- *        \  /         \  /        \  / 
- *       3 \/ 2       1 \/ 3      2 \/ 1
- *          
- *
- * For upright triangles the edges can be translated as follows: 1 => 2, 2 => 3, 3 => 1.
- *
- * For upside-down triangles, we need to take special care, since their order is specified 
- * clockwise, in order to keep or left-right-convention. Thus, the edge translation looks as
- * follows:  1 => 3, 2 => 1, 3 => 2
- *
- * In other words, for upright triangles we do a left-shift, on upside-down triangles we do
- * a right-shift.
- *
- */
 
-impl Edge {
-    fn from_int(x: i8) -> Edge {
-        match x {
-            0 => Edge::BOTTOMTOP,
-            1 => Edge::RIGHT,
-            2 => Edge::LEFT,
-            _ => panic!("Invalid argument"),
-        }
+    /**
+     * Flip coordinate at the y-axis
+     */
+    fn flip(&self) -> Coord {
+        Coord::from(self.c, self.b, self.a)
     }
 
-    fn to_int(&self) -> i8 {
-        match self {
-            Edge::BOTTOMTOP => 0,
-            Edge::RIGHT => 1,
-            Edge::LEFT => 2,
+    fn upright(&self) -> bool {
+        self.a + self.b + self.c == 2
+    }
+
+    fn right_neighbor(self) -> Coord {
+        if self.upright() {
+            return Coord::from(self.a, self.b, self.c - 1);
+        } else {
+            return Coord::from(self.a + 1, self.b, self.c);
         }
     }
 
-    fn rotate(&self, other: &Edge, upright: bool) -> Edge {
-        let shift_amount = other.to_int();
-        let shift_direction = if upright { -1 } else { 1 };
+    fn from_cartesian(x: i8, y: i8) -> Coord {
+        assert!(x >= 0 && y >= 0 && x < 63 && y < 63);
+        let upright : bool = (x + y) % 2 == 0;
+
+        let b : i8 = 1 - y;
+        let x_offset : i8 = (b - 1).abs() / 2;
+        let a : i8 = x_offset + x / 2;
+        let c : i8 = if upright { 2 - a - b } else {1 - a - b};
+
+        Coord::from(a,b,c)
+    }
+
+    fn to_cartesian(&self) -> (i8, i8) {
+        let y : i8 = 1 - self.b;
+        let x_offset : i8 = (self.b - 1).abs() / 2;
+        let x : i8 = 2 * (self.a - x_offset) + if self.upright() { y % 2 }  else { 1 - (y % 2) };
+
+
 
-        let shifted = (self.to_int() + shift_direction * shift_amount).rem_euclid(3);
+        return (x, y);
 
-        Edge::from_int(shifted)
     }
+}
+
+impl std::ops::Add<Coord> for Coord {
+    type Output = Coord;
+
+    fn add(self, _rhs: Coord) -> Coord {
+        return Coord {
+            a: self.a + _rhs.a,
+            b: self.b + _rhs.b,
+            c: self.c + _rhs.c
+        }
+    }
+}
+
+/* Numbers 1..7 are used to identify a given part */
+pub type PartID = u8;
+
+pub type Part = Vec<Coord>;
+
+/** Allow part definition with tuples for brevity
+ */
+fn generate_part(arr: &[(i8, i8, i8)]) -> Part {
+    let v: Vec<Coord> = arr.iter()
+        .map(|(a,b,c)| Coord::from(*a,*b,*c))
+        .collect();
 
+    return v;
 }
 
 pub fn generate_parts() -> HashMap<PartID, Part> {
     HashMap::from([
-        (1, vec![Edge::LEFT, Edge::LEFT, Edge::BOTTOMTOP, Edge::BOTTOMTOP, Edge::RIGHT, Edge::BOTTOMTOP, Edge::LEFT]),
-        (2, vec![Edge::RIGHT, Edge::BOTTOMTOP, Edge::RIGHT, Edge::RIGHT, Edge::BOTTOMTOP]),
-        (3, vec![Edge::RIGHT, Edge::BOTTOMTOP, Edge::LEFT, Edge::BOTTOMTOP, Edge::RIGHT]),
-        (4, vec![Edge::BOTTOMTOP, Edge::RIGHT, Edge::RIGHT, Edge::RIGHT, Edge::RIGHT]),
-        (5, vec![Edge::LEFT, Edge::BOTTOMTOP, Edge::LEFT, Edge::LEFT, Edge::LEFT]),
-        (6, vec![Edge::RIGHT, Edge::RIGHT, Edge::RIGHT, Edge::BOTTOMTOP, Edge::BOTTOMTOP, Edge::RIGHT]),
-        (7, vec![Edge::LEFT, Edge::LEFT, Edge::BOTTOMTOP, Edge::LEFT, Edge::RIGHT, Edge::RIGHT]),
+        (1, generate_part(&[(0,1,1), (0,0,1), (1,0,1), (1,0,0), (2,0,0), (1,-1,1)])),
+        (2, generate_part(&[(0,1,1), (0,1,0), (0,0,1), (1,0,1), (1,0,0)])),
+        (3, generate_part(&[(0,1,1), (0,0,1), (1,0,1), (1,0,0), (2,0,0), (2, -1, 0)])),
+        (4, generate_part(&[(0,1,1), (0,1,0), (1,1,0), (1,0,0), (2,0,0), (2,-1,0)])),
+        (5, generate_part(&[(0,1,1), (0,0,1), (1,0,1), (1,-1,1), (1,-1,2), (1,-2,3)])),
+        (6, generate_part(&[(0,1,1), (0,0,1), (1,0,1), (1,-1,1), (2,-1,1), (1,-1,2)])),
+        (7, generate_part(&[(0,1,1), (0,0,1), (1,0,1), (1,0,0), (1,1,0), (2,0,0)])),
     ])
 }
 
-/*
- * A map has two sides (left and right) and is not neccesarily convex.
- * We specify each side by a list of lines.
- * Each line is represented by yet another list of skip entries:
- * a two-tuple, where the first number designates the cells that are "skipped" (a.k.a inaccessible)
- * and the second number which specifies the number of valid fields.
- */
-pub type SkipEntryIO = (u8, u8);
-pub type MapSideIO = Vec<Vec<SkipEntryIO>>;
-pub type MapIO = (MapSideIO, MapSideIO);
-
 /*
  * For in-memory representation however, we simply use a two-dimensional array of cells.
  * Each cell can be a Barrier (meaning no part may be placed on the cell), Empty (meaning at the
@@ -168,12 +129,9 @@ pub enum Cell {
 impl Cell {
     fn is_empty(&self) -> bool {
         match self {
-            Cell::Empty => {
-                return true;
-            },
-            _ => {
-                return false;
-            }
+            Cell::Empty => true,
+            Cell::Barrier => false,
+            Cell::Occupied(_) => false,
         }
     }
 }
@@ -205,148 +163,124 @@ impl std::fmt::Display for Cell {
  * size of 16x16.
  * Since there are two sides, we store both of them in a tuple.
  */
-pub const MAP_WIDTH : u8 = 16;
-pub const MAP_HEIGHT : u8 = 5;
+pub const MAP_WIDTH : i8 = 16;
+pub const MAP_HEIGHT : i8 = 5;
 pub type Map = [[Cell; MAP_WIDTH as usize]; MAP_HEIGHT as usize];
 
 
-/**
- * Helper function that tells us whether a given coordinate is an upright or upside-down triangle
- */
-pub fn is_upright(x: u8, y: u8) -> bool {
-    if y % 2 == 0 {
-        /* On even rows, even columns have upright triangles */
-        return x % 2 == 0;
-    } else {
-        /* On odd rows, odd columns have upright triangles */
-        return x % 2 == 1;
-    }
-}
-
 /**
  * Helper function that returns true if the coordinates are within map bounds
  */
-pub fn in_bound(x: u8, y: u8) -> bool {
-    x < MAP_WIDTH && y < MAP_HEIGHT
+pub fn in_bound(x: i8, y: i8) -> bool {
+    x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT
 }
 
+
 /**
- * Moves along an edge and returns a new coordinate pair.
- * If the target would be out of bounds, it returns None.
+ * Call f for each triangle of the part at least. If f returns false, iteration will stop and the
+ * function will return false. Only returns true if all calls of r returned true.
  */
-pub fn move_along_edge(x: u8, y: u8, edge: Edge) -> Option<(u8, u8)> {
-    match edge {
-        Edge::LEFT => {
-            if x == 0 {
-                return None;
-            }
-
-            return Some((x - 1, y));
-        },
-        Edge::RIGHT => {
-            if x == MAP_WIDTH - 1 {
-                return None;
-            }
+pub fn for_each_triangle<F>(part: &Part, x: i8, y: i8, rotations: u8, flipped: bool, mut f: F) -> bool
+where F: FnMut(i8, i8) -> bool
+{
+    for coordinate in part {
+        let mut coord : Coord = if flipped { coordinate.flip() } else { coordinate.clone() };
+        coord = coord.rotate(rotations);
 
-            return Some((x + 1, y));
-        },
-        Edge::BOTTOMTOP => {
-            if (is_upright(x, y) && y == MAP_HEIGHT - 1) 
-                || (!is_upright(x,y) && y == 0) {
-                return None;
-            }
+        let world_pos = Coord::from_cartesian(x,y) + coord;
+        let (wx, wy) = world_pos.to_cartesian();
 
-            if is_upright(x,y) {
-                return Some((x, y + 1));
-            } else {
-                return Some((x, y - 1));
-            }
+        if in_bound(wx, wy) == false {
+            return false;
+        }
 
+        if f(wx, wy) == false {
+            return false;
         }
     }
+
+
+    true
 }
 
 
 /*
  * Parts can be inverted (flipped on the other side) and also rotated.
  * On a triangular grid, three rotations are possible.
- * We represent rotations by specifying which edge the base of the given part ends up at.
  * 
  * The task of our core logic function below is to figure out whether a given part can fit on a
  * specified location on the map.
  *
  * Returns true if the part can fit at the specified location on the map.
  */
-pub fn check_part(map: &Map, part: &Part, x: u8, y: u8, rotation: Edge) -> bool {
+pub fn check_part(map: &Map, part: &Part, x: i8, y: i8, rotations: u8, flipped: bool) -> bool {
     /* Make sure the start triangle can actually be placed */
     assert!(in_bound(x, y));
 
-    if !map[y as usize][x as usize].is_empty() {
-        return false;
-    }
-
-    let mut mx : u8 = x;
-    let mut my : u8 = y;
-
-    for movement in part {
-        let rotated_movement = movement.rotate(&rotation, is_upright(x, y));
-        let result = move_along_edge(mx, my, rotated_movement);
-
-        if result.is_none() {
+    //println!("Testing {},{},{},{}", x, y, rotation.to_int(), flipped);
+    let success = for_each_triangle(part, x, y, rotations, flipped, |x, y| {
+        if !in_bound(x,y) {
             return false;
         }
 
-        (mx, my) = result.unwrap();
-
-        if !map[my as usize][mx as usize].is_empty() || !in_bound(mx, my) {
+        let prev = map[y as usize][x as usize];
+        if !(prev.is_empty()) {
             return false;
         }
-    }
 
-    return true;
-}
+        //println!("Check for {},{} passed", x, y);
 
-/* Once we have found a valid position with `check_part`, we can place the part on the map.
- */
-pub fn place_part(map: &mut Map, id: PartID, part: &Part, x: u8, y: u8, rotation: Edge) {
-    map[y as usize][x as usize] = Cell::Occupied(id);
-
-    let mut mx : u8 = x;
-    let mut my : u8 = y;
-
-    for movement in part {
-        let rotated_movement = movement.rotate(&rotation, is_upright(x, y));
-        (mx, my) = move_along_edge(mx, my, rotated_movement).unwrap();
-        let prev = map[my as usize][mx as usize];
-        assert!(prev == Cell::Empty || prev == Cell::Occupied(id));
-        map[my as usize][mx as usize] = Cell::Occupied(id);
+        return true;
+    });
+    if success == true {
+        //println!("Accepted.");
+        return true;
+    } else {
+        return false;
     }
-
 }
 
-/* If a part is not placed right, we might need to remove it from the map again
- */
-fn erase_part(map: &mut Map, part: &Part, x: u8, y: u8, rotation: Edge) {
-    map[y as usize][x as usize] = Cell::Empty;
+///* Once we have found a valid position with `check_part`, we can place the part on the map.
+// */
+pub fn place_part(map: &mut Map, id: PartID, part: &Part, x: i8, y: i8, rotations: u8, flipped: bool) {
+    //println!("Placing {},{},{},{}", x, y, rotation.to_int(), flipped);
+    for_each_triangle(part, x, y, rotations, flipped, |x, y| {
+        if !in_bound(x,y) {
+            panic!();
+        }
+        //println!("placing {},{}", x, y);
 
-    let mut mx : u8 = x;
-    let mut my : u8 = y;
+        let prev = map[y as usize][x as usize];
+        if let Cell::Occupied(i) = prev {
+            assert_eq!(id, i);
+        } else {
+            assert!(prev.is_empty());
+        }
 
-    for movement in part {
-        let rotated_movement = movement.rotate(&rotation, is_upright(x, y));
-        (mx, my) = move_along_edge(mx, my, rotated_movement).unwrap();
-        map[my as usize][mx as usize] = Cell::Empty;
-    }
+        map[y as usize][x as usize] = Cell::Occupied(id);
 
+        return true;
+    });
 }
 
-pub fn solve(map: &mut Map, parts: &[(PartID, &Part)]) -> Option<()>{
+///* If a part is not placed right, we might need to remove it from the map again
+// */
+//fn erase_part(map: &mut Map, part: &Part, x: u8, y: u8, rotation: Edge, flipped: bool) {
+//    for_each_triangle(part, x, y, rotation, flipped, |x, y| {
+//        assert!(in_bound(x, y));
+//
+//        map[y as usize][x as usize] = Cell::Empty;
+//        true
+//    });
+//}
+//
+pub fn solve(map: Map, parts: &[(PartID, &Part)]) -> Option<()>{
     if parts.len() == 0 {
         return Some(());
     }
 
     let (id, part) = parts[0];
-
+    println!("{} parts remaining", parts.len());
 
     // TODO: restrict to list of non-barrier position to tighten the for loops
     for x in 0..MAP_WIDTH {
@@ -355,18 +289,21 @@ pub fn solve(map: &mut Map, parts: &[(PartID, &Part)]) -> Option<()>{
                 continue;
             }
 
-            for rotation in [Edge::BOTTOMTOP, Edge::LEFT, Edge::RIGHT] {
-                let valid_position = check_part(&map, &part, x, y, rotation);
-
-                if valid_position {
-                    // Place our part and try the others
-                    place_part(map, id, &part, x, y, rotation);
-                    if let Some(()) = solve(map, &parts[1..]) {
-                        // We have found a valid solution we can pass back!
-                        return Some(());
-                    } else {
-                        // Remove the part from our position and try again
-                        erase_part(map, &part, x, y, rotation);
+            for rotation in [0, 1, 2] {
+                for flipped in [true, false] {
+                    println!("Testing rotation {} and flipped={}", rotation, flipped);
+                    let valid_position = check_part(&map, &part, x, y, rotation, flipped);
+
+                    if valid_position {
+                        // Place our part and try the others
+                        let mut map_copy = map.clone();
+                        place_part(&mut map_copy, id, &part, x, y, rotation, flipped);
+                        print_map(&map_copy);
+                        println!("\n");
+                        if let Some(()) = solve(map_copy, &parts[1..]) {
+                            // We have found a valid solution we can pass back!
+                            return Some(());
+                        }
                     }
                 }
             }
@@ -376,53 +313,102 @@ pub fn solve(map: &mut Map, parts: &[(PartID, &Part)]) -> Option<()>{
     None
 }
 
+pub fn foreground_color(idx: u8) {
+    print!("\x1B[38;5;{}m", idx);
+}
+
+pub fn background_color(idx: u8) {
+    print!("\x1B[48;5;{}m", idx);
+}
+
+fn switch_color_to_cell(cell: Cell)  {
+    match cell {
+        Cell::Barrier => foreground_color(0),
+        Cell::Empty => foreground_color(15),
+        Cell::Occupied(1) => foreground_color(94),
+        Cell::Occupied(2) => foreground_color(89),
+        Cell::Occupied(3) => foreground_color(202),
+        Cell::Occupied(4) => foreground_color(220),
+        Cell::Occupied(5) => foreground_color(22),
+        Cell::Occupied(6) => foreground_color(196),
+        Cell::Occupied(7) => foreground_color(27),
+        _ => {}
+    }
+}
+
+//
+//  |O###O###O###
+//  OOO#OOO#OOO#
+//  ###O###O###O
+//  |#OOO#OOO#OOO
+pub fn print_map(m: &Map) {
+    for y in 0..MAP_HEIGHT {
+        if y % 2 == 0 {
+            switch_color_to_cell(Cell::Barrier);
+            print!("█");
+        }
+        for x in 0..MAP_WIDTH {
+            switch_color_to_cell(m[y as usize][x as usize]);
+
+            if (x + y) % 2 == 0 {
+                print!("o");
+            } else {
+                print!("###");
+            }
+        }
+        
+        switch_color_to_cell(Cell::Barrier);
+        println!("");
+
+        if y % 2 == 1 {
+            switch_color_to_cell(Cell::Barrier);
+            print!("█");
+        }
+        for x in 0..MAP_WIDTH {
+            switch_color_to_cell(m[y as usize][x as usize]);
+            if (x + y) % 2 == 1 {
+                print!("#");
+            } else {
+                print!("ooo");
+            }
+        }
+        println!("");
+
+    }
+    foreground_color(15);
+    background_color(0);
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
 
     #[test]
-    fn test_is_upright() {
-        for (x, y) in [(0,0), (1, 1), (2,0)] {
-            assert!(is_upright(x, y));
-        }
-
-        for (x, y) in [(0, 1), (0,3), (2,1)] {
-            assert!(!is_upright(x, y));
-        }
+    fn test_from_cartesian_coords() {
+        assert_eq!(Coord::from(0, 1, 1), Coord::from_cartesian(0,0));
+        assert_eq!(Coord::from(5, -1, -2), Coord::from_cartesian(8, 2));
+        assert_eq!(Coord::from(2, -1, 0), Coord::from_cartesian(3, 2));
+        assert_eq!(Coord::from(3,0,-2), Coord::from_cartesian(6, 1));
     }
 
     #[test]
-    fn test_edge_movement() {
-        let (x1, y1) = move_along_edge(0, 0, Edge::RIGHT).unwrap();
-        assert_eq!((1, 0), (x1, y1));
+    fn test_to_cartesian_coords() {
+        assert_eq!((0,0), Coord::from(0,1,1).to_cartesian());
+        assert_eq!((1,0), Coord::from(0,1,0).to_cartesian());
+        assert_eq!((2,0), Coord::from(1,1,0).to_cartesian());
+        assert_eq!((0,1), Coord::from(0,0,1).to_cartesian());
 
-        assert!(move_along_edge(15, 0, Edge::RIGHT).is_none());
-        assert!(move_along_edge(0, 1, Edge::LEFT).is_none());
+        assert_eq!((0,-1), Coord::from(-1, 2, 0).to_cartesian());
 
-        let (x2, y2) = move_along_edge(2, 1, Edge::BOTTOMTOP).unwrap();
-        assert_eq!((2, 0), (x2, y2));
-        
-    }
+        for x in 0..63 {
+            for y in 0..63 {
+                assert_eq!((x,y), Coord::from_cartesian(x,y).to_cartesian());
 
-    #[test]
-    fn test_rotation() {
-        // 120 deg counter-clockwise rotation
-        for (expected, original, upright) in [
-            (3, 1, true), (1, 2, true), (2, 3, true),
-            (2, 1, false), (1, 3, false), (3, 2, false)] {
-            assert_eq!(Edge::from_int(expected - 1), Edge::from_int(original - 1)
-                       .rotate(&Edge::RIGHT, upright));
+            }
         }
 
-        // 240 deg counter-clockwise rotation
-        for (expected, original, upright) in [
-            (3, 2, true), (1, 3, true), (2, 1, true),
-            (2, 3, false), (1, 2, false), (3, 1, false)] {
-            assert_eq!(Edge::from_int(expected - 1), Edge::from_int(original - 1)
-                       .rotate(&Edge::LEFT, upright));
-        }
     }
-
+    /*
     #[test]
     fn test_part_placement() {
         let B = Cell::Barrier;
@@ -440,12 +426,12 @@ mod test {
         let brown_part = parts.get(&1).unwrap();
 
 
-        assert!(check_part(&map, &brown_part, 4, 2, Edge::BOTTOMTOP));
+        assert!(check_part(&map, &brown_part, 4, 2, Edge::BOTTOMTOP, false));
         let mut called : usize = 0;
         for x in 0..MAP_WIDTH {
             for y in 0..MAP_HEIGHT {
                 for rotation in [Edge::LEFT, Edge::RIGHT, Edge::BOTTOMTOP] {
-                    if check_part(&map, &brown_part, x, y, rotation) {
+                    if check_part(&map, &brown_part, x, y, rotation, false) {
                         assert_eq!(Edge::BOTTOMTOP, rotation);
                         assert_eq!((4, 2), (x, y));
                         called += 1;
@@ -457,6 +443,29 @@ mod test {
 
     }
 
+
+    #[test]
+    fn test_erasing() {
+        let E = Cell::Empty;
+        let map : Map = [
+            [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+            [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+            [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+            [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+            [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+        ];
+        let parts = generate_parts();
+        let mut cmap = map.clone();
+
+        place_part(&mut cmap, 1, &parts[&1], 4, 4, Edge::BOTTOMTOP, true);
+        assert_ne!(map, cmap);
+        erase_part(&mut cmap, &parts[&1], 4, 4, Edge::BOTTOMTOP, true);
+
+        assert_eq!(map, cmap);
+
+
+    }
+
     #[test]
     fn halting_problem() {
         let B = Cell::Barrier;
@@ -490,4 +499,5 @@ mod test {
 
 
     }
+*/
 }

+ 12 - 79
src/main.rs

@@ -1,84 +1,17 @@
 use UbongoTrigoSolver::*;
-fn foreground_color(idx: u8) {
-    print!("\x1B[38;5;{}m", idx);
-}
-
-fn background_color(idx: u8) {
-    print!("\x1B[48;5;{}m", idx);
-}
-
-mod lib;
-
-fn switch_color_to_cell(cell: Cell)  {
-    match cell {
-        Cell::Barrier => foreground_color(0),
-        Cell::Empty => foreground_color(15),
-        Cell::Occupied(1) => foreground_color(94),
-        Cell::Occupied(2) => foreground_color(89),
-        Cell::Occupied(3) => foreground_color(202),
-        Cell::Occupied(4) => foreground_color(220),
-        Cell::Occupied(5) => foreground_color(22),
-        Cell::Occupied(6) => foreground_color(196),
-        Cell::Occupied(7) => foreground_color(27),
-        _ => {}
-    }
-}
-
-//
-//  |O###O###O###
-//  OOO#OOO#OOO#
-//  ###O###O###O
-//  |#OOO#OOO#OOO
-fn print_map(m: &Map) {
-    for y in 0..MAP_HEIGHT {
-        if y % 2 == 0 {
-            switch_color_to_cell(Cell::Barrier);
-            print!("█");
-        }
-        for x in 0..MAP_WIDTH {
-            switch_color_to_cell(m[y as usize][x as usize]);
-
-            if (x + y) % 2 == 0 {
-                print!("o");
-            } else {
-                print!("###");
-            }
-        }
-        
-        switch_color_to_cell(Cell::Barrier);
-        println!("");
-
-        if y % 2 == 1 {
-            switch_color_to_cell(Cell::Barrier);
-            print!("█");
-        }
-        for x in 0..MAP_WIDTH {
-            switch_color_to_cell(m[y as usize][x as usize]);
-            if (x + y) % 2 == 1 {
-                print!("#");
-            } else {
-                print!("ooo");
-            }
-        }
-        println!("");
-
-    }
-
-}
-
 
 fn main() {
-    foreground_color(0); // black
-    background_color(0); // white
+    //foreground_color(0); // black
+    //background_color(16); // white
     let B = Cell::Barrier;
     let E = Cell::Empty;
 
     let mut map : Map = [
-        [B, B, E, E, E, B, E, E, E, B, B, B, B, B, B, B],
-        [B, E, E, E, E, B, B, E, E, E, B, B, B, B, B, B],
-        [E, E, E, E, E, B, E, E, E, E, E, E, B, B, B, B],
-        [E, E, E, B, E, B, E, E, E, E, E, B, B, B, B, B],
-        [B, B, B, B, B, B, B, B, B, E, B, B, B, B, B, B],
+        [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+        [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+        [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+        [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
+        [E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E],
     ];
     let parts = generate_parts();
 
@@ -87,12 +20,12 @@ fn main() {
         .collect();
 
 
-    //let (_, part1) = available_parts[0];
-    //place_part(&mut map, 1, &part1, 3, 2, Edge::BOTTOMTOP);
-    //print_map(&map);
-    let result = solve(&mut map, &available_parts[1..]);
-    assert!(result.is_some());
+    let (_, part1) = available_parts[0];
+    place_part(&mut map, 1, &parts[&1], 3, 3, 0, false);
     print_map(&map);
+    //let result = solve(map, &available_parts[1..]);
+    //assert!(result.is_some());
+    //print_map(&map);
 
 
 }