@@ -3,50 +3,114 @@ defmodule AdventOfCode.Y2015.Day06 do
33 --- Day 6: Probably a Fire Hazard ---
44 Problem Link: https://adventofcode.com/2015/day/6
55 Difficulty: m
6- Tags: grid vector reduction slow
6+ Tags: grid vector reduction
77 """
8- alias AdventOfCode.Helpers . { InputReader , Transformers }
9- alias Aja.Vector
8+ import Bitwise
9+ alias AdventOfCode.Helpers.InputReader
1010
1111 def input , do: InputReader . read_from_file ( 2015 , 6 )
1212
1313 def run ( input \\ input ( ) ) do
1414 parsed_input = parse ( input )
15- grid = make_grid ( 1000 )
1615
17- task_1 = Task . async ( fn -> brightness ( Enum . reduce ( parsed_input , grid , & apply_1 / 2 ) ) end )
18- task_2 = Task . async ( fn -> brightness ( Enum . reduce ( parsed_input , grid , & apply_2 / 2 ) ) end )
16+ task_1 = Task . async ( fn -> part_1 ( parsed_input ) end )
17+ task_2 = Task . async ( fn -> part_2 ( parsed_input ) end )
1918
2019 { Task . await ( task_1 , :infinity ) , Task . await ( task_2 , :infinity ) }
2120 end
2221
2322 def parse ( input \\ input ( ) ) do
24- Enum . map ( Transformers . lines ( input ) , & parse_input / 1 )
23+ input
24+ |> String . split ( "\n " , trim: true )
25+ |> Enum . map ( & parse_input / 1 )
2526 end
2627
27- def apply_1 ( line , grid ) ,
28- do: apply ( line , grid , fn _ -> 1 end , fn _ -> 0 end , fn v -> ( v == 0 && 1 ) || 0 end )
28+ # Part 1 implementation using bitwise operations on rows (represented as 1000-bit integers)
29+ defp part_1 ( instructions ) do
30+ # Initial grid: 1000 rows of all zeros (represented as 0)
31+ grid = Tuple . duplicate ( 0 , 1000 )
2932
30- def apply_2 ( line , grid ) , do: apply ( line , grid , & ( & 1 + 1 ) , & max ( 0 , & 1 - 1 ) , & ( & 1 + 2 ) )
33+ instructions
34+ |> Enum . reduce ( grid , fn { cmd , x1 .. x2 // _ , y1 .. y2 // _ } , acc ->
35+ mask = ( ( 1 <<< ( x2 - x1 + 1 ) ) - 1 ) <<< x1
3136
32- defp apply ( { cmd , xs , ys } , grid , on , off , toggle ) do
33- Vector . foldl ( coords ( xs , ys ) , grid , fn [ x , y ] , acc ->
37+ Enum . reduce ( y1 .. y2 , acc , fn y , rows ->
38+ row = elem ( rows , y )
39+
40+ new_row =
41+ case cmd do
42+ "turn on" -> row ||| mask
43+ "turn off" -> row &&& bnot ( mask )
44+ "toggle" -> bxor ( row , mask )
45+ end
46+
47+ put_elem ( rows , y , new_row )
48+ end )
49+ end )
50+ |> Tuple . to_list ( )
51+ |> Enum . reduce ( 0 , fn row , acc -> acc + popcount ( row ) end )
52+ end
53+
54+ # Part 2 implementation using :counters for mutable-like performance
55+ defp part_2 ( instructions ) do
56+ c = :counters . new ( 1_000_000 , [ ] )
57+
58+ Enum . each ( instructions , fn { cmd , x1 .. x2 // _ , y1 .. y2 // _ } ->
3459 case cmd do
35- "turn on" -> Vector . update_at ( acc , x , & Vector . update_at ( & 1 , y , on ) )
36- "turn off" -> Vector . update_at ( acc , x , & Vector . update_at ( & 1 , y , off ) )
37- "toggle" -> Vector . update_at ( acc , x , & Vector . update_at ( & 1 , y , toggle ) )
60+ "turn on" ->
61+ for y <- y1 .. y2 do
62+ offset = y * 1000 + 1
63+ for x <- x1 .. x2 , do: :counters . add ( c , offset + x , 1 )
64+ end
65+
66+ "turn off" ->
67+ for y <- y1 .. y2 do
68+ offset = y * 1000 + 1
69+
70+ for x <- x1 .. x2 do
71+ idx = offset + x
72+ if :counters . get ( c , idx ) > 0 , do: :counters . add ( c , idx , - 1 )
73+ end
74+ end
75+
76+ "toggle" ->
77+ for y <- y1 .. y2 do
78+ offset = y * 1000 + 1
79+ for x <- x1 .. x2 , do: :counters . add ( c , offset + x , 2 )
80+ end
3881 end
3982 end )
83+
84+ # Sum all values
85+ Enum . reduce ( 1 .. 1_000_000 , 0 , fn i , acc -> acc + :counters . get ( c , i ) end )
4086 end
4187
42- defp make_grid ( dim ) , do: Vector . duplicate ( Vector . duplicate ( 0 , dim ) , dim )
43- defp brightness ( grid ) , do: Aja.Enum . reduce ( grid , 0 , & ( & 2 + Aja.Enum . sum ( & 1 ) ) )
44- defp coords ( xs , ys ) , do: Vector . new ( for x <- xs , y <- ys , do: [ x , y ] )
88+ # High performance popcount for large integers
89+ defp popcount ( 0 ) , do: 0
90+
91+ defp popcount ( n ) do
92+ n
93+ |> :binary . encode_unsigned ( )
94+ |> count_binary_ones ( 0 )
95+ end
96+
97+ defp count_binary_ones ( << >> , acc ) , do: acc
98+
99+ defp count_binary_ones ( << b , rest :: binary >> , acc ) do
100+ count_binary_ones (
101+ rest ,
102+ acc +
103+ for << ( bit :: 1 <- << b :: 8 >> ) >> , reduce: 0 do
104+ a -> a + bit
105+ end
106+ )
107+ end
45108
46109 @ regex ~r" (?<cmd>toggle|turn\s on|turn\s off)\s (?<x1>\d +),(?<y1>\d +)\s \S +\s (?<x2>\d +),(?<y2>\d +)"
47- defp parse_input ( line ) , do: format ( Regex . named_captures ( @ regex , line ) )
110+ defp parse_input ( line ) do
111+ % { "cmd" => cmd , "x1" => x1 , "x2" => x2 , "y1" => y1 , "y2" => y2 } =
112+ Regex . named_captures ( @ regex , line )
48113
49- defp format ( % { "cmd" => cmd , "x1" => x1 , "x2" => x2 , "y1" => y1 , "y2" => y2 } ) do
50114 { cmd , String . to_integer ( x1 ) .. String . to_integer ( x2 ) ,
51115 String . to_integer ( y1 ) .. String . to_integer ( y2 ) }
52116 end
0 commit comments