Updated point representation to better match my heavy bitfield usage
authorJustin Worthe <justin@worthe-it.co.za>
Thu, 9 Aug 2018 20:08:16 +0000 (22:08 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Thu, 9 Aug 2018 20:08:16 +0000 (22:08 +0200)
src/engine/bitwise_engine.rs
src/engine/command.rs
src/engine/geometry.rs
src/input/json.rs
tests/live_comparison.rs

index 40a8562..0cfd5fc 100644 (file)
@@ -85,13 +85,13 @@ impl BitwiseGameState {
     pub fn unoccupied_opponent_cell_count(&self) -> usize { self.opponent_buildings.occupied.count_zeros() as usize }
     pub fn location_of_unoccupied_player_cell(&self, i: usize) -> Point  {
         let bit = find_bit_index_from_rank(self.player_buildings.occupied, i as u64);
-        let point = Point::new(bit%SINGLE_MAP_WIDTH, bit/SINGLE_MAP_WIDTH);
+        let point = Point { index: bit };
         debug_assert!(point.to_either_bitfield() & self.player_buildings.occupied == 0);
         point
     }
     pub fn location_of_unoccupied_opponent_cell(&self, i: usize) -> Point {
         let bit = find_bit_index_from_rank(self.opponent_buildings.occupied, i as u64);
-        let point = Point::new(FULL_MAP_WIDTH - bit%SINGLE_MAP_WIDTH - 1, bit/SINGLE_MAP_WIDTH);
+        let point = Point { index: bit };
         debug_assert!(point.to_either_bitfield() & self.opponent_buildings.occupied == 0);
         point
     }
@@ -222,7 +222,7 @@ impl BitwiseGameState {
                 // This is used internally. I should not be making
                 // invalid moves!
                 debug_assert!(player_buildings.buildings[0] & bitfield == 0);
-                debug_assert!(p.x < FULL_MAP_WIDTH && p.y < MAP_HEIGHT);
+                debug_assert!(p.x() < FULL_MAP_WIDTH && p.y() < MAP_HEIGHT);
                 debug_assert!(player.energy >= price);
                 debug_assert!(b != BuildingType::Tesla ||
                               player_buildings.count_teslas() < TESLA_MAX);
@@ -314,20 +314,20 @@ impl BitwiseGameState {
                 player.energy -= TESLA_FIRING_ENERGY;
                 tesla.cooldown = TESLA_COOLDOWN;
 
-                let flipped_pos = tesla.pos.flip_x();
-
-                if flipped_pos.x >= SINGLE_MAP_WIDTH - 1 {
+                if tesla.pos.to_either_bitfield() & RIGHT_COL_MASK != 0 {
                     opponent.health = opponent.health.saturating_sub(TESLA_DAMAGE);
                 }
 
-                let missed_cells = ((SINGLE_MAP_WIDTH - flipped_pos.x) as u32).saturating_sub(2);
+                let x = tesla.pos.x();
+                let y = tesla.pos.y();
+                let missed_cells = ((SINGLE_MAP_WIDTH - x) as u32).saturating_sub(2);
                 
-                let top_row = if tesla.pos.y == 0 { 0 } else { tesla.pos.y - 1 };
+                let top_row = y.saturating_sub(1);
                 let top_row_mask = 255u64 << (top_row * SINGLE_MAP_WIDTH);
                 let mut destroy_mask = top_row_mask.wrapping_shl(missed_cells) & top_row_mask;
 
                 let mut hits = 0;
-                for _ in 0..(if tesla.pos.y == 0 || tesla.pos.y == MAP_HEIGHT-1 { 2 } else { 3 }) {
+                for _ in 0..(if y == 0 || y == MAP_HEIGHT-1 { 2 } else { 3 }) {
                     hits |= destroy_mask & opponent_buildings.buildings[0];
                     destroy_mask &= !hits;
                     destroy_mask = destroy_mask << SINGLE_MAP_WIDTH;
index 3ea0fbe..da7e1e0 100644 (file)
@@ -12,8 +12,8 @@ impl fmt::Display for Command {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             Command::Nothing => write!(f, ""),
-            Command::Build(p, b) => write!(f, "{},{},{}", p.x, p.y, b as u8),
-            Command::Deconstruct(p) => write!(f, "{},{},3", p.x, p.y),
+            Command::Build(p, b) => write!(f, "{},{},{}", p.x(), p.y(), b as u8),
+            Command::Deconstruct(p) => write!(f, "{},{},3", p.x(), p.y()),
         }
     }
 }
index de9d95a..bfa59da 100644 (file)
@@ -5,45 +5,36 @@ use engine::constants::*;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct Point {
-    pub x: u8,
-    pub y: u8
+    pub index: u8
 }
 
 impl Point {
     pub fn new(x: u8, y: u8) -> Point {
-        Point { x, y }
-    }
-    pub fn move_left(&self) -> Option<Point> {
-        self.x.checked_sub(1).map(|x| Point {
-            x,
-            ..*self
-        })
-    }
-    pub fn move_right(&self, size: &Point) -> Option<Point> {
-        if self.x + 1 >= size.x {
-            None
+        let flipped_x = if x >= SINGLE_MAP_WIDTH {
+            FULL_MAP_WIDTH - x - 1
         } else {
-            Some(Point {
-                x: self.x + 1,
-                ..*self
-            })
+            x
+        };
+        Point {
+            index: y * SINGLE_MAP_WIDTH + flipped_x
         }
     }
 
-    pub fn wrapping_move_left(&mut self) {
-        self.x = self.x.wrapping_sub(1);
+    pub fn new_double_bitfield(x: u8, y: u8, is_left_player: bool) -> (u64, u64) {
+        let bitfield = Point::new(x, y).to_either_bitfield();
+        if (x >= SINGLE_MAP_WIDTH) == is_left_player {
+            (0, bitfield)
+        } else {
+            (bitfield, 0)
+        }
     }
-    pub fn wrapping_move_right(&mut self) {
-        self.x = self.x.wrapping_add(1);
+
+    pub fn x(&self) -> u8 {
+        self.index % SINGLE_MAP_WIDTH
     }
 
-    pub fn flip_x(&self) -> Point {
-        let flipped_x = if self.x >= SINGLE_MAP_WIDTH {
-            FULL_MAP_WIDTH - self.x - 1
-        } else {
-            self.x
-        };
-        Point::new(flipped_x, self.y)
+    pub fn y(&self) -> u8 {
+        self.index / SINGLE_MAP_WIDTH
     }
 }
 
@@ -57,31 +48,8 @@ impl Point {
      * This involves mirroring the x dimension for the opponent's side
      */
 
-
-    pub fn to_bitfield(&self) -> (u64, u64) {
-        (self.to_left_bitfield(), self.to_right_bitfield())
-    }
-    
-    pub fn to_left_bitfield(&self) -> u64 {
-        if self.x >= SINGLE_MAP_WIDTH {
-            0
-        } else {
-            let index = self.y * SINGLE_MAP_WIDTH + self.x;
-            1 << index
-        }
-    }
-
-    pub fn to_right_bitfield(&self) -> u64 {
-        if self.x < SINGLE_MAP_WIDTH {
-            0
-        } else {
-            let index = self.y * SINGLE_MAP_WIDTH + FULL_MAP_WIDTH - self.x - 1;
-            1 << index
-        }
-    }
-
     pub fn to_either_bitfield(&self) -> u64 {
-        self.to_left_bitfield() | self.to_right_bitfield()
+        1u64 << self.index
     }
 }
 
@@ -95,8 +63,6 @@ impl PartialOrd for Point {
 }
 impl Ord for Point {
     fn cmp(&self, other: &Point) -> Ordering {
-        let a = self.flip_x();
-        let b = other.flip_x();
-        a.y.cmp(&b.y).then(a.x.cmp(&b.x))
+        self.index.cmp(&other.index)
     }
 }
index 3927a96..4aebcfa 100644 (file)
@@ -73,11 +73,12 @@ impl State {
                 for building in &cell.buildings {
                     let building_type = building.convert_building_type();
                     
-                    let (mut bitwise_buildings, bitfield) = if building.player_type == 'A' {
-                        (&mut player_buildings, point.to_left_bitfield())
+                    let mut bitwise_buildings = if building.player_type == 'A' {
+                        &mut player_buildings
                     } else {
-                        (&mut opponent_buildings, point.to_right_bitfield())
+                        &mut opponent_buildings
                     };
+                    let bitfield = point.to_either_bitfield();
 
                     bitwise_buildings.occupied |= bitfield;
                     if building.construction_time_left >= 0 {
@@ -108,11 +109,11 @@ impl State {
                     }
                 }
                 for missile in &cell.missiles {
-                    let bitfields = point.to_bitfield();
-                    let (mut bitwise_buildings, mut left, mut right) = if missile.player_type == 'A' {
-                        (&mut player_buildings, bitfields.0, bitfields.1)
+                    let (mut left, mut right) = engine::geometry::Point::new_double_bitfield(cell.x, cell.y, missile.player_type == 'A');
+                    let mut bitwise_buildings = if missile.player_type == 'A' {
+                        &mut player_buildings
                     } else {
-                        (&mut opponent_buildings, bitfields.1, bitfields.0)
+                        &mut opponent_buildings
                     };
 
                     for mut tier in bitwise_buildings.missiles.iter_mut() {
index 5761454..3297fa8 100644 (file)
@@ -3,7 +3,6 @@ extern crate zombot;
 use zombot::input::json;
 use zombot::engine::command::{Command, BuildingType};
 use zombot::engine::geometry::Point;
-use zombot::engine::constants::*;
 
 use std::fs::File;
 use std::io::prelude::*;
@@ -56,16 +55,5 @@ fn read_player_command(filename: &str) -> Command {
 }
 
 fn read_opponent_command(filename: &str) -> Command {
-    match read_player_command(filename) {
-        Command::Nothing => Command::Nothing,
-        Command::Build(p, b) => Command::Build(Point::new(
-            FULL_MAP_WIDTH - p.x - 1,
-            p.y
-        ), b),
-        Command::Deconstruct(p) => Command::Deconstruct(Point::new(
-            FULL_MAP_WIDTH - p.x - 1,
-            p.y
-        )),
-    }
-    
+    read_player_command(filename)
 }