I have created an abstract class named Solver that will house variables
and methods that will be used in every solving class to prevent having
to write the code in several different places.  The Solver class also
acts as a template in a way, as it forces its subclasses to implement
the solve() method.

The changes to HillClimbing are mostly moving methods and variables
common to the various solving algorithms to Solver.  I also changed my
choice in which flavor of the hill climbing algorithm to implement from
first choice to steepest ascent, as first choice may not converge on an
answer fast enough.  If steepest ascent doesn't work, I'll reimplement
first choice.

Problems to figure out:

1. Solver.intializeState - for each row, how to get it to choose a
number at random from the numberBank array at random and without
choosing a number that's already in the row.

2. How to write a hill climbing algorithm that implements steepest
ascent and random restarts (pseudocode only, not production code).

3. HillClimbing.solve() - How to compare two elements in each row of
currentState to verify that they were not present in initialState, and
if not, swap them.  Also, how to do this in a systematic fasion where
the program works through the entire two dimensional array to find the
swap that lowers the solution's score the most (and knows not to commit
to the swap if the score isn't lowered at all.