Hello, all. I am in the process of writing a peg solitaire solver in Python. It will allow the user to select the shape of the board in the future but for now I'm testing it with a standard 5 row triangle game, like the ones you find in Cracker Barrel. It's the game where you start out with pegs in holes, with one hole empty, then jump pegs (like checkers) and try to get exactly one peg remaining in the end. My solver will solve for all solutions for a given starting hole, whether the game ends with one peg, two, etc.
I chose Python as my language of choice for a few reasons:
- I'm new to Python so I want to learn it.
- I initially thought my data structure would be more involved so I chose Python over C++ to open up the opportunity to use dictionaries.
- Taking user-entered strings and using them as integers is just easier in interpreted languages.
Turns out C++ probably wouldn't have been too bad a choice but I'm enjoying the experience of learning Python.
Below, you'll find the code for the main recursive algorithm for moving along the board and solving it. The background you may need to understand it is as follows:
- There is a Simulation class that instantiates a simulation object, which contains the
move()
function. It also has as one of its members a list calledmoves
, which stores all the moves for each possible path that can be taken for each game. -
board
is a board object andtemp_stack
is the running stack for a current try at solving the game. -
board.poss_moves
is a list of all possible moves the 5 row triangle board contains.
So, when move
is called, it checks to see if there are any moves left. If not, the game is over. It appends to the simulation.moves
list a snapshot of the game and returns. If the game is not over, it cycles through the list of all possible moves and checks to see which ones that apply to the state of the board that has been passed through as a parameter. If a move can be made, it deep copies a new board and stack (so there can be fresh, new objects being passed into the recursion tree), adjusts the board copy, and updates the temp_stack copy that a new move has been made. Recursion happens as move
is called again with the copies as arguments.
The idea is that when the game ends, the program backs out of the recursion tree and picks up where it left off in the main loop. I know Python uses pass by reference so I was a bit unsure as to whether I'd be able to use a recursion tree effectively so this is why I used deep copy, so that there is a previous version of the board and temp_stack to revert to when the back tracking occurs. After all the loops expire, the program should back fully out of recursion and resume. But it doesn't. In fact, it winds up freezing up the computer.
Any thoughts as to what I'm doing wrong? Also, feel free to offer up ideas on how you might do this. I know other people have written peg solitaire solvers. Some have actually explicitly created tree data structures to house each board state but I feel that is not necessary if you use the 'imaginary' recursion tree.
Here's the move function:
def move(self, board, temp_stack):
if board.game_end():
self.moves.append({'final_peg_num': board.count_pegs(), 'list': temp_stack})
else:
for i in range(len(board.poss_moves)):
if board.can_move(board.poss_moves[i]):
new_board = deepcopy(board)
new_stack = deepcopy(temp_stack)
# move the peg and adjust the board
new_board.set_val(board.poss_moves[i])
# Append the stack
new_stack.append(board.poss_moves[i])
self.move(new_board, new_stack)
May the discussions begin!