[Advent Of Code 2022] Day 03 Discussion [SPOILERS]

That was a fun day!

The Exercism exercises Pangram and Sets helped me here :slight_smile:

Python Solution
    INPUT_TYPES = str

    def part1(self, parsed_input: InputType) -> int:
        """Find the common element across the first and second half of each line."""
        score = 0
        for line in parsed_input:
            middle = len(line) // 2
            a, b = line[:middle], line[middle:]
            common = set(a) & set(b)
            assert len(common) == 1
            score += SCORING.index(common.pop())

        return score

    def part2(self, parsed_input: InputType) -> int:
        """Find the common element across groups of three lines."""
        score = 0
        for lines in more_itertools.chunked(parsed_input, 3):
            common = set(lines[0]) & set(lines[1]) & set(lines[2])
            assert len(common) == 1
            score += SCORING.index(common.pop())

        return score
3 Likes

Working with sets in Python is so great that I feel at a disadvantage by not solving problems with sets in Python :smiley:

Typescript Solution

Complete code: Advent-Of-Code/day03.ts at master ยท andrerfcsantos/Advent-Of-Code ยท GitHub

type Backpack = string;

interface State {
  backpacks: Backpack[];
}

export function parse(input: string): State {
  return { backpacks: nonEmptyLines(input) };
}

export function part1(parsed: State): string {
  const commonItems = parsed.backpacks.map(findCommonItemTypeInCompartiments);
  const priorities = commonItems.map(getItemPriority);
  return sumNumbers(priorities).toString();
}

export function part2(parsed: State): string {
  const groups = chunkArray(parsed.backpacks, 3);

  const badgesforGroups = groups.map((group) => {
    const backpacks = group.map((backpack) => new Set(backpack));
    return Array.from(intersectSets(...backpacks))[0];
  });

  const priorities = badgesforGroups.map(getItemPriority);

  return sumNumbers(priorities).toString();
}

function findCommonItemTypeInCompartiments(backpack: Backpack): string {
  const middle = backpack.length / 2;
  const compartiments = [
    new Set(backpack.slice(0, middle)),
    new Set(backpack.slice(middle, backpack.length)),
  ];
  return Array.from(intersectSets(...compartiments))[0];
}

const LOWER_A = "a".charCodeAt(0);
const UPPER_A = "A".charCodeAt(0);

function getItemPriority(item: string): number {
  const c = item.charCodeAt(0);
  const diff = isUppercase(item) ? c - UPPER_A + 26 : c - LOWER_A;
  return diff + 1;
}
1 Like

I was also wondering if sets were the best approach. It is my longest-running problem so far. You think it would be better to get it done with some string magic?

Solution in Elixir
defmodule AdventOfCode.Day03 do
  def part1(args) do
    args
    |> String.split()
    |> Enum.map(&splitInHalf/1)
    |> Enum.map(&findDuplicate/1)
    |> Enum.map(&convertToPriority/1)
    |> Enum.sum()
  end

  def splitInHalf(str), do: String.split_at(str, div(String.length(str), 2))

  def findDuplicate({str1, str2}),
    do:
      MapSet.intersection(
        MapSet.new(String.codepoints(str1)),
        MapSet.new(String.codepoints(str2))
      )
      |> MapSet.to_list()
      |> hd()

  def findCommon([str1, str2, str3]),
    do:
      MapSet.intersection(
        MapSet.new(String.codepoints(str1)),
        MapSet.intersection(
          MapSet.new(String.codepoints(str2)),
          MapSet.new(String.codepoints(str3))
        )
      )
      |> MapSet.to_list()
      |> hd()

  def convertToPriority(<<c::utf8, _::binary>>) when c <= ?z and c >= ?a, do: c - ?a + 1
  def convertToPriority(<<c::utf8, _::binary>>) when c <= ?Z and c >= ?A, do: c - ?A + 27
  def convertToPriority(_), do: :error

  def part2(args) do
    args
    |> String.split()
    |> Stream.chunk_every(3)
    |> Enum.map(&findCommon/1)
    |> Enum.map(&convertToPriority/1)
    |> Enum.sum()
  end
end

@iHiD Can we have some nice syntax highlighting in Elixir code snippets?

1 Like

I need to learn to read directions more carefully. I thought part 2 required you to find the sets of 3 instead of just evaluating progressive groups of three.

I am still a relatively newbie, and feedback is welcome.

go Solution
func part1(lines []string) (res int) {
	for _, line := range lines {
		split := len(line) / 2
		res += decodeBinary(encodeBinary(line[:split]) & encodeBinary(line[split:]))
	}
	return res
}

func part2(lines []string) (res int) {
	for i := 0; i < len(lines); i += 3 {
		set := ^uint64(0)
		for _, line := range lines[i : i+3] {
			set &= encodeBinary(line)
		}
		res += decodeBinary(set)
	}
	return res
}

func decodeBinary(b uint64) (res int) {
	for b >>= 1; b > 0; res++ {
		b >>= 1
	}
	return res
}

func encodeBinary(line string) (res uint64) {
	for _, char := range line {
		res |= (1 << priority(char))
	}
	return res
}

func priority(char rune) int {
	switch {
	case unicode.IsLower(char):
		return int(char-'a') + 1
	default:
		return int(char-'A') + 27
	}
}
1 Like

This went a lot easier than Day 2, and Iโ€™ll have some fun cleaning this one up later I think.

Julia Solution

module day03
priorities = Dict(char => v for (char , v) in zip([โ€˜aโ€™:โ€˜zโ€™; โ€˜Aโ€™:โ€˜Zโ€™], 1:52))
compartments(rucksack) = [rucksack[1:div(end,2)], rucksack[div(end,2)+1:end]]

function part01()        
    score = 0
    for l in eachline("/Users/anagy/Documents/AdventOfCode2022/AdventOfCode2022-Julia/input/03/input.in")
        if length(l) != 0
            left, right = compartments(l)
            uniq = intersect(left, right)[1]
            score += priorities[uniq]
        end
    end
    score
end

function part02()
    score = 0
    groups = eachline("/Users/anagy/Documents/AdventOfCode2022/AdventOfCode2022-Julia/input/03/input.in")
    for group in Iterators.partition(groups, 3)
        uniq = intersect(group...)[1]
        score += priorities[uniq]
    end
    score
end

end
println(โ€œP01: (day03.part01())") println("P02: (day03.part02())โ€)

Ruby Solution
PRIORITIES = [nil, *'a'..'z', *'A'..'Z'].freeze

def priority(item_types) = PRIORITIES.index(item_types.map(&:chars).reduce(:&).first)

rucksacks = File.readlines('inputs/03.txt', chomp: true)
a = rucksacks.sum { |rucksack| priority(rucksack.partition(/.{#{rucksack.size/2}}/)[1..]) }
b = rucksacks.each_slice(3).sum { |group| priority(group) }