Introduction
In the Day 23 problem we have to face up two hard parts. The first includes reading data from really concise input and interpreting it, while the second is about searching in some space of states that need to be generated on the fly. Let’s see how we can deal with these problems in Kotlin.
Solution
We use the input map to get multiple information from it. The first (obvious one) is the location of
each amphipod on the map. We store them as the map from location F
to the amphipod type. What we need
more to properly compute the state changes, are the positions to which the amphipod can move in
the specified move. To safe time in computation, we calculate some set of spaces
and map collides
.
The spaces
keeps the coordinates of all fields, that can be occupied by an amphipod. The collides
map
stores the information about the fields on the way from one field on map to another and the number of steps
required to move between these places. We calculate this map by doing some path traversal in
scanPaths
function, that is capable of searching the graph of moves with remembering the paths’ statistics
in path
.
Having some MapState
we can think of generating the next states from the given state. We implement this
in MapState::reachable
by taking into account all the rules described in task. For example, we expressed
the rule of moving only from hallway to room or from room to hallway by writing
|
|
so we skip the situations in which we would move from hallway to hallway and from room to room.
Having these functions implemented, we can start searching for the smallest energy needed to get to
final state of the map. We use the Dijkstra algorithm to solve this problem, but we have to take
care of the states that we generate from current, not final state, as in the current moment of findMinEnergy
we know only some part of all possible states (that we generated from the previously visited states).
Day23.kt
|
|
Extra notes
Let’s take a look at the data class F
that we defined for the field on the map. It’s worth noticing how
data class
es work in Kotlin. If we take a look at the bytecode generated for this class, we would see
such fragment
|
|
that is a representation of hashCode
method for this class. We can see here, that in case of data class
es
the hashCode
, as well as equals
, toString
and other generated method takes into account only the
fields that are a part of primary constructor of the class (so in this case x
, y
and maxRow
fields) and
the other fields declared in class are not taken into account. The same applies to the inheritance from some other
classes - their fields will not be taken into account if we use data class
es, so all the generated
methods will only consider the fields from constructor from data class
. That’s because this type
of classes is intended to use with no inheritance, so model some really simple data. We have to
have this in the back of our minds, not to make some unreal assumptions about the generated code.