Commit dc8a345

bryfry <bryon.fryer@gmail.com>
2024-04-13 09:32:43
2023
1 parent 41a6625
2023/go/03/main.go
@@ -2,6 +2,8 @@ package main
 
 import (
 	"advent2023/internal/aoc"
+	"advent2023/internal/grid"
+
 	"strconv"
 	"strings"
 	"unicode"
@@ -9,13 +11,9 @@ import (
 	"github.com/google/uuid"
 )
 
-type cord struct {
-	x int
-	y int
-}
 
 type board struct {
-	grid     map[cord]uuid.UUID
+	grid     map[grid.Coord]uuid.UUID
 	parts    map[uuid.UUID]part
 	included map[uuid.UUID]part
 	symbols  []symbol
@@ -28,8 +26,8 @@ type part struct {
 }
 
 type symbol struct {
+	grid.Coord
 	repr string
-	cord cord
 }
 
 type gear struct {
@@ -40,7 +38,7 @@ type gear struct {
 
 func parseBoard(input []string) (b board) {
 	b = board{
-		grid:     map[cord]uuid.UUID{},
+		grid:     map[grid.Coord]uuid.UUID{},
 		parts:    map[uuid.UUID]part{},
 		included: map[uuid.UUID]part{},
 		symbols:  []symbol{},
@@ -54,7 +52,7 @@ func parseBoard(input []string) (b board) {
 
 			if unicode.IsDigit(col) {
 				partSB.WriteRune(col)
-				b.grid[cord{x, y}] = partID
+				b.grid[grid.Coord{X:x, Y:y}] = partID
 				// only continue if not at end of row
 				if x < len(row)-1 {
 					continue
@@ -78,13 +76,19 @@ func parseBoard(input []string) (b board) {
 			}
 
 			// symbol remaning, store it
-			b.symbols = append(b.symbols, symbol{repr: string(col), cord: cord{x, y}})
+			s := symbol{
+				repr: string(col), 
+				Coord: grid.Coord{X:x, Y:y},
+			}
+			b.symbols = append(b.symbols, s)
 		}
 	}
-	for _, s := range b.symbols {
 
+	// find included numbers via symbols
+	// find gears via "*" symbols
+	for _, s := range b.symbols {
 		neighbors := map[uuid.UUID]part{}
-		for _, c := range s.cord.neighbors() {
+		for _, c := range s.Coord.Neighbors() {
 			if id, ok := b.grid[c]; ok {
 				b.included[id] = b.parts[id]
 				neighbors[id] = b.parts[id]
@@ -104,20 +108,6 @@ func parseBoard(input []string) (b board) {
 	return b
 }
 
-func (c cord) neighbors() (cords []cord) {
-	cords = []cord{
-		{c.x - 1, c.y - 1},
-		{c.x - 1, c.y},
-		{c.x - 1, c.y + 1},
-		{c.x, c.y - 1},
-		{c.x, c.y + 1},
-		{c.x + 1, c.y - 1},
-		{c.x + 1, c.y},
-		{c.x + 1, c.y + 1},
-	}
-	return cords
-}
-
 func solvePart1(input []string) (solution int) {
 	b := parseBoard(input)
 	solution = 0
2023/go/04/main.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+	"advent2023/internal/aoc"
+)
+
+func solvePart1(input []string) (solution int) {
+	return 0
+}
+
+func solvePart2(input []string) (solution int) {
+	return 0
+}
+
+func main() {
+	day := 3
+	aoc.SolveExample(day, aoc.Part1, solvePart1, 0)
+	aoc.SolvePuzzle(day, aoc.Part1, solvePart1, aoc.UnknownExpected)
+	aoc.SolveExample(day, aoc.Part2, solvePart2, 0)
+	aoc.SolvePuzzle(day, aoc.Part2, solvePart2, aoc.UnknownExpected)
+}
2023/go/internal/aoc/aoc.go
@@ -5,11 +5,12 @@ import (
 	"fmt"
 	"log"
 	"os"
+	"time"
 )
 
 const (
-	example = "e"
-	puzzle  = "p"
+	Example = InputType("e")
+	Puzzle  = InputType("p")
 
 	Part1 = Part(1)
 	Part2 = Part(2)
@@ -47,18 +48,30 @@ type InputType string
 type Day int
 type Part int
 
-type solve func([]string) int
+type Solve func([]string) int
+
+type ProblemId struct {
+	Day  Day
+	Part Part
+}
 
 type Input struct {
-	Day      Day
-	Part     Part
+	ProblemId
+	Solve
 	Type     InputType
-	Solve    solve
 	Expected int
 }
 
-func (i Input) Calculate() (err error) {
+func (i Input) String() string {
+	var t string = "Example"
+	if i.Type == Puzzle {
+		t = "Puzzle"
+	}
+	return fmt.Sprintf("Day %02d, Part %d %7s: ", i.Day, i.Part, t)
 
+}
+
+func (i Input) Calculate() (err error) {
 	inputFilename := fmt.Sprintf("../input/%02d-%s%d.txt", i.Day, i.Type, i.Part)
 	input := []string{}
 	file, err := os.Open(inputFilename)
@@ -88,12 +101,37 @@ func (i Input) Calculate() (err error) {
 	return nil
 }
 
-func SolveExample(day Day, part Part, fn solve, expected int) {
-	fmt.Printf("Day %02d, Part %d Example: ", day, part)
+func EventDay(year int) (day int) {
+
+	start := time.Date(year, time.December, 1, 0, 0, 0, 0, time.UTC)
+	now := time.Now()
+	end := time.Date(year, time.December, 25, 0, 0, 0, 0, time.UTC)
+
+	if now.After(end) {
+		return 25
+	}
+	if now.Before(start) {
+		return 0
+	}
+	return now.Day()
+}
+
+func ReleasedDays(year int) (days []Day) {
+	today := EventDay(year)
+	days = []Day{}
+	for d := 1; d <= today; d++ {
+		days = append(days, Day(d))
+	}
+	return days
+}
+
+func SolveExample(day Day, part Part, fn Solve, expected int) {
 	i := Input{
-		Day:      day,
-		Part:     part,
-		Type:     example,
+		ProblemId: ProblemId{
+			Day:  day,
+			Part: part,
+		},
+		Type:     Example,
 		Solve:    fn,
 		Expected: expected,
 	}
@@ -103,12 +141,13 @@ func SolveExample(day Day, part Part, fn solve, expected int) {
 	}
 }
 
-func SolvePuzzle(day Day, part Part, fn solve, expected int) {
-	fmt.Printf("Day %02d, Part %d Puzzle:  ", day, part)
+func SolvePuzzle(day Day, part Part, fn Solve, expected int) {
 	i := Input{
-		Day:      day,
-		Part:     part,
-		Type:     puzzle,
+		ProblemId: ProblemId{
+			Day:  day,
+			Part: part,
+		},
+		Type:     Puzzle,
 		Solve:    fn,
 		Expected: expected,
 	}
2023/go/internal/grid/grid.go
@@ -0,0 +1,27 @@
+package grid
+
+// Cartesian
+type Coord struct {
+	X int
+	Y int
+}
+
+// Cartesian coordinates of all adjacent Neighbors
+// Does not take into consideration edges, maximum
+// .....
+// .nnn.
+// .ncn.
+// .nnn.
+// .....
+func (c Coord) Neighbors() []Coord {
+	return []Coord{
+		{X: c.X - 1, Y: c.Y - 1},
+		{X: c.X - 1, Y: c.Y},
+		{X: c.X - 1, Y: c.Y + 1},
+		{X: c.X, Y: c.Y - 1},
+		{X: c.X, Y: c.Y + 1},
+		{X: c.X + 1, Y: c.Y - 1},
+		{X: c.X + 1, Y: c.Y},
+		{X: c.X + 1, Y: c.Y + 1},
+	}
+}
2023/go/internal/solutions/01.go
@@ -0,0 +1,78 @@
+package solutions
+
+import (
+	//"advent2023/internal/aoc"
+	"log"
+	"strconv"
+	"strings"
+)
+
+func Day1Part1(input []string) (solution int) {
+	sum := 0
+	for _, line := range input {
+		first := ""
+		last := ""
+		for _, c := range line {
+			if _, err := strconv.Atoi(string(c)); err == nil {
+				if first == "" {
+					first = string(c)
+				}
+				last = string(c)
+			}
+		}
+		value, err := strconv.Atoi(strings.Join([]string{first, last}, ""))
+		if err != nil {
+			log.Fatal(err)
+		}
+		sum = sum + value
+	}
+	return sum
+}
+
+var digits map[string]string = map[string]string{
+	"one":   "1",
+	"1":     "1",
+	"two":   "2",
+	"2":     "2",
+	"three": "3",
+	"3":     "3",
+	"four":  "4",
+	"4":     "4",
+	"five":  "5",
+	"5":     "5",
+	"six":   "6",
+	"6":     "6",
+	"seven": "7",
+	"7":     "7",
+	"eight": "8",
+	"8":     "8",
+	"nine":  "9",
+	"9":     "9",
+}
+
+type digitIndex struct {
+	digit string
+	index int
+}
+
+func Day1Part2(input []string) (solution int) {
+	sum := 0
+	for _, line := range input {
+		first := digitIndex{digit: "", index: 5000}
+		last := digitIndex{digit: "", index: -1}
+		for k := range digits {
+			f := strings.Index(line, k)
+			if f != -1 && f < first.index {
+				first = digitIndex{digit: k, index: f}
+			}
+			l := strings.LastIndex(line, k)
+			if l != -1 && l > last.index {
+				last = digitIndex{digit: k, index: l}
+			}
+		}
+		joinedDigits := strings.Join([]string{digits[first.digit], digits[last.digit]}, "")
+		value, _ := strconv.Atoi(joinedDigits)
+		sum = sum + value
+	}
+	return sum
+}
2023/go/internal/solutions/main.go
@@ -0,0 +1,11 @@
+package solutions
+
+import "advent2023/internal/aoc"
+
+var Fn map[aoc.ProblemId]aoc.Solve
+
+func init() {
+	Fn = map[aoc.ProblemId]aoc.Solve{}
+	Fn[aoc.ProblemId{Day: aoc.Day1, Part: aoc.Part1}] = Day1Part1
+	Fn[aoc.ProblemId{Day: aoc.Day1, Part: aoc.Part2}] = Day1Part2
+}
2023/go/template/main.go
@@ -13,7 +13,7 @@ func solvePart2(input []string) (solution int) {
 }
 
 func main() {
-	day := 3
+	day := aoc.Day1
 	aoc.SolveExample(day, aoc.Part1, solvePart1, 0)
 	aoc.SolvePuzzle(day, aoc.Part1, solvePart1, aoc.UnknownExpected)
 	aoc.SolveExample(day, aoc.Part2, solvePart2, 0)
2023/go/Justfile
@@ -14,6 +14,9 @@ run-all: clean build-all
 	bin/02
 	bin/03
 
+template day:
+	cp -r template {{day}}
+
 build day: 
 	go build -o bin/{{day}} {{day}}/main.go
 
2023/go/main.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+	"advent2023/internal/aoc"
+	"advent2023/internal/solutions"
+	"fmt"
+)
+
+func main() {
+	for _, d := range aoc.ReleasedDays(2023) {
+
+		parts := []aoc.Part{aoc.Part1, aoc.Part2}
+		types := []aoc.InputType{aoc.Example, aoc.Puzzle}
+
+		for _, p := range parts {
+			for _, t := range types {
+				id := aoc.ProblemId{
+					Day:  d,
+					Part: p,
+				}
+				i := aoc.Input{
+					ProblemId: id,
+					Type:      t,
+					//Expected: expected,
+				}
+				var ok bool
+				i.Solve, ok = solutions.Fn[id]
+				if !ok {
+					fmt.Printf("%s Not implemented\n", i)
+					continue
+				}
+				fmt.Printf("%s", i)
+				_ = i.Calculate()
+			}
+		}
+	}
+}
2023/go/README.md
@@ -0,0 +1,14 @@
+# 2023 Advent of Code - Golang 
+
+Run all:
+
+```bash
+just
+```
+
+Run a specific day:
+```bash
+just run 01
+```
+
+
2023/zig/src/01.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+
+pub fn main() !void {
+    const stdout_file = std.io.getStdOut().writer();
+    var bw = std.io.bufferedWriter(stdout_file);
+    const stdout = bw.writer();
+
+    var file = try std.fs.cwd().openFile("../input/01-e1.txt", .{});
+    defer file.close();
+
+    var buf_reader = std.io.bufferedReader(file.reader());
+    var in_stream = buf_reader.reader();
+
+    var buf: [1024]u8 = undefined;
+    while (try in_stream.readUntilDelimiterOrEof(&buf, '\n')) |line| {
+        try stdout.print("{s}\n", .{line});
+    }
+    try bw.flush();
+}
2023/zig/src/buf-iter.zig
@@ -0,0 +1,43 @@
+const std = @import("std");
+
+const ReaderType = std.fs.File.Reader;
+const BufReaderType = std.io.BufferedReader(4096, ReaderType);
+const BufReaderReaderType = BufReaderType.Reader;
+
+pub const ReadByLineIterator = struct {
+    file: std.fs.File,
+    reader: ReaderType,
+    buf_reader: BufReaderType,
+    stream: ?BufReaderReaderType,
+    buf: [4096]u8,
+
+    pub fn next(self: *@This()) !?[]u8 {
+        if (self.stream == null) {
+            self.stream = self.buf_reader.reader();
+        }
+        if (self.stream) |stream| {
+            return stream.readUntilDelimiterOrEof(&self.buf, '\n');
+        }
+        unreachable;
+    }
+
+    pub fn deinit(self: *@This()) void {
+        self.file.close();
+    }
+};
+
+// Iterate over the lines in the file using a buffered reader.
+// Caller is responsible for calling deinit() on returned iterator when done.
+pub fn iterLines(filename: []const u8) !ReadByLineIterator {
+    var file = try std.fs.cwd().openFile(filename, .{});
+    var reader = file.reader();
+    var buf_reader = std.io.bufferedReader(reader);
+
+    return ReadByLineIterator{
+        .file = file,
+        .reader = reader,
+        .buf_reader = buf_reader,
+        .stream = null,
+        .buf = undefined,
+    };
+}
2023/zig/build.zig
@@ -0,0 +1,91 @@
+const std = @import("std");
+
+// Although this function looks imperative, note that its job is to
+// declaratively construct a build graph that will be executed by an external
+// runner.
+pub fn build(b: *std.Build) void {
+    // Standard target options allows the person running `zig build` to choose
+    // what target to build for. Here we do not override the defaults, which
+    // means any target is allowed, and the default is native. Other options
+    // for restricting supported target set are available.
+    const target = b.standardTargetOptions(.{});
+
+    // Standard optimization options allow the person running `zig build` to select
+    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
+    // set a preferred release mode, allowing the user to decide how to optimize.
+    const optimize = b.standardOptimizeOption(.{});
+
+    const lib = b.addStaticLibrary(.{
+        .name = "zig-hello",
+        // In this case the main source file is merely a path, however, in more
+        // complicated build scripts, this could be a generated file.
+        .root_source_file = .{ .path = "src/root.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    // This declares intent for the library to be installed into the standard
+    // location when the user invokes the "install" step (the default step when
+    // running `zig build`).
+    b.installArtifact(lib);
+
+    const exe = b.addExecutable(.{
+        .name = "01",
+        .root_source_file = .{ .path = "src/01.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    // This declares intent for the executable to be installed into the
+    // standard location when the user invokes the "install" step (the default
+    // step when running `zig build`).
+    b.installArtifact(exe);
+
+    // This *creates* a Run step in the build graph, to be executed when another
+    // step is evaluated that depends on it. The next line below will establish
+    // such a dependency.
+    const run_cmd = b.addRunArtifact(exe);
+
+    // By making the run step depend on the install step, it will be run from the
+    // installation directory rather than directly from within the cache directory.
+    // This is not necessary, however, if the application depends on other installed
+    // files, this ensures they will be present and in the expected location.
+    run_cmd.step.dependOn(b.getInstallStep());
+
+    // This allows the user to pass arguments to the application in the build
+    // command itself, like this: `zig build run -- arg1 arg2 etc`
+    if (b.args) |args| {
+        run_cmd.addArgs(args);
+    }
+
+    // This creates a build step. It will be visible in the `zig build --help` menu,
+    // and can be selected like this: `zig build run`
+    // This will evaluate the `run` step rather than the default, which is "install".
+    const run_step = b.step("run", "Run the app");
+    run_step.dependOn(&run_cmd.step);
+
+    // Creates a step for unit testing. This only builds the test executable
+    // but does not run it.
+    const lib_unit_tests = b.addTest(.{
+        .root_source_file = .{ .path = "src/root.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
+
+    const exe_unit_tests = b.addTest(.{
+        .root_source_file = .{ .path = "src/main.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
+
+    // Similar to creating the run step earlier, this exposes a `test` step to
+    // the `zig build --help` menu, providing a way for the user to request
+    // running the unit tests.
+    const test_step = b.step("test", "Run unit tests");
+    test_step.dependOn(&run_lib_unit_tests.step);
+    test_step.dependOn(&run_exe_unit_tests.step);
+}
2023/zig/build.zig.zon
@@ -0,0 +1,62 @@
+.{
+    .name = "zig-hello",
+    // This is a [Semantic Version](https://semver.org/).
+    // In a future version of Zig it will be used for package deduplication.
+    .version = "0.0.0",
+
+    // This field is optional.
+    // This is currently advisory only; Zig does not yet do anything
+    // with this value.
+    //.minimum_zig_version = "0.11.0",
+
+    // This field is optional.
+    // Each dependency must either provide a `url` and `hash`, or a `path`.
+    // `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
+    // Once all dependencies are fetched, `zig build` no longer requires
+    // Internet connectivity.
+    .dependencies = .{
+        // See `zig fetch --save <url>` for a command-line interface for adding dependencies.
+        //.example = .{
+        //    // When updating this field to a new URL, be sure to delete the corresponding
+        //    // `hash`, otherwise you are communicating that you expect to find the old hash at
+        //    // the new URL.
+        //    .url = "https://example.com/foo.tar.gz",
+        //
+        //    // This is computed from the file contents of the directory of files that is
+        //    // obtained after fetching `url` and applying the inclusion rules given by
+        //    // `paths`.
+        //    //
+        //    // This field is the source of truth; packages do not come from an `url`; they
+        //    // come from a `hash`. `url` is just one of many possible mirrors for how to
+        //    // obtain a package matching this `hash`.
+        //    //
+        //    // Uses the [multihash](https://multiformats.io/multihash/) format.
+        //    .hash = "...",
+        //
+        //    // When this is provided, the package is found in a directory relative to the
+        //    // build root. In this case the package's hash is irrelevant and therefore not
+        //    // computed. This field and `url` are mutually exclusive.
+        //    .path = "foo",
+        //},
+    },
+
+    // Specifies the set of files and directories that are included in this package.
+    // Only files and directories listed here are included in the `hash` that
+    // is computed for this package.
+    // Paths are relative to the build root. Use the empty string (`""`) to refer to
+    // the build root itself.
+    // A directory listed here means that all files within, recursively, are included.
+    .paths = .{
+        // This makes *all* files, recursively, included in this package. It is generally
+        // better to explicitly list the files and directories instead, to insure that
+        // fetching from tarballs, file system paths, and version control all result
+        // in the same contents hash.
+        "",
+        // For example...
+        //"build.zig",
+        //"build.zig.zon",
+        //"src",
+        //"LICENSE",
+        //"README.md",
+    },
+}