To reduce the scope of the problem there is a rule that you can only move to the right or to the top.
This means that A
can never be higher up or farther to the right than B
.
I was happy to hear this so that I didn't have to think about these scenarios anymore.
A couple of examples were then added to clarify what the test was looking for.
Admittedly, I have improved these examples here a bit.
The ones I was looking at had no color coding.
Also, the six paths example was missing.
Here, the interviewer asked me "Can you see the six paths?".
Obviously, I could not see the paths because they weren't there.
I started drawing lines with my finger on my screen and then proclaimed "Yes, I can see six paths.".
I'll add these interruptions any time I think the flow of the interview could have been better.
This was the first occurrence when I wished for an online whiteboard like Miro .
We started using it at work and I can't imagine a remote-life without it anymore.
I thought about getting a pen and paper but as my interviewers wouldn't be able to see what I doodle in front of me I ditched the idea.
For now, I was left with my imagination.
Unfortunately, this also means that my interviewers were left with my imagination which may have been a problem going forward.
Starting to code<
">syncfiddle for that.
I had never used this tool before.
My first question was "Where can I write tests?" because that's how I approach problems.
Sadly, the tool doesn't support this but my interviewer pointed me to a couple of console.log()
statements that would serve as my tests.
The first console.log()
was A(0;0)
and B(1;1)
.
Admittedly, I was happy so at least have some sort of editor I can code in.
But this one isn't great.
Some issues I had while coding:
No test-runner (still, my biggest issue)
All "tests" were equality checks (i.e. console.log(numberOfPaths(A, B) === 6))
) which isn't helpful because this doesn't show you how far off your solution is
No proper autocompletion (a couple of times I had syntax errors because I was missing a closing parenthesis)
Wrong comment syntax (I used CMD + /
to toggle comments for a line or a block and it used the wrong syntax which screwed my code)
If they would have asked me before the interview whether I use VSCode with LiveShare we could have used a proper editor.
That way I would have been in an environment I'm used to, where I know the shortcuts, and where I could have added a spec
-file.
Also, we could have seen each other's cursers which would have made it much easier to talk about specific parts of the code.
Before I started to code I said "I'm pretty sure this is going to be a recursive function in the end, but I'd like to start simple nonetheless.".
I do this because TDD wants you to write the least amount of code to fix a test.
So, I went ahead and wrote the following code.
function numberOfPaths ( A , B ) {
let stepsX = 0
for ( let i = A . x ; i < B . x ; i++ ) {
stepsX++
}
let stepsY = 0
for ( let i = A . y ; i < B . y ; i++ ) {
stepsY++
}
return stepsX + stepsY
}
With that code, the first "test" went green.
I was happy.
My interviewer was not.
He told me that this algorithm does compute the number of steps and not the number of paths.
That is true and also the reason why I called the variables steps and not paths .
I was fully aware that this algorithm is not correct (and stated this before I wrote it) but I always want to start simple and then become more complex over time.
That's how I approach problems and I thought that the interview was about that.
Anyway, I realized that I might need to show more of my algorithmic skills.
I had the intuition that he wanted me to write the recursive version right away.
With recursion, you need to make sure that you have some notion of "did I reach the end yet?" in your algorithm.
Also known as the abortion criteria.
Otherwise, no matter what you'll do you'll end up with a stack overflow error .
I removed the code I had written so far and implemented the following two methods.
function canMoveX ( A , B ) {
return A . x < B . x
}
function canMoveY ( A , B ) {
return A . y < B . y
}
Interruption.
"What are you doing there? Please explain what you're up to."
OK, I might have been too silent when I removed my old code and added these two methods.
I explained what I was going to write a recursive algorithm and that I think that I will need these two helper methods.
Then I stubbed out the following code.
function numberOfPaths ( A , B ) {
if ( ! canMoveX ( A , B ) && ! canMoveY ( A , B ) ) {
}
if ( ! canMoveX ( A , B ) ) {
}
if ( ! canMoveY ( A , B ) ) {
}
}
I explained that these are the conditions I'm thinking of.
When we can't move anymore we know that we've reached B
.
If we can only move into one direction we hit a border.
That also means no new paths can appear given the rules we defined.
I probably made a big mistake right then and there.
For me, part of the solution was to count how often a new path would split away along the way.
As it turned out at the end of the interview that was not the mental model of the interviewer.
This might have prevented a couple of misunderstandings along the way.
Given that I wanted to count splits along the way I wrote the following (wrong) code.
function numberOfPaths ( A , B , paths = 0 ) {
if ( ! canMoveX ( A , B ) && ! canMoveY ( A , B ) ) {
return paths
}
if ( ! canMoveX ( A , B ) ) {
return numberOfPaths ( { x : A . x , y : A . y + 1 } , B , paths)
}
if ( ! canMoveY ( A , B ) ) {
return numberOfPaths ( { x : A . x + 1 , y : A . y } , B , paths)
}
return (
numberOfPaths ( { x : A . x + 1 , y : A . y } , B , paths + 1 ) +
numberOfPaths ( { x : A . x , y : A . y + 1 } , B , paths + 1 )
)
}
At this point, I should add that the mental model of the interviewer was "count how often we arrive at B
".
That doesn't play nicely with incrementing a number when you reach a split position.
He also asked me why I was incrementing a variable when there is a split to which I replied that I thought the task was to count the number of paths.
Given that we looked at the problem from two different angles and that from his point of view I was heading in the wrong direction I can understand that he wanted me to re-iterate the problem.
We even looked at the initial diagram (at the top of this post) again and he wanted me to state the problem again.
I must admit, that I was put off a little by that.
Because I didn't think that I was stuck.
My "tests" we're failing but that was OK.
I did not assume to crank out the correct algorithm.
When I looked at my conditionals I felt confident that they were correct.
The resulting number was simply way too large which meant there must be something wrong when I incremented my counter.
Then something happened that I did not expect.
He changed my code.
Now it looked like this:
function numberOfPaths ( A , B , paths = 0 ) {
if ( ! canMoveX ( A , B ) && ! canMoveY ( A , B ) ) {
return 1
}
if ( ! canMoveX ( A , B ) ) {
return numberOfPaths ( { x : A . x , y : A . y + 1 } , B , paths)
}
if ( ! canMoveY ( A , B ) ) {
return numberOfPaths ( { x : A . x + 1 , y : A . y } , B , paths)
}
return (
numberOfPaths ( { x : A . x + 1 , y : A . y } , B , paths + 1 ) +
numberOfPaths ( { x : A . x , y : A . y + 1 } , B , paths + 1 )
)
}
Now all tests were "green".
However, my line of thought was gone.
Why?
Because this solution only makes sense in the "count how often we arrive at B
" model and not the "increase counter whenever we split a path" one.
In retrospect, it feels like the interviewer might have been convinced that I'm going nowhere.
He might have thought that by changing my code he's going to help me.
However, I felt this was extremely rude.
Whenever I'm pairing or interviewing someone I would always ask whether I'm allowed to type something before I do it.
Before that, I would highlight a piece of code (which didn't work in the editor we were using) to focus the attention of my pair.
The error I made was trying to carry the number of paths forward through the recursion.
A change that would fit better would be that one:
function numberOfPaths ( A , B ) {
if ( ! canMoveX ( A , B ) && ! canMoveY ( A , B ) ) {
return 0
}
if ( ! canMoveX ( A , B ) ) {
return numberOfPaths ( { x : A . x , y : A . y + 1 } , B )
}
if ( ! canMoveY ( A , B ) ) {
return numberOfPaths ( { x : A . x + 1 , y : A . y } , B )
}
return (
numberOfPaths ( { x : A . x + 1 , y : A . y } , B ) +
numberOfPaths ( { x : A . x , y : A . y + 1 } , B ) +
1
)
}
Yes, this code is still not entirely correct (it's off by one) but would have gotten me closer to the solution.
Of course, I can only guess that the interviewer couldn't follow.
And ultimately its probably my fault for not being clear about my approach earlier on.
What I would have wished for though would have been that the interviewer not simply point out the mistakes but asked me to re-iterate my approach.
After all, this interview is all new information and work conditions for me and not for him.
I'm also not saying that my approach is better than his.
It's definitively more complicated.
But hey, make it work, then make it pretty.
For completeness, here's the code following my model that would ultimately work.
function numberOfPaths ( A , B , initial = true ) {
if ( ! canMoveX ( A , B ) && ! canMoveY ( A , B ) ) {
return 0
}
if ( ! canMoveX ( A , B ) ) {
return numberOfPaths ( { x : A . x , y : A . y + 1 } , B , false )
}
if ( ! canMoveY ( A , B ) ) {
return numberOfPaths ( { x : A . x + 1 , y : A . y } , B , false )
}
return (
numberOfPaths ( { x : A . x + 1 , y : A . y } , B , false ) +
numberOfPaths ( { x : A . x , y : A . y + 1 } , B , false ) +
( initial ? 2 : 1 )
)
}
I haven't published the post yet but I keep thinking about it.
I've come up with another solution that even better fits my mental model.
Clear proof that 20 minutes doesn't tell you anything about a candidate.
function numberOfPaths ( A , B , newPaths = 1 ) {
if ( ! canMoveX ( A , B ) || ! canMoveY ( A , B ) ) {
return newPaths
}
return (
newPaths +
numberOfPaths ( { x : A . x + 1 , y : A . y } , B , 1 ) +
numberOfPaths ( { x : A . x , y : A . y + 1 } , B , 0 )
)
}