/
day_03.adb
149 lines (124 loc) · 4.26 KB
/
day_03.adb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
with Ada.Integer_Text_IO;
with Ada.Strings.Fixed;
with Ada.Strings.Maps.Constants;
with Ada.Text_IO;
with Ada.Containers.Vectors;
procedure Day_03 is
type Part_Number is record
From, To : Positive; -- span in the line
Value : Positive; -- decimal part number
end record;
package Part_Number_Vectors is
new Ada.Containers.Vectors (Positive, Part_Number);
type Part_Number_Pair is array (1 .. 2) of Part_Number;
type Symbol is record
Char : Character; -- '*' for gears
Position : Positive; -- pos in the line
Adjacent : Natural := 0; -- count of adjacent parts
Parts : Part_Number_Pair; -- first two adjacent parts for '*'
end record;
package Symbol_Vectors is
new Ada.Containers.Vectors (Positive, Symbol);
type Item_Vectors is record
Parts : Part_Number_Vectors.Vector;
Symbols : Symbol_Vectors.Vector;
end record;
procedure Parse_Line (Line : String; Result : out Item_Vectors);
-- Parse line and find possible part numbers and symbols
function Is_Adjacent (Part : Part_Number; Pos : Positive) return Boolean is
(Pos + 1 >= Part.From and Pos - 1 <= Part.To);
-- Check if Pos(-sition) is adjacent to the part number span
procedure Find_Gears
(Symbols : in out Symbol_Vectors.Vector;
Parts : Part_Number_Vectors.Vector);
-- Enumerate symbols adjanced to parts and find '*'. Collect parts for them
----------------
-- Parse_Line --
----------------
procedure Parse_Line (Line : String; Result : out Item_Vectors) is
From : Natural := Line'First;
begin
-- Look for part numbers:
while From <= Line'Last loop
declare
Part : Part_Number;
To : Natural;
begin
Ada.Strings.Fixed.Find_Token
(Line,
Ada.Strings.Maps.Constants.Decimal_Digit_Set,
Test => Ada.Strings.Inside,
From => From,
First => Part.From,
Last => To);
exit when To < Part.From;
Part.To := To;
Part.Value := Positive'Value (Line (Part.From .. Part.To));
Result.Parts.Append (Part);
From := Part.To + 1;
end;
end loop;
-- Look for symbols:
for J in Line'Range loop
if Line (J) not in '.' | '0' .. '9' then
Result.Symbols.Append
(Symbol'(Char => Line (J),
Position => J,
Adjacent => 0,
Parts => <>));
end if;
end loop;
end Parse_Line;
----------------
-- Find_Gears --
----------------
procedure Find_Gears
(Symbols : in out Symbol_Vectors.Vector;
Parts : Part_Number_Vectors.Vector) is
begin
for Symbol of Symbols loop
for Part of Parts loop
if Symbol.Char = '*'
and then Is_Adjacent (Part, Symbol.Position)
then
Symbol.Adjacent := Symbol.Adjacent + 1;
if Symbol.Adjacent in Symbol.Parts'Range then
Symbol.Parts (Symbol.Adjacent) := Part;
end if;
end if;
end loop;
end loop;
end Find_Gears;
procedure Increment_Total
(Symbols : Symbol_Vectors.Vector;
Total : in out Natural) is
begin
for Symbol of Symbols loop
if Symbol.Adjacent = 2 then
Total := Total + Symbol.Parts (1).Value * Symbol.Parts (2).Value;
end if;
end loop;
end Increment_Total;
Prev : Item_Vectors;
Total : Natural := 0;
begin
while not Ada.Text_IO.End_Of_File loop
declare
Line : String (1 .. 200);
Last : Natural;
Next : Item_Vectors;
begin
Ada.Text_IO.Get_Line (Line, Last);
pragma Assert (Last < Line'Last);
Parse_Line (Line (1 .. Last), Next);
Find_Gears (Next.Symbols, Prev.Parts);
Find_Gears (Prev.Symbols, Next.Parts);
Find_Gears (Next.Symbols, Next.Parts);
Increment_Total (Prev.Symbols, Total);
Prev.Symbols.Move (Source => Next.Symbols);
Prev.Parts.Move (Source => Next.Parts);
end;
end loop;
Increment_Total (Prev.Symbols, Total);
Ada.Integer_Text_IO.Put (Total);
end Day_03;