@@ -16,6 +16,35 @@ fn Rotation(comptime T: type) type {
1616 };
1717}
1818
19+ fn Dial (comptime T : type ) type {
20+ return struct {
21+ const Self = @This ();
22+
23+ initial : T ,
24+ position : T ,
25+
26+ pub fn init (initial_position : T ) Self {
27+ return .{
28+ .initial = initial_position ,
29+ .position = initial_position ,
30+ };
31+ }
32+ pub fn rotate (self : * Self , rotation : Rotation (T )) void {
33+ switch (rotation .dir ) {
34+ .Left = > {
35+ self .position = @mod (self .position - rotation .amount , 100 );
36+ },
37+ .Right = > {
38+ self .position = @mod (self .position + rotation .amount , 100 );
39+ },
40+ }
41+ }
42+ pub fn getCurrentPosition (self : Self ) T {
43+ return self .position ;
44+ }
45+ };
46+ }
47+
1948fn parseFromString (comptime T : type , input : []const u8 ) RotationParseError ! Rotation (T ) {
2049 if (input .len < 2 ) {
2150 return RotationParseError .InvalidLength ;
@@ -49,10 +78,24 @@ pub fn parseAllLines(comptime T: type, allocator: std.mem.Allocator, input: []co
4978 return try results .toOwnedSlice (allocator );
5079}
5180
52- pub fn part1 (input : []const u8 ) ! i32 {
53- // Parse and solve part 1
54- _ = input ;
55- return 0 ;
81+ pub fn part1 (input : []const u8 ) ! u32 {
82+ var gpa = std .heap .GeneralPurposeAllocator (.{}){};
83+ defer _ = gpa .deinit ();
84+ const allocator = gpa .allocator ();
85+ var amount : u32 = 0 ;
86+
87+ const rotations = try parseAllLines (i32 , allocator , input );
88+ defer allocator .free (rotations );
89+
90+ var dial = Dial (i32 ).init (50 );
91+ for (rotations ) | rotation | {
92+ dial .rotate (rotation );
93+ if (dial .getCurrentPosition () == 0 ) {
94+ amount += 1 ;
95+ }
96+ }
97+
98+ return amount ;
5699}
57100
58101pub fn part2 (input : []const u8 ) ! i32 {
@@ -104,3 +147,48 @@ test "parse with invalid length" {
104147 const asText = "G" ;
105148 try std .testing .expectError (RotationParseError .InvalidLength , parseFromString (u32 , asText ));
106149}
150+
151+ test "dial initialization" {
152+ var dial = Dial (i32 ).init (100 );
153+ try std .testing .expectEqual (@as (i32 , 100 ), dial .initial );
154+ try std .testing .expectEqual (@as (i32 , 100 ), dial .getCurrentPosition ());
155+ }
156+
157+ test "dial rotation right" {
158+ var dial = Dial (i32 ).init (0 );
159+ const rotation = Rotation (i32 ){ .dir = .Right , .amount = 10 };
160+ dial .rotate (rotation );
161+ try std .testing .expectEqual (@as (i32 , 0 ), dial .initial ); // initial unchanged
162+ try std .testing .expectEqual (@as (i32 , 10 ), dial .getCurrentPosition ());
163+ }
164+
165+ test "dial rotation left" {
166+ var dial = Dial (i32 ).init (50 );
167+ const rotation = Rotation (i32 ){ .dir = .Left , .amount = 20 };
168+ dial .rotate (rotation );
169+ try std .testing .expectEqual (@as (i32 , 50 ), dial .initial ); // initial unchanged
170+ try std .testing .expectEqual (@as (i32 , 30 ), dial .getCurrentPosition ());
171+ }
172+
173+ test "dial multiple rotations" {
174+ var dial = Dial (i32 ).init (0 );
175+ dial .rotate (.{ .dir = .Right , .amount = 10 });
176+ dial .rotate (.{ .dir = .Left , .amount = 5 });
177+ dial .rotate (.{ .dir = .Right , .amount = 15 });
178+ try std .testing .expectEqual (@as (i32 , 0 ), dial .initial ); // initial always unchanged
179+ try std .testing .expectEqual (@as (i32 , 20 ), dial .getCurrentPosition ());
180+ }
181+
182+ test "first part example" {
183+ const example_input = @embedFile ("example.txt" );
184+ const result = try part1 (example_input );
185+
186+ try std .testing .expectEqual (3 , result );
187+ }
188+
189+ test "first part input" {
190+ const example_input = @embedFile ("input.txt" );
191+ const result = try part1 (example_input );
192+
193+ try std .testing .expectEqual (1071 , result );
194+ }
0 commit comments