Add solution for day 3 part 1

Fix step 2

add commentary

remove inputs
master
eater 4 years ago
parent 954a9118cd
commit a03ed4c2d6

4
.gitignore vendored

@ -0,0 +1,4 @@
build
.idea
.gradle
input

@ -1,100 +0,0 @@
56583
83363
127502
138143
113987
147407
111181
92655
79802
64636
108805
148885
51022
120002
52283
53573
142374
143523
121158
63332
63203
142400
105515
140150
89910
93081
129752
86731
128755
134756
131066
77990
77081
85779
137271
72889
117608
132442
115294
59414
75495
79459
107669
81496
144432
69138
53410
71199
141799
63964
110945
102174
87697
88838
93552
145531
54602
65080
66865
139693
98048
60409
88384
138807
130854
75997
130900
125974
129123
93480
86042
128187
74981
88144
96629
148836
124473
57616
93477
104174
97407
123017
85408
64862
85298
88142
62182
128983
62981
124580
56339
94335
125521
121373
78777
125132
94411
57789
97384
79900

@ -1,100 +0,0 @@
56583
83363
127502
138143
113987
147407
111181
92655
79802
64636
108805
148885
51022
120002
52283
53573
142374
143523
121158
63332
63203
142400
105515
140150
89910
93081
129752
86731
128755
134756
131066
77990
77081
85779
137271
72889
117608
132442
115294
59414
75495
79459
107669
81496
144432
69138
53410
71199
141799
63964
110945
102174
87697
88838
93552
145531
54602
65080
66865
139693
98048
60409
88384
138807
130854
75997
130900
125974
129123
93480
86042
128187
74981
88144
96629
148836
124473
57616
93477
104174
97407
123017
85408
64862
85298
88142
62182
128983
62981
124580
56339
94335
125521
121373
78777
125132
94411
57789
97384
79900

@ -1 +0,0 @@
1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,10,19,1,6,19,23,1,13,23,27,1,6,27,31,1,31,10,35,1,35,6,39,1,39,13,43,2,10,43,47,1,47,6,51,2,6,51,55,1,5,55,59,2,13,59,63,2,63,9,67,1,5,67,71,2,13,71,75,1,75,5,79,1,10,79,83,2,6,83,87,2,13,87,91,1,9,91,95,1,9,95,99,2,99,9,103,1,5,103,107,2,9,107,111,1,5,111,115,1,115,2,119,1,9,119,0,99,2,0,14,0

