#-------------------------------------------------------------------------- #Copyright 2002, Jyrki Alakuijala and Hannu Helminen. #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License version 2 #as published by the Free Software Foundation. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #-------------------------------------------------------------------------- #done #bishop and queen attackmap should pass one square through a pawn attack #use ply - 1 depth search for ordering the moves, not just the evalutor #queen locks (with bishops and rooks, and queens if the defending queen is not defended) #castling should be favored more #first #queen reveals (when attacker is sufficiently defended) #queen (or rook) reveals (when the moving peace checks) - attacker is b, r or q #penalty if a piece (especially queen) cannot be moved safely? #TODO #prune the tree by trusting the evaluator #pawns for center #avoid blocking the center pawns #the use of hash tables for castling info is inefficient #for attacks, develop a new piece called weak pawn, for which captures are good? #rook locks (with bishops) #bishop attack on an immobilized rook #nullmove heuristic? #queen should not be active in the beginning #one bishop is less than half of two bishops #one knight is half of two knights #two knights of the same color is more of an even game than of opposite color #bishop capturing pawn at a2 or a7 should be evaluated deeper # progress indicator # time awareness # png format import chesspersonalities from nchess6 import * from Tkinter import * root = Tk() #f=Frame() # dummy photoimagenames = ( (free, "empty.gif"), (pawn, "pawnw.gif"), (knight, "knightw.gif"), (bishop, "bishopw.gif"), (rook, "rookw.gif"), (queen, "queenw.gif"), (king, "kingw.gif"), (-pawn, "pawnb.gif"), (-knight, "knightb.gif"), (-bishop, "bishopb.gif"), (-rook, "rookb.gif"), (-queen, "queenb.gif"), (-king, "kingb.gif") ) photoimages = {} for (piece, piecephotoname) in photoimagenames: photoimages[piece] = PhotoImage(file=piecephotoname) def formatTime(s): minutes = int(s / 60.0) sec = int(s - 60 * minutes) return "%02d:%02d" % (minutes, sec) def floatrange(start, end, step): a = [] for i in range(int(((end - start) / step) + 1.5)): a.append(start + i * step) return a class BoardUI: def build(m): global root m.root = root m.root.wm_title("pythonchess"); m.root.wm_iconname("chess"); m.mouseDownItem = None m.mouseDownIndex = None # m.time = [None, 300.0, 300.0] m.timeItems = [None, None, None] m.analysisItem = None m.gameNameItem = "Initial board setup" m.lastAnalysisTime = None m.lastMoveTime = None m.replayboard = None m.searchDepth = 2 m.defaultPromotion = StringVar() m.defaultPromotion.set('Q') m.playboard = None m.progressItem = None m.progressRange = [0.0, 1.0] m.progressVar = IntVar() m.progressVar.set(1) m.progressValue = 1 m.analysisVar = IntVar() m.analysisVar.set(1) m.analysisValue = 1 m.lastUserMove = 0 m.insidePonderingLoop = 0 m.boardOrientation = StringVar() m.boardOrientation.set('white') m.whiteDown = 1 m.slowmoveobject = None m.slowmovetoix = None m.automove = 1 m.stopRequest = 0 m.timeLeftBeforeLastMove = 0 m.realTurn = 1 m.moveNow = 0 m.userside = 1 m.controlDown = 0 m.filename = None m.menu_main = Menu() m.menu_file = Menu() m.menu_edit = Menu() m.menu_view = Menu() m.menu_opponent = Menu() m.menu_game = Menu() m.menu_help = Menu() m.menu_edit_promote = Menu() m.menu_file.add_command(label="New", command=m.filenew, underline=0); m.menu_file.add_command(label="Open...", command=m.fileopen, underline=0); m.menu_file.add_command(label="Save", command=m.filesave); m.menu_file.add_command(label="Save as...", command=m.filesaveas); m.menu_file.add_separator(); m.menu_file.add_command(label="Exit", command=m.fileexit, underline=1); m.menu_edit.add_command(label="Undo", command=m.replayprevious, underline=0) m.menu_edit.add_command(label="Replay", command=m.replaynext, underline=0) m.menu_edit.add_command(label="Rewind", command=m.replaystart, underline=2) m.menu_edit.add_separator(); m.menu_edit_promote.add_radiobutton(label="Queen", value='Q', variable=m.defaultPromotion) m.menu_edit_promote.add_radiobutton(label="Rook", value='R', variable=m.defaultPromotion) m.menu_edit_promote.add_radiobutton(label="Bishop", value='B', variable=m.defaultPromotion) m.menu_edit_promote.add_radiobutton(label="Knight", value='N', variable=m.defaultPromotion) m.menu_edit.add_cascade(label="Promotion", menu=m.menu_edit_promote, underline=0) m.menu_view.add_checkbutton(label="Show Analysis", command=m.showAnalysis, variable=m.analysisVar); m.menu_view.add_checkbutton(label="Show Progress", command=m.showProgress, variable=m.progressVar); m.menu_view.add_separator() m.menu_view.add_radiobutton(label="Normal Board - White Down", command=m.boardDir, value="white", variable=m.boardOrientation); m.menu_view.add_radiobutton(label="Rotated Board - White Up", command=m.boardDir, value="black", variable=m.boardOrientation); m.menu_main.add_cascade(label="File", menu=m.menu_file, underline=0) m.menu_main.add_cascade(label="Edit", menu=m.menu_edit, underline=0) m.menu_main.add_cascade(label="View", menu=m.menu_view, underline=0) m.menu_main.add_cascade(label="Opponent", menu=m.menu_opponent, underline=0) # m.menu_main.add_cascade(label="Game", menu=m.menu_game, underline=0) # m.menu_main.add_cascade(label="Help", menu=m.menu_help, underline=0) from chesspersonalities import persons m.personName = StringVar() for level in range(10): for person in persons.keys(): if (persons[person].plys == level): m.menu_opponent.add_radiobutton(label=person, value=person, variable=m.personName, command=(lambda x=person: m.SetOpponentName(x))) m.personName.set("Guido") m.root.configure(menu=m.menu_main); m.canvas = Canvas() m.canvas.after(1000, m.after1000) m.canvasInfo = Canvas() m.piecesOnCanvas = [] m.BuildCanvasGraphics() m.infoMarginX = 0 m.infoMarginY = 10 m.infoCursorInc = 16 m.board = Board() m.board.Setup() m.canvasInfo["width"] = 160 m.canvasInfo["height"] = 480 m.canvasInfo["background"] = "white" m.canvasInfo["highlightcolor"] = "white" m.canvasInfo["highlightbackground"] = "white" m.canvasInfo.pack(side=LEFT,expand=YES,fill=BOTH) # GenerateAllLegalMoves(m.board) m.SetOpponentName("Guido") # m.CopyBoardToWindowGraphic() m.UpdateInfo() m.hashTable = HashTable() m.hashTable.Init() # m.PrepareForNextMoveWhenOpponentThinks() m.replaystart() def SlowMoveStart(m, fromix, toix): m.UpdateProgress(1.0) if m.slowmoveobject: # previous slow move is still going on, stop it m.canvas.delete(m.slowmoveobject) (m.slowmovestartx, m.slowmovestarty) = m.IndexToGraphicsPosition(fromix) (m.slowmoveendx, m.slowmoveendy) = m.IndexToGraphicsPosition(toix) m.slowmoveobject = m.piecesOnCanvas[fromix][0] m.slowmovetoix = toix m.slowmovefraction = 0.0 length = ((m.slowmovestartx - m.slowmoveendx) ** 2 + (m.slowmovestarty - m.slowmoveendy) ** 2) ** 0.5 m.slowmovefractionincr = 0.02 + 0.1 ** (length / 150.0) m.canvas.after(50, m.AfterSlowMoveStart) m.canvas.tkraise(m.slowmoveobject) if m.mouseDownItem == m.slowmoveobject: m.mouseDownItem = None m.mouseDownIndex = None def AfterSlowMoveStart(m): dx = m.slowmoveendx - m.slowmovestartx dy = m.slowmoveendy - m.slowmovestarty incr = m.slowmovefractionincr if not m.insidePonderingLoop: # mate, move extra slow incr = incr * 0.15 m.slowmovefraction = m.slowmovefraction + incr if (m.slowmovefraction >= 1): if (m.mouseDownIndex == m.slowmovetoix): m.mouseDownIndex = None m.mouseDownItem = None m.canvas.delete(m.slowmoveobject) m.slowmoveobject = None m.slowmovetoix = None m.CopyBoardToWindowGraphic() m.UpdateProgress(None) else: m.canvas.tkraise(m.slowmoveobject) from math import atan scale = 6 a = 0.5 * atan((m.slowmovefraction - 0.5) * scale)/atan(scale*0.5) + 0.5 m.canvas.coords(m.slowmoveobject, m.slowmovestartx + a * dx, m.slowmovestarty + a * dy) m.canvas.after(50, m.AfterSlowMoveStart) def BuildCanvasGraphics(m): m.canvas.delete("all") m.xsize = 48 m.ysize = 48 m.canvas["width"] = 10 * m.xsize m.canvas["height"] = 10 * m.ysize m.canvas["background"] = "white" m.canvas["highlightcolor"] = "white" m.canvas["highlightbackground"] = "white" m.canvas.create_rectangle(m.xsize-1, m.ysize-1, 9*m.xsize+1, 9*m.ysize+1, fill="white", outline="black") for i in range(m.xsize, 9 * m.xsize, 4): m.canvas.create_line(i+1, m.ysize-1, m.xsize-1, i+1, fill="black") m.canvas.create_line(9 * m.xsize+1, i-1, i-1, 9 * m.ysize+1, fill="black") for column in range(1,9): coltxt = repr(column) if m.whiteDown: coltxt = repr(9 - column) m.canvas.create_text(m.xsize / 2, m.ysize * column + m.ysize / 2, text=coltxt) for row in range(1,9): rowtxt = " hgfedcba "[row] if m.whiteDown: rowtxt = " abcdefgh "[row] m.canvas.create_text(m.xsize * row + m.xsize / 2, 9 * m.ysize + m.ysize / 2, text=rowtxt) for column in range(10): for row in range(10): color = "white" margin = None if column == 0 or column == 9: margin = "column" margintext = " abcdefgh "[row] if row == 0 or row == 9: if margin == "column": margin = "corner" margintext = " " else: margin = "row" margintext = repr(column) if not margin and not (column + row) & 0x1: rect = m.canvas.create_rectangle( column * m.xsize, row * m.ysize, column * m.xsize + m.xsize, row * m.ysize + m.xsize) m.canvas.itemconfigure(rect, fill="white", outline="white") if margin: m.canvas.itemconfigure(rect, outline=color) m.canvas.pack(side=LEFT,expand=YES,fill=BOTH) m.canvas.bind('', m.mouseDown) m.canvas.bind('', m.mouseMove) m.canvas.bind('', m.mouseUp) m.canvas.bind('', m.keyRelease) m.canvas.bind('', m.keyPress) m.canvas.focus_set() def showAnalysis(m): m.analysisValue = m.analysisVar.get() m.UpdateAnalysisString("") def showProgress(m): m.progressValue = m.progressVar.get() def boardDir(m): m.whiteDown = (m.boardOrientation.get() == 'white') m.BuildCanvasGraphics() m.CopyBoardToWindowGraphic() def SetOpponentName(m, name): m.opponentName = name m.board.personality = chesspersonalities.persons[m.opponentName] m.board.personality.Randomize() m.UpdateInfo() m.searchDepth = m.board.personality.plys def UpdateProgress(m, progressVal): if m.progressItem: m.canvas.delete(m.progressItem) m.progressItem = None if progressVal != None: progress = m.progressRange[0] + progressVal * (m.progressRange[1] - m.progressRange[0]) temp = m.whiteDown m.whiteDown = 1 (startx, starty) = m.IndexToGraphicsPosition(63) (endx, endy) = m.IndexToGraphicsPosition(7) m.whiteDown = temp startx = startx + 48 - 5 endx = endx + 48 + 5 starty = starty - 25 endy = endy + 25 starty = endy - (endy - starty) * progress; m.progressItem = m.canvas.create_rectangle(startx, starty, endx, endy) def UpdateAnalysisString(m, str): if not m.root: return if m.analysisItem: m.canvas.delete(m.analysisItem) m.analysisItem = None if m.gameNameItem: m.canvas.delete(m.gameNameItem) m.gameNameItem = None if m.analysisValue: m.gameNameItem = m.canvas.create_text(10,5,anchor=NW,text=m.board.gamename) m.analysisItem = m.canvas.create_text(10,5 + m.infoCursorInc,anchor=NW,text=str) def NotifyFinish(m, endstr, winner): m.UpdateAnalysisString(endstr) if m.finishDialogActive: # only once per match m.finishDialogActive = 0 import tkMessageBox if winner == 0: tkMessageBox.showinfo(title="pythonchess", message=endstr + "\n\nThank you for the tight and interesting game.") elif winner == m.lastUserMove: tkMessageBox.showinfo(title="pythonchess", message=endstr + "\n\nCongratulations on winning!") else: tkMessageBox.showinfo(title="pythonchess", message=endstr + "\n\nThank you for letting me win this time.") def UpdateAnalysis(m, bestval, history, evals): if history == [None]: if bestval == 0: reps = m.board.CountRepetitions() if (reps >= 3): m.NotifyFinish("1/2 - 1/2, three repetitions", 0) else: m.NotifyFinish("1/2 - 1/2, stalemate", 0) elif m.board.turn < 0: m.NotifyFinish("1 - 0, mate", 1) elif bestval < 0: m.NotifyFinish("0 - 1, mate", -1) return import time timenow = time.time() analysisSpeed = None if m.lastAnalysisTime: period = timenow - m.lastAnalysisTime if period < 0.01: period = 0.01 analysisSpeed = evals / period m.lastAnalysisTime = timenow str = ("%.2f:" % (bestval * m.board.turn)) for move in history[:-1]: str = str + " " + repr(move) if analysisSpeed: str = str + " %.1f nodes/s" % analysisSpeed m.UpdateAnalysisString(str) def UpdateInfo(m): c = m.canvasInfo c.delete("all") # c.create_text(70, m.infoMarginY + 3.75 * m.infoCursorInc, # text=m.board.gamename,anchor=CENTER) for (col, xoff, anc) in ((1, 70, E), (-1, 90, W)): m.timeItems[col] = None c.create_text(m.infoMarginX + xoff, m.infoMarginY, text=(None, "you", m.opponentName)[col * m.userside],anchor=anc) c.create_text(m.infoMarginX + xoff, m.infoMarginY+m.infoCursorInc, text=(None, "white", "black")[col],anchor=anc) cursorpos = 90 movesToShow = 2 * (440 - cursorpos) / m.infoCursorInc startMove = max(m.board.moveCount - movesToShow, 0) startMove = (startMove & ~1) | (col == -1) for moveIx in range(startMove, m.board.moveCount, 2): movestr = repr(m.board.moveHistory[moveIx]) if moveIx > 1 and moveIx == startMove: movestr = "..." c.create_text(m.infoMarginX + xoff, cursorpos, text=movestr,anchor=anc) cursorpos = cursorpos + m.infoCursorInc m.UpdateTime() def UpdateTime(m): import time turn = m.realTurn if m.lastMoveTime == None: m.time = [0, 300.0, 300.0] else: m.time[turn] = m.timeLeftBeforeLastMove - (time.time() - m.lastMoveTime) if (m.time[turn] < 0): m.time[turn] = 0 for (col, xoff, anc) in ((1, 70, E), (-1, 90, W)): c = m.canvasInfo if m.timeItems[col]: c.delete(m.timeItems[col]) m.timeItems[col] = c.create_text(m.infoMarginX + xoff, m.infoMarginY + 2 * m.infoCursorInc,text=formatTime(m.time[col]),anchor=anc) def MoveUI(m, move): print "MOVE COUNT", m.board.moveCount # dragged piece is captured (by the computer), better to delete it if hasattr(move, "toIx"): if (move.toIx == m.mouseDownIndex or # better to delete the dragged piece also if the computer # captures enpassant (move.mode == move.capture and m.board.board[move.toIx] == 0)): m.canvas.delete(m.mouseDownItem) m.mouseDownIndex = None m.mouseDownItem = None if (m.replayboard and len(m.replayboard.moveHistory) > m.board.moveCount and move != m.replayboard.moveHistory[m.board.moveCount]): m.replayboard = None m.testreplaypossibilities() import time m.UpdateTime() m.lastMoveTime = time.time() if 1 or m.time[m.board.turn] > 0: legalmoves = GenerateAllLegalMoves(m.board) moveok = 0 for itermove in legalmoves: if repr(move) == repr(itermove): moveok = 1 if not moveok: print "move", move, "not in the list of legal moves" return m.board.Move(move) m.board.CalculateGameName() GenerateAllLegalMoves(m.board) m.realTurn = m.board.turn m.timeLeftBeforeLastMove = m.time[m.board.turn] m.UpdateInfo() else: print "no time left" def IndexToGraphicsPosition(m, ix): x = ix & 7 y = ix / 8 if not m.whiteDown: x = 7 - x y = 7 - y posy = (7 - y + 1.5) * m.ysize posx = (x + 1.5) * m.xsize return (posx, posy) def CopyBoardToWindowGraphic(m): for (piece,pos) in m.piecesOnCanvas: if piece == m.slowmoveobject or piece == m.mouseDownItem: continue # don't delete the moved object m.canvas.delete(piece) m.piecesOnCanvas = [] for y in range(8): for x in range(8): ix = y * 8 + x if ix == m.slowmovetoix or ix == m.mouseDownIndex: # piece is being moved, don't draw it in its final position yet continue (posx, posy) = m.IndexToGraphicsPosition(ix) ima = m.canvas.create_image(posx, posy) m.canvas.itemconfigure(ima, image=photoimages[m.board.board[ix]]) m.piecesOnCanvas.append((ima,ix)) def mouseDown(m, event): m.mouseDownX = event.x m.mouseDownY = event.y m.mouseDownXcell = event.x / m.xsize - 1 m.mouseDownYcell = 7 - (event.y / m.ysize - 1) if not m.whiteDown: m.mouseDownXcell = 7 - m.mouseDownXcell m.mouseDownYcell = 7 - m.mouseDownYcell if (m.mouseDownXcell in range(8) and m.mouseDownYcell in range(8)): ix = m.mouseDownYcell * 8 + m.mouseDownXcell m.mouseDownIndex = ix m.mouseDownPiece = m.board.board[ix] m.mouseDownItem = None for (item, pos) in m.piecesOnCanvas: if pos == ix: m.mouseDownItem = item m.canvas.tkraise(m.mouseDownItem) break def load(m, file): m.board.Setup() fd = open(file, 'r') lines = fd.readlines() for ix in range(len(lines)): line = lines[ix][:-1] move = StringToMove(m.board, line) if move: print "moving", move m.MoveUI(move) GenerateAllLegalMoves(m.board) else: print "problem on line", ix, line m.CopyBoardToWindowGraphic() m.PrepareForNextMoveWhenOpponentThinks() def save(m, file): fd = open(file, "w") for move in m.board.moveHistory: print "writing", move fd.write(repr(move) + '\n') m.CopyBoardToWindowGraphic() def cyclePersonalities(m, plys): matchname = "Ken" # use Ken for high plys keys = chesspersonalities.persons.keys() curix = keys.index(m.opponentName) keys = keys[curix+1:] + keys[:curix+1] for key in keys: person = chesspersonalities.persons[key] if person.plys == plys: matchname = key match = person break m.SetOpponentName(matchname) def filenew(m): m.filename = None m.replaystart() m.board.personality.Randomize() m.board.CalculateGameName() m.UpdateInfo() m.PrepareForNextMoveWhenOpponentThinks() def fileopen(m): import tkFileDialog m.filename = tkFileDialog.askopenfilename() if (m.filename and len(m.filename)): m.load(m.filename) m.moveNow = 1 m.stopRequest = 1 m.PrepareForNextMoveWhenOpponentThinks() def filesave(m): if m.filename: m.save(m.filename) else: m.filesaveas() def filesaveas(m): import tkFileDialog m.filename = tkFileDialog.asksaveasfilename() m.save(m.filename) def fileexit(m): m.stopRequest = 1 print "Exiting..." m.root.destroy() m.root = None m.moveNow = 1 def keyPress(m, event): if event.keysym == 'Control_L' or event.keysym == 'Control_R': m.controlDown = 1 def keyRelease(m, event): if event.keysym == 'Control_L' or event.keysym == 'Control_R': m.controlDown = 0 from string import upper key = upper(event.keysym) state = event.state if len(key) == 1 and key in "QRBN": m.defaultPromotion.set(key) if key == "SPACE": m.moveNow = 1 m.stopRequest = 0 if key == "A": m.automove = not m.automove if len(key) == 1 and key in "0123456789": depth = "0123456789".index(key) m.cyclePersonalities(depth) if key == 'P': print GenerateAllLegalMoves(m.board) PrintBoard(m.board) PrintBoardDebug(m.board) e = Eval(m.board) print "Eval xuup:::", e if key == 'T': m.replaynext() if key == 'HOME': m.replaystart() if key == 'BACKSPACE' or key == 'LEFT': m.replayprevious() if key == 'RIGHT': m.replaynext() if m.controlDown: if key == 'Z': m.replayprevious() if key == 'Y': m.replaynext() # print event, dir(event), # for i in dir(event): # print i, getattr(event, i) def after1000(m): m.nodesEvaluated = 0 m.UpdateTime() if m.playboard != None: m.UpdateProgress(m.playboard.progressEstimate) # else: # m.UpdateProgress(None) if m.root: m.canvas.after(1000, m.after1000) def enablereplayboard(m): if not m.replayboard or m.replayboard.moveCount <= m.board.moveCount: import copy m.replayboard = copy.deepcopy(m.board) return replayboardinvalid = 0 for ix in range(len(m.board.moveHistory)): if repr(m.board.moveHistory[ix]) != repr(m.replayboard.moveHistory[ix]): replayboardinvalid = 1 if replayboardinvalid: import copy m.replayboard = copy.deepcopy(m.board) def testreplaypossibilities(m): # disable/enable replay menus pass def cleanup(m): if m.slowmoveobject: # previous slow move is still going on, stop it m.canvas.delete(m.slowmoveobject) if m.mouseDownItem: m.canvas.delete(m.mouseDownItem) m.mouseDownIndex = None m.mouseDownItem = None def replaystart(m): m.finishDialogActive = 1 m.cleanup() m.lastMoveTime = None m.enablereplayboard() m.board.Setup() m.CopyBoardToWindowGraphic() m.UpdateInfo() m.testreplaypossibilities() m.PrepareForNextMoveWhenOpponentThinks() def replayprevious(m): if m.board.moveCount >= 1: m.cleanup() m.enablereplayboard() m.board.RetractMove() m.moveNow = 1 # m.opponentMoveReady = 1 m.stopRequest = 1 m.userside = -m.userside m.CopyBoardToWindowGraphic() m.UpdateInfo() m.testreplaypossibilities() m.PrepareForNextMoveWhenOpponentThinks() else: m.lastMoveTime = None def replaynext(m): m.cleanup() mc = m.board.moveCount print "replayhisto", m.replayboard.moveHistory print "trying", mc moves = GenerateAllLegalMoves(m.board) print "trying", mc move = m.replayboard.moveHistory[mc] m.MoveUI(m.replayboard.moveHistory[mc]) moves = GenerateAllLegalMoves(m.board) m.CopyBoardToWindowGraphic() m.UpdateInfo() m.testreplaypossibilities() def mouseMove(m, event): if m.mouseDownItem: dx = event.x - m.mouseDownX dy = event.y - m.mouseDownY (posx, posy) = m.IndexToGraphicsPosition(m.mouseDownIndex) m.canvas.coords(m.mouseDownItem, posx + dx, posy + dy) m.canvas.tkraise(m.mouseDownItem) def mouseUp(m, event): promotionLookup = {'Q': queen, 'R': rook, 'B': bishop, 'N': knight} if m.mouseDownItem: dx = event.x - m.mouseDownX dy = event.y - m.mouseDownY (posx, posy) = m.IndexToGraphicsPosition(m.mouseDownIndex) m.canvas.coords(m.mouseDownItem, posx + dx, posy + dy) m.mouseUpXcell = int(posx + dx) / m.xsize - 1 m.mouseUpYcell = 7 - (int(posy + dy) / m.ysize - 1) if not m.whiteDown: m.mouseUpXcell = 7 - m.mouseUpXcell m.mouseUpYcell = 7 - m.mouseUpYcell toix = m.mouseUpYcell * 8 + m.mouseUpXcell allmoves = GenerateAllLegalMoves(m.board) for move in allmoves: if (move.fromIx == m.mouseDownIndex and move.toIx == toix): if (not move.promotion or abs(move.promotion) == promotionLookup[m.defaultPromotion.get()]): m.userside = m.board.turn m.MoveUI(move) m.opponentMoveReady = 1 print "the user is moving", move break m.canvas.delete(m.mouseDownItem) m.mouseDownItem = None m.mouseDownIndex = None m.CopyBoardToWindowGraphic() def updateDuringThinking(m): m.canvas.update() if m.moveNow == 1: return 1 return 0 def updateDuringPondering(m): if m.root: m.canvas.update() if m.moveNow or m.opponentMoveReady == 1: return 1 return 0 def updateDuringKillerMoves(m): if m.root: m.canvas.update() return 0 def AutoMove(m): if m.stopRequest: print "Stop request issued. No need to move." return # m.hashTable.Init() # testing only import time starttime = time.time() import copy m.lastUserMove = -m.board.turn moves = GenerateAllLegalMoves(m.board) # PrintBoardDebug(m.board) sortedmoves = OrderSimple(m.board, moves) # print "sorted", sortedmoves anotherboard = copy.deepcopy(m.board) anotherboard.pondering = 0 m.playboard = anotherboard # PrepareKillerMovesForMoveSorting(anotherboard, m.updateDuringThinking) prev = (None,None,[None]) if len(sortedmoves): prev = (0.0,sortedmoves[0],["(0-ply move)"]) (bestval, bestmove, history) = prev movelist = [] # calculate progress range progressRanges = [] progressRangesTotal = 0 # for depth in range(1, m.searchDepth + 1): searchStep = 1.0 for depth in floatrange(1, m.searchDepth, searchStep): # estimate a branching factor branching = 9.0 a = branching ** depth progressRanges.append([progressRangesTotal, progressRangesTotal + a]) progressRangesTotal = progressRangesTotal + a for ix in range(len(progressRanges)): progressRanges[ix][0] = progressRanges[ix][0] / progressRangesTotal progressRanges[ix][1] = progressRanges[ix][1] / progressRangesTotal ix = -1 for depth in floatrange(1, m.searchDepth, searchStep): ix = ix + 1 # print "progu", progressRanges[ix] m.progressRange = progressRanges[ix] m.hashTable.evals = 0 m.hashTable.collisions = 0 m.hashTable.hashCount = 0 (bestval, bestmove, history) = ABnegaSearchZW(anotherboard, m.hashTable, -1e10, +1e10, movelist, depth, m.updateDuringThinking) RecordKillerMovesForMoveSorting(anotherboard, movelist) if bestmove: prev = (bestval, bestmove, history) print "DBEST", depth, bestmove, "(", bestval, "-", history, '( eval=', m.hashTable.evals, 'hc=', m.hashTable.hashCount, 'col=', m.hashTable.collisions, "))" m.UpdateAnalysis(bestval, history, m.hashTable.evals + m.hashTable.hashCount) if (history == [None]): m.moveNow = 0 m.stopRequest = 0 m.progressEstimate = None m.playboard = None m.UpdateProgress(None) return # game is finished if m.moveNow: break m.UpdateProgress(1.0) m.playboard = None m.progressEstimate = None m.moveNow = 0 if not bestmove: (bestval, bestmove, history) = prev print "stop", m.stopRequest if bestmove and m.stopRequest == 0: print "BEST", bestmove, "(", bestval, "-", history, "(", m.hashTable.evals, '/', m.hashTable.hashCount, "))" m.UpdateAnalysis(bestval, history, m.hashTable.evals + m.hashTable.hashCount) m.MoveUI(bestmove) # print bestmove # PrintBoard(m.board) # PrintBoardDebug(m.board) # print "after", GenerateAllLegalMoves(m.board) if hasattr(bestmove, "fromIx") and hasattr(bestmove, "toIx"): m.SlowMoveStart(bestmove.fromIx, bestmove.toIx) else: m.CopyBoardToWindowGraphic() m.UpdateProgress(None) m.stopRequest = 0 print "TIME: ", time.time() - starttime def PrepareForNextMoveWhenOpponentThinks(m): if m.insidePonderingLoop: return # this loop is already active m.insidePonderingLoop = 1 while 1: m.opponentMoveReady = 0 # this seems like a complete waste of time, but it will # fill the hash table with useful data # the evaluation function is the costly thing, not the search, # and this will calculate the evaluation when the opponent thinks print "prior", GenerateAllLegalMoves(m.board) import copy m.hashTable.evals = 0 m.hashTable.hashCount = 0 movelist = [] anotherboard = copy.deepcopy(m.board) # PrepareKillerMovesForMoveSorting(anotherboard, m.updateDuringKillerMoves) anotherboard.pondering = 1 while 1: for idepth in range(1, 10): depth = min(idepth, m.searchDepth + 1) evalDepth = max(idepth - (m.searchDepth + 1), 0) # depth is limited to avoid excessive memory consumption print "pondering loop", depth, evalDepth anotherboard.evalDepth = evalDepth (bestval, bestmove, history) = ABnegaSearchZW(anotherboard, m.hashTable, -1e10, +1e10, movelist, depth, m.updateDuringPondering) RecordKillerMovesForMoveSorting(anotherboard, movelist) anotherboard.evalDepth = 0 if bestmove: print bestmove, "(", bestval, "-", history, "(", m.hashTable.evals, '/', m.hashTable.hashCount, "))" m.UpdateAnalysis(bestval, history, m.hashTable.evals + m.hashTable.hashCount) # if (bestmove == "mate"): if (history == [None]): # the game has ended from one reason or the other, # so it is a good idea to stop pondering m.insidePonderingLoop = 0 return if not m.root: print "exiting 2" import sys sys.exit() if m.moveNow or m.opponentMoveReady: break if m.moveNow or m.opponentMoveReady: break evalDepth = evalDepth + 1 if m.moveNow: m.moveNow = 0 m.opponentMoveReady = 0 m.AutoMove() else: m.moveNow = 0 m.opponentMoveReady = 0 m.stopRequest = 0 if m.automove: m.CopyBoardToWindowGraphic() m.canvas.update() m.AutoMove() boardui = BoardUI() boardui.build() boardui.canvas.mainloop()