This is an implementation of a Wining_Move function for a Single-Pile Nim Game.
There are several aproaches to this game.
Fibbonaci Sequence: In order for Player 1 to win, she must try to leave Player 2 with a number of nims that is in the Fibbonaci sequence. To ensure that, she must find all the previous numbers of the fibbonaci sequence up to N, and try to make Player 2 stay in those numbers. To do that, Player 1 should divide the problem into smaller problems, and look at the difference between N and its closest smaller Fib number as a new pile. The new pile would have to be also divided Fibo numbers, and so on. This is a recursive solution. It is a bottom-up solution.
Dynamic Programming: This approach makes no assumptions whatsoever. The main idea behind this algorithm is to explore all possible ways to leave the other player without a Winning_Move. The recursion in this algorithm comes into play by recursively solving smaller problems. It is an up-bottom solution.
In our solution, we will let the machine make its own decissions independently with no a-priori information of a winning strategy, but just the rules of the game. Thus, we will implement the Dynamic Programming approach.
In [2]:
max_moves = {} #makes a hashmap for easy lookup (so program doesn't take forever)
def winning_move(pile, n_max_move):
if pile <= n_max_move: #if your max move is the same as your pile, you win
return pile
for move in range(n_max_move, 0, -1): #cycle through all your options
new_pile = pile - move #your opponent's pile is based on how many you take
if new_pile <= 2 * move:
new_max = new_pile #the max your opponent can take is either their whole pile (if < 2 * how many you took)
else:
new_max = 2 * move #or 2 * how many you took
if (new_pile, new_max) in max_moves: #if we've done this combo before
opponent_move = max_moves[new_pile, new_max] #read the value from the hashmap
else:
opponent_move = winning_move(new_pile, new_max) #else, call winningmove for your opponent
max_moves[new_pile, new_max] = opponent_move #add the value to the hashmap
if opponent_move == 0: #if your opponent has no winning moves
return move #return the number of chips you took
return 0 #if you've gone through all your options and your opponent always had winning moves, you don't have a winning move
In [3]:
def main(ipile):
imaxMoves = ipile-1
m = winning_move(ipile, imaxMoves)
print "Starting pile: is %d, Player1 max-move is %d, and winning-number is %d" %(ipile, imaxMoves, m)
if (m == 0):
print "\t Winning-number is %d. Player 1 has no movements that would lead to a win." %(m)
return 0
iturn = 1
while (ipile > 0 and imaxMoves > 0 and ipile > m):
iturn=iturn+1
if (iturn % 2 == 0):
player = 2
else:
player = 1
ipile = ipile - m
imaxMoves = m * 2
m = winning_move(ipile, imaxMoves)
if (m == 0):
print "\t pile: is %d, Player%d max-move is %d, and winning-number is %d. So Player%d has no moves that would lead to a win." %(ipile, player, imaxMoves, m, player)
break
print "\t pile: is %d, Player%d max-move is %d, and winning-number is %d" %(ipile, player, imaxMoves, m)
main(22)
In [ ]: