The error was that function next-game-state
expected a matrix, but it returned a seq
of seq
. That was from partition
(which was fed by for
, which also creates a seq
). Hence the problem only showed up during the second iteration.
Hiding the problem was our use of get-in
. While being helpful, get-in
returns nil
for non-existing indices, or for structures other than a map
or a vector
(in our case it was a seq
of seq
), or even for nil
!
(get-in [0 1 2] [:non-existing-index-or-key]) ;=> nil!
(get-in nil nil) ;=> nil and quiet!
We meant for our overall structure of states to be a matrix. Then the following would work:
(get-in [[:| :#]
[:| :.]]
[0 1]) ;=> :#
But after an iteration, our overall structure of states (in loop
's parameter 'g') was not a matrix anymore. It became seq
of seq
instead, hence get-in
bit us silently:
(get-in '((:| :#)
(:| :.))
[0 1]) ;=> nil!
Locating it took a while. The fix was easy: https://github.com/VanClojure/AdventOfCode18/commit/435b78f.
Testing it wouldn't be trivial. Why? Because an instinctive way is to test for equality with =
, but =
considers vectors and seq
(with equal items) equal. We would need an extra check of (type ...)
or (vector? ...)
.
Do you know a gut feeling telling you something is wrong? A hint to the problem existed even earlier: Function next-game-state
required :while (some? cell)
. That didn't make sense, as all cells were initially in a non-nil
state. Hence, if the execution forces you to do something that seems strange, don't allow to be forced, but look at what's happening. Trust your gut.
Once we agree on function signatures, how about splitting their implementation and writing tests between various people. That prevents bias. It gets the test to the implementor early (hence faster feedback for the implementor, and writing and testing integration early).
That would have helped with assumptions and spreading the knowledge.
We agreed that the cells ("types") would be keywords, but the providers only assumed them to be :|, :#
and :.
After our initial delegation (main functions), we added some helper functions: char->keyword
and cell
. However, the others didn't know, hence couldn't reuse them. One way to keep up to date could be: more frequent GIT push and continuously piping grep defn
into some dashboard. That would need function signatures to take one line each (is that common?), or a more advanced filter (for functions with multiple arities). Alternatively, have a shared live document (for example in Google Docs), where you copy-and-paste the functions signatures.
Is it worthwhile? Any existing or custom ones?
I love how Andrea made function next-game-state
flexible to integrate by having a function as a parameter neighbour-provider
. It makes it easy to test. That made it plug-and-play instead of plug-and-pray.