18 votes

Day 2: Rock Paper Scissors

Today's problem description: https://adventofcode.com/2022/day/2

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

22 comments

  1. tjf
    (edited )
    Link
    My Python solution, taking advantage of modular arithmetic. Part 1 #!/usr/bin/env pypy3 import sys def main(): score = 0 for line in sys.stdin: rnd = line.split() opp = ord(rnd[0]) - ord('A') #...

    My Python solution, taking advantage of modular arithmetic.

    Part 1
    #!/usr/bin/env pypy3
    
    import sys
    
    def main():
        score = 0
        for line in sys.stdin:
            rnd = line.split()
            opp = ord(rnd[0]) - ord('A')    # ascii
            me = ord(rnd[1]) - ord('X')     # haxx!
            score += me + 1 + ((me - opp + 1) % 3) * 3
    
        print(score)
    
    if __name__ == '__main__':
        main()
    
    Part 2
    #!/usr/bin/env pypy3
    
    import sys
    
    def main():
        score = 0
        for line in sys.stdin:
            rnd = line.split()
            opp = ord(rnd[0]) - ord('A')                    # ascii
            me = (ord(rnd[1]) - ord('X') - 1 + opp) % 3     # haxx!
            score += me + 1 + ((me - opp + 1) % 3) * 3
    
        print(score)
    
    if __name__ == '__main__':
        main()
    
    7 votes
  2. [3]
    tomf
    Link
    I think they've got a spreadsheeter on the team this year... that or its pity. Either way, this is the final form. both parts I combined the points from the get go and added onto the array for the...

    I think they've got a spreadsheeter on the team this year... that or its pity. Either way, this is the final form.

    both parts

    I combined the points from the get go and added onto the array for the second part. No use writing it out twice... or thrice, I guess.

    =ARRAYFORMULA(
      BYCOL(
       IFERROR(
        VLOOKUP(
         A2:A, 
         {"B Z",9,9;
          "A Y",8,4;
          "C X",7,2;
          "C Z",6,7;
          "B Y",5,5;
          "A X",4,3; 
          "A Z",3,8;
          "C Y",2,6;
          "B X",1,1},
         {2,3},0)),
       LAMBDA(x,SUM(x))))
    
    5 votes
    1. [2]
      bhrgunatha
      Link Parent
      No offence, but it's rare to see a beautiful spreadsheet answer, but this one is lovely.

      No offence, but it's rare to see a beautiful spreadsheet answer, but this one is lovely.

      3 votes
  3. Crestwave
    Link
    Got a bit hacky with my calculations, but this was pretty fun overall. Part 1 #!/usr/bin/awk -f BEGIN { shape["A"] = 1 shape["B"] = 2 shape["C"] = 3 shape["X"] = 1 shape["Y"] = 2 shape["Z"] = 3...

    Got a bit hacky with my calculations, but this was pretty fun overall.

    Part 1
    #!/usr/bin/awk -f
    BEGIN {
            shape["A"] = 1
            shape["B"] = 2
            shape["C"] = 3
            shape["X"] = 1
            shape["Y"] = 2
            shape["Z"] = 3
    
            outcome[0] = 3
            outcome[1] = 6
            outcome[2] = 0
    }
    
    { score += outcome[((shape[$2]-shape[$1])+3)%3] + shape[$2] }
    
    END { print(score) }
    
    Part 2
    #!/usr/bin/awk -f
    BEGIN {
            shape["A"] = 1
            shape["B"] = 2
            shape["C"] = 3
    }
    
    {
            if ($2 == "X")
                    score += ((shape[$1]-1)+2) % 3 + 1
            else if ($2 == "Y")
                    score += shape[$1] + 3
            else if ($2 == "Z")
                    score += ((shape[$1]) % 3) + 6 + 1
    }
    
    END { print(score) }
    
    4 votes
  4. asterisk
    Link
    Python by_selected: int = 0 by_outcome: int = 0 selected = { "X": 1, "Y": 2, "Z": 3, } outcome = { "A": [3, 6, 0], "B": [0, 3, 6], "C": [6, 0, 3], } with open("input.txt") as file: for line in...
    Python
    by_selected: int = 0
    by_outcome: int = 0
    
    selected = {
        "X": 1,
        "Y": 2,
        "Z": 3,
    }
    
    outcome = {
        "A": [3, 6, 0],
        "B": [0, 3, 6],
        "C": [6, 0, 3],
    }
    
    with open("input.txt") as file:
        for line in file:
            elf, me = line.split()
            by_selected += selected[me] + outcome[elf][selected[me] - 1]
            result = (selected[me] - 1) * 3
            by_outcome += result + outcome[elf].index(result) + 1
    
    print(by_selected)  # Part One: 10941
    print(by_outcome)  # Part Two: 13071
    
    3 votes
  5. fazit
    (edited )
    Link
    That was interesting. At first I thought I could find a matrix that gives me the shape output for the desired outcome, but that proved cumbersome. Then I decided I can just search for the desired...

    That was interesting. At first I thought I could find a matrix that gives me the shape output for the desired outcome, but that proved cumbersome.
    Then I decided I can just search for the desired outcome and then get the right shape.
    I had to refactor my outcome matrix for part 2, since it was set up the other way round, and checking over multiple lists which result fits my desired outcome seems ugly.

    Python
    with open('./02/input.txt', 'r') as f:
        strategy_guide = f.readlines()
    
    # Opponent Move: Row, Player Move: Column
    #           Rock    Paper   Scissor
    # Rock      3
    # Paper             3
    # Scissor                       3
    outcome_matrix = [
        [3, 6, 0],
        [0, 3, 6],
        [6, 0, 3]
    ]
    
    shape_reward = [1, 2, 3]
    
    LUT_RPS_1 = {
        'A': 0,
        'B': 1,
        'C': 2,
        'X': 0,
        'Y': 1,
        'Z': 2
    }
    
    def reward_function(game_str: str) -> int:
        opponent, player = game_str.replace("\n", "").split(" ")
        opponent_play = LUT_RPS_1[opponent]
        player_play = LUT_RPS_1[player]
    
        outcome = outcome_matrix[opponent_play][player_play]
        shape_bonus = shape_reward[player_play]
    
        score = outcome + shape_bonus
    
        return score
    
    scores = [reward_function(game_str) for game_str in strategy_guide]
    total_score = sum(scores)
    print(f"Total score for Part 1: {total_score}")
    
    LUT_outcome = {
        'X': 0,
        'Y': 3,
        'Z': 6
    }
    
    def find_player_shape_score(opponent, outcome) -> int:
        opponent_play = LUT_RPS_1[opponent]
        desired_outcome = LUT_outcome[outcome]
    
        player_shape_idx = outcome_matrix[opponent_play].index(desired_outcome)  #CPU cycles are cheap, my time is not
        shape_bonus = shape_reward[player_shape_idx]
        return shape_bonus
    
    
    def reward_function_2(game_str: str) -> int:
        opponent, outcome = game_str.replace("\n", "").split(" ")
        outcome_score = LUT_outcome[outcome]
        shape_score = find_player_shape_score(opponent, outcome)
        score = outcome_score + shape_score
        return score
    
    scores_2 = [reward_function_2(game_str) for game_str in strategy_guide]
    total_score_2 = sum(scores_2)
    print(f"Total score for Part 2: {total_score_2}")
    
    3 votes
  6. bhrgunatha
    (edited )
    Link
    I immediately thought of a cheeky literal shortcut but decided to write a symbolic approach because it's fun and more in the spirit of a programming exercise like you might get at school. Part 1...

    I immediately thought of a cheeky literal shortcut but decided to write a symbolic approach because it's fun and more in the spirit of a programming exercise like you might get at school.

    Part 1
    (define (part-01 input)
      (for/sum ([choices (in-list input)])
        (match-define (list (app shape elf)
                            (app shape self)) (string-split choices))
        (game-score elf self)))
    
    (define (shape token)
      (case token
        [("A" "X") 'rock]
        [("B" "Y") 'paper]
        [("C" "Z") 'scissors]))
    
    (define (game-score elf self)
      ;; yes, I confused myself by mixing up the order of the tokens and had to waste time debugging.  
      (+ (shape-score self)
         (match* (elf self)
           [(same      same)      3]
           [('scissors 'rock)     6]
           [('rock     'paper)    6]
           [('paper    'scissors) 6]
           [(_ _)                 0])))
    
    (define (shape-score shape)
      (case shape
        [(rock)     1]
        [(paper)    2]
        [(scissors) 3]))  
    
    Part 2
    (define (part-02 input)
      (for/sum ([choices (in-list input)])
        (match-define (list (app shape elf)
                            (app shape self)) (string-split choices))
        (outcome-score elf self)))
    
    (define (outcome-score elf self)
      (case self
        [(rock)     (+ 0 (shape-score (lose elf)))]
        [(paper)    (+ 3 (shape-score       elf))]
        [(scissors) (+ 6 (shape-score (win  elf)))]))
    
    (define (lose elf)
      (case elf
        [(rock)     'scissors]
        [(scissors) 'paper]
        [(paper)    'rock]))
    
    (define (win elf)
      (case elf
        [(rock)     'paper]
        [(scissors) 'rock]
        [(paper)    'scissors]))
    

    The cheeky, but valid approach
    attempt 1: ~70% elapsed time
    attempt 2: ~35% elapsed time

    Speedrun

    Attempt 1:

    (with-input-from-file "day02.input"
      (thunk
       (for/fold ([part1 0]
                  [part2 0]
                  #:result (list part1 part2))
                 ([choices (in-lines)])
         (values (+ part1
                    (case choices
                      [("A X") 4] [("A Y") 8] [("A Z") 3]
                      [("B X") 1] [("B Y") 5] [("B Z") 9]
                      [("C X") 7] [("C Y") 2] [("C Z") 6]))
                 (+ part2
                    (case choices
                      [("A X") 3] [("A Y") 4] [("A Z") 8]
                      [("B X") 1] [("B Y") 5] [("B Z") 9]
                      [("C X") 2] [("C Y") 6] [("C Z") 7]))))))
    

    Attempt 2 (aka throw memory at it):

    (with-input-from-file "day02.input"
       (thunk
        (define outcomes
          (make-immutable-hash
           '(("A X" . (4 3)) ("A Y" . (8 4)) ("A Z" . (3 8))
             ("B X" . (1 1)) ("B Y" . (5 5)) ("B Z" . (9 9))
             ("C X" . (7 2)) ("C Y" . (2 6)) ("C Z" . (6 7)))))
        (for/fold ([part1 0]
                   [part2 0]
                   #:result (list part1 part2))
                  ([choices (in-lines)])
          (define scores (hash-ref outcomes choices))
          (values (+ part1 (first scores))
                  (+ part2 (second scores))))))
    
    3 votes
  7. MeckiSpaghetti
    (edited )
    Link
    Ruby both parts OUTCOMES = { 'A X' => [4, 3], 'A Y' => [8, 4], 'A Z' => [3, 8], 'B X' => [1, 1], 'B Y' => [5, 5], 'B Z' => [9, 9], 'C X' => [7, 2], 'C Y' => [2, 6], 'C Z' => [6, 7] } rows = File...

    Ruby

    both parts
    OUTCOMES = {
      'A X' => [4, 3],
      'A Y' => [8, 4],
      'A Z' => [3, 8],
      'B X' => [1, 1],
      'B Y' => [5, 5],
      'B Z' => [9, 9],
      'C X' => [7, 2],
      'C Y' => [2, 6],
      'C Z' => [6, 7]
    }
    
    rows = File
         .read("input.txt")
         .split("\n")
         .map(&OUTCOMES)
         
    p rows.sum(&:first) # Part 1
    p rows.sum(&:last) # Part 2
    
    3 votes
  8. p4t44
    Link
    My questionable solutions in Java: part 1 public static void dayTwoPartOne() throws IOException { System.out.println(Files.readAllLines(Path.of("input-day2.txt")) .stream() .mapToInt(s->...

    My questionable solutions in Java:

    part 1
    public static void dayTwoPartOne() throws IOException {
            System.out.println(Files.readAllLines(Path.of("input-day2.txt"))
                .stream()
                .mapToInt(s-> ((int)s.charAt(2) - 87) + switch(((int)s.charAt(2) - 88) - ((int)s.charAt(0) - 65)){
                    case -2 -> 6;
                    case -1 -> 0;
                    case 0 -> 3;
                    case 1 -> 6;
                    case 2 -> 0;
                    default -> throw new IllegalArgumentException();
                }
            ).sum());
        }
    
    
    part 2
    
    public static void dayTwoPartTwo() throws IOException {
            System.out.println(Files.readAllLines(Path.of("input-day2.txt"))
                .stream()
                .mapToInt(s-> switch((int)s.charAt(2) - 88){
                    case 0 ->  0 + ((int)s.charAt(0) - 63)%3 + 1;
                    case 1 -> 3 + (int)s.charAt(0) - 64;
                    case 2 ->  6 + ((int)s.charAt(0) - 64)%3 + 1;
                    default -> throw new IllegalArgumentException();
                }
            ).sum());
        }
    
    3 votes
  9. Toric
    Link
    Part 2 tripped me up, had to move the original parser out of utilities and make a seperate parser for each one. Really starting to appreciate rusts type system, allows me to reason really well...

    Part 2 tripped me up, had to move the original parser out of utilities and make a seperate parser for each one.

    Really starting to appreciate rusts type system, allows me to reason really well about the data im passing around and how im transforming it.

    Utilities
    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
    pub enum Play {
        Rock = 1,
        Paper = 2,
        Scissors = 3,
    }
    
    #[derive(Debug, PartialEq, Eq)]
    pub struct Strategy {
        pub elf: Play,
        pub you: Play,
    }
    
    pub fn calc_score(input: &Strategy) -> usize{
        //play enum has value corresponding to its score.
        let mut score = input.you as usize;
        //an enum wins if (you-elf)%3 = 1, looses if it = 2
        let gamestatus = (input.you as i8 - input.elf as i8).rem_euclid(3);
        match gamestatus {
            1 => score+=6,
            2 => (),
            0 => score += 3,
            _ => panic!("gamestatus was {}!, you were {}, elf was {}", gamestatus, input.you as i8, input.elf as i8)
        }
        score
    }
    
    driver
    mod part1;
    mod part2;
    mod utilities;
    
    fn main() {
        let _input = include_str!("./input.txt");
    
        println!("Part One");
        println!("Result: {}", part1::part1(_input));
    
        println!("Part Two");
        println!("Result: {}", part2::part2(_input));
    }
    
    part1
    use crate::utilities::*;
    pub fn part1(input: &str) -> usize {
        parse(input).iter().map(calc_score).sum()
    }
    
    pub fn parse(input: &str) -> Vec<Strategy> {
        input
            .lines()
            .map(|line| {
                let elf = match line.as_bytes()[0] {
                    b'A' => Play::Rock,
                    b'B' => Play::Paper,
                    b'C' => Play::Scissors,
                    _ => panic!("your opponent not playing defined strategy!"),
                };
                let you = match line.as_bytes()[2] {
                    b'X' => Play::Rock,
                    b'Y' => Play::Paper,
                    b'Z' => Play::Scissors,
                    _ => panic!("you are not playing defined strategy!"),
                };
                Strategy { elf, you }
            })
            .collect()
    }
    
    part2
    use crate::utilities::*;
    
    pub enum GameResult {
        Win,
        Loss,
        Draw,
    }
    
    pub struct ResultStrategy {
        pub elf: Play,
        pub you: GameResult,
    }
    
    pub fn part2(input: &str) -> usize {
        parse(input)
            .iter()
            .map(gen_strategy)
            .map(|strat| calc_score(&strat))
            .sum()
    }
    
    fn gen_strategy(input: &ResultStrategy) -> Strategy {
        match input.you {
            GameResult::Win => Strategy {
                elf: input.elf,
                you: gen_win(input.elf),
            },
            GameResult::Draw => Strategy {
                elf: input.elf,
                you: input.elf,
            },
            GameResult::Loss => Strategy {
                elf: input.elf,
                you: gen_loss(input.elf),
            },
        }
    }
    
    fn gen_win(opponent: Play) -> Play {
        match opponent {
            Play::Rock => Play::Paper,
            Play::Paper => Play::Scissors,
            Play::Scissors => Play::Rock,
        }
    }
    
    fn gen_loss(opponent: Play) -> Play {
        match opponent {
            Play::Rock => Play::Scissors,
            Play::Paper => Play::Rock,
            Play::Scissors => Play::Paper,
        }
    }
    
    pub fn parse(input: &str) -> Vec<ResultStrategy> {
        input
            .lines()
            .map(|line| {
                let elf = match line.as_bytes()[0] {
                    b'A' => Play::Rock,
                    b'B' => Play::Paper,
                    b'C' => Play::Scissors,
                    _ => panic!("your opponent not playing defined strategy!"),
                };
                let you = match line.as_bytes()[2] {
                    b'X' => GameResult::Loss,
                    b'Y' => GameResult::Draw,
                    b'Z' => GameResult::Win,
                    _ => panic!("you are not playing defined strategy!"),
                };
                ResultStrategy { elf, you }
            })
            .collect()
    }
    
    3 votes
  10. kari
    Link
    I'm a big dumb dumb, so I just did it in the first way I thought of which is uh... not exactly elegant. Nim (both parts) import std/strutils type RockPaperScissorsMove = enum rpsmInvalid = 0,...

    I'm a big dumb dumb, so I just did it in the first way I thought of which is uh... not exactly elegant.

    Nim (both parts)
    import std/strutils
    
    type
      RockPaperScissorsMove = enum
        rpsmInvalid = 0, rpsmRock = 1, rpsmPaper = 2, rpsmScissors = 3
      RockPaperScissorsOutcome = enum
        rpsoLoss = 0, rpsoDraw = 3, rpsoWin = 6, rpsoInvalid
    
    proc calcScoreP1(myMove, theirMove: RockPaperScissorsMove): int {.inline.} = 
      if myMove == rpsmRock:
        if theirMove == rpsmRock:
          return int(rpsoDraw) + int(myMove)
        if theirMove == rpsmScissors:
          return int(rpsoWin) + int(myMove)
        if theirMove == rpsmPaper:
          return int(rpsoLoss) + int(myMove)
    
      if myMove == rpsmPaper:
        if theirMove == rpsmPaper:
          return int(rpsoDraw) + int(myMove)
        if theirMove == rpsmRock:
          return int(rpsoWin) + int(myMove)
        if theirMove == rpsmScissors:
          return int(rpsoLoss) + int(myMove)
    
      if myMove == rpsmScissors:
        if theirMove == rpsmScissors:
          return int(rpsoDraw) + int(myMove)
        if theirMove == rpsmPaper:
          return int(rpsoWin) + int(myMove)
        if theirMove == rpsmRock:
          return int(rpsoLoss) + int(myMove)
    
      return 0
    
    proc calcScoreP2(theirMove: RockPaperScissorsMove, desiredOutcome: RockPaperScissorsOutcome): int {.inline.} =
      if desiredOutcome == rpsoLoss:
        if theirMove == rpsmRock:
          return int(rpsoLoss) + int(rpsmScissors)
        if theirMove == rpsmPaper:
          return int(rpsoLoss) + int(rpsmRock)
        if theirMove == rpsmScissors:
          return int(rpsoLoss) + int(rpsmPaper)
    
      if desiredOutcome == rpsoDraw:
        if theirMove == rpsmRock:
          return int(rpsoDraw) + int(rpsmRock)
        if theirMove == rpsmPaper:
          return int(rpsoDraw) + int(rpsmPaper)
        if theirMove == rpsmScissors:
          return int(rpsoDraw) + int(rpsmScissors)
    
      if desiredOutcome == rpsoWin:
        if theirMove == rpsmRock:
          return int(rpsoWin) + int(rpsmPaper)
        if theirMove == rpsmPaper:
          return int(rpsoWin) + int(rpsmScissors)
        if theirMove == rpsmScissors:
          return int(rpsoWin) + int(rpsmRock)
    
      return 0
    
    proc day02*() = 
      var 
        scoreP1 = 0
        scoreP2 = 0
    
      # Assumes the input is in the correct format
      for line in lines("inputs/day02.in"):
        let moves = line.splitWhitespace()
        let theirMove: RockPaperScissorsMove = case moves[0]:
          of "A":
            rpsmRock
          of "B":
            rpsmPaper
          of "C":
            rpsmScissors
          else:
            rpsmInvalid
    
        let myMove: RockPaperScissorsMove = case moves[1]:
          of "X":
            rpsmRock
          of "Y":
            rpsmPaper
          of "Z":
            rpsmScissors
          else:
            rpsmInvalid
    
        let desiredOutcome: RockPaperScissorsOutcome = case moves[1]:
          of "X":
            rpsoLoss
          of "Y":
            rpsoDraw
          of "Z":
            rpsoWin
          else:
            rpsoInvalid
        
        scoreP1 += calcScoreP1(myMove, theirMove)
        scoreP2 += calcScoreP2(theirMove, desiredOutcome)
      
      echo("Part 1: " & $scoreP1)
      echo("Part 2: " & $scoreP2)
    
    2 votes
  11. soks_n_sandals
    Link
    I took a fairly straightforward and literal approach. I tried to use as few IF statements as possible for both parts. I'm timing my solutions and I'm happy to have both parts executing in around...

    I took a fairly straightforward and literal approach. I tried to use as few IF statements as possible for both parts. I'm timing my solutions and I'm happy to have both parts executing in around 0.1s.

    Part 1 + 2
    #!/usr/bin/env bash
    
    file=day2.dat
    part=2
    
    if [[ ${part} -eq 1 ]]; then
    # /////////////////////////////////////////////////////////////////////////////
    #part 1 0.13s
    
    score=0
    urPick=0
    opPick=0
    #   -- read file, check outcome
    while read -r line || [[ -n "${line}" ]]; do
    
    #   -- get points for just picking something
        if [[ "{$line:3:1}" =~ "X" ]]; then
            urPick=1
        elif [[ "{$line:3:1}" =~ "Y" ]]; then
            urPick=2
        else
            urPick=3
        fi
    
    #   -- winning plays
    #   -- rock (X) beats scissors (C), paper (Y) beats rock (A), scissors (Z) beat paper (B) | rock = 1 pt, paper = 2, scissors =3
        if [[ "${line}" =~ "C X" || "${line}" =~ "A Y" || "${line}" =~ "B Z" ]]; then
            (( score = score + 6 + urPick ))
        elif [[ "${line}" =~ "A X" || "${line}" =~ "B Y" || "${line}" =~ "C Z" || "${line}" =~ "Z C" || "${line}" =~ "Y B" || "${line}" =~ "X A" ]]; then
            (( score = score + urPick + 3 ))
        else
            (( score = score + urPick ))
        fi
    
    done < ${file}
    
    echo Part A $score
    # /////////////////////////////////////////////////////////////////////////////
    fi
    
    if [[ ${part} -eq 2 ]]; then
    # /////////////////////////////////////////////////////////////////////////////
    #part 2 0.08s
    score=0
    outcome=0
    opPick=0
    #   -- read file, check outcome
    while read -r line || [[ -n "${line}" ]]; do
    
    #   -- get points for just picking something
        if [[ "{$line:3:1}" =~ "X" ]]; then
            outcome=1 
        elif [[ "{$line:3:1}" =~ "Y" ]]; then
            outcome=2
        else
            outcome=3
        fi
    
    #   -- do whatever outcome is
        if [[ $outcome -eq 1 ]]; then #lose
            if [[ "{$line:1:1}" =~ "A" ]]; then
                urPick=3
            elif [[ "{$line:1:1}" =~ "B" ]]; then
                urPick=1
            else
                urPick=2
            fi
            (( score = score + urPick ))
        fi
    
        if [[ $outcome -eq 2 ]]; then #draw
            if [[ "{$line:1:1}" =~ "A" ]]; then
                urPick=1
            elif [[ "{$line:1:1}" =~ "B" ]]; then
                urPick=2
            else
                urPick=3
            fi
            (( score = score + urPick + 3 ))
        fi
    
        if [[ $outcome -eq 3 ]]; then #win
            if [[ "{$line:1:1}" =~ "A" ]]; then
                urPick=2
            elif [[ "{$line:1:1}" =~ "B" ]]; then
                urPick=3
            else
                urPick=1
            fi
            (( score = score + 6 + urPick ))
        fi
    
    done < ${file}
    
    echo Part B $score
    
    # /////////////////////////////////////////////////////////////////////////////
    fi
    
    2 votes
  12. Crespyl
    Link
    I'm confident that there's a tighter solution, but this is a simple puzzle and I'm in a lazy mood: Parts 1 & 2, Clojure (defn line-result [line] (case (first line) \A (case (last line) \X (+ 1 3)...

    I'm confident that there's a tighter solution, but this is a simple puzzle and I'm in a lazy mood:

    Parts 1 & 2, Clojure
    (defn line-result [line]
      (case (first line)
        \A (case (last line)
             \X (+ 1 3)
             \Y (+ 2 6)
             \Z (+ 3 0))
        \B (case (last line)
             \X (+ 1 0)
             \Y (+ 2 3)
             \Z (+ 3 6))
        \C (case (last line)
             \X (+ 1 6)
             \Y (+ 2 0)
             \Z (+ 3 3))))
    
    (defn part-1 [input]
      (reduce + (map line-result input)))
    
    (defn line-result-2 [line]
      (case (first line)
        \A (case (last line)
             \X (+ 3 0)
             \Y (+ 1 3)
             \Z (+ 2 6))
        \B (case (last line)
             \X (+ 1 0)
             \Y (+ 2 3)
             \Z (+ 3 6))
        \C (case (last line)
             \X (+ 2 0)
             \Y (+ 3 3)
             \Z (+ 1 6))))
    
    (defn part-2 [input]
      (reduce + (map line-result-2 input)))
    
    2 votes
  13. 0d_billie
    Link
    Well today's python was even jankier! Part 2 took me absolutely forever, because I mixed up which letter corresponded to which outcome, and was getting very whacky results. Part 1...

    Well today's python was even jankier! Part 2 took me absolutely forever, because I mixed up which letter corresponded to which outcome, and was getting very whacky results.

    Part 1
    #!/usr/bin/python3
    
    p1_options = ["A","B","C"]
    p2_options = ["X","Y","Z"]
    
    def get_option(choice,options):
        # convert letters to numbers
        return options.index(choice)
    
    def rock_paper_scissors(p1,p2):
        score = 0
        # win
        if p1 - p2 == -1 or p1 - p2 == 2:
            score += 6 + (p2 + 1)
        # draw
        if p1 == p2:
            score += 3 + (p2 + 1)
        # lose
        if p1 - p2 == 1 or p1 - p2 == -2:
            score += 0 + (p2 + 1)
    
        return score
    
    with open("input_proper_1") as input:
        total = 0
        for line in input:
            p1 = line.strip()[0]
            p1 = get_option(p1,p1_options)
    
            p2 = line.strip()[-1]
            p2 = get_option(p2,p2_options)
    
            total += rock_paper_scissors(p1,p2)
    
    print(total)
    
    Part 2
    #!/usr/bin/python3
    
    p1_options = ["A","B","C"]
    p2_options = ["X","Y","Z"]
    
    def get_option(choice,options):
        # convert letters to numbers
        return options.index(choice)
    
    def rock_paper_scissors(p1,p2):
        score = 0
        # win
        if p2 == 2:
            if p1 == 2:
                score += 6 + 1
            else:
                score += 6 + p1 + 2
        # draw
        if p2 == 1:
            score += 3 + (p1 + 1)
        # lose
        if p2 == 0:
            if p1 == 0:
                score += 3
            else:
                score += p1
        return score
    
    with open("input_proper_1") as input:
        total = 0
        for line in input:
            p1 = line.strip()[0]
            p1 = get_option(p1,p1_options)
    
            p2 = line.strip()[-1]
            p2 = get_option(p2,p2_options)
    
            total += rock_paper_scissors(p1,p2)
    
    print(total)
    
    General musing and possible spoiler I feel like if I knew how to make circular arrays, this would have been trivial to solve, since you could easily just make -1, 0, +1 correspond to win, draw, lose, respectively.
    2 votes
  14. wycy
    (edited )
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; #[derive(Debug,PartialEq,Eq)] enum GameResult { Win = 6, Draw = 3, Lose = 0, } impl From<&str> for GameResult...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    #[derive(Debug,PartialEq,Eq)]
    enum GameResult {
        Win = 6,
        Draw = 3,
        Lose = 0,
    }
    impl From<&str> for GameResult {
        fn from(c: &str) -> Self {
            match c {
                "X" => Self::Lose,
                "Y" => Self::Draw,
                "Z" => Self::Win,
                other => panic!("Unknown directive: {}",other),
            }
        }
    }
    
    #[derive(Copy,Clone,Debug,PartialEq,Eq)]
    enum Play {
        Rock = 1,
        Paper = 2,
        Scissors = 3,
    }
    impl From<&str> for Play {
        fn from(c: &str) -> Self {
            match c {
                "A" | "X" => Self::Rock,
                "B" | "Y" => Self::Paper,
                "C" | "Z" => Self::Scissors,
                other => panic!("Unknown play: {}",other),
            }
        }
    }
    
    // plays: (their play, my play)
    fn result(plays: &(Play,Play)) -> GameResult {
        use Play::*;
        use GameResult::*;
        match plays {
            (Rock,Rock) | (Paper,Paper) | (Scissors,Scissors) => { Draw }, 
            (Rock,Paper)     => { Win },
            (Rock,Scissors)  => { Lose },
            (Paper,Rock)     => { Lose },
            (Paper,Scissors) => { Win },
            (Scissors,Rock)  => { Win },
            (Scissors,Paper) => { Lose },
        }
    }
    
    fn play_from_result(their_play: Play, result: &GameResult) -> Play {
        use Play::*;
        use GameResult::*;
        match result {
            Draw => their_play,
            Win => {
                match their_play {
                    Rock     => Paper,
                    Paper    => Scissors,
                    Scissors => Rock,
                }},
            Lose => {
                match their_play {
                    Rock     => Scissors,
                    Paper    => Rock,
                    Scissors => Paper,
                }},
        }
    }
    
    fn plays(s: &String) -> (Play,Play) {
        let plays: Vec<_> = s.split_whitespace().collect();
        (Play::from(plays[0]), Play::from(plays[1]))
    }
    
    fn play_and_result(s: &String) -> (Play,GameResult) {
        let play_and_result: Vec<_> = s.split_whitespace().collect();
        (Play::from(play_and_result[0]), GameResult::from(play_and_result[1]))
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let part1 = input
            .iter()
            .map(|x| {
                let (their_play,my_play) = plays(x);
                let result = result(&(their_play,my_play));
                my_play as usize + result as usize
            })
            .sum::<usize>();
        println!("Part 1: {}", part1); // 11906
    
        // Part 2
        let part2 = input
            .iter()
            .map(|x| {
                let (play,result) = play_and_result(x);
                let my_play = play_from_result(play,&result);
                my_play as usize + result as usize
            })
            .sum::<usize>();
        println!("Part 2: {}", part2); //  11186
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  15. csos95
    Link
    I think I'm going to need to write a few extension modules for math/iterator stuff because the standard library functions for them seem to be pretty bare-bones compared to what I'm used to in...

    I think I'm going to need to write a few extension modules for math/iterator stuff because the standard library functions for them seem to be pretty bare-bones compared to what I'm used to in Rust.
    Another thing is that it feels like I'm having to write a ton of code compared to what I'd do for the same problem in Rust/Elixir.
    Hopefully it's just because I'm not used to the language and I'm doing some weird stuff (like the unreachable default cases in parseChoice/parceOutcome).

    Haxe
    using Lambda;
    using StringTools;
    
    enum Choice {
    	Rock;
    	Paper;
    	Scissors;
    }
    
    enum Outcome {
    	Lose;
    	Draw;
    	Win;
    }
    
    typedef Round = {
    	var p1 : Choice;
    	var p2 : Choice;
    }
    
    class Day2 {
    	static public function run(input: String) {
    		part1(input);
    		part2(input);
    	}
    
    	static function part1(input: String) {
    		var score = input
    			.trim()
    			.split("\n")
    			.map(parseRoundPart1)
    			.map(scoreRound)
    			.fold((score, total) -> score + total, 0);
    
    		Sys.println('day2 part1: ${score}');
    	}
    
    	static function part2(input: String) {
    		var score = input
    			.trim()
    			.split("\n")
    			.map(parseRoundPart2)
    			.map(scoreRound)
    			.fold((score, total) -> score + total, 0);
    
    		Sys.println('day2 part2: ${score}');
    	}
    
    	static function parseRoundPart1(round: String): Round {
    		var choices = round.split(" ");
    		var round = {
    			p1: parseChoice(choices[0]),
    			p2: parseChoice(choices[1]),
    		};
    
    		return round;
    	}
    
    	static function parseChoice(choice: String): Choice {
    		return switch (choice) {
    			case "A" | "X":
    				Rock;
    			case "B" | "Y":
    				Paper;
    			case "C" | "Z":
    				Scissors;
    			default:
    				Rock;
    		}
    	}
    
    	static function scoreRound(round: Round): Int {
    		return choiceScore(round.p2) + outcomeScore(round);
    	}
    
    	static function choiceScore(choice: Choice): Int {
    		return switch (choice) {
    			case Rock: 1;
    			case Paper: 2;
    			case Scissors: 3;
    		};
    	}
    
    	static function outcomeScore(round: Round): Int {
    		var p1Score = choiceScore(round.p1);
    		var p2Score = choiceScore(round.p2);
    		return if (p2Score == p1Score) {
    			3;
    		} else if (p2Score == p1Score + 1 || p2Score == p1Score - 2) {
    			6;
    		} else {
    			0;
    		};
    	}
    
    	static function parseRoundPart2(round: String): Round {
    		var choices = round.split(" ");
    		var p1Choice = parseChoice(choices[0]);
    		var outcome = parseOutcome(choices[1]);
    		var p2Choice = calculateChoice(p1Choice, outcome);
    		var round = {
    			p1: p1Choice,
    			p2: p2Choice,
    		};
    
    		return round;
    	}
    
    	static function parseOutcome(outcome: String): Outcome {
    		return switch (outcome) {
    			case "X":
    				Lose;
    			case "Y":
    				Draw;
    			case "Z":
    				Win;
    			default:
    				Lose;
    		};
    	}
    
    	static function calculateChoice(p1Choice: Choice, outcome: Outcome): Choice {
    		return switch [p1Choice, outcome] {
    			case [Rock, Lose] | [Paper, Win] | [Scissors, Draw]: Scissors;
    			case [Rock, Draw] | [Paper, Lose] | [Scissors, Win]: Rock;
    			case [Rock, Win] | [Paper, Draw] | [Scissors, Lose]: Paper;
    		};
    	}
    }
    
    1 vote
  16. Eabryt
    Link
    My Python solution is ugly and I knew it was ugly as I was writing it, but it works so I don't really care. Parts 1 & 2 def part1(lines): score = 0 values = {"X": 1, "Y": 2, "Z": 3} winner = {"A":...

    My Python solution is ugly and I knew it was ugly as I was writing it, but it works so I don't really care.

    Parts 1 & 2
    def part1(lines):
        score = 0
        values = {"X": 1, "Y": 2, "Z": 3}
        winner = {"A": "Y", "B": "Z", "C": "X"}
        tie = {"A": "X", "B": "Y", "C": "Z"}
        for line in lines:
            first, last = line.strip().split(' ')
            if last == winner[first]:
                score += 6
            elif last == tie[first]:
                score += 3
            score += values[last]
        print(f"Score: {score}")
    
    
    def part2(lines):
        score = 0
        values = {"X": 1, "Y": 2, "Z": 3}
        winner = {"A": "Y", "B": "Z", "C": "X"}
        tie = {"A": "X", "B": "Y", "C": "Z"}
        loser = {"A": "Z", "B": "X", "C": "Y"}
        for line in lines:
            first, last = line.strip().split(' ')
            if last == 'Z':
                throw = winner[first]
                score += 6
                score += values[throw]
            elif last == 'Y':
                throw = tie[first]
                score += 3
                score += values[throw]
            else:
                throw = loser[first]
                score += values[throw]
        print(f"Score: {score}")
    
    
    
    def openFile():
        return open("input.txt", "r").readlines()
    
    
    def main():
        f = openFile()
        lines = part1(f)
        part2(f)
    
    
    if __name__ == '__main__':
        main()
    
    1 vote
  17. vord
    Link
    So I think I'll win today's "Ugly Janky Contest." I was already feeling lazy with Part 1, and Part 2 threw me for enough of a loop that its a mess. Copying the map stuff I learned from yesterday's...

    So I think I'll win today's "Ugly Janky Contest." I was already feeling lazy with Part 1, and Part 2 threw me for enough of a loop that its a mess.

    Copying the map stuff I learned from yesterday's submissions here...real nice, I wish I had practiced more with it in my younger years. Also formally going into Typescript to avoid learning too many bad habits.

    Code
    import * as fs from 'node:fs/promises';
    
    function allEqual(array: string[]): boolean {
    	return array.every(a => { if (a == array[0]) {
    								return  true;
    							} }
    						);
    };
    
    function scoreRound_p1(round: string): number {
    	const throws = round.replace("X","A")
    						.replace("Y","B")
    						.replace("Z","C");
    	const winning_combos = ["A B", "B C", "C A"];
    	const win_score: number = 6;
    	const draw_score: number = 3;
    	const lose_score: number = 0;
    	const combos = throws.split(" ");
    	// Throwing Rock is 1 point
    	let throw_score: number = 1;
    
    	// Throwing Paper gives 2, Scissors 3
    	if (combos[combos.length-1] == "B") {
    		throw_score += 1;
    	} else if (combos[combos.length-1] == "C") {
    		throw_score += 2;
    	}
    
    	if (winning_combos.includes(throws)) {
    		throw_score += win_score;
    	}
    	else if (allEqual(combos)) {
    		throw_score += draw_score;
    	}
    	else {
    		throw_score += lose_score;
    	}
    	return throw_score;
    };
    
    function throwScore(enemy: string, goal: number): number {
    	let score: number = 1;
    	if (enemy == "A") {
    		if (goal == 0) {
    			score += 2;
    		} else if (goal == 6) {
    			score += 1;
    		};
    	} else if (enemy == "B"){
    		if(goal == 6){
    			score += 2;
    		} else if (goal == 3) {
    			score += 1;
    		};
    	} else {
    		if(goal == 3) {
    			score += 2;
    		} else if (goal == 0) {
    			score += 1;
    		};
    	};
    	return score;
    }
    ;
    
    function scoreRound_p2(round: string): number {
    
    	const win_score: number = 6;
    	const draw_score: number = 3;
    	const lose_score: number = 0;
    	const combos = round.split(" ");
    	let score: number = 0;
    
    	if (combos[combos.length-1] == "X") {
    		score += lose_score;
    	} else if (combos[combos.length-1] == "Z") {
    		score += win_score;
    	} else {
    		score += draw_score;
    	}
    	let total: number = throwScore(combos[0],score) + score;
    
    	return total;
    };
    
    async function main(): Promise<void> {
    	const file = await fs.readFile("input/day2","utf-8");
    	let rounds = file.split("\n");
    	
    	//let scores: number[] = rounds.map( (round: string, index: number) => scoreRound_p1(round))
    	let scores: number[] = rounds.map( (round: string, index: number) => scoreRound_p2(round))
    
    	let total_score: number = scores.reduce((score: number, a: number) => score + a,0);
    	console.log(total_score);
    }
    
    main();
    
    1 vote
  18. Gyrfalcon
    Link
    I am actually happier with this than I thought I would be. I did most of the thinking and worked out the values for all the combinations, and just let Python crank on the calculating. I am also...

    I am actually happier with this than I thought I would be. I did most of the thinking and worked out the values for all the combinations, and just let Python crank on the calculating. I am also really happy with how I decided to do my testing, although I think if I tried harder I could probably make something that dynamically builds up the tests as I add days. Either way, it comes in handy. This code is also a little nicer because I actually ran and listened to mypy and black before posting my code! I am also going to leave out most of my fixture code, but if anyone is interested, just ask.

    Parts 1 and 2
    scoring = {
        "A X": 4,
        "A Y": 8,
        "A Z": 3,
        "B X": 1,
        "B Y": 5,
        "B Z": 9,
        "C X": 7,
        "C Y": 2,
        "C Z": 6,
    }
    
    decoded_scoring = {
        "A X": 3,
        "A Y": 4,
        "A Z": 8,
        "B X": 1,
        "B Y": 5,
        "B Z": 9,
        "C X": 2,
        "C Y": 6,
        "C Z": 7,
    }
    
    
    def score_game(game: str, decoded: bool = False) -> int:
        return decoded_scoring[game] if decoded else scoring[game]
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        games = load_file.load_cleaned_lines(filepath)
        return (
            sum([score_game(game) for game in games]),
            sum([score_game(game, True) for game in games]),
        )
    

    And my new file loader that works in the way it probably should have from the beginning:

    def load_cleaned_lines(filename: str) -> list[str]:
        with open(filename, "r") as fp:
            lines = fp.readlines()
    
        return [line.strip() for line in lines]
    
    1 vote
  19. balooga
    Link
    Here's my TypeScript solution const POINTS_FOR_PLAYING_ROCK = 1; const POINTS_FOR_PLAYING_PAPER = 2; const POINTS_FOR_PLAYING_SCISSORS = 3; const POINTS_FOR_VICTORY = 6; const POINTS_FOR_DRAW = 3;...
    Here's my TypeScript solution
    const POINTS_FOR_PLAYING_ROCK = 1;
    const POINTS_FOR_PLAYING_PAPER = 2;
    const POINTS_FOR_PLAYING_SCISSORS = 3;
    
    const POINTS_FOR_VICTORY = 6;
    const POINTS_FOR_DRAW = 3;
    const POINTS_FOR_DEFEAT = 0;
    
    const OPPONENT_CHOOSES_ROCK = 'A';
    const OPPONENT_CHOOSES_PAPER = 'B';
    const OPPONENT_CHOOSES_SCISSORS = 'C';
    
    type InputData = [string, string][];
    
    function formatInput(input: string): InputData {
      return input.split('\n').map(e => e.split(' ')) as InputData;
    }
    
    export function run(input: string): string[] {
      const data = formatInput(input);
    
    Part 1
      const scoreBothPlayersMoves = matches => {
        const PLAYER_CHOOSES_ROCK = 'X';
        const PLAYER_CHOOSES_PAPER = 'Y';
        const PLAYER_CHOOSES_SCISSORS = 'Z';
        const outcomes = {
          [PLAYER_CHOOSES_ROCK]: {
            [OPPONENT_CHOOSES_ROCK]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_ROCK,
            [OPPONENT_CHOOSES_PAPER]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_ROCK,
            [OPPONENT_CHOOSES_SCISSORS]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_ROCK,
          },
          [PLAYER_CHOOSES_PAPER]: {
            [OPPONENT_CHOOSES_ROCK]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_PAPER,
            [OPPONENT_CHOOSES_PAPER]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_PAPER,
            [OPPONENT_CHOOSES_SCISSORS]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_PAPER,
          },
          [PLAYER_CHOOSES_SCISSORS]: {
            [OPPONENT_CHOOSES_ROCK]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_SCISSORS,
            [OPPONENT_CHOOSES_PAPER]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_SCISSORS,
            [OPPONENT_CHOOSES_SCISSORS]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_SCISSORS,
          },
        };
        let score = 0;
        for (const match of matches) {
          score += outcomes[match[1]][match[0]];
        }
        return score;
      };
    
    Part 2
    const scoreExpectedOutcomes = matches => {
        const EXPECTED_OUTCOME_IS_DEFEAT = 'X';
        const EXPECTED_OUTCOME_IS_DRAW = 'Y';
        const EXPECTED_OUTCOME_IS_VICTORY = 'Z';
        const outcomes = {
          [OPPONENT_CHOOSES_ROCK]: {
            [EXPECTED_OUTCOME_IS_DEFEAT]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_SCISSORS,
            [EXPECTED_OUTCOME_IS_DRAW]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_ROCK,
            [EXPECTED_OUTCOME_IS_VICTORY]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_PAPER,
          },
          [OPPONENT_CHOOSES_PAPER]: {
            [EXPECTED_OUTCOME_IS_DEFEAT]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_ROCK,
            [EXPECTED_OUTCOME_IS_DRAW]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_PAPER,
            [EXPECTED_OUTCOME_IS_VICTORY]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_SCISSORS,
          },
          [OPPONENT_CHOOSES_SCISSORS]: {
            [EXPECTED_OUTCOME_IS_DEFEAT]: POINTS_FOR_DEFEAT + POINTS_FOR_PLAYING_PAPER,
            [EXPECTED_OUTCOME_IS_DRAW]: POINTS_FOR_DRAW + POINTS_FOR_PLAYING_SCISSORS,
            [EXPECTED_OUTCOME_IS_VICTORY]: POINTS_FOR_VICTORY + POINTS_FOR_PLAYING_ROCK,
          },
        };
        let score = 0;
        for (const match of matches) {
          score += outcomes[match[0]][match[1]];
        }
        return score;
      };
    
      return [`${scoreBothPlayersMoves(data)}`, `${scoreExpectedOutcomes(data)}`];
    }
    
    1 vote
  20. whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_2.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let total_score = contents.lines().fold(0, |acc, line| {
    		assert!(line.len() >= 3);
    		let me = line.chars().nth(2).unwrap();
    		let opponent = line.chars().next().unwrap();
    		acc + Move::score(Move::of(me), Move::of(opponent))
    	});
    	println!("{total_score}");
    	Ok(())
    }
    
    enum Move {
    	Rock,
    	Paper,
    	Scissors,
    }
    
    impl Move {
    	fn of(s: char) -> Move {
    		match s {
    			'A' | 'X' => Move::Rock,
    			'B' | 'Y' => Move::Paper,
    			'C' | 'Z' => Move::Scissors,
    			_ => unreachable!(),
    		}
    	}
    
    	fn score(me: Move, opponent: Move) -> u64 {
    		match (me, opponent) {
    			(Self::Rock, Self::Rock) => 4, // 1 for rock, 3 because rock ties rock
    			(Self::Rock, Self::Paper) => 1, // 1 for rock, 0 because rock loses to paper
    			(Self::Rock, Self::Scissors) => 7, // 1 for rock, 6 because rock beats scissors 
    			(Self::Paper, Self::Rock) => 8, // 2 for paper, 6 because paper beats rock
    			(Self::Paper, Self::Paper) => 5, // 2 for paper, 3 because paper ties paper
    			(Self::Paper, Self::Scissors) => 2, // 2 for paper, 0 because paper loses to scissors
    			(Self::Scissors, Self::Rock) => 3, // 3 for scissors, 0 because scissors loses to rock
    			(Self::Scissors, Self::Paper) => 9, // 3 for scissors, 6 because scissors beats paper
    			(Self::Scissors, Self::Scissors) => 6, // 3 for scissors, 3 because scissors ties scissors
    		}
    	}
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_2.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let total_score = contents.lines().fold(0, |acc, line| {
    		assert!(line.len() >= 3);
    		let me = line.chars().nth(2).unwrap();
    		let opponent = Move::of(line.chars().next().unwrap());
    		acc + Move::score(Outcome::of(me).to_move(opponent.clone()), opponent)
    	});
    	println!("{total_score}");
    	Ok(())
    }
    
    #[derive(Clone, PartialEq, Eq)]
    enum Move {
    	Rock,
    	Paper,
    	Scissors,
    }
    
    impl Move {
    	fn of(s: char) -> Move {
    		match s {
    			'A' | 'X' => Move::Rock,
    			'B' | 'Y' => Move::Paper,
    			'C' | 'Z' => Move::Scissors,
    			_ => unreachable!(),
    		}
    	}
    
    	fn beats(&self) -> Move {
    		match self {
    			Move::Rock => Move::Scissors,
    			Move::Paper => Move::Rock,
    			Move::Scissors => Move::Paper,
    		}
    	}
    
    	fn loses_to(&self) -> Move {
    		match self {
    			Move::Rock => Move::Paper,
    			Move::Paper => Move::Scissors,
    			Move::Scissors => Move::Rock,
    		}
    	}
    
    	fn ties(&self) -> Move {
    		return self.clone()
    	}
    
    	fn intrinsic_score(&self) -> u64 {
    		match self {
    			Move::Rock => 1,
    			Move::Paper => 2,
    			Move::Scissors => 3,
    		}
    	}
    
    	fn score(me: Move, opponent: Move) -> u64 {
    		me.intrinsic_score() + if me.loses_to() == opponent {
    			0
    		} else if me.ties() == opponent {
    			3
    		} else {
    			6
    		}
    	}
    }
    
    enum Outcome {
    	Win,
    	Lose,
    	Tie,
    }
    
    impl Outcome {
    	fn of(s: char) -> Outcome {
    		match s {
    			'X' => Outcome::Lose,
    			'Y' => Outcome::Tie,
    			'Z' => Outcome::Win,
    			_ => unreachable!(),
    		}
    	}
    
    	fn to_move(&self, other: Move) -> Move {
    		match self {
    			Outcome::Win => other.loses_to(),
    			Outcome::Lose => other.beats(),
    			Outcome::Tie => other.ties(),
    		}
    	}
    }
    
    1 vote