As a challenge I've decided to try and code a minimax
algorithm without using the internet. I haven't even written the AI class, but I have built a Node class and a search ahead method. The problem I'm running into is that all of the values I'm getting are poorly weighed towards the first player, which makes no sense.
I've even tried playing through some of the branches myself on a physical board to make sure I'm not going crazy, and a lot of the values just aren't matching up. I'd love to know what I'm screwing up, this has been driving me crazy!
When I included a print statement in my search ahead that prints the values of all successive moves, and then input a depth greater than 3, for some reason all of the returned values are negative.
Here's some example values when I check the node1 with a depth of 3:
pocket 8: [-1, -1, -1, -1, -1]pocket 9: [0, -1, -1, -1, -1]pocket 10: [-1, -1, -2, -2, -2]pocket 11: [-1, -1, -2, -2, -2]pocket 12: [-8, -1, -2, -2, -2]pocket 13: [-1, -3, -2, -2, -2]pocket 1: [-1, -1, -2, -2, -8, -3].
When I actually play the game, the resulting values are different than what search ahead seems to think they are(where the values represent the difference between the bottom players score and the top players score)
import copyimport randomimport numpyclass Board(): def __init__(self,turn,position,pockets,game_state): self.turn=turn self.position=position self.pockets=pockets self.game_state=game_state def make_pieces(self): for i in range(1,7): self.pockets[f"pocket_{i}"]=4 for i in range(8,14): self.pockets[f"pocket_{i}"]=4 self.pockets["pocket_7"]=0 self.pockets["pocket_14"]=0 def move_pieces(self): starting_position=self.position current_position=self.position pieces_in_pocket=self.pockets[f"pocket_{self.position}"] pieces_dropped=0 move_again="False" valid_move="True" board_values=[valid_move,move_again] if starting_position==7 or starting_position==14: valid_move="False" move_again="False" board_values=[valid_move,move_again] return board_values elif self.turn=="top" and starting_position in range(1,8): valid_move="False" move_again="False" board_values=[valid_move,move_again] return board_values elif self.turn=="bottom" and starting_position in range(8,15): valid_move="False" move_again="True" board_values=[valid_move,move_again] return board_values else: valid_move="True" if self.pockets[f"pocket_{self.position}"]==0: valid_move="False" final_move=current_position move_again="False" board_values=[valid_move,move_again] return board_values else: for i in range(0,pieces_in_pocket): if current_position==13 and self.turn=="bottom": current_position=0 if current_position==6 and self.turn=="top": current_position=7 if current_position==14 and self.turn=="top": current_position=0 self.pockets[f"pocket_{current_position+1}"]+=1 self.pockets[f"pocket_{starting_position}"]-=1 pieces_in_pocket-=1 pieces_dropped+=1 current_position+=1 final_move=current_position if final_move==7 and self.turn=="bottom": move_again="True" elif final_move==14 and self.turn=="top": move_again="True" else: move_again="False" for i in range(1,7): if final_move==i and self.pockets[f"pocket_{i}"]==1 and self.turn=="bottom": if self.pockets[f"pocket_{14-i}"]==0: self.pockets[f"pocket_7"]=self.pockets[f"pocket_7"] else: self.pockets["pocket_7"]=self.pockets["pocket_7"]+self.pockets[f"pocket_{14-i}"] self.pockets[f"pocket_{14-i}"]=0 self.pockets[f"pocket_7"]=self.pockets[f"pocket_7"]+self.pockets[f"pocket_{i}"] self.pockets[f"pocket_{i}"]=0 for i in range(8,14): if final_move==i and self.pockets[f"pocket_{i}"]==1 and self.turn=="top": if self.pockets[f"pocket_{14-i}"]==0: self.pockets[f"pocket_14"]=self.pockets[f"pocket_14"] else: self.pockets[f"pocket_14"]=self.pockets["pocket_14"]+self.pockets[f"pocket_{14-i}"] self.pockets[f"pocket_{14-i}"]=0 self.pockets[f"pocket_14"]=self.pockets["pocket_14"]+self.pockets[f"pocket_{i}"] self.pockets[f"pocket_{i}"]=0 board_values=[valid_move,move_again] return board_valuesclass Node(): def __init__(self,game_state,pocket,children,depth,values,parent): self.game_state=game_state self.pocket=pocket self.children=children self.depth=depth self.values=values self.parent=parent def search_ahead(self): if self.depth==1: test_board=copy.deepcopy(self.game_state) test_board.position=self.pocket useful_list=test_board.move_pieces() pocket_7=test_board.pockets["pocket_7"] pocket_14=test_board.pockets["pocket_14"] value=pocket_7-pocket_14 if useful_list[0]=="False" and self.parent==None: return None elif useful_list[0]=="False" and self.parent!=None: return None elif useful_list[0]=="True" and self.parent!=None: self.parent.values.append(value) elif useful_list[0]=="True" and self.parent==None: self.values.append(value) elif self.depth!=1: test_board=copy.deepcopy(self.game_state) test_board.position=self.pocket useful_list=test_board.move_pieces() if useful_list[0]=="False" and self.parent==None: self.values="Invalid" return None elif useful_list[0]=="False" and self.parent!=None: return None if useful_list[1]=="True" and test_board.turn=="bottom": test_board.turn="bottom" elif useful_list[1]=="True" and test_board.turn=="top": test_board.turn="top" elif useful_list[1]=="False" and test_board.turn=="bottom": test_board.turn="top" elif useful_list[1]==False and test_board.turn=="top": test_board.turn="bottom" if test_board.turn=="bottom": condition="max" for i in range(1,7): self.children.append(Node(test_board,i,[],(self.depth-1),[],self)) elif test_board.turn=="top": condition="min" for i in range(8,14): self.children.append(Node(test_board,i,[],(self.depth-1),[],self)) for node in self.children: node.search_ahead() if condition=="max" and self.parent!=None: x=max(self.values) self.values=x self.parent.values.append(x) if condition=="min" and self.parent!=None: x=min(self.values) self.parent.values.append(x) print(self.pocket) print(self.values)game_board=Board("bottom",1,"","")game_board.pockets={"pocket_1":[],"pocket_2":[],"pocket_3":[],"pocket_4":[],"pocket_5":[],"pocket_6":[],"pocket_7":[],"pocket_8":[],"pocket_9":[],"pocket_10":[],"pocket_11":[],"pocket_12":[],"pocket_13":[],"pocket_14":[]}game_board.make_pieces()#Bottom pocket nodesnodep1=Node(game_board,1,[],7,[],None)nodep2=Node(game_board,2,[],1,[],None)nodep3=Node(game_board,3,[],1,[],None)nodep4=Node(game_board,4,[],1,[],None)nodep5=Node(game_board,5,[],1,[],None)nodep6=Node(game_board,6,[],1,[],None)#Top pocket nodesnodep8=Node(game_board,8,[],1,[],None)nodep9=Node(game_board,9,[],1,[],None)nodep10=Node(game_board,10,[],1,[],None)nodep11=Node(game_board,11,[],1,[],None)nodep12=Node(game_board,12,[],1,[],None)nodep13=Node(game_board,13,[],1,[],None)nodep1.search_ahead()