Advertisement
berninator

AOC Day 15 Rust

Dec 21st, 2022
196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 13.09 KB | None | 0 0
  1. use rayon::prelude::*;
  2. use indicatif::{ProgressBar, ProgressStyle};
  3.  
  4. use std::{ptr, vec};
  5. use opencl3::{device::*, command_queue::*, context::*, program::*, kernel::*, memory::*, types::*};
  6.  
  7. #[derive(Debug, PartialEq, Eq, Hash)]
  8. struct Sensor {
  9.     x: isize,
  10.     y: isize,
  11.     closest_beacon: Beacon,
  12. }
  13.  
  14. impl Sensor {
  15.     fn _new(x: isize, y: isize, beacon_x: isize, beacon_y: isize) -> Self {
  16.         Self {
  17.             x, y,
  18.             closest_beacon: Beacon::new(beacon_x, beacon_y),
  19.         }
  20.     }
  21.    
  22.     fn from_str(sensor_line: &str) -> Self {
  23.         let mut tokens = sensor_line.split_whitespace();
  24.         let x: isize =
  25.             tokens.nth(2).unwrap()
  26.             .replace(',', "")
  27.             .chars().skip(2)
  28.             .collect::<String>()
  29.             .parse().unwrap();
  30.  
  31.         let y: isize =
  32.             tokens.next().unwrap()
  33.             .replace(':', "")
  34.             .chars().skip(2)
  35.             .collect::<String>()
  36.             .parse().unwrap();
  37.  
  38.         let beacon_x: isize =
  39.             tokens.nth(4).unwrap()
  40.             .replace(',', "")
  41.             .chars().skip(2)
  42.             .collect::<String>()
  43.             .parse().unwrap();
  44.  
  45.         let beacon_y: isize =
  46.             tokens.next().unwrap()
  47.             .chars().skip(2)
  48.             .collect::<String>()
  49.             .parse().unwrap();
  50.        
  51.         Self {
  52.             x, y,
  53.             closest_beacon: Beacon::new(beacon_x, beacon_y)
  54.         }
  55.     }
  56.    
  57.     fn beacon_cannot_exist(&self, x: isize, y: isize) -> bool {
  58.         // If we're on top of the known beacon, then a beacon definitely can exist here
  59.         if x == self.closest_beacon.x && y == self.closest_beacon.y {
  60.             return false;
  61.         }
  62.        
  63.         // A beacon cannot exist if it's within the same distance to the known closest beacon
  64.         let distance_to_beacon = self.distance_to(self.closest_beacon.x, self.closest_beacon.y);
  65.         let distance_to_this_point = self.distance_to(x, y);
  66.         distance_to_this_point <= distance_to_beacon
  67.     }
  68.    
  69.     fn distance_to(&self, x: isize, y: isize) -> usize {
  70.         self.x.abs_diff(x) + self.y.abs_diff(y)
  71.     }
  72. }
  73.  
  74. #[derive(Debug, PartialEq, Eq, Hash)]
  75. struct Beacon {
  76.     x: isize,
  77.     y: isize,
  78. }
  79.  
  80. impl Beacon {
  81.     fn new(x: isize, y: isize) -> Self {
  82.         Self { x, y }
  83.     }
  84. }
  85.  
  86. pub fn solve() {
  87.     let input = std::fs::read_to_string("inputs/day15.txt")
  88.         .expect("Error reading input!");
  89.  
  90.     let mut sensors = Vec::<Sensor>::new();
  91.     let mut x_bounds = (isize::MAX, isize::MIN);
  92.     for line in input.lines() {
  93.         let sensor = Sensor::from_str(line);
  94.         let distance = sensor.distance_to(sensor.closest_beacon.x, sensor.closest_beacon.y) as isize;
  95.         x_bounds.0 = *([x_bounds.0, sensor.x - distance, sensor.x + distance, sensor.closest_beacon.x].iter().min().unwrap());
  96.         x_bounds.1 = *([x_bounds.1, sensor.x - distance, sensor.x + distance, sensor.closest_beacon.x].iter().max().unwrap());
  97.         sensors.push(sensor);
  98.     }
  99.    
  100.     // Part 1
  101.     let cannot_exist =
  102.         (x_bounds.0..=x_bounds.1).into_par_iter()
  103.         .filter(|x| sensors.iter().any(|sensor| sensor.beacon_cannot_exist(*x, 2_000_000)))
  104.         .count();
  105.     println!("Part 1: {cannot_exist}");
  106.  
  107.     // Part 2
  108.     println!("Attempting Part 2...");
  109.  
  110.     // Setup OpenCL Device
  111.     let device_id = *get_all_devices(CL_DEVICE_TYPE_GPU).unwrap().first().unwrap();
  112.     let device = Device::new(device_id);
  113.     let context = Context::from_device(&device).unwrap();
  114.     let queue = CommandQueue::create_default_with_properties(&context, CL_QUEUE_PROFILING_ENABLE, 0).unwrap();
  115.  
  116.     // Read and Build the Kernel
  117.     let kernel_source = std::fs::read_to_string("src/day15.cl")
  118.         .expect("Error reading OpenCL source!");
  119.     let program = Program::create_and_build_from_source(&context, &kernel_source, "").unwrap();
  120.     let kernel = Kernel::create(&program, "can_contain_new_beacon").unwrap();
  121.    
  122.     // Setup arguments
  123.     let num_of_sensors: cl_int = sensors.len() as i32;
  124.     let host_sensor_x: Vec<cl_long> = sensors.iter().map(|sensor| sensor.x as i64).collect();
  125.     let host_sensor_y: Vec<cl_long> = sensors.iter().map(|sensor| sensor.y as i64).collect();
  126.     let host_beacon_distance: Vec<cl_long> =
  127.         sensors.iter()
  128.         .map(|sensor| sensor.distance_to(sensor.closest_beacon.x, sensor.closest_beacon.y) as i64)
  129.         .collect();
  130.     let mut host_beacon_location: [cl_long; 2] = [0, 0];
  131.    
  132.     // Create and write to buffers
  133.     let mut dev_sensor_x = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, host_sensor_x.len(), ptr::null_mut()).unwrap() };
  134.     let mut dev_sensor_y = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, host_sensor_y.len(), ptr::null_mut()).unwrap() };
  135.     let mut dev_beacon_distance = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, host_beacon_distance.len(), ptr::null_mut()).unwrap() };
  136.     let mut dev_beacon_location = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_WRITE_ONLY, 2, ptr::null_mut()).unwrap() };
  137.     unsafe {
  138.         queue.enqueue_write_buffer(&mut dev_sensor_x, CL_BLOCKING, 0, &host_sensor_x, &[]).unwrap();
  139.         queue.enqueue_write_buffer(&mut dev_sensor_y, CL_BLOCKING, 0, &host_sensor_y, &[]).unwrap();
  140.         queue.enqueue_write_buffer(&mut dev_beacon_distance, CL_BLOCKING, 0, &host_beacon_distance, &[]).unwrap();
  141.         queue.enqueue_write_buffer(&mut dev_beacon_location, CL_BLOCKING, 0, &host_beacon_location, &[]).unwrap();
  142.     };
  143.    
  144.     // Step through search space in chunks using OpenCL
  145.     let step_size: usize = 10_000;
  146.     let progress = ProgressBar::new(4_000_001);
  147.     progress.set_style(ProgressStyle::with_template("[{elapsed_precise}] {bar:80} {human_pos:>9}/{human_len:9} ({eta_precise})").unwrap());
  148.     for base_x in (0..=4_000_000 as cl_long).step_by(step_size) {
  149.         progress.inc(step_size as u64);
  150.  
  151.         // Execute Kernel
  152.         let candidate_beacon_location = unsafe {
  153.             let kernel_event =
  154.                 ExecuteKernel::new(&kernel)
  155.                 .set_arg(&base_x)
  156.                 .set_arg(&num_of_sensors)
  157.                 .set_arg(&dev_sensor_x)
  158.                 .set_arg(&dev_sensor_y)
  159.                 .set_arg(&dev_beacon_distance)
  160.                 .set_arg(&dev_beacon_location)
  161.                 .set_global_work_sizes(&[step_size, 4_000_001])
  162.                 .enqueue_nd_range(&queue)
  163.                 .unwrap();
  164.                
  165.             let events: Vec<cl_event> = vec![kernel_event.get()];
  166.             let read_event = queue.enqueue_read_buffer(&dev_beacon_location, CL_BLOCKING, 0, &mut host_beacon_location, &events).unwrap();
  167.             read_event.wait().unwrap();
  168.            
  169.             host_beacon_location
  170.         };
  171.  
  172.         if candidate_beacon_location[0] != 0 || candidate_beacon_location[1] != 0 {
  173.             progress.finish();
  174.             println!("Part 2: {} {candidate_beacon_location:?}", candidate_beacon_location[0] * 4_000_000 + candidate_beacon_location[1]);
  175.             break;
  176.         }
  177.     }
  178. }
  179.  
  180. #[cfg(test)]
  181. mod tests {
  182.     use std::{ptr, vec, iter::repeat};
  183.     use super::*;
  184.  
  185.     #[test]
  186.     fn part1_data_test() {
  187.         let input = std::fs::read_to_string("inputs/day15_test.txt")
  188.             .expect("Error reading input!");
  189.        
  190.         let mut sensors = Vec::<Sensor>::new();
  191.         let mut x_bounds = (isize::MAX, isize::MIN);
  192.         for line in input.lines() {
  193.             let sensor = Sensor::from_str(line);
  194.             let distance = sensor.distance_to(sensor.closest_beacon.x, sensor.closest_beacon.y) as isize;
  195.             x_bounds.0 = *([x_bounds.0, sensor.x - distance, sensor.x + distance, sensor.closest_beacon.x].iter().min().unwrap());
  196.             x_bounds.1 = *([x_bounds.1, sensor.x - distance, sensor.x + distance, sensor.closest_beacon.x].iter().max().unwrap());
  197.             sensors.push(sensor);
  198.         }
  199.        
  200.         let cannot_exist =
  201.             (x_bounds.0..=x_bounds.1).into_par_iter()
  202.             .filter(|x| sensors.iter().any(|sensor| sensor.beacon_cannot_exist(*x, 10)))
  203.             .count();
  204.         assert_eq!(cannot_exist, 26);
  205.     }
  206.  
  207.     #[test]
  208.     fn part2_data_test() {
  209.         let input = std::fs::read_to_string("inputs/day15_test.txt")
  210.             .expect("Error reading input!");
  211.         let mut sensors = Vec::<Sensor>::new();
  212.         for line in input.lines() {
  213.             let sensor = Sensor::from_str(line);
  214.             sensors.push(sensor);
  215.         }
  216.        
  217.         let search_space: Vec<(isize, isize)> =
  218.             (0..=20).into_iter()
  219.             .flat_map(|x| repeat(x).zip(0..=20))
  220.             .collect();
  221.        
  222.         let solutions: Vec<(isize, isize)> =
  223.             search_space.into_par_iter()
  224.             .filter(|(x, y)| sensors.iter().all(|sensor| !sensor.beacon_cannot_exist(*x, *y)))
  225.             .filter(|(x, y)| sensors.iter().all(|sensor| *x != sensor.closest_beacon.x || *y != sensor.closest_beacon.y))
  226.             .collect();
  227.  
  228.         let tuning_freq: isize =  4_000_000 * solutions[0].0 + solutions[0].1;
  229.         assert_eq!(tuning_freq, 56_000_011);
  230.     }
  231.  
  232.     #[test]
  233.     fn part2_opencl_test() {
  234.         let input = std::fs::read_to_string("inputs/day15_test.txt")
  235.             .expect("Error reading input!");
  236.        
  237.         // Parse Sensor Data
  238.         let mut sensors = Vec::<Sensor>::new();
  239.         for line in input.lines() {
  240.             let sensor = Sensor::from_str(line);
  241.             sensors.push(sensor);
  242.         }
  243.        
  244.         // Setup OpenCL Context
  245.         let device_id = *get_all_devices(CL_DEVICE_TYPE_GPU).unwrap().first().unwrap();
  246.         let device = Device::new(device_id);
  247.         let context = Context::from_device(&device).unwrap();
  248.         let queue = CommandQueue::create_default_with_properties(&context, CL_QUEUE_PROFILING_ENABLE, 0).unwrap();
  249.  
  250.         // Read and Build Kernel
  251.         let kernel_source = std::fs::read_to_string("src/day15.cl")
  252.             .expect("Error reading OpenCL source!");
  253.         let program = Program::create_and_build_from_source(&context, &kernel_source, "").unwrap();
  254.         let kernel = Kernel::create(&program, "can_contain_new_beacon").unwrap();
  255.        
  256.         // Setup arguments
  257.         let num_of_sensors: cl_int = sensors.len() as i32;
  258.         let host_sensor_x: Vec<cl_long> = sensors.iter().map(|sensor| sensor.x as i64).collect();
  259.         let host_sensor_y: Vec<cl_long> = sensors.iter().map(|sensor| sensor.y as i64).collect();
  260.         let host_beacon_distance: Vec<cl_long> =
  261.             sensors.iter()
  262.             .map(|sensor| sensor.distance_to(sensor.closest_beacon.x, sensor.closest_beacon.y) as i64)
  263.             .collect();
  264.         let mut host_beacon_location: [cl_long; 2] = [0, 0];
  265.        
  266.         // Create and write to buffers
  267.         let mut dev_sensor_x = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, sensors.len(), ptr::null_mut()).unwrap() };
  268.         let mut dev_sensor_y = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, sensors.len(), ptr::null_mut()).unwrap() };
  269.         let mut dev_beacon_distance = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_READ_ONLY, sensors.len(), ptr::null_mut()).unwrap() };
  270.         let mut dev_beacon_location = unsafe { Buffer::<cl_long>::create(&context, CL_MEM_WRITE_ONLY, 2, ptr::null_mut()).unwrap() };
  271.         unsafe {
  272.             queue.enqueue_write_buffer(&mut dev_sensor_x, CL_BLOCKING, 0, &host_sensor_x, &[]).unwrap();
  273.             queue.enqueue_write_buffer(&mut dev_sensor_y, CL_BLOCKING, 0, &host_sensor_y, &[]).unwrap();
  274.             queue.enqueue_write_buffer(&mut dev_beacon_distance, CL_BLOCKING, 0, &host_beacon_distance, &[]).unwrap();
  275.             queue.enqueue_write_buffer(&mut dev_beacon_location, CL_BLOCKING, 0, &host_beacon_location, &[]).unwrap();
  276.         };
  277.  
  278.         // Step through search space in chunks using OpenCL
  279.         let step_size: usize = 2;
  280.         for base_x in (0..=20 as cl_long).step_by(step_size) {
  281.             let candidate_beacon_location = unsafe {
  282.                 let kernel_event =
  283.                     ExecuteKernel::new(&kernel)
  284.                     .set_arg(&base_x)
  285.                     .set_arg(&num_of_sensors)
  286.                     .set_arg(&dev_sensor_x)
  287.                     .set_arg(&dev_sensor_y)
  288.                     .set_arg(&dev_beacon_distance)
  289.                     .set_arg(&dev_beacon_location)
  290.                     .set_global_work_sizes(&[step_size, 21])
  291.                     .enqueue_nd_range(&queue)
  292.                     .unwrap();
  293.                    
  294.                 let events: Vec<cl_event> = vec![kernel_event.get()];
  295.                 let read_event = queue.enqueue_read_buffer(&dev_beacon_location, CL_BLOCKING, 0, &mut host_beacon_location, &events).unwrap();
  296.                 read_event.wait().unwrap();
  297.                
  298.                 host_beacon_location
  299.             };
  300.  
  301.             if candidate_beacon_location[0] != 0 || candidate_beacon_location[1] != 0 {
  302.                 assert_eq!(4_000_000 * candidate_beacon_location[0] + candidate_beacon_location[1], 56_000_011);
  303.                 break;
  304.             }
  305.         }
  306.     }
  307. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement