Skip to content

Instantly share code, notes, and snippets.

@bsadia
Last active January 5, 2023 16:52
Show Gist options
  • Save bsadia/0ce9f1bd214d980e813ba2e458cb5267 to your computer and use it in GitHub Desktop.
Save bsadia/0ce9f1bd214d980e813ba2e458cb5267 to your computer and use it in GitHub Desktop.
Advent of code 2022 in Julia

Advent of Code 2022 in Julia

Day 01:

# initialization
elf_calories = 0 # stores calories for each elf
max_elf_calories = 0 # stores maximum calories
list_elf_calories = [] # A list to store each elf's total calories 
for l in calories
	if !isempty(l)
		elf_calories +=  l
	else
		if elf_calories > max_elf_calories
			max_elf_calories = elf_calories
		end
		list_elf_calories = push!(list_elf_calories, elf_calories)
		elf_calories = 0
	end
end
sort!(list_elf_calories, rev = true) # sorting in decending order
top_three_elf_Calories = sum(list_elf_calories[1:n]) # adding top 3 

return max_elf_calories, top_three_elf_Calories
end

# read input file 
calories = readdlm("data/input_day01.txt", skipblanks=false) 
max_cal, max_n_cal = max_elf_calories(calories, 3)
println("Part 1: The total calories are: ", max_cal)
println("Part 2: The total calories carrying by the top three elfs are :", max_n_cal)

Day 02:

Part 01:

function total_score(guide)
	
	total_score = 0
	winner_rule = ["A Y", "B Z", "C X"] # rules for winn
	lose_rule = ["A Z", "B X", "C Y"] # rules for loss
	draw_rule = ["A X", "B Y", "C Z"] # rules for draw
	
	for item in guide
		
		if item in winner_rule 
			total_score += endswith(item, "X") ? 7 :
			endswith(item, "Y") ? 8 : 9
		end

		if item in lose_rule
			total_score += endswith(item, "X") ? 1 : 
			endswith(item, "Y") ? 2 : 3
		end
		
		
		if item in draw_rule
			total_score += endswith(item, "X") ? 4 : 
			endswith(item, "Y") ? 5 : 6
		end
	end
	
	return total_score
end

Part 02:

function total_score_with_diff_Strategy(guide)
	new_score = 0

	winner_rule = ["A Y", "B Z", "C X"] # 
	loss_rule = ["A Z", "B X", "C Y"]
	draw_rule = ["A X", "B Y", "C Z"]
	
	for item in guide
		# This block defines our new strategy for winning
		if endswith(item, "Z") 
			temp = winner_rule[findfirst(contains(first(item)), winner_rule)]
			new_score += endswith(temp, "X") ? 7 :
			endswith(temp, "Y") ? 8 : 9
		end

		# This block defines our new strategy for losing
		if endswith(item, "X") 
			temp = loss_rule[findfirst(contains(first(item)), loss_rule)]
			new_score += endswith(temp, "X") ? 1 : 
			endswith(temp, "Y") ? 2 : 3
		end
		
		# This block defines our new strategy for draw
		if endswith(item, "Y") 
			temp = draw_rule[findfirst(contains(first(item)), draw_rule)]
			new_score += endswith(temp, "X") ? 4 : 
			endswith(temp, "Y") ? 5 : 6
		end
		
	end
	return new_score
end

Day 03:

Part 01:

function same_items(data::Vector{String}, priorities::Dict)
	
	total_sum::Int64 = 0
	
	
	for str in data
		common_item = str[1:length(str) ÷2]  str[length(str) ÷2+1:end] # \cap + <TAB> for intersection (∩) symbol \div + <TAB> for ÷ symbol
		total_sum += priorities[common_item[1]]
	end
	
	return total_sum
end

Part 02:

function badge_items(data::Vector{String}, priorities::Dict)
	
	total_sum::Int64 = 0
	
	for str in 1:3:length(data)
		common_item = (data[str:str+2]...)
		total_sum += priorities[common_item[1]]
	end
	
	return total_sum
