This is another iteration of my previous question.
This program plays the game Chopsticks using the Minimax algorithm. I search the tree using recursion. The problem, however, is that it is just too slow. I can only search with a depth of about six; otherwise, it just takes too long. How can I improve my code for performance?
I'm writing this for 1) fun and 2) to beat my friend at Chopsticks. The latter is very important.
import itertools
from copy import deepcopy
class State:
def __init__(self):
self.person_left = 1
self.person_right = 1
self.computer_left = 1
self.computer_right = 1
self.is_computer_turn = False
def tap(self, is_to_left, is_from_left):
if self.is_computer_turn:
computer_value = self.computer_left if is_from_left else self.computer_right
if is_to_left:
if computer_value != 0 and self.person_left != 0:
self.person_left += computer_value
else:
return False
else:
if computer_value != 0 and self.person_right != 0:
self.person_right += computer_value
else:
return False
else:
person_value = self.person_left if is_from_left else self.person_right
if is_to_left:
if person_value != 0 and self.computer_left != 0:
self.computer_left += person_value
else:
return False
else:
if person_value != 0 and self.computer_right != 0:
self.computer_right += person_value
else:
return False
if self.computer_left >= 5: self.computer_left = 0
if self.computer_right >= 5: self.computer_right = 0
if self.person_left >= 5: self.person_left = 0
if self.person_right >= 5: self.person_right = 0
self.is_computer_turn = not self.is_computer_turn
return True
def put_together(self, sticks_on_left):
if self.is_computer_turn:
if sticks_on_left < 0 or (self.computer_right + (self.computer_left - sticks_on_left)) < 0:
return False
if sticks_on_left == self.computer_right:
return False
if sticks_on_left == self.computer_left or self.computer_right == (self.computer_right + (self.computer_left - sticks_on_left)):
return False
self.computer_right += (self.computer_left - sticks_on_left)
self.computer_left = sticks_on_left
else:
if sticks_on_left < 0 or (self.person_right + (self.person_left - sticks_on_left)) < 0:
return False
if sticks_on_left == self.person_right:
return False
if sticks_on_left == self.person_left or self.person_right == (self.person_right + (self.person_left - sticks_on_left)):
return False
self.person_right += (self.person_left - sticks_on_left)
self.person_left = sticks_on_left
self.is_computer_turn = not self.is_computer_turn
return True
def is_leaf(self, past_iter):
if past_iter > 6:
return True
if (self.person_left == self.person_right == 0) or (self.computer_left == self.computer_right == 0):
return True
else:
return False
def value(self, past_iter):
if self.person_left == self.person_right == 0:
return 6 - past_iter + 10
else:
if self.computer_left == self.computer_right == 0:
return -1 * (6 - past_iter + 10)
else:
return 0
def best_move_value(state, do_max, past_iter):
if state.is_leaf(past_iter):
return state.value(past_iter)
else:
child_nodes = gen_child_nodes(state)
child_node_values = [best_move_value(s, not do_max, (past_iter + 1)) for s in child_nodes]
if past_iter != 0:
if do_max:
return max(child_node_values)
else:
return min(child_node_values)
else:
return child_nodes[child_node_values.index(max(child_node_values))]
def gen_child_nodes(state):
child_nodes = []
for to_left, from_left in itertools.product((True, False), repeat=2):
testing_state = deepcopy(state)
if testing_state.tap(to_left, from_left):
child_nodes.append(testing_state)
for arg in range(0, 5):
testing_state = deepcopy(state)
if testing_state.put_together(arg):
child_nodes.append(testing_state)
return child_nodes
game = State()
move = ['','','']
while not game.is_leaf(0):
print game.person_left, " ", game.person_right, " ", game.computer_left, " ", game.computer_right
if game.is_computer_turn:
game = best_move_value(game, True, 0)
game.is_computer_turn = False
else:
while not ((move[0] == 'split') or (move[0] == 'tap')):
string = raw_input('What would you like to do?\n'
'Type \'split\', then how many fingers you\'d like to have left on the left hand after splitting\n'
'Type \'tap\', whether you are tapping to the computer\'s left hand (True or False), and whether your tapping hand is your left hand (True or False)\n'
'Examples: \'split 3\', \'tap True True\', \'split\' 1\', \'tap False True\'\n')
move = string.split()
if move[0] == 'split':
game.put_together(int(move[1]))
else:
if move[0] == 'tap':
game.tap((move[1])[0] == 'T', (move[2])[0] == 'T')
move = ['', '', '']