Advent of Code 2022 Day 1 - Calorie Counting

#advent-of-code#rust

Natcha Luangaroonchai

โดยปกติแล้วกวางเรนเดียร์ของซานต้าจะกินอาหารกวางเรนเดียร์เป็นประจำ แต่พวกมันต้องการพลังเวทย์มนต์จำนวนมากในการส่งของขวัญวันคริสต์มาส เพื่อการนั้นแล้ว ของว่างสุดโปรดของพวกมันคือผลดวงดาวชนิดพิเศษที่เติบโตในป่าลึกเท่านั้น เหล่าเอลฟ์ได้พาคุณเดินทางประจำปีไปยังป่าที่ผลไม้ชนิดนี้ขึ้นอยู่

สารบัญ

TL;DR

GitHub


การจะจัดหาพลังงานเวทย์มนตร์ให้เพียงพอนั้น คณะสำรวจจำเป็นต้องได้รับดวงดาวอย่างน้อย 50 ดวงภายในวันที่ 25 ธันวาคม แม้พวกเอลฟ์จะยืนยันว่าในป่ามีผลไม้มากมาย แต่คุณก็เก็บผลไม้ที่เจอระหว่างทางเผื่อไว้ด้วย

ป่าที่คุณมาสำรวจนั้นรกชัฏยากต่อการใช้ยานพาหนะหรือแม้แต่ทางอากาศ การเดินทางของพวกเอลฟ์จึงมักจะเป็นการเดินทางด้วยเท้า เมื่อเรือของคุณเข้าใกล้ฝั่ง เหล่าเอลฟ์จะเริ่มนับสต๊อกเสบียงของพวกเขา สิ่งสำคัญอย่างแรกคือเสบียงอาหารที่เอลฟ์แต่ละคนพกติดตัวมา

เหล่าเอลฟ์จะผลัดกันเขียนจำนวนแคลอรีที่มีอยู่ในอาหาร ของว่าง อาหารปันส่วน ฯลฯ ที่พวกเขานำมาด้วย บรรทัดละหนึ่งรายการ เอลฟ์แต่ละคนจะแยกเสบียงของตนเองออกจากสินค้าคงคลังของเอลฟ์ก่อนหน้า ด้วยบรรทัดว่าง

ตัวอย่างเช่น

1000
2000
3000

4000

5000
6000

7000
8000
9000

10000

ตัวเลขที่เห็นคือรายการพลังงานของอาหารที่เอลฟ์แต่ละคนมี จากรายการข้างต้นแปลว่ามีเอลฟ์ห้าคนในลิสต์

เอลฟ์คนแรกพกอาหารที่ให้พลังงาน 1000, 2000 และ 3000 แคลอรี่​ ซึ่งผลรวมทั้งหมดจะได้ 6000 แคลอรี่
เอลฟ์คนที่สองพกอาหารที่ให้พลังงาน 4000 แคลอรี่
เอลฟ์คนที่สามพกอาหารที่ให้พลังงาน 5000 และ 6000 แคลอรี่ ซึ่งผลรวมทั้งหมดจะได้ 11000 แคลอรี่
เอลฟ์คนที่สี่พกอาหารที่ให้พลังงาน 7000, 8000 และ 9000 แคลอรี่ ซึ่งผลรวมทั้งหมดจะได้ 24000 แคลอรี่
เอลฟ์คนที่ห้าพกอาหารที่ให้พลังงาน 10000 แคลอรี่

ในกรณีที่เอลฟ์ต้องการอาหารเพิ่มเติม พวกเขาอยากรู้ว่าเอลฟ์ที่พกอาหารมามากที่สุดนั้นมีกี่แคลอรี่ ในตัวอย่างด้านบนคือเอลฟ์คนที่สี่​ซึ่งพกอาหารมาทั้งหมด 24,000 แคลอรี่


โจทย์ต้องการให้หาว่าผลรวมของพลังงานอาหารที่มากที่สุดของเอลฟ์เท่ากับเท่าไร