@ -0,0 +1,28 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
}
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
sourceSets {
main {
kotlin {
srcDirs 'src'
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

Binary file not shown.

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
day-3/gradlew vendored

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
day-3/gradlew.bat vendored

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

@ -0,0 +1,2 @@
rootProject.name = 'day-3'

@ -0,0 +1,185 @@
package main
import java.io.File
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
fun main() {
val coll = loadLines(File("./input").readText())
val intersections = coll.intersections();
// Get shorted manhattan path
println("Shortest manhattan distance: ")
println(intersections.map { abs(it.x) + abs(it.y) }.sorted().first())
println("Shortest wire distance: ")
println(intersections.map { it.segmentA.getDistance(it) + it.segmentB.getDistance(it) }.sorted().first())
}
/**
* Really dirty line reader that makes a LineCollection from input
*/
fun loadLines(input: String): LineCollection {
return LineCollection(
input
.trim()
// Split lines
.lines()
.map {
// This records the current point in the line
var current = Point(0, 0)
Line(
it
.trim()
// Segments are split with ,
.split(',')
.map {
// First character is the direction
var d = it[0];
// Rest is distance/length
var distance = it.substring(1).toInt()
// Down and Left are basically Up and Right but inverted.
if (d == 'D' || d == 'L') {
d = if (d == 'D') 'U' else 'R'
distance = -distance
}
if (d == 'U') {
// if direction is up, line is over the Y axis
Segment(Axis.Y, current.y, current.y + distance, current.x).also {
// Set the new current point
current = Point(current.x, current.y + distance);
}
} else {
// If direction is anything else it's Right, so it runs over the X axis
Segment(Axis.X, current.x, current.x + distance, current.y).also {
// Set the new current point
current = Point(current.x + distance, current.y);
}
}
}
)
}
)
}
// Helper class indicating a point (x, y) coordinates
data class Point(val x: Int, val y: Int)
// Class that holds all lines known
data class LineCollection(val lines: List<Line>) {
fun intersections(): List<Intersection> {
val items = mutableListOf<Intersection>()
for (i in 0 until lines.count() - 1) {
for (j in (i + 1) until lines.count()) {
items.addAll(lines[i].intersections(lines[j]));
}
}
return items;
}
}
// Helper class that holds a segment and it's corresponding line
data class LineSegment(val line: Line, val segment: Segment) {
// Get distance to point in line.
// It is assumed that the point is part of the [segment]
fun getDistance(it: Point): Int {
// Since point intersects, use the point coordinate instead of [Segment.end]
val lastEnd = if (segment.axis == Axis.X) it.y else it.x
// Distance traveled
var cost = 0
for (sgm in line.segments) {
// If we found the same segment as we're holding we can stop
if (sgm === segment) {
break
}
// Add length of segment to cost
cost += sgm.high - sgm.low
}
// Get length to point from last segment start
return cost + (max(lastEnd, segment.start) - min(lastEnd, segment.start))
}
fun getDistance(it: Intersection) = getDistance(Point(it.x, it.y))
}
// Holds an intersection, it's point and the 2 LineSegments intersecting
data class Intersection(val x: Int, val y: Int, val segmentA: LineSegment, val segmentB: LineSegment)
// Line is a collection of Segments
data class Line(val segments: List<Segment>) {
// Find all intersections between this and given line
fun intersections(rhs: Line): List<Intersection> {
// flatMap will allow use to return an array
// and will concat all returned arrays
return segments.flatMap { seg ->
// map but it ignores all null returns
rhs.segments.mapNotNull { otherSeg ->
// Find intersection ( ?. indicates only access .let if result is not null )
otherSeg.intersection(seg)?.let {
// Create intersection object
Intersection(it.x, it.y, LineSegment(this, seg), LineSegment(rhs, otherSeg))
}
}
}
}
}
// Quick helper function to make a range between 2 ints
infix fun Int.between(rhs: Int) = (this + 1) until rhs
// Enum with known Axis'es
enum class Axis {
X,
Y;
}
// Class for a segment in a line, a segment is basically a single direction stroke
//
// if [axis] is X, [start] and [end] are on the X axis, and [other] is on the Y axis
// if [axis] is Y, [start] and [end] are on the Y axis, and [other] is on the X axis
data class Segment(val axis: Axis, val start: Int, val end: Int, val other: Int) {
// Get the lowest value from start or end
// helpful for intersection calculation
val low by lazy { min(start, end) }
// Get the highest value from start or end
// helpful for intersection calculation
val high by lazy { max(start, end) }
// Find intersection between 2 segments
fun intersection(rhs: Segment): Point? {
// If 2 lines are on a different axis,
// the calculations are different from them being on the same axis
return if (axis != rhs.axis) {
// If on different axis, check if [other] is between each others [low] and [high]
if (rhs.other in low between high && other in rhs.low between rhs.high) {
if (rhs.axis == Axis.Y) {
Point(other, rhs.other)
} else {
Point(rhs.other, other)
}
} else {
null
}
} else {
// If lines are on the same axis, [which is an edge case that never happens :)]
// [other] should be the same for both to intersect
if (other != rhs.other) {
null
} else {
// only intersect if our [high] is higher than their [low] and vice versa
if (low < rhs.high && rhs.low < high) {
val first = min(rhs.high, high)
Point(if (axis == Axis.Y) other else first, if (axis == Axis.Y) first else other)
} else {
null
}
}
}
}
}
Loading…
Cancel
Save