My attempt at the 2020 Advent of Code. https://adventofcode.com/about
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.0 KiB

  1. use std::{
  2. env::var,
  3. fs::{create_dir_all, read_to_string, write},
  4. path::PathBuf,
  5. thread::spawn,
  6. time::Instant,
  7. };
  8. use anyhow::Result;
  9. use dotenv::dotenv;
  10. use reqwest::blocking::Client;
  11. mod day_01;
  12. mod day_02;
  13. mod day_03;
  14. mod day_04;
  15. mod day_05;
  16. mod day_06;
  17. mod day_07;
  18. mod day_08;
  19. mod day_09;
  20. mod day_10;
  21. mod day_11;
  22. mod day_12;
  23. mod day_13;
  24. mod day_14;
  25. mod day_15;
  26. mod day_16;
  27. mod day_17;
  28. mod day_18;
  29. mod day_19;
  30. mod day_20;
  31. mod day_21;
  32. mod day_22;
  33. mod day_23;
  34. mod day_24;
  35. mod day_25;
  36. const SHOULD_RUN: bool = true;
  37. type DayResult = Result<(Option<String>, Option<String>)>;
  38. type DayFn = fn(String) -> DayResult;
  39. fn main() -> Result<()> {
  40. let main_start = Instant::now();
  41. create_dir_all("data")?;
  42. dotenv()?;
  43. download_data(var("ADVENT_OF_CODE_SESSION")?)?;
  44. // Define the days as:
  45. // * The day's number.
  46. // * The filename for the data.
  47. // * The day's function.
  48. // * Whether or not to run the function and print the result.
  49. let days: Vec<(&str, &str, DayFn, bool)> = vec![
  50. ("01", "day_01.txt", day_01::solve, SHOULD_RUN),
  51. ("02", "day_02.txt", day_02::solve, SHOULD_RUN),
  52. ("03", "day_03.txt", day_03::solve, SHOULD_RUN),
  53. ("04", "day_04.txt", day_04::solve, SHOULD_RUN),
  54. ("05", "day_05.txt", day_05::solve, SHOULD_RUN),
  55. ("06", "day_06.txt", day_06::solve, SHOULD_RUN),
  56. ("07", "day_07.txt", day_07::solve, SHOULD_RUN),
  57. ("08", "day_08.txt", day_08::solve, SHOULD_RUN),
  58. ("09", "day_09.txt", day_09::solve, SHOULD_RUN),
  59. ("10", "day_10.txt", day_10::solve, SHOULD_RUN),
  60. ("11", "day_11.txt", day_11::solve, SHOULD_RUN),
  61. ("12", "day_12.txt", day_12::solve, SHOULD_RUN),
  62. ("13", "day_13.txt", day_13::solve, SHOULD_RUN),
  63. ("14", "day_14.txt", day_14::solve, SHOULD_RUN),
  64. ("15", "day_15.txt", day_15::solve, SHOULD_RUN),
  65. ("16", "day_16.txt", day_16::solve, SHOULD_RUN),
  66. ("17", "day_17.txt", day_17::solve, SHOULD_RUN),
  67. ("18", "day_18.txt", day_18::solve, SHOULD_RUN),
  68. ("19", "day_19.txt", day_19::solve, SHOULD_RUN),
  69. ("20", "day_20.txt", day_20::solve, SHOULD_RUN),
  70. ("21", "day_21.txt", day_21::solve, SHOULD_RUN),
  71. ("22", "day_22.txt", day_22::solve, SHOULD_RUN),
  72. ("23", "day_23.txt", day_23::solve, SHOULD_RUN),
  73. ("24", "day_24.txt", day_24::solve, SHOULD_RUN),
  74. ("25", "day_25.txt", day_25::solve, SHOULD_RUN),
  75. ];
  76. let mut results = days
  77. .into_iter()
  78. .map(|day| spawn(move || run_solution(day)))
  79. .filter_map(|thread| thread.join().unwrap())
  80. .collect::<Vec<String>>();
  81. results.sort();
  82. println!("{}", results.join("\n"));
  83. let runtime = Instant::now().duration_since(main_start);
  84. println!(" 🏃 Total Runtime: {:?}", runtime);
  85. Ok(())
  86. }
  87. fn run_solution(
  88. (index, filename, day, should_run): (&str, &str, DayFn, bool),
  89. ) -> Option<String> {
  90. let mut output = String::new();
  91. if !should_run {
  92. return None;
  93. }
  94. let start = Instant::now();
  95. let result = day(read_data(filename).unwrap_or_default()).unwrap();
  96. let runtime = Instant::now().duration_since(start);
  97. if result.0.is_none() && result.1.is_none() {
  98. return None;
  99. }
  100. output += &format!(" 🌟 Day {} |\n", index);
  101. if let Some(result) = result.0 {
  102. output += &format!(" 🎆 Part 1 | {}\n", result);
  103. }
  104. if let Some(result) = result.1 {
  105. output += &format!(" 🎇 Part 2 | {}\n", result);
  106. }
  107. output += &format!(" 🌠 Runtime | {:?}\n", runtime);
  108. Some(output)
  109. }
  110. fn download_data(session: String) -> Result<()> {
  111. let sources = vec![
  112. ("day_01.txt", "https://adventofcode.com/2020/day/1/input"),
  113. ("day_02.txt", "https://adventofcode.com/2020/day/2/input"),
  114. ("day_03.txt", "https://adventofcode.com/2020/day/3/input"),
  115. ("day_04.txt", "https://adventofcode.com/2020/day/4/input"),
  116. ("day_05.txt", "https://adventofcode.com/2020/day/5/input"),
  117. ("day_06.txt", "https://adventofcode.com/2020/day/6/input"),
  118. ("day_07.txt", "https://adventofcode.com/2020/day/7/input"),
  119. ("day_08.txt", "https://adventofcode.com/2020/day/8/input"),
  120. ("day_09.txt", "https://adventofcode.com/2020/day/9/input"),
  121. ("day_10.txt", "https://adventofcode.com/2020/day/10/input"),
  122. ("day_11.txt", "https://adventofcode.com/2020/day/11/input"),
  123. ("day_12.txt", "https://adventofcode.com/2020/day/12/input"),
  124. ("day_13.txt", "https://adventofcode.com/2020/day/13/input"),
  125. ("day_14.txt", "https://adventofcode.com/2020/day/14/input"),
  126. ("day_15.txt", "https://adventofcode.com/2020/day/15/input"),
  127. ("day_16.txt", "https://adventofcode.com/2020/day/16/input"),
  128. ];
  129. let http = Client::builder().build()?;
  130. for (filename, source) in sources {
  131. let location = PathBuf::from(format!("data/{}", filename));
  132. if location.exists() {
  133. // println!("{} exists, skipping download.", filename);
  134. continue;
  135. }
  136. let data = http
  137. .get(source)
  138. .header("Cookie", format!("session={}", session))
  139. .send()?
  140. .text()?;
  141. write(location, data)?;
  142. }
  143. Ok(())
  144. }
  145. fn read_data(filename: &str) -> Result<String> {
  146. read_to_string(PathBuf::from(format!("data/{}", filename)))
  147. .map_err(Into::into)
  148. }