เริ่มต้นเหมือนกับทุกครั้งด้วยการดาวน์โหลดอินพุตไฟล์เข้ามาในโปรแกรมและทำการแปลงตัวเลขในแต่ละบรรทัดให้เป็น u32

ในภาษา Rust การแปลงสตริงให้เป็น u32 สามารถทำได้ด้วยการเรียกใช้ฟังก์ชัน parse โดยมีเงื่อนไขว่าถ้าเจอบรรทัดที่เป็นค่าว่าง is_empty ให้ทำเอาผลรวมแคลอรี่เพิ่มเข้าไปที่ elves ที่เป็นประเภท Vec<u32> และทำการรีเซ็ต carrying_calories ให้เป็น 0 เพื่อใช้สำหรับคำนวณพลังงานรวมของเอลฟ์คนถัดไป

use std::{env, fs, io, io::BufRead, path};

fn main() {
    let args: Vec<String> = env::args().collect();
    let input = &args[1];
    let mut elves: Vec<u32> = vec![];

    if let Ok(lines) = read_lines(input) {
        let mut carrying_calories = 0u32;
        for line in lines {
            if let Ok(line) = line {
                if line.is_empty() {
                    elves.push(carrying_calories);
                    carrying_calories = 0;
                    continue;
                }

                carrying_calories += line.parse::<u32>().expect("invalid number");
            }
        }
    }
    
    // descending order
    elves.sort_by(|a, b| b.cmp(a));

    println!("first part answer is: {}", elves[0]);
}

fn read_lines<P: AsRef<path::Path>>(path: P) -> io::Result<io::Lines<io::BufReader<fs::File>>> {
    let file = fs::File::open(path)?;
    Ok(io::BufReader::new(file).lines())
}

เมื่อได้ Vec<u32> ที่มีผลรวมของพลังงานทั้งหมดแล้วให้ทำการเรียงลำดับจากมากไปน้อยด้วยฟังก์ชัน sort_by ตามโค้ดด้านบน เท่านี้ elves[0] ก็คือเอลฟ์คนที่พกอาหารที่มีผลรวมพลังงานมากที่สุดซึ่งเป็นคำตอบของพาร์ทแรกแล้ว


เมื่อได้คำตอบแล้วว่าใครพกอาหารที่ให้พลังงานมามากที่สุดเหล่าเอลฟ์ก็กังวลว่าอาหารอาจจะไม่พอ เพื่อป้องกันปัญหานี้เหล่าเอลฟ์อยากรู้ว่าใครที่พกอาหารที่ให้พลังงานมากที่สุดสามคนแรก เพื่อที่ว่าถ้าเอลฟ์คนใดคนนึงกินอาหารหมดก่อน พวกเขาก็ยังมีอีกสองคนสำรองอยู่ว

ตัวอย่างข้างต้นเอลฟ์สามอันดับแรกคือเอลฟ์ที่สี่ (มีพลังงาน 24,000 แคลอรี) ตามด้วยเอลฟ์ที่สาม (มีพลังงาน 11,000 แคลอรี) ตามด้วยเอลฟ์ที่ห้า (มีพลังงาน 10,000 แคลอรี) ผลรวมของแคลอรีที่เอลฟ์ทั้งสามนี้มีคือ 45,000

ค้นหาเอลฟ์สามอันดับแรกที่มีพลังงานมากที่สุดและหาว่ามีพลังงานทั้งหมดกี่แคลอรี?


ในพาร์ทที่สองโจทย์ต้องการให้ผลรวมของพลังงานที่มากที่สุดของเอลฟ์สามคนแรกซึ่งตรงนี้สามารถหาคำตอบได้ด้วยการเอา elves[0] + elves[1] + elves[2] แบบนี้ได้เลย


fn main() {
    ...

    // descending order
    elves.sort_by(|a, b| b.cmp(a));

    println!("first part answer is: {}", elves[0]);
    println!("second part answer: {}", elves[0] + elves[1] + elves[2]);
}