{"id":20275647,"url":"https://github.com/solidiquis/advent_of_code_2023","last_synced_at":"2025-08-03T17:36:08.840Z","repository":{"id":210358388,"uuid":"724882482","full_name":"solidiquis/advent_of_code_2023","owner":"solidiquis","description":"Advent of Code 2023 in Rust","archived":false,"fork":false,"pushed_at":"2023-12-02T09:02:44.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-14T06:29:15.883Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/solidiquis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-29T01:31:28.000Z","updated_at":"2023-11-29T01:35:48.000Z","dependencies_parsed_at":"2024-11-14T13:10:31.274Z","dependency_job_id":"4c31c579-cdbd-4dd6-8aea-02d36636159e","html_url":"https://github.com/solidiquis/advent_of_code_2023","commit_stats":null,"previous_names":["solidiquis/advent_of_code_2023"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solidiquis%2Fadvent_of_code_2023","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solidiquis%2Fadvent_of_code_2023/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solidiquis%2Fadvent_of_code_2023/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solidiquis%2Fadvent_of_code_2023/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/solidiquis","download_url":"https://codeload.github.com/solidiquis/advent_of_code_2023/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241767479,"owners_count":20016961,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T13:10:24.811Z","updated_at":"2025-03-04T01:29:59.081Z","avatar_url":"https://github.com/solidiquis.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Advent of Code 2023 (Rust)\n\n’Tis the season for pagan worship. This year we will propitiate the god of memory safety, [Ferris](https://ferristhecrab.com/).\n\n# Table of Solutions\n* [Day I Part I](#day-i-part-i)\n* [Day I Part II](#day-i-part-ii)\n* [Day II Part I](#day-ii-part-i)\n* [Day II Part II](#day-ii-part-ii)\n\n## [Day I Part I](https://adventofcode.com/2023/day/1)\n\nThe efficient solution to this problem requires the \"Two Pointer Technique.\" The algorithm is as follows:\n1. Iterate line by line through the test case.\n2. For each line, produce an iterator over the characters of that line.\n3. Initialize a left cursor and a right cursor which indexes into the first and last items of the character iterator, respectively.\n4. Traverse the character iterator rightward and leftward simultaneously until you encounter the first and the last character which can be parsed into a base 10 digit OR until the two cursors meet.\n5. If you successfully generated a pair of characters then parse them together into an integer and increment a running total.\n\n```rust\npub fn solution_part_i(test_case: \u0026str) -\u003e Result\u003cu32\u003e {\n    let test_case = load_test_case_to_string(test_case)?;\n\n    let mut total = 0;\n\n    for line in test_case.lines() {\n        let chars = line.chars().collect::\u003cVec\u003c_\u003e\u003e();\n\n        let mut left_cursor = 0;\n        let mut right_cursor = match chars.len().checked_sub(1) {\n            Some(num) =\u003e num,\n            None =\u003e continue,\n        };\n\n        let mut left = None;\n        let mut right = None;\n\n        while left_cursor \u003c= right_cursor \u0026\u0026 (left.is_none() || right.is_none()) {\n            if left.is_none() {\n                if chars[left_cursor].is_ascii_digit() {\n                    left = Some(chars[left_cursor]);\n                } else {\n                    left_cursor += 1;\n                }\n            }\n\n            if right.is_none() {\n                if chars[right_cursor].is_ascii_digit() {\n                    right = Some(chars[right_cursor])\n                } else {\n                    right_cursor -= 1;\n                }\n            }\n        }\n\n        let num = format!(\n            \"{}{}\",\n            left.map_or_else(String::new, |c| c.to_string()),\n            right.map_or_else(String::new, |c| c.to_string())\n        );\n\n        total += num.parse::\u003cu32\u003e().unwrap_or(0);\n    }\n\n    Ok(total)\n}\n```\n\n## [Day I Part II](https://adventofcode.com/2023/day/1)\n\nThis one was pretty challenging for day one I must admit. It honestly made me go \"wtf.\" The most efficient solution here based from what I can tell would be to build your own finite state machine (or deterministic finite automata) that you'd use to parse each line to find the English spelling of numbers i.e. \"one\", \"two\", and so on. The problem, however, is the fact that you'd have overlapping cases such as \"oneight\" which would require a pretty sophisticated state machine to parse. Now it's possible that I'm overlooking a very simple solution here, but my approach was to use the [Aho-Corasick algorithm](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm) to find the first and last instances of \"one\", \"two\", etc. — even if they overlap — keep track of their starting offsets, and the compare that with the ultimate value of each cursor from part one's approach to determine whether or not the left and/or right item in each pair need to be updated. Here's the solution:\n\n```rust\npub fn solution_part_ii(test_case: \u0026str) -\u003e Result\u003cusize\u003e {\n    let test_case = load_test_case_to_string(test_case)?;\n\n    let patterns = [\n        \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\",\n    ];\n\n    let ac = AhoCorasick::new(patterns)?;\n\n    let alpha_to_char = |numstr| match numstr {\n        \"one\" =\u003e '1',\n        \"two\" =\u003e '2',\n        \"three\" =\u003e '3',\n        \"four\" =\u003e '4',\n        \"five\" =\u003e '5',\n        \"six\" =\u003e '6',\n        \"seven\" =\u003e '7',\n        \"eight\" =\u003e '8',\n        \"nine\" =\u003e '9',\n        _ =\u003e unreachable!(),\n    };\n\n    let mut total = 0;\n\n    for line in test_case.lines() {\n        let first_alpha_match = ac.find_overlapping_iter(line).next();\n        let last_alpha_match = ac.find_overlapping_iter(line).last();\n\n        let chars = line.chars().collect::\u003cVec\u003c_\u003e\u003e();\n\n        let mut left_cursor = 0;\n        let mut right_cursor = match chars.len().checked_sub(1) {\n            Some(num) =\u003e num,\n            None =\u003e continue,\n        };\n\n        let mut left = None;\n        let mut right = None;\n\n        while left_cursor \u003c= right_cursor \u0026\u0026 (left.is_none() || right.is_none()) {\n            if left.is_none() {\n                if chars[left_cursor].is_ascii_digit() {\n                    left = Some(chars[left_cursor]);\n                } else {\n                    left_cursor += 1;\n                }\n            }\n\n            if right.is_none() {\n                if chars[right_cursor].is_ascii_digit() {\n                    right = Some(chars[right_cursor])\n                } else {\n                    right_cursor -= 1;\n                }\n            }\n        }\n\n        if let Some(mat) = first_alpha_match {\n            if left_cursor \u003e mat.start() {\n                left = Some(alpha_to_char(\u0026line[mat.start()..mat.end()]));\n            }\n        }\n\n        if let Some(mat) = last_alpha_match {\n            if right_cursor \u003c mat.start() {\n                right = Some(alpha_to_char(\u0026line[mat.start()..mat.end()]));\n            }\n        }\n\n        let num = format!(\n            \"{}{}\",\n            left.map_or_else(String::new, |c| c.to_string()),\n            right.map_or_else(String::new, |c| c.to_string())\n        );\n\n        total += num.parse::\u003cusize\u003e().unwrap_or(0);\n    }\n\n    Ok(total)\n}\n```\n\n## [Day II Part I](https://adventofcode.com/2023/day/2)\n\nThis is one very straightforward.\n\n```rust\npub struct Bag {\n    pub red_cubes: usize,\n    pub blue_cubes: usize,\n    pub green_cubes: usize,\n}\n\npub fn solution_part_i(\n    path_to_data: \u0026str,\n    Bag {\n        red_cubes,\n        blue_cubes,\n        green_cubes,\n    }: \u0026Bag,\n) -\u003e Result\u003cusize\u003e {\n    let data = load_data_to_string(path_to_data)?;\n\n    let mut total = 0;\n\n    for (game_number, game) in data.lines().enumerate() {\n        // Skip \"Game \"\n        let cube_sets = game\n            .chars()\n            .skip_while(|c| *c != ':')\n            .skip(2) // Skip \": \"\n            .collect::\u003cString\u003e();\n\n        let mut red = 0;\n        let mut blue = 0;\n        let mut green = 0;\n\n        for cube_set in cube_sets.split(\"; \") {\n            for cube in cube_set.split(\", \") {\n                let [num, color]: [\u0026str; 2] = cube\n                    .split(' ')\n                    .collect::\u003cVec\u003c\u0026str\u003e\u003e()\n                    .try_into()\n                    .map_err(|_| Error::MalformedData)?;\n\n                let num_cubes = num.parse::\u003cusize\u003e()?;\n\n                match color {\n                    \"red\" =\u003e red = red.max(num_cubes),\n                    \"blue\" =\u003e blue = blue.max(num_cubes),\n                    \"green\" =\u003e green = green.max(num_cubes),\n                    _ =\u003e return Err(Error::MalformedData)?,\n                }\n            }\n        }\n\n        if red \u003c= *red_cubes \u0026\u0026 blue \u003c= *blue_cubes \u0026\u0026 green \u003c= *green_cubes {\n            total += game_number + 1\n        }\n    }\n\n    Ok(total)\n}\n```\n\n## [Day II Part II](https://adventofcode.com/2023/day/2)\n\nThis one is also very straightforward.\n\n```rust\npub fn solution_part_ii(path_to_data: \u0026str) -\u003e Result\u003cusize\u003e {\n    let data = load_data_to_string(path_to_data)?;\n\n    let mut total = 0;\n\n    for game in data.lines() {\n        // Skip \"Game \"\n        let cube_sets = game\n            .chars()\n            .skip_while(|c| *c != ':')\n            .skip(2) // Skip \": \"\n            .collect::\u003cString\u003e();\n\n        let mut red = 0;\n        let mut blue = 0;\n        let mut green = 0;\n\n        for cube_set in cube_sets.split(\"; \") {\n            for cube in cube_set.split(\", \") {\n                let [num, color]: [\u0026str; 2] = cube\n                    .split(' ')\n                    .collect::\u003cVec\u003c\u0026str\u003e\u003e()\n                    .try_into()\n                    .map_err(|_| Error::MalformedData)?;\n\n                let num_cubes = num.parse::\u003cusize\u003e()?;\n\n                match color {\n                    \"red\" =\u003e red = red.max(num_cubes),\n                    \"blue\" =\u003e blue = blue.max(num_cubes),\n                    \"green\" =\u003e green = green.max(num_cubes),\n                    _ =\u003e return Err(Error::MalformedData)?,\n                }\n            }\n        }\n\n        total += red * green * blue;\n    }\n\n    Ok(total)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolidiquis%2Fadvent_of_code_2023","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolidiquis%2Fadvent_of_code_2023","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolidiquis%2Fadvent_of_code_2023/lists"}