Programming by Wishful ThinkingOctober 6, 2017
You’ve probably heard the advice to break your work up into manageable chunks, or to break a complex problem down into simpler parts. This is a handy technique to use while programming as well
You write a function to solve a particular problem, and you write it as if any complex functionality you wish for, has already been written. Afterwards,, you go fill in these functions, and apply the same technique.
Let’s say you’re writing a Tic-Tac-Toe game, and you need to write a function called
getWinner. It takes the board as a 2D array. Each cell in the array contains the string “X”, “O” or “.” (“.” is an empty cell). It returns “X” or “O” if the respective player has a row of 3, and
null otherwise. Here’s what the process for checking that looks like
- Check the horizontal rows. If there’s no winner, …
- Check the vertical rows. If there’s no winner, …
- Return whether there’s a winner on the diagonals.
We just wish that
getHorizontalWinner and its friends existed, so our problem would be as simple as this. However, wishing upon a star doesn’t get you very far, so we still have to implement each of these functions, but at least they’re smaller problems to solve.
Let’s dig in and write
Again, we just write our code as if we already have
getLineWinner, which takes an array of 3 board cells, and returns the player who made a winning line there, if any, otherwise null.
getVerticalWinner will look very similar:
You could, if you want, add a
getColumn function that abstracts away getting an array for a given column of the board, but in this case, I don’t think it’s necessary.
Pretty simple, right? Now the only thing we have left to write is
And we’re done! You could replace the check for
line !== "." with a function like
isEmpty, which would then do the check. This function would be pretty handy when implementing the rest of the game as well, for example to check whether it is valid for a player to put a symbol in a specific cell. But for this example, I’m leaving it as-is.
The most important thing to note here, is that you abstract away details by calling functions you wish existed. This way, you’re naturally splitting your code into reasonably-sized functions, dealing with different levels of abstraction.
I hope this has been helpful to you.