end
data = readlines("data/input_day03.txt")
numbers = collect(1:52)
alphabets = collect("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
priorities = Dict(alphabets[i] => numbers[i] for i in 1:52)
println("Part 01: Sum of the prioroties of common items: ", same_items(data, priorities))
println("Part 02: Sum of priorities of badge items: ", badge_items(data, priorities))

Day 04:

function camp_cleanup(input::Vector{String})
	
	common_count::Int64 = 0
	overlap_count::Int64 = 0
	
	assignment = map(s->parse.(Int64, s), split.(input, r"\-|,"))
	
	for pairs in assignment
		intersect(pairs[1]:pairs[2], pairs[3]:pairs[4])  (pairs[1]:pairs[2],  pairs[3]:pairs[4]) ? common_count += 1 : nothing

		!isempty(intersect(pairs[1]:pairs[2], pairs[3]:pairs[4])) ? overlap_count += 1 : nothing
			
	end
	return [common_count, overlap_count]
end

Day 05:

function parse_data(crates_data)
	
	total_stacks = split(crates_data[end])
	# Create empty crates to fill with data later
	stacks = [Stack{Char}() for i in 1:length(total_stacks)]
	
	for i in length(crates_data)-1:-1:1
		crate = crates_data[i][2:4:end]
		
		for j in 1:length(crate)
			!isequal(crate[j], ' ') ? push!(stacks[j], crate[j]) : nothing
		end
	end
	return stacks
end 
function day_05(stacks_01, stacks_02, instructions)
	
	for i in instructions
		if !isempty(i)
			m = match(r".*(?<crate> \d+) .* (?<from>\d+) .* (?<to>\d+)", i)
			crate, from, to = parse.(Int, m)

			# Part 1:
			pop_items_1 = [pop!(stacks_01[from]) for i in 1:crate]
			[push!(stacks_01[to], j) for j in pop_items_1]
		
			# Part 2:
			pop_items_02 = reverse!([pop!(stacks_02[from]) for i in 1:crate])
     		[push!(stacks_02[to], j) for j in pop_items_02]
		end
	end
	
	return [String([first(c) for c in stacks_01]), String([first(c) for c in stacks_02])]
end	
	# using DataStructures # This package is required to run this code.
	input = read("data/input_day05.txt", String)
	crates, instructions = split.(split(input, "\n\n"), "\n")
	part_01 = parse_data(crates)
	part_02 = parse_data(crates)

	top_crate_1, top_crate_2= day_05(part_01, part_02, instructions)
	println("Part 01: Top crates are: ", top_crate_1)
	println("Part 02: Top crates are: ", top_crate_2)
	

Day 06:

function unique_marker(datastream, marker_size)
	buffer::Vector{Char} = []
	for signal in datastream
		for i in 1:length(signal) # characters
			append!(buffer, signal[i])
			 if isequal(length(buffer), marker_size)
			 	allunique(buffer) ? [return i] :  popfirst!(buffer)
			 end
		end
	end
end

data = split(read("data/input_day06.txt", String), "\n")
println("Part 1: Total characters to process are: ", unique_marker(data, 4))
println("Part 2: Total characters to process are: ", unique_marker(data, 14))

Day 07:

using PEG
using Test

function handle_dir(words)
    return words
end

function handle_file(files)
    path = join(dir_path);
    if get(dirSize, path, "nothing") == "nothing"
        append!(dirSize[path], files[1]) 
    else
        dirSize[path] =  dirSize[path] + files[1]
        tmpDir = deepcopy(dir_path)
        pop!(tmpDir)
        while length(tmpDir) > 0
            dirSize[join(tmpDir)] = dirSize[join(tmpDir)] + files[1]
            pop!(tmpDir)
        end
    end   
    return files
end

function handle_cmd(words)   
    if words[3] == "cd"
        if words[5] == ".."
            pop!(dir_path)
        else
            push!(dir_path, words[5])
            dirSize[join(dir_path)] = 0 
        end
    end
    return words
end

function day_07(data::Vector{SubString{String}}, part::Int64)
    global dir_path = String[]
    global dirSize = Dict{String,Int64}()
    # Rules for parsing the input data
    @rule cmdSpace = r"\s*"
    @rule cmdStart = "\$"
    @rule cmdArgs = r"[\.a-z\s\/]*"
    @rule cmd = r"[a-z]+"
    @rule sizeRule = r"[0-9]+" |> x -> parse(Int, x)
    @rule iCmd = cmdStart & cmdSpace & cmd & cmdSpace & cmdArgs |> handle_cmd
    @rule num = sizeRule & cmdArgs |> handle_file
    @rule dire = cmd & cmdArgs |> handle_dir
    @rule exper = iCmd, num, dire
    
         
    [parse_whole(exper, l) for l in data]
    

    # Part 1
    part == 1 && return sum([s for s in values(dirSize) if s <= 100000])

    # Part 2
    if (part == 2)

        total_size = 70000000
        size_needed = 30000000

        space_used = [dirSize[s] for s in keys(dirSize) if s == "/"]
        free_space = total_size - space_used[1]
        space_Needed = size_needed - free_space
        return minimum([s for s in values(dirSize) if s > space_Needed])
    else
        return error("Part not implemented. Please provide a valid part number between 1 and 2.")
    end
end

test_input = split(read("data_aoc/test_day07.txt", String), "\n")

@test (day_07(test_input, 1) == 95437)
@test (day_07(test_input, 2) == 24933642)

data = split(read("data_aoc/input_day07.txt", String), "\n")

println("Part 1: The size of the total size is ", day_07(data, 1))
println("Part 2: The size of the directory to delete is  ", day_07(data, 2))

Day 08:

function get_visible_trees(data::Matrix{Int64}) # Part 1
	
    visible_trees::Int64 = 0
    for x in axes(data,1), y in axes(data, 2)
        curr_val = data[x,y]
        top = data[begin:x-1, y]
        bottom = data[x+1:end, y]
        left = data[x, begin:y-1]
        right = data[x, y+1:end]
        if(curr_val .> maximum(top, init = -99) ||
            curr_val > maximum(bottom, init = -99) || 
            curr_val > maximum(left, init = -99) || 
            curr_val > maximum(right, init = -99))
            visible_trees += 1
        end
    end
	
    return visible_trees
end

unction get_scenic_score(data::Matrix{Int64})
	
    scenic_score = Int[]
	
    for x in axes(data,1), y in axes(data, 2)
        curr_val = data[x,y]
        top = reverse(data[begin:x-1, y])
        bottom = (data[x+1:end, y])
        left = reverse(data[x, begin:y-1])
        right = (data[x, y+1:end])

        top_score = isnothing(findfirst(curr_val .<= top)) ? length(top) : findfirst(curr_val .<= top)

        bottom_score = isnothing(findfirst(curr_val .<= bottom)) ? length(bottom) : findfirst(curr_val .<= bottom) 

        left_score = isnothing(findfirst(curr_val .<= left)) ? length(left) : findfirst(curr_val .<= left)
		
        right_score = isnothing(findfirst(curr_val .<= right)) ? length(right) : findfirst(curr_val .<= right)

        push!(scenic_score, (top_score * bottom_score * left_score * right_score))
    end
	
    return maximum(scenic_score)
end


test_file = "data/test_day08.txt"
test_input = vcat(map(x->(parse.(Int64, x))', collect.(readlines(test_file)))...)
	
@test (get_visible_trees(test_input) == 21)
@test (get_scenic_score(test_input) == 8)
	
file = "data/input_day08.txt"
input = vcat(map(x->(parse.(Int64, x))', collect.(readlines(file)))...)
	
println("Part 01: Total number of trees visible are: ", get_visible_trees(input))
println("Part 02: The highest scenic score is: ", get_scenic_score(input))

Day 09:

Part 1:

function day09_01(data)
    tail_movements = 0
    visited_positions = Array{Int64,1}[]
    head_movements = Array{Int64,1}[]
    head_val = [0, 0]
    tail_val = [0, 0]
    #moves = U D L R
    for moves in data
        direction, value = split(moves, " ")
        value = parse(Int, value)
        for i in 1:value
            direction == "U" ? head_val[2] += 1 :
            direction == "D" ? head_val[2] -= 1 :
            direction == "R" ? head_val[1] += 1 :
            direction == "L" ? head_val[1] -= 1 : nothing

            push!(head_movements, copy(head_val))

            if head_val[1] == tail_val[1] + 2 || head_val[1] == tail_val[1] - 2 ||
               head_val[2] == tail_val[2] + 2 || head_val[2] == tail_val[2] - 2
                tail_val[1] += sign(head_val[1] - tail_val[1])
                tail_val[2] += sign(head_val[2] - tail_val[2])

            elseif head_val[1] != tail_val[1] && head_val[2] != tail_val[2] &&
                   head_val[1] != tail_val[1] + 1 && head_val[1] != tail_val[1] - 1 &&
                   head_val[2] != tail_val[2] + 1 && head_val[2] != tail_val[2] - 1
                tail_val[1] += sign(head_val[1] - tail_val[1])
                tail_val[2] += sign(head_val[2] - tail_val[2])

            end
            #tail_movements += 1
            push!(visited_positions, copy(tail_val))

        end

    end
    return visited_positions
end

Part 2:

function day09_02(data)

    ninth_tail_positions = Array{Int64,1}[]
    head_movements = Array{Int64,1}[]
    head_val = [0, 0]
    all_tails = [] 
    nine_tail_movements = 0
    
    for k in 1:9
        push!(all_tails, [0, 0])
    end
    for moves in data
        direction, value = split(moves, " ")
        value = parse(Int, value)
        for i in 1:value
            if direction == "U"
                head_val[2] += 1
            elseif direction == "D"
                head_val[2] -= 1
            elseif direction == "R"
                head_val[1] += 1
            elseif direction == "L"
                head_val[1] -= 1
            end
            push!(head_movements, copy(head_val))
            temp = deepcopy(head_val)
            for j in 1:9
                if temp[1] == all_tails[j][1] + 2 || temp[1] == all_tails[j][1] - 2 ||
                   temp[2] == all_tails[j][2] + 2 || temp[2] == all_tails[j][2] - 2
                    all_tails[j][1] += sign(temp[1] - all_tails[j][1])
                    all_tails[j][2] += sign(temp[2] - all_tails[j][2])
                   
                elseif temp[1] != all_tails[j][1] && temp[2] != all_tails[j][2] &&
                       temp[1] != all_tails[j][1] + 1 && temp[1] != all_tails[j][1] - 1 &&
                       temp[2] != all_tails[j][2] + 1 && temp[2] != all_tails[j][2] - 1
                    all_tails[j][1] += sign(temp[1] - all_tails[j][1])
                    all_tails[j][2] += sign(temp[2] - all_tails[j][2])
                end
                temp = deepcopy(all_tails[j])
               # nine_tail_movements += 1
            end
	    
            push!(ninth_tail_positions, copy(all_tails[9]))


        end

    end
    return ninth_tail_positions
end
data = readlines("data_aoc/input_day09.txt");

visited_positions = day09_01(data);
ninth_tail_pos = day09_02(data);

println("Part 1: points that tail visited  atleast once are: ", length(unique!(visited_positions)))
println("Part 2: points that 9th tail visited  atleast once are: ", length(unique!(ninth_tail_pos)))

Day 10:

using DataStructures
function day_10(data)
  cycle_x = SortedDict() 
  X = 1 # initialize X
  cycle = 0 #initial value of cycle
  cycle_x[cycle] = X # add initial value to dictionary
  for instruction in data
    if instruction == "noop"
      cycle += 1 
      cycle_x[cycle] = X
    elseif startswith(instruction, "addx ")
      cycle_x[cycle+1] = cycle_x[cycle+2] = X
      cycle += 2
      X += parse(Int, instruction[6:end])
    end
  end
  return cycle_x
end

input = readlines("data_aoc/input_day10.txt");
signal_strength = day_10(input);

sum_signal_Strength = sum([signal_strength[s] * s for s in keys(signal_strength) if s in [20, 60, 100, 140, 180, 220]]);
println("Part 01: The sum of signal strength is: $(sum_signal_Strength). \n");

# Part 2
lit = [(mod((index-1), 40) in value - 1:value + 1 ? "#" : ".") for (index, value) in pairs(signal_strength)];
[(mod((i-1), 40) == 39 ? println(val) : print(val)) for (i, val) in enumerate(lit)];

Day 11:

# Parsing input data
function parse_input(data)
    parsed_input = []

    number = [parse(Int, filter(isnumeric, strip.(split(l, " "), ',')[2])) + 1 for l in data if startswith(l, "Monkey")]

    items = [parse.(Int, strip.(split(l, " "), ',')[5:end]) for l in data if contains(l, "Starting items")]

    operation = [eval(Meta.parse("old -> " * (split(l, " = "))[2])) for l in data if contains(l, "Operation")]

    test = [parse(Int, (split(l, " "))[end]) for l in data if contains(l, "Test")]

    if_true = [parse(Int, (split(l, " "))[end]) + 1 for l in data if contains(l, "If true")]

    if_false = [parse(Int, (split(l, " "))[end]) + 1 for l in data if contains(l, "If false")]

    for i in 1:lastindex(number)
        push!(parsed_input, (number[i], items[i], operation[i], test[i], if_true[i], if_false[i]))
    end
    return parsed_input
end

# Part 01:
function day11_part1(parsed_input)
    total_time = []
    for round in 1:20
        for (monkey, info) in enumerate(parsed_input)
            monkey_num, starting_items, op, test_val, true_place, false_place = deepcopy(info)
            push!(total_time, ([monkey_num, length(starting_items)]))
            for item in starting_items
                new_item = Int(floor(op(item) / 3))
                target = new_item % test_val == 0 ? true_place : false_place
                popfirst!(parsed_input[monkey][2])
                push!(parsed_input[target][2], new_item)
            end
        end
    end
    return total_time
end

# Part 02:
function day11_part2(parsed_input, magic_number)
    total_time = []
    for round in 1:10000
        for (monkey, info) in enumerate(parsed_input)
            monkey_num, starting_items, op, test_val, true_place, false_place = deepcopy(info)

            push!(total_time, ([monkey_num, length(starting_items)]))
            for item in starting_items
                new_item = op(item) % magic_number 
                target = new_item % test_val == 0 ? true_place : false_place
                popfirst!(parsed_input[monkey][2])
                push!(parsed_input[target][2], new_item)
            end
        end
    end
    return total_time
end

# computing total inspections
function compute_total_inspections(total_time)
    total_sum = []
    [push!(total_sum, sum(filter(x -> x[1] == j, total_time))[2]) for j in unique!(map(x -> x[1], total_time))]
    return reduce(*, sort(total_sum, rev=true)[1:2])
end

data = readlines("data_aoc/input_day11.txt");
magic_number = lcm([parse(Int, (split(l, " "))[end]) for l in data if contains(l, "Test")]);

part_01 = compute_total_inspections(day11_part1(parse_input(data)));
part_02 = compute_total_inspections(day11_part2(parse_input(data), magic_number));

println("Part 1: Level of monkey business after 20 rounds is $(part_01). ")
println("Part 2: Level of monkey business after 10000 rounds is $(part_02).")

Day 12:

function input_parsing(data::Matrix{Char})
    start_pos = findfirst(isequal('S'), data)
    dest = findfirst(isequal('E'), data)
    replace!(data, 'S' => 'a')
    new_start_pos = (findall(isequal('a'), data))
    return data, start_pos, dest, new_start_pos
end

function compute_cost(data::Matrix{Char}, dest::CartesianIndex{2}, all_start_pos::Array{CartesianIndex{2},1})
    elevation = Dict('a':'z' .=> 0:25)
    elevation['E'] = 25
    directions = ((0, -1), (0, 1), (1, 0), (-1, 0))

    steps_required = Array{Any,1}[] # stores steps for each starting point to destination
    for start_pos in all_start_pos
        steps = Dict{CartesianIndex{2},Int}()
        steps[start_pos] = 0 # steps required to reach destination from start position a
        queue = [start_pos]
        while !isempty(queue)
            current_loc = pop!(queue)
            current_loc == dest ? push!(steps_required, [start_pos, steps[current_loc]]) : nothing
            for (k, d) in enumerate(CartesianIndex.(directions))
                new_loc = current_loc + d
                checkbounds(Bool, data, new_loc) || continue
                if ((elevation[data[new_loc]] - elevation[data[current_loc]]) <= 1) && !haskey(steps, new_loc)
                    steps[new_loc] = steps[current_loc] + 1
                    pushfirst!(queue, new_loc)
                end
            end
        end
    end
    return steps_required

end

input_file = reduce(vcat, permutedims.(collect.(readlines("data_aoc/input_day12.txt"))));
data, start_pos, dest, new_start_pos = input_parsing(input_file);
steps_required = compute_cost(data, dest, new_start_pos);


[steps_required[start_pos][2], map(x -> values(x)[2], steps_required) |> minimum] |> println

Day 13:

using JSON
using BenchmarkTools
packets = JSON.parse.(filter(x -> length(x) > 0, readlines("data_aoc/input_day13.txt")));
# My recursive function
function right_order_pair(left::Any, right::Any)
    if isa(left, Integer) && isa(right, Integer)
        return cmp(left, right)
    elseif isa(left, Integer)
        return right_order_pair([left], right)
    elseif isa(right, Integer)
        return right_order_pair(left, [right])

    elseif isa(left, Vector) && isa(right, Vector)
        if isempty(left)
            isempty(right) ? 0 : -1 # left == right or left < right
        elseif isempty(right)
            return 1 # left > right
        else

            if right_order_pair(left[1], right[1]) == 0
                return right_order_pair(left[2:end], right[2:end])
            else
                return right_order_pair(left[1], right[1])
            end
        end
    end
end
# using my recursive function Part 1:
@time (((i + 1) ÷ 2) for i in 1:2:length(packets) if right_order_pair(packets[i], packets[i+1]) == -1) |> sum |> println

# using builtin function 
# Rules for array and integer comparisons:
Base.isless(left::Vector{Any}, right::Integer) = isless(left, [right])
Base.isless(left::Integer, right::Vector{Any}) = isless([left], right)
Base.isequal(left::Vector{Any}, right::Integer) = isequal(left, [right])
Base.isequal(left::Integer, right::Vector{Any}) = isequal([left], right)
# part 1:
@time (((i + 1) ÷ 2) for i in 1:2:length(packets) if cmp(packets[i], packets[i+1]) == -1) |> sum |> println
# Part 2:
findall(packet -> packet in ([[[2]], [[6]]]), sort!(append!(packets, [[[2]], [[6]]]))) |> prod |> println

Day 14:

using DataStructures

function parse_input(data::Vector{String})
    cave = CartesianIndex{2}[]
    for l in data
        input_data = (map(x -> (parse.(Int, split(x, ","))), split(l, "->")))
        coordinates = CartesianIndex{2}[] # to store the coordinates from the input
        [push!(coordinates, CartesianIndex(points[1], points[2])) for points in input_data]

        for i in eachindex(coordinates)
            previous_index = coordinates[i]
            if i <= (length(coordinates) - 1)
                current_index = coordinates[i+1]
                if current_index[2] != previous_index[2]
                    ymin = min(previous_index[2], current_index[2])
                    ymax = max(previous_index[2], current_index[2])
                    [push!(cave, CartesianIndex(current_index[1], y)) for y in ymin:ymax]
                end
                if current_index[1] != previous_index[1]
                    xmin = min(previous_index[1], current_index[1])
                    xmax = max(previous_index[1], current_index[1])
                    [push!(cave, CartesianIndex(x, current_index[2])) for x in xmin:xmax]
                end
            end

        end
    end

    return push!(unique!(cave), CartesianIndex(500, 0))
end



function prepare_cave_part1(cave::Vector{CartesianIndex{2}})

    cave_map = SortedDict{CartesianIndex{2},Char}()
    min_max = extrema(cave) # finding the min and max values of the cave coordinates

    for pt in min_max[1]:min_max[2]
        pt in cave && pt != CartesianIndex(500, 0) ? push!(cave_map, pt => '#') :
        pt == CartesianIndex(500, 0) ? push!(cave_map, pt => '+') : push!(cave_map, pt => '.')
    end
    return cave_map, min_max
end



function prepare_cave_part2(cave::Vector{CartesianIndex{2}})

    min_max = extrema(cave)
    y_max = (min_max[2]+CartesianIndex(0, 2))[2]
    y_min = min_max[1][2]
    sand_point = CartesianIndex(500, 0)

    floor_for_sand = CartesianIndex((sand_point[1] - (y_max + 1)), y_max):CartesianIndex((sand_point[1] + (y_max + 1)), y_max)

    [push!(cave, f) for f in floor_for_sand]


    new_cave_cordinates = CartesianIndex(sand_point[1] - (y_max + 1), y_min):CartesianIndex((sand_point[1] + (y_max + 1)), y_max)

    cave_map = SortedDict{CartesianIndex{2},Char}()
    for pt in new_cave_cordinates
        pt in cave && pt != CartesianIndex(500, 0) ? push!(cave_map, pt => '#') :
        pt == CartesianIndex(500, 0) ? push!(cave_map, pt => '+') : push!(cave_map, pt => '.')
    end

    return cave_map, extrema(new_cave_cordinates)
end




function falling_sand_part1(sand_point::CartesianIndex{2}, cave_map::SortedDict{CartesianIndex{2},Char}, y_max::Int64, x_min::Int64, x_max::Int64)
    sand_moves = [CartesianIndex(0, 1), CartesianIndex(-1, 1), CartesianIndex(1, 1)]
    while true
        possible_sand_moves = [sand_point + move for move in sand_moves]
        next_sand_point = [p for p in possible_sand_moves if haskey(cave_map, p) && cave_map[p] == '.']

        if isempty(next_sand_point)
            push!(cave_map, sand_point => 'o')
            return true
        end

        if next_sand_point[1][1] <= x_min || next_sand_point[1][1] >= x_max || next_sand_point[1][2] >= y_max
            break
        end
        sand_point = next_sand_point[1]
    end
    return false
end


function falling_sand_part2(sand_point::CartesianIndex{2}, cave_map::SortedDict{CartesianIndex{2},Char}, y_max::Int64)

    sand_moves = [CartesianIndex(0, 1), CartesianIndex(-1, 1), CartesianIndex(1, 1)]

    while sand_point[2] < (y_max + 2)
        possible_sand_moves = [(sand_point + move) for move in sand_moves]
        next_sand_point = [p for p in possible_sand_moves if haskey(cave_map, p) && cave_map[p] == '.']
        if cave_map[CartesianIndex(500, 0)] == 'o'
            break
        end
        if isempty(next_sand_point)
            push!(cave_map, sand_point => 'o')
            return true
        end
        sand_point = next_sand_point[1]
    end
    return false
end


#for printing the cave map
function print_cave(cave_map, xmin, xmax)
    for (index, value) in enumerate(cave_map)
        (index - 1) % ((xmax - xmin) + 1) == 0 ? println() : print(value[2])
    end
    println()
end


function day14_part1(cave::Vector{CartesianIndex{2}})

    cave_map, min_max = prepare_cave_part1(cave)
    sand_point = CartesianIndex(500, 0)
    y_max = min_max[2][2]
    x_min = min_max[1][1]
    x_max = min_max[2][1]

    sand_unit = 0
    while true
        if !(falling_sand_part1(sand_point, cave_map, y_max, x_min, x_max))
            break
        end
        sand_unit += 1
    end

    print_cave(cave_map, x_min, x_max)
    return sand_unit
end

function day14_part2(cave::Vector{CartesianIndex{2}})
    cave_map, min_max = prepare_cave_part2(cave)
    sand_point = CartesianIndex(500, 0)
    y_max = min_max[2][2]
    x_min = min_max[1][1]
    x_max = min_max[2][1]

    sand_unit = 0
    while true
        if !(falling_sand_part2(sand_point, cave_map, y_max))
            break
        end
        sand_unit += 1
    end
    print_cave(cave_map, x_min, x_max)
    return sand_unit
end


data = readlines("data_aoc/input_day14.txt");

println("Part 1: Number of sand units: ", day14_part1(parse_input(data)))
println("Part 2: Number of sand units: ", day14_part2(parse_input(data)))

Day 15:

using BenchmarkTools
function parse_input()
    data = split.(readlines("data_aoc/input_day15.txt"), ": ")
    sensor_cord = Vector{Int64}[]
    beacon_cord = Vector{Int64}[]
    for sensor in data
        x_sensor, y_sensor = [parse(Int, x[2]) for x in split.(split(sensor[1][findfirst("x", sensor[1])[1]:end], ", "), "=")]
        push!(sensor_cord, [x_sensor, y_sensor])

        x_beacon, y_beacon = [parse(Int, x[2]) for x in split.(split(sensor[2][findfirst("x", sensor[2])[1]:end], ", "), "=")]
        push!(beacon_cord, [x_beacon, y_beacon])

    end
    return sensor_cord, beacon_cord
end

manhattan(a::Vector{Int64}, b::Vector{Int64}) = sum(abs.((b - a)));

# Part 1:
function no_beacon_position(y_limit::Int64=2000000)
    sensor_cord, beacon_cord = parse_input()
    distance = [manhattan(sensor, beacon) for (sensor, beacon) in zip(sensor_cord, beacon_cord)]
    range_dict::Vector{UnitRange} = []

    for (sensor, dist) in zip(sensor_cord, distance)

        min_val_x = sensor[1] - (dist - abs(sensor[2] - y_limit))
        max_val_x = sensor[1] + (dist - abs(sensor[2] - y_limit))
        push!(range_dict, (min_val_x:max_val_x))
    end
    reduced = reduce(vcat, unique(range_dict))

    return (abs(minimum(reduced)) + maximum(reduced))
end

# Part 2:
function distress_beacon_frequency(x_limit::Int64=0, y_limit::Int64=4000000)
    sensor_cord, beacon_cord = parse_input()
    distance = [manhattan(sensor, beacon) + 1 for (sensor, beacon) in zip(sensor_cord, beacon_cord)]

    for (sensor, dist) in zip(sensor_cord, distance)

        for i in 0:dist
            p1 = [sensor[1] - dist + i, sensor[2] + i]
            p2 = [sensor[1] - dist + i, sensor[2] - i]
            p3 = [sensor[1] + dist - i, sensor[2] + i]
            p4 = [sensor[1] + dist - i, sensor[2] - i]
            distress_beacon_pos = [p1, p2, p3, p4]

            for distress_beacon in distress_beacon_pos
                if maximum(distress_beacon .>= x_limit) && maximum(distress_beacon .<= y_limit)
                    if all(x -> manhattan(distress_beacon, x[1]) > manhattan(x[1], x[2]), zip(sensor_cord, beacon_cord))
                        return distress_beacon[1] * y_limit + distress_beacon[2]
                    end
                end
            end
        end
    end
end
@time no_beacon_position()
@time distress_beacon_frequency()

Day 16:

using DataStructures
using RegularExpressions
using BenchmarkTools

function parse_input()
    valve_data = SortedDict{String,Dict}()
    input = readlines("data_aoc/input_day16.txt")

    line_regex = r"Valve (\w+) has flow rate=(\d+); tunnels? leads? to valves? (.*)"

    for line in input
        matchd = match(line_regex, line).captures
        valve_name = matchd[1]
        flow_rate = parse(Int, matchd[2])

        valve_data[valve_name] = Dict(
            "flow_rate" => flow_rate,
            "tunnels" => Set{String}()
        )

        tunnel_names = split(matchd[3], ", ")
        for tunnel_name in tunnel_names
            push!(valve_data[valve_name]["tunnels"], tunnel_name)
        end
    end
    return valve_data
end


function compute_distance()
    valve_data = parse_input()
    dist = Dict()

    for valve in keys(valve_data)
        valve != "AA" && (valve_data[valve]["flow_rate"] == 0) ? continue : nothing

        dist[valve] = Dict("AA" => 0, valve => 0)
        visited = Set([valve])

        queue = Tuple[(0, valve)]

        while !isempty(queue)
            distance, position = popfirst!(queue)
            for neighbor in valve_data[position]["tunnels"]
                neighbor in visited ? continue : nothing
                push!(visited, neighbor)
                if (valve_data[neighbor]["flow_rate"]) != 0
                    dist[valve][neighbor] = distance + 1
                end
                push!(queue, (distance + 1, neighbor))
            end
        end
        delete!(dist[valve], valve)
        if valve != "AA"
            delete!(dist[valve], "AA")
        end
    end
    return dist, valve_data
end

function day_16()
    dist, valve_data = compute_distance()
    nonempty_valves = collect(keys(filter(x -> x[1] != "AA", dist)))

    # bitmasking
    indicies = Dict([valve => 1 << i for (i, valve) in enumerate(nonempty_valves)])

    function dfs(valve, time, bitmask, max_pressure, cache)
        cache[bitmask] = max(get(cache, bitmask, 0), max_pressure)

        for (neighbor, dist_to) in pairs(dist[valve])
            (indicies[neighbor] & bitmask != 0) ? continue : nothing
            rem_time = time - dist_to - 1
            rem_time <= 0 ? continue : nothing
            dfs(neighbor, rem_time, bitmask | indicies[neighbor], max_pressure + valve_data[neighbor]["flow_rate"] * rem_time, cache)
        end
        return cache
    end

    part_1 = maximum(values(dfs("AA", 30, 0, 0, Dict{Int,Int}())))
    
    my_second_visit = dfs("AA", 26, 0, 0, Dict{Int,Int}())

    part_2 = maximum(pressure_1 + pressure_2 for (bitmask_1, pressure_1) in my_second_visit for (bitmask_2, pressure_2) in my_second_visit if (bitmask_1 & bitmask_2 == 0))
    return part_1, part_2

end
@time part_1, part_2 = day_16()

Day 17:

using DataStructures

function move_rock(rock::Vector{Tuple{Int64, Int64}}, coord::Tuple{Int64, Int64}, chamber::SortedSet)
  for (x, y) in rock
    x += coord[1]
    y += coord[2]
    if x < 1 || y < 1 || y > 7 || (x, y) in chamber
      return rock, false
    end
  end
  new_coords = [r .+ coord for r in rock]
  return new_coords, true
end

function pyroclastic_flow(input_file::String)
  jets = [Int(e) - 61 for e in read(input_file, String)]
  jet_iter = 1
  rock_iter = 1
  chamber = SortedSet()
  heights = []
  seen = SortedDict()

  rocks = [[(0, 0), (0, 1), (0, 2), (0, 3)],
    [(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)],
    [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)],
    [(0, 0), (1, 0), (2, 0), (3, 0)],
    [(0, 0), (0, 1), (1, 0), (1, 1)]]

  for n in 1:3000
    max_y = isempty(chamber) ? 0 : last(chamber)[1]
    rock_pos = map(x -> x .+ (max_y + 4, 3), rocks[rock_iter])
    rock_iter = mod1(rock_iter + 1, length(rocks))
    while true
      jet = jets[jet_iter]
      jet_iter = mod1(jet_iter + 1, length(jets))
      rock_pos, ismoved = move_rock(rock_pos, (0, jet), chamber)
      rock_pos, ismoved = move_rock(rock_pos, (-1, 0), chamber)
      if !ismoved
        union!(chamber, rock_pos)
        break
      end
    end
    key_val = (rock_iter, jet_iter)
    current_height = last(chamber)[1]

    if haskey(seen, key_val)
      last_rock, last_height = seen[key_val]
      remaining_rocks = 1_000_000_000_000 - n
      cycle_length = n - last_rock
      height_increase = current_height - last_height
      quotient, remainder = divrem(remaining_rocks, cycle_length)
      if remainder == 0
        # println("found cycle with $n steps and $current_height height and $cycle_length length and $height_increase height and $quotient quotient")
        return heights[2022], current_height + height_increase * quotient, chamber
      end
    else
      seen[key_val] = (n, current_height)
    end
    push!(heights, current_height)
  end

end
part_1, part_2, chamber = pyroclastic_flow("data_aoc/input_day17.txt");
@show part_1;
@show part_2;

function print_chamber(chamber)
  max_y = isempty(chamber) ? 0 : last(chamber)[1]
  for y in max_y:-1:4306 # printing only the last 100
    print('|')
    for x in 1:7
      if (y, x) in chamber
        print('#')
      else
        print('.')
      end
    end
    println('|')
  end
  println("+-------+")
end
print_chamber(chamber)

Day 18:

function cube_neighbors((x, y, z))
    return Set([(x + 1, y, z), (x - 1, y, z),
        (x, y + 1, z), (x, y - 1, z),
        (x, y, z + 1), (x, y, z - 1)])
end


function day_18(cubes, part_1=0, part_2=0)
    # part 1
    for cube  cubes
        for neighbors  cube_neighbors(cube)
            (neighbors  cubes) ? [part_1 += 1] : nothing
        end
    end

    # part 2
    min_Val = minimum(extrema(cubes)[1]) - 1
    max_val = maximum(extrema(cubes)[2]) + 1

    queue = [(min_Val, min_Val, min_Val)]
    visited = Set()
    while !isempty(queue)
        cube = popfirst!(queue)
        for neighbor  cube_neighbors(cube)
            !(all([d >= min_Val && d <= max_val for d  neighbor])) ? continue : nothing
            neighbor  visited ? continue : nothing

            neighbor  cubes ? [part_2 += 1] : [push!(visited, neighbor)
                push!(queue, neighbor)]
        end
    end
    return part_1, part_2

end
cubes = map(x -> Tuple(parse.(Int, x)), split.(split(read("data_aoc/input_day18.txt", String), "\n"), ","));
p1, p2 = day_18(cubes)

Day 19:

function parse_input()
    blueprints = Vector{Vector{Int}}()
    for line in eachsplit(read("data_aoc/input_day19.txt", String), "\n", keepempty=false)
        numbers = [parse.(Int, line[x]) for x  findall(r"(\d+)", line)]
        push!(blueprints, numbers)
    end
    return blueprints
end


sort_critera(x) = 1000 * x[4] + 100 * x[3] + 10 * x[2] + x[1]

function bfs(robot_cost, robots, time, queue_size)
    current_inventory = [0, 0, 0, 0]
    current_mined = [0, 0, 0, 0]
    queue = Tuple[(0, robots, current_inventory, current_mined)]
    max_mined_geodes = 0
    previous_time = 0

    while !isempty(queue)
        current_time, robots, current_invent, current_mined = popfirst!(queue)

        if current_time > previous_time
            if length(queue) > queue_size
                queue = sort(queue, by=x -> sort_critera(x[4]), rev=true)[1:queue_size]
            end
            previous_time = current_time
        end

        if current_time == time
            max_mined_geodes = max(max_mined_geodes, current_mined[4])
            continue
        end

        new_mined = current_mined .+ robots
        new_invent = current_invent .+ robots
        push!(queue, (current_time + 1, robots, new_invent, new_mined))

        for i in 1:4
            if all(current_invent .>= robot_cost[i])
                new_robots = deepcopy(robots)
                new_robots[i] += 1
                new_invent = current_invent .- robot_cost[i] .+ robots
                push!(queue, (current_time + 1, new_robots, new_invent, new_mined))
            end
        end
    end

    return max_mined_geodes
end

function geode_mining()
    part_1 = 0
    part_2 = 1
    blueprints = parse_input()
    robots = [1, 0, 0, 0]
    for (id, ore, clay_ore, obsidian_ore, obsidian_clay, geode_ore, geode_obsidian) in blueprints
        robot_cost = [(ore, 0, 0, 0), (clay_ore, 0, 0, 0), (obsidian_ore, obsidian_clay, 0, 0), (geode_ore, 0, geode_obsidian, 0)]
        # @show robot_cost
        part_1 += (id * bfs(robot_cost, robots, 24, 500))
        # @show part_1
        if id <= 3
            part_2 *= bfs(robot_cost, robots, 32, 2500)
            # @show part_2
        end
    end
    return part_1, part_2
end
part_1, part_2 = geode_mining()
println("Part 01: ", part_1)
println("Part 02: ", part_2)

Day 20:

function grove_pos_sys(encrypted_file::Vector{Int}, part1::Bool=true)
    if part1
        mixed_file = collect(deepcopy(enumerate(encrypted_file)))
        itr = 1
    else
        encrypted_file = encrypted_file .* 811589153
        mixed_file = collect(deepcopy(enumerate(encrypted_file)))
        itr = 10
    end

    for _ in 1:itr
        for data in enumerate(encrypted_file)
            index = findfirst(==(data), mixed_file)
            deleteat!(mixed_file, index)
            insert!(mixed_file, mod1(index + data[2], length(encrypted_file) - 1), data)
        end
    end
    zero_pos = findfirst(x -> x[2] == 0, mixed_file)
    decrypted_file = map(x -> x[2], mixed_file)

    return [decrypted_file[mod1((zero_pos + x), length(encrypted_file))] for x in [1000, 2000, 3000]] |> sum
end
input_data = parse.(Int, readlines("data_aoc/input_day20.txt"));
println("Part 1: ", grove_pos_sys(input_data, true))
println("Part 2: ", grove_pos_sys(input_data, false))

Dat 21:

using SymPy
using BenchmarkTools

function yelling_monkeys(input_data)
    input_t = deepcopy(input_data)
    monkeys = Dict{String,Int}()
    while !isempty(input_t)
        line = popfirst!(input_t)
        name, expression = split(line, ": ")
        if contains(expression, r"\d+")
            monkeys[name] = parse(Int, expression)
        else
            left, op, right = split(expression, " ")
            if left in keys(monkeys) && right in keys(monkeys)
                monkeys[name] = eval(Meta.parse("$(monkeys[left]) $op $(monkeys[right])"))
            else
                push!(input_t, line)
            end
        end
        haskey(monkeys, "root") && return monkeys["root"]
    end
end


operations = Dict{String,Function}()
operations["+"] = (x, y) -> x + y
operations["*"] = (x, y) -> x * y
operations["-"] = (x, y) -> x - y
operations["/"] = (x, y) -> x / y


function yelling_monkeys_part_2(input_data)
    input_t = deepcopy(input_data)
    monkeys = Dict{String,Any}()
    while !isempty(input_t)
        line = popfirst!(input_t)
        name, expression = split(line, ": ")
        if contains(expression, r"\d+")
            if name == "humn"
                monkeys[name] = Sym("x")
            else
                monkeys[name] = parse(Int, expression)
            end
        else
            left, op, right = split(expression, " ")
            if left in keys(monkeys) && right in keys(monkeys)
                name == "root" && return solve(monkeys[left] - monkeys[right]).evalf(14)

                monkeys[name] = operations[op](monkeys[left], monkeys[right])
            else
                push!(input_t, line)
            end
        end
    end
end

input_data = readlines("data_aoc/input_day21.txt");
@time println("Part 01: ", yelling_monkeys(input_data))
@time println("Part 02: ", yelling_monkeys_part_2(input_data)[1])

Day 22:

function parse_input()
    input, sequence = split(read("data_aoc/input_day22.txt", String), "\n\n")
    input_ = split(input, "\n")
    @show max_length = maximum(length(x) for x in input_)
    @show length(input_)
    grid = fill(' ', length(input_), max_length)
    for (i, line) in enumerate(input_)
        for (j, char) in enumerate(line)
            grid[i, j] = char
        end
    end
    return grid, sequence
end

function day_22_part1(grid, sequence)
    r = 1
    c = findfirst(grid[1, x] == '.' for x in eachindex(grid))
    dr, dc = 0, 1
    for (x, y) in eachmatch(r"(\d+)([RL]?)", sequence)
        for _ in 1:parse(Int, x)
            nr, nc = r, c
            while true
                nr = mod1((nr + dr), size(grid)[1])
                nc = mod1((nc + dc), size(grid)[2])
                grid[nr, nc] != ' ' ? break : nothing
            end
            grid[nr, nc] == '#' ? break : nothing
            r, c = nr, nc
        end
        y == "R" ? (dr, dc) = (dc, -dr) :
        y == "L" ? (dr, dc) = (-dc, dr) : nothing

    end

    facing = Dict((0, 1) => 0, (1, 0) => 1, (0, -1) => 2, (-1, 0) => 3)

    return ((1000 * r) + (4 * c) + facing[dr, dc])
end



function wrapping(nr, nc, dr, dc)  

    if dr == -1
        if nr <= 1 && 51 <= nc <= 100 # moving up edge 1-6
            dr, dc = 0, 1 # turn right
            nr, nc = nc + 101, 1
        elseif nr < 1 && 101 <= nc <= 150 # up edge 2-6
            nr, nc = 200, nc - 101
        elseif nr == 100 && 1 <= nc <= 50  # moving up edge 4-3
            dr, dc = 0, 1 # start moving right
            nr, nc = nc + 51, 51
        end
    elseif dr == 1
        if nr > 200 && 1 <= nc <= 50  # moving down 6-2
            nr, nc = 1, nc + 101 # keep moving down
        elseif nr == 51 && 101 <= nc <= 150  # moving down 2-3
            dr, dc = 0, -1 # start moving left
            nr, nc = nc - 51, 100
        elseif nr == 151 && 51 <= nc <= 100  # moving down 5-6
            dr, dc = 0, -1 # start moving left
            nr, nc = nc + 101, 50
        end
    elseif dc == 1
        if nc >= 151 && 1 <= nr <= 50  # moving right 5-2
            dc = -1 # start moving left
            nr, nc = 150 - nr, 100
        elseif nc == 101 && 101 <= nr <= 150 # moving right 2-5
            dc = -1 # start moving left
            nr, nc = 150 - nr, 150
        elseif nc == 100 && 51 <= nr <= 100  # moving right 3-2
            dr, dc = -1, 0 # start moving up
            nr, nc = 50, nr + 51
        elseif nc == 51 && 151 <= nr <= 200  # moving right 6-5
            dr, dc = -1, 0 # start moving up
            nr, nc = 150, nr - 101
        end
    elseif dc == -1
        if nc < 1 && 151 <= nr <= 200  # left 1-4
            dr, dc = 1, 0 # moving down
            nr, nc = 1, nr - 101
        elseif nc == 50 && 51 <= nr <= 100  # moving left 3-4
            dr, dc = 1, 0 # start moving down
            nr, nc = 101, nr - 51
        elseif nc == 50 && 1 <= nr <= 50  # moving left 4-1
            dc = 1 # start moving right
            nr, nc = 150 - nr, 1
        elseif nc < 1 && 101 <= nr <= 150  # moving left 6-1
            dc = 1 # start moving right
            nr, nc = 150 - nr, 51
        end

    end
    return nr, nc, dr, dc
end

function day_22_part2(grid, sequence)
    r = 1
    c = findfirst(grid[1, x] == '.' for x in eachindex(grid))
    dr, dc = 0, 1
    for (x, y) in eachmatch(r"(\d+)([RL]?)", sequence)
        for _ in 1:parse(Int, x)
            cdr = deepcopy(dr)
            cdc = deepcopy(dc)
            nr = r + dr
            nc = c + dc
            nr, nc, dr, dc = wrapping(nr, nc, dr, dc)
            if grid[nr, nc] == '#'
                dr = cdr
                dc = cdc
                break
            end
            r, c = nr, nc
        end
        y == "R" ? (dr, dc) = (dc, -dr) :
        y == "L" ? (dr, dc) = (-dc, dr) : nothing
    end
    facing = Dict((0, 1) => 0, (1, 0) => 1, (0, -1) => 2, (-1, 0) => 3)

    return ((1000 * r) + (4 * c) + facing[dr, dc])
end

grid, sequence = parse_input();
part_1 = day_22_part1(grid, sequence) # 31568
part_2 = day_22_part2(grid, sequence) # 36540  

Day 23:

using BenchmarkTools

function elf_rounds(elves::Set{CartesianIndex{2}}, moves::Array{Int64})
    all_neighbors = [(-1, 0), (-1, 1), (-1, -1), (1, 0), (1, 1), (1, -1), (0, -1), (0, 1)]
    direction_order = Dict(
        1 => (all_neighbors[1], all_neighbors[2], all_neighbors[3]), # N, NE, NW
        2 => (all_neighbors[4], all_neighbors[5], all_neighbors[6]), # S, SE, SW
        3 => (all_neighbors[7], all_neighbors[3], all_neighbors[6]), # W, NW, SW
        4 => (all_neighbors[8], all_neighbors[2], all_neighbors[5]) # E, NE, SE
    )

    proposal = Dict{CartesianIndex{2},CartesianIndex{2}}()
    proposed_once = Set{CartesianIndex{2}}()
    proposed_twice = Set{CartesianIndex{2}}()


    for elf in elves
        all(!(elf + CartesianIndex(x) in elves) for x in all_neighbors) ? continue : nothing
        for move in moves
            if all(!(elf + CartesianIndex(x) in elves) for x in direction_order[move])
                proposal[elf] = elf + CartesianIndex(direction_order[move][1])
                proposal[elf] in proposed_twice ? continue :
                proposal[elf] in proposed_once ? push!(proposed_twice, proposal[elf]) : push!(proposed_once, proposal[elf])
                break
            end
        end
    end

    for (current, next) in proposal
        if !(next in proposed_twice)
            delete!(elves, current)
            push!(elves, next)
        end
    end
    push!(moves, popfirst!(moves))
    return elves
end

function show_final_pos(elves::Set{CartesianIndex{2}})
    min_x = extrema(elves)[1][1]
    max_x = extrema(elves)[2][1]
    min_y = extrema(elves)[1][2]
    max_y = extrema(elves)[2][2]
    min_point = CartesianIndex(min_x, min_y)
    max_point = CartesianIndex(max_x, max_y)
    for (index, pt) in enumerate(min_point:max_point)
        (index - 1) % ((max_x - min_x) + 1) == 0 ? println() : pt in elves ? print('#') : print('.')
    end
end

function unstable_diffusion(input_map::String, print_map::Bool=false)
    elves_map = map(x -> x[1], reduce(vcat, permutedims.(map(x -> split(x, ""), split(input_map)))))
    elves = Set(findall(x -> x == '#', elves_map))
    moves = [1, 2, 3, 4]

    for _ in 1:10
        elves = elf_rounds(elves, moves)
    end

    min_x = extrema(elves)[1][1]
    max_x = extrema(elves)[2][1]
    min_y = extrema(elves)[1][2]
    max_y = extrema(elves)[2][2]
    part_1 = (max_x - min_x + 1) * (max_y - min_y + 1) - length(elves)

    last_elves_set = Set(elves)
    part_2 = 11
    while true
        elves = elf_rounds(elves, moves)
        if last_elves_set == elves
            break
        end
        last_elves_set = Set(elves)
        part_2 += 1
    end
    if print_map
        show_final_pos(elves)
    end
    return part_1, part_2
end
input_data = read("data_aoc/input_day23.txt", String);
@time unstable_diffusion(input_data, true)

Day 24:

using DataStructures
using BenchmarkTools
function parse_input(input_data)
    blizzards = Dict()
    for (r, line) in enumerate(input_data[2:end-1])
        for (c, char) in enumerate(line[2:end-1])
            if char in ['<', '>', '^', 'v']
                if !haskey(blizzards, char)
                    blizzards[char] = Set()
                end
                push!(blizzards[char], (r - 1, c - 1))
            end
        end
    end
    width = length(input_data[2:end-1])
    height = length(input_data[1][2:end-1])
    return blizzards, width, height
end


function get_blizzard_pos(neighbors, time_m, blizzards, width, height)

    for (i, br, bc) in [('^', -1, 0), ('v', 1, 0), ('<', 0, -1), ('>', 0, 1)]
        b_z_cordx = mod(neighbors[1] - (br * time_m), (width))
        b_z_cordy = mod(neighbors[2] - (bc * time_m), (height))
        if (b_z_cordx, b_z_cordy) in blizzards[i]
            return true
        end
    end
    return false
end



function bfs(start_pos, goal_pos, r, c, blizzards, time=0)
    neighbor_offset = [(1, 0), (0, 1), (0, 0), (0, -1), (-1, 0)]
    queue = [(time, start_pos)]
    seen = Set()
    while !isempty(queue)
        time_m, current_pos = popfirst!(queue)
        time_m += 1

        for n in neighbor_offset
            neighbors = n .+ current_pos

            if (neighbors == goal_pos)

                return time_m
            end

            (neighbors[1] < 0 || neighbors[2] < 0 || neighbors[1] >= r || neighbors[2] >= c) && (neighbors != start_pos) ? continue : nothing

            check_blizzard = false

            if neighbors != start_pos
                check_blizzard = get_blizzard_pos(neighbors, time_m, blizzards, width, height)
            end
            if !check_blizzard
                key_val = (neighbors, time_m)
                key_val in seen ? continue : nothing
                push!(seen, key_val)
                push!(queue, (time_m, neighbors))
            end
        end
    end
end
input_data = split(read("data_aoc/input_day24.txt", String), "\n");
blizzards, width, height = parse_input(input_data);
start_pos = (-1, 0)
goal_pos = (width, height - 1)
part_1 = bfs(start_pos, goal_pos, width, height, blizzards, 0)
part_2 = bfs(start_pos, goal_pos, width, height, blizzards, bfs(goal_pos, start_pos, width, height, blizzards, part_1))

Day 25:

function snafu_computation()
    input = readlines("data_aoc/input_day25.txt")
    snafu_dec = Dict('2' => 2,
        '1' => 1,
        '0' => 0,
        '-' => -1,
        '=' => -2)
    dec_snafu = Dict(v => k for (k, v) in snafu_dec)

    decimal_number = 0
    for line in input
        coefficient = 1
        for x in line[end:-1:1]
            decimal_number += snafu_dec[x] * coefficient
            coefficient *= 5
        end
        # println("decimal of snafu $line is $decimal_number number: ")
    end
    # @show decimal_number
    snafu_number = ""
    while decimal_number > 0
        remainder = decimal_number % 5
        decimal_number ÷= 5

        if remainder <= 2
            snafu_number = dec_snafu[remainder] * snafu_number
        else
            snafu_number = dec_snafu[remainder-5] * snafu_number
            decimal_number += 1
        end
    end

    #  @show snafu_number
    return snafu_number
end
snafu_computation()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment