Этот точно как раз для моего часового пояса — в отличие от вчера-сегодня…

Синтаксический анализ выглядит намного проще во второй день — просто серия строк с 2 символами (3, если вы включаете пробел между ними, 4 с новой строкой) и 3 возможности для каждого символа. С учетом сказанного, я думаю, что мое решение здесь немного странное. Но я начну с кода парсинга — надеюсь, здесь не так много нового:

pub fn read_from_system(path: &str) -> Vec<[u8;3]> {
    let mut v: Vec<[u8;3]> = Vec::with_capacity(2600); // There are ~2500 lines in the input so this should definitely be enough
    let mut hold_buf: [u8;BUF_SIZE*2] = [0;BUF_SIZE*2];
    let mut read = 0;
    // Read into buffer, scan for double newline, then parse the previous items and sum them into a single value
    unsafe  {
        let fh = syscall!(OPEN,path.as_ptr(),0,path.len());
        while read == 0 {
            let mut buf: [u8;BUF_SIZE] = [0;BUF_SIZE];
            read = syscall!(READ, fh, buf.as_ptr(), BUF_SIZE);
            parse_block(&mut buf, &mut v, &mut hold_buf);
        }

        syscall!(CLOSE,fh);
    }
    return v;
}

fn parse_block(b :&mut [u8;BUF_SIZE], v: &mut Vec<[u8;3]> , hb: &mut [u8;BUF_SIZE*2]){
    let mut raw_lines = Vec::new(); 
    add_old_chars(&mut raw_lines, hb); // Logic dictates raw elf does not have a full elf in it yet
    *hb = [0;BUF_SIZE*2];
    get_lines(b, v);
}

fn get_lines(b: &mut [u8;BUF_SIZE], v: &mut Vec<[u8;3]>) {
    let mut line_buf = Vec::with_capacity(3);
    for char in b {
        if char == &NEW_LINE {
            let line = [line_buf[0],line_buf[1],line_buf[2]];
            v.push(line);
            line_buf.clear()
        } else {
            line_buf.push(*char);
        }
    }
}

fn add_old_chars(v: &mut Vec<u8>, c: &mut [u8;BUF_SIZE*2]) {
    for item in c {
        if item == &0 {
            break
        } else {
            v.push(*item)
        }

    }
}

Это то же самое, что и день 1, но намного проще и нет двойных новых строк, которые нужно проверять. На этот раз имеет смысл сохранить все как u8. Для этой головоломки я ввел несколько констант и перечисление. Однако часть 1 и часть 2 не следуют единому методу:

const ROCK: u8 = b'A';
const PAPER: u8 = b'B';
const SCIS: u8 = b'C';

const MY_ROCK: u8 = b'X';
const MY_PAPER: u8 = b'Y';
const MY_SCIS: u8 = b'Z';

const LOSE: u8 = b'X';
const DRAW: u8 = b'Y';
const WIN: u8 = b'Z';

enum Outcome {
    Win,
    Lose,
    Draw
}

fn parse_line(buf: &[u8]) -> (Outcome, u8) {
    // Returns a bool for won/lost and a u8 for choice score (not total score!)
    let elf_choice = buf[0];
    let my_choice = buf[2];
    let choice_score = match my_choice {
        MY_ROCK => 1,
        MY_PAPER => 2,
        MY_SCIS => 3,
        _ => panic!("Invalid choice! {}", my_choice)
    };
    let res = rps(my_choice,elf_choice);

    return (res,choice_score)

}

fn rps(me:u8,elf:u8) -> Outcome {
    match me {
        MY_ROCK => {
            match elf {
                ROCK => return Outcome::Draw,
                PAPER => return Outcome::Lose,
                SCIS => return Outcome::Win,
                _ => panic!("elf choice not valid! {}",elf)
            }
        },
        MY_PAPER => {
            match elf {
                ROCK => return Outcome::Win,
                PAPER => return Outcome::Draw,
                SCIS => return Outcome::Lose,
                _ => panic!("elf choice not valid! {}",elf)
            }
        },
        MY_SCIS => {
            match elf {
                ROCK => return Outcome::Lose,
                PAPER => return Outcome::Win,
                SCIS => return Outcome::Draw,
                _ => panic!("elf choice not valid! {}",elf)
            }
        },
        _ => panic!("My choice not valid {}", me)
    }
}

fn pick_move(elf: u8, outcome: u8) -> u8 {
    match outcome {
        WIN => match elf {
            ROCK => 2, //paper
            PAPER => 3, //scissors
            SCIS => 1, //rock
            _ => panic!("Unknown elf choice!"),
        },
        LOSE => match elf {
            ROCK => 3, //scissors
            PAPER => 1, //rock
            SCIS => 2, //paper
            _ => panic!("Unknown elf choice!"),
        },
        DRAW => match elf {
            ROCK => 1, //rock
            PAPER => 2, //paper
            SCIS => 3, //scissors
            _ => panic!("Unknown elf choice!"),
        },
        _ => panic!("Unknown outcome")
    }
}

pub fn part1(v: &Vec<[u8;3]>) -> u32 {
    v.iter().map(|line| {
        let (outcome,score) = parse_line(line);
        match outcome {
            Outcome::Draw => score + 3,
            Outcome::Lose => score,
            Outcome::Win => score + 6
        }
    })
    .fold(0, |acc, x| acc + (x as u32))
}

pub fn part2(v: &Vec<[u8;3]>) -> u32 {
    v.iter().map(|line| {
        let outcome_score = match line[2] {
            LOSE => 0,
            DRAW => 3,
            WIN => 6,
            _ => panic!("Unknown outcome!")
        };
        let choice_score = pick_move(line[0],line[2]);
        outcome_score + choice_score
    }).fold(0, |acc, x| acc + (x as u32))
}

Методология фундаментальна — перебирать каждую строку, сопоставлять символы дважды.

Оно работает. Это не слишком медленно. Но это громоздко, и это одно из тех плохих решений, которые многословны, но и не очень понятны для него. В общем, я не в восторге от этого. Кто-то поделился со мной замечательным однострочником, который работает, признавая, что символы UTF-8 для каждого выбора разделены только числом 1 и упорядочены таким образом, чтобы баллы выровнялись… Я оставлю вас, чтобы выяснить отдых для среды без спойлеров.

Итак, мы здесь. Немного поспешил, потому что сегодня я тоже прошел первый день, но я определенно начинаю чувствовать себя намного более комфортно с управлением встроенной памятью.

Вперед на 3 день!