From f6a49daaf9f897762a2c4283b3fbb2eef8a54d96 Mon Sep 17 00:00:00 2001 From: Henk Kalkwater Date: Tue, 1 Dec 2020 15:42:11 +0100 Subject: [PATCH] Make code more reusable --- source/app.d | 28 +++++++++++++++++++++------- source/day1.d | 15 ++++++++++----- source/dayutil.d | 29 +++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 source/dayutil.d diff --git a/source/app.d b/source/app.d index ab055e7..dcc841b 100644 --- a/source/app.d +++ b/source/app.d @@ -6,6 +6,7 @@ import std.stdio; import std.getopt; import day1; +import dayutil; immutable string progName = "aoc-2020"; @@ -13,25 +14,38 @@ void function(string[])[] programs = [ &day1.run ]; +void printUsage(string name) { + printUsage(name, null); +} + +void printUsage(string name, string message) { + stderr.writeln("USAGE: %s [day] [part]".format(name)); + if (message != null) { + stderr.writeln(message); + } + exit(-1); +} + void main(string[] args) { int day; if (args.length < 2) { - stderr.writeln("USAGE: %s [day]".format(args[0])); - exit(-1); + printUsage(args[0]); } try { day = to!int(args[1]); } catch (ConvException e) { - stderr.writeln("[day] is not an integer"); - exit(-1); + printUsage(args[0], "[day] is not an integer"); } if (day <= 0 || day > programs.length) { - stderr.writeln("Day must be between 1 and %d".format(programs.length - 1)); - exit(-1); + printUsage(args[0], "[day] must be between 1 and %d".format(programs.length - 1)); } - programs[day - 1](args[2..$]); + try { + programs[day - 1](args[2..$]); + } catch(ArgumentException e) { + printUsage(args[0], e.msg); + } } diff --git a/source/day1.d b/source/day1.d index 52d2061..e351370 100644 --- a/source/day1.d +++ b/source/day1.d @@ -3,20 +3,25 @@ import std.array; import std.conv; import std.exception; import std.format; +import std.functional; import std.range; import std.stdio; +import dayutil; + immutable string progName = "aoc-2020"; void run(string[] args) { - enforce(args.length == 1, "Please provide a part to run %s 1 [part]".format(progName)); - int part = to!int(args[0]); - enforce(part > 0 && part <= 2, "Parts %d to %d supported".format(1, 2)); + /* For each line on stdin, copy it, map it to an integer and sort it. + Sorting a range makes it a SortedRange and functions like contains(range, elem) + will make use of optimised implementations, in the case of contains(range, elem) + it will use a binary search instead of a linear search */ auto numbers = stdin.byLineCopy.map!(a => to!int(a)).array.sort; - auto fun = part == 1 ? &part1 : &part2; - writeln(fun(numbers)); + int solution = parts!int(args, partial!(part1, numbers), partial!(part2, numbers)); + enforce(solution >= 0, "No solutions found"); + writeln(solution); } int part1(SortedRange!(int[]) numbers) { diff --git a/source/dayutil.d b/source/dayutil.d new file mode 100644 index 0000000..1d22d64 --- /dev/null +++ b/source/dayutil.d @@ -0,0 +1,29 @@ +import std.conv; +import std.exception; +import std.format; + +class ArgumentException : Exception{ + mixin basicExceptionCtors; +} + +/** + * Helper function for implementing + * + * Looks at the first argument string, and calls the delegate dgs[i + 1], while making sure nothing + goes out bounds. + * + * Params: + * args = Reference to string with arguments, usually from main. + * dgs = list of delegates. Which one will be called depends on args[0] + */ +R parts(R)(ref string[] args, R delegate()[] dgs ...) { + ulong len = dgs.length; + enforce!ArgumentException(args.length >= 1, "Please provide a part to run as a command line argument"); + int part = to!int(args[0]); + enforce!ArgumentException(part > 0 && part <= len, "This day supports parts %d to %d".format(1, len)); + + // Remove the first argument + args = args[1..$]; + + return dgs[part - 1](); +}