Zulko / easyAI

Python artificial intelligence framework for games
http://zulko.github.io/easyAI/
Other
639 stars 126 forks source link

Chess #12

Closed Mcmann283 closed 7 years ago

Mcmann283 commented 9 years ago

image I would like to have some help adjusting possible_moves(self) and make_move(self,move) sections to work with chess. It seems that the Negamax algorithm

  1. calls possible_moves(self) which prints correctly ['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1'] for FEN position 8/8/2pk4/8/8/8/3PK3/8 w - - 0 1
  2. next it trys to go through each move 'd2d3' using the make_move(self,move) method. This updates self.board
  3. it now looks at the ['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5'] possibilities after 'd2d3' is called
  4. here it trys to do 'd2d4' --the second move from the first list. But, since the self.board has been changed 9 times (2. and 3.) and is currently the position after 'd6c5' is made, it returns the error "d2d4 is not in the move list"

There has to be a way that I can keep possible_moves(self) and make_move(self,move) in sync with the way Negamax uses them in steps 1 through 4 above without the not-in-list error occurring because at some moment Negamax is checking a different branch of the tree and has a different self.board that it is using. I imagine that the solution can be easily spotted by someone more familiar with Negamax's nuances than I. I am grateful for any assistance. Here is the code and its print out.

The Code
from easyAI import TwoPlayersGame, Human_Player, AI_Player, Negamax

class Chess(TwoPlayersGame):
    def __init__(self,players):
        global PieceMoved
        self.players = players
        self.board = PGN("8/8/2pk4/8/8/8/3PK3/8 w - - 0 1")
        self.nplayer = 2
        PieceMoved = []
    def possible_moves(self):
        global Continuations
        global ForReply
        global ListMoves
        Begin(self.board)
        OrigContin = Continuations[:]
        OrigForReply = ForReply[:]
        ListMoves = []
        FT1 = [100019,100020,100021,100022,100023,100024,100025,100026,100027,100028,100029,100030,100031,100032,100033,100034,100035,100036,100037,100038,100039,100040,100041,100042,100043,100044,100045,100046,100047,100048,100049,100050,100051,100052,100053,100054,100055,100056,100057,100058,100059,100060,100061,100062,100063,100064,100065,100066,100067,100068,100069,100070,100071,100072,100073,100074,100075,100076,100077,100078,100079,100080,100081,100082]
        FT2 = ["a8","a7","a6","a5","a4","a3","a2","a1","b8","b7","b6","b5","b4","b3","b2","b1","c8","c7","c6","c5","c4","c3","c2","c1","d8","d7","d6","d5","d4","d3","d2","d1","e8","e7","e6","e5","e4","e3","e2","e1","f8","f7","f6","f5","f4","f3","f2","f1","g8","g7","g6","g5","g4","g3","g2","g1","h8","h7","h6","h5","h4","h3","h2","h1"]
        for ccc in OrigContin:
            ForTwoLines = OrigForReply[OrigContin.index(ccc)]
            FromSquare = FT2[FT1.index(ForTwoLines[0])]
            ToSquare = FT2[FT1.index(ForTwoLines[1])]
            ListMoves.append(FromSquare + ToSquare)
        return ListMoves
    def make_move(self,move):
        global Continuations
        global ListMoves
        print(ListMoves)
        print(move)
        try:
            self.board = Continuations[ListMoves.index(move)]
        except ValueError:
            print(move," is not in the move list")
    def win(self):
        Eval = EvalCaptures(self.board)
        #100 means white has captured blacks pawn
        #-100 means black has captured whites pawn
        if (Eval == 100) or (Eval == -100):
            return 1
        else:
            return 0
    def is_over(self):
        return self.win()
    def show(self):
        pass
    def scoring(self):
        if game.win():
            return 100
        else:
            return 0
ai = Negamax(2) #The AI will think 2 moves in advance
game = Chess([ Human_Player(),AI_Player(ai)])
history = game.play()
Its Print Out
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
d2d3
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
c6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6c7
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6d7
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6e7
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6e6
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6e5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6d5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d2d4
d2d4  is not in the move list
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
d2d3
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
e2d3
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
c6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
e2e3
e2e3  is not in the move list
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
d2d3
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
e2f3
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
c6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
e2f2
e2f2  is not in the move list
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
d2d3
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
e2f1
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
c6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
e2e1
e2e1  is not in the move list
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
d2d3
['d2d3', 'd2d4', 'e2d3', 'e2e3', 'e2f3', 'e2f2', 'e2f1', 'e2e1', 'e2d1']
e2d1
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
c6c5
['c6c5', 'd6c7', 'd6d7', 'd6e7', 'd6e6', 'd6e5', 'd6d5', 'd6c5']
d2d3
d2d3  is not in the move list

Move #1: player 2 plays d2d3 :

Player 1 what do you play ? 
JohnAD commented 7 years ago

Without the rest of the code, I can't accurately predict what is happening. But, from the general structure, it appears that Continuations, ListMoves, and ForReply are being stored (and maybe manipulated) outside of the Chess class instance. Because of that, when the many variants of Chess are being spawned (via .copy()) by the Negamax AI, those variables are not changing in response.

I recommend that you store those three data structures inside the class. (self.Continuations rather than global Continuations.) If those data structures need manipulation by an outside library, have Chess call those. You might have to pass references to that outside library into Chess. I can only guess at the details, however.

Or better, place those three data structures inside the self.board's PGN class.

FYI, the .copy() can be referenced in line 80 of easyAI/AI/Negamax.py. link here