diff --git a/Code/Configuracion.py b/Code/Configuracion.py index 07e51ba..ed4f836 100644 --- a/Code/Configuracion.py +++ b/Code/Configuracion.py @@ -135,7 +135,7 @@ def __init__(self, user): self.rivalInicial = "tarrasch" self.rival = self.buscaRival(self.rivalInicial) - self.tutorInicial = "deepfish" + self.tutorInicial = "mcbrain" self.tutor = self.buscaRival(self.tutorInicial) self.tutorMultiPV = 10 # 0: maximo self.tutorDifPts = 0 diff --git a/Code/DBgames.py b/Code/DBgames.py index 2c1e446..d275713 100644 --- a/Code/DBgames.py +++ b/Code/DBgames.py @@ -431,6 +431,9 @@ def __init__(self, nomFichero): self.rowidReader = Util.RowidReader(self.nomFichero, self.tabla) + def reset_cache(self): + self.cache = {} + def addcache(self, rowid, reg): if len(self.cache) > self.maxcache: keys = self.cache.keys() @@ -879,9 +882,18 @@ def leeAllRecno(self, recno): self._cursor.execute("SELECT %s FROM %s WHERE rowid =%d" % (select, self.tabla, rowid)) return self._cursor.fetchone() + def leeRegAllRecno(self, recno): + raw = self.leeAllRecno(recno) + alm = Util.Almacen() + for campo in self.liCamposAll: + setattr(alm, campo, raw[campo]) + return alm, raw + def leePartidaRecno(self, recno): raw = self.leeAllRecno(recno) + return self.leePartidaRaw(raw) + def leePartidaRaw(self, raw): p = Partida.PartidaCompleta() xpgn = raw["PGN"] rtags = None @@ -1005,6 +1017,8 @@ def modifica(self, recno, partidaCompleta): self.dbSTAT.append(pvAnt, resAnt, -1) self.dbSTAT.append(pvNue, resNue, +1) + del self.cache[rowid] + return True def inserta(self, partidaCompleta): @@ -1038,3 +1052,74 @@ def inserta(self, partidaCompleta): def guardaPartidaRecno(self, recno, partidaCompleta): return self.inserta(partidaCompleta) if recno is None else self.modifica(recno, partidaCompleta) + + def massive_change_tags(self, li_tags_change, liRegistros, remove, overwrite): + dtag = Util.SymbolDict({tag:val for tag, val in li_tags_change}) + + def work_tag(tag, alm): + if tag in dtag: + ant = getattr(alm, tag.upper()) + if (ant and overwrite) or not ant: + setattr(alm, tag.upper(), dtag[tag]) + + if remove: + remove = remove.upper() + + for recno in liRegistros: + alm, raw = self.leeRegAllRecno(recno) + + work_tag("Event", alm) + work_tag("Site", alm) + work_tag("Date", alm) + + p = self.leePartidaRaw(raw) + if remove: + for n, (tag, val) in enumerate(p.liTags): + if tag.upper() == remove: + del p.liTags[n] + break + setattr(alm, remove, "") + + st_tag_ant_upper = set() + for n, (tag, val) in enumerate(p.liTags): + if overwrite: + if tag in dtag: + p.liTags[n] = [tag, dtag[tag]] + st_tag_ant_upper.add(tag.upper()) + setattr(alm, tag.upper(), p.liTags[n][1]) + + for tag_new in dtag: + if tag_new.upper() not in st_tag_ant_upper: + p.liTags.append([tag_new, dtag[tag_new]]) + setattr(alm, tag_new.upper(), dtag[tag_new]) + + rowid = self.liRowids[recno] + pgn = {"FULLGAME": p.save()} + xpgn = Util.var2blob(pgn) + sql = "UPDATE GAMES SET EVENT=?, SITE=?, DATE=?, WHITE=?, BLACK=?, RESULT=?, " \ + "ECO=?, WHITEELO=?, BLACKELO=?, PGN=? WHERE ROWID = %d" % rowid + self._cursor.execute(sql, (alm.EVENT, alm.SITE, alm.DATE, alm.WHITE, alm.BLACK, alm.RESULT, + alm.ECO, alm.WHITEELO, alm.BLACKELO, xpgn)) + + self._conexion.commit() + + self.reset_cache() + + def insert_pks(self, path_pks): + f = open(path_pks, "rb") + txt = f.read() + f.close() + dic = Util.txt2dic(txt) + fen = dic.get("FEN") + if fen: + return _("This pks file is not a complete game") + + liTags = dic.get("liPGN", []) + + partidaCompleta = Partida.PartidaCompleta(liTags=liTags) + partidaCompleta.recuperaDeTexto(dic["PARTIDA"]) + + if not self.inserta(partidaCompleta): + return _("This game already exists.") + + return None diff --git a/Code/DBgamesFEN.py b/Code/DBgamesFEN.py index 20c2b9a..45c14a9 100644 --- a/Code/DBgamesFEN.py +++ b/Code/DBgamesFEN.py @@ -45,7 +45,6 @@ def __init__(self, nomFichero): self._conexion.text_factory = lambda x: unicode(x, "utf-8", "ignore") self._conexion.row_factory = sqlite3.Row self._cursor = self._conexion.cursor() - self.tabla = "games" self.select = ",".join(self.liCamposRead) self.order = None self.filter = None @@ -62,17 +61,17 @@ def __init__(self, nomFichero): atexit.register(self.close) - self.rowidReader = Util.RowidReader(self.nomFichero, self.tabla) + def reset_cache(self): + self.cache = {} def controlInicial(self): cursor = self._conexion.cursor() - cursor.execute("pragma table_info(%s)" % self.tabla) + cursor.execute("pragma table_info(GAMES)") liCampos = cursor.fetchall() cursor.close() if not liCampos: - sql = "CREATE TABLE %s (" % self.tabla - sql += "FEN VARCHAR NOT NULL PRIMARY KEY," + sql = "CREATE TABLE GAMES (FEN VARCHAR NOT NULL PRIMARY KEY," for field in self.liCamposBase: if field != "FEN": sql += "%s VARCHAR," % field @@ -85,21 +84,26 @@ def controlInicial(self): cursor.execute(sql) cursor.close() + def lee_rowids(self): + sql = "SELECT ROWID FROM GAMES" + if self.filter: + sql += " WHERE %s" % self.filter + if self.order: + sql += " ORDER BY %s" % self.order + self._cursor.execute(sql) + self.liRowids = [ row[0] for row in self._cursor.fetchall()] + def close(self): if self._conexion: self._cursor.close() self._conexion.close() self._conexion = None - if self.rowidReader: - self.rowidReader.stopnow() - self.rowidReader = None def ponOrden(self, liOrden): li = ["%s %s" % (campo, tipo) for campo, tipo in liOrden] self.order = ",".join(li) - self.liRowids = [] - self.rowidReader.run(self.liRowids, self.filter, self.order) self.liOrden = liOrden + self.lee_rowids() def dameOrden(self): return self.liOrden @@ -117,44 +121,23 @@ def addcache(self, rowid, reg): def field(self, nfila, name): rowid = self.liRowids[nfila] if rowid not in self.cache: - self._cursor.execute("SELECT %s FROM %s WHERE rowid =%d" % (self.select, self.tabla, rowid)) + self._cursor.execute("SELECT %s FROM GAMES WHERE rowid =%d" % (self.select, rowid)) reg = self._cursor.fetchone() self.addcache(rowid, reg) return self.cache[rowid][name] - def siFaltanRegistrosPorLeer(self): - if not self.rowidReader: - return False - return not self.rowidReader.terminado() - def reccount(self): - if not self.rowidReader: - return 0 - n = self.rowidReader.reccount() - # Si es cero y no ha terminado de leer, se le da tiempo para que devuelva algo - while n == 0 and not self.rowidReader.terminado(): - time.sleep(0.05) - n = self.rowidReader.reccount() - return n + return len(self.liRowids) def setFilter(self, condicion): self.filter = condicion - - self.liRowids = [] - self.rowidReader.run(self.liRowids, condicion, self.order) + self.lee_rowids() def dameFEN_PV(self, fila): xpv = self.field(fila, "XPV") fen = self.field(fila, "FEN") return fen, xpv2pv(xpv) - def all_reccount(self): - self.liRowids = [] - self.rowidReader.run(self.liRowids, None, None) - while not self.rowidReader.terminado(): - time.sleep(0.1) - return self.reccount() - def rotulo(self): rotulo = os.path.basename(self.nomFichero)[:-4] if rotulo == "Positions Database": @@ -172,17 +155,17 @@ def leerPGN(self, fichero, dlTmp): sicodec = codec not in ("utf-8", "ascii") liRegs = [] - stRegs = set() nRegs = 0 conexion = self._conexion cursor = self._cursor - self.liCamposBase = ["FEN", "EVENT", "SITE", "DATE", "WHITE", "BLACK", "RESULT", "PLIES"] - self.liCamposWork = ["XPV", ] - self.liCamposBLOB = ["PGN", ] + cursor.execute("SELECT FEN FROM GAMES") + liRows = cursor.fetchall() + stRegs = set(row[0] for row in liRows) + - sql = "insert into games (FEN,EVENT,SITE,DATE,WHITE,BLACK,RESULT,XPV,PGN,PLIES) values (?,?,?,?,?,?,?,?,?,?);" + sql = "insert into GAMES (FEN,EVENT,SITE,DATE,WHITE,BLACK,RESULT,XPV,PGN,PLIES) values (?,?,?,?,?,?,?,?,?,?);" liCabs = self.liCamposBase[:-1] # all except PLIES PGN, TAGS liCabs.append("PLYCOUNT") @@ -195,7 +178,7 @@ def leerPGN(self, fichero, dlTmp): if fen in stRegs: dup = True else: - cursor.execute("SELECT COUNT(*) FROM games WHERE FEN = ?", (fen,)) + cursor.execute("SELECT COUNT(*) FROM GAMES WHERE FEN = ?", (fen,)) num = cursor.fetchone()[0] dup = num > 0 if dup: @@ -246,27 +229,38 @@ def leerPGN(self, fichero, dlTmp): conexion.commit() dlTmp.ponContinuar() + self.lee_rowids() def leeAllRecno(self, recno): rowid = self.liRowids[recno] select = ",".join(self.liCamposAll) - self._cursor.execute("SELECT %s FROM %s WHERE rowid =%d" % (select, self.tabla, rowid)) + self._cursor.execute("SELECT %s FROM GAMES WHERE rowid =%d" % (select, rowid)) return self._cursor.fetchone() + def leeRegAllRecno(self, recno): + raw = self.leeAllRecno(recno) + alm = Util.Almacen() + for campo in self.liCamposAll: + setattr(alm, campo, raw[campo]) + return alm, raw + def leePartidaRecno(self, recno): raw = self.leeAllRecno(recno) + return self.leePartidaRaw(raw) + def leePartidaRaw(self, raw): p = Partida.PartidaCompleta(fen=raw["FEN"]) xpgn = raw["PGN"] - rtags = None if xpgn: xpgn = Util.blob2var(xpgn) - if type(xpgn) == str: # Version -9 + if type(xpgn) in (str, unicode): # Version -9 p.readPGN(VarGen.configuracion, xpgn) return p p.restore(xpgn["FULLGAME"]) return p + rtags = None + p.leerPV(xpv2pv(raw["XPV"])) rots = ["Event", "Site", "Date", "Round", "White", "Black", "Result", "WhiteTitle", "BlackTitle", "WhiteElo", "BlackElo", "WhiteUSCF", "BlackUSCF", "WhiteNA", "BlackNA", @@ -287,9 +281,187 @@ def leePartidaRecno(self, recno): return p def borrarLista(self, lista): - cSQL = "DELETE FROM %s WHERE rowid = ?" % self.tabla + cSQL = "DELETE FROM GAMES WHERE rowid = ?" lista.sort(reverse=True) for recno in lista: self._cursor.execute(cSQL,(self.liRowids[recno],)) del self.liRowids[recno] self._conexion.commit() + + def modifica(self, recno, partidaCompleta): + reg_ant = self.leeAllRecno(recno) + + pgn = {} + pgn["FULLGAME"] = partidaCompleta.save() + xpgn = Util.var2blob(pgn) + + dTags = {} + for key, value in partidaCompleta.liTags: + dTags[key.upper()] = value + dTags["PLIES"] = partidaCompleta.numJugadas() + + liFields = [] + liData = [] + for field in self.liCamposBase: + if reg_ant[field] != dTags.get(field): + liFields.append("%s=?"%field) + liData.append(dTags.get(field)) + if xpgn != reg_ant["PGN"]: + liFields.append("PGN=?") + liData.append(xpgn) + + pvNue = partidaCompleta.pv() + xpv = pv2xpv(pvNue) + liFields.append("XPV=?") + liData.append(xpv) + + rowid = self.liRowids[recno] + if len(liFields) == 0: + return True + fields = ",".join(liFields) + sql = "UPDATE GAMES SET %s WHERE ROWID = %d" % (fields, rowid) + self._cursor.execute(sql, liData) + self._conexion.commit() + + del self.cache[rowid] + + return True + + def si_existe_fen(self, fen): + self._cursor.execute("SELECT COUNT(*) FROM GAMES WHERE FEN = ?", (fen,)) + num = self._cursor.fetchone()[0] + return num + + def inserta(self, partidaCompleta): + pgn = {} + pgn["FULLGAME"] = partidaCompleta.save() + xpgn = Util.var2blob(pgn) + + dTags = {} + for key, value in partidaCompleta.liTags: + dTags[key.upper()] = value + dTags["PLIES"] = partidaCompleta.numJugadas() + + pv = partidaCompleta.pv() + xpv = pv2xpv(pv) + data = [xpv, xpgn] + for field in self.liCamposBase: + data.append(dTags.get(field, None)) + + sql = "insert into GAMES (XPV,PGN,FEN,EVENT,SITE,DATE,WHITE,BLACK,RESULT,PLIES)" \ + " values (?,?,?,?,?,?,?,?,?,?);" + self._cursor.execute(sql, data) + lastrowid = self._cursor.lastrowid + self._conexion.commit() + + self.liRowids.append(lastrowid) + + return True + + def guardaPartidaRecno(self, recno, partidaCompleta): + return self.inserta(partidaCompleta) if recno is None else self.modifica(recno, partidaCompleta) + + def appendDB(self, db, liRecnos, dlTmp): + duplicados = importados = 0 + + conexion = self._conexion + cursor = self._cursor + + sql = "insert into GAMES (FEN,EVENT,SITE,DATE,WHITE,BLACK,XPV,PGN,PLIES) values (?,?,?,?,?,?,?,?,?);" + + for recno in liRecnos: + raw = db.leeAllRecno(recno) + + fen = raw["FEN"] + cursor.execute("SELECT COUNT(*) FROM GAMES WHERE FEN = ?", (fen,)) + num = cursor.fetchone()[0] + dup = num > 0 + if dup: + duplicados += 1 + else: + reg = (fen, raw["EVENT"], raw["SITE"], raw["DATE"], raw["WHITE"], raw["BLACK"], raw["XPV"], raw["PGN"], + raw["PLIES"]) + importados += 1 + cursor.execute(sql, reg) + + if not dlTmp.actualiza(duplicados + importados, duplicados, importados): + break + + dlTmp.actualiza(duplicados + importados, duplicados, importados) + dlTmp.ponSaving() + + conexion.commit() + + dlTmp.ponContinuar() + self.lee_rowids() + + def massive_change_tags(self, li_tags_change, liRegistros, remove, overwrite): + dtag = Util.SymbolDict({tag:val for tag, val in li_tags_change}) + + def work_tag(tag, alm): + if tag in dtag: + ant = getattr(alm, tag.upper()) + if (ant and overwrite) or not ant: + setattr(alm, tag.upper(), dtag[tag]) + + if remove: + remove = remove.upper() + + for recno in liRegistros: + alm, raw = self.leeRegAllRecno(recno) + + work_tag("Event", alm) + work_tag("Site", alm) + work_tag("Date", alm) + + p = self.leePartidaRaw(raw) + if remove: + for n, (tag, val) in enumerate(p.liTags): + if tag.upper() == remove: + del p.liTags[n] + break + setattr(alm, remove, "") + + st_tag_ant_upper = set() + for n, (tag, val) in enumerate(p.liTags): + if overwrite: + if tag in dtag: + p.liTags[n] = [tag, dtag[tag]] + st_tag_ant_upper.add(tag.upper()) + setattr(alm, tag.upper(), p.liTags[n][1]) + + for tag_new in dtag: + if tag_new.upper() not in st_tag_ant_upper: + p.liTags.append([tag_new, dtag[tag_new]]) + setattr(alm, tag_new.upper(), dtag[tag_new]) + + rowid = self.liRowids[recno] + pgn = {"FULLGAME": p.save()} + xpgn = Util.var2blob(pgn) + sql = "UPDATE GAMES SET EVENT=?, SITE=?, DATE=?, WHITE=?, BLACK=?, PGN=?, RESULT=? WHERE ROWID = %d" % rowid + self._cursor.execute(sql, (alm.EVENT, alm.SITE, alm.DATE, alm.WHITE, alm.BLACK, xpgn, alm.RESULT)) + + self._conexion.commit() + + self.reset_cache() + + def insert_pks(self, path_pks): + f = open(path_pks, "rb") + txt = f.read() + f.close() + dic = Util.txt2dic(txt) + fen = dic.get("FEN") + if not fen: + return _("This pks file is a complete game") + + if self.si_existe_fen(fen): + return _("This position already exists.") + + liTags = dic.get("liPGN", []) + + partidaCompleta = Partida.PartidaCompleta(fen=fen, liTags=liTags) + partidaCompleta.recuperaDeTexto(dic["PARTIDA"]) + + self.inserta(partidaCompleta) + + return None diff --git a/Code/DGT.py b/Code/DGT.py index 13779c2..6d26b23 100644 --- a/Code/DGT.py +++ b/Code/DGT.py @@ -68,9 +68,8 @@ def log(cad): log.write(".6.[%s]\n%s\n" % (Util.hoy(), cad)) log.close() -# CALLBACKS - +# CALLBACKS def registerStatusFunc(dato): envia("status", dato) return 1 @@ -88,9 +87,8 @@ def registerWhiteMoveInputFunc(dato): def registerBlackMoveInputFunc(dato): return envia("blackMove", _dgt2pv(dato)) -# Activar/desactivar/reactivar - +# Activar/desactivar/reactivar def activar(): dgt = None for path in ("", @@ -191,9 +189,8 @@ def writeClocks(wclock, bclock): dgt = VarGen.dgt dgt._DGTDLL_SetNRun(wclock, bclock, 0) -# Utilidades para la trasferencia de datos - +# Utilidades para la trasferencia de datos def _dgt2fen(dato): n = 0 ndato = len(dato) @@ -239,9 +236,8 @@ def _dgt2pv(dato): return dato[1:3] + dato[4:6] -# Lo mismo, de otra forma - +# Lo mismo, de otra forma def xdgt2fen(xdgt): liD = xdgt.split(" ") dgt = liD[0] diff --git a/Code/EnginesWindows.py b/Code/EnginesWindows.py index 1502184..7eb9abf 100644 --- a/Code/EnginesWindows.py +++ b/Code/EnginesWindows.py @@ -240,14 +240,14 @@ def mas(cm): cm.ponMultiPV(20, 500) mas(cm) - cm = ConfigMotor("deepfish", "Tord Romstad, Marco Costalba, Joona Kiiski, fork by Marco Zerbinati", "7 32bit", "https://github.com/Zerbinati/DeepFishMZ") - cm.path = "windows/DeepFishMZ 32.exe" - cm.path_64 = "windows/DeepFishMZ 64 BMI2.exe", "7 64bit bmi2" + cm = ConfigMotor("mcbrain", "Michael Byrne (based on stockfish)", "2.1a 32bit", "https://github.com/MichaelB7/Stockfish/releases") + cm.path = "McBrain_2017_v21a_x32_old.exe" + cm.path_64 = "McBrain_2017_v21a_x64_bmi2.exe", "2.1a 64bit bmi2" cm.elo = 3200 - cm.ordenUCI("Ponder", "false") + cm.ordenUCI("Tactical", "8") cm.ordenUCI("Hash", "64") cm.ordenUCI("Threads", "1") - cm.ponMultiPV(20, 500) + cm.ponMultiPV(20, 256) mas(cm) cm = ConfigMotor("gull", "Vadim Demichev", "3 32bit", "https://sourceforge.net/projects/gullchess/") @@ -258,7 +258,7 @@ def mas(cm): cm.ponMultiPV(20, 64) mas(cm) - cm = ConfigMotor("irina", "Lucas Monge", "0.15", "") + cm = ConfigMotor("irina", "Lucas Monge", "0.15", "https://github.com/lukasmonk/irina") cm.path = "irina.exe" cm.elo = 1200 mas(cm) @@ -279,6 +279,7 @@ def dicMotoresFixedElo(): ("rodent", 600, 2600), ("amyan", 1000, 2400), ("rhetoric", 1300, 2600), + ("mcbrain", 1200, 2800), ("cheng", 800, 2500), ("greko", 1600, 2400), ("hamsters", 1000, 2000), diff --git a/Code/Entrenamientos.py b/Code/Entrenamientos.py index 40af08e..06bfd07 100644 --- a/Code/Entrenamientos.py +++ b/Code/Entrenamientos.py @@ -639,7 +639,6 @@ def turn_on_lights(self, name): title = _("Uwe Auerswald") folder = "Trainings/Tactics by Uwe Auerswald" icono = Iconos.Uwe() - li_tam_blocks = (6, 10, 15, 30, 45, 90) li_tam_blocks = (5, 10, 20, 40, 80) resp = PantallaTurnOnLights.pantallaTurnOnLigths(self.procesador, name, title, icono, folder, li_tam_blocks) diff --git a/Code/GestorEntMaq.py b/Code/GestorEntMaq.py index dab40dc..5da50f6 100644 --- a/Code/GestorEntMaq.py +++ b/Code/GestorEntMaq.py @@ -77,7 +77,7 @@ def inicio(self, dic, aplazamiento=None, siPrimeraJugadaHecha=False): self.book = dic.get("BOOK", None) elo = getattr(self.cm, "elo", 0) - self.maxMoveBook = elo / 200 if 0 <= elo <= 1700 else 9999 + self.maxMoveBook = elo / 200 if 0 < elo <= 1700 else 9999 if self.book: self.book.polyglot() self.bookRR = dic.get("BOOKRR", "mp") @@ -358,7 +358,7 @@ def reiniciar(self, siPregunta): if siPregunta: if not QTUtil2.pregunta(self.pantalla, _("Restart the game?")): return - + self.analizaTerminar() self.partida.reset() if self.siTiempo: self.pantalla.paraReloj() @@ -792,13 +792,14 @@ def mueveHumano(self, desde, hasta, coronacion=None): if num: rmTutor = self.mrmTutor.rmBest() menu = QTVarios.LCMenu(self.pantalla) - submenu = menu.submenu(_("There are %d best moves") % num, Iconos.Motor()) - submenu.opcion("tutor", "%s (%s)" % (_("Show tutor"), rmTutor.abrTextoBase()), + menu.opcion("None", _("There are %d best moves") % num, Iconos.Motor()) + menu.separador() + menu.opcion("tutor", "&1. %s (%s)" % (_("Show tutor"), rmTutor.abrTextoBase()), Iconos.Tutor()) - submenu.separador() - submenu.opcion("try", _("Try again"), Iconos.Atras()) - submenu.separador() - submenu.opcion("user", "%s (%s)" % (_("Select my move"), rmUser.abrTextoBase()), + menu.separador() + menu.opcion("try", "&2. %s" % _("Try again"), Iconos.Atras()) + menu.separador() + menu.opcion("user", "&3. %s (%s)" % (_("Select my move"), rmUser.abrTextoBase()), Iconos.Player()) self.pantalla.cursorFueraTablero() resp = menu.lanza() diff --git a/Code/GestorEverest.py b/Code/GestorEverest.py index af61104..bcee87f 100644 --- a/Code/GestorEverest.py +++ b/Code/GestorEverest.py @@ -126,7 +126,7 @@ def restart(self, lost_points): licoment.append(_("You have exceeded the limit of lost points.")) if change_game: - licoment.append(_("You have also exceeded the limit of tries, then you falls to the previous game.")) + licoment.append(_("You have exceeded the limit of tries, you will fall back to the previous.")) elif lost_points: licoment.append(_("You must repeat the game from beginning.")) if licoment: diff --git a/Code/GestorMateMap.py b/Code/GestorMateMap.py index f09479e..2d9d324 100644 --- a/Code/GestorMateMap.py +++ b/Code/GestorMateMap.py @@ -241,7 +241,7 @@ def ponResultado(self, quien): mensaje = _("Game ended") if quien == kGanamos: - mensaje = _("Congratulations you have win %s.") % self.workmap.nameAim() + mensaje = _("Congratulations you have won %s.") % self.workmap.nameAim() self.workmap.winAim(self.partida.pv()) QTUtil2.mensaje(self.pantalla, mensaje) diff --git a/Code/GestorPartida.py b/Code/GestorPartida.py index 77e17d1..556ab69 100644 --- a/Code/GestorPartida.py +++ b/Code/GestorPartida.py @@ -57,8 +57,8 @@ def inicio(self, partidaCompleta, siCompleta): self.siguienteJugada() - def reiniciar(self,): - if self.siCambios and not QTUtil2.pregunta(self.pantalla, _("You will lost all changes, are you sure?")): + def reiniciar(self): + if self.siCambios and not QTUtil2.pregunta(self.pantalla, _("You will loose all changes, are you sure?")): return p = Partida.PartidaCompleta() p.restore(self.reinicio) @@ -322,33 +322,11 @@ def configurarGS(self): self.tablero.rotaTablero() elif resp == "posicion": - resp = Voyager.voyagerFEN(self.pantalla, self.fen) - if resp is not None: - self.fen = resp - self.posicApertura = None - - if self.xpgn: - siInicio = self.fen == ControlPosicion.FEN_INICIAL - li = self.xpgn.split("\n") - lin = [] - siFen = False - for linea in li: - if linea.startswith("["): - if "FEN " in linea: - siFen = True - if siInicio: - continue - linea = '[FEN "%s"]' % self.fen - lin.append(linea) - else: - break - if not siFen: - linea = '[FEN "%s"]' % self.fen - lin.append(linea) - self.liPGN = lin - self.xpgn = "\n".join(lin) + "\n\n*" - - self.reiniciar() + ini_fen = self.partida.iniPosicion.fen() + cur_fen = Voyager.voyagerFEN(self.pantalla, ini_fen) + if cur_fen and cur_fen != ini_fen: + self.partida.resetFEN(cur_fen) + self.inicio(self.partida, self.siCompleta) elif resp == "pasteposicion": texto = QTUtil.traePortapapeles() diff --git a/Code/GestorSolo.py b/Code/GestorSolo.py index 72fa258..b14a7d7 100644 --- a/Code/GestorSolo.py +++ b/Code/GestorSolo.py @@ -201,7 +201,6 @@ def tituloVentanaPGN(self): return "%s-%s (%s, %s,%s)" % (white, black, event, date, result) def procesarAccion(self, clave): - if clave == k_mainmenu: self.finPartida() diff --git a/Code/Init.py b/Code/Init.py index e45e367..ff38ab1 100644 --- a/Code/Init.py +++ b/Code/Init.py @@ -1,5 +1,5 @@ DEBUG = False -VERSION = "10.11.9" +VERSION = "10.12" import os import sys diff --git a/Code/MotoresExternos.py b/Code/MotoresExternos.py index 2372251..8cfd135 100644 --- a/Code/MotoresExternos.py +++ b/Code/MotoresExternos.py @@ -23,7 +23,7 @@ def leeTXT(self, txt): self.max = int(li[5]) elif self.tipo == "check": - self.default = self.default == "true" + self.default = self.default.lower() == "true" self.valor = self.valor.lower() == "true" elif self.tipo == "button": diff --git a/Code/PGNreader.py b/Code/PGNreader.py index f1bcc98..cbacb41 100644 --- a/Code/PGNreader.py +++ b/Code/PGNreader.py @@ -315,6 +315,10 @@ def readGames(pgnfile): siCab = False siMov = True pgnMov = [linea, ] + else: + siCab = False + siMov = True + pgnMov = [] elif siMov: if linea: if linea[0] == '[' and linea.endswith("]"): diff --git a/Code/Partida.py b/Code/Partida.py index 3ca9cae..b1ca7d8 100644 --- a/Code/Partida.py +++ b/Code/Partida.py @@ -401,3 +401,14 @@ def pgn(self): txt += "\n%s"%self.pgnBase() return txt + def resetFEN(self, fen): + ok = False + for n, tag in enumerate(self.liTags): + if tag[0] == "FEN": + self.liTags[n] = ("FEN", fen) + ok = True + break + if not ok and fen != ControlPosicion.FEN_INICIAL: + self.liTags.append(("FEN", fen)) + Partida.resetFEN(self, fen) + diff --git a/Code/Procesador.py b/Code/Procesador.py index f574b1c..e061821 100644 --- a/Code/Procesador.py +++ b/Code/Procesador.py @@ -677,8 +677,8 @@ def tools(self): menu1 = menu.submenu(_("Database"), Iconos.Database()) menu1.opcion("database", _("Complete games"), Iconos.DatabaseC()) - # menu1.separador() - # menu1.opcion("databaseFEN", _("Positions"), Iconos.DatabaseF()) # TODO + menu1.separador() + menu1.opcion("databaseFEN", _("Positions"), Iconos.DatabaseF()) # TODO menu.separador() menu.opcion("manual_save", _("Save positions to FNS/PGN"), Iconos.ManualSave()) diff --git a/Code/QT/Controles.py b/Code/QT/Controles.py index 9ba190e..108e343 100644 --- a/Code/QT/Controles.py +++ b/Code/QT/Controles.py @@ -193,6 +193,7 @@ def ponFuente(self, f): def rehacer(self, liOpciones, valorInicial): self.liOpciones = liOpciones self.clear() + nindex = 0 for n, opcion in enumerate(liOpciones): if len(opcion) == 2: etiqueta, clave = opcion @@ -201,7 +202,8 @@ def rehacer(self, liOpciones, valorInicial): etiqueta, clave, icono = opcion self.addItem(icono, etiqueta, clave) if clave == valorInicial: - self.setCurrentIndex(n) + nindex = n + self.setCurrentIndex(nindex) def ponValor(self, valor): for n, opcion in enumerate(self.liOpciones): diff --git a/Code/QT/DatosNueva.py b/Code/QT/DatosNueva.py index d2fb4e8..78e3598 100644 --- a/Code/QT/DatosNueva.py +++ b/Code/QT/DatosNueva.py @@ -107,8 +107,7 @@ def dameCategoria(wParent, configuracion, procesador): titulo = _("Competition") ancho, alto = QTUtil.tamEscritorio() ancho = min(ancho, 700) - txt = _( - "
The aim is to obtain the highest possible score :") + txt = _("
The aim is to obtain the highest possible score :") Info.info(wParent, _("Lucas Chess"), titulo, txt, ancho, Iconos.pmAyudaGR()) return None diff --git a/Code/QT/FormLayout.py b/Code/QT/FormLayout.py index 8d126da..eb29f48 100644 --- a/Code/QT/FormLayout.py +++ b/Code/QT/FormLayout.py @@ -64,10 +64,12 @@ def __init__(self, label, minimo, maximo, ancho, chlabel): class Combobox: - def __init__(self, label, lista): + def __init__(self, label, lista, si_editable=False, tooltip=None): self.tipo = COMBOBOX self.lista = lista # (clave,titulo),.... self.label = label + ":" + self.si_editable = si_editable + self.tooltip = tooltip class FontCombobox: @@ -422,6 +424,11 @@ def setup(self, dispatch): field.valueChanged.connect(dispatch) elif tipo == COMBOBOX: field = Controles.CB(self, config.lista, value) + if config.si_editable: + field.setEditable(True) + if config.tooltip: + field.setToolTip(config.tooltip) + field.lista = config.lista if dispatch: field.currentIndexChanged.connect(dispatch) diff --git a/Code/QT/InfoBase.py b/Code/QT/InfoBase.py index c7a9994..3497ce8 100644 --- a/Code/QT/InfoBase.py +++ b/Code/QT/InfoBase.py @@ -59,7 +59,7 @@ def listaMotores(self, bloque): ["Critter 1.6a 32bits", "Richard Vida", "http://www.vlasak.biz/critter/"], ["Texel 1.05", "Peter Österlund", "http://web.comhem.se/petero2home/javachess/index.html#texel"], ["Stockfish 8", "Tord Romstad, Marco Costalba, Joona Kiiski", "http://stockfishchess.org/"], - ["Deepfish 4.1", "Tord Romstad, Marco Costalba, Joona Kiiski, fork by Marco Zerbinati", "https://github.com/Zerbinati/DeepFishMZ"], + ["McBrain 2.1a", "Michael Byrne (based on stockfish)", "https://github.com/MichaelB7/Stockfish/releases"], ["Gull 3", "Vadim Demichev", "https://sourceforge.net/projects/gullchess/"], ] li.sort(key=lambda x: x[0]) @@ -110,7 +110,7 @@ def version(num, liBase, liResto): return txt # Version 10 - liBase = ["Remes", "Max Aloyau", "Alfonso Solbes", "tico-tico"] + liBase = ["Remes", "Max Aloyau", "Alfonso Solbes", "tico-tico", "Nils Andersson", "Bernhard"] liResto = ["Immortalchess forum", ] txt += version(10, liBase, liResto) diff --git a/Code/QT/Pantalla.py b/Code/QT/Pantalla.py index 49ff25d..378bb68 100644 --- a/Code/QT/Pantalla.py +++ b/Code/QT/Pantalla.py @@ -184,8 +184,11 @@ def changeEvent(self, event): def muestraVariantes(self, titulo): flags = QtCore.Qt.Dialog | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint + self.setWindowFlags(flags) + self.setWindowTitle(titulo if titulo else "-") + return self.exec_() def ajustaTam(self): diff --git a/Code/QT/PantallaAnalisisParam.py b/Code/QT/PantallaAnalisisParam.py index d429eb7..413d93f 100644 --- a/Code/QT/PantallaAnalisisParam.py +++ b/Code/QT/PantallaAnalisisParam.py @@ -181,7 +181,7 @@ def paramAnalisis(parent, configuracion, siModoAmpliado, siTodosMotores=False): liGen.append((config, defecto)) liGen.append(SEPARADOR) - liGen.append((_("To redo any existing prior analyses (if they exist)") + ":", alm.siBorrarPrevio)) + liGen.append((_("Redo any existing prior analyses (if they exist)") + ":", alm.siBorrarPrevio)) liGen.append((_("Start from the end of the game") + ":", alm.desdeelfinal)) diff --git a/Code/QT/PantallaAperturas.py b/Code/QT/PantallaAperturas.py index 9842b9c..5f71275 100644 --- a/Code/QT/PantallaAperturas.py +++ b/Code/QT/PantallaAperturas.py @@ -353,6 +353,8 @@ def nueva(self): bloque = self.editar(None) if bloque: self.liBloques.append(bloque) + if not self.edNombre.texto().strip(): + self.edNombre.ponTexto(bloque.trNombre) self.grid.refresh() self.grid.gobottom() @@ -382,16 +384,19 @@ def borrar(self): self.grid.refresh() def aceptar(self): - self.nombre = self.edNombre.texto().strip() - - if not self.nombre: - QTUtil2.mensError(self, _("Not indicated the name of training")) - return - if not self.liBloques: QTUtil2.mensError(self, _("you have not indicated any opening")) return + self.nombre = self.edNombre.texto().strip() + if not self.nombre: + if len(self.liBloques) == 1: + self.nombre = self.liBloques[0].trNombre + else: + QTUtil2.mensError(self, _("Not indicated the name of training")) + return + + self.accept() def cancelar(self): diff --git a/Code/QT/PantallaEntMaq.py b/Code/QT/PantallaEntMaq.py index 4c65ff7..5d056d1 100644 --- a/Code/QT/PantallaEntMaq.py +++ b/Code/QT/PantallaEntMaq.py @@ -357,7 +357,7 @@ def cambiadoDepth(self, num): self.edRtiempo.setEnabled(num == 0) def posicionEditar(self): - resp = Voyager.voyagerFEN(self, self.fen) + resp = Voyager.voyagerFEN(self, self.fen, wownerowner=self.procesador.pantalla) if resp is not None: self.fen = resp self.muestraPosicion() diff --git a/Code/QT/PantallaManualSave.py b/Code/QT/PantallaManualSave.py index e466e2b..9cc4390 100644 --- a/Code/QT/PantallaManualSave.py +++ b/Code/QT/PantallaManualSave.py @@ -91,7 +91,7 @@ def __init__(self, procesador): self.bt_fns = Controles.PB(self, "", self.fns_select, plano=False).anchoMinimo(300) bt_no_fns = Controles.PB(self, "", self.fns_unselect).ponIcono(Iconos.Delete()).anchoFijo(16) ## Codec - lb_codec = Controles.LB(self, _("Write with the codec") + ": ") + lb_codec = Controles.LB(self, _("Encoding") + ": ") liCodecs = [k for k in set(v for k, v in encodings.aliases.aliases.iteritems())] liCodecs.sort() liCodecs = [(k, k) for k in liCodecs] @@ -366,7 +366,7 @@ def write_file(fich, f, txt, quien): def change_position(self): prev = self.analyzing self.stop() - fen = Voyager.voyagerFEN(self, self.posicion.fen()) + fen = Voyager.voyagerFEN(self, self.posicion.fen(), wownerowner=self.procesador.pantalla) if fen is not None: self.em_solucion.ponTexto("") self.posicion.leeFen(fen) @@ -378,12 +378,13 @@ def change_position(self): self.start() def reset_motor(self): + clave = self.cb_engine.valor() + if not clave: + return self.analyzing = False if self.gestor_motor: self.gestor_motor.terminar() self.stop() - - clave = self.cb_engine.valor() conf_motor = self.configuracion.buscaRivalExt(clave) multipv = self.sb_multipv.valor() @@ -396,7 +397,7 @@ def ext_engines(self): valor = self.cb_engine.valor() liMotores = self.configuracion.comboMotoresCompleto() motor = self.configuracion.tutor.clave - for clave, rotulo in liMotores: + for rotulo, clave in liMotores: if clave == valor: motor = valor break diff --git a/Code/QT/PantallaRoutes.py b/Code/QT/PantallaRoutes.py index 5472e0b..c307b84 100644 --- a/Code/QT/PantallaRoutes.py +++ b/Code/QT/PantallaRoutes.py @@ -20,7 +20,8 @@ def __init__(self, procesador): self.procesador = procesador wsvg = QtSvg.QSvgWidget() - wsvg.load(QtCore.QByteArray(self.route.get_txt())) + x = self.route.get_txt().encode("utf-8") + wsvg.load(QtCore.QByteArray(x)) wsvg.setFixedSize(762, 762.0 * 658.0 / 1148.0) lySVG = Colocacion.H().relleno(1).control(wsvg).relleno(1) diff --git a/Code/QT/PantallaSavePGN.py b/Code/QT/PantallaSavePGN.py index 9290337..79df016 100644 --- a/Code/QT/PantallaSavePGN.py +++ b/Code/QT/PantallaSavePGN.py @@ -49,7 +49,7 @@ def __init__(self, owner, pgn, configuracion): self.bt_file = Controles.PB(self, "", self.file_select, plano=False).anchoMinimo(300) # Codec - lb_codec = Controles.LB(self, _("Write with the codec") + ": ") + lb_codec = Controles.LB(self, _("Encoding") + ": ") liCodecs = [k for k in set(v for k, v in encodings.aliases.aliases.iteritems())] liCodecs.sort() liCodecs = [(k, k) for k in liCodecs] diff --git a/Code/QT/PantallaSolo.py b/Code/QT/PantallaSolo.py index ca4386b..cca20fa 100644 --- a/Code/QT/PantallaSolo.py +++ b/Code/QT/PantallaSolo.py @@ -1,3 +1,5 @@ +from Code import TrListas + from Code.QT import Colocacion from Code.QT import Columnas from Code.QT import Controles @@ -5,8 +7,8 @@ from Code.QT import Grid from Code.QT import Iconos from Code.QT import QTVarios -from Code import TrListas -from Code import Util +from Code.QT import FormLayout + class WEtiquetasPGN(QTVarios.WDialogo): @@ -45,17 +47,13 @@ def __init__(self, procesador, liPGN): self.recuperarVideo() def creaLista(self, liPGN): - sd = Util.SymbolDict() - for eti, val in liPGN: - sd[eti] = val + st = {eti for eti, val in liPGN} - li = [] + li = liPGN[:] listandard = ("Event", "Site", "Date", "Round", "White", "Black", "Result", "ECO", "WhiteElo", "BlackElo") for eti in listandard: - li.append([eti, sd.get(eti,"")]) - for eti, val in liPGN: - if eti not in listandard: - li.append([eti,val]) + if eti not in st: + li.append([eti, ""]) while len(li) < 30: li.append(["", ""]) self.liPGN = li @@ -119,3 +117,127 @@ def editarEtiquetasPGN(procesador, liPGN): return li else: return None + + +def massive_change_tags(owner, configuracion, num_selected): + APPLY_ALL, APPLY_SELECTED = range(2) + SAVE_NOTHING, SAVE_LABELS, SAVE_LABELS_VALUES = range(3) + NUM_OTHERS = 4 + + dic = configuracion.leeVariables("MASSIVE_CHANGE_TAGS") + if not dic: + dic = {} + + liBase = [] + sep = (None, None) + + def sepBase(): + liBase.append(sep) + + sepBase() + + liBase.append((None, _("Basic tags"))) + liBase.append(("%s:" % _("Event"), dic.get("EVENT", ""))) + liBase.append(("%s:" % _("Site"), dic.get("SITE", ""))) + liBase.append(("%s:" % _("Date"), dic.get("DATE", ""))) + + sepBase() + + liBaseOther = ["Round", "White", "Black", "Result", "WhiteTitle", "BlackTitle", "WhiteElo", "BlackElo", + "WhiteUSCF", "BlackUSCF", "WhiteNA", "BlackNA", "WhiteType", "BlackType", "EventDate", + "EventSponsor", "Section", "Stage", "Board", "Opening", "Variation", "SubVariation", + "ECO", "Time", "UTCTime", "UTCDate", "TimeControl", "SetUp", "Termination"] + li0 = ["", "Event", "Site", "Date"] + li0.extend(liBaseOther) + li = [[uno, uno] for uno in li0] + combo = FormLayout.Combobox(_("Remove the tag (in English)"), li, si_editable=True) + liBase.append((combo, "")) + + sepBase() + + liBase.append((None, _("Configuration"))) + + liA = [(_("All read"), APPLY_ALL), ("%s [%d]"%(_("Only selected"), num_selected), APPLY_SELECTED)] + config = FormLayout.Combobox(_("Apply to"), liA) + liBase.append((config, dic.get("APPLY", APPLY_SELECTED if num_selected > 1 else APPLY_ALL))) + sepBase() + + liBase.append((_("Overwrite"), dic.get("OVERWRITE", True))) + sepBase() + + liS = [(_("Nothing"), SAVE_NOTHING), + (_("Labels"), SAVE_LABELS), + ("%s+%s" % (_("Labels"), _("Values")), SAVE_LABELS_VALUES) + ] + config = FormLayout.Combobox(_("Save as default"), liS) + liBase.append((config, dic.get("SAVE", SAVE_LABELS_VALUES))) + + + liOther = [sep] + + for x in range(NUM_OTHERS): + li = [[uno, uno] for uno in liBaseOther] + previo = dic.get("OTHERS_TAG_%d" % x, "") + li.insert(0, [previo, previo]) + if previo: + li.insert(0, ["", ""]) + + combo = FormLayout.Combobox("%s %d" % (_("Tag"), x + 1), li, si_editable=True) + liOther.append((combo, previo)) + liOther.append(("%s %d:" % (_("Value"), x+1), dic.get("OTHERS_VALUE_%d" % x, ""))) + liOther.append(sep) + + liOther.append((None, "** %s" % _("All official tags must be indicated with their name in English."))) + + + lista = [] + lista.append((liBase, _("Base"), "")) + lista.append((liOther, _("Others"), "")) + + # Editamos + resultado = FormLayout.fedit(lista, title=_("Massive change of tags"), parent=owner, anchoMinimo=640, icon=Iconos.PGN()) + if resultado: + accion, resp = resultado + liBase, liOther = resp + + dic = {} + dic["EVENT"], dic["SITE"], dic["DATE"], dic["REMOVE"], dic["APPLY"], dic["OVERWRITE"], dic["SAVE"] = liBase + + liTags = [] + for tag in ("Event", "Site", "Date"): + tagu = tag.upper() + if dic[tagu]: + liTags.append((tag, dic[tagu])) + + for x in range(0, NUM_OTHERS*2, 2): + tag = liOther[x] + if tag and tag.upper!= "FEN": + val = liOther[x + 1] + ntag = x / 2 + dic["OTHERS_TAG_%d" % ntag] = tag + dic["OTHERS_VALUE_%d" % ntag] = val + if val: + liTags.append((tag, val)) + + save = dic["SAVE"] + + if save == SAVE_NOTHING or save == SAVE_LABELS: + for x in ("EVENT", "SITE", "DATE"): + del dic[x] + for x in range(NUM_OTHERS): + key = "OTHERS_VALUE_%d" % x + if key in dic: + del dic[key] + if save == SAVE_NOTHING: + key = "OTHERS_TAG_%d" % x + if key in dic: + del dic[key] + + configuracion.escVariables("MASSIVE_CHANGE_TAGS", dic) + + if liTags or dic["REMOVE"]: + return (liTags, dic["REMOVE"], dic["OVERWRITE"], dic["APPLY"] == APPLY_ALL) + + return None + + diff --git a/Code/QT/PantallaTorneos.py b/Code/QT/PantallaTorneos.py index 53aa27e..5a65efb 100644 --- a/Code/QT/PantallaTorneos.py +++ b/Code/QT/PantallaTorneos.py @@ -657,6 +657,9 @@ def gmCrear(self): liGen.append((None, None)) liGen.append((_("Select all"), False)) + liGen.append((None, None)) + liGen.append((_("Random order"), True)) + reg = Util.Almacen() reg.form = None @@ -695,6 +698,8 @@ def dispatch(valor): if si: liSel.append(en.huella()) + rnd = liResp[-1] + self.configuracion.escVariables("crear_torneo", dicValores) nSel = len(liSel) @@ -707,7 +712,8 @@ def dispatch(valor): for y in range(x + 1, nSel): self.torneo.nuevoGame(liSel[x], liSel[y], minutos, segundos) self.torneo.nuevoGame(liSel[y], liSel[x], minutos, segundos) - self.torneo.randomize() + if rnd: + self.torneo.randomize() self.gridGames.refresh() self.gridGames.gobottom() diff --git a/Code/QT/PantallaWashing.py b/Code/QT/PantallaWashing.py index dc9bab8..38f55f7 100644 --- a/Code/QT/PantallaWashing.py +++ b/Code/QT/PantallaWashing.py @@ -206,7 +206,7 @@ def file(self): menu.opcion(fich, fich, Iconos.PuntoRojo()) resp = menu.lanza() if resp: - if QTUtil2.pregunta(self, "%s\n%s" % ( _("Current data will be removed and overwrited."), + if QTUtil2.pregunta(self, "%s\n%s" % ( _("Current data will be removed and overwritten."), _("Are you sure?")) ): shutil.copy(os.path.join(self.configuracion.carpeta, resp+".wsm"), self.dbwashing.file) self.wreload = True @@ -214,7 +214,7 @@ def file(self): self.accept() elif resp.startswith("new_"): tactic = resp[4:] - if QTUtil2.pregunta(self, "%s\n%s" % ( _("Current data will be removed and overwrited."), + if QTUtil2.pregunta(self, "%s\n%s" % ( _("Current data will be removed and overwritten."), _("Are you sure?")) ): self.dbwashing.new(tactic) self.wreload = True diff --git a/Code/QT/PantallaWorkMap.py b/Code/QT/PantallaWorkMap.py index f0d44fd..3123161 100644 --- a/Code/QT/PantallaWorkMap.py +++ b/Code/QT/PantallaWorkMap.py @@ -4,6 +4,7 @@ from Code import ControlPosicion from Code import Jugada from Code import Partida +from Code import TrListas from Code.QT import Colocacion from Code.QT import Columnas from Code.QT import Controles @@ -19,9 +20,9 @@ class WMap(QTVarios.WDialogo): def __init__(self, procesador, mapa): - self.workmap = WorkMap.WorkMap(mapa) - titulo = _F(mapa) + dic = TrListas.maps() + titulo = dic[mapa] icono = getattr(Iconos, mapa)() QTVarios.WDialogo.__init__(self, procesador.pantalla, titulo, icono, mapa) diff --git a/Code/QT/QTVarios.py b/Code/QT/QTVarios.py index 88100c6..3e3faf9 100644 --- a/Code/QT/QTVarios.py +++ b/Code/QT/QTVarios.py @@ -1022,7 +1022,7 @@ def __init__(self, parent, titulo, siErroneos, icono): self.lbRotDuplicados = Controles.LB(self, _("Duplicated") + ":").ponFuente(f) self.lbDuplicados = Controles.LB(self, "0").ponFuente(f) - lbRotImportados = Controles.LB(self, _("Imported") + ":").ponFuente(f) + self.lbRotImportados = lbRotImportados = Controles.LB(self, _("Imported") + ":").ponFuente(f) self.lbImportados = Controles.LB(self, "0").ponFuente(f) self.btCancelarSeguir = Controles.PB(self, _("Cancel"), self.cancelar, plano=False).ponIcono(Iconos.Delete()) @@ -1052,6 +1052,9 @@ def cancelar(self): self.siCancelado = True self.ponContinuar() + def ponExportados(self): + self.lbRotImportados.ponTexto(_("Exported") + ":") + def ponSaving(self): self.btCancelarSeguir.setDisabled(True) self.btCancelarSeguir.ponTexto(_("Saving...")) diff --git a/Code/QT/Voyager.py b/Code/QT/Voyager.py index 48ec282..54c448d 100644 --- a/Code/QT/Voyager.py +++ b/Code/QT/Voyager.py @@ -380,7 +380,7 @@ def scanner(self): pos = QTUtil.escondeWindow(self.wparent) seguir = True if self.chb_scanner_ask.valor() and not QTUtil2.pregunta(None, _("Bring the window to scan to front"), - etiSi=_("Capture"), etiNo=_("Cancel"), si_top=True): + etiSi=_("Accept"), etiNo=_("Cancel"), si_top=True): seguir = False if seguir: fich_png = self.configuracion.ficheroTemporal("png") @@ -800,13 +800,21 @@ def cancelar(self): self.reject() -def voyagerFEN(wowner, fen): - pos = QTUtil.escondeWindow(wowner) +def voyagerFEN(wowner, fen, si_esconde=True, wownerowner=None): + if si_esconde: + pos = QTUtil.escondeWindow(wowner) + if wownerowner: + pos_ownerowner = QTUtil.escondeWindow(wownerowner) partida = Partida.Partida(fen=fen) dlg = Voyager(wowner, False, partida) resp = dlg.resultado if dlg.exec_() else None - wowner.move(pos) - wowner.show() + if si_esconde: + if wownerowner: + wownerowner.move(pos_ownerowner) + wownerowner.show() + wowner.move(pos) + wowner.show() + return resp diff --git a/Code/QT/WBG_Games.py b/Code/QT/WBG_Games.py index acc0e9e..2ca7192 100644 --- a/Code/QT/WBG_Games.py +++ b/Code/QT/WBG_Games.py @@ -5,6 +5,8 @@ from Code import Partida from Code import DBgames +from Code import TrListas +from Code import Util from Code.QT import Colocacion from Code.QT import Columnas from Code.QT import Controles @@ -14,8 +16,7 @@ from Code.QT import PantallaPGN from Code.QT import QTUtil2 from Code.QT import QTVarios -from Code import TrListas -from Code import Util +from Code.QT import PantallaSolo class WGames(QtGui.QWidget): @@ -39,6 +40,8 @@ def __init__(self, procesador, winBookGuide, dbGames, wsummary, siMoves=True): self.liFiltro = [] self.where = None + self.last_opening = None + # Grid oColumnas = Columnas.ListaColumnas() oColumnas.nueva("numero", _("N."), 70, siCentrado=True) @@ -46,8 +49,7 @@ def __init__(self, procesador, winBookGuide, dbGames, wsummary, siMoves=True): ancho = 70 for clave in liBasic: rotulo = TrListas.pgnLabel(clave) - siCentrado = clave != "EVENT" - oColumnas.nueva(clave, rotulo, ancho, siCentrado=siCentrado) + oColumnas.nueva(clave, rotulo, ancho, siCentrado=True) self.grid = Grid.Grid(self, oColumnas, siSelecFilas=True, siSeleccionMultiple=True, xid="wgames") @@ -65,6 +67,7 @@ def __init__(self, procesador, winBookGuide, dbGames, wsummary, siMoves=True): (_("Last"), Iconos.Final(), self.tw_gobottom), None, (_("Filter"), Iconos.Filtrar(), self.tw_filtrar), None, (_("Remove"), Iconos.Borrar(), self.tw_borrar),None, + (_("Utilities"), Iconos.Utilidades(), self.tw_utilities), None, ] self.tbWork = Controles.TBrutina(self, liAccionesWork, tamIcon=24) @@ -198,7 +201,7 @@ def actualiza(self, siObligatorio=False): self.gridCambiadoRegistro(None, recno, None) def gridCambiadoRegistro(self, grid, fila, oCol): - if fila >= 0: + if self.gridNumDatos(grid) > fila >= 0: pv = self.dbGames.damePV(fila) p = Partida.Partida() p.leerPV(pv) @@ -220,6 +223,7 @@ def editar(self, recno, partidaCompleta): QTUtil2.mensError(self, _("This game already exists.")) else: self.actualiza(True) + self.grid.goto(recno, 0) def tw_nuevo(self): recno = None @@ -263,10 +267,10 @@ def raw_sql(): def opening(): me = QTUtil2.unMomento(self) import Code.QT.PantallaAperturas as PantallaAperturas - w = PantallaAperturas.WAperturas(self, self.configuracion, "") + w = PantallaAperturas.WAperturas(self, self.configuracion, self.last_opening) me.final() if w.exec_(): - ap = w.resultado() + self.last_opening = ap = w.resultado() pv = getattr(ap, "a1h8", "") self.dbGames.filterPV(pv) self.numJugada = pv.count(" ") @@ -321,6 +325,8 @@ def tg_file(self): submenu.opcion(self.tg_importar_PGN, _("A PGN file"), Iconos.FichPGN()) submenu.separador() submenu.opcion(self.tg_importar_DB, _("Other database"), Iconos.DatabaseC()) + submenu.separador() + submenu.opcion(self.tg_importar_pks, _("A PKS file"), Iconos.JuegaSolo()) menu.separador() submenu = menu.submenu(_("Export to"), Iconos.DatabaseMas()) @@ -329,20 +335,57 @@ def tg_file(self): submenu.opcion(self.tg_exportar_DB, _("Other database"), Iconos.DatabaseC()) menu.separador() - submenu = menu.submenu(_("Utilities"), Iconos.Utilidades()) + resp = menu.lanza() + if resp: + resp() + + def tw_utilities(self): + menu = QTVarios.LCMenu(self) ico = Iconos.PuntoAzul() icoT = Iconos.Tacticas() - menu1 = submenu.submenu(_("Polyglot book"), ico) + menu1 = menu.submenu(_("Polyglot book"), ico) menu1.opcion(self.tw_uti_pcreate, _("Create a new book"), ico) menu1.separador() menu1.opcion(self.tw_uti_pmerge, _("Merge two books in one"), ico) - submenu.separador() - submenu.opcion(self.tw_uti_tactic, _("Create tactics training"), icoT) - + menu.separador() + menu.opcion(self.tw_uti_tactic, _("Create tactics training"), icoT) + menu.separador() + menu.opcion(self.tw_massive_change_tags, _("Massive change of tags"), Iconos.PGN()) resp = menu.lanza() if resp: resp() + def tg_importar_pks(self): + path_pks = QTUtil2.leeFichero(self, self.configuracion.dirJS, "pks") + if path_pks: + direc = os.path.dirname(path_pks) + if direc != self.configuracion.dirJS: + self.configuracion.dirJS = direc + self.configuracion.graba() + + mens_error = self.dbGames.insert_pks(path_pks) + if mens_error: + QTUtil2.mensError(self, mens_error) + return + self.actualiza(True) + self.grid.gobottom(0) + + def tw_massive_change_tags(self): + resp = PantallaSolo.massive_change_tags(self, self.configuracion, len(self.grid.recnosSeleccionados())) + if resp: + recno = self.grid.recno() + liTags, remove, overwrite, si_all = resp + liRegistros = range(self.dbGames.reccount()) if si_all else self.grid.recnosSeleccionados() + nRegistros = len(liRegistros) + if (nRegistros == 1 or + ((nRegistros > 1) and + QTUtil2.pregunta(self, _("Are you sure do you want to change the %d registers?" % nRegistros)))): + um = QTUtil2.unMomento(self) + self.dbGames.massive_change_tags(liTags, liRegistros, remove, overwrite) + self.grid.refresh() + self.grid.goto(recno, 0) + um.final() + def tw_uti_pcreate(self): PantallaBooks.polyglotCrear(self) @@ -482,7 +525,7 @@ def tg_exportar_PGN(self): try: fpgn = codecs.open(pathPGN, modo, 'utf-8', 'ignore') except: - QTUtil2.mensError(self, "%s : %s\n" % (_("Unable to save"), pathPGN.replace("/", "\\"))) + QTUtil2.mensError(self, "%s : %s\n" % (_("Unable to save"), pathPGN)) return pb = QTUtil2.BarraProgreso1(self, _("Exporting...")) @@ -502,7 +545,7 @@ def tg_exportar_PGN(self): fpgn.close() pb.cerrar() - QTUtil2.mensaje(self, _X(_("Saved to %1"), pathPGN.replace("/", "\\"))) + QTUtil2.mensaje(self, _X(_("Saved to %1"), pathPGN)) def tg_importar_PGN(self): path = QTVarios.select_pgn(self) @@ -530,18 +573,6 @@ def tg_importar_DB(self): self.actualiza(True) self.wsummary.reset() - def tg_importarFEN(self): - # Elegimos el fichero PGN - path = QTVarios.select_pgn(self) - if not path: - return None - - dlTmp = QTVarios.ImportarFicheroPGN(self) - dlTmp.show() - self.dbGames.leerPGN(path, dlTmp) - - self.actualiza(True) - def changeDBgames(self, pathFich): self.configuracion.ficheroDBgames = pathFich self.configuracion.graba() diff --git a/Code/QT/WBG_GamesFEN.py b/Code/QT/WBG_GamesFEN.py index 4eea378..da70f6c 100644 --- a/Code/QT/WBG_GamesFEN.py +++ b/Code/QT/WBG_GamesFEN.py @@ -10,11 +10,11 @@ from Code.QT import Controles from Code.QT import Grid from Code.QT import Iconos -from Code.QT import PantallaBooks from Code.QT import PantallaPGN from Code.QT import QTUtil2 from Code.QT import QTVarios from Code.QT import Voyager +from Code.QT import PantallaSolo from Code import TrListas from Code import Util @@ -41,8 +41,6 @@ def __init__(self, procesador, winBookGuide, dbGamesFEN): oColumnas.nueva("numero", _("N."), 70, siCentrado=True) liBasic = dbGamesFEN.liCamposBase for clave in liBasic: - if clave == "FEN": - continue rotulo = TrListas.pgnLabel(clave) siCentrado = clave != "EVENT" @@ -65,6 +63,7 @@ def __init__(self, procesador, winBookGuide, dbGamesFEN): (_("Last"), Iconos.Final(), self.tw_gobottom), None, (_("Filter"), Iconos.Filtrar(), self.tw_filtrar), None, (_("Remove"), Iconos.Borrar(), self.tw_borrar),None, + (_("Utilities"), Iconos.Utilidades(), self.tw_utilities), None, ] self.tbWork = Controles.TBrutina(self, liAccionesWork, tamIcon=24) @@ -80,6 +79,10 @@ def __init__(self, procesador, winBookGuide, dbGamesFEN): self.setNameToolBar() + def reread(self): + self.dbGamesFEN.lee_rowids() + self.grid.refresh() + def limpiaColumnas(self): for col in self.grid.oColumnas.liColumnas: cab = col.cabecera @@ -174,7 +177,7 @@ def tw_filtrar(self): self.liFiltro = w.liFiltro self.where = w.where() - self.dbGames.filter(self.where) + self.dbGamesFEN.setFilter(self.where) self.grid.refresh() self.grid.gotop() self.updateStatus() @@ -187,16 +190,23 @@ def tw_gotop(self): def tw_nuevo(self): # Se genera un PGN - fen = Voyager.voyagerFEN(self, "") + fen = Voyager.voyagerFEN(self, "", False) if fen is not None: - pgn = '[FEN "%s"]' % fen + if self.dbGamesFEN.si_existe_fen(fen): + QTUtil2.mensError(self, _("This position already exists.")) + return + hoy = Util.hoy() + pgn = '[Date "%d.%02d.%02d"]\n[FEN "%s"]' % (hoy.year, hoy.month, hoy.day, fen) nuevoPGN, pv, dicPGN = self.procesador.gestorUnPGN(self, pgn) - if nuevoPGN: - if not self.dbGamesFEN.cambiarUno(None, nuevoPGN, pv, dicPGN): - QTUtil2.mensError(self, _("This position already exists.")) - else: + if dicPGN: + liTags = [(clave, valor) for clave, valor in dicPGN.iteritems()] + partida_completa = Partida.PartidaCompleta(fen=fen, liTags=liTags) + if pv: + partida_completa.leerPV(pv) + if self.dbGamesFEN.inserta(partida_completa): self.actualiza() + self.grid.refresh() self.grid.gobottom() def tw_editar(self): @@ -205,13 +215,19 @@ def tw_editar(self): recno = li[0] partidaCompleta = self.dbGamesFEN.leePartidaRecno(recno) - partidaCompleta = self.procesador.gestorPartida(self, partidaCompleta, True) - if partidaCompleta: - if not self.dbGamesFEN.guardaPartidaRecno(recno, partidaCompleta): - QTUtil2.mensError(self, _("This position already exists.")) - else: - self.grid.refresh() - self.updateStatus() + fen0 = partidaCompleta.iniPosicion.fen() + partidaCompleta = self.procesador.gestorPartida(self, partidaCompleta, False) + if partidaCompleta is not None: + fen1 = partidaCompleta.iniPosicion.fen() + if fen0 != fen1: + if self.dbGamesFEN.si_existe_fen(fen1): + QTUtil2.mensError(self, _("This position already exists.")) + return + + self.dbGamesFEN.guardaPartidaRecno(recno, partidaCompleta) + self.actualiza() + self.grid.refresh() + self.updateStatus() def tw_borrar(self): li = self.grid.recnosSeleccionados() @@ -227,6 +243,13 @@ def tw_borrar(self): um.final() + def tw_utilities(self): + menu = QTVarios.LCMenu(self) + menu.opcion(self.tg_massive_change_tags, _("Massive change of tags"), Iconos.PGN()) + resp = menu.lanza() + if resp: + resp() + def tg_file(self): menu = QTVarios.LCMenu(self) @@ -239,6 +262,8 @@ def tg_file(self): submenu.opcion(self.tg_importar_PGN, _("A PGN file"), Iconos.FichPGN()) submenu.separador() submenu.opcion(self.tg_importar_DB, _("Other database"), Iconos.DatabaseC()) + submenu.separador() + submenu.opcion(self.tg_importar_pks, _("A PKS file"), Iconos.JuegaSolo()) menu.separador() submenu = menu.submenu(_("Export to"), Iconos.DatabaseMas()) @@ -251,28 +276,36 @@ def tg_file(self): if resp: resp() - def tw_uti_pcreate(self): - PantallaBooks.polyglotCrear(self) - - def tw_uti_pmerge(self): - PantallaBooks.polyglotUnir(self) - - def tw_uti_tactic(self): - dbf = self.dbGamesFEN.dbf - - def rutinaDatos(recno): - dic = {} - dbf.goto(recno) - for clave in dbf.liCampos: - dic[clave] = getattr(dbf.reg, clave) - dic["PGN"] = Util.blob2var(dic["PGN"]) - return dic + def tg_importar_pks(self): + path_pks = QTUtil2.leeFichero(self, self.configuracion.dirJS, "pks") + if path_pks: + direc = os.path.dirname(path_pks) + if direc != self.configuracion.dirJS: + self.configuracion.dirJS = direc + self.configuracion.graba() - liRegistros = self.grid.recnosSeleccionados() - if len(liRegistros) < 2: - liRegistros = range(dbf.reccount()) + mens_error = self.dbGamesFEN.insert_pks(path_pks) + if mens_error: + QTUtil2.mensError(self, mens_error) + return + self.actualiza(True) + self.grid.gobottom(0) - PantallaPGN.crearTactic(self.procesador, self, liRegistros, rutinaDatos) + def tg_massive_change_tags(self): + resp = PantallaSolo.massive_change_tags(self, self.configuracion, len(self.grid.recnosSeleccionados())) + if resp: + recno = self.grid.recno() + liTags, remove, overwrite, si_all = resp + liRegistros = range(self.dbGamesFEN.reccount()) if si_all else self.grid.recnosSeleccionados() + nRegistros = len(liRegistros) + if (nRegistros == 1 or + ((nRegistros > 1) and + QTUtil2.pregunta(self, _("Are you sure do you want to change the %d registers?" % nRegistros)))): + um = QTUtil2.unMomento(self) + self.dbGamesFEN.massive_change_tags(liTags, liRegistros, remove, overwrite) + self.reread() + self.grid.goto(recno, 0) + um.final() def tg_change(self): pathFich = QTUtil2.leeFichero(self, os.path.dirname(self.configuracion.ficheroDBgames), "lcf", @@ -343,76 +376,44 @@ def tg_exportar_DB(self): if modo == "w" and Util.existeFichero(path): Util.borraFichero(path) - Util.borraFichero(path+"_s1") dlTmp = QTVarios.ImportarFicheroDB(self) + dlTmp.ponExportados() dlTmp.show() dbn = DBgamesFEN.DBgamesFEN(path) dbn.appendDB(self.dbGamesFEN, li, dlTmp) def tg_exportar_PGN(self): - li = self.grid.recnosSeleccionados() - if li: - if len(li) > 1: - menu = QTVarios.LCMenu(self) - menu.opcion(True, _("All read"), Iconos.PuntoVerde()) - menu.separador() - menu.opcion(False, _("Only selected"), Iconos.PuntoAzul()) - resp = menu.lanza() - if resp is None: - return - siTodos = resp - else: - siTodos = True - - if siTodos: - li = range(self.dbGamesFEN.reccount()) - - # Fichero donde aadir - pathPGN = QTUtil2.salvaFichero(self, _("Export"), self.configuracion.dirPGN, - _("File") + " pgn (*.pgn)", - False) - if pathPGN: - carpeta, nomf = os.path.split(pathPGN) - if carpeta != self.configuracion.dirPGN: - self.configuracion.dirPGN = carpeta - self.configuracion.graba() - - # Grabamos - modo = "w" - if Util.existeFichero(pathPGN): - yn = QTUtil2.preguntaCancelar(self, - _X(_("The file %1 already exists, what do you want to do?"), pathPGN), - si=_("Append"), no=_("Overwrite")) - if yn is None: - return - if yn: - modo = "a" - try: - fpgn = codecs.open(pathPGN, modo, 'utf-8', 'ignore') - except: - QTUtil2.mensError(self, "%s : %s\n" % (_("Unable to save"), pathPGN.replace("/", "\\"))) - return - - pb = QTUtil2.BarraProgreso1(self, _("Exporting...")) - pb.mostrar() - total = len(li) - pb.ponTotal(total) - - if modo == "a": - fpgn.write("\n\n") - for n, recno in enumerate(li): - pgn = self.dbGamesFEN.leePGNRecno(recno) - pb.pon(n + 1) - if pb.siCancelado(): - break - fpgn.write(pgn) - fpgn.write("\n\n") - - fpgn.close() - pb.cerrar() - QTUtil2.mensaje(self, _X(_("Saved to %1"), pathPGN.replace("/", "\\"))) + resp = self.tg_exportar("pgn") + if not resp: + return + li, modo, path = resp + + try: + fpgn = codecs.open(path, modo, 'utf-8', 'ignore') + except: + QTUtil2.mensError(self, "%s : %s\n" % (_("Unable to save"), path)) + return + + pb = QTUtil2.BarraProgreso1(self, _("Exporting...")) + pb.mostrar() + total = len(li) + pb.ponTotal(total) + + if modo == "a": + fpgn.write("\n\n") + for n, recno in enumerate(li): + p = self.dbGamesFEN.leePartidaRecno(recno) + pb.pon(n + 1) + if pb.siCancelado(): + break + fpgn.write(p.pgn()) + fpgn.write("\n\n") + + fpgn.close() + pb.cerrar() + QTUtil2.mensaje(self, _X(_("Saved to %1"), path)) def tg_importar_PGN(self): path = QTVarios.select_pgn(self) @@ -427,28 +428,16 @@ def tg_importar_PGN(self): def tg_importar_DB(self): path = QTVarios.select_ext(self, "lcf") - if not path: - return None - - dlTmp = QTVarios.ImportarFicheroDB(self) - dlTmp.show() - - dbn = DBgamesFEN.DBgamesFEN(path) - self.dbGamesFEN.appendDB(dbn, range(dbn.all_reccount()), dlTmp) - - self.actualiza(True) - - def tg_importarFEN(self): - # Elegimos el fichero PGN - path = QTVarios.select_pgn(self) - if not path: - return None + if path: + dlTmp = QTVarios.ImportarFicheroDB(self) + dlTmp.show() - dlTmp = QTVarios.ImportarFicheroPGN(self) - dlTmp.show() - self.dbGamesFEN.leerPGN(path, dlTmp) + dbn = DBgamesFEN.DBgamesFEN(path) + dbn.lee_rowids() + liRecnos = range(dbn.reccount()) + self.dbGamesFEN.appendDB(dbn, liRecnos, dlTmp) - self.actualiza(True) + self.actualiza(True) def changeDBgames(self, pathFich): self.configuracion.ficheroDBgamesFEN = pathFich diff --git a/Code/QT/WCapturas.py b/Code/QT/WCapturas.py index f4888f0..72ed3c5 100644 --- a/Code/QT/WCapturas.py +++ b/Code/QT/WCapturas.py @@ -1,3 +1,5 @@ +DIVISOR = 12 + from PyQt4 import QtCore, QtGui from Code.QT import Colocacion @@ -10,7 +12,7 @@ def __init__(self, wParent, tablero): super(CapturaLista, self).__init__(wParent) self.setFixedWidth(10) - anchoPZ = int(tablero.ancho/12) + anchoPZ = int(tablero.ancho/DIVISOR) self.pantalla = wParent.parent() self.tipoMaterial = VarGen.configuracion.tipoMaterial @@ -37,7 +39,7 @@ def mousePressEvent(self, event): self.pantalla.activaCapturas(False) def resetPZ(self, tablero): - anchoPZ = int(tablero.ancho/12) + anchoPZ = int(tablero.ancho/DIVISOR) for k, li in self.dic.iteritems(): for lb in li: tablero.piezas.change_label(lb, anchoPZ) diff --git a/Code/Routes.py b/Code/Routes.py index adcc376..047682f 100644 --- a/Code/Routes.py +++ b/Code/Routes.py @@ -4,6 +4,7 @@ from Code import Partida from Code import Util +from Code import TrListas class Reg: @@ -37,7 +38,7 @@ def xpos(self): @property def name(self): - return self._name + return _F(self._name) class Line: @@ -173,7 +174,11 @@ def read_level(self): def read_svg(self, base): with open(base % "svg", "rb") as f: - return f.read() + x = f.read() + dic = TrListas.transsiberian() + for k, v in dic.iteritems(): + x = x.replace(k, v) + return x def read_dat(self, base): lines = [] diff --git a/Code/Torneo.py b/Code/Torneo.py index 8a8b14f..af9e3f1 100644 --- a/Code/Torneo.py +++ b/Code/Torneo.py @@ -259,7 +259,7 @@ def pgn(self, torneo): base = self._partida.pgnBase() - return cabecera + "\n" + base + " %s\n" % rs + return cabecera + "\n" + base + " %s\n\n" % rs class Torneo: @@ -456,16 +456,20 @@ def numGames(self): def liGames(self): return self._liGames - def randomize(self, n=0): - if n == 50: - return + def randomize(self): random.shuffle(self._liGames) - prev = self._liGames[0] - for gm in self._liGames[1:]: - if gm.hwhite() == prev.hwhite() and gm.hblack() == prev.hblack(): - return self.randomize(n+1) - prev = gm - return + num_games = len(self._liGames) + for n in range(1, num_games-1): + gm1 = self._liGames[n] + gm0 = self._liGames[n-1] + if gm0.hwhite() == gm1.hwhite() or gm0.hblack() == gm1.hblack(): + for pos in range(n+1, num_games): + gm2 = self._liGames[pos] + if not(gm2.hwhite() == gm1.hwhite() or gm2.hblack() == gm1.hblack() + or gm2.hwhite() == gm0.hwhite() or gm2.hblack() == gm0.hblack()): + self._liGames[pos] = gm1 + self._liGames[n] = gm2 + break def delGames(self, lista): li = [] diff --git a/Code/TrListas.py b/Code/TrListas.py index 5f7c6dd..2580f59 100644 --- a/Code/TrListas.py +++ b/Code/TrListas.py @@ -2,29 +2,35 @@ def categoria(key): - return {"PRINCIPIANTE": _("Beginner"), + return { + "FUTUROPRINCIPIANTE": _("Future beginner"), + "PRINCIPIANTE": _("Beginner"), "AFICIONADO": _("Amateur"), "CANDIDATOMAESTRO": _("Candidate Master"), "MAESTRO": _("Master"), "CANDIDATOGRANMAESTRO": _("International Master"), "GRANMAESTRO": _("Grandmaster"), - "ALUCINANTE": _("Mind-bending") + "ALUCINANTE": _("Mind-bending"), }[key] def pgnLabel(key): - return {"EVENT": _("Event"), - "SITE": _("Site"), - "DATE": _("Date"), - "ROUND": _("Round"), - "WHITE": _("White"), - "BLACK": _("Black"), - "RESULT": _("Result"), - "ECO": _("ECO"), - "FEN": _("FEN"), - "PLIES": _("Plies"), - "WHITEELO": _("White elo"), - "BLACKELO": _("Black elo")}.get(key, key) + if key: + return {"EVENT": _("Event"), + "SITE": _("Site"), + "DATE": _("Date"), + "ROUND": _("Round"), + "WHITE": _("White"), + "BLACK": _("Black"), + "RESULT": _("Result"), + "COMMENT": _("Comment"), + "ECO": _("ECO"), + "FEN": _("FEN"), + "PLIES": _("Plies"), + "WHITEELO": _("White elo"), + "BLACKELO": _("Black elo")}.get(key, _F(key)) + else: + return "" def dicNomPiezas(): @@ -2127,7 +2133,7 @@ def dicTraining(): "Tactics by UNED chess school": _("Tactics by UNED chess school"), # "Decoy": _("Decoy"), - "Interception-blocade": _("Interception - blocade"), + "Interception-blocade": _("Interception-blocade"), # "Pawn promotion": _("Pawn promotion"), "X-ray attack": _("X-ray attack"), # "Pin": _("Pin"), @@ -2194,4 +2200,167 @@ def MicElo(): _("very combative"), _("very conservative"), _("counterattacker"), + _("combative"), + _("extremely precise"), + _("wide repertoire"), ] + + +def transsiberian(): + return { + "Yaroslavl": _("Yaroslavl"), + "Kirov": _("Kirov"), + "Perm": _("Perm"), + "Ekaterinburg": _("Ekaterinburg"), + "MOSCOW": _("MOSCOW"), + "Tyumen": _("Tyumen"), + "Omsk": _("Omsk"), + "Novosibirsk": _("Novosibirsk"), + "Krasnoyarsk": _("Krasnoyarsk"), + "Tayshet": _("Tayshet"), + "Irkutsk": _("Irkutsk"), + "Ulan-Ude": _("Ulan-Ude"), + "Chita": _("Chita"), + "Skovrodino": _("Skovrodino"), + "Belogorsk": _("Belogorsk"), + "Khabarovsk": _("Khabarovsk"), + "Vladivostok": _("Vladivostok"), + "Moscow": _("Moscow"), + "Khotkovo": _("Khotkovo"), + "Sergiyev Posad": _("Sergiyev Posad"), + "Alexandrov": _("Alexandrov"), + "Balakirevo": _("Balakirevo"), + "Berendeevo": _("Berendeevo"), + "Petrovskoye": _("Petrovskoye"), + "Rostov Yaroslavsky": _("Rostov Yaroslavsky"), + "Danilov": _("Danilov"), + "Lyubim": _("Lyubim"), + "Bui": _("Bui"), + "Galich": _("Galich"), + "Manturovo": _("Manturovo"), + "Sharya": _("Sharya"), + "Svetcha": _("Svetcha"), + "Kotelnich": _("Kotelnich"), + "Pozdino": _("Pozdino"), + "Bum-Kombinat": _("Bum-Kombinat"), + "Zuevka": _("Zuevka"), + "Yar": _("Yar"), + "Glazov": _("Glazov"), + "Balyezino": _("Balyezino"), + "Chepsta": _("Chepsta"), + "Vereshchagino": _("Vereshchagino"), + "Mendeleevo": _("Mendeleevo"), + "Chaikovskaya": _("Chaikovskaya"), + "Overyata": _("Overyata"), + "Ferma": _("Ferma"), + "Kungur": _("Kungur"), + "Shalya": _("Shalya"), + "Kuzino": _("Kuzino"), + "Krylosovo": _("Krylosovo"), + "Pervouralsk": _("Pervouralsk"), + "Yekaterinburg": _("Yekaterinburg"), + "Bogdanovich": _("Bogdanovich"), + "Kamyshlov": _("Kamyshlov"), + "Talitsa": _("Talitsa"), + "Yushala": _("Yushala"), + "Yalutorovsk": _("Yalutorovsk"), + "Ishim": _("Ishim"), + "Nazyvayevsk": _("Nazyvayevsk"), + "Kalachinsk": _("Kalachinsk"), + "Tatarsk": _("Tatarsk"), + "Barabinsk": _("Barabinsk"), + "Chulym": _("Chulym"), + "Ob": _("Ob"), + "Bolotnaya": _("Bolotnaya"), + "Yurga": _("Yurga"), + "Tayga": _("Tayga"), + "Anzhero-Sudzhensk": _("Anzhero-Sudzhensk"), + "Mariinsk": _("Mariinsk"), + "Bogotol": _("Bogotol"), + "Achinsk": _("Achinsk"), + "Chernorechensk": _("Chernorechensk"), + "Uyar": _("Uyar"), + "Zaozyornaya": _("Zaozyornaya"), + "Kansk-Yeniseysky": _("Kansk-Yeniseysky"), + "Ilanskaya": _("Ilanskaya"), + "Taishet": _("Taishet"), + "Razgon": _("Razgon"), + "Kamyshet": _("Kamyshet"), + "Nizhneudinsk": _("Nizhneudinsk"), + "Tulun": _("Tulun"), + "Kuytun": _("Kuytun"), + "Zima": _("Zima"), + "Kutulik": _("Kutulik"), + "Cheremkhovo": _("Cheremkhovo"), + "Polovina": _("Polovina"), + "Usolye-Sibirskoye": _("Usolye-Sibirskoye"), + "Telma": _("Telma"), + "Angarsk": _("Angarsk"), + "Meget": _("Meget"), + "Irkutsk-Sort": _("Irkutsk-Sort"), + "Slyudyanka": _("Slyudyanka"), + "Baykalsk": _("Baykalsk"), + "Vydrino": _("Vydrino"), + "Tankhoi": _("Tankhoi"), + "Mysovaya": _("Mysovaya"), + "Posolskaya": _("Posolskaya"), + "Selenginsk": _("Selenginsk"), + "Ulan Ude": _("Ulan Ude"), + "Onokhoy": _("Onokhoy"), + "Novoilinski": _("Novoilinski"), + "Petrovsk-Zabaykalsky": _("Petrovsk-Zabaykalsky"), + "Bada": _("Bada"), + "Khilok": _("Khilok"), + "Mogzon": _("Mogzon"), + "Sokhondo": _("Sokhondo"), + "Yablonovaya": _("Yablonovaya"), + "Darasun": _("Darasun"), + "Karymskaya": _("Karymskaya"), + "Onon": _("Onon"), + "Shilka-Pass.": _("Shilka-Pass."), + "Priiskovaya": _("Priiskovaya"), + "Kuenga": _("Kuenga"), + "Chernyshevsk-Zabaykalsky": _("Chernyshevsk-Zabaykalsky"), + "Bushuley": _("Bushuley"), + "Zilovo": _("Zilovo"), + "Ksenevskaya": _("Ksenevskaya"), + "Mogocha": _("Mogocha"), + "Amazar": _("Amazar"), + "Yerofei Pavlovich": _("Yerofei Pavlovich"), + "Urusha": _("Urusha"), + "Takhtamigda": _("Takhtamigda"), + "Skovorodino": _("Skovorodino"), + "Bolshoy Never": _("Bolshoy Never"), + "Magdagachi": _("Magdagachi"), + "Ushumun": _("Ushumun"), + "Shimanovskaya": _("Shimanovskaya"), + "Ledyanaya": _("Ledyanaya"), + "Svobodny": _("Svobodny"), + "Zavitaya": _("Zavitaya"), + "Bureya": _("Bureya"), + "Arkhara": _("Arkhara"), + "Obluchye": _("Obluchye"), + "Izvestkovaya": _("Izvestkovaya"), + "Bira": _("Bira"), + "Birobidzhan": _("Birobidzhan"), + "Volochayevka": _("Volochayevka"), + "Priamurskaya": _("Priamurskaya"), + "Verino": _("Verino"), + "Khor": _("Khor"), + "Vyazemskaya": _("Vyazemskaya"), + "Bikin": _("Bikin"), + "Dalnerechensk": _("Dalnerechensk"), + "Lazo": _("Lazo"), + "Spassk-Dalny": _("Spassk-Dalny"), + "Sibirtsevo": _("Sibirtsevo"), + "Ussuriysk": _("Ussuriysk"), + "Uglovaya": _("Uglovaya"), + } + + +def maps(): + return { + "Africa":_("Africa map"), + "WorldMap": _("World map"), + } + diff --git a/Code/Traducir.py b/Code/Traducir.py index 43f38df..39c07fc 100644 --- a/Code/Traducir.py +++ b/Code/Traducir.py @@ -4,10 +4,12 @@ def _F(txt): - return _(txt) + return _(txt) if txt else "" def _SP(clave): + if not clave: + return "" clave = clave.strip() t = _F(clave) if t == clave: @@ -21,6 +23,8 @@ def _SP(clave): def _X(clave, op1, op2=None, op3=None): + if not clave: + return "" resp = clave.replace("%1", op1) if op2: resp = resp.replace("%2", op2) diff --git a/Code/Util.py b/Code/Util.py index ab78b37..435a307 100644 --- a/Code/Util.py +++ b/Code/Util.py @@ -454,8 +454,7 @@ def __init__(self, dic=None): self.__setitem__(k, v) def __contains__(self, clave): - clu = clave.upper() - return clu in self._dic + return clave.upper() in self._dic def __len__(self): return len(self._keys) diff --git a/Code/XMotorRespuesta.py b/Code/XMotorRespuesta.py index eda240d..f7f8710 100644 --- a/Code/XMotorRespuesta.py +++ b/Code/XMotorRespuesta.py @@ -824,10 +824,25 @@ def mejorMovAjustadoNivel(self, nTipo): if sel <= t: return k - def mejorMovAjustadoSuperior(self, nivel, mindifpuntos, maxmate, aterrizaje): - + def mejorMovAjustadoBlundersNegativo(self, mindifpuntos, maxmate): + rm0 = self.liMultiPV[0] if self.mejorMovDetectaBlunders(None, mindifpuntos, maxmate): - return self.liMultiPV[0] + return rm0 + if rm0.puntosABS() < -10: + li = [] + for rm in self.liMultiPV: + if rm.puntosABS() == rm0.puntosABS(): + li.append(rm) + if len(li) == 1: + return rm0 + else: + return random.choice(li) + return None + + def mejorMovAjustadoSuperior(self, nivel, mindifpuntos, maxmate, aterrizaje): + resp = self.mejorMovAjustadoBlundersNegativo(mindifpuntos, maxmate) + if resp: + return resp # Buscamos una jugada positiva que no sea de mate # Si no la hay, cogemos el ultimo @@ -859,9 +874,9 @@ def mejorMovAjustadoSuperior(self, nivel, mindifpuntos, maxmate, aterrizaje): return li[0] if nLi < nivel else li[-nivel] def mejorMovAjustadoInferior(self, nivel, mindifpuntos, maxmate, aterrizaje): - - if self.mejorMovDetectaBlunders(None, mindifpuntos, maxmate): - return self.liMultiPV[0] + resp = self.mejorMovAjustadoBlundersNegativo(mindifpuntos, maxmate) + if resp: + return resp # Buscamos una jugada positiva que no sea de mate # Si no la hay, cogemos el ultimo @@ -891,9 +906,9 @@ def mejorMovAjustadoInferior(self, nivel, mindifpuntos, maxmate, aterrizaje): return li[-1] if nLi < nivel else li[nivel - 1] def mejorMovAjustadoSimilar(self, mindifpuntos, maxmate, aterrizaje): - - if self.mejorMovDetectaBlunders(None, mindifpuntos, maxmate): - return self.liMultiPV[0] + resp = self.mejorMovAjustadoBlundersNegativo(mindifpuntos, maxmate) + if resp: + return resp # Buscamos una jugada positiva que no sea de mate # Si no la hay, cogemos el ultimo @@ -925,7 +940,6 @@ def mejorMovAjustadoSimilar(self, mindifpuntos, maxmate, aterrizaje): def mejorMovAjustado(self, nTipo): if self.liMultiPV: - rmSel = None aterrizaje = 50 siPersonalidad = nTipo >= 1000 # Necesario para grabar los puntos diff --git a/Engines/Windows/deepfish/srcD/book.cpp b/Engines/Windows/deepfish/srcD/book.cpp deleted file mode 100644 index a0e5865..0000000 --- a/Engines/Windows/deepfish/srcD/book.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish 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, see . -*/ - -/* - The code in this file is based on the opening book code in PolyGlot - by Fabien Letouzey. PolyGlot is available under the GNU General - Public License, and can be downloaded from http://wbec-ridderkerk.nl -*/ - -#include -#include - -#include "book.h" -#include "misc.h" -#include "movegen.h" - -using namespace std; - -namespace { - - // A Polyglot book is a series of "entries" of 16 bytes. All integers are - // stored in big-endian format, with the highest byte first (regardless of - // size). The entries are ordered according to the key in ascending order. - struct Entry { - uint64_t key; - uint16_t move; - uint16_t count; - uint32_t learn; - }; - - // Random numbers from PolyGlot, used to compute book hash keys - const union { - Key PolyGlotRandoms[781]; - struct { - Key psq[12][64]; // [piece][square] - Key castling[4]; // [castling flag] - Key enpassant[8]; // [file] - Key turn; - } Zobrist; - } PG = {{ - 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, - 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, - 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, - 0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL, - 0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL, - 0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL, - 0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL, - 0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL, - 0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL, - 0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL, - 0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL, - 0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL, - 0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL, - 0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL, - 0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL, - 0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL, - 0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL, - 0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL, - 0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL, - 0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL, - 0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL, - 0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL, - 0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL, - 0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL, - 0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL, - 0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL, - 0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL, - 0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL, - 0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL, - 0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL, - 0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL, - 0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL, - 0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL, - 0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL, - 0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL, - 0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL, - 0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL, - 0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL, - 0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL, - 0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL, - 0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL, - 0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL, - 0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL, - 0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL, - 0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL, - 0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL, - 0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL, - 0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL, - 0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL, - 0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL, - 0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL, - 0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL, - 0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL, - 0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL, - 0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL, - 0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL, - 0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL, - 0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL, - 0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL, - 0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL, - 0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL, - 0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL, - 0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL, - 0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL, - 0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL, - 0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL, - 0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL, - 0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL, - 0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL, - 0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL, - 0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL, - 0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL, - 0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL, - 0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL, - 0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL, - 0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL, - 0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL, - 0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL, - 0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL, - 0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL, - 0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL, - 0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL, - 0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL, - 0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL, - 0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL, - 0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL, - 0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL, - 0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL, - 0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL, - 0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL, - 0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL, - 0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL, - 0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL, - 0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL, - 0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL, - 0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL, - 0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL, - 0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL, - 0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL, - 0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL, - 0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL, - 0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL, - 0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL, - 0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL, - 0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL, - 0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL, - 0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL, - 0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL, - 0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL, - 0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL, - 0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL, - 0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL, - 0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL, - 0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL, - 0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL, - 0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL, - 0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL, - 0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL, - 0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL, - 0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL, - 0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL, - 0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL, - 0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL, - 0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL, - 0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL, - 0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL, - 0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL, - 0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL, - 0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL, - 0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL, - 0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL, - 0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL, - 0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL, - 0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL, - 0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL, - 0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL, - 0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL, - 0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL, - 0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL, - 0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL, - 0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL, - 0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL, - 0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL, - 0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL, - 0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL, - 0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL, - 0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL, - 0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL, - 0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL, - 0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL, - 0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL, - 0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL, - 0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL, - 0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL, - 0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL, - 0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL, - 0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL, - 0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL, - 0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL, - 0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL, - 0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL, - 0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL, - 0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL, - 0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL, - 0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL, - 0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL, - 0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL, - 0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL, - 0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL, - 0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL, - 0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL, - 0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL, - 0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL, - 0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL, - 0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL, - 0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL, - 0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL, - 0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL, - 0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL, - 0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL, - 0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL, - 0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL, - 0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL, - 0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL, - 0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL, - 0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL, - 0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL, - 0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL, - 0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL, - 0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL, - 0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL, - 0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL, - 0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL, - 0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL, - 0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL, - 0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL, - 0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL, - 0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL, - 0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL, - 0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL, - 0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL, - 0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL, - 0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL, - 0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL, - 0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL, - 0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL, - 0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL, - 0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL, - 0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL, - 0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL, - 0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL, - 0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL, - 0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL, - 0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL, - 0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL, - 0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL, - 0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL, - 0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL, - 0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL, - 0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL, - 0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL, - 0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL, - 0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL, - 0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL, - 0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL, - 0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL, - 0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL, - 0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL, - 0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL, - 0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL, - 0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL, - 0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL, - 0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL, - 0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL, - 0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL, - 0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL, - 0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL, - 0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL, - 0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL, - 0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL, - 0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL, - 0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL, - 0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL, - 0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL, - 0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL, - 0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL, - 0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL, - 0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL, - 0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL, - 0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL, - 0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL, - 0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL, - 0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL, - 0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL, - 0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL, - 0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL, - 0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL, - 0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL, - 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL, - 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL, - 0xF8D626AAAF278509ULL - }}; - - // polyglot_key() returns the PolyGlot hash key of the given position - Key polyglot_key(const Position& pos) { - - Key key = 0; - Bitboard b = pos.pieces(); - - while (b) - { - Square s = pop_lsb(&b); - Piece pc = pos.piece_on(s); - - // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 - key ^= PG.Zobrist.psq[2 * (type_of(pc) - 1) + (color_of(pc) == WHITE)][s]; - } - - b = pos.can_castle(ANY_CASTLING); - - while (b) - key ^= PG.Zobrist.castling[pop_lsb(&b)]; - - if (pos.ep_square() != SQ_NONE) - key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())]; - - if (pos.side_to_move() == WHITE) - key ^= PG.Zobrist.turn; - - return key; - } - -} // namespace - -PolyglotBook::PolyglotBook() : rng(now() % 10000) {} - -PolyglotBook::~PolyglotBook() { if (is_open()) close(); } - - -/// operator>>() reads sizeof(T) chars from the file's binary byte stream and -/// converts them into a number of type T. A Polyglot book stores numbers in -/// big-endian format. - -template PolyglotBook& PolyglotBook::operator>>(T& n) { - - n = 0; - for (size_t i = 0; i < sizeof(T); ++i) - n = T((n << 8) + ifstream::get()); - - return *this; -} - -template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) { - return *this >> e.key >> e.move >> e.count >> e.learn; -} - - -/// open() tries to open a book file with the given name after closing any -/// existing one. - -bool PolyglotBook::open(const char* fName) { - - if (is_open()) // Cannot close an already closed file - close(); - - ifstream::open(fName, ifstream::in | ifstream::binary); - - fileName = is_open() ? fName : ""; - ifstream::clear(); // Reset any error flag to allow a retry ifstream::open() - return !fileName.empty(); -} - - -/// probe() tries to find a book move for the given position. If no move is -/// found, it returns MOVE_NONE. If pickBest is true, then it always returns -/// the highest-rated move, otherwise it randomly chooses one based on the -/// move score. - -Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) { - - if (fileName != fName && !open(fName.c_str())) - return MOVE_NONE; - - Entry e; - uint16_t best = 0; - unsigned sum = 0; - Move move = MOVE_NONE; - Key key = polyglot_key(pos); - - seekg(find_first(key) * sizeof(Entry), ios_base::beg); - - while (*this >> e, e.key == key && good()) - { - best = max(best, e.count); - sum += e.count; - - // Choose book move according to its score. If a move has a very high - // score it has a higher probability of being choosen than a move with - // a lower score. Note that first entry is always chosen. - if ( (!pickBest && sum && rng.rand() % sum < e.count) - || (pickBest && e.count == best)) - move = Move(e.move); - } - - if (!move) - return MOVE_NONE; - - // A PolyGlot book move is encoded as follows: - // - // bit 0- 5: destination square (from 0 to 63) - // bit 6-11: origin square (from 0 to 63) - // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) - // - // Castling moves follow the "king captures rook" representation. If a book - // move is a promotion, we have to convert it to our representation and in - // all other cases, we can directly compare with a Move after having masked - // out the special Move flags (bit 14-15) that are not supported by PolyGlot. - int pt = (move >> 12) & 7; - if (pt) - move = make(from_sq(move), to_sq(move), PieceType(pt + 1)); - - // Add 'special move' flags and verify it is legal - for (const auto& m : MoveList(pos)) - if (move == (m ^ type_of(m))) - return m; - - return MOVE_NONE; -} - - -/// find_first() takes a book key as input, and does a binary search through -/// the book file for the given key. Returns the index of the leftmost book -/// entry with the same key as the input. - -size_t PolyglotBook::find_first(Key key) { - - seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size - - size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1; - Entry e; - - assert(low <= high); - - while (low < high && good()) - { - mid = (low + high) / 2; - - assert(mid >= low && mid < high); - - seekg(mid * sizeof(Entry), ios_base::beg); - *this >> e; - - if (key <= e.key) - high = mid; - else - low = mid + 1; - } - - assert(low == high); - - return low; -} diff --git a/Engines/Windows/deepfish/srcD/book.h b/Engines/Windows/deepfish/srcD/book.h deleted file mode 100644 index 619bed3..0000000 --- a/Engines/Windows/deepfish/srcD/book.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish 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, see . -*/ - -#ifndef BOOK_H_INCLUDED -#define BOOK_H_INCLUDED - -#include -#include - -#include "misc.h" -#include "position.h" - -class PolyglotBook : private std::ifstream { -public: - PolyglotBook(); - ~PolyglotBook(); - Move probe(const Position& pos, const std::string& fName, bool pickBest); - -private: - template PolyglotBook& operator>>(T& n); - - bool open(const char* fName); - size_t find_first(Key key); - - PRNG rng; - std::string fileName; -}; - -#endif // #ifndef BOOK_H_INCLUDED diff --git a/Engines/Windows/deepfish/srcD/opt.cpp b/Engines/Windows/deepfish/srcD/opt.cpp deleted file mode 100644 index b64557e..0000000 --- a/Engines/Windows/deepfish/srcD/opt.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright(C)2004-2008 Tord Romstad (Glaurung author) - Copyright(C)2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation,either version 3 of the License, or - (at your option) any later version. - - Stockfish 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, see . -*/ - -#include - -#include "thread.h" -#include "tt.h" - -using namespace std; - -#define TRUE 1 -#define FALSE 0 - -#define MEMALIGN(a, b, c) a = _aligned_malloc (c, b) -#define ALIGNED_FREE(x) _aligned_free (x) - -int large_use; - -#ifndef _WIN32 // Linux -#include -#include - -static int num; - -void SETUP_PRIVILEGES(){} - -void CREATE_MEM(void** A,int align,uint64_t size) -{ - large_use=FALSE; - - num=shmget(IPC_PRIVATE,size,IPC_CREAT|SHM_R|SHM_W|SHM_HUGETLB); - if(num==-1) - { - printf("info string LargePages FAILED %llu Mb\n",size>>20); - MEMALIGN((*A),align,size); - } - else - { - (*A)=shmat(num,NULL,0x0); - large_use=TRUE; - printf("info string LargePages OK %llu Mb\n",size>>20); - std::cout<<"info string HUGELTB "<<(size>>20)<>20); - } -} - -void FREE_MEM(void* A) -{ - if(!A) - return; - if(!large_use) - { - ALIGNED_FREE(A); - return; - } - shmdt(A); - shmctl(num,IPC_RMID,NULL); - large_use=FALSE; -} - -void SETUP_PRIVILEGES(){} - -#else - -void CREATE_MEM(void** A,int align,uint64_t size) -{ - large_use=FALSE; - (*A)=VirtualAlloc(NULL,size,MEM_LARGE_PAGES|MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE); - if((*A)) - { - large_use=TRUE; - printf("info string %llu Mb LargePages\n",size>>20); - } - else - { - printf("info string %llu Mb(no LargePages)\n",size>>20); - MEMALIGN((*A),align,size); - } -} - -void CREATE_MEM2(void** A,uint64_t size) -{ - large_use=FALSE; - (*A)=VirtualAlloc(NULL,size,MEM_LARGE_PAGES|MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE); - if((*A)) - { - large_use=TRUE; - printf("info string LargePages %llu Mb\n",size>>20); - } -} - -void FREE_MEM(void* A) -{ - if(!A) - return; - if(!large_use) - ALIGNED_FREE(A); - else - { - VirtualFree(A,0,MEM_RELEASE); - large_use=FALSE; - } -} - -void SETUP_PRIVILEGES() -{ - HANDLE TH,PROC; - TOKEN_PRIVILEGES tp; - - PROC=GetCurrentProcess(); - OpenProcessToken(PROC,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&TH); - LookupPrivilegeValue(NULL,TEXT("SeLockMemoryPrivilege"),&tp.Privileges[0].Luid); - tp.PrivilegeCount=1; - tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; - AdjustTokenPrivileges(TH,FALSE,&tp,0,NULL,0); - CloseHandle(TH); -} -#endif diff --git a/Engines/Windows/deepfish/srcD/psqt.cpp b/Engines/Windows/deepfish/srcD/psqt.cpp deleted file mode 100644 index 60180ad..0000000 --- a/Engines/Windows/deepfish/srcD/psqt.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish 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, see . -*/ - -#include - -#include "types.h" - -Value PieceValue[PHASE_NB][PIECE_NB] = { -{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, -{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; - -namespace PSQT { - -#define S(mg, eg) make_score(mg, eg) - -// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece -// type on a given square a (middlegame, endgame) score pair is assigned. Table -// is defined for files A..D and white side: it is symmetric for black side and -// second half of the files. -const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { - { }, - { // Pawn - { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, - { S(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) }, - { S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) }, - { S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) }, - { S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) }, - { S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) }, - { S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) } - }, - { // Knight - { S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) }, - { S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) }, - { S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) }, - { S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) }, - { S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) }, - { S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) }, - { S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) }, - { S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) } - }, - { // Bishop - { S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) }, - { S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) }, - { S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) }, - { S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) }, - { S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) }, - { S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) }, - { S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) }, - { S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) } - }, - { // Rook - { S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) }, - { S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) }, - { S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) }, - { S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) }, - { S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) }, - { S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) }, - { S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) }, - { S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) } - }, - { // Queen - { S( 0,-70), S(-3,-57), S(-4,-41), S(-1,-29) }, - { S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) }, - { S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) }, - { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) }, - { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) }, - { S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) }, - { S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) }, - { S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) } - }, - { // King - { S(291, 28), S(344, 76), S(294,103), S(219,112) }, - { S(289, 70), S(329,119), S(263,170), S(205,159) }, - { S(226,109), S(271,164), S(202,195), S(136,191) }, - { S(204,131), S(212,194), S(175,194), S(137,204) }, - { S(177,132), S(205,187), S(143,224), S( 94,227) }, - { S(147,118), S(188,178), S(113,199), S( 70,197) }, - { S(116, 72), S(158,121), S( 93,142), S( 48,161) }, - { S( 94, 30), S(120, 76), S( 78,101), S( 31,111) } - } -}; - -#undef S - -Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - -// init() initializes piece-square tables: the white halves of the tables are -// copied from Bonus[] adding the piece value, then the black halves of the -// tables are initialized by flipping and changing the sign of the white scores. -void init() { - - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; - PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; - - Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); - - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - File f = std::min(file_of(s), FILE_H - file_of(s)); - psq[WHITE][pt][ s] = v + Bonus[pt][rank_of(s)][f]; - psq[BLACK][pt][~s] = -psq[WHITE][pt][s]; - } - } -} - -} // namespace PSQT diff --git a/Engines/Windows/deepfish/srcD/syzygy/tbcore.cpp b/Engines/Windows/deepfish/srcD/syzygy/tbcore.cpp deleted file mode 100644 index f45da95..0000000 --- a/Engines/Windows/deepfish/srcD/syzygy/tbcore.cpp +++ /dev/null @@ -1,1378 +0,0 @@ -/* - Copyright (c) 2011-2013 Ronald de Man - This file may be redistributed and/or modified without restrictions. - - tbcore.c contains engine-independent routines of the tablebase probing code. - This file should not need too much adaptation to add tablebase probing to - a particular engine, provided the engine is written in C or C++. -*/ - -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif -#include "tbcore.h" - -#define TBMAX_PIECE 254 -#define TBMAX_PAWN 256 -#define HSHMAX 5 - -#define Swap(a,b) {int tmp=a;a=b;b=tmp;} - -#define TB_PAWN 1 -#define TB_KNIGHT 2 -#define TB_BISHOP 3 -#define TB_ROOK 4 -#define TB_QUEEN 5 -#define TB_KING 6 - -#define TB_WPAWN TB_PAWN -#define TB_BPAWN (TB_PAWN | 8) - -static LOCK_T TB_mutex; - -static bool initialized = false; -static int num_paths = 0; -static char *path_string = NULL; -static char **paths = NULL; - -static int TBnum_piece, TBnum_pawn; -static struct TBEntry_piece TB_piece[TBMAX_PIECE]; -static struct TBEntry_pawn TB_pawn[TBMAX_PAWN]; - -static struct TBHashEntry TB_hash[1 << TBHASHBITS][HSHMAX]; - -#define DTZ_ENTRIES 64 - -static struct DTZTableEntry DTZ_table[DTZ_ENTRIES]; - -static void init_indices(void); -static uint64 calc_key_from_pcs(int *pcs, int mirror); -static void free_wdl_entry(struct TBEntry *entry); -static void free_dtz_entry(struct TBEntry *entry); - -static FD open_tb(const char *str, const char *suffix) -{ - int i; - FD fd; - char file[256]; - - for (i = 0; i < num_paths; i++) { - strcpy(file, paths[i]); - strcat(file, "/"); - strcat(file, str); - strcat(file, suffix); -#ifndef _WIN32 - fd = open(file, O_RDONLY); -#else - fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#endif - if (fd != FD_ERR) return fd; - } - return FD_ERR; -} - -static void close_tb(FD fd) -{ -#ifndef _WIN32 - close(fd); -#else - CloseHandle(fd); -#endif -} - -static char *map_file(const char *name, const char *suffix, uint64 *mapping) -{ - FD fd = open_tb(name, suffix); - if (fd == FD_ERR) - return NULL; -#ifndef _WIN32 - struct stat statbuf; - fstat(fd, &statbuf); - *mapping = statbuf.st_size; - char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, - MAP_SHARED, fd, 0); - if (data == (char *)(-1)) { - printf("Could not mmap() %s.\n", name); - exit(1); - } -#else - DWORD size_low, size_high; - size_low = GetFileSize(fd, &size_high); -// *size = ((uint64)size_high) << 32 | ((uint64)size_low); - HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, - NULL); - if (map == NULL) { - printf("CreateFileMapping() failed.\n"); - exit(1); - } - *mapping = (uint64)map; - char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); - if (data == NULL) { - printf("MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); - exit(1); - } -#endif - close_tb(fd); - return data; -} - -#ifndef _WIN32 -static void unmap_file(char *data, uint64 size) -{ - if (!data) return; - munmap(data, size); -} -#else -static void unmap_file(char *data, uint64 mapping) -{ - if (!data) return; - UnmapViewOfFile(data); - CloseHandle((HANDLE)mapping); -} -#endif - -static void add_to_hash(struct TBEntry *ptr, uint64 key) -{ - int i, hshidx; - - hshidx = key >> (64 - TBHASHBITS); - i = 0; - while (i < HSHMAX && TB_hash[hshidx][i].ptr) - i++; - if (i == HSHMAX) { - printf("HSHMAX too low!\n"); - exit(1); - } else { - TB_hash[hshidx][i].key = key; - TB_hash[hshidx][i].ptr = ptr; - } -} - -static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'}; - -static void init_tb(char *str) -{ - FD fd; - struct TBEntry *entry; - int i, j, pcs[16]; - uint64 key, key2; - int color; - char *s; - - fd = open_tb(str, WDLSUFFIX); - if (fd == FD_ERR) return; - close_tb(fd); - - for (i = 0; i < 16; i++) - pcs[i] = 0; - color = 0; - for (s = str; *s; s++) - switch (*s) { - case 'P': - pcs[TB_PAWN | color]++; - break; - case 'N': - pcs[TB_KNIGHT | color]++; - break; - case 'B': - pcs[TB_BISHOP | color]++; - break; - case 'R': - pcs[TB_ROOK | color]++; - break; - case 'Q': - pcs[TB_QUEEN | color]++; - break; - case 'K': - pcs[TB_KING | color]++; - break; - case 'v': - color = 0x08; - break; - } - for (i = 0; i < 8; i++) - if (pcs[i] != pcs[i+8]) - break; - key = calc_key_from_pcs(pcs, 0); - key2 = calc_key_from_pcs(pcs, 1); - if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { - if (TBnum_piece == TBMAX_PIECE) { - printf("TBMAX_PIECE limit too low!\n"); - exit(1); - } - entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; - } else { - if (TBnum_pawn == TBMAX_PAWN) { - printf("TBMAX_PAWN limit too low!\n"); - exit(1); - } - entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; - } - entry->key = key; - entry->ready = 0; - entry->num = 0; - for (i = 0; i < 16; i++) - entry->num += (ubyte)pcs[i]; - entry->symmetric = (key == key2); - entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); - if (entry->num > Tablebases::MaxCardinality) - Tablebases::MaxCardinality = entry->num; - - if (entry->has_pawns) { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - ptr->pawns[0] = (ubyte)pcs[TB_WPAWN]; - ptr->pawns[1] = (ubyte)pcs[TB_BPAWN]; - if (pcs[TB_BPAWN] > 0 - && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { - ptr->pawns[0] = (ubyte)pcs[TB_BPAWN]; - ptr->pawns[1] = (ubyte)pcs[TB_WPAWN]; - } - } else { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - for (i = 0, j = 0; i < 16; i++) - if (pcs[i] == 1) j++; - if (j >= 3) ptr->enc_type = 0; - else if (j == 2) ptr->enc_type = 2; - else { /* only for suicide */ - j = 16; - for (i = 0; i < 16; i++) { - if (pcs[i] < j && pcs[i] > 1) j = pcs[i]; - ptr->enc_type = ubyte(1 + j); - } - } - } - add_to_hash(entry, key); - if (key2 != key) add_to_hash(entry, key2); -} - -void Tablebases::init(const std::string& path) -{ - char str[16]; - int i, j, k, l; - - if (initialized) { - free(path_string); - free(paths); - struct TBEntry *entry; - for (i = 0; i < TBnum_piece; i++) { - entry = (struct TBEntry *)&TB_piece[i]; - free_wdl_entry(entry); - } - for (i = 0; i < TBnum_pawn; i++) { - entry = (struct TBEntry *)&TB_pawn[i]; - free_wdl_entry(entry); - } - for (i = 0; i < DTZ_ENTRIES; i++) - if (DTZ_table[i].entry) - free_dtz_entry(DTZ_table[i].entry); - } else { - init_indices(); - initialized = true; - } - - const char *p = path.c_str(); - if (strlen(p) == 0 || !strcmp(p, "")) return; - path_string = (char *)malloc(strlen(p) + 1); - strcpy(path_string, p); - num_paths = 0; - for (i = 0;; i++) { - if (path_string[i] != SEP_CHAR) - num_paths++; - while (path_string[i] && path_string[i] != SEP_CHAR) - i++; - if (!path_string[i]) break; - path_string[i] = 0; - } - paths = (char **)malloc(num_paths * sizeof(char *)); - for (i = j = 0; i < num_paths; i++) { - while (!path_string[j]) j++; - paths[i] = &path_string[j]; - while (path_string[j]) j++; - } - - LOCK_INIT(TB_mutex); - - TBnum_piece = TBnum_pawn = 0; - MaxCardinality = 0; - - for (i = 0; i < (1 << TBHASHBITS); i++) - for (j = 0; j < HSHMAX; j++) { - TB_hash[i][j].key = 0ULL; - TB_hash[i][j].ptr = NULL; - } - - for (i = 0; i < DTZ_ENTRIES; i++) - DTZ_table[i].entry = NULL; - - for (i = 1; i < 6; i++) { - sprintf(str, "K%cvK", pchr[i]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) { - sprintf(str, "K%cvK%c", pchr[i], pchr[j]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) { - sprintf(str, "K%c%cvK", pchr[i], pchr[j]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = 1; k < 6; k++) { - sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) { - sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = i; k < 6; k++) - for (l = (i == k) ? j : k; l < 6; l++) { - sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) - for (l = 1; l < 6; l++) { - sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) - for (l = k; l < 6; l++) { - sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - - printf("info string Found %d tablebases.\n", TBnum_piece + TBnum_pawn); -} - -static const signed char offdiag[] = { - 0,-1,-1,-1,-1,-1,-1,-1, - 1, 0,-1,-1,-1,-1,-1,-1, - 1, 1, 0,-1,-1,-1,-1,-1, - 1, 1, 1, 0,-1,-1,-1,-1, - 1, 1, 1, 1, 0,-1,-1,-1, - 1, 1, 1, 1, 1, 0,-1,-1, - 1, 1, 1, 1, 1, 1, 0,-1, - 1, 1, 1, 1, 1, 1, 1, 0 -}; - -static const ubyte triangle[] = { - 6, 0, 1, 2, 2, 1, 0, 6, - 0, 7, 3, 4, 4, 3, 7, 0, - 1, 3, 8, 5, 5, 8, 3, 1, - 2, 4, 5, 9, 9, 5, 4, 2, - 2, 4, 5, 9, 9, 5, 4, 2, - 1, 3, 8, 5, 5, 8, 3, 1, - 0, 7, 3, 4, 4, 3, 7, 0, - 6, 0, 1, 2, 2, 1, 0, 6 -}; - -static const ubyte invtriangle[] = { - 1, 2, 3, 10, 11, 19, 0, 9, 18, 27 -}; - -static const ubyte invdiag[] = { - 0, 9, 18, 27, 36, 45, 54, 63, - 7, 14, 21, 28, 35, 42, 49, 56 -}; - -static const ubyte flipdiag[] = { - 0, 8, 16, 24, 32, 40, 48, 56, - 1, 9, 17, 25, 33, 41, 49, 57, - 2, 10, 18, 26, 34, 42, 50, 58, - 3, 11, 19, 27, 35, 43, 51, 59, - 4, 12, 20, 28, 36, 44, 52, 60, - 5, 13, 21, 29, 37, 45, 53, 61, - 6, 14, 22, 30, 38, 46, 54, 62, - 7, 15, 23, 31, 39, 47, 55, 63 -}; - -static const ubyte lower[] = { - 28, 0, 1, 2, 3, 4, 5, 6, - 0, 29, 7, 8, 9, 10, 11, 12, - 1, 7, 30, 13, 14, 15, 16, 17, - 2, 8, 13, 31, 18, 19, 20, 21, - 3, 9, 14, 18, 32, 22, 23, 24, - 4, 10, 15, 19, 22, 33, 25, 26, - 5, 11, 16, 20, 23, 25, 34, 27, - 6, 12, 17, 21, 24, 26, 27, 35 -}; - -static const ubyte diag[] = { - 0, 0, 0, 0, 0, 0, 0, 8, - 0, 1, 0, 0, 0, 0, 9, 0, - 0, 0, 2, 0, 0, 10, 0, 0, - 0, 0, 0, 3, 11, 0, 0, 0, - 0, 0, 0, 12, 4, 0, 0, 0, - 0, 0, 13, 0, 0, 5, 0, 0, - 0, 14, 0, 0, 0, 0, 6, 0, - 15, 0, 0, 0, 0, 0, 0, 7 -}; - -static const ubyte flap[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 12, 18, 18, 12, 6, 0, - 1, 7, 13, 19, 19, 13, 7, 1, - 2, 8, 14, 20, 20, 14, 8, 2, - 3, 9, 15, 21, 21, 15, 9, 3, - 4, 10, 16, 22, 22, 16, 10, 4, - 5, 11, 17, 23, 23, 17, 11, 5, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ubyte ptwist[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 47, 35, 23, 11, 10, 22, 34, 46, - 45, 33, 21, 9, 8, 20, 32, 44, - 43, 31, 19, 7, 6, 18, 30, 42, - 41, 29, 17, 5, 4, 16, 28, 40, - 39, 27, 15, 3, 2, 14, 26, 38, - 37, 25, 13, 1, 0, 12, 24, 36, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ubyte invflap[] = { - 8, 16, 24, 32, 40, 48, - 9, 17, 25, 33, 41, 49, - 10, 18, 26, 34, 42, 50, - 11, 19, 27, 35, 43, 51 -}; - -static const ubyte invptwist[] = { - 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11, - 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10, - 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9, - 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8 -}; - -static const ubyte file_to_file[] = { - 0, 1, 2, 3, 3, 2, 1, 0 -}; - -static const short KK_idx[10][64] = { - { -1, -1, -1, 0, 1, 2, 3, 4, - -1, -1, -1, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57 }, - { 58, -1, -1, -1, 59, 60, 61, 62, - 63, -1, -1, -1, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 99, - 100,101,102,103,104,105,106,107, - 108,109,110,111,112,113,114,115}, - {116,117, -1, -1, -1,118,119,120, - 121,122, -1, -1, -1,123,124,125, - 126,127,128,129,130,131,132,133, - 134,135,136,137,138,139,140,141, - 142,143,144,145,146,147,148,149, - 150,151,152,153,154,155,156,157, - 158,159,160,161,162,163,164,165, - 166,167,168,169,170,171,172,173 }, - {174, -1, -1, -1,175,176,177,178, - 179, -1, -1, -1,180,181,182,183, - 184, -1, -1, -1,185,186,187,188, - 189,190,191,192,193,194,195,196, - 197,198,199,200,201,202,203,204, - 205,206,207,208,209,210,211,212, - 213,214,215,216,217,218,219,220, - 221,222,223,224,225,226,227,228 }, - {229,230, -1, -1, -1,231,232,233, - 234,235, -1, -1, -1,236,237,238, - 239,240, -1, -1, -1,241,242,243, - 244,245,246,247,248,249,250,251, - 252,253,254,255,256,257,258,259, - 260,261,262,263,264,265,266,267, - 268,269,270,271,272,273,274,275, - 276,277,278,279,280,281,282,283 }, - {284,285,286,287,288,289,290,291, - 292,293, -1, -1, -1,294,295,296, - 297,298, -1, -1, -1,299,300,301, - 302,303, -1, -1, -1,304,305,306, - 307,308,309,310,311,312,313,314, - 315,316,317,318,319,320,321,322, - 323,324,325,326,327,328,329,330, - 331,332,333,334,335,336,337,338 }, - { -1, -1,339,340,341,342,343,344, - -1, -1,345,346,347,348,349,350, - -1, -1,441,351,352,353,354,355, - -1, -1, -1,442,356,357,358,359, - -1, -1, -1, -1,443,360,361,362, - -1, -1, -1, -1, -1,444,363,364, - -1, -1, -1, -1, -1, -1,445,365, - -1, -1, -1, -1, -1, -1, -1,446 }, - { -1, -1, -1,366,367,368,369,370, - -1, -1, -1,371,372,373,374,375, - -1, -1, -1,376,377,378,379,380, - -1, -1, -1,447,381,382,383,384, - -1, -1, -1, -1,448,385,386,387, - -1, -1, -1, -1, -1,449,388,389, - -1, -1, -1, -1, -1, -1,450,390, - -1, -1, -1, -1, -1, -1, -1,451 }, - {452,391,392,393,394,395,396,397, - -1, -1, -1, -1,398,399,400,401, - -1, -1, -1, -1,402,403,404,405, - -1, -1, -1, -1,406,407,408,409, - -1, -1, -1, -1,453,410,411,412, - -1, -1, -1, -1, -1,454,413,414, - -1, -1, -1, -1, -1, -1,455,415, - -1, -1, -1, -1, -1, -1, -1,456 }, - {457,416,417,418,419,420,421,422, - -1,458,423,424,425,426,427,428, - -1, -1, -1, -1, -1,429,430,431, - -1, -1, -1, -1, -1,432,433,434, - -1, -1, -1, -1, -1,435,436,437, - -1, -1, -1, -1, -1,459,438,439, - -1, -1, -1, -1, -1, -1,460,440, - -1, -1, -1, -1, -1, -1, -1,461 } -}; - -static int binomial[5][64]; -static int pawnidx[5][24]; -static int pfactor[5][4]; - -static void init_indices(void) -{ - int i, j, k; - -// binomial[k-1][n] = Bin(n, k) - for (i = 0; i < 5; i++) - for (j = 0; j < 64; j++) { - int f = j; - int l = 1; - for (k = 1; k <= i; k++) { - f *= (j - k); - l *= (k + 1); - } - binomial[i][j] = f / l; - } - - for (i = 0; i < 5; i++) { - int s = 0; - for (j = 0; j < 6; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][0] = s; - s = 0; - for (; j < 12; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][1] = s; - s = 0; - for (; j < 18; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][2] = s; - s = 0; - for (; j < 24; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][3] = s; - } -} - -static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) -{ - uint64 idx; - int i, j, k, m, l, p; - int n = ptr->num; - - if (pos[0] & 0x04) { - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - } - if (pos[0] & 0x20) { - for (i = 0; i < n; i++) - pos[i] ^= 0x38; - } - - for (i = 0; i < n; i++) - if (offdiag[pos[i]]) break; - if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i]]; - - switch (ptr->enc_type) { - - case 0: /* 111 */ - i = (pos[1] > pos[0]); - j = (pos[2] > pos[0]) + (pos[2] > pos[1]); - - if (offdiag[pos[0]]) - idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); - else if (offdiag[pos[1]]) - idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; - else if (offdiag[pos[2]]) - idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; - else - idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); - i = 3; - break; - - case 1: /* K3 */ - j = (pos[2] > pos[0]) + (pos[2] > pos[1]); - - idx = KK_idx[triangle[pos[0]]][pos[1]]; - if (idx < 441) - idx = idx + 441 * (pos[2] - j); - else { - idx = 441*62 + (idx - 441) + 21 * lower[pos[2]]; - if (!offdiag[pos[2]]) - idx -= j * 21; - } - i = 3; - break; - - default: /* K2 */ - idx = KK_idx[triangle[pos[0]]][pos[1]]; - i = 2; - break; - } - idx *= factor[0]; - - for (; i < n;) { - int t = norm[i]; - for (j = i; j < i + t; j++) - for (k = j + 1; k < i + t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - int s = 0; - for (m = i; m < i + t; m++) { - p = pos[m]; - for (l = 0, j = 0; l < i; l++) - j += (p > pos[l]); - s += binomial[m - i][p - j]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i += t; - } - - return idx; -} - -// determine file of leftmost pawn and sort pawns -static int pawn_file(struct TBEntry_pawn *ptr, int *pos) -{ - int i; - - for (i = 1; i < ptr->pawns[0]; i++) - if (flap[pos[0]] > flap[pos[i]]) - Swap(pos[0], pos[i]); - - return file_to_file[pos[0] & 0x07]; -} - -static uint64 encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) -{ - uint64 idx; - int i, j, k, m, s, t; - int n = ptr->num; - - if (pos[0] & 0x04) - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - - for (i = 1; i < ptr->pawns[0]; i++) - for (j = i + 1; j < ptr->pawns[0]; j++) - if (ptwist[pos[i]] < ptwist[pos[j]]) - Swap(pos[i], pos[j]); - - t = ptr->pawns[0] - 1; - idx = pawnidx[t][flap[pos[0]]]; - for (i = t; i > 0; i--) - idx += binomial[t - i][ptwist[pos[i]]]; - idx *= factor[0]; - -// remaining pawns - i = ptr->pawns[0]; - t = i + ptr->pawns[1]; - if (t > i) { - for (j = i; j < t; j++) - for (k = j + 1; k < t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - s = 0; - for (m = i; m < t; m++) { - int p = pos[m]; - for (k = 0, j = 0; k < i; k++) - j += (p > pos[k]); - s += binomial[m - i][p - j - 8]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i = t; - } - - for (; i < n;) { - t = norm[i]; - for (j = i; j < i + t; j++) - for (k = j + 1; k < i + t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - s = 0; - for (m = i; m < i + t; m++) { - int p = pos[m]; - for (k = 0, j = 0; k < i; k++) - j += (p > pos[k]); - s += binomial[m - i][p - j]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i += t; - } - - return idx; -} - -// place k like pieces on n squares -static int subfactor(int k, int n) -{ - int i, f, l; - - f = n; - l = 1; - for (i = 1; i < k; i++) { - f *= n - i; - l *= i + 1; - } - - return f / l; -} - -static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) -{ - int i, k, n; - uint64 f; - static int pivfac[] = { 31332, 28056, 462 }; - - n = 64 - norm[0]; - - f = 1; - for (i = norm[0], k = 0; i < num || k == order; k++) { - if (k == order) { - factor[0] = static_cast(f); - f *= pivfac[enc_type]; - } else { - factor[i] = static_cast(f); - f *= subfactor(norm[i], n); - n -= norm[i]; - i += norm[i]; - } - } - - return f; -} - -static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) -{ - int i, k, n; - uint64 f; - - i = norm[0]; - if (order2 < 0x0f) i += norm[i]; - n = 64 - i; - - f = 1; - for (k = 0; i < num || k == order || k == order2; k++) { - if (k == order) { - factor[0] = static_cast(f); - f *= pfactor[norm[0] - 1][file]; - } else if (k == order2) { - factor[norm[0]] = static_cast(f); - f *= subfactor(norm[norm[0]], 48 - norm[0]); - } else { - factor[i] = static_cast(f); - f *= subfactor(norm[i], n); - n -= norm[i]; - i += norm[i]; - } - } - - return f; -} - -static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces) -{ - int i, j; - - for (i = 0; i < ptr->num; i++) - norm[i] = 0; - - switch (ptr->enc_type) { - case 0: - norm[0] = 3; - break; - case 2: - norm[0] = 2; - break; - default: - norm[0] = ubyte(ptr->enc_type - 1); - break; - } - - for (i = norm[0]; i < ptr->num; i += norm[i]) - for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) - norm[i]++; -} - -static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces) -{ - int i, j; - - for (i = 0; i < ptr->num; i++) - norm[i] = 0; - - norm[0] = ptr->pawns[0]; - if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1]; - - for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i]) - for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) - norm[i]++; -} - -static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64 *tb_size) -{ - int i; - int order; - - for (i = 0; i < ptr->num; i++) - ptr->pieces[0][i] = ubyte(data[i + 1] & 0x0f); - order = data[0] & 0x0f; - set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]); - tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type); - - for (i = 0; i < ptr->num; i++) - ptr->pieces[1][i] = ubyte(data[i + 1] >> 4); - order = data[0] >> 4; - set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]); - tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type); -} - -static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64 *tb_size) -{ - int i; - int order; - - for (i = 0; i < ptr->num; i++) - ptr->pieces[i] = ubyte(data[i + 1] & 0x0f); - order = data[0] & 0x0f; - set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces); - tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type); -} - -static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) -{ - int i, j; - int order, order2; - - j = 1 + (ptr->pawns[1] > 0); - order = data[0] & 0x0f; - order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[0][i] = ubyte(data[i + j] & 0x0f); - set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]); - tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f); - - order = data[0] >> 4; - order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[1][i] = ubyte(data[i + j] >> 4); - set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]); - tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f); -} - -static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) -{ - int i, j; - int order, order2; - - j = 1 + (ptr->pawns[1] > 0); - order = data[0] & 0x0f; - order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[i] = ubyte(data[i + j] & 0x0f); - set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces); - tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f); -} - -static void calc_symlen(struct PairsData *d, int s, char *tmp) -{ - int s1, s2; - - ubyte* w = d->sympat + 3 * s; - s2 = (w[2] << 4) | (w[1] >> 4); - if (s2 == 0x0fff) - d->symlen[s] = 0; - else { - s1 = ((w[1] & 0xf) << 8) | w[0]; - if (!tmp[s1]) calc_symlen(d, s1, tmp); - if (!tmp[s2]) calc_symlen(d, s2, tmp); - d->symlen[s] = ubyte(d->symlen[s1] + d->symlen[s2] + 1); - } - tmp[s] = 1; -} - -ushort ReadUshort(ubyte* d) { - return ushort(d[0] | (d[1] << 8)); -} - -uint32 ReadUint32(ubyte* d) { - return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); -} - -static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 *size, unsigned char **next, ubyte *flags, int wdl) -{ - struct PairsData *d; - int i; - - *flags = data[0]; - if (data[0] & 0x80) { - d = (struct PairsData *)malloc(sizeof(struct PairsData)); - d->idxbits = 0; - if (wdl) - d->min_len = data[1]; - else - d->min_len = 0; - *next = data + 2; - size[0] = size[1] = size[2] = 0; - return d; - } - - int blocksize = data[1]; - int idxbits = data[2]; - int real_num_blocks = ReadUint32(&data[4]); - int num_blocks = real_num_blocks + *(ubyte *)(&data[3]); - int max_len = data[8]; - int min_len = data[9]; - int h = max_len - min_len + 1; - int num_syms = ReadUshort(&data[10 + 2 * h]); - d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms); - d->blocksize = blocksize; - d->idxbits = idxbits; - d->offset = (ushort*)(&data[10]); - d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t); - d->sympat = &data[12 + 2 * h]; - d->min_len = min_len; - *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; - - uint64 num_indices = (tb_size + (1ULL << idxbits) - 1) >> idxbits; - size[0] = 6ULL * num_indices; - size[1] = 2ULL * num_blocks; - size[2] = (1ULL << blocksize) * real_num_blocks; - - // char tmp[num_syms]; - char tmp[4096]; - for (i = 0; i < num_syms; i++) - tmp[i] = 0; - for (i = 0; i < num_syms; i++) - if (!tmp[i]) - calc_symlen(d, i, tmp); - - d->base[h - 1] = 0; - for (i = h - 2; i >= 0; i--) - d->base[i] = (d->base[i + 1] + ReadUshort((ubyte*)(d->offset + i)) - ReadUshort((ubyte*)(d->offset + i + 1))) / 2; - for (i = 0; i < h; i++) - d->base[i] <<= 64 - (min_len + i); - - d->offset -= d->min_len; - - return d; -} - -static int init_table_wdl(struct TBEntry *entry, char *str) -{ - ubyte *next; - int f, s; - uint64 tb_size[8]; - uint64 size[8 * 3]; - ubyte flags; - - // first mmap the table into memory - - entry->data = map_file(str, WDLSUFFIX, &entry->mapping); - if (!entry->data) { - printf("Could not find %s" WDLSUFFIX, str); - return 0; - } - - ubyte *data = (ubyte *)entry->data; - if (data[0] != WDL_MAGIC[0] || - data[1] != WDL_MAGIC[1] || - data[2] != WDL_MAGIC[2] || - data[3] != WDL_MAGIC[3]) { - printf("Corrupted table.\n"); - unmap_file(entry->data, entry->mapping); - entry->data = 0; - return 0; - } - - int split = data[4] & 0x01; - int files = data[4] & 0x02 ? 4 : 1; - - data += 5; - - if (!entry->has_pawns) { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - setup_pieces_piece(ptr, data, &tb_size[0]); - data += ptr->num + 1; - data += ((uintptr_t)data) & 0x01; - - ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1); - data = next; - if (split) { - ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1); - data = next; - } else - ptr->precomp[1] = NULL; - - ptr->precomp[0]->indextable = (char *)data; - data += size[0]; - if (split) { - ptr->precomp[1]->indextable = (char *)data; - data += size[3]; - } - - ptr->precomp[0]->sizetable = (ushort *)data; - data += size[1]; - if (split) { - ptr->precomp[1]->sizetable = (ushort *)data; - data += size[4]; - } - - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp[0]->data = data; - data += size[2]; - if (split) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp[1]->data = data; - } - } else { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - s = 1 + (ptr->pawns[1] > 0); - for (f = 0; f < 4; f++) { - setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f); - data += ptr->num + s; - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1); - data = next; - if (split) { - ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1); - data = next; - } else - ptr->file[f].precomp[1] = NULL; - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0]->indextable = (char *)data; - data += size[6 * f]; - if (split) { - ptr->file[f].precomp[1]->indextable = (char *)data; - data += size[6 * f + 3]; - } - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0]->sizetable = (ushort *)data; - data += size[6 * f + 1]; - if (split) { - ptr->file[f].precomp[1]->sizetable = (ushort *)data; - data += size[6 * f + 4]; - } - } - - for (f = 0; f < files; f++) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp[0]->data = data; - data += size[6 * f + 2]; - if (split) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp[1]->data = data; - data += size[6 * f + 5]; - } - } - } - - return 1; -} - -static int init_table_dtz(struct TBEntry *entry) -{ - ubyte *data = (ubyte *)entry->data; - ubyte *next; - int f, s; - uint64 tb_size[4]; - uint64 size[4 * 3]; - - if (!data) - return 0; - - if (data[0] != DTZ_MAGIC[0] || - data[1] != DTZ_MAGIC[1] || - data[2] != DTZ_MAGIC[2] || - data[3] != DTZ_MAGIC[3]) { - printf("Corrupted table.\n"); - return 0; - } - - int files = data[4] & 0x02 ? 4 : 1; - - data += 5; - - if (!entry->has_pawns) { - struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; - setup_pieces_piece_dtz(ptr, data, &tb_size[0]); - data += ptr->num + 1; - data += ((uintptr_t)data) & 0x01; - - ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0); - data = next; - - ptr->map = data; - if (ptr->flags & 2) { - int i; - for (i = 0; i < 4; i++) { - ptr->map_idx[i] = static_cast(data + 1 - ptr->map); - data += 1 + data[0]; - } - data += ((uintptr_t)data) & 0x01; - } - - ptr->precomp->indextable = (char *)data; - data += size[0]; - - ptr->precomp->sizetable = (ushort *)data; - data += size[1]; - - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp->data = data; - data += size[2]; - } else { - struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; - s = 1 + (ptr->pawns[1] > 0); - for (f = 0; f < 4; f++) { - setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f); - data += ptr->num + s; - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0); - data = next; - } - - ptr->map = data; - for (f = 0; f < files; f++) { - if (ptr->flags[f] & 2) { - int i; - for (i = 0; i < 4; i++) { - ptr->map_idx[f][i] = static_cast(data + 1 - ptr->map); - data += 1 + data[0]; - } - } - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp->indextable = (char *)data; - data += size[3 * f]; - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp->sizetable = (ushort *)data; - data += size[3 * f + 1]; - } - - for (f = 0; f < files; f++) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp->data = data; - data += size[3 * f + 2]; - } - } - - return 1; -} - -template -static ubyte decompress_pairs(struct PairsData *d, uint64 idx) -{ - if (!d->idxbits) - return ubyte(d->min_len); - - uint32 mainidx = static_cast(idx >> d->idxbits); - int litidx = (idx & ((1ULL << d->idxbits) - 1)) - (1ULL << (d->idxbits - 1)); - uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); - if (!LittleEndian) - block = BSWAP32(block); - - ushort idxOffset = *(ushort *)(d->indextable + 6 * mainidx + 4); - if (!LittleEndian) - idxOffset = ushort((idxOffset << 8) | (idxOffset >> 8)); - litidx += idxOffset; - - if (litidx < 0) { - do { - litidx += d->sizetable[--block] + 1; - } while (litidx < 0); - } else { - while (litidx > d->sizetable[block]) - litidx -= d->sizetable[block++] + 1; - } - - uint32 *ptr = (uint32 *)(d->data + (block << d->blocksize)); - - int m = d->min_len; - ushort *offset = d->offset; - base_t *base = d->base - m; - ubyte *symlen = d->symlen; - int sym, bitcnt; - - uint64 code = *((uint64 *)ptr); - if (LittleEndian) - code = BSWAP64(code); - - ptr += 2; - bitcnt = 0; // number of "empty bits" in code - for (;;) { - int l = m; - while (code < base[l]) l++; - sym = offset[l]; - if (!LittleEndian) - sym = ((sym & 0xff) << 8) | (sym >> 8); - sym += static_cast((code - base[l]) >> (64 - l)); - if (litidx < (int)symlen[sym] + 1) break; - litidx -= (int)symlen[sym] + 1; - code <<= l; - bitcnt += l; - if (bitcnt >= 32) { - bitcnt -= 32; - uint32 tmp = *ptr++; - if (LittleEndian) - tmp = BSWAP32(tmp); - code |= ((uint64)tmp) << bitcnt; - } - } - - ubyte *sympat = d->sympat; - while (symlen[sym] != 0) { - ubyte* w = sympat + (3 * sym); - int s1 = ((w[1] & 0xf) << 8) | w[0]; - if (litidx < (int)symlen[s1] + 1) - sym = s1; - else { - litidx -= (int)symlen[s1] + 1; - sym = (w[2] << 4) | (w[1] >> 4); - } - } - - return sympat[3 * sym]; -} - -void load_dtz_table(char *str, uint64 key1, uint64 key2) -{ - int i; - struct TBEntry *ptr, *ptr3; - struct TBHashEntry *ptr2; - - DTZ_table[0].key1 = key1; - DTZ_table[0].key2 = key2; - DTZ_table[0].entry = NULL; - - // find corresponding WDL entry - ptr2 = TB_hash[key1 >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - if (ptr2[i].key == key1) break; - if (i == HSHMAX) return; - ptr = ptr2[i].ptr; - - ptr3 = (struct TBEntry *)malloc(ptr->has_pawns - ? sizeof(struct DTZEntry_pawn) - : sizeof(struct DTZEntry_piece)); - - ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping); - ptr3->key = ptr->key; - ptr3->num = ptr->num; - ptr3->symmetric = ptr->symmetric; - ptr3->has_pawns = ptr->has_pawns; - if (ptr3->has_pawns) { - struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3; - entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0]; - entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1]; - } else { - struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3; - entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type; - } - if (!init_table_dtz(ptr3)) - free(ptr3); - else - DTZ_table[0].entry = ptr3; -} - -static void free_wdl_entry(struct TBEntry *entry) -{ - unmap_file(entry->data, entry->mapping); - if (!entry->has_pawns) { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - free(ptr->precomp[0]); - if (ptr->precomp[1]) - free(ptr->precomp[1]); - } else { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - int f; - for (f = 0; f < 4; f++) { - free(ptr->file[f].precomp[0]); - if (ptr->file[f].precomp[1]) - free(ptr->file[f].precomp[1]); - } - } -} - -static void free_dtz_entry(struct TBEntry *entry) -{ - unmap_file(entry->data, entry->mapping); - if (!entry->has_pawns) { - struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; - free(ptr->precomp); - } else { - struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; - int f; - for (f = 0; f < 4; f++) - free(ptr->file[f].precomp); - } - free(entry); -} - -static int wdl_to_map[5] = { 1, 3, 0, 2, 0 }; -static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 }; - diff --git a/Engines/Windows/deepfish/srcD/syzygy/tbcore.h b/Engines/Windows/deepfish/srcD/syzygy/tbcore.h deleted file mode 100644 index cdaf2ac..0000000 --- a/Engines/Windows/deepfish/srcD/syzygy/tbcore.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright (c) 2011-2013 Ronald de Man -*/ - -#ifndef TBCORE_H -#define TBCORE_H - -#ifndef _WIN32 -#include -#define SEP_CHAR ':' -#define FD int -#define FD_ERR -1 -#else -#include -#define SEP_CHAR ';' -#define FD HANDLE -#define FD_ERR INVALID_HANDLE_VALUE -#endif - -#ifndef _WIN32 -#define LOCK_T pthread_mutex_t -#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) -#define LOCK(x) pthread_mutex_lock(&(x)) -#define UNLOCK(x) pthread_mutex_unlock(&(x)) -#else -#define LOCK_T HANDLE -#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) -#define LOCK(x) WaitForSingleObject(x, INFINITE) -#define UNLOCK(x) ReleaseMutex(x) -#endif - -#ifndef _MSC_VER -#define BSWAP32(v) __builtin_bswap32(v) -#define BSWAP64(v) __builtin_bswap64(v) -#else -#define BSWAP32(v) _byteswap_ulong(v) -#define BSWAP64(v) _byteswap_uint64(v) -#endif - -#define WDLSUFFIX ".rtbw" -#define DTZSUFFIX ".rtbz" -#define WDLDIR "RTBWDIR" -#define DTZDIR "RTBZDIR" -#define TBPIECES 6 - -typedef unsigned long long uint64; -typedef unsigned int uint32; -typedef unsigned char ubyte; -typedef unsigned short ushort; - -const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d }; -const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 }; - -#define TBHASHBITS 10 - -struct TBHashEntry; - -typedef uint64 base_t; - -struct PairsData { - char *indextable; - ushort *sizetable; - ubyte *data; - ushort *offset; - ubyte *symlen; - ubyte *sympat; - int blocksize; - int idxbits; - int min_len; - base_t base[1]; // C++ complains about base[]... -}; - -struct TBEntry { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; -} -#ifndef _WIN32 -__attribute__((__may_alias__)) -#endif -; - -struct TBEntry_piece { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte enc_type; - struct PairsData *precomp[2]; - int factor[2][TBPIECES]; - ubyte pieces[2][TBPIECES]; - ubyte norm[2][TBPIECES]; -}; - -struct TBEntry_pawn { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte pawns[2]; - struct { - struct PairsData *precomp[2]; - int factor[2][TBPIECES]; - ubyte pieces[2][TBPIECES]; - ubyte norm[2][TBPIECES]; - } file[4]; -}; - -struct DTZEntry_piece { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte enc_type; - struct PairsData *precomp; - int factor[TBPIECES]; - ubyte pieces[TBPIECES]; - ubyte norm[TBPIECES]; - ubyte flags; // accurate, mapped, side - ushort map_idx[4]; - ubyte *map; -}; - -struct DTZEntry_pawn { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte pawns[2]; - struct { - struct PairsData *precomp; - int factor[TBPIECES]; - ubyte pieces[TBPIECES]; - ubyte norm[TBPIECES]; - } file[4]; - ubyte flags[4]; - ushort map_idx[4][4]; - ubyte *map; -}; - -struct TBHashEntry { - uint64 key; - struct TBEntry *ptr; -}; - -struct DTZTableEntry { - uint64 key1; - uint64 key2; - struct TBEntry *entry; -}; - -#endif - diff --git a/Engines/Windows/deepfish/srcD/syzygy/tbprobe.cpp b/Engines/Windows/deepfish/srcD/syzygy/tbprobe.cpp deleted file mode 100644 index 6f6627a..0000000 --- a/Engines/Windows/deepfish/srcD/syzygy/tbprobe.cpp +++ /dev/null @@ -1,824 +0,0 @@ -/* - Copyright (c) 2013 Ronald de Man - This file may be redistributed and/or modified without restrictions. - - tbprobe.cpp contains the Stockfish-specific routines of the - tablebase probing code. It should be relatively easy to adapt - this code to other chess engines. -*/ - -#define NOMINMAX - -#include - -#include "../position.h" -#include "../movegen.h" -#include "../bitboard.h" -#include "../search.h" - -#include "tbprobe.h" -#include "tbcore.h" - -#include "tbcore.cpp" - -namespace Zobrist { - extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; -} - -int Tablebases::MaxCardinality = 0; - -// Given a position with 6 or fewer pieces, produce a text string -// of the form KQPvKRP, where "KQP" represents the white pieces if -// mirror == 0 and the black pieces if mirror == 1. -static void prt_str(Position& pos, char *str, int mirror) -{ - Color color; - PieceType pt; - int i; - - color = !mirror ? WHITE : BLACK; - for (pt = KING; pt >= PAWN; --pt) - for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - *str++ = pchr[6 - pt]; - *str++ = 'v'; - color = ~color; - for (pt = KING; pt >= PAWN; --pt) - for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - *str++ = pchr[6 - pt]; - *str++ = 0; -} - -// Given a position, produce a 64-bit material signature key. -// If the engine supports such a key, it should equal the engine's key. -static uint64 calc_key(Position& pos, int mirror) -{ - Color color; - PieceType pt; - int i; - uint64 key = 0; - - color = !mirror ? WHITE : BLACK; - for (pt = PAWN; pt <= KING; ++pt) - for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - key ^= Zobrist::psq[WHITE][pt][i - 1]; - color = ~color; - for (pt = PAWN; pt <= KING; ++pt) - for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - key ^= Zobrist::psq[BLACK][pt][i - 1]; - - return key; -} - -// Produce a 64-bit material key corresponding to the material combination -// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white -// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black -// pawns, ..., kings. -static uint64 calc_key_from_pcs(int *pcs, int mirror) -{ - int color; - PieceType pt; - int i; - uint64 key = 0; - - color = !mirror ? 0 : 8; - for (pt = PAWN; pt <= KING; ++pt) - for (i = 0; i < pcs[color + pt]; i++) - key ^= Zobrist::psq[WHITE][pt][i]; - color ^= 8; - for (pt = PAWN; pt <= KING; ++pt) - for (i = 0; i < pcs[color + pt]; i++) - key ^= Zobrist::psq[BLACK][pt][i]; - - return key; -} - -bool is_little_endian() { - union { - int i; - char c[sizeof(int)]; - } x; - x.i = 1; - return x.c[0] == 1; -} - -static ubyte decompress_pairs(struct PairsData *d, uint64 idx) -{ - static const bool isLittleEndian = is_little_endian(); - return isLittleEndian ? decompress_pairs(d, idx) - : decompress_pairs(d, idx); -} - -// probe_wdl_table and probe_dtz_table require similar adaptations. -static int probe_wdl_table(Position& pos, int *success) -{ - struct TBEntry *ptr; - struct TBHashEntry *ptr2; - uint64 idx; - uint64 key; - int i; - ubyte res; - int p[TBPIECES]; - - // Obtain the position's material signature key. - key = pos.material_key(); - - // Test for KvK. - if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0])) - return 0; - - ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - if (ptr2[i].key == key) break; - if (i == HSHMAX) { - *success = 0; - return 0; - } - - ptr = ptr2[i].ptr; - if (!ptr->ready) { - LOCK(TB_mutex); - if (!ptr->ready) { - char str[16]; - prt_str(pos, str, ptr->key != key); - if (!init_table_wdl(ptr, str)) { - ptr2[i].key = 0ULL; - *success = 0; - UNLOCK(TB_mutex); - return 0; - } - // Memory barrier to ensure ptr->ready = 1 is not reordered. -#ifdef _MSC_VER - _ReadWriteBarrier(); -#else - __asm__ __volatile__ ("" ::: "memory"); -#endif - ptr->ready = 1; - } - UNLOCK(TB_mutex); - } - - int bside, mirror, cmirror; - if (!ptr->symmetric) { - if (key != ptr->key) { - cmirror = 8; - mirror = 0x38; - bside = (pos.side_to_move() == WHITE); - } else { - cmirror = mirror = 0; - bside = !(pos.side_to_move() == WHITE); - } - } else { - cmirror = pos.side_to_move() == WHITE ? 0 : 8; - mirror = pos.side_to_move() == WHITE ? 0 : 0x38; - bside = 0; - } - - // p[i] is to contain the square 0-63 (A1-H8) for a piece of type - // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. - // Pieces of the same type are guaranteed to be consecutive. - if (!ptr->has_pawns) { - struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; - ubyte *pc = entry->pieces[bside]; - for (i = 0; i < entry->num;) { - Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), - (PieceType)(pc[i] & 0x07)); - do { - p[i++] = pop_lsb(&bb); - } while (bb); - } - idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); - res = decompress_pairs(entry->precomp[bside], idx); - } else { - struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; - int k = entry->file[0].pieces[0][0] ^ cmirror; - Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); - i = 0; - do { - p[i++] = pop_lsb(&bb) ^ mirror; - } while (bb); - int f = pawn_file(entry, p); - ubyte *pc = entry->file[f].pieces[bside]; - for (; i < entry->num;) { - bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), - (PieceType)(pc[i] & 0x07)); - do { - p[i++] = pop_lsb(&bb) ^ mirror; - } while (bb); - } - idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]); - res = decompress_pairs(entry->file[f].precomp[bside], idx); - } - - return ((int)res) - 2; -} - -static int probe_dtz_table(Position& pos, int wdl, int *success) -{ - struct TBEntry *ptr; - uint64 idx; - int i, res; - int p[TBPIECES]; - - // Obtain the position's material signature key. - uint64 key = pos.material_key(); - - if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { - for (i = 1; i < DTZ_ENTRIES; i++) - if (DTZ_table[i].key1 == key) break; - if (i < DTZ_ENTRIES) { - struct DTZTableEntry table_entry = DTZ_table[i]; - for (; i > 0; i--) - DTZ_table[i] = DTZ_table[i - 1]; - DTZ_table[0] = table_entry; - } else { - struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - if (ptr2[i].key == key) break; - if (i == HSHMAX) { - *success = 0; - return 0; - } - ptr = ptr2[i].ptr; - char str[16]; - int mirror = (ptr->key != key); - prt_str(pos, str, mirror); - if (DTZ_table[DTZ_ENTRIES - 1].entry) - free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); - for (i = DTZ_ENTRIES - 1; i > 0; i--) - DTZ_table[i] = DTZ_table[i - 1]; - load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror)); - } - } - - ptr = DTZ_table[0].entry; - if (!ptr) { - *success = 0; - return 0; - } - - int bside, mirror, cmirror; - if (!ptr->symmetric) { - if (key != ptr->key) { - cmirror = 8; - mirror = 0x38; - bside = (pos.side_to_move() == WHITE); - } else { - cmirror = mirror = 0; - bside = !(pos.side_to_move() == WHITE); - } - } else { - cmirror = pos.side_to_move() == WHITE ? 0 : 8; - mirror = pos.side_to_move() == WHITE ? 0 : 0x38; - bside = 0; - } - - if (!ptr->has_pawns) { - struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; - if ((entry->flags & 1) != bside && !entry->symmetric) { - *success = -1; - return 0; - } - ubyte *pc = entry->pieces; - for (i = 0; i < entry->num;) { - Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), - (PieceType)(pc[i] & 0x07)); - do { - p[i++] = pop_lsb(&bb); - } while (bb); - } - idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor); - res = decompress_pairs(entry->precomp, idx); - - if (entry->flags & 2) - res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res]; - - if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) - res *= 2; - } else { - struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; - int k = entry->file[0].pieces[0] ^ cmirror; - Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); - i = 0; - do { - p[i++] = pop_lsb(&bb) ^ mirror; - } while (bb); - int f = pawn_file((struct TBEntry_pawn *)entry, p); - if ((entry->flags[f] & 1) != bside) { - *success = -1; - return 0; - } - ubyte *pc = entry->file[f].pieces; - for (; i < entry->num;) { - bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), - (PieceType)(pc[i] & 0x07)); - do { - p[i++] = pop_lsb(&bb) ^ mirror; - } while (bb); - } - idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor); - res = decompress_pairs(entry->file[f].precomp, idx); - - if (entry->flags[f] & 2) - res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res]; - - if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1)) - res *= 2; - } - - return res; -} - -// Add underpromotion captures to list of captures. -static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end) -{ - ExtMove *moves, *extra = end; - - for (moves = stack; moves < end; moves++) { - Move move = moves->move; - if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) { - (*extra++).move = (Move)(move - (1 << 12)); - (*extra++).move = (Move)(move - (2 << 12)); - (*extra++).move = (Move)(move - (3 << 12)); - } - } - - return extra; -} - -static int probe_ab(Position& pos, int alpha, int beta, int *success) -{ - int v; - ExtMove stack[64]; - ExtMove *moves, *end; - StateInfo st; - - // Generate (at least) all legal non-ep captures including (under)promotions. - // It is OK to generate more, as long as they are filtered out below. - if (!pos.checkers()) { - end = generate(pos, stack); - // Since underpromotion captures are not included, we need to add them. - end = add_underprom_caps(pos, stack, end); - } else - end = generate(pos, stack); - - for (moves = stack; moves < end; moves++) { - Move capture = moves->move; - if (!pos.capture(capture) || type_of(capture) == ENPASSANT - || !pos.legal(capture)) - continue; - pos.do_move(capture, st, pos.gives_check(capture)); - v = -probe_ab(pos, -beta, -alpha, success); - pos.undo_move(capture); - if (*success == 0) return 0; - if (v > alpha) { - if (v >= beta) { - *success = 2; - return v; - } - alpha = v; - } - } - - v = probe_wdl_table(pos, success); - if (*success == 0) return 0; - if (alpha >= v) { - *success = 1 + (alpha > 0); - return alpha; - } else { - *success = 1; - return v; - } -} - -// Probe the WDL table for a particular position. -// If *success != 0, the probe was successful. -// The return value is from the point of view of the side to move: -// -2 : loss -// -1 : loss, but draw under 50-move rule -// 0 : draw -// 1 : win, but draw under 50-move rule -// 2 : win -int Tablebases::probe_wdl(Position& pos, int *success) -{ - int v; - - *success = 1; - v = probe_ab(pos, -2, 2, success); - - // If en passant is not possible, we are done. - if (pos.ep_square() == SQ_NONE) - return v; - if (!(*success)) return 0; - - // Now handle en passant. - int v1 = -3; - // Generate (at least) all legal en passant captures. - ExtMove stack[192]; - ExtMove *moves, *end; - StateInfo st; - - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - for (moves = stack; moves < end; moves++) { - Move capture = moves->move; - if (type_of(capture) != ENPASSANT - || !pos.legal(capture)) - continue; - pos.do_move(capture, st, pos.gives_check(capture)); - int v0 = -probe_ab(pos, -2, 2, success); - pos.undo_move(capture); - if (*success == 0) return 0; - if (v0 > v1) v1 = v0; - } - if (v1 > -3) { - if (v1 >= v) v = v1; - else if (v == 0) { - // Check whether there is at least one legal non-ep move. - for (moves = stack; moves < end; moves++) { - Move capture = moves->move; - if (type_of(capture) == ENPASSANT) continue; - if (pos.legal(capture)) break; - } - if (moves == end && !pos.checkers()) { - end = generate(pos, end); - for (; moves < end; moves++) { - Move move = moves->move; - if (pos.legal(move)) - break; - } - } - // If not, then we are forced to play the losing ep capture. - if (moves == end) - v = v1; - } - } - - return v; -} - -// This routine treats a position with en passant captures as one without. -static int probe_dtz_no_ep(Position& pos, int *success) -{ - int wdl, dtz; - - wdl = probe_ab(pos, -2, 2, success); - if (*success == 0) return 0; - - if (wdl == 0) return 0; - - if (*success == 2) - return wdl == 2 ? 1 : 101; - - ExtMove stack[192]; - ExtMove *moves, *end = NULL; - StateInfo st; - - if (wdl > 0) { - // Generate at least all legal non-capturing pawn moves - // including non-capturing promotions. - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - for (moves = stack; moves < end; moves++) { - Move move = moves->move; - if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) - || !pos.legal(move)) - continue; - pos.do_move(move, st, pos.gives_check(move)); - int v = -Tablebases::probe_wdl(pos, success); - pos.undo_move(move); - if (*success == 0) return 0; - if (v == wdl) - return v == 2 ? 1 : 101; - } - } - - dtz = 1 + probe_dtz_table(pos, wdl, success); - if (*success >= 0) { - if (wdl & 1) dtz += 100; - return wdl >= 0 ? dtz : -dtz; - } - - if (wdl > 0) { - int best = 0xffff; - for (moves = stack; moves < end; moves++) { - Move move = moves->move; - if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN - || !pos.legal(move)) - continue; - pos.do_move(move, st, pos.gives_check(move)); - int v = -Tablebases::probe_dtz(pos, success); - pos.undo_move(move); - if (*success == 0) return 0; - if (v > 0 && v + 1 < best) - best = v + 1; - } - return best; - } else { - int best = -1; - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - for (moves = stack; moves < end; moves++) { - int v; - Move move = moves->move; - if (!pos.legal(move)) - continue; - pos.do_move(move, st, pos.gives_check(move)); - if (st.rule50 == 0) { - if (wdl == -2) v = -1; - else { - v = probe_ab(pos, 1, 2, success); - v = (v == 2) ? 0 : -101; - } - } else { - v = -Tablebases::probe_dtz(pos, success) - 1; - } - pos.undo_move(move); - if (*success == 0) return 0; - if (v < best) - best = v; - } - return best; - } -} - -static int wdl_to_dtz[] = { - -1, -101, 0, 101, 1 -}; - -// Probe the DTZ table for a particular position. -// If *success != 0, the probe was successful. -// The return value is from the point of view of the side to move: -// n < -100 : loss, but draw under 50-move rule -// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) -// 0 : draw -// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) -// 100 < n : win, but draw under 50-move rule -// -// The return value n can be off by 1: a return value -n can mean a loss -// in n+1 ply and a return value +n can mean a win in n+1 ply. This -// cannot happen for tables with positions exactly on the "edge" of -// the 50-move rule. -// -// This implies that if dtz > 0 is returned, the position is certainly -// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine -// picks moves that preserve dtz + 50-move-counter <= 99. -// -// If n = 100 immediately after a capture or pawn move, then the position -// is also certainly a win, and during the whole phase until the next -// capture or pawn move, the inequality to be preserved is -// dtz + 50-movecounter <= 100. -// -// In short, if a move is available resulting in dtz + 50-move-counter <= 99, -// then do not accept moves leading to dtz + 50-move-counter == 100. -// -int Tablebases::probe_dtz(Position& pos, int *success) -{ - *success = 1; - int v = probe_dtz_no_ep(pos, success); - - if (pos.ep_square() == SQ_NONE) - return v; - if (*success == 0) return 0; - - // Now handle en passant. - int v1 = -3; - - ExtMove stack[192]; - ExtMove *moves, *end; - StateInfo st; - - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - for (moves = stack; moves < end; moves++) { - Move capture = moves->move; - if (type_of(capture) != ENPASSANT - || !pos.legal(capture)) - continue; - pos.do_move(capture, st, pos.gives_check(capture)); - int v0 = -probe_ab(pos, -2, 2, success); - pos.undo_move(capture); - if (*success == 0) return 0; - if (v0 > v1) v1 = v0; - } - if (v1 > -3) { - v1 = wdl_to_dtz[v1 + 2]; - if (v < -100) { - if (v1 >= 0) - v = v1; - } else if (v < 0) { - if (v1 >= 0 || v1 < -100) - v = v1; - } else if (v > 100) { - if (v1 > 0) - v = v1; - } else if (v > 0) { - if (v1 == 1) - v = v1; - } else if (v1 >= 0) { - v = v1; - } else { - for (moves = stack; moves < end; moves++) { - Move move = moves->move; - if (type_of(move) == ENPASSANT) continue; - if (pos.legal(move)) break; - } - if (moves == end && !pos.checkers()) { - end = generate(pos, end); - for (; moves < end; moves++) { - Move move = moves->move; - if (pos.legal(move)) - break; - } - } - if (moves == end) - v = v1; - } - } - - return v; -} - -// Check whether there has been at least one repetition of positions -// since the last capture or pawn move. -static int has_repeated(StateInfo *st) -{ - while (1) { - int i = 4, e = std::min(st->rule50, st->pliesFromNull); - if (e < i) - return 0; - StateInfo *stp = st->previous->previous; - do { - stp = stp->previous->previous; - if (stp->key == st->key) - return 1; - i += 2; - } while (i <= e); - st = st->previous; - } -} - -static Value wdl_to_Value[5] = { - -VALUE_MATE + MAX_PLY + 1, - VALUE_DRAW - 2, - VALUE_DRAW, - VALUE_DRAW + 2, - VALUE_MATE - MAX_PLY - 1 -}; - -// Use the DTZ tables to filter out moves that don't preserve the win or draw. -// If the position is lost, but DTZ is fairly high, only keep moves that -// maximise DTZ. -// -// A return value false indicates that not all probes were successful and that -// no moves were filtered out. -bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score) -{ - int success; - - int dtz = probe_dtz(pos, &success); - if (!success) return false; - - StateInfo st; - - // Probe each move. - for (size_t i = 0; i < rootMoves.size(); i++) { - Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move)); - int v = 0; - if (pos.checkers() && dtz > 0) { - ExtMove s[192]; - if (generate(pos, s) == s) - v = 1; - } - if (!v) { - if (st.rule50 != 0) { - v = -Tablebases::probe_dtz(pos, &success); - if (v > 0) v++; - else if (v < 0) v--; - } else { - v = -Tablebases::probe_wdl(pos, &success); - v = wdl_to_dtz[v + 2]; - } - } - pos.undo_move(move); - if (!success) return false; - rootMoves[i].score = (Value)v; - } - - // Obtain 50-move counter for the root position. - // In Stockfish there seems to be no clean way, so we do it like this: - int cnt50 = st.previous->rule50; - - // Use 50-move counter to determine whether the root position is - // won, lost or drawn. - int wdl = 0; - if (dtz > 0) - wdl = (dtz + cnt50 <= 100) ? 2 : 1; - else if (dtz < 0) - wdl = (-dtz + cnt50 <= 100) ? -2 : -1; - - // Determine the score to report to the user. - score = wdl_to_Value[wdl + 2]; - // If the position is winning or losing, but too few moves left, adjust the - // score to show how close it is to winning or losing. - // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci(). - if (wdl == 1 && dtz <= 100) - score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); - else if (wdl == -1 && dtz >= -100) - score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200); - - // Now be a bit smart about filtering out moves. - size_t j = 0; - if (dtz > 0) { // winning (or 50-move rule draw) - int best = 0xffff; - for (size_t i = 0; i < rootMoves.size(); i++) { - int v = rootMoves[i].score; - if (v > 0 && v < best) - best = v; - } - int max = best; - // If the current phase has not seen repetitions, then try all moves - // that stay safely within the 50-move budget, if there are any. - if (!has_repeated(st.previous) && best + cnt50 <= 99) - max = 99 - cnt50; - for (size_t i = 0; i < rootMoves.size(); i++) { - int v = rootMoves[i].score; - if (v > 0 && v <= max) - rootMoves[j++] = rootMoves[i]; - } - } else if (dtz < 0) { // losing (or 50-move rule draw) - int best = 0; - for (size_t i = 0; i < rootMoves.size(); i++) { - int v = rootMoves[i].score; - if (v < best) - best = v; - } - // Try all moves, unless we approach or have a 50-move rule draw. - if (-best * 2 + cnt50 < 100) - return true; - for (size_t i = 0; i < rootMoves.size(); i++) { - if (rootMoves[i].score == best) - rootMoves[j++] = rootMoves[i]; - } - } else { // drawing - // Try all moves that preserve the draw. - for (size_t i = 0; i < rootMoves.size(); i++) { - if (rootMoves[i].score == 0) - rootMoves[j++] = rootMoves[i]; - } - } - rootMoves.resize(j, Search::RootMove(MOVE_NONE)); - - return true; -} - -// Use the WDL tables to filter out moves that don't preserve the win or draw. -// This is a fallback for the case that some or all DTZ tables are missing. -// -// A return value false indicates that not all probes were successful and that -// no moves were filtered out. -bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score) -{ - int success; - - int wdl = Tablebases::probe_wdl(pos, &success); - if (!success) return false; - score = wdl_to_Value[wdl + 2]; - - StateInfo st; - - int best = -2; - - // Probe each move. - for (size_t i = 0; i < rootMoves.size(); i++) { - Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move)); - int v = -Tablebases::probe_wdl(pos, &success); - pos.undo_move(move); - if (!success) return false; - rootMoves[i].score = (Value)v; - if (v > best) - best = v; - } - - size_t j = 0; - for (size_t i = 0; i < rootMoves.size(); i++) { - if (rootMoves[i].score == best) - rootMoves[j++] = rootMoves[i]; - } - rootMoves.resize(j, Search::RootMove(MOVE_NONE)); - - return true; -} - diff --git a/Engines/Windows/deepfish/srcD/syzygy/tbprobe.h b/Engines/Windows/deepfish/srcD/syzygy/tbprobe.h deleted file mode 100644 index b23fdf6..0000000 --- a/Engines/Windows/deepfish/srcD/syzygy/tbprobe.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TBPROBE_H -#define TBPROBE_H - -#include "../search.h" - -namespace Tablebases { - -extern int MaxCardinality; - -void init(const std::string& path); -int probe_wdl(Position& pos, int *success); -int probe_dtz(Position& pos, int *success); -bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); -bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score); -void filter_root_moves(Position& pos, Search::RootMoves& rootMoves); - -} - -#endif diff --git a/Engines/Windows/deepfish/windows/DeepFishMZ 32.exe b/Engines/Windows/deepfish/windows/DeepFishMZ 32.exe deleted file mode 100644 index 3b7a232..0000000 Binary files a/Engines/Windows/deepfish/windows/DeepFishMZ 32.exe and /dev/null differ diff --git a/Engines/Windows/deepfish/windows/DeepFishMZ 64 BMI2.exe b/Engines/Windows/deepfish/windows/DeepFishMZ 64 BMI2.exe deleted file mode 100644 index db60142..0000000 Binary files a/Engines/Windows/deepfish/windows/DeepFishMZ 64 BMI2.exe and /dev/null differ diff --git a/Engines/Windows/mcbrain/AUTHORS b/Engines/Windows/mcbrain/AUTHORS new file mode 100644 index 0000000..9b91b93 --- /dev/null +++ b/Engines/Windows/mcbrain/AUTHORS @@ -0,0 +1,98 @@ +# Generated with 'git shortlog -sn | cut -c8-', which sorts by commits, manually ordered the first four authors, merged duplicates + +Tord Romstad +Marco Costalba (mcostalba) +Joona Kiiski (zamar) +Gary Linscott (glinscott) +Lucas Braesch (lucasart) +Bill Henry (VoyagerOne) +mstembera +Stéphane Nicolet (Stephane Nicolet, snicolet) +Stefan Geschwentner +Alain SAVARD (Rocky640) +Jörg Oster (Joerg Oster, joergoster) +Reuven Peleg +Chris Caino (Chris Cain, ceebo) +Jean-Francois Romang +homoSapiensSapiens +Leonid Pechenik +Stefano Cardanobile (Stefano80) +Arjun Temurnikar +Uri Blass (uriblass) +jundery +Ajith (ajithcj) +hxim +Ralph Stößer (Ralph Stoesser) +Guenther Demetz +Jonathan Calovski (Mysseno) +Tom Vijlbrief +mbootsector +Daylen Yang +ElbertoOne +Henri Wiechers +loco-loco +Joost VandeVondele (Joost Vandevondele) +Ronald de Man (syzygy) +DU-jdto +David Zar +Eelco de Groot +Jerry Donald +NicklasPersson +Ryan Schmitt +Alexander Kure +Dan Schmidt +H. Felix Wittmann +Jacques +Joseph R. Prostko +Justin Blanchard +Linus Arver +Luca Brivio +Lyudmil Antonov +Rodrigo Exterckötter Tjäder +Ron Britvich +RyanTaker +Vince Negri +erbsenzaehler +Joseph Hellis (jhellis3) +shane31 +Andrew Grant +Andy Duplain +Auguste Pop +Balint Pfliegel +Dariusz Orzechowski +DiscanX +Ernesto Gatti +Gregor Cramer +Hiraoka Takuya (HiraokaTakuya) +Hongzhi Cheng +IIvec +Kelly Wilson +Ken T Takusagawa +Kojirion +Krgp +Matt Sullivan +Matthew Lai +Matthew Sullivan +Michel Van den Bergh +Niklas Fiekas +Oskar Werkelin Ahlin +Pablo Vazquez +Pascal Romaret +Raminder Singh +Richard Lloyd +Ryan Takker +Thanar2 +absimaldata +atumanian +braich +fanon +gamander +gguliash +kinderchocolate +pellanda +ppigazzini +renouve +sf-x +thaspel +unknown + diff --git a/Engines/Windows/deepfish/Copying.txt b/Engines/Windows/mcbrain/Copying.txt similarity index 100% rename from Engines/Windows/deepfish/Copying.txt rename to Engines/Windows/mcbrain/Copying.txt diff --git a/Engines/Windows/mcbrain/McBrain_2017_v21a_x32_old.exe b/Engines/Windows/mcbrain/McBrain_2017_v21a_x32_old.exe new file mode 100644 index 0000000..a273b44 Binary files /dev/null and b/Engines/Windows/mcbrain/McBrain_2017_v21a_x32_old.exe differ diff --git a/Engines/Windows/mcbrain/McBrain_2017_v21a_x64_bmi2.exe b/Engines/Windows/mcbrain/McBrain_2017_v21a_x64_bmi2.exe new file mode 100644 index 0000000..7c61e38 Binary files /dev/null and b/Engines/Windows/mcbrain/McBrain_2017_v21a_x64_bmi2.exe differ diff --git a/Engines/Windows/deepfish/README.md b/Engines/Windows/mcbrain/Readme.md similarity index 93% rename from Engines/Windows/deepfish/README.md rename to Engines/Windows/mcbrain/Readme.md index 319bca8..29ebf59 100644 --- a/Engines/Windows/deepfish/README.md +++ b/Engines/Windows/mcbrain/Readme.md @@ -1,5 +1,8 @@ ### Overview +[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) + Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is not a complete chess program and requires some UCI-compatible GUI (e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess @@ -54,7 +57,7 @@ for correspondence games (because of tablebase adjudication). The "SyzygyProbeLimit" option should normally be left at its default value. -**What to expect** +**What to expect** If the engine is searching a position that is not in the tablebases (e.g. a position with 7 pieces), it will access the tablebases during the search. If the engine reports a very large score (typically 123.xx), this means @@ -109,4 +112,4 @@ to where the source code can be found. If you make any changes to the source code, these changes must also be made available under the GPL. For full details, read the copy of the GPL found in the file named -*Copying.txt* +*Copying.txt*. diff --git a/Engines/Windows/mcbrain/Top CPU Contributors.txt b/Engines/Windows/mcbrain/Top CPU Contributors.txt new file mode 100644 index 0000000..0bb2a92 --- /dev/null +++ b/Engines/Windows/mcbrain/Top CPU Contributors.txt @@ -0,0 +1,93 @@ +Contributors with >10,000 CPU hours as of November 3, 2016 +Thank you! + +Username CPU Hours Games played +cw 220301 16924200 +glinscott 186639 13936027 +fastgm 184045 14608140 +mibere 165859 13563572 +crunchy 160974 14091929 +spams 143806 10956698 +bking_US 136938 10558137 +dsmith 103332 7622414 +BrunoBanani 100008 7448565 +ctoks 99216 7989224 +JojoM 96528 8138437 +vdbergh 88372 6322455 +drabel 75214 6034715 +velislav 71485 5483953 +sqrt2 70825 5471595 +BRAVONE 66265 5356681 +malala 57618 4480635 +psk 54292 4337164 +leszek 52415 4254611 +marrco 51573 4132787 +Freja 48348 3773248 +Thanar 47723 4062940 +Fisherman 46361 3865994 +renouve 46003 3544864 +CSU_Dynasty 45136 4096148 +rap 44619 3219490 +dv8silencer 44175 3961325 +tinker 43975 3261777 +tvijlbrief 42291 2965762 +sunu 41289 3172937 +mhunt 38278 2697512 +Antihistamine 37735 2795761 +finfish 36001 2734928 +brabos 32630 2566008 +jromang 32044 2166097 +robnjr 31781 2726352 +CoffeeOne 29940 2597953 +sterni1971 28924 2737221 +EthanOConnor 28429 2143255 +Pyafue 27266 1986098 +jkiiski 27009 1925255 +biffhero 26557 2033420 +nssy 25231 2037166 +mgrabiak 24000 1974653 +slakovv 23548 2031279 +Sharaf_DG 22175 1790697 +homyur 21415 1705644 +team-oh 20347 1653708 +Zirie 20204 1493227 +nabildanial 19538 1586321 +nesoneg 19306 1493435 +cuistot 19105 1387031 +Patrick_G 19027 1406466 +mhoram 18304 1396701 +rkl 17566 1409460 +ville 17541 1540130 +oryx 17480 1578240 +rstoesser 17264 1335177 +xor12 16786 1492708 +jundery 16786 1115855 +bigpen0r 16700 1287118 +iisiraider 16366 1089410 +davar 16266 1328093 +vdv 16072 1629971 +VoyagerOne 16049 1485459 +Bobo1239 15837 1550883 +DragonLord 15791 1251348 +purplefishies 15602 1106850 +Isidor 14598 1317485 +speedycpu 14215 874201 +OssumOpossum 14078 1029265 +enedene 13378 935618 +bpfliegel 12944 886523 +AdrianSA 12921 924980 +JanErik 12782 1106788 +dju 12600 901552 +jpulman 12015 854815 +ttruscott 11929 976348 +fatmurphy 11726 901134 +ElbertoOne 11641 1082697 +j3corre 11638 973654 +chris 11450 1228430 +pb00067 11248 1021031 +modolief 11185 926456 +Dark_wizzie 10933 1017910 +SC 10637 925516 +Thomas A. 10485 736094 +mschmidt 10354 818594 +infinity 10020 746397 diff --git a/Engines/Windows/deepfish/srcD/Makefile b/Engines/Windows/mcbrain/src/Makefile similarity index 81% rename from Engines/Windows/deepfish/srcD/Makefile rename to Engines/Windows/mcbrain/src/Makefile index 90d5c6a..20286a8 100644 --- a/Engines/Windows/deepfish/srcD/Makefile +++ b/Engines/Windows/mcbrain/src/Makefile @@ -22,22 +22,25 @@ ### ========================================================================== ### Establish the operating system name -UNAME = $(shell uname) +KERNEL = $(shell uname -s) +ifeq ($(KERNEL),Linux) + OS = $(shell uname -o) +endif ### Executable name -EXE = stockfish +EXE = McBrain_2017 ### Installation dir definitions PREFIX = /usr/local BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds -PGOBENCH = ./$(EXE) bench 16 1 1000 default time +PGOBENCH = ./$(EXE) bench ### Object files OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ - material.o misc.o movegen.o movepick.o opt.o pawns.o position.o psqt.o \ - search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o + material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \ + search.o thread.o timeman.o tt.o tzbook.o uci.o ucioption.o syzygy/tbprobe.o ### ========================================================================== ### Section 2. High-level Configuration @@ -47,6 +50,7 @@ OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode +# sanitize = yes/no --- (-fsanitize ) --- enable undefined behavior checks # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations # arch = (name) --- (-arch) --- Target architecture # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system @@ -62,6 +66,7 @@ OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ ### 2.1. General and architecture defaults optimize = yes debug = no +sanitize = no bits = 32 prefetch = no popcnt = no @@ -145,8 +150,17 @@ endif ifeq ($(COMP),gcc) comp=gcc CXX=g++ - CXXFLAGS += -pedantic -Wextra -Wshadow -m$(bits) - ifneq ($(UNAME),Darwin) + CXXFLAGS += -pedantic -Wextra -Wshadow + + ifeq ($(ARCH),armv7) + ifeq ($(OS),Android) + CXXFLAGS += -m$(bits) + endif + else + CXXFLAGS += -m$(bits) + endif + + ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif endif @@ -154,7 +168,7 @@ endif ifeq ($(COMP),mingw) comp=mingw - ifeq ($(UNAME),Linux) + ifeq ($(KERNEL),Linux) ifeq ($(bits),64) ifeq ($(shell which x86_64-w64-mingw32-c++-posix),) CXX=x86_64-w64-mingw32-c++ @@ -185,27 +199,33 @@ endif ifeq ($(COMP),clang) comp=clang CXX=clang++ - CXXFLAGS += -pedantic -Wextra -Wshadow -m$(bits) - LDFLAGS += -m$(bits) - ifeq ($(UNAME),Darwin) - CXXFLAGS += -stdlib=libc++ - DEPENDFLAGS += -stdlib=libc++ + CXXFLAGS += -pedantic -Wextra -Wshadow + + ifeq ($(ARCH),armv7) + ifeq ($(OS),Android) + CXXFLAGS += -m$(bits) + LDFLAGS += -m$(bits) + endif + else + CXXFLAGS += -m$(bits) + LDFLAGS += -m$(bits) endif endif ifeq ($(comp),icc) - profile_prepare = icc-profile-prepare profile_make = icc-profile-make profile_use = icc-profile-use - profile_clean = icc-profile-clean else - profile_prepare = gcc-profile-prepare +ifeq ($(comp),clang) + profile_make = clang-profile-make + profile_use = clang-profile-use +else profile_make = gcc-profile-make profile_use = gcc-profile-use - profile_clean = gcc-profile-clean +endif endif -ifeq ($(UNAME),Darwin) +ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9 endif @@ -223,21 +243,27 @@ endif ### On mingw use Windows threads, otherwise POSIX ifneq ($(comp),mingw) # On Android Bionic's C library comes with its own pthread implementation bundled in - ifneq ($(arch),armv7) + ifneq ($(OS),Android) # Haiku has pthreads in its libroot, so only link it in on other platforms - ifneq ($(UNAME),Haiku) + ifneq ($(KERNEL),Haiku) LDFLAGS += -lpthread endif endif endif -### 3.2 Debugging +### 3.2.1 Debugging ifeq ($(debug),no) CXXFLAGS += -DNDEBUG else CXXFLAGS += -g endif +### 3.2.2 Debugging with undefined behavior sanitizers +ifeq ($(sanitize),yes) + CXXFLAGS += -g3 -fsanitize=undefined + LDFLAGS += -fsanitize=undefined +endif + ### 3.3 Optimization ifeq ($(optimize),yes) @@ -245,7 +271,7 @@ ifeq ($(optimize),yes) ifeq ($(comp),gcc) - ifeq ($(UNAME),Darwin) + ifeq ($(KERNEL),Darwin) ifeq ($(arch),i386) CXXFLAGS += -mdynamic-no-pic endif @@ -254,23 +280,21 @@ ifeq ($(optimize),yes) endif endif - ifeq ($(arch),armv7) + ifeq ($(OS), Android) CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp endif endif ifeq ($(comp),icc) - ifeq ($(UNAME),Darwin) + ifeq ($(KERNEL),Darwin) CXXFLAGS += -mdynamic-no-pic endif endif ifeq ($(comp),clang) - ifeq ($(UNAME),Darwin) - ifeq ($(pext),no) + ifeq ($(KERNEL),Darwin) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) - endif ifeq ($(arch),i386) CXXFLAGS += -mdynamic-no-pic endif @@ -326,7 +350,7 @@ ifeq ($(comp),gcc) endif ifeq ($(comp),mingw) - ifeq ($(UNAME),Linux) + ifeq ($(KERNEL),Linux) ifeq ($(optimize),yes) ifeq ($(debug),no) CXXFLAGS += -flto @@ -338,7 +362,7 @@ endif ### 3.9 Android 5 can only run position independent executables. Note that this ### breaks Android 4.0 and earlier. -ifeq ($(arch),armv7) +ifeq ($(OS), Android) CXXFLAGS += -fPIE LDFLAGS += -fPIE -pie endif @@ -394,30 +418,27 @@ help: @echo "" -.PHONY: build profile-build -build: - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity +.PHONY: help build profile-build strip install clean objclean profileclean help \ + config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ + clang-profile-use clang-profile-make + +build: config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all -profile-build: - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity +profile-build: config-sanity objclean profileclean @echo "" - @echo "Step 0/4. Preparing for profile build." - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) - @echo "" - @echo "Step 1/4. Building executable for benchmark ..." - @touch *.cpp *.h syzygy/*.cpp syzygy/*.h + @echo "Step 1/4. Building instrumented executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." $(PGOBENCH) > /dev/null @echo "" - @echo "Step 3/4. Building final executable ..." - @touch *.cpp *.h syzygy/*.cpp syzygy/*.h + @echo "Step 3/4. Building optimized executable ..." + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) @echo "" @echo "Step 4/4. Deleting profile data ..." - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean strip: strip $(EXE) @@ -427,8 +448,19 @@ install: -cp $(EXE) $(BINDIR) -strip $(BINDIR)/$(EXE) -clean: - $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda ./syzygy/*.o ./syzygy/*.gcda +#clean all +clean: objclean profileclean + @rm -f .depend *~ core + +# clean binaries and objects +objclean: + @rm -f $(EXE) $(EXE).exe *.o ./syzygy/*.o + +# clean auxiliary profiling files +profileclean: + @rm -rf profdir + @rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno + @rm -f stockfish.profdata *.profraw default: help @@ -443,9 +475,12 @@ config-sanity: @echo "" @echo "Config:" @echo "debug: '$(debug)'" + @echo "sanitize: '$(sanitize)'" @echo "optimize: '$(optimize)'" @echo "arch: '$(arch)'" @echo "bits: '$(bits)'" + @echo "kernel: '$(KERNEL)'" + @echo "os: '$(OS)'" @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" @echo "sse: '$(sse)'" @@ -459,6 +494,7 @@ config-sanity: @echo "Testing config sanity. If this fails, try 'make help' ..." @echo "" @test "$(debug)" = "yes" || test "$(debug)" = "no" + @test "$(sanitize)" = "yes" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7" @@ -472,8 +508,18 @@ config-sanity: $(EXE): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) -gcc-profile-prepare: - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean +clang-profile-make: + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ + EXTRACXXFLAGS='-fprofile-instr-generate ' \ + EXTRALDFLAGS=' -fprofile-instr-generate' \ + all + +clang-profile-use: + llvm-profdata merge -output=stockfish.profdata *.profraw + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ + EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ + EXTRALDFLAGS='-fprofile-use ' \ + all gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ @@ -487,14 +533,8 @@ gcc-profile-use: EXTRALDFLAGS='-lgcov' \ all -gcc-profile-clean: - @rm -rf *.gcda *.gcno syzygy/*.gcda syzygy/*.gcno bench.txt - -icc-profile-prepare: - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean - @mkdir profdir - icc-profile-make: + @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \ all @@ -504,9 +544,6 @@ icc-profile-use: EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ all -icc-profile-clean: - @rm -rf profdir bench.txt - .depend: -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null diff --git a/Engines/Windows/deepfish/srcD/benchmark.cpp b/Engines/Windows/mcbrain/src/benchmark.cpp similarity index 94% rename from Engines/Windows/deepfish/srcD/benchmark.cpp rename to Engines/Windows/mcbrain/src/benchmark.cpp index 9512540..21c6ec1 100644 --- a/Engines/Windows/deepfish/srcD/benchmark.cpp +++ b/Engines/Windows/mcbrain/src/benchmark.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,7 +76,14 @@ const vector Defaults = { "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw // 7-man positions - "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124" // Draw + "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw + + // Mate and stalemate positions + "8/8/8/8/8/6k1/6p1/6K1 w - -", + "5k2/5P2/5K2/8/8/8/8/8 b - -", + "8/8/8/8/8/4k3/4p3/4K3 w - -", + "8/8/8/8/8/5K2/8/3Q1k2 b - -", + "7k/7P/6K1/8/3B4/8/8/8 b - -" }; } // namespace @@ -110,7 +117,7 @@ void benchmark(const Position& current, istream& is) { limits.movetime = stoi(limit); // movetime is in millisecs else if (limitType == "nodes") - limits.nodes = stoi(limit); + limits.nodes = stoll(limit); else if (limitType == "mate") limits.mate = stoi(limit); diff --git a/Engines/Windows/deepfish/srcD/bitbase.cpp b/Engines/Windows/mcbrain/src/bitbase.cpp similarity index 92% rename from Engines/Windows/deepfish/srcD/bitbase.cpp rename to Engines/Windows/mcbrain/src/bitbase.cpp index 25570e1..1a6e807 100644 --- a/Engines/Windows/deepfish/srcD/bitbase.cpp +++ b/Engines/Windows/mcbrain/src/bitbase.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -123,9 +123,9 @@ namespace { // Immediate win if a pawn can be promoted without getting captured else if ( us == WHITE && rank_of(psq) == RANK_7 - && ksq[us] != psq + DELTA_N - && ( distance(ksq[~us], psq + DELTA_N) > 1 - || (StepAttacksBB[KING][ksq[us]] & (psq + DELTA_N)))) + && ksq[us] != psq + NORTH + && ( distance(ksq[~us], psq + NORTH) > 1 + || (StepAttacksBB[KING][ksq[us]] & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn @@ -166,12 +166,12 @@ namespace { if (Us == WHITE) { if (rank_of(psq) < RANK_7) // Single push - r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N)]; + r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)]; if ( rank_of(psq) == RANK_2 // Double push - && psq + DELTA_N != ksq[Us] - && psq + DELTA_N != ksq[Them]) - r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N + DELTA_N)]; + && psq + NORTH != ksq[Us] + && psq + NORTH != ksq[Them]) + r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)]; } return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; diff --git a/Engines/Windows/deepfish/srcD/bitboard.cpp b/Engines/Windows/mcbrain/src/bitboard.cpp similarity index 98% rename from Engines/Windows/deepfish/srcD/bitboard.cpp rename to Engines/Windows/mcbrain/src/bitboard.cpp index 318ce04..3f05d8b 100644 --- a/Engines/Windows/deepfish/srcD/bitboard.cpp +++ b/Engines/Windows/mcbrain/src/bitboard.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -205,8 +205,8 @@ void Bitboards::init() { StepAttacksBB[make_piece(c, pt)][s] |= to; } - Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; - Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; + Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST }; + Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index); init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index); diff --git a/Engines/Windows/deepfish/srcD/bitboard.h b/Engines/Windows/mcbrain/src/bitboard.h similarity index 93% rename from Engines/Windows/deepfish/srcD/bitboard.h rename to Engines/Windows/mcbrain/src/bitboard.h index 390966e..bef6467 100644 --- a/Engines/Windows/deepfish/srcD/bitboard.h +++ b/Engines/Windows/mcbrain/src/bitboard.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -124,13 +124,13 @@ inline Bitboard file_bb(Square s) { } -/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns +/// shift() moves a bitboard one step along direction D. Mainly for pawns -template -inline Bitboard shift_bb(Bitboard b) { - return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 - : Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7 - : Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 +template +inline Bitboard shift(Bitboard b) { + return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 + : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 + : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : 0; } @@ -164,7 +164,7 @@ inline Bitboard in_front_bb(Color c, Rank r) { /// forward_bb() returns a bitboard representing all the squares along the line /// in front of the given one, from the point of view of the given color: -/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) +/// ForwardBB[c][s] = in_front_bb(c, rank_of(s)) & file_bb(s) inline Bitboard forward_bb(Color c, Square s) { return ForwardBB[c][s]; @@ -174,7 +174,7 @@ inline Bitboard forward_bb(Color c, Square s) { /// pawn_attack_span() returns a bitboard representing all the squares that can be /// attacked by a pawn of the given color when it moves along its file, starting /// from the given square: -/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s); +/// PawnAttackSpan[c][s] = in_front_bb(c, rank_of(s)) & adjacent_files_bb(s); inline Bitboard pawn_attack_span(Color c, Square s) { return PawnAttackSpan[c][s]; @@ -291,7 +291,7 @@ inline Square lsb(Bitboard b) { inline Square msb(Bitboard b) { assert(b); - return Square(63 - __builtin_clzll(b)); + return Square(63 ^ __builtin_clzll(b)); } #elif defined(_WIN64) && defined(_MSC_VER) diff --git a/Engines/Windows/deepfish/srcD/endgame.cpp b/Engines/Windows/mcbrain/src/endgame.cpp similarity index 94% rename from Engines/Windows/deepfish/srcD/endgame.cpp rename to Engines/Windows/mcbrain/src/endgame.cpp index 04469e8..d382478 100644 --- a/Engines/Windows/deepfish/srcD/endgame.cpp +++ b/Engines/Windows/mcbrain/src/endgame.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -83,26 +83,6 @@ namespace { return sq; } - // Get the material key of Position out of the given endgame key code - // like "KBPKN". The trick here is to first forge an ad-hoc FEN string - // and then let a Position object do the work for us. - Key key(const string& code, Color c) { - - assert(code.length() > 0 && code.length() < 8); - assert(code[0] == 'K'); - - string sides[] = { code.substr(code.find('K', 1)), // Weak - code.substr(0, code.find('K', 1)) }; // Strong - - std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); - - string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" - + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10"; - - StateInfo st; - return Position().set(fen, false, &st, nullptr).material_key(); - } - } // namespace @@ -132,8 +112,9 @@ Endgames::Endgames() { template void Endgames::add(const string& code) { - map()[key(code, WHITE)] = std::unique_ptr>(new Endgame(WHITE)); - map()[key(code, BLACK)] = std::unique_ptr>(new Endgame(BLACK)); + StateInfo st; + map()[Position().set(code, WHITE, &st).material_key()] = std::unique_ptr>(new Endgame(WHITE)); + map()[Position().set(code, BLACK, &st).material_key()] = std::unique_ptr>(new Endgame(BLACK)); } @@ -259,8 +240,8 @@ Value Endgame::operator()(const Position& pos) const { result = Value(80) - 8 * distance(wksq, psq); else - result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S) - - distance(bksq, psq + DELTA_S) + result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) + - distance(bksq, psq + SOUTH) - distance(psq, queeningSq)); return strongSide == pos.side_to_move() ? result : -result; @@ -496,7 +477,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. if ( r <= RANK_5 - && bksq == wpsq + DELTA_N + && bksq == wpsq + NORTH && distance(wksq, wpsq) - tempo >= 2 && distance(wksq, brsq) - tempo >= 2) return SCALE_FACTOR_DRAW; @@ -517,10 +498,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && file_of(wrsq) == f && wrsq < wpsq && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) - && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo) + && (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo) && ( distance(bksq, wrsq) + tempo >= 3 || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo - && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo)))) + && (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - 8 * distance(wpsq, queeningSq) - 2 * distance(wksq, queeningSq)); @@ -671,17 +652,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (relative_rank(strongSide, pawnSq) <= RANK_5) return SCALE_FACTOR_DRAW; - else - { - Bitboard path = forward_bb(strongSide, pawnSq); + + Bitboard path = forward_bb(strongSide, pawnSq); - if (path & pos.pieces(weakSide, KING)) - return SCALE_FACTOR_DRAW; + if (path & pos.pieces(weakSide, KING)) + return SCALE_FACTOR_DRAW; - if ( (pos.attacks_from(weakBishopSq) & path) - && distance(weakBishopSq, pawnSq) >= 3) - return SCALE_FACTOR_DRAW; - } + if ( (pos.attacks_from(weakBishopSq) & path) + && distance(weakBishopSq, pawnSq) >= 3) + return SCALE_FACTOR_DRAW; } return SCALE_FACTOR_NONE; } diff --git a/Engines/Windows/deepfish/srcD/endgame.h b/Engines/Windows/mcbrain/src/endgame.h similarity index 98% rename from Engines/Windows/deepfish/srcD/endgame.h rename to Engines/Windows/mcbrain/src/endgame.h index 5f6b4bb..509e8d4 100644 --- a/Engines/Windows/deepfish/srcD/endgame.h +++ b/Engines/Windows/mcbrain/src/endgame.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Engines/Windows/deepfish/srcD/evaluate.cpp b/Engines/Windows/mcbrain/src/evaluate.cpp similarity index 66% rename from Engines/Windows/deepfish/srcD/evaluate.cpp rename to Engines/Windows/mcbrain/src/evaluate.cpp index d533eed..6337045 100644 --- a/Engines/Windows/deepfish/srcD/evaluate.cpp +++ b/Engines/Windows/mcbrain/src/evaluate.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -73,6 +73,10 @@ namespace { // by the evaluation functions. struct EvalInfo { + Material::Entry* me; + Pawns::Entry* pe; + Bitboard mobilityArea[COLOR_NB]; + // attackedBy[color][piece type] is a bitboard representing all squares // attacked by a given color and piece type (can be also ALL_PIECES). Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; @@ -106,47 +110,36 @@ namespace { // a white knight on g5 and black's king is on g8, this white knight adds 2 // to kingAdjacentZoneAttacksCount[WHITE]. int kingAdjacentZoneAttacksCount[COLOR_NB]; - - Bitboard pinnedPieces[COLOR_NB]; - Material::Entry* me; - Pawns::Entry* pi; }; #define V(v) Value(v) #define S(mg, eg) make_score(mg, eg) - // MobilityBonus[PieceType][attacked] contains bonuses for middle and end - // game, indexed by piece type and number of attacked squares in the MobilityArea. - const Score MobilityBonus[][32] = { - {}, {}, - { S(-75,-76), S(-56,-54), S( -9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights - S( 22, 26), S( 30, 28), S( 36, 29) }, - { S(-48,-58), S(-21,-19), S( 16, -2), S( 26, 12), S( 37, 22), S( 51, 42), // Bishops - S( 54, 54), S( 63, 58), S( 65, 63), S( 71, 70), S( 79, 74), S( 81, 86), - S( 92, 90), S( 97, 94) }, - { S(-56,-78), S(-25,-18), S(-11, 26), S( -5, 55), S( -4, 70), S( -1, 81), // Rooks - S( 8,109), S( 14,120), S( 21,128), S( 23,143), S( 31,154), S( 32,160), - S( 43,165), S( 49,168), S( 59,169) }, - { S(-40,-35), S(-25,-12), S( 2, 7), S( 4, 19), S( 14, 37), S( 24, 55), // Queens - S( 25, 62), S( 40, 76), S( 43, 79), S( 47, 87), S( 54, 94), S( 56,102), - S( 60,111), S( 70,116), S( 72,118), S( 73,122), S( 75,128), S( 77,130), - S( 85,133), S( 94,136), S( 99,140), S(108,157), S(112,158), S(113,161), - S(118,174), S(119,177), S(123,191), S(128,199) } + // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, + // indexed by piece type and number of attacked squares in the mobility area. + const Score MobilityBonus[4][32] = { + { S(-75,-76), S(-57,-54), S( -9,-28), S( -2,-10), S( 6, 5), S( 14, 12), // Knights + S( 22, 26), S( 29, 29), S( 36, 29) }, + { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops + S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), + S( 91, 88), S( 98, 97) }, + { S(-60,-77), S(-26,-20), S(-11, 27), S( -6, 57), S( -3, 69), S( -1, 82), // Rooks + S( 10,109), S( 16,121), S( 24,131), S( 25,143), S( 32,155), S( 32,163), + S( 43,167), S( 48,171), S( 56,173) }, + { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens + S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104), + S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136), + S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175), + S(106,184), S(109,191), S(113,206), S(116,212) } }; - // Outpost[knight/bishop][supported by pawn] contains bonuses for knights and - // bishops outposts, bigger if outpost piece is supported by a pawn. + // Outpost[knight/bishop][supported by pawn] contains bonuses for minor + // pieces if they can reach an outpost square, bigger if that square is + // supported by a pawn. If the minor piece occupies an outpost square + // then score is doubled. const Score Outpost[][2] = { - { S(43,11), S(65,20) }, // Knights - { S(20, 3), S(29, 8) } // Bishops - }; - - // ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for - // knights and bishops which can reach an outpost square in one move, bigger - // if outpost square is supported by a pawn. - const Score ReachableOutpost[][2] = { - { S(21, 5), S(35, 8) }, // Knights - { S( 8, 0), S(14, 4) } // Bishops + { S(22, 6), S(33, 9) }, // Knight + { S( 9, 2), S(14, 4) } // Bishop }; // RookOnFile[semiopen/open] contains bonuses for each rook when there is no @@ -156,17 +149,21 @@ namespace { // ThreatBySafePawn[PieceType] contains bonuses according to which piece // type is attacked by a pawn which is protected or is not attacked. const Score ThreatBySafePawn[PIECE_TYPE_NB] = { - S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) }; - - // Threat[by minor/by rook][attacked PieceType] contains - // bonuses according to which piece type attacks which one. - // Attacks on lesser pieces which are pawn-defended are not considered. - const Score Threat[][PIECE_TYPE_NB] = { - { S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72,107), S(48,118) }, // by Minor - { S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // by Rook + S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) + }; + + // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to + // which piece type attacks which one. Attacks on lesser pieces which are + // pawn-defended are not considered. + const Score ThreatByMinor[PIECE_TYPE_NB] = { + S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72, 107), S(48, 118) }; - // ThreatByKing[on one/on many] contains bonuses for King attacks on + const Score ThreatByRook[PIECE_TYPE_NB] = { + S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) + }; + + // ThreatByKing[on one/on many] contains bonuses for king attacks on // pawns or pieces which are not pawn-defended. const Score ThreatByKing[2] = { S(3, 62), S(9, 138) }; @@ -180,7 +177,16 @@ namespace { // PassedFile[File] contains a bonus according to the file of a passed pawn const Score PassedFile[FILE_NB] = { S( 9, 10), S( 2, 10), S( 1, -8), S(-20,-12), - S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10) + S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10) + }; + + // Protector[PieceType-2][distance] contains a protecting bonus for our king, + // indexed by piece type and distance between the piece and the king. + const Score Protector[4][8] = { + { S(0, 0), S( 7, 9), S( 7, 1), S( 1, 5), S(-10,-4), S( -1,-4), S( -7,-3), S(-16,-10) }, // Knight + { S(0, 0), S(11, 8), S(-7,-1), S(-1,-2), S( -1,-7), S(-11,-3), S( -9,-1), S(-16, -1) }, // Bishop + { S(0, 0), S(10, 0), S(-2, 2), S(-5, 4), S( -6, 2), S(-14,-3), S( -2,-9), S(-12, -7) }, // Rook + { S(0, 0), S( 3,-5), S( 2,-5), S(-4, 0), S( -9,-6), S(-4, 7), S(-13,-7), S(-10, -7) } // Queen }; // Assorted bonuses and penalties used by evaluation @@ -188,15 +194,16 @@ namespace { const Score BishopPawns = S( 8, 12); const Score RookOnPawn = S( 8, 24); const Score TrappedRook = S(92, 0); - const Score CloseEnemies = S( 7, 0); - const Score SafeCheck = S(20, 20); + const Score WeakQueen = S(50, 10); const Score OtherCheck = S(10, 10); + const Score CloseEnemies = S( 7, 0); + const Score PawnlessFlank = S(20, 80); const Score ThreatByHangingPawn = S(71, 61); - const Score LooseEnemies = S( 0, 25); - const Score WeakQueen = S(35, 0); + const Score ThreatByRank = S(16, 3); const Score Hanging = S(48, 27); + const Score MobilityAdjust = S( 5, 10); const Score ThreatByPawnPush = S(38, 22); - const Score Unstoppable = S( 0, 20); + const Score HinderPassedPawn = S( 7, 0); // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only @@ -206,22 +213,17 @@ namespace { #undef S #undef V - // King danger constants and variables. The king danger scores are looked-up - // in KingDanger[]. Various little "meta-bonuses" measuring the strength - // of the enemy attack are added up into an integer, which is used as an - // index to KingDanger[]. - Score KingDanger[400]; - // KingAttackWeights[PieceType] contains king attack weights by piece type - const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 7, 5, 4, 1 }; + const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 }; // Penalties for enemy's safe checks - const int QueenContactCheck = 89; - const int QueenCheck = 62; - const int RookCheck = 57; - const int BishopCheck = 48; - const int KnightCheck = 78; + const int QueenCheck = 745; + const int RookCheck = 688; + const int BishopCheck = 588; + const int KnightCheck = 924; + // Threshold for lazy evaluation + const Value LazyThreshold = Value(1500); // eval_init() initializes king and attack bitboards for a given color // adding pawn attacks. To be done at the beginning of the evaluation. @@ -229,25 +231,34 @@ namespace { template void eval_init(const Position& pos, EvalInfo& ei) { - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Square Down = (Us == WHITE ? SOUTH : NORTH); + const Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB); + + // Find our pawns on the first two ranks, and those which are blocked + Bitboard b = pos.pieces(Us, PAWN) & (shift(pos.pieces()) | LowRanks); + + // Squares occupied by those pawns, by our king, or controlled by enemy pawns + // are excluded from the mobility area. + ei.mobilityArea[Us] = ~(b | pos.square(Us) | ei.pe->pawn_attacks(Them)); - ei.pinnedPieces[Us] = pos.pinned_pieces(Us); - Bitboard b = ei.attackedBy[Them][KING]; - ei.attackedBy[Them][ALL_PIECES] |= b; - ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); - ei.attackedBy2[Us] = ei.attackedBy[Us][PAWN] & ei.attackedBy[Us][KING]; + // Initialise the attack bitboards with the king and pawn information + b = ei.attackedBy[Us][KING] = pos.attacks_from(pos.square(Us)); + ei.attackedBy[Us][PAWN] = ei.pe->pawn_attacks(Us); - // Init king safety tables only if we are going to use them - if (pos.non_pawn_material(Us) >= QueenValueMg) + ei.attackedBy2[Us] = b & ei.attackedBy[Us][PAWN]; + ei.attackedBy[Us][ALL_PIECES] = b | ei.attackedBy[Us][PAWN]; + + // Init our king safety tables only if we are going to use them + if (pos.non_pawn_material(Them) >= QueenValueMg) { - ei.kingRing[Them] = b | shift_bb(b); - b &= ei.attackedBy[Us][PAWN]; - ei.kingAttackersCount[Us] = popcount(b); - ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; + ei.kingRing[Us] = b | shift(b); + ei.kingAttackersCount[Them] = popcount(b & ei.pe->pawn_attacks(Them)); + ei.kingAdjacentZoneAttacksCount[Them] = ei.kingAttackersWeight[Them] = 0; } else - ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; + ei.kingRing[Us] = ei.kingAttackersCount[Them] = 0; } @@ -255,11 +266,7 @@ namespace { // color and type. template - Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, - const Bitboard* mobilityArea) { - Bitboard b, bb; - Square s; - Score score = SCORE_ZERO; + Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility) { const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1)); const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -267,6 +274,10 @@ namespace { : Rank5BB | Rank4BB | Rank3BB); const Square* pl = pos.squares(Us); + Bitboard b, bb; + Square s; + Score score = SCORE_ZERO; + ei.attackedBy[Us][Pt] = 0; while ((s = *pl++) != SQ_NONE) @@ -276,7 +287,7 @@ namespace { : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) : pos.attacks_from(s); - if (ei.pinnedPieces[Us] & s) + if (pos.pinned_pieces(Us) & s) b &= LineBB[pos.square(Us)][s]; ei.attackedBy2[Us] |= ei.attackedBy[Us][ALL_PIECES] & b; @@ -289,26 +300,24 @@ namespace { ei.kingAdjacentZoneAttacksCount[Us] += popcount(b & ei.attackedBy[Them][KING]); } - if (Pt == QUEEN) - b &= ~( ei.attackedBy[Them][KNIGHT] - | ei.attackedBy[Them][BISHOP] - | ei.attackedBy[Them][ROOK]); + int mob = popcount(b & ei.mobilityArea[Us]); - int mob = popcount(b & mobilityArea[Us]); + mobility[Us] += MobilityBonus[Pt-2][mob]; - mobility[Us] += MobilityBonus[Pt][mob]; + // Bonus for this piece as a king protector + score += Protector[Pt-2][distance(s, pos.square(Us))]; if (Pt == BISHOP || Pt == KNIGHT) { // Bonus for outpost squares - bb = OutpostRanks & ~ei.pi->pawn_attacks_span(Them); + bb = OutpostRanks & ~ei.pe->pawn_attacks_span(Them); if (bb & s) - score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & s)]; + score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & s)] * 2; else { bb &= b & ~pos.pieces(Us); if (bb) - score += ReachableOutpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & bb)]; + score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & bb)]; } // Bonus when behind a pawn @@ -318,7 +327,7 @@ namespace { // Penalty for pawns on the same color square as the bishop if (Pt == BISHOP) - score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + score -= BishopPawns * ei.pe->pawns_on_same_color_squares(Us, s); // An important Chess960 pattern: A cornered bishop blocked by a friendly // pawn diagonally in front of it is a very serious problem, especially @@ -327,7 +336,7 @@ namespace { && pos.is_chess960() && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) { - Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); + Square d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2 @@ -342,17 +351,16 @@ namespace { score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); // Bonus when on an open or semi-open file - if (ei.pi->semiopen_file(Us, file_of(s))) - score += RookOnFile[!!ei.pi->semiopen_file(Them, file_of(s))]; + if (ei.pe->semiopen_file(Us, file_of(s))) + score += RookOnFile[!!ei.pe->semiopen_file(Them, file_of(s))]; - // Penalize when trapped by the king, even more if the king cannot castle + // Penalty when trapped by the king, even more if the king cannot castle else if (mob <= 3) { Square ksq = pos.square(Us); if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) - && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) - && !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) + && !ei.pe->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); } } @@ -360,7 +368,8 @@ namespace { if (Pt == QUEEN) { // Penalty if any relative pin or discovered attack against the queen - if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s)) + Bitboard pinners; + if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, pinners)) score -= WeakQueen; } } @@ -369,47 +378,44 @@ namespace { Trace::add(Pt, Us, score); // Recursively call evaluate_pieces() of next piece type until KING is excluded - return score - evaluate_pieces(pos, ei, mobility, mobilityArea); + return score - evaluate_pieces(pos, ei, mobility); } template<> - Score evaluate_pieces(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; } + Score evaluate_pieces(const Position&, EvalInfo&, Score*) { return SCORE_ZERO; } template<> - Score evaluate_pieces< true, WHITE, KING>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; } + Score evaluate_pieces< true, WHITE, KING>(const Position&, EvalInfo&, Score*) { return SCORE_ZERO; } // evaluate_king() assigns bonuses and penalties to a king of a given color - const Bitboard WhiteCamp = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB; - const Bitboard BlackCamp = Rank8BB | Rank7BB | Rank6BB | Rank5BB | Rank4BB; const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; - const Bitboard KingFlank[COLOR_NB][FILE_NB] = { - { QueenSide & WhiteCamp, QueenSide & WhiteCamp, QueenSide & WhiteCamp, CenterFiles & WhiteCamp, - CenterFiles & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp }, - { QueenSide & BlackCamp, QueenSide & BlackCamp, QueenSide & BlackCamp, CenterFiles & BlackCamp, - CenterFiles & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp }, + const Bitboard KingFlank[FILE_NB] = { + QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide }; template Score evaluate_king(const Position& pos, const EvalInfo& ei) { - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Bitboard Camp = (Us == WHITE ? ~Bitboard(0) ^ Rank6BB ^ Rank7BB ^ Rank8BB + : ~Bitboard(0) ^ Rank1BB ^ Rank2BB ^ Rank3BB); - Bitboard undefended, b, b1, b2, safe, other; - int attackUnits; const Square ksq = pos.square(Us); + Bitboard undefended, b, b1, b2, safe, other; + int kingDanger; // King shelter and enemy pawns storm - Score score = ei.pi->king_safety(pos, ksq); + Score score = ei.pe->king_safety(pos, ksq); // Main king safety evaluation if (ei.kingAttackersCount[Them]) { - // Find the attacked squares which are defended only by the king... + // Find the attacked squares which are defended only by our king... undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING] & ~ei.attackedBy2[Us]; @@ -418,56 +424,51 @@ namespace { b = ei.attackedBy[Them][ALL_PIECES] & ~ei.attackedBy[Us][ALL_PIECES] & ei.kingRing[Us] & ~pos.pieces(Them); - // Initialize the 'attackUnits' variable, which is used later on as an - // index into the KingDanger[] array. The initial value is based on the + // Initialize the 'kingDanger' variable, which will be transformed + // later into a king danger score. The initial value is based on the // number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king and the quality of // the pawn shelter (current 'score' value). - attackUnits = std::min(72, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) - + 9 * ei.kingAdjacentZoneAttacksCount[Them] - + 21 * popcount(undefended) - + 12 * (popcount(b) + !!ei.pinnedPieces[Us]) - - 64 * !pos.count(Them) - - mg_value(score) / 8; - - // Analyse the enemy's safe queen contact checks. Firstly, find the - // undefended squares around the king reachable by the enemy queen... - b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); + kingDanger = std::min(807, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + + 101 * ei.kingAdjacentZoneAttacksCount[Them] + + 235 * popcount(undefended) + + 134 * (popcount(b) + !!pos.pinned_pieces(Us)) + - 717 * !pos.count(Them) + - 7 * mg_value(score) / 5 - 5; - // ...and keep squares supported by another enemy piece - attackUnits += QueenContactCheck * popcount(b & ei.attackedBy2[Them]); - - // Analyse the safe enemy's checks which are possible on next move... - safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); - - // ... and some other potential checks, only requiring the square to be - // safe from pawn-attacks, and not being occupied by a blocked pawn. - other = ~( ei.attackedBy[Us][PAWN] - | (pos.pieces(Them, PAWN) & shift_bb(pos.pieces(PAWN)))); + // Analyse the safe enemy's checks which are possible on next move + safe = ~pos.pieces(Them); + safe &= ~ei.attackedBy[Us][ALL_PIECES] | (undefended & ei.attackedBy2[Them]); b1 = pos.attacks_from(ksq); b2 = pos.attacks_from(ksq); // Enemy queen safe checks if ((b1 | b2) & ei.attackedBy[Them][QUEEN] & safe) - attackUnits += QueenCheck, score -= SafeCheck; + kingDanger += QueenCheck; - // For other pieces, also consider the square safe if attacked twice, - // and only defended by a queen. + // For minors and rooks, also consider the square safe if attacked twice, + // and only defended by our queen. safe |= ei.attackedBy2[Them] & ~(ei.attackedBy2[Us] | pos.pieces(Them)) & ei.attackedBy[Us][QUEEN]; + // Some other potential checks are also analysed, even from squares + // currently occupied by the opponent own pieces, as long as the square + // is not attacked by our pawns, and is not occupied by a blocked pawn. + other = ~( ei.attackedBy[Us][PAWN] + | (pos.pieces(Them, PAWN) & shift(pos.pieces(PAWN)))); + // Enemy rooks safe and other checks if (b1 & ei.attackedBy[Them][ROOK] & safe) - attackUnits += RookCheck, score -= SafeCheck; + kingDanger += RookCheck; else if (b1 & ei.attackedBy[Them][ROOK] & other) score -= OtherCheck; // Enemy bishops safe and other checks if (b2 & ei.attackedBy[Them][BISHOP] & safe) - attackUnits += BishopCheck, score -= SafeCheck; + kingDanger += BishopCheck; else if (b2 & ei.attackedBy[Them][BISHOP] & other) score -= OtherCheck; @@ -475,18 +476,19 @@ namespace { // Enemy knights safe and other checks b = pos.attacks_from(ksq) & ei.attackedBy[Them][KNIGHT]; if (b & safe) - attackUnits += KnightCheck, score -= SafeCheck; + kingDanger += KnightCheck; else if (b & other) score -= OtherCheck; - // Finally, extract the king danger score from the KingDanger[] - // array and subtract the score from the evaluation. - score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; + // Transform the kingDanger units into a Score, and substract it from the evaluation + if (kingDanger > 0) + score -= make_score(std::min(kingDanger * kingDanger / 4096, 2 * int(BishopValueMg)), 0); } // King tropism: firstly, find squares that opponent attacks in our king flank - b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][file_of(ksq)]; + File kf = file_of(ksq); + b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[kf] & Camp; assert(((Us == WHITE ? b << 4 : b >> 4) & b) == 0); assert(popcount(Us == WHITE ? b << 4 : b >> 4) == popcount(b)); @@ -498,6 +500,10 @@ namespace { score -= CloseEnemies * popcount(b); + // Penalty when our king is on a pawnless flank + if (!(pos.pieces(PAWN) & KingFlank[kf])) + score -= PawnlessFlank; + if (DoTrace) Trace::add(KING, Us, score); @@ -511,23 +517,16 @@ namespace { template Score evaluate_threats(const Position& pos, const EvalInfo& ei) { - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); - const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); - const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB); - const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - - enum { Minor, Rook }; + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); + const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); + const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB); + const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - Bitboard b, weak, defended, safeThreats; + Bitboard b, weak, defended, stronglyProtected, safeThreats; Score score = SCORE_ZERO; - // Small bonus if the opponent has loose pawns or pieces - if ( (pos.pieces(Them) ^ pos.pieces(Them, QUEEN, KING)) - & ~(ei.attackedBy[Us][ALL_PIECES] | ei.attackedBy[Them][ALL_PIECES])) - score += LooseEnemies; - // Non-pawn enemies attacked by a pawn weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Us][PAWN]; @@ -536,7 +535,7 @@ namespace { b = pos.pieces(Us, PAWN) & ( ~ei.attackedBy[Them][ALL_PIECES] | ei.attackedBy[Us][ALL_PIECES]); - safeThreats = (shift_bb(b) | shift_bb(b)) & weak; + safeThreats = (shift(b) | shift(b)) & weak; if (weak ^ safeThreats) score += ThreatByHangingPawn; @@ -545,12 +544,18 @@ namespace { score += ThreatBySafePawn[type_of(pos.piece_on(pop_lsb(&safeThreats)))]; } - // Non-pawn enemies defended by a pawn - defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Them][PAWN]; + // Squares strongly protected by the opponent, either because they attack the + // square with a pawn, or because they attack the square twice and we don't. + stronglyProtected = ei.attackedBy[Them][PAWN] + | (ei.attackedBy2[Them] & ~ei.attackedBy2[Us]); + + // Non-pawn enemies, strongly protected + defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) + & stronglyProtected; - // Enemies not defended by a pawn and under our attack + // Enemies not strongly protected and under our attack weak = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] + & ~stronglyProtected & ei.attackedBy[Us][ALL_PIECES]; // Add a bonus according to the kind of attacking pieces @@ -558,11 +563,21 @@ namespace { { b = (defended | weak) & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); while (b) - score += Threat[Minor][type_of(pos.piece_on(pop_lsb(&b)))]; + { + Square s = pop_lsb(&b); + score += ThreatByMinor[type_of(pos.piece_on(s))]; + if (type_of(pos.piece_on(s)) != PAWN) + score += ThreatByRank * (int)relative_rank(Them, s); + } b = (pos.pieces(Them, QUEEN) | weak) & ei.attackedBy[Us][ROOK]; while (b) - score += Threat[Rook ][type_of(pos.piece_on(pop_lsb(&b)))]; + { + Square s = pop_lsb(&b); + score += ThreatByRook[type_of(pos.piece_on(s))]; + if (type_of(pos.piece_on(s)) != PAWN) + score += ThreatByRank * (int)relative_rank(Them, s); + } score += Hanging * popcount(weak & ~ei.attackedBy[Them][ALL_PIECES]); @@ -570,16 +585,22 @@ namespace { if (b) score += ThreatByKing[more_than_one(b)]; } + + // Some mobility bonus was allocated in evaluate_pieces for major pieces even + // for some squares controlled by the enemy. Penalize those squares. + b = (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]) + & ei.mobilityArea[Us] & stronglyProtected; + score -= MobilityAdjust * popcount(b); // Bonus if some pawns can safely push and attack an enemy piece b = pos.pieces(Us, PAWN) & ~TRank7BB; - b = shift_bb(b | (shift_bb(b & TRank2BB) & ~pos.pieces())); + b = shift(b | (shift(b & TRank2BB) & ~pos.pieces())); b &= ~pos.pieces() & ~ei.attackedBy[Them][PAWN] & (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]); - b = (shift_bb(b) | shift_bb(b)) + b = (shift(b) | shift(b)) & pos.pieces(Them) & ~ei.attackedBy[Us][PAWN]; @@ -592,25 +613,28 @@ namespace { } - // evaluate_passed_pawns() evaluates the passed pawns of the given color + // evaluate_passer_pawns() evaluates the passed pawns and candidate passed + // pawns of the given color. template - Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { + Score evaluate_passer_pawns(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b, squaresToQueen, defendedSquares, unsafeSquares; + Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; Score score = SCORE_ZERO; - b = ei.pi->passed_pawns(Us); + b = ei.pe->passed_pawns(Us); while (b) { Square s = pop_lsb(&b); - assert(pos.pawn_passed(Us, s)); assert(!(pos.pieces(PAWN) & forward_bb(Us, s))); + bb = forward_bb(Us, s) & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); + score -= HinderPassedPawn * popcount(bb); + int r = relative_rank(Us, s) - RANK_2; int rr = r * (r - 1); @@ -636,7 +660,7 @@ namespace { // in the pawn's path attacked or occupied by the enemy. defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s); - Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); + bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); if (!(pos.pieces(Us) & bb)) defendedSquares &= ei.attackedBy[Us][ALL_PIECES]; @@ -662,13 +686,17 @@ namespace { mbonus += rr + r * 2, ebonus += rr + r * 2; } // rr != 0 + // Scale down bonus for candidate passers which need more than one + // pawn push to become passed. + if (!pos.pawn_passed(Us, s + pawn_push(Us))) + mbonus /= 2, ebonus /= 2; + score += make_score(mbonus, ebonus) + PassedFile[file_of(s)]; } if (DoTrace) Trace::add(PASSED, Us, score); - // Add the scores to the middlegame and endgame eval return score; } @@ -684,8 +712,8 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); const Bitboard SpaceMask = - Us == WHITE ? (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB) - : (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB); + Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) + : CenterFiles & (Rank7BB | Rank6BB | Rank5BB); // Find the safe squares for our pieces inside the area defined by // SpaceMask. A square is unsafe if it is attacked by an enemy @@ -703,12 +731,12 @@ namespace { // Since SpaceMask[Us] is fully on our half of the board... assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0); - // ...count safe + (behind & safe) with a single popcount + // ...count safe + (behind & safe) with a single popcount. int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); bonus = std::min(16, bonus); - int weight = pos.count(Us); + int weight = pos.count(Us) - 2 * ei.pe->open_files(); - return make_score(bonus * weight * weight / 22, 0); + return make_score(bonus * weight * weight / 18, 0); } @@ -719,15 +747,15 @@ namespace { int kingDistance = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); - int pawns = pos.count(WHITE) + pos.count(BLACK); + bool bothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); // Compute the initiative bonus for the attacking side - int initiative = 8 * (asymmetry + kingDistance - 15) + 12 * pawns; + int initiative = 8 * (asymmetry + kingDistance - 17) + 12 * pos.count() + 16 * bothFlanks; // Now apply the bonus: note that we find the attacking side by extracting // the sign of the endgame value, and that we carefully cap the bonus so - // that the endgame score will never be divided by more than two. - int value = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg / 2)); + // that the endgame score will never change sign after the bonus. + int value = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg)); return make_score(0, value); } @@ -741,8 +769,7 @@ namespace { // If we don't already have an unusual scale factor, check for certain // types of endgames, and use a lower scale for those. - if ( ei.me->game_phase() < PHASE_MIDGAME - && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) + if (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN) { if (pos.opposite_bishops()) { @@ -750,19 +777,18 @@ namespace { // is almost a draw, in case of KBP vs KB, it is even more a draw. if ( pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material(BLACK) == BishopValueMg) - sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9); + return more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9); // Endgame with opposite-colored bishops, but also other pieces. Still // a bit drawish, but not as drawish as with only the two bishops. - else - sf = ScaleFactor(46); + return ScaleFactor(46); } // Endings where weaker side can place his king in front of the opponent's // pawns are drawish. else if ( abs(eg) <= BishopValueEg && pos.count(strongSide) <= 2 && !pos.pawn_passed(~strongSide, pos.square(~strongSide))) - sf = ScaleFactor(37 + 7 * pos.count(strongSide)); + return ScaleFactor(37 + 7 * pos.count(strongSide)); } return sf; @@ -779,49 +805,38 @@ Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); + Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; + Value v; EvalInfo ei; - Score score, mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; - - // Initialize score by reading the incrementally updated scores included in - // the position object (material + piece square tables). Score is computed - // internally from the white point of view. - score = pos.psq_score(); // Probe the material hash table ei.me = Material::probe(pos); - score += ei.me->imbalance(); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.me->specialized_eval_exists()) return ei.me->evaluate(pos); + // Initialize score by reading the incrementally updated scores included in + // the position object (material + piece square tables) and the material + // imbalance. Score is computed internally from the white point of view. + Score score = pos.psq_score() + ei.me->imbalance(); + // Probe the pawn hash table - ei.pi = Pawns::probe(pos); - score += ei.pi->pawns_score(); + ei.pe = Pawns::probe(pos); + score += ei.pe->pawns_score(); + + // Early exit if score is high + v = (mg_value(score) + eg_value(score)) / 2; + if (abs(v) > LazyThreshold) + return pos.side_to_move() == WHITE ? v : -v; // Initialize attack and king safety bitboards - ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0; - ei.attackedBy[WHITE][KING] = pos.attacks_from(pos.square(WHITE)); - ei.attackedBy[BLACK][KING] = pos.attacks_from(pos.square(BLACK)); eval_init(pos, ei); eval_init(pos, ei); - // Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area - Bitboard blockedPawns[] = { - pos.pieces(WHITE, PAWN) & (shift_bb(pos.pieces()) | Rank2BB | Rank3BB), - pos.pieces(BLACK, PAWN) & (shift_bb(pos.pieces()) | Rank7BB | Rank6BB) - }; - - // Do not include in mobility area squares protected by enemy pawns, or occupied - // by our blocked pawns or king. - Bitboard mobilityArea[] = { - ~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square(WHITE)), - ~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square(BLACK)) - }; - // Evaluate all pieces but king and pawns - score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility); score += mobility[WHITE] - mobility[BLACK]; // Evaluate kings after all other pieces because we need full attack @@ -834,34 +849,23 @@ Value Eval::evaluate(const Position& pos) { - evaluate_threats(pos, ei); // Evaluate passed pawns, we need full attack information including king - score += evaluate_passed_pawns(pos, ei) - - evaluate_passed_pawns(pos, ei); - - // If both sides have only pawns, score for potential unstoppable pawns - if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) - { - Bitboard b; - if ((b = ei.pi->passed_pawns(WHITE)) != 0) - score += Unstoppable * int(relative_rank(WHITE, frontmost_sq(WHITE, b))); - - if ((b = ei.pi->passed_pawns(BLACK)) != 0) - score -= Unstoppable * int(relative_rank(BLACK, frontmost_sq(BLACK, b))); - } + score += evaluate_passer_pawns(pos, ei) + - evaluate_passer_pawns(pos, ei); // Evaluate space for both sides, only during opening - if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222) + if (pos.non_pawn_material() >= 12222) score += evaluate_space(pos, ei) - evaluate_space(pos, ei); // Evaluate position potential for the winning side - score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score)); + score += evaluate_initiative(pos, ei.pe->pawn_asymmetry(), eg_value(score)); // Evaluate scale factor for the winning side ScaleFactor sf = evaluate_scale_factor(pos, ei, eg_value(score)); // Interpolate between a middlegame and a (scaled by 'sf') endgame score - Value v = mg_value(score) * int(ei.me->game_phase()) - + eg_value(score) * int(PHASE_MIDGAME - ei.me->game_phase()) * sf / SCALE_FACTOR_NORMAL; + v = mg_value(score) * int(ei.me->game_phase()) + + eg_value(score) * int(PHASE_MIDGAME - ei.me->game_phase()) * sf / SCALE_FACTOR_NORMAL; v /= int(PHASE_MIDGAME); @@ -870,10 +874,11 @@ Value Eval::evaluate(const Position& pos) { { Trace::add(MATERIAL, pos.psq_score()); Trace::add(IMBALANCE, ei.me->imbalance()); - Trace::add(PAWN, ei.pi->pawns_score()); + Trace::add(PAWN, ei.pe->pawns_score()); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); - Trace::add(SPACE, evaluate_space(pos, ei) - , evaluate_space(pos, ei)); + if (pos.non_pawn_material() >= 12222) + Trace::add(SPACE, evaluate_space(pos, ei) + , evaluate_space(pos, ei)); Trace::add(TOTAL, score); } @@ -920,19 +925,3 @@ std::string Eval::trace(const Position& pos) { return ss.str(); } - - -/// init() computes evaluation weights, usually at startup - -void Eval::init() { - - const int MaxSlope = 322; - const int Peak = 47410; - int t = 0; - - for (int i = 0; i < 400; ++i) - { - t = std::min(Peak, std::min(i * i - 16, t + MaxSlope)); - KingDanger[i] = make_score(t * 268 / 7700, 0); - } -} diff --git a/Engines/Windows/deepfish/srcD/evaluate.h b/Engines/Windows/mcbrain/src/evaluate.h similarity index 93% rename from Engines/Windows/deepfish/srcD/evaluate.h rename to Engines/Windows/mcbrain/src/evaluate.h index 26661dc..d5cf2f2 100644 --- a/Engines/Windows/deepfish/srcD/evaluate.h +++ b/Engines/Windows/mcbrain/src/evaluate.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ namespace Eval { const Value Tempo = Value(20); // Must be visible to search -void init(); std::string trace(const Position& pos); template diff --git a/Engines/Windows/deepfish/srcD/main.cpp b/Engines/Windows/mcbrain/src/main.cpp similarity index 80% rename from Engines/Windows/deepfish/srcD/main.cpp rename to Engines/Windows/mcbrain/src/main.cpp index 1d36e39..c63bc2e 100644 --- a/Engines/Windows/deepfish/srcD/main.cpp +++ b/Engines/Windows/mcbrain/src/main.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,42 +21,35 @@ #include #include "bitboard.h" -#include "evaluate.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" +#include "tzbook.h" #include "uci.h" -#include "syzygy/tbprobe.h" +#include "tbprobe.h" -void SETUP_PRIVILEGES(); -void FREE_MEM(void *); +namespace PSQT { + void init(); +} int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; - #ifndef BENCH - SETUP_PRIVILEGES(); - #endif + UCI::init(Options); - TT.resize(Options["Hash"]); PSQT::init(); Bitboards::init(); Position::init(); Bitbases::init(); - Search::init(); - Eval::init(); Pawns::init(); Threads.init(); Tablebases::init(Options["SyzygyPath"]); + TT.resize(Options["Hash"]); + tzbook.init(Options["BookPath"]); UCI::loop(argc, argv); - if (large_use) { - FREE_MEM(TT.mem); - TT.mem = nullptr; - } - Threads.exit(); return 0; -} \ No newline at end of file +} diff --git a/Engines/Windows/deepfish/srcD/material.cpp b/Engines/Windows/mcbrain/src/material.cpp similarity index 95% rename from Engines/Windows/deepfish/srcD/material.cpp rename to Engines/Windows/mcbrain/src/material.cpp index f73977b..498ef33 100644 --- a/Engines/Windows/deepfish/srcD/material.cpp +++ b/Engines/Windows/mcbrain/src/material.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,14 +31,11 @@ namespace { // Polynomial material imbalance parameters - // pair pawn knight bishop rook queen - const int Linear[6] = { 1667, -168, -1027, -166, 238, -138 }; - const int QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen - { 0 }, // Bishop pair - { 40, 2 }, // Pawn + {1667 }, // Bishop pair + { 40, 0 }, // Pawn { 32, 255, -3 }, // Knight OUR PIECES { 0, 104, 4, 0 }, // Bishop { -26, -2, 47, 105, -149 }, // Rook @@ -56,6 +53,11 @@ namespace { { 101, 100, -37, 141, 268, 0 } // Queen }; + // PawnsSet[count] contains a bonus/malus indexed by number of pawns + const int PawnsSet[9] = { + 24, -32, 107, -51, 117, -9, -126, -21, 31 + }; + // Endgame evaluation and scaling functions are accessed directly and not through // the function maps because they correspond to more than one material hash key. Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; @@ -92,7 +94,7 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); - int bonus = 0; + int bonus = PawnsSet[pieceCount[Us][PAWN]]; // Second-degree polynomial material imbalance by Tord Romstad for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) @@ -100,7 +102,7 @@ namespace { if (!pieceCount[Us][pt1]) continue; - int v = Linear[pt1]; + int v = 0; for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] diff --git a/Engines/Windows/deepfish/srcD/material.h b/Engines/Windows/mcbrain/src/material.h similarity index 97% rename from Engines/Windows/deepfish/srcD/material.h rename to Engines/Windows/mcbrain/src/material.h index bec2d66..ccf97b7 100644 --- a/Engines/Windows/deepfish/srcD/material.h +++ b/Engines/Windows/mcbrain/src/material.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -56,11 +56,11 @@ struct Entry { } Key key; - int16_t value; - uint8_t factor[COLOR_NB]; EndgameBase* evaluationFunction; EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each // side (e.g. KPKP, KBPsKs) + int16_t value; + uint8_t factor[COLOR_NB]; Phase gamePhase; }; diff --git a/Engines/Windows/deepfish/srcD/misc.cpp b/Engines/Windows/mcbrain/src/misc.cpp similarity index 53% rename from Engines/Windows/deepfish/srcD/misc.cpp rename to Engines/Windows/mcbrain/src/misc.cpp index 19e493b..880e958 100644 --- a/Engines/Windows/deepfish/srcD/misc.cpp +++ b/Engines/Windows/mcbrain/src/misc.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,10 +18,29 @@ along with this program. If not, see . */ +#ifdef _WIN32 +#if _WIN32_WINNT < 0x0601 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes +#endif +#include +// The needed Windows API for processor groups could be missed from old Windows +// versions, so instead of calling them directly (forcing the linker to resolve +// the calls at compile time), try to load them at runtime. To do this we need +// first to define the corresponding function pointers. +extern "C" { +typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); +typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); +typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); +} +#endif + #include #include #include #include +#include #include "misc.h" #include "thread.h" @@ -32,7 +51,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -static const string Version = " "; +const string Version = "2.1a"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We @@ -103,7 +122,7 @@ const string engine_info(bool to_uci) { string month, day, year; stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" - ss << "DeepFishMZ" << Version << setfill('0'); + ss << "McBrain " << Version << setfill('0'); if (Version.empty()) { @@ -114,10 +133,9 @@ const string engine_info(bool to_uci) { ss << (Is64Bit ? " 64" : "") << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) << (to_uci ? "\nid author ": " by ") - << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott\n" - << "compiled by M.Z"; + << "M. Byrne, based on Stockfish"; - return ss.str(); + return ss.str(); } @@ -186,3 +204,122 @@ void prefetch(void* addr) { } #endif + +void prefetch2(void* addr) { + + prefetch(addr); + prefetch((uint8_t*)addr + 64); +} + +namespace WinProcGroup { + +#ifndef _WIN32 + +void bindThisThread(size_t) {} + +#else + +/// get_group() retrieves logical processor information using Windows specific +/// API and returns the best group id for the thread with index idx. Original +/// code from Texel by Peter Österlund. + +int get_group(size_t idx) { + + int threads = 0; + int nodes = 0; + int cores = 0; + DWORD returnLength = 0; + DWORD byteOffset = 0; + + // Early exit if the needed API is not available at runtime + HMODULE k32 = GetModuleHandle("Kernel32.dll"); + auto fun1 = (fun1_t)GetProcAddress(k32, "GetLogicalProcessorInformationEx"); + if (!fun1) + return -1; + + // First call to get returnLength. We expect it to fail due to null buffer + if (fun1(RelationAll, nullptr, &returnLength)) + return -1; + + // Once we know returnLength, allocate the buffer + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; + ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); + + // Second call, now we expect to succeed + if (!fun1(RelationAll, buffer, &returnLength)) + { + free(buffer); + return -1; + } + + while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength) + { + if (ptr->Relationship == RelationNumaNode) + nodes++; + + else if (ptr->Relationship == RelationProcessorCore) + { + cores++; + threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; + } + + byteOffset += ptr->Size; + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); + } + + free(buffer); + + std::vector groups; + + // Run as many threads as possible on the same node until core limit is + // reached, then move on filling the next node. + for (int n = 0; n < nodes; n++) + for (int i = 0; i < cores / nodes; i++) + groups.push_back(n); + + // In case a core has more than one logical processor (we assume 2) and we + // have still threads to allocate, then spread them evenly across available + // nodes. + for (int t = 0; t < threads - cores; t++) + groups.push_back(t % nodes); + + // If we still have more threads than the total number of logical processors + // then return -1 and let the OS to decide what to do. + return idx < groups.size() ? groups[idx] : -1; +} + + +/// bindThisThread() set the group affinity of the current thread + +void bindThisThread(size_t idx) { + + // If OS already scheduled us on a different group than 0 then don't overwrite + // the choice, eventually we are one of many one-threaded processes running on + // some Windows NUMA hardware, for instance in fishtest. To make it simple, + // just check if running threads are below a threshold, in this case all this + // NUMA machinery is not needed. + if (Threads.size() < 8) + return; + + // Use only local variables to be thread-safe + int group = get_group(idx); + + if (group == -1) + return; + + // Early exit if the needed API are not available at runtime + HMODULE k32 = GetModuleHandle("Kernel32.dll"); + auto fun2 = (fun2_t)GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); + auto fun3 = (fun3_t)GetProcAddress(k32, "SetThreadGroupAffinity"); + + if (!fun2 || !fun3) + return; + + GROUP_AFFINITY affinity; + if (fun2(group, &affinity)) + fun3(GetCurrentThread(), &affinity, nullptr); +} + +#endif + +} // namespace WinProcGroup diff --git a/Engines/Windows/deepfish/srcD/misc.h b/Engines/Windows/mcbrain/src/misc.h similarity index 85% rename from Engines/Windows/deepfish/srcD/misc.h rename to Engines/Windows/mcbrain/src/misc.h index a2307fe..b63e613 100644 --- a/Engines/Windows/deepfish/srcD/misc.h +++ b/Engines/Windows/mcbrain/src/misc.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +31,7 @@ const std::string engine_info(bool to_uci = false); void prefetch(void* addr); +void prefetch2(void* addr); void start_logger(const std::string& fname); void dbg_hit_on(bool b); @@ -97,4 +98,15 @@ class PRNG { { return T(rand64() & rand64() & rand64()); } }; + +/// Under Windows it is not possible for a process to run on more than one +/// logical processor group. This usually means to be limited to use max 64 +/// cores. To overcome this, some special platform specific API should be +/// called to set group affinity for each thread. Original code from Texel by +/// Peter Österlund. + +namespace WinProcGroup { + void bindThisThread(size_t idx); +} + #endif // #ifndef MISC_H_INCLUDED diff --git a/Engines/Windows/deepfish/srcD/movegen.cpp b/Engines/Windows/mcbrain/src/movegen.cpp similarity index 88% rename from Engines/Windows/deepfish/srcD/movegen.cpp rename to Engines/Windows/mcbrain/src/movegen.cpp index efa47bd..ac5f565 100644 --- a/Engines/Windows/deepfish/srcD/movegen.cpp +++ b/Engines/Windows/mcbrain/src/movegen.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,8 +42,8 @@ namespace { assert(!pos.checkers()); - const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E - : KingSide ? DELTA_W : DELTA_E; + const Square K = Chess960 ? kto > kfrom ? WEST : EAST + : KingSide ? WEST : EAST; for (Square s = kto; s != kfrom; s += K) if (pos.attackers_to(s) & enemies) @@ -65,23 +65,23 @@ namespace { } - template + template ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - *moveList++ = make(to - Delta, to, QUEEN); + *moveList++ = make(to - D, to, QUEEN); if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { - *moveList++ = make(to - Delta, to, ROOK); - *moveList++ = make(to - Delta, to, BISHOP); - *moveList++ = make(to - Delta, to, KNIGHT); + *moveList++ = make(to - D, to, ROOK); + *moveList++ = make(to - D, to, BISHOP); + *moveList++ = make(to - D, to, KNIGHT); } // Knight promotion is the only promotion that can give a direct check // that's not already included in the queen promotion. if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ksq)) - *moveList++ = make(to - Delta, to, KNIGHT); + *moveList++ = make(to - D, to, KNIGHT); else (void)ksq; // Silence a warning under MSVC @@ -94,13 +94,13 @@ namespace { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); - const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); - const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); + const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); + const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); + const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); Bitboard emptySquares; @@ -115,8 +115,8 @@ namespace { { emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); - Bitboard b1 = shift_bb(pawnsNotOn7) & emptySquares; - Bitboard b2 = shift_bb(b1 & TRank3BB) & emptySquares; + Bitboard b1 = shift(pawnsNotOn7) & emptySquares; + Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; if (Type == EVASIONS) // Consider only blocking squares { @@ -138,8 +138,8 @@ namespace { Bitboard dcCandidates = pos.discovered_check_candidates(); if (pawnsNotOn7 & dcCandidates) { - Bitboard dc1 = shift_bb(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); - Bitboard dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; + Bitboard dc1 = shift(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); + Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; b1 |= dc1; b2 |= dc2; @@ -168,9 +168,9 @@ namespace { if (Type == EVASIONS) emptySquares &= target; - Bitboard b1 = shift_bb(pawnsOn7) & enemies; - Bitboard b2 = shift_bb(pawnsOn7) & enemies; - Bitboard b3 = shift_bb(pawnsOn7) & emptySquares; + Bitboard b1 = shift(pawnsOn7) & enemies; + Bitboard b2 = shift(pawnsOn7) & enemies; + Bitboard b3 = shift(pawnsOn7) & emptySquares; Square ksq = pos.square(Them); @@ -187,8 +187,8 @@ namespace { // Standard and en-passant captures if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { - Bitboard b1 = shift_bb(pawnsNotOn7) & enemies; - Bitboard b2 = shift_bb(pawnsNotOn7) & enemies; + Bitboard b1 = shift(pawnsNotOn7) & enemies; + Bitboard b2 = shift(pawnsNotOn7) & enemies; while (b1) { diff --git a/Engines/Windows/deepfish/srcD/movegen.h b/Engines/Windows/mcbrain/src/movegen.h similarity index 96% rename from Engines/Windows/deepfish/srcD/movegen.h rename to Engines/Windows/mcbrain/src/movegen.h index 2721f15..33177ed 100644 --- a/Engines/Windows/deepfish/srcD/movegen.h +++ b/Engines/Windows/mcbrain/src/movegen.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Engines/Windows/deepfish/srcD/movepick.cpp b/Engines/Windows/mcbrain/src/movepick.cpp similarity index 57% rename from Engines/Windows/deepfish/srcD/movepick.cpp rename to Engines/Windows/mcbrain/src/movepick.cpp index 78765be..bfa5e4f 100644 --- a/Engines/Windows/deepfish/srcD/movepick.cpp +++ b/Engines/Windows/mcbrain/src/movepick.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,13 +26,12 @@ namespace { enum Stages { - MAIN_SEARCH, GOOD_CAPTURES, KILLERS, QUIET, BAD_CAPTURES, - EVASION, ALL_EVASIONS, - QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS, - QSEARCH_WITHOUT_CHECKS, QCAPTURES_2, - PROBCUT, PROBCUT_CAPTURES, - RECAPTURE, RECAPTURES, - STOP + MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLERS, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES, + EVASION, EVASIONS_INIT, ALL_EVASIONS, + PROBCUT, PROBCUT_INIT, PROBCUT_CAPTURES, + QSEARCH_WITH_CHECKS, QCAPTURES_1_INIT, QCAPTURES_1, QCHECKS, + QSEARCH_NO_CHECKS, QCAPTURES_2_INIT, QCAPTURES_2, + QSEARCH_RECAPTURES, QRECAPTURES }; // Our insertion sort, which is guaranteed to be stable, as it should be @@ -77,7 +76,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s) stage = pos.checkers() ? EVASION : MAIN_SEARCH; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) @@ -92,17 +91,17 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) stage = QSEARCH_WITH_CHECKS; else if (d > DEPTH_QS_RECAPTURES) - stage = QSEARCH_WITHOUT_CHECKS; + stage = QSEARCH_NO_CHECKS; else { - stage = RECAPTURE; + stage = QSEARCH_RECAPTURES; recaptureSquare = s; - ttm = MOVE_NONE; + return; } ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Value th) @@ -112,13 +111,13 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th) stage = PROBCUT; - // In ProbCut we generate captures with SEE higher than the given threshold + // In ProbCut we generate captures with SEE higher than or equal to the given threshold ttMove = ttm && pos.pseudo_legal(ttm) && pos.capture(ttm) - && pos.see(ttm) > threshold ? ttm : MOVE_NONE; + && pos.see_ge(ttm, threshold)? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } @@ -142,71 +141,102 @@ template<> void MovePicker::score() { const HistoryStats& history = pos.this_thread()->history; - const FromToStats& fromTo = pos.this_thread()->fromTo; - const CounterMoveStats* cm = (ss-1)->counterMoves; - const CounterMoveStats* fm = (ss-2)->counterMoves; - const CounterMoveStats* f2 = (ss-4)->counterMoves; + const CounterMoveStats& cmh = *(ss-1)->counterMoves; + const CounterMoveStats& fmh = *(ss-2)->counterMoves; + const CounterMoveStats& fm2 = *(ss-4)->counterMoves; Color c = pos.side_to_move(); for (auto& m : *this) - m.value = history[pos.moved_piece(m)][to_sq(m)] - + (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) - + (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) - + (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) - + fromTo.get(c, m); + m.value = cmh[pos.moved_piece(m)][to_sq(m)] + + fmh[pos.moved_piece(m)][to_sq(m)] + + fm2[pos.moved_piece(m)][to_sq(m)] + + history.get(c, m); } template<> void MovePicker::score() { - // Try winning and equal captures ordered by MVV/LVA, then non-captures ordered - // by history value, then bad captures and quiet moves with a negative SEE ordered - // by SEE value. + // Try captures ordered by MVV/LVA, then non-captures ordered by stats heuristics const HistoryStats& history = pos.this_thread()->history; - const FromToStats& fromTo = pos.this_thread()->fromTo; Color c = pos.side_to_move(); - Value see; for (auto& m : *this) - if ((see = pos.see_sign(m)) < VALUE_ZERO) - m.value = see - HistoryStats::Max; // At the bottom - - else if (pos.capture(m)) + if (pos.capture(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; else - m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m); + m.value = history.get(c, m); } -/// generate_next_stage() generates, scores, and sorts the next bunch of moves -/// when there are no more moves to try for the current stage. +/// next_move() is the most important method of the MovePicker class. It returns +/// a new pseudo legal move every time it is called, until there are no more moves +/// left. It picks the move with the biggest value from a list of generated moves +/// taking care not to return the ttMove if it has already been searched. -void MovePicker::generate_next_stage() { +Move MovePicker::next_move(bool skipQuiets, bool skipBadCaptures) { - assert(stage != STOP); + Move move; - cur = moves; + switch (stage) { - switch (++stage) { + case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: + case QSEARCH_NO_CHECKS: case PROBCUT: + ++stage; + return ttMove; - case GOOD_CAPTURES: case QCAPTURES_1: case QCAPTURES_2: - case PROBCUT_CAPTURES: case RECAPTURES: - endMoves = generate(pos, moves); + case CAPTURES_INIT: + endBadCaptures = cur = moves; + endMoves = generate(pos, cur); score(); - break; + ++stage; - case KILLERS: - killers[0] = ss->killers[0]; - killers[1] = ss->killers[1]; - killers[2] = countermove; - cur = killers; - endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]); - break; + case GOOD_CAPTURES: + while (cur < endMoves) + { + move = pick_best(cur++, endMoves); + if (move != ttMove) + { + if (pos.see_ge(move, VALUE_ZERO)) + return move; - case QUIET: - endMoves = generate(pos, moves); + // Losing capture, move it to the beginning of the array + *endBadCaptures++ = move; + } + } + + ++stage; + move = ss->killers[0]; // First killer move + if ( move != MOVE_NONE + && move != ttMove + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; + + case KILLERS: + ++stage; + move = ss->killers[1]; // Second killer move + if ( move != MOVE_NONE + && move != ttMove + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; + + case COUNTERMOVE: + ++stage; + move = countermove; + if ( move != MOVE_NONE + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1] + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; + + case QUIET_INIT: + cur = endBadCaptures; + endMoves = generate(pos, cur); score(); if (depth < 3 * ONE_PLY) { @@ -215,118 +245,104 @@ void MovePicker::generate_next_stage() { insertion_sort(cur, goodQuiet); } else insertion_sort(cur, endMoves); - break; + ++stage; + + case QUIET: + while (cur < endMoves + && (!skipQuiets || cur->value >= VALUE_ZERO)) + { + move = *cur++; + if ( move != ttMove + && move != ss->killers[0] + && move != ss->killers[1] + && move != countermove) + return move; + } + ++stage; + cur = moves; // Point to beginning of bad captures case BAD_CAPTURES: - // Just pick them in reverse order to get correct ordering - cur = moves + MAX_MOVES - 1; - endMoves = endBadCaptures; + if (cur < endBadCaptures && !skipBadCaptures) + return *cur++; break; + case EVASIONS_INIT: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; + case ALL_EVASIONS: - endMoves = generate(pos, moves); - if (endMoves - moves > 1) - score(); + while (cur < endMoves) + { + move = pick_best(cur++, endMoves); + if (move != ttMove) + return move; + } break; - case CHECKS: - endMoves = generate(pos, moves); - break; + case PROBCUT_INIT: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; - case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS: - case PROBCUT: case RECAPTURE: case STOP: - stage = STOP; + case PROBCUT_CAPTURES: + while (cur < endMoves) + { + move = pick_best(cur++, endMoves); + if ( move != ttMove + && pos.see_ge(move, threshold)) + return move; + } break; - default: - assert(false); - } -} - - -/// next_move() is the most important method of the MovePicker class. It returns -/// a new pseudo legal move every time it is called, until there are no more moves -/// left. It picks the move with the biggest value from a list of generated moves -/// taking care not to return the ttMove if it has already been searched. - -Move MovePicker::next_move() { - - Move move; - - while (true) - { - while (cur == endMoves && stage != STOP) - generate_next_stage(); - - switch (stage) { - - case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: - case QSEARCH_WITHOUT_CHECKS: case PROBCUT: - ++cur; - return ttMove; + case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; - case GOOD_CAPTURES: + case QCAPTURES_1: case QCAPTURES_2: + while (cur < endMoves) + { move = pick_best(cur++, endMoves); if (move != ttMove) - { - if (pos.see_sign(move) >= VALUE_ZERO) - return move; - - // Losing capture, move it to the tail of the array - *endBadCaptures-- = move; - } - break; - - case KILLERS: - move = *cur++; - if ( move != MOVE_NONE - && move != ttMove - && pos.pseudo_legal(move) - && !pos.capture(move)) - return move; - break; - - case QUIET: - move = *cur++; - if ( move != ttMove - && move != killers[0] - && move != killers[1] - && move != killers[2]) return move; + } + if (stage == QCAPTURES_2) break; + cur = moves; + endMoves = generate(pos, cur); + ++stage; - case BAD_CAPTURES: - return *cur--; - - case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2: - move = pick_best(cur++, endMoves); + case QCHECKS: + while (cur < endMoves) + { + move = cur++->move; if (move != ttMove) return move; - break; + } + break; - case PROBCUT_CAPTURES: - move = pick_best(cur++, endMoves); - if (move != ttMove && pos.see(move) > threshold) - return move; - break; + case QSEARCH_RECAPTURES: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; - case RECAPTURES: + case QRECAPTURES: + while (cur < endMoves) + { move = pick_best(cur++, endMoves); if (to_sq(move) == recaptureSquare) return move; - break; - - case CHECKS: - move = *cur++; - if (move != ttMove) - return move; - break; - - case STOP: - return MOVE_NONE; - - default: - assert(false); } + break; + + default: + assert(false); } + + return MOVE_NONE; } diff --git a/Engines/Windows/deepfish/srcD/movepick.h b/Engines/Windows/mcbrain/src/movepick.h similarity index 62% rename from Engines/Windows/deepfish/srcD/movepick.h rename to Engines/Windows/mcbrain/src/movepick.h index 8028d48..d87ce85 100644 --- a/Engines/Windows/deepfish/srcD/movepick.h +++ b/Engines/Windows/mcbrain/src/movepick.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,34 +26,46 @@ #include "movegen.h" #include "position.h" -#include "search.h" #include "types.h" -/// The Stats struct stores moves statistics. According to the template parameter -/// the class can store History and Countermoves. History records how often -/// different moves have been successful or unsuccessful during the current search -/// and is used for reduction and move ordering decisions. -/// Countermoves store the move that refute a previous one. Entries are stored -/// using only the moving piece and destination square, hence two moves with -/// different origin but same destination and piece will be considered identical. -template -struct Stats { +/// HistoryStats records how often quiet moves have been successful or unsuccessful +/// during the current search, and is used for reduction and move ordering decisions. +struct HistoryStats { static const Value Max = Value(1 << 28); + Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } + void clear() { std::memset(table, 0, sizeof(table)); } + void update(Color c, Move m, Value v) { + + Square from = from_sq(m); + Square to = to_sq(m); + + table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324; + table[c][from][to] += int(v) * 32; + } + +private: + Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; +}; + + +/// A template struct, used to generate MoveStats and CounterMoveHistoryStats: +/// MoveStats store the move that refute a previous one. +/// CounterMoveHistoryStats is like HistoryStats, but with two consecutive moves. +/// Entries are stored using only the moving piece and destination square, hence +/// two moves with different origin but same destination and piece will be +/// considered identical. +template +struct Stats { const T* operator[](Piece pc) const { return table[pc]; } T* operator[](Piece pc) { return table[pc]; } void clear() { std::memset(table, 0, sizeof(table)); } - void update(Piece pc, Square to, Move m) { table[pc][to] = m; } - void update(Piece pc, Square to, Value v) { - if (abs(int(v)) >= 324) - return; - - table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324); + table[pc][to] -= table[pc][to] * abs(int(v)) / 936; table[pc][to] += int(v) * 32; } @@ -62,30 +74,9 @@ struct Stats { }; typedef Stats MoveStats; -typedef Stats HistoryStats; -typedef Stats CounterMoveStats; +typedef Stats CounterMoveStats; typedef Stats CounterMoveHistoryStats; -struct FromToStats { - - Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } - void clear() { std::memset(table, 0, sizeof(table)); } - - void update(Color c, Move m, Value v) - { - if (abs(int(v)) >= 324) - return; - - Square f = from_sq(m); - Square t = to_sq(m); - - table[c][f][t] -= table[c][f][t] * abs(int(v)) / 324; - table[c][f][t] += int(v) * 32; - } - -private: - Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; -}; /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a @@ -93,6 +84,7 @@ struct FromToStats { /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// beta algorithm, MovePicker attempts to return the moves which are most likely /// to get a cut-off first. +namespace Search { struct Stack; } class MovePicker { public: @@ -103,12 +95,11 @@ class MovePicker { MovePicker(const Position&, Move, Depth, Square); MovePicker(const Position&, Move, Depth, Search::Stack*); - Move next_move(); + Move next_move(bool skipQuiets = false, bool skipBadCaptures = false); private: template void score(); - void generate_next_stage(); - ExtMove* begin() { return moves; } + ExtMove* begin() { return cur; } ExtMove* end() { return endMoves; } const Position& pos; @@ -116,12 +107,11 @@ class MovePicker { Move countermove; Depth depth; Move ttMove; - ExtMove killers[3]; Square recaptureSquare; Value threshold; int stage; - ExtMove* endBadCaptures = moves + MAX_MOVES - 1; - ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves; + ExtMove *cur, *endMoves, *endBadCaptures; + ExtMove moves[MAX_MOVES]; }; #endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/Engines/Windows/deepfish/srcD/pawns.cpp b/Engines/Windows/mcbrain/src/pawns.cpp similarity index 72% rename from Engines/Windows/deepfish/srcD/pawns.cpp rename to Engines/Windows/mcbrain/src/pawns.cpp index 72496fc..10f6723 100644 --- a/Engines/Windows/deepfish/srcD/pawns.cpp +++ b/Engines/Windows/mcbrain/src/pawns.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,46 +37,51 @@ namespace { // Backward pawn penalty by opposed flag const Score Backward[2] = { S(56, 33), S(41, 19) }; - // Unsupported pawn penalty for pawns which are neither isolated or backward, - // by number of pawns it supports [less than 2 / exactly 2]. - const Score Unsupported[2] = { S(17, 8), S(21, 12) }; + // Unsupported pawn penalty for pawns which are neither isolated or backward + const Score Unsupported = S(17, 8); // Connected pawn bonus by opposed, phalanx, twice supported and rank Score Connected[2][2][2][RANK_NB]; // Doubled pawn penalty - const Score Doubled = S(18,38); + const Score Doubled = S(18, 38); // Lever bonus by rank const Score Lever[RANK_NB] = { S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), - S(17, 16), S(33, 32), S(0, 0), S(0, 0) }; + S(17, 16), S(33, 32), S(0, 0), S(0, 0) + }; - // Weakness of our pawn shelter in front of the king by [distance from edge][rank] + // Weakness of our pawn shelter in front of the king by [distance from edge][rank]. + // RANK_1 = 0 is used for files where we have no pawns or our pawn is behind our king. const Value ShelterWeakness[][RANK_NB] = { - { V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) }, - { V(120), V( 0), V(28), V(76), V(88), V(103), V(104) }, - { V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) }, - { V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } }; - - // Danger of enemy pawns moving toward our king by [type][distance from edge][rank] + { V(100), V(20), V(10), V(46), V(82), V( 86), V( 98) }, + { V(116), V( 4), V(28), V(87), V(94), V(108), V(104) }, + { V(109), V( 1), V(59), V(87), V(62), V( 91), V(116) }, + { V( 75), V(12), V(43), V(59), V(90), V( 84), V(112) } + }; + + // Danger of enemy pawns moving toward our king by [type][distance from edge][rank]. + // For the unopposed and unblocked cases, RANK_1 = 0 is used when opponent has no pawn + // on the given file, or their pawn is behind our king. const Value StormDanger[][4][RANK_NB] = { - { { V( 0), V( 67), V( 134), V(38), V(32) }, - { V( 0), V( 57), V( 139), V(37), V(22) }, - { V( 0), V( 43), V( 115), V(43), V(27) }, - { V( 0), V( 68), V( 124), V(57), V(32) } }, - { { V(20), V( 43), V( 100), V(56), V(20) }, - { V(23), V( 20), V( 98), V(40), V(15) }, - { V(23), V( 39), V( 103), V(36), V(18) }, - { V(28), V( 19), V( 108), V(42), V(26) } }, - { { V( 0), V( 0), V( 75), V(14), V( 2) }, - { V( 0), V( 0), V( 150), V(30), V( 4) }, - { V( 0), V( 0), V( 160), V(22), V( 5) }, - { V( 0), V( 0), V( 166), V(24), V(13) } }, - { { V( 0), V(-283), V(-281), V(57), V(31) }, - { V( 0), V( 58), V( 141), V(39), V(18) }, - { V( 0), V( 65), V( 142), V(48), V(32) }, - { V( 0), V( 60), V( 126), V(51), V(19) } } }; + { { V( 0), V(-290), V(-274), V(57), V(41) }, //BlockedByKing + { V( 0), V( 60), V( 144), V(39), V(13) }, + { V( 0), V( 65), V( 141), V(41), V(34) }, + { V( 0), V( 53), V( 127), V(56), V(14) } }, + { { V( 4), V( 73), V( 132), V(46), V(31) }, //Unopposed + { V( 1), V( 64), V( 143), V(26), V(13) }, + { V( 1), V( 47), V( 110), V(44), V(24) }, + { V( 0), V( 72), V( 127), V(50), V(31) } }, + { { V( 0), V( 0), V( 79), V(23), V( 1) }, //BlockedByPawn + { V( 0), V( 0), V( 148), V(27), V( 2) }, + { V( 0), V( 0), V( 161), V(16), V( 1) }, + { V( 0), V( 0), V( 171), V(22), V(15) } }, + { { V(22), V( 45), V( 104), V(62), V( 6) }, //Unblocked + { V(31), V( 30), V( 99), V(39), V(19) }, + { V(23), V( 29), V( 96), V(41), V(15) }, + { V(21), V( 23), V( 116), V(41), V(15) } } + }; // Max bonus for king safety. Corresponds to start position with all the pawns // in front of the king and no enemy pawn on the horizon. @@ -88,14 +93,15 @@ namespace { template Score evaluate(const Position& pos, Pawns::Entry* e) { - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); - const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); + const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); Bitboard b, neighbours, stoppers, doubled, supported, phalanx; + Bitboard lever, leverPush, connected; Square s; - bool opposed, lever, connected, backward; + bool opposed, backward; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; @@ -103,10 +109,10 @@ namespace { Bitboard ourPawns = pos.pieces(Us , PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); - e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; - e->kingSquares[Us] = SQ_NONE; + e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; e->semiopenFiles[Us] = 0xFF; - e->pawnAttacks[Us] = shift_bb(ourPawns) | shift_bb(ourPawns); + e->kingSquares[Us] = SQ_NONE; + e->pawnAttacks[Us] = shift(ourPawns) | shift(ourPawns); e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares); e->pawnsOnSquares[Us][WHITE] = pos.count(Us) - e->pawnsOnSquares[Us][BLACK]; @@ -117,13 +123,14 @@ namespace { File f = file_of(s); - e->semiopenFiles[Us] &= ~(1 << f); + e->semiopenFiles[Us] &= ~(1 << f); e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); // Flag the pawn opposed = theirPawns & forward_bb(Us, s); stoppers = theirPawns & passed_pawn_mask(Us, s); lever = theirPawns & pawnAttacksBB[s]; + leverPush = theirPawns & pawnAttacksBB[s + Up]; doubled = ourPawns & (s + Up); neighbours = ourPawns & adjacent_files_bb(f); phalanx = neighbours & rank_bb(s); @@ -142,14 +149,19 @@ namespace { // The pawn is backward when it cannot safely progress to that rank: // either there is a stopper in the way on this rank, or there is a // stopper on adjacent file which controls the way to that rank. - backward = (b | shift_bb(b & adjacent_files_bb(f))) & stoppers; + backward = (b | shift(b & adjacent_files_bb(f))) & stoppers; assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours)); } // Passed pawns will be properly scored in evaluation because we need - // full attack info to evaluate them. - if (!stoppers && !(ourPawns & forward_bb(Us, s))) + // full attack info to evaluate them. Include also not passed pawns + // which could become passed after one or two pawn pushes when are + // not attacked more times than defended. + if ( !(stoppers ^ lever ^ leverPush) + && !(ourPawns & forward_bb(Us, s)) + && popcount(supported) >= popcount(lever) + && popcount(phalanx) >= popcount(leverPush)) e->passedPawns[Us] |= s; // Score this pawn @@ -160,7 +172,7 @@ namespace { score -= Backward[opposed]; else if (!supported) - score -= Unsupported[more_than_one(neighbours & pawnAttacksBB[s])]; + score -= Unsupported; if (connected) score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)]; @@ -183,8 +195,8 @@ namespace Pawns { /// hard-coded tables, when makes sense, we prefer to calculate them with a formula /// to reduce independent parameters and to allow easier tuning and better insight. -void init() -{ +void init() { + static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 }; for (int opposed = 0; opposed <= 1; ++opposed) @@ -194,7 +206,7 @@ void init() { int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; v += (apex ? v / 2 : 0); - Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8); + Connected[opposed][phalanx][apex][r] = make_score(v, v * (r-2) / 4); } } @@ -215,19 +227,20 @@ Entry* probe(const Position& pos) { e->key = key; e->score = evaluate(pos, e) - evaluate(pos, e); e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]); + e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); return e; } /// Entry::shelter_storm() calculates shelter and storm penalties for the file -/// the king is on, as well as the two adjacent files. +/// the king is on, as well as the two closest files. template Value Entry::shelter_storm(const Position& pos, Square ksq) { const Color Them = (Us == WHITE ? BLACK : WHITE); - enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; + enum { BlockedByKing, Unopposed, BlockedByPawn, Unblocked }; Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); Bitboard ourPawns = b & pos.pieces(Us); @@ -243,12 +256,13 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) { b = theirPawns & file_bb(f); Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; - safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] + int d = std::min(f, FILE_H - f); + safety -= ShelterWeakness[d][rkUs] + StormDanger [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : - rkUs == RANK_1 ? NoFriendlyPawn : + rkUs == RANK_1 ? Unopposed : rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] - [std::min(f, FILE_H - f)][rkThem]; + [d][rkThem]; } return safety; diff --git a/Engines/Windows/deepfish/srcD/pawns.h b/Engines/Windows/mcbrain/src/pawns.h similarity index 95% rename from Engines/Windows/deepfish/srcD/pawns.h rename to Engines/Windows/mcbrain/src/pawns.h index 24843e3..15b0b77 100644 --- a/Engines/Windows/deepfish/srcD/pawns.h +++ b/Engines/Windows/mcbrain/src/pawns.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,6 +38,7 @@ struct Entry { Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int pawn_asymmetry() const { return asymmetry; } + int open_files() const { return openFiles; } int semiopen_file(Color c, File f) const { return semiopenFiles[c] & (1 << f); @@ -74,6 +75,7 @@ struct Entry { int semiopenFiles[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int asymmetry; + int openFiles; }; typedef HashTable Table; diff --git a/Engines/Windows/deepfish/srcD/position.cpp b/Engines/Windows/mcbrain/src/position.cpp similarity index 75% rename from Engines/Windows/deepfish/srcD/position.cpp rename to Engines/Windows/mcbrain/src/position.cpp index bc8e92a..ceed0f1 100644 --- a/Engines/Windows/deepfish/srcD/position.cpp +++ b/Engines/Windows/mcbrain/src/position.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,8 @@ #include #include -#include // For std::memset, std::memcmp +#include // For offsetof() +#include // For std::memset, std::memcmp #include #include @@ -31,25 +32,27 @@ #include "thread.h" #include "tt.h" #include "uci.h" +#include "tbprobe.h" using std::string; +namespace PSQT { + extern Score psq[PIECE_NB][SQUARE_NB]; +} + namespace Zobrist { - Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; - Key side; - Key exclusion; + Key side, noPawns; } -Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion; } - namespace { const string PieceToChar(" PNBRQK pnbrqk"); -// min_attacker() is a helper function used by see() to locate the least +// min_attacker() is a helper function used by see_ge() to locate the least // valuable attacker for the side to move, remove the attacker we just found // from the bitboards and scan for new X-ray attacks behind it. @@ -96,11 +99,25 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { } os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase - << std::setfill('0') << std::setw(16) << pos.key() << std::dec << "\nCheckers: "; + << std::setfill('0') << std::setw(16) << pos.key() + << std::setfill(' ') << std::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b; ) os << UCI::square(pop_lsb(&b)) << " "; + if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) + && !pos.can_castle(ANY_CASTLING)) + { + StateInfo st; + Position p; + p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); + Tablebases::ProbeState s1, s2; + Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); + int dtz = Tablebases::probe_dtz(p, &s2); + os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")" + << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")"; + } + return os; } @@ -112,10 +129,9 @@ void Position::init() { PRNG rng(1070372); - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[c][pt][s] = rng.rand(); + for (Piece pc : Pieces) + for (Square s = SQ_A1; s <= SQ_H8; ++s) + Zobrist::psq[pc][s] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); @@ -132,7 +148,7 @@ void Position::init() { } Zobrist::side = rng.rand(); - Zobrist::exclusion = rng.rand(); + Zobrist::noPawns = rng.rand(); } @@ -164,8 +180,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th 4) En passant target square (in algebraic notation). If there's no en passant target square, this is "-". If a pawn has just made a 2-square move, this - is the position "behind" the pawn. This is recorded regardless of whether - there is a pawn in position to make an en passant capture. + is the position "behind" the pawn. This is recorded only if there is a pawn + in position to make an en passant capture, and if there really is a pawn + that might have advanced two squares. 5) Halfmove clock. This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the @@ -182,7 +199,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); - std::fill_n(&pieceList[0][0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); + std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; ss >> std::noskipws; @@ -198,7 +215,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(color_of(Piece(idx)), type_of(Piece(idx)), sq); + put_piece(Piece(idx), sq); ++sq; } } @@ -242,7 +259,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th { st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); - if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) + if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)) + || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) st->epSquare = SQ_NONE; } else @@ -296,8 +314,8 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info(StateInfo* si) const { - si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE)); - si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK)); + si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinnersForKing[WHITE]); + si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinnersForKing[BLACK]); Square ksq = square(~sideToMove); @@ -317,7 +335,8 @@ void Position::set_check_info(StateInfo* si) const { void Position::set_state(StateInfo* si) const { - si->key = si->pawnKey = si->materialKey = 0; + si->key = si->materialKey = 0; + si->pawnKey = Zobrist::noPawns; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->psq = SCORE_ZERO; si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); @@ -328,8 +347,8 @@ void Position::set_state(StateInfo* si) const { { Square s = pop_lsb(&b); Piece pc = piece_on(s); - si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s]; - si->psq += PSQT::psq[color_of(pc)][type_of(pc)][s]; + si->key ^= Zobrist::psq[pc][s]; + si->psq += PSQT::psq[pc][s]; } if (si->epSquare != SQ_NONE) @@ -343,17 +362,39 @@ void Position::set_state(StateInfo* si) const { for (Bitboard b = pieces(PAWN); b; ) { Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; + si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; + } + + for (Piece pc : Pieces) + { + if (type_of(pc) != PAWN && type_of(pc) != KING) + si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; + + for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) + si->materialKey ^= Zobrist::psq[pc][cnt]; } +} + + +/// Position::set() is an overload to initialize the position object with +/// the given endgame code string like "KBPKN". It is mainly a helper to +/// get the material key out of an endgame code. Position is not playable, +/// indeed is even not guaranteed to be legal. + +Position& Position::set(const string& code, Color c, StateInfo* si) { - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt) - si->materialKey ^= Zobrist::psq[c][pt][cnt]; + assert(code.length() > 0 && code.length() < 8); + assert(code[0] == 'K'); - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) - si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; + string sides[] = { code.substr(code.find('K', 1)), // Weak + code.substr(0, code.find('K', 1)) }; // Strong + + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); + + string fenStr = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" + + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10"; + + return set(fenStr, false, si, nullptr); } @@ -420,27 +461,33 @@ Phase Position::game_phase() const { } -/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) that -/// are blocking attacks on the square 's' from 'sliders'. A piece blocks a slider -/// if removing that piece from the board would result in a position where square 's' -/// is attacked. For example, a king-attack blocking piece can be either a pinned or -/// a discovered check piece, according if its color is the opposite or the same of -/// the color of the slider. +/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) +/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a +/// slider if removing that piece from the board would result in a position where +/// square 's' is attacked. For example, a king-attack blocking piece can be either +/// a pinned or a discovered check piece, according if its color is the opposite +/// or the same of the color of the slider. -Bitboard Position::slider_blockers(Bitboard sliders, Square s) const { +Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const { - Bitboard b, pinners, result = 0; + Bitboard result = 0; + pinners = 0; - // Pinners are sliders that attack 's' when a pinned piece is removed - pinners = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) - | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + // Snipers are sliders that attack 's' when a piece is removed + Bitboard snipers = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) + | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; - while (pinners) + while (snipers) { - b = between_bb(s, pop_lsb(&pinners)) & pieces(); - - if (!more_than_one(b)) - result |= b; + Square sniperSq = pop_lsb(&snipers); + Bitboard b = between_bb(s, sniperSq) & pieces(); + + if (!more_than_one(b)) + { + result |= b; + if (b & pieces(color_of(piece_on(s)))) + pinners |= sniperSq; + } } return result; } @@ -661,23 +708,24 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); - assert(color_of(piece_on(from)) == us); - assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == (type_of(m) != CASTLING ? them : us)); - assert(captured != KING); + assert(color_of(pc) == us); + assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(type_of(captured) != KING); if (type_of(m) == CASTLING) { - assert(pt == KING); + assert(pc == make_piece(us, KING)); + assert(captured == make_piece(us, ROOK)); Square rfrom, rto; do_castling(us, from, to, rfrom, rto); - captured = NO_PIECE_TYPE; - st->psq += PSQT::psq[us][ROOK][rto] - PSQT::psq[us][ROOK][rfrom]; - k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; + k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + captured = NO_PIECE; } if (captured) @@ -686,13 +734,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. - if (captured == PAWN) + if (type_of(captured) == PAWN) { if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(pc == make_piece(us, PAWN)); assert(to == st->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); @@ -701,28 +749,28 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { board[capsq] = NO_PIECE; // Not done by remove_piece() } - st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; + st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists - remove_piece(them, captured, capsq); + remove_piece(captured, capsq); // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[them][captured][capsq]; - st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; + k ^= Zobrist::psq[captured][capsq]; + st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; prefetch(thisThread->materialTable[st->materialKey]); // Update incremental scores - st->psq -= PSQT::psq[them][captured][capsq]; + st->psq -= PSQT::psq[captured][capsq]; // Reset rule 50 counter st->rule50 = 0; } // Update hash key - k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; + k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset en passant square if (st->epSquare != SQ_NONE) @@ -741,10 +789,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) - move_piece(us, pt, from, to); + move_piece(pc, from, to); // If the moving piece is a pawn do some special extra work - if (pt == PAWN) + if (type_of(pc) == PAWN) { // Set en-passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 @@ -756,40 +804,40 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else if (type_of(m) == PROMOTION) { - PieceType promotion = promotion_type(m); + Piece promotion = make_piece(us, promotion_type(m)); assert(relative_rank(us, to) == RANK_8); - assert(promotion >= KNIGHT && promotion <= QUEEN); + assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); - remove_piece(us, PAWN, to); - put_piece(us, promotion, to); + remove_piece(pc, to); + put_piece(promotion, to); // Update hash keys - k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; - st->pawnKey ^= Zobrist::psq[us][PAWN][to]; - st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] - ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->pawnKey ^= Zobrist::psq[pc][to]; + st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] + ^ Zobrist::psq[pc][pieceCount[pc]]; // Update incremental score - st->psq += PSQT::psq[us][promotion][to] - PSQT::psq[us][PAWN][to]; + st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; // Update material st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable - st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; - prefetch(thisThread->pawnsTable[st->pawnKey]); + st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + prefetch2(thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter st->rule50 = 0; } // Update incremental scores - st->psq += PSQT::psq[us][pt][to] - PSQT::psq[us][pt][from]; + st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; // Set capture piece - st->capturedType = captured; + st->capturedPiece = captured; // Update the key with the final value st->key = k; @@ -818,20 +866,20 @@ void Position::undo_move(Move m) { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(to)); + Piece pc = piece_on(to); assert(empty(from) || type_of(m) == CASTLING); - assert(st->capturedType != KING); + assert(type_of(st->capturedPiece) != KING); if (type_of(m) == PROMOTION) { assert(relative_rank(us, to) == RANK_8); - assert(pt == promotion_type(m)); - assert(pt >= KNIGHT && pt <= QUEEN); + assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); - remove_piece(us, pt, to); - put_piece(us, PAWN, to); - pt = PAWN; + remove_piece(pc, to); + pc = make_piece(us, PAWN); + put_piece(pc, to); } if (type_of(m) == CASTLING) @@ -841,9 +889,9 @@ void Position::undo_move(Move m) { } else { - move_piece(us, pt, to, from); // Put the piece back at the source square + move_piece(pc, to, from); // Put the piece back at the source square - if (st->capturedType) + if (st->capturedPiece) { Square capsq = to; @@ -851,14 +899,14 @@ void Position::undo_move(Move m) { { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(type_of(pc) == PAWN); assert(to == st->previous->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(capsq) == NO_PIECE); - assert(st->capturedType == PAWN); + assert(st->capturedPiece == make_piece(~us, PAWN)); } - put_piece(~us, st->capturedType, capsq); // Restore the captured piece + put_piece(st->capturedPiece, capsq); // Restore the captured piece } } @@ -871,7 +919,7 @@ void Position::undo_move(Move m) { /// Position::do_castling() is a helper used to do/undo a castling move. This -/// is a bit tricky, especially in Chess960. +/// is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -881,11 +929,11 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 - remove_piece(us, KING, Do ? from : to); - remove_piece(us, ROOK, Do ? rfrom : rto); + remove_piece(make_piece(us, KING), Do ? from : to); + remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(us, KING, Do ? to : from); - put_piece(us, ROOK, Do ? rto : rfrom); + put_piece(make_piece(us, KING), Do ? to : from); + put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } @@ -935,121 +983,125 @@ void Position::undo_null_move() { Key Position::key_after(Move m) const { - Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = piece_on(to); Key k = st->key ^ Zobrist::side; if (captured) - k ^= Zobrist::psq[~us][captured][to]; + k ^= Zobrist::psq[captured][to]; - return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from]; + return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; } -/// Position::see() is a static exchange evaluator: It tries to estimate the -/// material gain or loss resulting from a move. +/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the +/// SEE value of move is greater or equal to the given value. We'll use an +/// algorithm similar to alpha-beta pruning with a null window. -Value Position::see_sign(Move m) const { +bool Position::see_ge(Move m, Value v) const { assert(is_ok(m)); - // Early return if SEE cannot be negative because captured piece value - // is not less then capturing one. Note that king moves always return - // here because king midgame value is set to 0. - if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))]) - return VALUE_KNOWN_WIN; - - return see(m); -} + // Castling moves are implemented as king capturing the rook so cannot be + // handled correctly. Simply assume the SEE value is VALUE_ZERO that is always + // correct unless in the rare case the rook ends up under attack. + if (type_of(m) == CASTLING) + return VALUE_ZERO >= v; -Value Position::see(Move m) const { + Square from = from_sq(m), to = to_sq(m); + PieceType nextVictim = type_of(piece_on(from)); + Color stm = ~color_of(piece_on(from)); // First consider opponent's move + Value balance; // Values of the pieces taken by us minus opponent's ones + Bitboard occupied, stmAttackers; - Square from, to; - Bitboard occupied, attackers, stmAttackers; - Value swapList[32]; - int slIndex = 1; - PieceType captured; - Color stm; + if (type_of(m) == ENPASSANT) + { + occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn + balance = PieceValue[MG][PAWN]; + } + else + { + balance = PieceValue[MG][piece_on(to)]; + occupied = 0; + } - assert(is_ok(m)); + if (balance < v) + return false; - from = from_sq(m); - to = to_sq(m); - swapList[0] = PieceValue[MG][piece_on(to)]; - stm = color_of(piece_on(from)); - occupied = pieces() ^ from; + if (nextVictim == KING) + return true; - // Castling moves are implemented as king capturing the rook so cannot - // be handled correctly. Simply return VALUE_ZERO that is always correct - // unless in the rare case the rook ends up under attack. - if (type_of(m) == CASTLING) - return VALUE_ZERO; + balance -= PieceValue[MG][nextVictim]; - if (type_of(m) == ENPASSANT) - { - occupied ^= to - pawn_push(stm); // Remove the captured pawn - swapList[0] = PieceValue[MG][PAWN]; - } + if (balance >= v) + return true; - // Find all attackers to the destination square, with the moving piece - // removed, but possibly an X-ray attacker added behind it. - attackers = attackers_to(to, occupied) & occupied; + bool relativeStm = true; // True if the opponent is to move + occupied ^= pieces() ^ from ^ to; - // If the opponent has no attackers we are finished - stm = ~stm; - stmAttackers = attackers & pieces(stm); - if (!stmAttackers) - return swapList[0]; + // Find all attackers to the destination square, with the moving piece removed, + // but possibly an X-ray attacker added behind it. + Bitboard attackers = attackers_to(to, occupied) & occupied; - // The destination square is defended, which makes things rather more - // difficult to compute. We proceed by building up a "swap list" containing - // the material gain or loss at each stop in a sequence of captures to the - // destination square, where the sides alternately capture, and always - // capture with the least valuable piece. After each capture, we look for - // new X-ray attacks from behind the capturing piece. - captured = type_of(piece_on(from)); + while (true) + { + stmAttackers = attackers & pieces(stm); - do { - assert(slIndex < 32); + // Don't allow pinned pieces to attack pieces except the king as long all + // pinners are on their original square. + if (!(st->pinnersForKing[stm] & ~occupied)) + stmAttackers &= ~st->blockersForKing[stm]; - // Add the new entry to the swap list - swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; + if (!stmAttackers) + return relativeStm; // Locate and remove the next least valuable attacker - captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); - stm = ~stm; - stmAttackers = attackers & pieces(stm); - ++slIndex; + nextVictim = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + + if (nextVictim == KING) + return relativeStm == bool(attackers & pieces(~stm)); - } while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture + balance += relativeStm ? PieceValue[MG][nextVictim] + : -PieceValue[MG][nextVictim]; - // Having built the swap list, we negamax through it to find the best - // achievable score from the point of view of the side to move. - while (--slIndex) - swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]); + relativeStm = !relativeStm; - return swapList[0]; + if (relativeStm == (balance >= v)) + return relativeStm; + + stm = ~stm; + } } /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. -bool Position::is_draw() const { +bool Position::is_draw(int ply) const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; - StateInfo* stp = st; - for (int i = 2, e = std::min(st->rule50, st->pliesFromNull); i <= e; i += 2) + int end = std::min(st->rule50, st->pliesFromNull); + + if (end < 4) + return false; + + StateInfo* stp = st->previous->previous; + int cnt = 0; + + for (int i = 4; i <= end; i += 2) { stp = stp->previous->previous; - if (stp->key == st->key) - return true; // Draw at first repetition + // At root position ply is 1, so return a draw score if a position + // repeats once earlier but after or at the root, or repeats twice + // strictly before the root. + if ( stp->key == st->key + && ++cnt + (ply - i > 0) == 2) + return true; } return false; @@ -1140,17 +1192,15 @@ bool Position::pos_is_ok(int* failedStep) const { } if (step == Lists) - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - if (pieceCount[c][pt] != popcount(pieces(c, pt))) - return false; + for (Piece pc : Pieces) + { + if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))) + return false; - for (int i = 0; i < pieceCount[c][pt]; ++i) - if ( board[pieceList[c][pt][i]] != make_piece(c, pt) - || index[pieceList[c][pt][i]] != i) - return false; - } + for (int i = 0; i < pieceCount[pc]; ++i) + if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) + return false; + } if (step == Castling) for (Color c = WHITE; c <= BLACK; ++c) diff --git a/Engines/Windows/deepfish/srcD/position.h b/Engines/Windows/mcbrain/src/position.h similarity index 80% rename from Engines/Windows/deepfish/srcD/position.h rename to Engines/Windows/mcbrain/src/position.h index 7fce1db..e571048 100644 --- a/Engines/Windows/deepfish/srcD/position.h +++ b/Engines/Windows/mcbrain/src/position.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,25 +22,13 @@ #define POSITION_H_INCLUDED #include -#include // For offsetof() #include -#include // For std::unique_ptr +#include // For std::unique_ptr #include -#include #include "bitboard.h" #include "types.h" -class Position; -class Thread; - -namespace PSQT { - - extern Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - - void init(); -} - /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the @@ -58,12 +46,13 @@ struct StateInfo { Score psq; Square epSquare; - // Not copied when making a move + // Not copied when making a move (will be recomputed anyhow) Key key; Bitboard checkersBB; - PieceType capturedType; + Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; + Bitboard pinnersForKing[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; }; @@ -75,9 +64,9 @@ typedef std::unique_ptr> StateListPtr; /// pieces, side to move, hash keys, castling info, etc. Important methods are /// do_move() and undo_move(), used by the search to update node info when /// traversing the search tree. +class Thread; class Position { - public: static void init(); @@ -87,6 +76,7 @@ class Position { // FEN string input/output Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); + Position& set(const std::string& code, Color c, StateInfo* si); const std::string fen() const; // Position representation @@ -100,6 +90,7 @@ class Position { Square ep_square() const; bool empty(Square s) const; template int count(Color c) const; + template int count() const; template const Square* squares(Color c) const; template Square square(Color c) const; @@ -121,7 +112,7 @@ class Position { Bitboard attacks_from(Piece pc, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; - Bitboard slider_blockers(Bitboard sliders, Square s) const; + Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves bool legal(Move m) const; @@ -131,26 +122,25 @@ class Position { bool gives_check(Move m) const; bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; - PieceType captured_piece_type() const; + Piece captured_piece() const; // Piece specific bool pawn_passed(Color c, Square s) const; bool opposite_bishops() const; // Doing and undoing moves - void do_move(Move m, StateInfo& st, bool givesCheck); + void do_move(Move m, StateInfo& newSt); + void do_move(Move m, StateInfo& newSt, bool givesCheck); void undo_move(Move m); - void do_null_move(StateInfo& st); + void do_null_move(StateInfo& newSt); void undo_null_move(); - // Static exchange evaluation - Value see(Move m) const; - Value see_sign(Move m) const; + // Static Exchange Evaluation + bool see_ge(Move m, Value value) const; // Accessing hash keys Key key() const; Key key_after(Move m) const; - Key exclusion_key() const; Key material_key() const; Key pawn_key() const; @@ -161,11 +151,11 @@ class Position { bool is_chess960() const; Thread* this_thread() const; uint64_t nodes_searched() const; - void set_nodes_searched(uint64_t n); - bool is_draw() const; + bool is_draw(int ply) const; int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; + Value non_pawn_material() const; // Position consistency check, for debugging bool pos_is_ok(int* failedStep = nullptr) const; @@ -178,9 +168,9 @@ class Position { void set_check_info(StateInfo* si) const; // Other helpers - void put_piece(Color c, PieceType pt, Square s); - void remove_piece(Color c, PieceType pt, Square s); - void move_piece(Color c, PieceType pt, Square from, Square to); + void put_piece(Piece pc, Square s); + void remove_piece(Piece pc, Square s); + void move_piece(Piece pc, Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); @@ -188,8 +178,8 @@ class Position { Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; - int pieceCount[COLOR_NB][PIECE_TYPE_NB]; - Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; + int pieceCount[PIECE_NB]; + Square pieceList[PIECE_NB][16]; int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; @@ -245,16 +235,20 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { } template inline int Position::count(Color c) const { - return pieceCount[c][Pt]; + return pieceCount[make_piece(c, Pt)]; +} + +template inline int Position::count() const { + return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)]; } template inline const Square* Position::squares(Color c) const { - return pieceList[c][Pt]; + return pieceList[make_piece(c, Pt)]; } template inline Square Position::square(Color c) const { - assert(pieceCount[c][Pt] == 1); - return pieceList[c][Pt][0]; + assert(pieceCount[make_piece(c, Pt)] == 1); + return pieceList[make_piece(c, Pt)][0]; } inline Square Position::ep_square() const { @@ -342,6 +336,10 @@ inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } +inline Value Position::non_pawn_material() const { + return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; +} + inline int Position::game_ply() const { return gamePly; } @@ -354,13 +352,9 @@ inline uint64_t Position::nodes_searched() const { return nodes; } -inline void Position::set_nodes_searched(uint64_t n) { - nodes = n; -} - inline bool Position::opposite_bishops() const { - return pieceCount[WHITE][BISHOP] == 1 - && pieceCount[BLACK][BISHOP] == 1 + return pieceCount[W_BISHOP] == 1 + && pieceCount[B_BISHOP] == 1 && opposite_colors(square(WHITE), square(BLACK)); } @@ -369,66 +363,68 @@ inline bool Position::is_chess960() const { } inline bool Position::capture_or_promotion(Move m) const { - assert(is_ok(m)); return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); } inline bool Position::capture(Move m) const { - - // Castling is encoded as "king captures the rook" assert(is_ok(m)); + // Castling is encoded as "king captures rook" return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; } -inline PieceType Position::captured_piece_type() const { - return st->capturedType; +inline Piece Position::captured_piece() const { + return st->capturedPiece; } inline Thread* Position::this_thread() const { return thisThread; } -inline void Position::put_piece(Color c, PieceType pt, Square s) { +inline void Position::put_piece(Piece pc, Square s) { - board[s] = make_piece(c, pt); + board[s] = pc; byTypeBB[ALL_PIECES] |= s; - byTypeBB[pt] |= s; - byColorBB[c] |= s; - index[s] = pieceCount[c][pt]++; - pieceList[c][pt][index[s]] = s; - pieceCount[c][ALL_PIECES]++; + byTypeBB[type_of(pc)] |= s; + byColorBB[color_of(pc)] |= s; + index[s] = pieceCount[pc]++; + pieceList[pc][index[s]] = s; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; } -inline void Position::remove_piece(Color c, PieceType pt, Square s) { +inline void Position::remove_piece(Piece pc, Square s) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] - // are not guaranteed to be invariant to a do_move() + undo_move() sequence. + // are not invariant to a do_move() + undo_move() sequence. byTypeBB[ALL_PIECES] ^= s; - byTypeBB[pt] ^= s; - byColorBB[c] ^= s; + byTypeBB[type_of(pc)] ^= s; + byColorBB[color_of(pc)] ^= s; /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ - Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; + Square lastSquare = pieceList[pc][--pieceCount[pc]]; index[lastSquare] = index[s]; - pieceList[c][pt][index[lastSquare]] = lastSquare; - pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; - pieceCount[c][ALL_PIECES]--; + pieceList[pc][index[lastSquare]] = lastSquare; + pieceList[pc][pieceCount[pc]] = SQ_NONE; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; } -inline void Position::move_piece(Color c, PieceType pt, Square from, Square to) { +inline void Position::move_piece(Piece pc, Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[c] ^= from_to_bb; + byTypeBB[type_of(pc)] ^= from_to_bb; + byColorBB[color_of(pc)] ^= from_to_bb; board[from] = NO_PIECE; - board[to] = make_piece(c, pt); + board[to] = pc; index[to] = index[from]; - pieceList[c][pt][index[to]] = to; + pieceList[pc][index[to]] = to; +} + +inline void Position::do_move(Move m, StateInfo& newSt) { + do_move(m, newSt, gives_check(m)); } #endif // #ifndef POSITION_H_INCLUDED diff --git a/Engines/Windows/mcbrain/src/psqt.cpp b/Engines/Windows/mcbrain/src/psqt.cpp new file mode 100644 index 0000000..3baf8a8 --- /dev/null +++ b/Engines/Windows/mcbrain/src/psqt.cpp @@ -0,0 +1,126 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish 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, see . +*/ + +#include + +#include "types.h" + +Value PieceValue[PHASE_NB][PIECE_NB] = { + { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, + { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } +}; + +namespace PSQT { + +#define S(mg, eg) make_score(mg, eg) + +// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece +// type on a given square a (middlegame, endgame) score pair is assigned. Table +// is defined for files A..D and white side: it is symmetric for black side and +// second half of the files. +const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { + { }, + { // Pawn + { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, + { S(-11, 7), S( 6,-4), S( 7, 8), S( 3,-2) }, + { S(-18,-4), S( -2,-5), S( 19, 5), S(24, 4) }, + { S(-17, 3), S( -9, 3), S( 20,-8), S(35,-3) }, + { S( -6, 8), S( 5, 9), S( 3, 7), S(21,-6) }, + { S( -6, 8), S( -8,-5), S( -6, 2), S(-2, 4) }, + { S( -4, 3), S( 20,-9), S( -8, 1), S(-4,18) } + }, + { // Knight + { S(-144, -98), S(-96,-82), S(-80,-46), S(-73,-14) }, + { S( -83, -69), S(-43,-54), S(-21,-17), S(-10, 9) }, + { S( -71, -50), S(-22,-39), S( 0, -7), S( 9, 28) }, + { S( -25, -41), S( 18,-25), S( 43, 6), S( 47, 38) }, + { S( -26, -46), S( 16,-25), S( 38, 3), S( 50, 40) }, + { S( -11, -54), S( 37,-38), S( 56, -7), S( 65, 27) }, + { S( -62, -65), S(-17,-50), S( 5,-24), S( 14, 13) }, + { S(-194,-109), S(-66,-89), S(-42,-50), S(-29,-13) } + }, + { // Bishop + { S(-44,-58), S(-13,-31), S(-25,-37), S(-34,-19) }, + { S(-20,-34), S( 20, -9), S( 12,-14), S( 1, 4) }, + { S( -9,-23), S( 27, 0), S( 21, -3), S( 11, 16) }, + { S(-11,-26), S( 28, -3), S( 21, -5), S( 10, 16) }, + { S(-11,-26), S( 24, -4), S( 16, -7), S( 9, 14) }, + { S(-17,-24), S( 16, -2), S( 12, 0), S( 2, 13) }, + { S(-23,-34), S( 17,-10), S( 6,-12), S( -2, 6) }, + { S(-35,-55), S(-11,-32), S(-19,-36), S(-29,-17) } + }, + { // Rook + { S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) }, + { S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) }, + { S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) }, + { S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) }, + { S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) }, + { S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) }, + { S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) }, + { S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) } + }, + { // Queen + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // King + { S(267, 0), S(320, 48), S(270, 75), S(195, 84) }, + { S(264, 43), S(304, 92), S(238,143), S(180,132) }, + { S(200, 83), S(245,138), S(176,167), S(110,165) }, + { S(177,106), S(185,169), S(148,169), S(110,179) }, + { S(149,108), S(177,163), S(115,200), S( 66,203) }, + { S(118, 95), S(159,155), S( 84,176), S( 41,174) }, + { S( 86, 50), S(128, 99), S( 63,122), S( 18,139) }, + { S( 63, 9), S( 89, 55), S( 47, 80), S( 0, 90) } + } +}; + +#undef S + +Score psq[PIECE_NB][SQUARE_NB]; + +// init() initializes piece-square tables: the white halves of the tables are +// copied from Bonus[] adding the piece value, then the black halves of the +// tables are initialized by flipping and changing the sign of the white scores. +void init() { + + for (Piece pc = W_PAWN; pc <= W_KING; ++pc) + { + PieceValue[MG][~pc] = PieceValue[MG][pc]; + PieceValue[EG][~pc] = PieceValue[EG][pc]; + + Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); + + for (Square s = SQ_A1; s <= SQ_H8; ++s) + { + File f = std::min(file_of(s), FILE_H - file_of(s)); + psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f]; + psq[~pc][~s] = -psq[pc][s]; + } + } +} + +} // namespace PSQT diff --git a/Engines/Windows/deepfish/srcD/search.cpp b/Engines/Windows/mcbrain/src/search.cpp similarity index 64% rename from Engines/Windows/deepfish/srcD/search.cpp rename to Engines/Windows/mcbrain/src/search.cpp index 61614c6..55af8e0 100644 --- a/Engines/Windows/deepfish/srcD/search.cpp +++ b/Engines/Windows/mcbrain/src/search.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,12 +29,14 @@ #include "misc.h" #include "movegen.h" #include "movepick.h" +#include "position.h" #include "search.h" #include "timeman.h" #include "thread.h" #include "tt.h" +#include "tzbook.h" #include "uci.h" -#include "syzygy/tbprobe.h" +#include "tbprobe.h" namespace Search { @@ -45,7 +47,6 @@ namespace Search { namespace Tablebases { int Cardinality; - uint64_t Hits; bool RootInTB; bool UseRule50; Depth ProbeDepth; @@ -63,6 +64,10 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; + // Sizes and phases of the skip-blocks, used for distributing search depths across the threads + const int skipSize[] = { 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 }; + const int skipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; + // Razoring and futility margin based on depth const int razor_margin[4] = { 483, 570, 603, 554 }; Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); } @@ -75,6 +80,12 @@ namespace { return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY; } + // History and stats update bonus, based on depth + Value stat_bonus(Depth depth) { + int d = depth / ONE_PLY ; + return d > 17 ? VALUE_ZERO : Value(d * d + 2 * d - 2); + } + // Skill structure is used to implement strength limit struct Skill { Skill(int l) : level(l) {} @@ -113,8 +124,8 @@ namespace { std::copy(newPv.begin(), newPv.begin() + 3, pv); StateInfo st[2]; - pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0])); - pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1])); + pos.do_move(newPv[0], st[0]); + pos.do_move(newPv[1], st[1]); expectedPosKey = pos.key(); pos.undo_move(newPv[1]); pos.undo_move(newPv[0]); @@ -126,44 +137,19 @@ namespace { Move pv[3]; }; - // Set of rows with half bits set to 1 and half to 0. It is used to allocate - // the search depths across the threads. - typedef std::vector Row; - - const Row HalfDensity[] = { - {0, 1}, - {1, 0}, - {0, 0, 1, 1}, - {0, 1, 1, 0}, - {1, 1, 0, 0}, - {1, 0, 0, 1}, - {0, 0, 0, 1, 1, 1}, - {0, 0, 1, 1, 1, 0}, - {0, 1, 1, 1, 0, 0}, - {1, 1, 1, 0, 0, 0}, - {1, 1, 0, 0, 0, 1}, - {1, 0, 0, 0, 1, 1}, - {0, 0, 0, 0, 1, 1, 1, 1}, - {0, 0, 0, 1, 1, 1, 1, 0}, - {0, 0, 1, 1, 1, 1, 0 ,0}, - {0, 1, 1, 1, 1, 0, 0 ,0}, - {1, 1, 1, 1, 0, 0, 0 ,0}, - {1, 1, 1, 0, 0, 0, 0 ,1}, - {1, 1, 0, 0, 0, 0, 1 ,1}, - {1, 0, 0, 0, 0, 1, 1 ,1}, - }; - - const size_t HalfDensitySize = std::extent::value; - EasyMoveManager EasyMove; Value DrawValue[COLOR_NB]; - CounterMoveHistoryStats CounterMoveHistory; + int respect, tactical; + int variety; + + bool botvinnikMarkov, findMate, futility, limitStrength, lmr, null, probCut, pruning, razor, + showInfo; template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning); template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); + Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); @@ -172,49 +158,22 @@ namespace { void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, Value bonus); void check_time(); -} // namespace - -/// Search::init() is called during startup to initialize various lookup tables - -void Search::init() { - - for (int imp = 0; imp <= 1; ++imp) - for (int d = 1; d < 64; ++d) - for (int mc = 1; mc < 64; ++mc) - { - double r = log(d) * log(mc) / 2; - if (r < 0.80) - continue; - - Reductions[NonPV][imp][d][mc] = int(std::round(r)); - Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0); - - // Increase reduction for non-PV nodes when eval is not improving - if (!imp && Reductions[NonPV][imp][d][mc] >= 2) - Reductions[NonPV][imp][d][mc]++; - } - - for (int d = 0; d < 16; ++d) - { - FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8)); - FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8)); - } -} +} // namespace /// Search::clear() resets search state to zero, to obtain reproducible results void Search::clear() { TT.clear(); - CounterMoveHistory.clear(); for (Thread* th : Threads) { - th->history.clear(); th->counterMoves.clear(); - th->fromTo.clear(); + th->history.clear(); + th->counterMoveHistory.clear(); + th->resetCalls = true; } Threads.main()->previousScore = VALUE_INFINITE; @@ -236,7 +195,7 @@ uint64_t Search::perft(Position& pos, Depth depth) { cnt = 1, nodes++; else { - pos.do_move(m, st, pos.gives_check(m)); + pos.do_move(m, st); cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); nodes += cnt; pos.undo_move(m); @@ -254,18 +213,74 @@ template uint64_t Search::perft(Position&, Depth); /// the UCI 'go' command. It searches from the root position and outputs the "bestmove". void MainThread::search() { + + //Initialize lookup tables + double lmrDep , lmrMc, lmrDiv; + lmrDep = Options["LMRDepth"]; + lmrDiv = Options["LMRDivisor"]; + lmrMc = Options["LMRMoveCount"]; + + + for (int imp = 0; imp <= 1; ++imp) + for (int d = 1; d < 64; ++d) + for (int mc = 1; mc < 64; ++mc) + { + + double r = log(d * lmrDep / 100) * log(mc * lmrMc / 100) / (lmrDiv / 100); + + Reductions[NonPV][imp][d][mc] = int(std::round(r)); + Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0); + + // Increase reduction for non-PV nodes when eval is not improving + if (!imp && Reductions[NonPV][imp][d][mc] >= 2) + Reductions[NonPV][imp][d][mc]++; + } + + for (int d = 0; d < 16; ++d) + { + FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8)); + FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8)); + } Color us = rootPos.side_to_move(); Time.init(Limits, us, rootPos.game_ply()); - -#ifdef DEEP - int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns -#else - const int contempt = 0; -#endif - DrawValue[ us] = VALUE_DRAW - Value(contempt); - DrawValue[~us] = VALUE_DRAW + Value(contempt); - + + // Read search options + botvinnikMarkov = Options["Botvinnik-Markov"]; + findMate = Options["FindMate"]; + futility = Options["Futility"]; + limitStrength = Options["UCI_LimitStrength"]; + lmr = Options["LMR"]; + null = Options["NullMove"]; + probCut = Options["ProbCut"]; + pruning = Options["Pruning"]; + razor = Options["Razoring"]; + showInfo = Options["ShowInfo"]; + respect = Options["Respect"] * PawnValueEg / 100; // From centipawns + tactical = Options["Tactical"]; + variety = Options["Variety"]; + + + DrawValue[ us] = VALUE_DRAW + Value(respect); + DrawValue[~us] = VALUE_DRAW - Value(respect); + + if (Options["UCI_Limit_Strength"]) + { + + int uci_elo = (Options["UCI_Elo"]); + int lower_elo = uci_elo - 75; + int upper_elo = uci_elo + 75; + + int use_rating = rand() % (upper_elo - lower_elo +1 ) + lower_elo; + int NodesToSearch = pow(1.0069555500567,(((use_rating)/1200) -1 ) + + (use_rating - 1200)) * 32 ; + Limits.nodes = NodesToSearch; + + if (Options["UCI_Elo_Delay"]) + std::this_thread::sleep_for (std::chrono::seconds(Time.optimum()/1000)); + } + + if (rootMoves.empty()) { rootMoves.push_back(RootMove(MOVE_NONE)); @@ -273,16 +288,29 @@ void MainThread::search() { << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; } - else - { - for (Thread* th : Threads) - if (th != this) - th->start_searching(); - - - Thread::search(); // Let's start searching! - } - + else + { + Move bookMove = MOVE_NONE; + + if (!Limits.infinite && !Limits.mate) + bookMove = tzbook.probe2(rootPos); + + if (bookMove && std::count(rootMoves.begin(), rootMoves.end(), bookMove)) + { + std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), bookMove)); + for (Thread* th : Threads) + if (th != this) + std::swap(th->rootMoves[0], *std::find(th->rootMoves.begin(), th->rootMoves.end(), bookMove)); + } + else + { + for (Thread* th : Threads) + if (th != this) + th->start_searching(); + + Thread::search(); // Let's start searching! + } + } // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. if (Limits.npmsec) @@ -316,9 +344,13 @@ void MainThread::search() { && rootMoves[0].pv[0] != MOVE_NONE) { for (Thread* th : Threads) - if ( th->completedDepth > bestThread->completedDepth - && th->rootMoves[0].score > bestThread->rootMoves[0].score) + { + Depth depthDiff = th->completedDepth - bestThread->completedDepth; + Value scoreDiff = th->rootMoves[0].score - bestThread->rootMoves[0].score; + + if (scoreDiff > 0 && depthDiff >= 0) bestThread = th; + } } previousScore = bestThread->rootMoves[0].score; @@ -329,6 +361,7 @@ void MainThread::search() { sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); + if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); @@ -336,18 +369,23 @@ void MainThread::search() { } -// Thread::search() is the main iterative deepening loop. It calls search() -// repeatedly with increasing depth until the allocated thinking time has been -// consumed, the user stops the search, or the maximum search depth is reached. +/// Thread::search() is the main iterative deepening loop. It calls search() +/// repeatedly with increasing depth until the allocated thinking time has been +/// consumed, the user stops the search, or the maximum search depth is reached. void Thread::search() { - Stack stack[MAX_PLY+7], *ss = stack+5; // To allow referencing (ss-5) and (ss+2) + Stack stack[MAX_PLY+7], *ss = stack+4; // To allow referencing (ss-4) and (ss+2) Value bestValue, alpha, beta, delta; Move easyMove = MOVE_NONE; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); - std::memset(ss-5, 0, 8 * sizeof(Stack)); + std::memset(ss-4, 0, 7 * sizeof(Stack)); + (ss-1)->threatMove = MOVE_NONE; + (ss-2)->threatMove = MOVE_NONE; + + for(int i = 4; i > 0; i--) + (ss-i)->counterMoves = &this->counterMoveHistory[NO_PIECE][0]; // Use as sentinel bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -364,6 +402,7 @@ void Thread::search() { size_t multiPV = Options["MultiPV"]; Skill skill(Options["Skill Level"]); + if (tactical) multiPV = pow(2, tactical); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves. @@ -377,13 +416,12 @@ void Thread::search() { && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth / ONE_PLY <= Limits.depth)) { - // Set up the new depths for the helper threads skipping on average every - // 2nd ply (using a half-density matrix). - if (!mainThread) + // Distribute search depths across the threads + if (idx) { - const Row& row = HalfDensity[(idx - 1) % HalfDensitySize]; - if (row[(rootDepth / ONE_PLY + rootPos.game_ply()) % row.size()]) - continue; + int i = (idx - 1) % 20; + if (((rootDepth / ONE_PLY + rootPos.game_ply() + skipPhase[i]) / skipSize[i]) % 2) + continue; } // Age out PV variability metric @@ -411,7 +449,7 @@ void Thread::search() { // high/low anymore. while (true) { - bestValue = ::search(rootPos, ss, alpha, beta, rootDepth, false); + bestValue = ::search(rootPos, ss, alpha, beta, rootDepth, false, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -421,7 +459,7 @@ void Thread::search() { // search the already searched PV lines are preserved. std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end()); - // If search has been stopped, break immediately. Sorting and + // If search has been stopped, we break immediately. Sorting and // writing PV back to TT is safe because RootMoves is still // valid, although it refers to the previous iteration. if (Signals.stop) @@ -467,11 +505,7 @@ void Thread::search() { if (!mainThread) continue; - if (Signals.stop) - sync_cout << "info nodes " << Threads.nodes_searched() - << " time " << Time.elapsed() << sync_endl; - - else if (PVIdx + 1 == multiPV || Time.elapsed() > 3000) + if (Signals.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000) sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; } @@ -480,16 +514,23 @@ void Thread::search() { if (!mainThread) continue; - - // If skill level is enabled and time is up, pick a sub-optimal best move - if (skill.enabled() && skill.time_to_pick(rootDepth)) - skill.pick_best(multiPV); - - // Have we found a "mate in x"? - if ( Limits.mate - && bestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - bestValue <= 2 * Limits.mate) - Signals.stop = true; + + // If skill level is enabled and time is up, pick a sub-optimal best move + if (skill.enabled() && skill.time_to_pick(rootDepth)) + skill.pick_best(multiPV); + + if (Options["FastPlay"]) + { + if ( Time.elapsed() > Time.optimum() / 256 + && ( abs(bestValue) > 12300 || abs(bestValue) >= VALUE_MATE_IN_MAX_PLY )) + Signals.stop = true; + } + + // Have we found a "mate in x"? + if ( Limits.mate + && bestValue >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - bestValue <= 2 * Limits.mate) + Signals.stop = true; // Do we have time for the next iteration? Can we stop searching now? if (Limits.use_time_management()) @@ -507,12 +548,11 @@ void Thread::search() { bool doEasyMove = rootMoves[0].pv[0] == easyMove && mainThread->bestMoveChanges < 0.03 - && Time.elapsed() > Time.optimum() * 5 / 42; - + && Time.elapsed() > Time.optimum() * 5 / 44; if ( rootMoves.size() == 1 || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628 - || (mainThread->easyMovePlayed = doEasyMove)) + || (mainThread->easyMovePlayed = doEasyMove, doEasyMove)) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -550,7 +590,7 @@ namespace { // search<>() is the main search function for both PV and non-PV nodes template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning) { const bool PvNode = NT == PV; const bool rootNode = PvNode && (ss-1)->ply == 0; @@ -565,18 +605,20 @@ namespace { StateInfo st; TTEntry* tte; Key posKey; - Move ttMove, move, excludedMove, bestMove; - Depth extension, newDepth, predictedDepth; - Value bestValue, value, ttValue, eval, nullValue; - bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning; + Move ttMove, move, excludedMove, bestMove, threatMove; + Depth extension, newDepth; + Value bestValue, value, ttValue, eval; + bool ttHit, inCheck, givesCheck, singularExtensionNode, improving, bmExtNode; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, skipBadCaptures; Piece moved_piece; int moveCount, quietCount; // Step 1. Initialize node + bmExtNode = false; Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); moveCount = quietCount = ss->moveCount = 0; + ss->history = VALUE_ZERO; bestValue = -VALUE_INFINITE; ss->ply = (ss-1)->ply + 1; @@ -584,9 +626,13 @@ namespace { if (thisThread->resetCalls.load(std::memory_order_relaxed)) { thisThread->resetCalls = false; - thisThread->callsCnt = 0; + // At low node count increase the checking rate to about 0.1% of nodes + // otherwise use a default value. + thisThread->callsCnt = Limits.nodes ? std::min((int64_t)4096, Limits.nodes / 1024) + : 4096; } - if (++thisThread->callsCnt > 4096) + + if (--thisThread->callsCnt <= 0) { for (Thread* th : Threads) th->resetCalls = true; @@ -601,7 +647,7 @@ namespace { if (!rootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY) + if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; @@ -619,16 +665,16 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; - ss->counterMoves = nullptr; - (ss+1)->skipEarlyPruning = false; + ss->currentMove = ss->threatMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + ss->counterMoves = &thisThread->counterMoveHistory[NO_PIECE][0]; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + Square prevSq = to_sq((ss-1)->currentMove); // Step 4. Transposition table lookup. We don't want the score of a partial // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = excludedMove ? pos.exclusion_key() : pos.key(); + posKey = pos.key() ^ Key(excludedMove); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0] @@ -642,25 +688,24 @@ namespace { && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) { - ss->currentMove = ttMove; // Can be MOVE_NONE - - // If ttMove is quiet, update killers, history, counter move on TT hit - if (ttValue >= beta && ttMove) + // If ttMove is quiet, update move sorting heuristics on TT hit + if (ttMove) { - int d = depth / ONE_PLY; - - if (!pos.capture_or_promotion(ttMove)) + if (ttValue >= beta) { - Value bonus = Value(d * d + 2 * d - 2); - update_stats(pos, ss, ttMove, nullptr, 0, bonus); - } + if (!pos.capture_or_promotion(ttMove)) + update_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); - // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) + // Extra penalty for a quiet TT move in previous ply when it gets refuted + if ((ss-1)->moveCount == 1 && !pos.captured_piece()) + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + } + // Penalty for a quiet ttMove that fails low + else if (!pos.capture_or_promotion(ttMove)) { - Value penalty = Value(d * d + 4 * d + 1); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); + Value penalty = -stat_bonus(depth + ONE_PLY); + thisThread->history.update(pos.side_to_move(), ttMove, penalty); + update_cm_stats(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } } return ttValue; @@ -669,18 +714,19 @@ namespace { // Step 4a. Tablebase probe if (!rootNode && TB::Cardinality) { - int piecesCnt = pos.count(WHITE) + pos.count(BLACK); + int piecesCount = pos.count(); - if ( piecesCnt <= TB::Cardinality - && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) + if ( piecesCount <= TB::Cardinality + && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { - int found, v = Tablebases::probe_wdl(pos, &found); + TB::ProbeState err; + TB::WDLScore v = Tablebases::probe_wdl(pos, &err); - if (found) + if (err != TB::ProbeState::FAIL) { - TB::Hits++; + thisThread->tbHits++; int drawScore = TB::UseRule50 ? 1 : 0; @@ -725,53 +771,156 @@ namespace { ss->staticEval, TT.generation()); } - if (ss->skipEarlyPruning) + if (skipEarlyPruning) goto moves_loop; - -#ifdef DEEP + + if (findMate) { // Step 6. Razoring (skipped when in check) - if ( !PvNode - && depth < 4 * ONE_PLY - && ttMove == MOVE_NONE - && eval + razor_margin[depth / ONE_PLY] <= alpha) + if ( razor + && !PvNode + && depth < 4 * ONE_PLY + && ttMove == MOVE_NONE + && eval + razor_margin[depth / ONE_PLY] <= alpha + && abs(eval) < 2 * VALUE_KNOWN_WIN) { - if ( depth <= ONE_PLY - && eval + razor_margin[3 * ONE_PLY] <= alpha) - return qsearch(pos, ss, alpha, beta, DEPTH_ZERO); - - Value ralpha = alpha - razor_margin[depth / ONE_PLY]; - Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); - if (v <= ralpha) - return v; - } - + if (depth <= ONE_PLY) + return qsearch(pos, ss, alpha, alpha+1); + + Value ralpha = alpha - razor_margin[depth / ONE_PLY]; + Value v = qsearch(pos, ss, ralpha, ralpha+1); + if (v <= ralpha) + return v; + } + } + else { + // Step 6. Razoring (skipped when in check) + if ( razor + && !PvNode + && depth < 4 * ONE_PLY + && eval + razor_margin[depth / ONE_PLY] <= alpha) + { + if (depth <= ONE_PLY) + return qsearch(pos, ss, alpha, alpha+1); + + Value ralpha = alpha - razor_margin[depth / ONE_PLY]; + Value v = qsearch(pos, ss, ralpha, ralpha+1); + if (v <= ralpha) + return v; + } + } + if (findMate) { + // Step 7. Futility pruning: child node (skipped when in check) + if ( futility + && !PvNode + && depth < 7 * ONE_PLY + && eval - futility_margin(depth) >= beta + && eval < VALUE_KNOWN_WIN // Do not return unproven wins + && pos.non_pawn_material(pos.side_to_move()) + && pos.non_pawn_material(~pos.side_to_move())) + return eval; + } + + else { // Step 7. Futility pruning: child node (skipped when in check) - if ( !rootNode + if ( futility + && !rootNode && depth < 7 * ONE_PLY && eval - futility_margin(depth) >= beta && eval < VALUE_KNOWN_WIN // Do not return unproven wins && pos.non_pawn_material(pos.side_to_move())) - return eval - futility_margin(depth); - + return eval; + } + ss->threatMove = MOVE_NONE; + if (findMate){ + // Step 8. Null move search with verification search (is omitted in PV nodes) + if ( null + && !PvNode + && eval >= beta + && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) + && pos.non_pawn_material(pos.side_to_move()) + && pos.non_pawn_material(~pos.side_to_move()) + && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 == thisThread->pair) + && !(depth > 12 * ONE_PLY && MoveList(pos).size() < 4)) + + { + + assert(eval - beta >= 0); + // Null move dynamic reduction based on depth and value + Depth R = (abs(beta) < 2 * VALUE_KNOWN_WIN) ? ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) \ + / PawnValueMg, 3)) * ONE_PLY : ((823 + 67 * depth / ONE_PLY) / 256) * ONE_PLY; + + + + ss->currentMove = MOVE_NULL; + ss->counterMoves = &thisThread->counterMoveHistory[NO_PIECE][0]; + + pos.do_null_move(st); + Value nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1) + : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode, true); + pos.undo_null_move(); + ss->threatMove = (ss+1)->currentMove; + + if (nullValue >= beta) + { + // Do not return unproven mate scores + if (nullValue >= VALUE_MATE_IN_MAX_PLY) + nullValue = beta; + + if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) + return nullValue; + + // Do verification search at high depths + // increase reduction ... + R += ONE_PLY; + // but disable nmp for the side to move for first part of the search tree + int nmp_ply = thisThread->nmp_ply; + int pair = thisThread->pair; + thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4; + thisThread->pair = (ss->ply % 2) == 0; + + Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta) + : search(pos, ss, beta-1, beta, depth-R, false, true); + + thisThread->pair = pair; + thisThread->nmp_ply = nmp_ply; + + if (v >= beta) + return nullValue; + } + + else if (botvinnikMarkov) + { + // Trigger Botvinnik-Markov extension if current threat is the same as for 2 plies + threatMove = (ss+1)->currentMove; + if ( ss->ply >= 2 + && threatMove != MOVE_NONE + && threatMove == (ss-2)->threatMove) + bmExtNode = true; + } + } + } + else { // Step 8. Null move search with verification search (is omitted in PV nodes) - if ( !PvNode + if ( null + && !PvNode && eval >= beta && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) - && pos.non_pawn_material(pos.side_to_move())) + && pos.non_pawn_material(pos.side_to_move()) + && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 == thisThread->pair)) + { - ss->currentMove = MOVE_NULL; - ss->counterMoves = nullptr; assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; + Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; + + ss->currentMove = MOVE_NULL; + ss->counterMoves = &thisThread->counterMoveHistory[NO_PIECE][0]; pos.do_null_move(st); - (ss+1)->skipEarlyPruning = true; - nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); - (ss+1)->skipEarlyPruning = false; + Value nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1) + : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode, true); pos.undo_null_move(); if (nullValue >= beta) @@ -783,30 +932,53 @@ namespace { if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) return nullValue; - // Do verification search at high depths - ss->skipEarlyPruning = true; - Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta, DEPTH_ZERO) - : search(pos, ss, beta-1, beta, depth-R, false); - ss->skipEarlyPruning = false; - - if (v >= beta) - return nullValue; - } + // Do verification search at high depths + // increase reduction ... + R += ONE_PLY; + // but disable nmp for the side to move for first part of the search tree + int nmp_ply = thisThread->nmp_ply; + int pair = thisThread->pair; + thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4; + thisThread->pair = (ss->ply % 2) == 0; + + Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta) + : search(pos, ss, beta-1, beta, depth-R, false, true); + + thisThread->pair = pair; + thisThread->nmp_ply = nmp_ply; + + if (v >= beta) + return nullValue; + } + + else if (botvinnikMarkov) + { + // Trigger Botvinnik-Markov extension if current threat is the same as for 2 plies + threatMove = (ss+1)->currentMove; + if ( ss->ply >= 2 + && threatMove != MOVE_NONE + && threatMove == (ss-2)->threatMove) + bmExtNode = true; + } } - + } + + if (findMate) { // Step 9. ProbCut (skipped when in check) // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - if ( !PvNode - && depth >= 5 * ONE_PLY - && abs(beta) < VALUE_MATE_IN_MAX_PLY) + if ( probCut + && !PvNode + && depth >= 5 * ONE_PLY + && ss->ply % 2 == 1 + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && abs(eval) < 2 * VALUE_KNOWN_WIN) { Value rbeta = std::min(beta + 200, VALUE_INFINITE); Depth rdepth = depth - 4 * ONE_PLY; assert(rdepth >= ONE_PLY); - assert((ss-1)->currentMove != MOVE_NONE); - assert((ss-1)->currentMove != MOVE_NULL); + assert(is_ok((ss-1)->currentMove)); MovePicker mp(pos, ttMove, rbeta - ss->staticEval); @@ -814,14 +986,47 @@ namespace { if (pos.legal(move)) { ss->currentMove = move; - ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(move)]; - pos.do_move(move, st, pos.gives_check(move)); - value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); + ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)]; + + pos.do_move(move, st); + value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode, false); pos.undo_move(move); if (value >= rbeta) return value; } } + } + else { + // Step 9. ProbCut (skipped when in check) + // If we have a good enough capture and a reduced search returns a value + // much above beta, we can (almost) safely prune the previous move. + if ( probCut + && !PvNode + && depth >= 5 * ONE_PLY + && abs(beta) < VALUE_MATE_IN_MAX_PLY) + { + Value rbeta = std::min(beta + 200, VALUE_INFINITE); + Depth rdepth = depth - 4 * ONE_PLY; + + assert(rdepth >= ONE_PLY); + assert(is_ok((ss-1)->currentMove)); + + MovePicker mp(pos, ttMove, rbeta - ss->staticEval); + + while ((move = mp.next_move()) != MOVE_NONE) + if (pos.legal(move)) + { + ss->currentMove = move; + ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)]; + + pos.do_move(move, st); + value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode, false); + pos.undo_move(move); + if (value >= rbeta) + return value; + } + } + } // Step 10. Internal iterative deepening (skipped when in check) if ( depth >= 6 * ONE_PLY @@ -829,20 +1034,20 @@ namespace { && (PvNode || ss->staticEval + 256 >= beta)) { Depth d = (3 * depth / (4 * ONE_PLY) - 2) * ONE_PLY; - ss->skipEarlyPruning = true; - search(pos, ss, alpha, beta, d, cutNode); - ss->skipEarlyPruning = false; + search(pos, ss, alpha, beta, d, cutNode, true); tte = TT.probe(posKey, ttHit); ttMove = ttHit ? tte->move() : MOVE_NONE; } -#endif moves_loop: // When in check search starts from here - const CounterMoveStats* cmh = (ss-1)->counterMoves; - const CounterMoveStats* fmh = (ss-2)->counterMoves; - const CounterMoveStats* fmh2 = (ss-4)->counterMoves; + const CounterMoveStats& cmh = *(ss-1)->counterMoves; + const CounterMoveStats& fmh = *(ss-2)->counterMoves; + const CounterMoveStats& fm2 = *(ss-4)->counterMoves; + const bool cm_ok = is_ok((ss-1)->currentMove); + const bool fm_ok = is_ok((ss-2)->currentMove); + const bool f2_ok = is_ok((ss-4)->currentMove); MovePicker mp(pos, ttMove, depth, ss); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc @@ -852,16 +1057,17 @@ namespace { singularExtensionNode = !rootNode && depth >= 8 * ONE_PLY + //&& abs(beta) < 2 * VALUE_KNOWN_WIN && ttMove != MOVE_NONE - /* && ttValue != VALUE_NONE Already implicit in the next condition */ - && abs(ttValue) < VALUE_KNOWN_WIN + && ttValue != VALUE_NONE && !excludedMove // Recursive singular search is not allowed && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY; + skipQuiets = skipBadCaptures = false; // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs - while ((move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move(skipQuiets, skipBadCaptures)) != MOVE_NONE) { assert(is_ok(move)); @@ -877,12 +1083,11 @@ namespace { ss->moveCount = ++moveCount; - if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) + if (showInfo && rootNode && thisThread == Threads.main() && Time.elapsed() > 5000) sync_cout << "info depth " << depth / ONE_PLY << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->PVIdx << sync_endl; - if (PvNode) (ss+1)->pv = nullptr; @@ -897,77 +1102,142 @@ namespace { moveCountPruning = depth < 16 * ONE_PLY && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; - // Step 12. Extend checks - if ( givesCheck - && !moveCountPruning - && pos.see_sign(move) >= VALUE_ZERO) - extension = ONE_PLY; - - // Singular extension search. If all moves but one fail low on a search of - // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move - // is singular and should be extended. To verify this we do a reduced search - // on all the other moves but the ttMove and if the result is lower than - // ttValue minus a margin then we extend the ttMove. - if ( singularExtensionNode - && move == ttMove - && !extension - && pos.legal(move)) - { - Value rBeta = ttValue - 2 * depth / ONE_PLY; - Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY; - ss->excludedMove = move; - ss->skipEarlyPruning = true; - value = search(pos, ss, rBeta - 1, rBeta, d, cutNode); - ss->skipEarlyPruning = false; - ss->excludedMove = MOVE_NONE; - - if (value < rBeta) - extension = ONE_PLY; - } - - // Update the current move (this must be done after singular extension search) + // Step 12. Extensions + + // Singular extension search. If all moves but one fail low on a search of + // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move + // is singular and should be extended. To verify this we do a reduced search + // on all the other moves but the ttMove and if the result is lower than + // ttValue minus a margin then we extend the ttMove. + if ( singularExtensionNode + && move == ttMove + //&& !extension + && pos.legal(move)) + { + Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); + Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY; + ss->excludedMove = move; + value = search(pos, ss, rBeta - 1, rBeta, d, cutNode, true); + ss->excludedMove = MOVE_NONE; + + if (value < rBeta) + extension = ONE_PLY; + } + // Extend checks + else if (findMate) + { + + if ( givesCheck && pos.see_ge(move, VALUE_ZERO)) + extension = ONE_PLY; + } + else if ( givesCheck + && !moveCountPruning + && pos.see_ge(move, VALUE_ZERO)) + extension = ONE_PLY; + else if ( botvinnikMarkov && bmExtNode ) + extension = ONE_PLY; + + // Calculate new depth for this move newDepth = depth - ONE_PLY + extension; -#ifdef DEEP - // Step 13. Pruning at shallow depth - if ( !rootNode - && !captureOrPromotion - && !inCheck - && !givesCheck - && bestValue > VALUE_MATED_IN_MAX_PLY - && !pos.advanced_pawn_push(move)) + if (findMate) + { + // Step 13. Pruning at shallow depth + if ( pruning + && !PvNode + && bestValue > VALUE_MATED_IN_MAX_PLY) + { + if ( !captureOrPromotion + && !givesCheck + && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= 5000)) + { + // Move count based pruning + if (moveCountPruning) + { + skipQuiets = true; + continue; + } + + // Reduced depth of the next LMR search + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; + + // Countermoves based pruning + if ( lmrDepth < 3 + && ((cmh[moved_piece][to_sq(move)] < VALUE_ZERO) || !cm_ok) + && ((fmh[moved_piece][to_sq(move)] < VALUE_ZERO) || !fm_ok) + && ((fm2[moved_piece][to_sq(move)] < VALUE_ZERO) || !f2_ok || (cm_ok && fm_ok))) + continue; + + // Futility pruning: parent node + if ( lmrDepth < 7 + && !inCheck + && ss->staticEval + 256 + 200 * lmrDepth <= alpha) + continue; + + // Prune moves with negative SEE + if ( lmrDepth < 8 + && !pos.see_ge(move, Value(-35 * lmrDepth * lmrDepth))) + continue; + } + else if (depth < 7 * ONE_PLY + && !extension + && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) + { + + skipBadCaptures = moveCountPruning && depth < 2; + continue; + } + } + } + // Step 13. Pruning at shallow depth + else { + if ( pruning + && !rootNode + && bestValue > VALUE_MATED_IN_MAX_PLY) { - // Move count based pruning - if (moveCountPruning) - continue; - - predictedDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); - - // Countermoves based pruning - if ( predictedDepth < 3 * ONE_PLY - && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) - && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO) - && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh))) - continue; - - // Futility pruning: parent node - if ( predictedDepth < 7 * ONE_PLY - && ss->staticEval + 256 + 200 * predictedDepth / ONE_PLY <= alpha) - continue; - - // Prune moves with negative SEE at low depths and below a decreasing - // threshold at higher depths. - if (predictedDepth < 8 * ONE_PLY) + if ( !captureOrPromotion + && !givesCheck + && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= 5000)) { - Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO - : -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY) / ONE_PLY; + // Move count based pruning + if (moveCountPruning) + { + skipQuiets = true; + continue; + } + + // Reduced depth of the next LMR search + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; + + // Countermoves based pruning + if ( lmrDepth < 3 + && ((cmh[moved_piece][to_sq(move)] < VALUE_ZERO) || !cm_ok) + && ((fmh[moved_piece][to_sq(move)] < VALUE_ZERO) || !fm_ok) + && ((fm2[moved_piece][to_sq(move)] < VALUE_ZERO) || !f2_ok || (cm_ok && fm_ok))) + continue; + + // Futility pruning: parent node + if ( lmrDepth < 7 + && !inCheck + && ss->staticEval + 256 + 200 * lmrDepth <= alpha) + continue; - if (pos.see_sign(move) < see_v) + // Prune moves with negative SEE + if ( lmrDepth < 8 + && !pos.see_ge(move, Value(-35 * lmrDepth * lmrDepth))) continue; } + else if (depth < 7 * ONE_PLY + && !extension + && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) + { + + skipBadCaptures = moveCountPruning && depth < 2; + continue; + } } + } -#endif // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); @@ -978,15 +1248,79 @@ namespace { continue; } + // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->counterMoves = &CounterMoveHistory[moved_piece][to_sq(move)]; + ss->counterMoves = &thisThread->counterMoveHistory[moved_piece][to_sq(move)]; // Step 14. Make the move pos.do_move(move, st, givesCheck); + + if (findMate) + { + // Step 15. Reduced depth search (LMR). If the move fails high it will be + // re-searched at full depth. + if ( depth >= 3 * ONE_PLY + && moveCount > 1 + && (!captureOrPromotion || moveCountPruning) + && thisThread->maxPly > depth + && !(depth >= 16 * ONE_PLY && ss->ply <= 3 * ONE_PLY)) + { + Depth r = reduction(improving, depth, moveCount); + + if (captureOrPromotion) + r -= r ? ONE_PLY : DEPTH_ZERO; + else + { + // Increase reduction for cut nodes + if (cutNode) + r += 2 * ONE_PLY; + + // Decrease reduction for moves that escape a capture. Filter out + // castling moves, because they are coded as "king captures rook" and + // hence break make_move(). + else if ( type_of(move) == NORMAL + && !pos.see_ge(make_move(to_sq(move), from_sq(move)), VALUE_ZERO)) + r -= 2 * ONE_PLY; + + ss->history = cmh[moved_piece][to_sq(move)] + + fmh[moved_piece][to_sq(move)] + + fm2[moved_piece][to_sq(move)] + + thisThread->history.get(~pos.side_to_move(), move) + - 4000; // Correction factor + + // Decrease/increase reduction by comparing opponent's stat score + if (ss->history > VALUE_ZERO && (ss-1)->history < VALUE_ZERO) + r -= ONE_PLY; + + else if (ss->history < VALUE_ZERO && (ss-1)->history > VALUE_ZERO) + r += ONE_PLY; + + // Decrease/increase reduction for moves with a good/bad history + r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY); + } + + // The "Tactical" option allows McBrain to look at more positions per search depth, but McBrain will + // play weaker overall. It sets the "MultiPV" option up to 256 on an exponential scale. + // It may help in analysis. + + if ( tactical && ss->ply < depth / 2 - ONE_PLY) + r = DEPTH_ZERO; + + Depth d = std::max(newDepth - r, ONE_PLY); + + value = -search(pos, ss+1, -(alpha+1), -alpha, d, true, false); + + doFullDepthSearch = (value > alpha && d != newDepth); + } + else + doFullDepthSearch = !PvNode || moveCount > 1; + } // Step 15. Reduced depth search (LMR). If the move fails high it will be // re-searched at full depth. - if ( depth >= 3 * ONE_PLY + else { + if ( lmr + && depth >= 3 * ONE_PLY && moveCount > 1 && (!captureOrPromotion || moveCountPruning)) { @@ -1002,38 +1336,52 @@ namespace { // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and - // hence break make_move(). Also use see() instead of see_sign(), - // because the destination square is empty. + // hence break make_move(). else if ( type_of(move) == NORMAL - && type_of(pos.piece_on(to_sq(move))) != PAWN - && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO) + && !pos.see_ge(make_move(to_sq(move), from_sq(move)), VALUE_ZERO)) r -= 2 * ONE_PLY; + ss->history = cmh[moved_piece][to_sq(move)] + + fmh[moved_piece][to_sq(move)] + + fm2[moved_piece][to_sq(move)] + + thisThread->history.get(~pos.side_to_move(), move) + - 4000; // Correction factor + + // Decrease/increase reduction by comparing opponent's stat score + if (ss->history > VALUE_ZERO && (ss-1)->history < VALUE_ZERO) + r -= ONE_PLY; + + else if (ss->history < VALUE_ZERO && (ss-1)->history > VALUE_ZERO) + r += ONE_PLY; + // Decrease/increase reduction for moves with a good/bad history - Value val = thisThread->history[moved_piece][to_sq(move)] - + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO) - + thisThread->fromTo.get(~pos.side_to_move(), move); - int rHist = (val - 8000) / 20000; - r = std::max(DEPTH_ZERO, (r / ONE_PLY - rHist) * ONE_PLY); + r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY); } + + // The "Tactical" option allows McBrain to look at more positions per search depth, but McBrain will + // play weaker overall. It sets the "MultiPV" option up to 256 on an exponential scale. + // It may help in analysis. + + if ( tactical && ss->ply < depth / 2 - ONE_PLY) + r = DEPTH_ZERO; Depth d = std::max(newDepth - r, ONE_PLY); - value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + value = -search(pos, ss+1, -(alpha+1), -alpha, d, true, false); doFullDepthSearch = (value > alpha && d != newDepth); } + else doFullDepthSearch = !PvNode || moveCount > 1; + } // Step 16. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha) + : -qsearch(pos, ss+1, -(alpha+1), -alpha) + : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode, false); // For PV nodes only, do a full PV search on the first move or after a fail // high (in the latter case search only if value < beta), otherwise let the @@ -1044,9 +1392,9 @@ namespace { (ss+1)->pv[0] = MOVE_NONE; value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, newDepth, false); + givesCheck ? -qsearch(pos, ss+1, -beta, -alpha) + : -qsearch(pos, ss+1, -beta, -alpha) + : - search(pos, ss+1, -beta, -alpha, newDepth, false, false); } // Step 17. Undo move @@ -1089,6 +1437,11 @@ namespace { // move position in the list is preserved - just the PV is pushed up. rm.score = -VALUE_INFINITE; } + + //Add a little variety to play + if (variety && value + (variety * 5 * PawnValueEg / 100) >= 0 ) + value += rand() % (variety * 5); + if (value > bestValue) { @@ -1096,13 +1449,6 @@ namespace { if (value > alpha) { - // If there is an easy move for this position, clear it if unstable - if ( PvNode - && thisThread == Threads.main() - && EasyMove.get(pos.key()) - && (move != EasyMove.get(pos.key()) || moveCount > 1)) - EasyMove.clear(); - bestMove = move; if (PvNode && !rootNode) // Update pv even in fail-high case @@ -1134,38 +1480,28 @@ namespace { // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. + + assert(moveCount || !inCheck || excludedMove || !MoveList(pos).size()); + if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; else if (bestMove) { - int d = depth / ONE_PLY; - // Quiet best move: update killers, history and countermoves + // Quiet best move: update move sorting heuristics if (!pos.capture_or_promotion(bestMove)) - { - Value bonus = Value(d * d + 2 * d - 2); - update_stats(pos, ss, bestMove, quietsSearched, quietCount, bonus); - } + update_stats(pos, ss, bestMove, quietsSearched, quietCount, stat_bonus(depth)); // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) - { - Value penalty = Value(d * d + 4 * d + 1); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); - } + if ((ss-1)->moveCount == 1 && !pos.captured_piece()) + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); } // Bonus for prior countermove that caused the fail low else if ( depth >= 3 * ONE_PLY - && !pos.captured_piece_type() - && is_ok((ss-1)->currentMove)) - { - int d = depth / ONE_PLY; - Value bonus = Value(d * d + 2 * d - 2); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus); - } + && !pos.captured_piece() + && cm_ok) + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); tte->save(posKey, value_to_tt(bestValue, ss->ply), bestValue >= beta ? BOUND_LOWER : @@ -1179,8 +1515,7 @@ namespace { // qsearch() is the quiescence search function, which is called by the main - // search function when the remaining depth is zero (or, to be more precise, - // less than ONE_PLY). + // search function with depth zero, or recursively with depth less than ONE_PLY. template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { @@ -1213,7 +1548,7 @@ namespace { ss->ply = (ss-1)->ply + 1; // Check for an instant draw or if the maximum ply has been reached - if (pos.is_draw() || ss->ply >= MAX_PLY) + if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; @@ -1237,10 +1572,7 @@ namespace { && ttValue != VALUE_NONE // Only in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) - { - ss->currentMove = ttMove; // Can be MOVE_NONE return ttValue; - } // Evaluate the position statically if (InCheck) @@ -1313,7 +1645,7 @@ namespace { continue; } - if (futilityBase <= alpha && pos.see(move) <= VALUE_ZERO) + if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1328,7 +1660,7 @@ namespace { // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) && type_of(move) != PROMOTION - && pos.see_sign(move) < VALUE_ZERO) + && !pos.see_ge(move, VALUE_ZERO)) continue; // Speculative prefetch as early as possible @@ -1348,6 +1680,10 @@ namespace { assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); + //Add a little variety to play + if (variety && value + (variety * 5 * PawnValueEg / 100) >= 0 ) + value += rand() % (variety * 5); + // Check for a new best move if (value > bestValue) { @@ -1428,23 +1764,13 @@ namespace { void update_cm_stats(Stack* ss, Piece pc, Square s, Value bonus) { - CounterMoveStats* cmh = (ss-1)->counterMoves; - CounterMoveStats* fmh1 = (ss-2)->counterMoves; - CounterMoveStats* fmh2 = (ss-4)->counterMoves; - - if (cmh) - cmh->update(pc, s, bonus); - - if (fmh1) - fmh1->update(pc, s, bonus); - - if (fmh2) - fmh2->update(pc, s, bonus); + for (int i : {1, 2, 4}) + if (is_ok((ss-i)->currentMove)) + (ss-i)->counterMoves->update(pc, s, bonus); } - // update_stats() updates killers, history, countermove and countermove plus - // follow-up move history when a new quiet best move is found. + // update_stats() updates move sorting heuristics when a new quiet best move is found void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, Value bonus) { @@ -1457,11 +1783,10 @@ namespace { Color c = pos.side_to_move(); Thread* thisThread = pos.this_thread(); - thisThread->fromTo.update(c, move, bonus); - thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus); + thisThread->history.update(c, move, bonus); update_cm_stats(ss, pos.moved_piece(move), to_sq(move), bonus); - if ((ss-1)->counterMoves) + if (is_ok((ss-1)->currentMove)) { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves.update(pos.piece_on(prevSq), prevSq, move); @@ -1470,8 +1795,7 @@ namespace { // Decrease all the other played quiet moves for (int i = 0; i < quietsCnt; ++i) { - thisThread->fromTo.update(c, quiets[i], -bonus); - thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); + thisThread->history.update(c, quiets[i], -bonus); update_cm_stats(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); } } @@ -1533,7 +1857,7 @@ namespace { if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10) || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && Threads.nodes_searched() >= Limits.nodes)) + || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) Signals.stop = true; } @@ -1550,7 +1874,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t PVIdx = pos.this_thread()->PVIdx; size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); - uint64_t nodes_searched = Threads.nodes_searched(); + uint64_t nodesSearched = Threads.nodes_searched(); + uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); for (size_t i = 0; i < multiPV; ++i) { @@ -1574,16 +1899,16 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { << " multipv " << i + 1 << " score " << UCI::value(v); - if (!tb && i == PVIdx) + if (showInfo && !tb && i == PVIdx) ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - ss << " nodes " << nodes_searched - << " nps " << nodes_searched * 1000 / elapsed; + ss << " nodes " << nodesSearched + << " nps " << nodesSearched * 1000 / elapsed; if (elapsed > 1000) // Earlier makes little sense ss << " hashfull " << TT.hashfull(); - ss << " tbhits " << TB::Hits + ss << " tbhits " << tbHits << " time " << elapsed << " pv"; @@ -1595,20 +1920,22 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { } - /// RootMove::extract_ponder_from_tt() is called in case we have no ponder move /// before exiting the search, for instance, in case we stop the search during a /// fail high at root. We try hard to have a ponder move to return to the GUI, /// otherwise in case of 'ponder on' we have nothing to think on. -bool RootMove::extract_ponder_from_tt(Position& pos) -{ +bool RootMove::extract_ponder_from_tt(Position& pos) { + StateInfo st; bool ttHit; assert(pv.size() == 1); - pos.do_move(pv[0], st, pos.gives_check(pv[0])); + if (!pv[0]) + return false; + + pos.do_move(pv[0], st); TTEntry* tte = TT.probe(pos.key(), ttHit); if (ttHit) @@ -1624,7 +1951,6 @@ bool RootMove::extract_ponder_from_tt(Position& pos) void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) { - Hits = 0; RootInTB = false; UseRule50 = Options["Syzygy50MoveRule"]; ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; @@ -1653,17 +1979,12 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) RootInTB = root_probe_wdl(pos, rootMoves, TB::Score); // Only probe during search if winning - if (TB::Score <= VALUE_DRAW) + if (RootInTB && TB::Score <= VALUE_DRAW) Cardinality = 0; } - if (RootInTB) - { - Hits = rootMoves.size(); - - if (!UseRule50) - TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 - : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 - : VALUE_DRAW; - } + if (RootInTB && !UseRule50) + TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 + : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 + : VALUE_DRAW; } diff --git a/Engines/Windows/deepfish/srcD/search.h b/Engines/Windows/mcbrain/src/search.h similarity index 89% rename from Engines/Windows/deepfish/srcD/search.h rename to Engines/Windows/mcbrain/src/search.h index 599d058..570903e 100644 --- a/Engines/Windows/deepfish/srcD/search.h +++ b/Engines/Windows/mcbrain/src/search.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,11 +25,10 @@ #include #include "misc.h" -#include "position.h" +#include "movepick.h" #include "types.h" -template struct Stats; -typedef Stats CounterMoveStats; +class Position; namespace Search { @@ -39,16 +38,18 @@ namespace Search { struct Stack { Move* pv; + CounterMoveStats* counterMoves; int ply; Move currentMove; Move excludedMove; Move killers[2]; + Move threatMove; Value staticEval; - bool skipEarlyPruning; + Value history; int moveCount; - CounterMoveStats* counterMoves; }; + /// RootMove struct is used for moves at the root of the tree. For each root move /// we store a score and a PV (really a refutation in the case of moves which /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. @@ -68,6 +69,7 @@ struct RootMove { typedef std::vector RootMoves; + /// LimitsType struct stores information sent by GUI about available time to /// search the current move, maximum depth/time, if we are in analysis mode or /// if we have to ponder while it's our opponent's turn to move. @@ -89,8 +91,9 @@ struct LimitsType { TimePoint startTime; }; -/// The SignalsType struct stores atomic flags updated during the search -/// typically in an async fashion e.g. to stop the search by the GUI. + +/// SignalsType struct stores atomic flags updated during the search, typically +/// in an async fashion e.g. to stop the search by the GUI. struct SignalsType { std::atomic_bool stop, stopOnPonderhit; diff --git a/Engines/Windows/mcbrain/src/tbprobe.cpp b/Engines/Windows/mcbrain/src/tbprobe.cpp new file mode 100644 index 0000000..6e3b91f --- /dev/null +++ b/Engines/Windows/mcbrain/src/tbprobe.cpp @@ -0,0 +1,1690 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (c) 2013 Ronald de Man + Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish 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, see . +*/ + +#include +#include +#include +#include // For std::memset +#include +#include +#include +#include +#include +#include + +#include "bitboard.h" +#include "movegen.h" +#include "position.h" +#include "search.h" +#include "thread_win32.h" +#include "types.h" + +#include "tbprobe.h" + +#ifndef _WIN32 +#include +#include +#include +#include +#else +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#endif + +using namespace Tablebases; + +int Tablebases::MaxCardinality; + +namespace { + +// Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables +enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, SingleValue = 128 }; + +inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } +inline Square operator^=(Square& s, int i) { return s = Square(int(s) ^ i); } +inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } + +// DTZ tables don't store valid scores for moves that reset the rule50 counter +// like captures and pawn moves but we can easily recover the correct dtz of the +// previous move if we know the position's WDL score. +int dtz_before_zeroing(WDLScore wdl) { + return wdl == WDLWin ? 1 : + wdl == WDLCursedWin ? 101 : + wdl == WDLBlessedLoss ? -101 : + wdl == WDLLoss ? -1 : 0; +} + +// Return the sign of a number (-1, 0, 1) +template int sign_of(T val) { + return (T(0) < val) - (val < T(0)); +} + +// Numbers in little endian used by sparseIndex[] to point into blockLength[] +struct SparseEntry { + char block[4]; // Number of block + char offset[2]; // Offset within the block +}; + +static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes"); + +typedef uint16_t Sym; // Huffman symbol + +struct LR { + enum Side { Left, Right, Value }; + + uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12 + // bits is the right-hand symbol. If symbol has length 1, + // then the first byte is the stored value. + template + Sym get() { + return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] : + S == Right ? (lr[2] << 4) | (lr[1] >> 4) : + S == Value ? lr[0] : (assert(false), Sym(-1)); + } +}; + +static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes"); + +const int TBPIECES = 6; + +struct PairsData { + int flags; + size_t sizeofBlock; // Block size in bytes + size_t span; // About every span values there is a SparseIndex[] entry + int blocksNum; // Number of blocks in the TB file + int maxSymLen; // Maximum length in bits of the Huffman symbols + int minSymLen; // Minimum length in bits of the Huffman symbols + Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value + LR* btree; // btree[sym] stores the left and right symbols that expand sym + uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536 + int blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum + SparseEntry* sparseIndex; // Partial indices into blockLength[] + size_t sparseIndexSize; // Size of SparseIndex[] table + uint8_t* data; // Start of Huffman compressed data + std::vector base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l + std::vector symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256 + Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups + uint64_t groupIdx[TBPIECES+1]; // Start index used for the encoding of the group's pieces + int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1) +}; + +// Helper struct to avoid manually defining entry copy constructor as we +// should because the default one is not compatible with std::atomic_bool. +struct Atomic { + Atomic() = default; + Atomic(const Atomic& e) { ready = e.ready.load(); } // MSVC 2013 wants assignment within body + std::atomic_bool ready; +}; + +// We define types for the different parts of the WLDEntry and DTZEntry with +// corresponding specializations for pieces or pawns. + +struct WLDEntryPiece { + PairsData* precomp; +}; + +struct WDLEntryPawn { + uint8_t pawnCount[2]; // [Lead color / other color] + WLDEntryPiece file[2][4]; // [wtm / btm][FILE_A..FILE_D] +}; + +struct DTZEntryPiece { + PairsData* precomp; + uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss + uint8_t* map; +}; + +struct DTZEntryPawn { + uint8_t pawnCount[2]; + DTZEntryPiece file[4]; + uint8_t* map; +}; + +struct TBEntry : public Atomic { + void* baseAddress; + uint64_t mapping; + Key key; + Key key2; + int pieceCount; + bool hasPawns; + bool hasUniquePieces; +}; + +// Now the main types: WDLEntry and DTZEntry +struct WDLEntry : public TBEntry { + WDLEntry(const std::string& code); + ~WDLEntry(); + union { + WLDEntryPiece pieceTable[2]; // [wtm / btm] + WDLEntryPawn pawnTable; + }; +}; + +struct DTZEntry : public TBEntry { + DTZEntry(const WDLEntry& wdl); + ~DTZEntry(); + union { + DTZEntryPiece pieceTable; + DTZEntryPawn pawnTable; + }; +}; + +typedef decltype(WDLEntry::pieceTable) WDLPieceTable; +typedef decltype(DTZEntry::pieceTable) DTZPieceTable; +typedef decltype(WDLEntry::pawnTable ) WDLPawnTable; +typedef decltype(DTZEntry::pawnTable ) DTZPawnTable; + +auto item(WDLPieceTable& e, int stm, int ) -> decltype(e[stm])& { return e[stm]; } +auto item(DTZPieceTable& e, int , int ) -> decltype(e)& { return e; } +auto item(WDLPawnTable& e, int stm, int f) -> decltype(e.file[stm][f])& { return e.file[stm][f]; } +auto item(DTZPawnTable& e, int , int f) -> decltype(e.file[f])& { return e.file[f]; } + +template struct Ret { typedef int type; }; +template<> struct Ret { typedef WDLScore type; }; + +int MapPawns[SQUARE_NB]; +int MapB1H1H7[SQUARE_NB]; +int MapA1D1D4[SQUARE_NB]; +int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB] + +// Comparison function to sort leading pawns in ascending MapPawns[] order +bool pawns_comp(Square i, Square j) { return MapPawns[i] < MapPawns[j]; } +int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); } + +const Value WDL_to_value[] = { + -VALUE_MATE + MAX_PLY + 1, + VALUE_DRAW - 2, + VALUE_DRAW, + VALUE_DRAW + 2, + VALUE_MATE - MAX_PLY - 1 +}; + +const std::string PieceToChar = " PNBRQK pnbrqk"; + +int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements +int LeadPawnIdx[5][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB] +int LeadPawnsSize[5][4]; // [leadPawnsCnt][FILE_A..FILE_D] + +enum { BigEndian, LittleEndian }; + +template +inline void swap_byte(T& x) +{ + char tmp, *c = (char*)&x; + for (int i = 0; i < Half; ++i) + tmp = c[i], c[i] = c[End - i], c[End - i] = tmp; +} +template<> inline void swap_byte(uint8_t&) {} + +template T number(void* addr) +{ + const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; + const bool IsLittleEndian = (Le.c[0] == 4); + + T v; + + if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) + std::memcpy(&v, addr, sizeof(T)); + else + v = *((T*)addr); + + if (LE != IsLittleEndian) + swap_byte(v); + return v; +} + +class HashTable { + + typedef std::pair EntryPair; + typedef std::pair Entry; + + static const int TBHASHBITS = 10; + static const int HSHMAX = 5; + + Entry hashTable[1 << TBHASHBITS][HSHMAX]; + + std::deque wdlTable; + std::deque dtzTable; + + void insert(Key key, WDLEntry* wdl, DTZEntry* dtz) { + Entry* entry = hashTable[key >> (64 - TBHASHBITS)]; + + for (int i = 0; i < HSHMAX; ++i, ++entry) + if (!entry->second.first || entry->first == key) { + *entry = std::make_pair(key, std::make_pair(wdl, dtz)); + return; + } + + std::cerr << "HSHMAX too low!" << std::endl; + exit(1); + } + +public: + template::value ? 0 : 1> + E* get(Key key) { + Entry* entry = hashTable[key >> (64 - TBHASHBITS)]; + + for (int i = 0; i < HSHMAX; ++i, ++entry) + if (entry->first == key) + return std::get(entry->second); + + return nullptr; + } + + void clear() { + std::memset(hashTable, 0, sizeof(hashTable)); + wdlTable.clear(); + dtzTable.clear(); + } + size_t size() const { return wdlTable.size(); } + void insert(const std::vector& pieces); +}; + +HashTable EntryTable; + +class TBFile : public std::ifstream { + + std::string fname; + +public: + // Look for and open the file among the Paths directories where the .rtbw + // and .rtbz files can be found. Multiple directories are separated by ";" + // on Windows and by ":" on Unix-based operating systems. + // + // Example: + // C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6 + static std::string Paths; + + TBFile(const std::string& f) { + +#ifndef _WIN32 + const char SepChar = ':'; +#else + const char SepChar = ';'; +#endif + std::stringstream ss(Paths); + std::string path; + + while (std::getline(ss, path, SepChar)) { + fname = path + "/" + f; + std::ifstream::open(fname); + if (is_open()) + return; + } + } + + // Memory map the file and check it. File should be already open and will be + // closed after mapping. + uint8_t* map(void** baseAddress, uint64_t* mapping, const uint8_t* TB_MAGIC) { + + assert(is_open()); + + close(); // Need to re-open to get native file descriptor + +#ifndef _WIN32 + struct stat statbuf; + int fd = ::open(fname.c_str(), O_RDONLY); + fstat(fd, &statbuf); + *mapping = statbuf.st_size; + *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + ::close(fd); + + if (*baseAddress == MAP_FAILED) { + std::cerr << "Could not mmap() " << fname << std::endl; + exit(1); + } +#else + HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + DWORD size_high; + DWORD size_low = GetFileSize(fd, &size_high); + HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr); + CloseHandle(fd); + + if (!mmap) { + std::cerr << "CreateFileMapping() failed" << std::endl; + exit(1); + } + + *mapping = (uint64_t)mmap; + *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); + + if (!*baseAddress) { + std::cerr << "MapViewOfFile() failed, name = " << fname + << ", error = " << GetLastError() << std::endl; + exit(1); + } +#endif + uint8_t* data = (uint8_t*)*baseAddress; + + if ( *data++ != *TB_MAGIC++ + || *data++ != *TB_MAGIC++ + || *data++ != *TB_MAGIC++ + || *data++ != *TB_MAGIC) { + std::cerr << "Corrupted table in file " << fname << std::endl; + unmap(*baseAddress, *mapping); + *baseAddress = nullptr; + return nullptr; + } + + return data; + } + + static void unmap(void* baseAddress, uint64_t mapping) { + +#ifndef _WIN32 + munmap(baseAddress, mapping); +#else + UnmapViewOfFile(baseAddress); + CloseHandle((HANDLE)mapping); +#endif + } +}; + +std::string TBFile::Paths; + +WDLEntry::WDLEntry(const std::string& code) { + + StateInfo st; + Position pos; + + memset(this, 0, sizeof(WDLEntry)); + + ready = false; + key = pos.set(code, WHITE, &st).material_key(); + pieceCount = popcount(pos.pieces()); + hasPawns = pos.pieces(PAWN); + + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt < KING; ++pt) + if (popcount(pos.pieces(c, pt)) == 1) + hasUniquePieces = true; + + if (hasPawns) { + // Set the leading color. In case both sides have pawns the leading color + // is the side with less pawns because this leads to better compression. + bool c = !pos.count(BLACK) + || ( pos.count(WHITE) + && pos.count(BLACK) >= pos.count(WHITE)); + + pawnTable.pawnCount[0] = pos.count(c ? WHITE : BLACK); + pawnTable.pawnCount[1] = pos.count(c ? BLACK : WHITE); + } + + key2 = pos.set(code, BLACK, &st).material_key(); +} + +WDLEntry::~WDLEntry() { + + if (baseAddress) + TBFile::unmap(baseAddress, mapping); + + for (int i = 0; i < 2; ++i) + if (hasPawns) + for (File f = FILE_A; f <= FILE_D; ++f) + delete pawnTable.file[i][f].precomp; + else + delete pieceTable[i].precomp; +} + +DTZEntry::DTZEntry(const WDLEntry& wdl) { + + memset(this, 0, sizeof(DTZEntry)); + + ready = false; + key = wdl.key; + key2 = wdl.key2; + pieceCount = wdl.pieceCount; + hasPawns = wdl.hasPawns; + hasUniquePieces = wdl.hasUniquePieces; + + if (hasPawns) { + pawnTable.pawnCount[0] = wdl.pawnTable.pawnCount[0]; + pawnTable.pawnCount[1] = wdl.pawnTable.pawnCount[1]; + } +} + +DTZEntry::~DTZEntry() { + + if (baseAddress) + TBFile::unmap(baseAddress, mapping); + + if (hasPawns) + for (File f = FILE_A; f <= FILE_D; ++f) + delete pawnTable.file[f].precomp; + else + delete pieceTable.precomp; +} + +void HashTable::insert(const std::vector& pieces) { + + std::string code; + + for (PieceType pt : pieces) + code += PieceToChar[pt]; + + TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK + + if (!file.is_open()) + return; + + file.close(); + + MaxCardinality = std::max((int)pieces.size(), MaxCardinality); + + wdlTable.push_back(WDLEntry(code)); + dtzTable.push_back(DTZEntry(wdlTable.back())); + + insert(wdlTable.back().key , &wdlTable.back(), &dtzTable.back()); + insert(wdlTable.back().key2, &wdlTable.back(), &dtzTable.back()); +} + +// TB tables are compressed with canonical Huffman code. The compressed data is divided into +// blocks of size d->sizeofBlock, and each block stores a variable number of symbols. +// Each symbol represents either a WDL or a (remapped) DTZ value, or a pair of other symbols +// (recursively). If you keep expanding the symbols in a block, you end up with up to 65536 +// WDL or DTZ values. Each symbol represents up to 256 values and will correspond after +// Huffman coding to at least 1 bit. So a block of 32 bytes corresponds to at most +// 32 x 8 x 256 = 65536 values. This maximum is only reached for tables that consist mostly +// of draws or mostly of wins, but such tables are actually quite common. In principle, the +// blocks in WDL tables are 64 bytes long (and will be aligned on cache lines). But for +// mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so +// in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long. +// The generator picks the size that leads to the smallest table. The "book" of symbols and +// Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file +// will have one table for wtm and one for btm, a TB file with pawns will have tables per +// file a,b,c,d also in this case one set for wtm and one for btm. +int decompress_pairs(PairsData* d, uint64_t idx) { + + // Special case where all table positions store the same value + if (d->flags & TBFlag::SingleValue) + return d->minSymLen; + + // First we need to locate the right block that stores the value at index "idx". + // Because each block n stores blockLength[n] + 1 values, the index i of the block + // that contains the value at position idx is: + // + // for (i = -1, sum = 0; sum <= idx; i++) + // sum += blockLength[i + 1] + 1; + // + // This can be slow, so we use SparseIndex[] populated with a set of SparseEntry that + // point to known indices into blockLength[]. Namely SparseIndex[k] is a SparseEntry + // that stores the blockLength[] index and the offset within that block of the value + // with index I(k), where: + // + // I(k) = k * d->span + d->span / 2 (1) + + // First step is to get the 'k' of the I(k) nearest to our idx, using defintion (1) + uint32_t k = idx / d->span; + + // Then we read the corresponding SparseIndex[] entry + uint32_t block = number(&d->sparseIndex[k].block); + int offset = number(&d->sparseIndex[k].offset); + + // Now compute the difference idx - I(k). From defintion of k we know that + // + // idx = k * d->span + idx % d->span (2) + // + // So from (1) and (2) we can compute idx - I(K): + int diff = idx % d->span - d->span / 2; + + // Sum the above to offset to find the offset corresponding to our idx + offset += diff; + + // Move to previous/next block, until we reach the correct block that contains idx, + // that is when 0 <= offset <= d->blockLength[block] + while (offset < 0) + offset += d->blockLength[--block] + 1; + + while (offset > d->blockLength[block]) + offset -= d->blockLength[block++] + 1; + + // Finally, we find the start address of our block of canonical Huffman symbols + uint32_t* ptr = (uint32_t*)(d->data + block * d->sizeofBlock); + + // Read the first 64 bits in our block, this is a (truncated) sequence of + // unknown number of symbols of unknown length but we know the first one + // is at the beginning of this 64 bits sequence. + uint64_t buf64 = number(ptr); ptr += 2; + int buf64Size = 64; + Sym sym; + + while (true) { + int len = 0; // This is the symbol length - d->min_sym_len + + // Now get the symbol length. For any symbol s64 of length l right-padded + // to 64 bits we know that d->base64[l-1] >= s64 >= d->base64[l] so we + // can find the symbol length iterating through base64[]. + while (buf64 < d->base64[len]) + ++len; + + // All the symbols of a given length are consecutive integers (numerical + // sequence property), so we can compute the offset of our symbol of + // length len, stored at the beginning of buf64. + sym = (buf64 - d->base64[len]) >> (64 - len - d->minSymLen); + + // Now add the value of the lowest symbol of length len to get our symbol + sym += number(&d->lowestSym[len]); + + // If our offset is within the number of values represented by symbol sym + // we are done... + if (offset < d->symlen[sym] + 1) + break; + + // ...otherwise update the offset and continue to iterate + offset -= d->symlen[sym] + 1; + len += d->minSymLen; // Get the real length + buf64 <<= len; // Consume the just processed symbol + buf64Size -= len; + + if (buf64Size <= 32) { // Refill the buffer + buf64Size += 32; + buf64 |= (uint64_t)number(ptr++) << (64 - buf64Size); + } + } + + // Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols. + // We binary-search for our value recursively expanding into the left and + // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1 + // that will store the value we need. + while (d->symlen[sym]) { + + Sym left = d->btree[sym].get(); + + // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and + // expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then + // we know that, for instance the ten-th value (offset = 10) will be on + // the left side because in Recursive Pairing child symbols are adjacent. + if (offset < d->symlen[left] + 1) + sym = left; + else { + offset -= d->symlen[left] + 1; + sym = d->btree[sym].get(); + } + } + + return d->btree[sym].get(); +} + +bool check_dtz_stm(WDLEntry*, int, File) { return true; } + +bool check_dtz_stm(DTZEntry* entry, int stm, File f) { + + int flags = entry->hasPawns ? entry->pawnTable.file[f].precomp->flags + : entry->pieceTable.precomp->flags; + + return (flags & TBFlag::STM) == stm + || ((entry->key == entry->key2) && !entry->hasPawns); +} + +// DTZ scores are sorted by frequency of occurrence and then assigned the +// values 0, 1, 2, ... in order of decreasing frequency. This is done for each +// of the four WDLScore values. The mapping information necessary to reconstruct +// the original values is stored in the TB file and read during map[] init. +WDLScore map_score(WDLEntry*, File, int value, WDLScore) { return WDLScore(value - 2); } + +int map_score(DTZEntry* entry, File f, int value, WDLScore wdl) { + + const int WDLMap[] = { 1, 3, 0, 2, 0 }; + + int flags = entry->hasPawns ? entry->pawnTable.file[f].precomp->flags + : entry->pieceTable.precomp->flags; + + uint8_t* map = entry->hasPawns ? entry->pawnTable.map + : entry->pieceTable.map; + + uint16_t* idx = entry->hasPawns ? entry->pawnTable.file[f].map_idx + : entry->pieceTable.map_idx; + if (flags & TBFlag::Mapped) + value = map[idx[WDLMap[wdl + 2]] + value]; + + // DTZ tables store distance to zero in number of moves or plies. We + // want to return plies, so we have convert to plies when needed. + if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies)) + || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) + || wdl == WDLCursedWin + || wdl == WDLBlessedLoss) + value *= 2; + + return value + 1; +} + +// Compute a unique index out of a position and use it to probe the TB file. To +// encode k pieces of same type and color, first sort the pieces by square in +// ascending order s1 <= s2 <= ... <= sk then compute the unique index as: +// +// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk] +// +template::type> +T do_probe_table(const Position& pos, Entry* entry, WDLScore wdl, ProbeState* result) { + + const bool IsWDL = std::is_same::value; + + Square squares[TBPIECES]; + Piece pieces[TBPIECES]; + uint64_t idx; + int next = 0, size = 0, leadPawnsCnt = 0; + PairsData* d; + Bitboard b, leadPawns = 0; + File tbFile = FILE_A; + + // A given TB entry like KRK has associated two material keys: KRvk and Kvkr. + // If both sides have the same pieces keys are equal. In this case TB tables + // only store the 'white to move' case, so if the position to lookup has black + // to move, we need to switch the color and flip the squares before to lookup. + bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move()); + + // TB files are calculated for white as stronger side. For instance we have + // KRvK, not KvKR. A position where stronger side is white will have its + // material key == entry->key, otherwise we have to switch the color and + // flip the squares before to lookup. + bool blackStronger = (pos.material_key() != entry->key); + + int flipColor = (symmetricBlackToMove || blackStronger) * 8; + int flipSquares = (symmetricBlackToMove || blackStronger) * 070; + int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move(); + + // For pawns, TB files store 4 separate tables according if leading pawn is on + // file a, b, c or d after reordering. The leading pawn is the one with maximum + // MapPawns[] value, that is the one most toward the edges and with lowest rank. + if (entry->hasPawns) { + + // In all the 4 tables, pawns are at the beginning of the piece sequence and + // their color is the reference one. So we just pick the first one. + Piece pc = Piece(item(entry->pawnTable, 0, 0).precomp->pieces[0] ^ flipColor); + + assert(type_of(pc) == PAWN); + + leadPawns = b = pos.pieces(color_of(pc), PAWN); + while (b) + squares[size++] = pop_lsb(&b) ^ flipSquares; + + leadPawnsCnt = size; + + std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); + + tbFile = file_of(squares[0]); + if (tbFile > FILE_D) + tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1 + + d = item(entry->pawnTable , stm, tbFile).precomp; + } else + d = item(entry->pieceTable, stm, tbFile).precomp; + + // DTZ tables are one-sided, i.e. they store positions only for white to + // move or only for black to move, so check for side to move to be stm, + // early exit otherwise. + if (!IsWDL && !check_dtz_stm(entry, stm, tbFile)) + return *result = CHANGE_STM, T(); + + // Now we are ready to get all the position pieces (but the lead pawns) and + // directly map them to the correct color and square. + b = pos.pieces() ^ leadPawns; + while (b) { + Square s = pop_lsb(&b); + squares[size] = s ^ flipSquares; + pieces[size++] = Piece(pos.piece_on(s) ^ flipColor); + } + + // Then we reorder the pieces to have the same sequence as the one stored + // in precomp->pieces[i]: the sequence that ensures the best compression. + for (int i = leadPawnsCnt; i < size; ++i) + for (int j = i; j < size; ++j) + if (d->pieces[i] == pieces[j]) + { + std::swap(pieces[i], pieces[j]); + std::swap(squares[i], squares[j]); + break; + } + + // Now we map again the squares so that the square of the lead piece is in + // the triangle A1-D1-D4. + if (file_of(squares[0]) > FILE_D) + for (int i = 0; i < size; ++i) + squares[i] ^= 7; // Horizontal flip: SQ_H1 -> SQ_A1 + + // Encode leading pawns starting with the one with minimum MapPawns[] and + // proceeding in ascending order. + if (entry->hasPawns) { + idx = LeadPawnIdx[leadPawnsCnt][squares[0]]; + + std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp); + + for (int i = 1; i < leadPawnsCnt; ++i) + idx += Binomial[i][MapPawns[squares[i]]]; + + goto encode_remaining; // With pawns we have finished special treatments + } + + // In positions withouth pawns, we further flip the squares to ensure leading + // piece is below RANK_5. + if (rank_of(squares[0]) > RANK_4) + for (int i = 0; i < size; ++i) + squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1 + + // Look for the first piece of the leading group not on the A1-D4 diagonal + // and ensure it is mapped below the diagonal. + for (int i = 0; i < d->groupLen[0]; ++i) { + if (!off_A1H8(squares[i])) + continue; + + if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C3 + for (int j = i; j < size; ++j) + squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63); + break; + } + + // Encode the leading group. + // + // Suppose we have KRvK. Let's say the pieces are on square numbers wK, wR + // and bK (each 0...63). The simplest way to map this position to an index + // is like this: + // + // index = wK * 64 * 64 + wR * 64 + bK; + // + // But this way the TB is going to have 64*64*64 = 262144 positions, with + // lots of positions being equivalent (because they are mirrors of each + // other) and lots of positions being invalid (two pieces on one square, + // adjacent kings, etc.). + // Usually the first step is to take the wK and bK together. There are just + // 462 ways legal and not-mirrored ways to place the wK and bK on the board. + // Once we have placed the wK and bK, there are 62 squares left for the wR + // Mapping its square from 0..63 to available squares 0..61 can be done like: + // + // wR -= (wR > wK) + (wR > bK); + // + // In words: if wR "comes later" than wK, we deduct 1, and the same if wR + // "comes later" than bK. In case of two same pieces like KRRvK we want to + // place the two Rs "together". If we have 62 squares left, we can place two + // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be + // swapped and still get the same position.) + // + // In case we have at least 3 unique pieces (inlcuded kings) we encode them + // together. + if (entry->hasUniquePieces) { + + int adjust1 = squares[1] > squares[0]; + int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]); + + // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3 + // triangle to 0...5. There are 63 squares for second piece and and 62 + // (mapped to 0...61) for the third. + if (off_A1H8(squares[0])) + idx = ( MapA1D1D4[squares[0]] * 63 + + (squares[1] - adjust1)) * 62 + + squares[2] - adjust2; + + // First piece is on a1-h8 diagonal, second below: map this occurence to + // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal + // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27. + else if (off_A1H8(squares[1])) + idx = ( 6 * 63 + rank_of(squares[0]) * 28 + + MapB1H1H7[squares[1]]) * 62 + + squares[2] - adjust2; + + // First two pieces are on a1-h8 diagonal, third below + else if (off_A1H8(squares[2])) + idx = 6 * 63 * 62 + 4 * 28 * 62 + + rank_of(squares[0]) * 7 * 28 + + (rank_of(squares[1]) - adjust1) * 28 + + MapB1H1H7[squares[2]]; + + // All 3 pieces on the diagonal a1-h8 + else + idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28 + + rank_of(squares[0]) * 7 * 6 + + (rank_of(squares[1]) - adjust1) * 6 + + (rank_of(squares[2]) - adjust2); + } else + // We don't have at least 3 unique pieces, like in KRRvKBB, just map + // the kings. + idx = MapKK[MapA1D1D4[squares[0]]][squares[1]]; + +encode_remaining: + idx *= d->groupIdx[0]; + Square* groupSq = squares + d->groupLen[0]; + + // Encode remainig pawns then pieces according to square, in ascending order + bool remainingPawns = entry->hasPawns && entry->pawnTable.pawnCount[1]; + + while (d->groupLen[++next]) + { + std::sort(groupSq, groupSq + d->groupLen[next]); + uint64_t n = 0; + + // Map down a square if "comes later" than a square in the previous + // groups (similar to what done earlier for leading group pieces). + for (int i = 0; i < d->groupLen[next]; ++i) + { + auto f = [&](Square s) { return groupSq[i] > s; }; + auto adjust = std::count_if(squares, groupSq, f); + n += Binomial[i + 1][groupSq[i] - adjust - 8 * remainingPawns]; + } + + remainingPawns = false; + idx += n * d->groupIdx[next]; + groupSq += d->groupLen[next]; + } + + // Now that we have the index, decompress the pair and get the score + return map_score(entry, tbFile, decompress_pairs(d, idx), wdl); +} + +// Group together pieces that will be encoded together. The general rule is that +// a group contains pieces of same type and color. The exception is the leading +// group that, in case of positions withouth pawns, can be formed by 3 different +// pieces (default) or by the king pair when there is not a unique piece apart +// from the kings. When there are pawns, pawns are always first in pieces[]. +// +// As example KRKN -> KRK + N, KNNK -> KK + NN, KPPKP -> P + PP + K + K +// +// The actual grouping depends on the TB generator and can be inferred from the +// sequence of pieces in piece[] array. +template +void set_groups(T& e, PairsData* d, int order[], File f) { + + int n = 0, firstLen = e.hasPawns ? 0 : e.hasUniquePieces ? 3 : 2; + d->groupLen[n] = 1; + + // Number of pieces per group is stored in groupLen[], for instance in KRKN + // the encoder will default on '111', so groupLen[] will be (3, 1). + for (int i = 1; i < e.pieceCount; ++i) + if (--firstLen > 0 || d->pieces[i] == d->pieces[i - 1]) + d->groupLen[n]++; + else + d->groupLen[++n] = 1; + + d->groupLen[++n] = 0; // Zero-terminated + + // The sequence in pieces[] defines the groups, but not the order in which + // they are encoded. If the pieces in a group g can be combined on the board + // in N(g) different ways, then the position encoding will be of the form: + // + // g1 * N(g2) * N(g3) + g2 * N(g3) + g3 + // + // This ensures unique encoding for the whole position. The order of the + // groups is a per-table parameter and could not follow the canonical leading + // pawns/pieces -> remainig pawns -> remaining pieces. In particular the + // first group is at order[0] position and the remaining pawns, when present, + // are at order[1] position. + bool pp = e.hasPawns && e.pawnTable.pawnCount[1]; // Pawns on both sides + int next = pp ? 2 : 1; + int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0); + uint64_t idx = 1; + + for (int k = 0; next < n || k == order[0] || k == order[1]; ++k) + if (k == order[0]) // Leading pawns or pieces + { + d->groupIdx[0] = idx; + idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f] + : e.hasUniquePieces ? 31332 : 462; + } + else if (k == order[1]) // Remaining pawns + { + d->groupIdx[1] = idx; + idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]]; + } + else // Remainig pieces + { + d->groupIdx[next] = idx; + idx *= Binomial[d->groupLen[next]][freeSquares]; + freeSquares -= d->groupLen[next++]; + } + + d->groupIdx[n] = idx; +} + +// In Recursive Pairing each symbol represents a pair of childern symbols. So +// read d->btree[] symbols data and expand each one in his left and right child +// symbol until reaching the leafs that represent the symbol value. +uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { + + visited[s] = true; // We can set it now because tree is acyclic + Sym sr = d->btree[s].get(); + + if (sr == 0xFFF) + return 0; + + Sym sl = d->btree[s].get(); + + if (!visited[sl]) + d->symlen[sl] = set_symlen(d, sl, visited); + + if (!visited[sr]) + d->symlen[sr] = set_symlen(d, sr, visited); + + return d->symlen[sl] + d->symlen[sr] + 1; +} + +uint8_t* set_sizes(PairsData* d, uint8_t* data) { + + d->flags = *data++; + + if (d->flags & TBFlag::SingleValue) { + d->blocksNum = d->span = + d->blockLengthSize = d->sparseIndexSize = 0; // Broken MSVC zero-init + d->minSymLen = *data++; // Here we store the single value + return data; + } + + // groupLen[] is a zero-terminated list of group lengths, the last groupIdx[] + // element stores the biggest index that is the tb size. + uint64_t tbSize = d->groupIdx[std::find(d->groupLen, d->groupLen + 7, 0) - d->groupLen]; + + d->sizeofBlock = 1ULL << *data++; + d->span = 1ULL << *data++; + d->sparseIndexSize = (tbSize + d->span - 1) / d->span; // Round up + int padding = number(data++); + d->blocksNum = number(data); data += sizeof(uint32_t); + d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] + // does not point out of range. + d->maxSymLen = *data++; + d->minSymLen = *data++; + d->lowestSym = (Sym*)data; + d->base64.resize(d->maxSymLen - d->minSymLen + 1); + + // The canonical code is ordered such that longer symbols (in terms of + // the number of bits of their Huffman code) have lower numeric value, + // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). + // Starting from this we compute a base64[] table indexed by symbol length + // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. + // See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf + for (int i = d->base64.size() - 2; i >= 0; --i) { + d->base64[i] = (d->base64[i + 1] + number(&d->lowestSym[i]) + - number(&d->lowestSym[i + 1])) / 2; + + assert(d->base64[i] * 2 >= d->base64[i+1]); + } + + // Now left-shift by an amount so that d->base64[i] gets shifted 1 bit more + // than d->base64[i+1] and given the above assert condition, we ensure that + // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i + // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i]. + for (size_t i = 0; i < d->base64.size(); ++i) + d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits + + data += d->base64.size() * sizeof(Sym); + d->symlen.resize(number(data)); data += sizeof(uint16_t); + d->btree = (LR*)data; + + // The comrpession scheme used is "Recursive Pairing", that replaces the most + // frequent adjacent pair of symbols in the source message by a new symbol, + // reevaluating the frequencies of all of the symbol pairs with respect to + // the extended alphabet, and then repeating the process. + // See http://www.larsson.dogma.net/dcc99.pdf + std::vector visited(d->symlen.size()); + + for (Sym sym = 0; sym < d->symlen.size(); ++sym) + if (!visited[sym]) + d->symlen[sym] = set_symlen(d, sym, visited); + + return data + d->symlen.size() * sizeof(LR) + (d->symlen.size() & 1); +} + +template +uint8_t* set_dtz_map(WDLEntry&, T&, uint8_t*, File) { return nullptr; } + +template +uint8_t* set_dtz_map(DTZEntry&, T& p, uint8_t* data, File maxFile) { + + p.map = data; + + for (File f = FILE_A; f <= maxFile; ++f) { + if (item(p, 0, f).precomp->flags & TBFlag::Mapped) + for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x + item(p, 0, f).map_idx[i] = (uint16_t)(data - p.map + 1); + data += *data + 1; + } + } + + return data += (uintptr_t)data & 1; // Word alignment +} + +template +void do_init(Entry& e, T& p, uint8_t* data) { + + const bool IsWDL = std::is_same::value; + + PairsData* d; + + enum { Split = 1, HasPawns = 2 }; + + assert(e.hasPawns == !!(*data & HasPawns)); + assert((e.key != e.key2) == !!(*data & Split)); + + data++; // First byte stores flags + + const int Sides = IsWDL && (e.key != e.key2) ? 2 : 1; + const File MaxFile = e.hasPawns ? FILE_D : FILE_A; + + bool pp = e.hasPawns && e.pawnTable.pawnCount[1]; // Pawns on both sides + + assert(!pp || e.pawnTable.pawnCount[0]); + + for (File f = FILE_A; f <= MaxFile; ++f) { + + for (int i = 0; i < Sides; i++) + item(p, i, f).precomp = new PairsData(); + + int order[][2] = { { *data & 0xF, pp ? *(data + 1) & 0xF : 0xF }, + { *data >> 4, pp ? *(data + 1) >> 4 : 0xF } }; + data += 1 + pp; + + for (int k = 0; k < e.pieceCount; ++k, ++data) + for (int i = 0; i < Sides; i++) + item(p, i, f).precomp->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF); + + for (int i = 0; i < Sides; ++i) + set_groups(e, item(p, i, f).precomp, order[i], f); + } + + data += (uintptr_t)data & 1; // Word alignment + + for (File f = FILE_A; f <= MaxFile; ++f) + for (int i = 0; i < Sides; i++) + data = set_sizes(item(p, i, f).precomp, data); + + if (!IsWDL) + data = set_dtz_map(e, p, data, MaxFile); + + for (File f = FILE_A; f <= MaxFile; ++f) + for (int i = 0; i < Sides; i++) { + (d = item(p, i, f).precomp)->sparseIndex = (SparseEntry*)data; + data += d->sparseIndexSize * sizeof(SparseEntry) ; + } + + for (File f = FILE_A; f <= MaxFile; ++f) + for (int i = 0; i < Sides; i++) { + (d = item(p, i, f).precomp)->blockLength = (uint16_t*)data; + data += d->blockLengthSize * sizeof(uint16_t); + } + + for (File f = FILE_A; f <= MaxFile; ++f) + for (int i = 0; i < Sides; i++) { + data = (uint8_t*)(((uintptr_t)data + 0x3F) & ~0x3F); // 64 byte alignment + (d = item(p, i, f).precomp)->data = data; + data += d->blocksNum * d->sizeofBlock; + } +} + +template +void* init(Entry& e, const Position& pos) { + + const bool IsWDL = std::is_same::value; + + static Mutex mutex; + + // Avoid a thread reads 'ready' == true while another is still in do_init(), + // this could happen due to compiler reordering. + if (e.ready.load(std::memory_order_acquire)) + return e.baseAddress; + + std::unique_lock lk(mutex); + + if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock + return e.baseAddress; + + // Pieces strings in decreasing order for each color, like ("KPP","KR") + std::string fname, w, b; + for (PieceType pt = KING; pt >= PAWN; --pt) { + w += std::string(popcount(pos.pieces(WHITE, pt)), PieceToChar[pt]); + b += std::string(popcount(pos.pieces(BLACK, pt)), PieceToChar[pt]); + } + + const uint8_t TB_MAGIC[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 }, + { 0x71, 0xE8, 0x23, 0x5D } }; + + fname = (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w) + + (IsWDL ? ".rtbw" : ".rtbz"); + + uint8_t* data = TBFile(fname).map(&e.baseAddress, &e.mapping, TB_MAGIC[IsWDL]); + if (data) + e.hasPawns ? do_init(e, e.pawnTable, data) : do_init(e, e.pieceTable, data); + + e.ready.store(true, std::memory_order_release); + return e.baseAddress; +} + +template::type> +T probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) { + + if (!(pos.pieces() ^ pos.pieces(KING))) + return T(WDLDraw); // KvK + + E* entry = EntryTable.get(pos.material_key()); + + if (!entry || !init(*entry, pos)) + return *result = FAIL, T(); + + return do_probe_table(pos, entry, wdl, result); +} + +// For a position where the side to move has a winning capture it is not necessary +// to store a winning value so the generator treats such positions as "don't cares" +// and tries to assign to it a value that improves the compression ratio. Similarly, +// if the side to move has a drawing capture, then the position is at least drawn. +// If the position is won, then the TB needs to store a win value. But if the +// position is drawn, the TB may store a loss value if that is better for compression. +// All of this means that during probing, the engine must look at captures and probe +// their results and must probe the position itself. The "best" result of these +// probes is the correct result for the position. +// DTZ table don't store values when a following move is a zeroing winning move +// (winning capture or winning pawn move). Also DTZ store wrong values for positions +// where the best move is an ep-move (even if losing). So in all these cases set +// the state to ZEROING_BEST_MOVE. +template +WDLScore search(Position& pos, ProbeState* result) { + + WDLScore value, bestValue = WDLLoss; + StateInfo st; + + auto moveList = MoveList(pos); + size_t totalCount = moveList.size(), moveCount = 0; + + for (const Move& move : moveList) + { + if ( !pos.capture(move) + && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) + continue; + + moveCount++; + + pos.do_move(move, st); + value = -search(pos, result); + pos.undo_move(move); + + if (*result == FAIL) + return WDLDraw; + + if (value > bestValue) + { + bestValue = value; + + if (value >= WDLWin) + { + *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move + return value; + } + } + } + + // In case we have already searched all the legal moves we don't have to probe + // the TB because the stored score could be wrong. For instance TB tables + // do not contain information on position with ep rights, so in this case + // the result of probe_wdl_table is wrong. Also in case of only capture + // moves, for instance here 4K3/4q3/6p1/2k5/6p1/8/8/8 w - - 0 7, we have to + // return with ZEROING_BEST_MOVE set. + bool noMoreMoves = (moveCount && moveCount == totalCount); + + if (noMoreMoves) + value = bestValue; + else + { + value = probe_table(pos, result); + + if (*result == FAIL) + return WDLDraw; + } + + // DTZ stores a "don't care" value if bestValue is a win + if (bestValue >= value) + return *result = ( bestValue > WDLDraw + || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue; + + return *result = OK, value; +} + +} // namespace + +void Tablebases::init(const std::string& paths) { + + EntryTable.clear(); + MaxCardinality = 0; + TBFile::Paths = paths; + + if (paths.empty() || paths == "") + return; + + // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27 + int code = 0; + for (Square s = SQ_A1; s <= SQ_H8; ++s) + if (off_A1H8(s) < 0) + MapB1H1H7[s] = code++; + + // MapA1D1D4[] encodes a square in the a1-d1-d4 triangle to 0..9 + std::vector diagonal; + code = 0; + for (Square s = SQ_A1; s <= SQ_D4; ++s) + if (off_A1H8(s) < 0 && file_of(s) <= FILE_D) + MapA1D1D4[s] = code++; + + else if (!off_A1H8(s) && file_of(s) <= FILE_D) + diagonal.push_back(s); + + // Diagonal squares are encoded as last ones + for (auto s : diagonal) + MapA1D1D4[s] = code++; + + // MapKK[] encodes all the 461 possible legal positions of two kings where + // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4 + // diagonal, the other one shall not to be above the a1-h8 diagonal. + std::vector> bothOnDiagonal; + code = 0; + for (int idx = 0; idx < 10; idx++) + for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1) + if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 + { + for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) + if ((StepAttacksBB[KING][s1] | s1) & s2) + continue; // Illegal position + + else if (!off_A1H8(s1) && off_A1H8(s2) > 0) + continue; // First on diagonal, second above + + else if (!off_A1H8(s1) && !off_A1H8(s2)) + bothOnDiagonal.push_back(std::make_pair(idx, s2)); + + else + MapKK[idx][s2] = code++; + } + + // Legal positions with both kings on diagonal are encoded as last ones + for (auto p : bothOnDiagonal) + MapKK[p.first][p.second] = code++; + + // Binomial[] stores the Binomial Coefficents using Pascal rule. There + // are Binomial[k][n] ways to choose k elements from a set of n elements. + Binomial[0][0] = 1; + + for (int n = 1; n < 64; n++) // Squares + for (int k = 0; k < 6 && k <= n; ++k) // Pieces + Binomial[k][n] = (k > 0 ? Binomial[k - 1][n - 1] : 0) + + (k < n ? Binomial[k ][n - 1] : 0); + + // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible + // available squares when the leading one is in 's'. Moreover the pawn with + // highest MapPawns[] is the leading pawn, the one nearest the edge and, + // among pawns with same file, the one with lowest rank. + int availableSquares = 47; // Available squares when lead pawn is in a2 + + // Init the tables for the encoding of leading pawns group: with 6-men TB we + // can have up to 4 leading pawns (KPPPPK). + for (int leadPawnsCnt = 1; leadPawnsCnt <= 4; ++leadPawnsCnt) + for (File f = FILE_A; f <= FILE_D; ++f) + { + // Restart the index at every file because TB table is splitted + // by file, so we can reuse the same index for different files. + int idx = 0; + + // Sum all possible combinations for a given file, starting with + // the leading pawn on rank 2 and increasing the rank. + for (Rank r = RANK_2; r <= RANK_7; ++r) + { + Square sq = make_square(f, r); + + // Compute MapPawns[] at first pass. + // If sq is the leading pawn square, any other pawn cannot be + // below or more toward the edge of sq. There are 47 available + // squares when sq = a2 and reduced by 2 for any rank increase + // due to mirroring: sq == a3 -> no a2, h2, so MapPawns[a3] = 45 + if (leadPawnsCnt == 1) + { + MapPawns[sq] = availableSquares--; + MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip + } + LeadPawnIdx[leadPawnsCnt][sq] = idx; + idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]]; + } + // After a file is traversed, store the cumulated per-file index + LeadPawnsSize[leadPawnsCnt][f] = idx; + } + + for (PieceType p1 = PAWN; p1 < KING; ++p1) { + EntryTable.insert({KING, p1, KING}); + + for (PieceType p2 = PAWN; p2 <= p1; ++p2) { + EntryTable.insert({KING, p1, p2, KING}); + EntryTable.insert({KING, p1, KING, p2}); + + for (PieceType p3 = PAWN; p3 < KING; ++p3) + EntryTable.insert({KING, p1, p2, KING, p3}); + + for (PieceType p3 = PAWN; p3 <= p2; ++p3) { + EntryTable.insert({KING, p1, p2, p3, KING}); + + for (PieceType p4 = PAWN; p4 <= p3; ++p4) + EntryTable.insert({KING, p1, p2, p3, p4, KING}); + + for (PieceType p4 = PAWN; p4 < KING; ++p4) + EntryTable.insert({KING, p1, p2, p3, KING, p4}); + } + + for (PieceType p3 = PAWN; p3 <= p1; ++p3) + for (PieceType p4 = PAWN; p4 <= (p1 == p3 ? p2 : p3); ++p4) + EntryTable.insert({KING, p1, p2, KING, p3, p4}); + } + } + + sync_cout << "info string Found " << EntryTable.size() << " tablebases" << sync_endl; +} + +// Probe the WDL table for a particular position. +// If *result != FAIL, the probe was successful. +// The return value is from the point of view of the side to move: +// -2 : loss +// -1 : loss, but draw under 50-move rule +// 0 : draw +// 1 : win, but draw under 50-move rule +// 2 : win +WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) { + + *result = OK; + return search(pos, result); +} + +// Probe the DTZ table for a particular position. +// If *result != FAIL, the probe was successful. +// The return value is from the point of view of the side to move: +// n < -100 : loss, but draw under 50-move rule +// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) +// 0 : draw +// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) +// 100 < n : win, but draw under 50-move rule +// +// The return value n can be off by 1: a return value -n can mean a loss +// in n+1 ply and a return value +n can mean a win in n+1 ply. This +// cannot happen for tables with positions exactly on the "edge" of +// the 50-move rule. +// +// This implies that if dtz > 0 is returned, the position is certainly +// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine +// picks moves that preserve dtz + 50-move-counter <= 99. +// +// If n = 100 immediately after a capture or pawn move, then the position +// is also certainly a win, and during the whole phase until the next +// capture or pawn move, the inequality to be preserved is +// dtz + 50-movecounter <= 100. +// +// In short, if a move is available resulting in dtz + 50-move-counter <= 99, +// then do not accept moves leading to dtz + 50-move-counter == 100. +int Tablebases::probe_dtz(Position& pos, ProbeState* result) { + + *result = OK; + WDLScore wdl = search(pos, result); + + if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws + return 0; + + // DTZ stores a 'don't care' value in this case, or even a plain wrong + // one as in case the best move is a losing ep, so it cannot be probed. + if (*result == ZEROING_BEST_MOVE) + return dtz_before_zeroing(wdl); + + int dtz = probe_table(pos, result, wdl); + + if (*result == FAIL) + return 0; + + if (*result != CHANGE_STM) + return (dtz + 100 * (wdl == WDLBlessedLoss || wdl == WDLCursedWin)) * sign_of(wdl); + + // DTZ stores results for the other side, so we need to do a 1-ply search and + // find the winning move that minimizes DTZ. + StateInfo st; + int minDTZ = 0xFFFF; + + for (const Move& move : MoveList(pos)) + { + bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; + + pos.do_move(move, st); + + // For zeroing moves we want the dtz of the move _before_ doing it, + // otherwise we will get the dtz of the next move sequence. Search the + // position after the move to get the score sign (because even in a + // winning position we could make a losing capture or going for a draw). + dtz = zeroing ? -dtz_before_zeroing(search(pos, result)) + : -probe_dtz(pos, result); + + pos.undo_move(move); + + if (*result == FAIL) + return 0; + + // Convert result from 1-ply search. Zeroing moves are already accounted + // by dtz_before_zeroing() that returns the DTZ of the previous move. + if (!zeroing) + dtz += sign_of(dtz); + + // Skip the draws and if we are winning only pick positive dtz + if (dtz < minDTZ && sign_of(dtz) == sign_of(wdl)) + minDTZ = dtz; + } + + // Special handle a mate position, when there are no legal moves, in this + // case return value is somewhat arbitrary, so stick to the original TB code + // that returns -1 in this case. + return minDTZ == 0xFFFF ? -1 : minDTZ; +} + +// Check whether there has been at least one repetition of positions +// since the last capture or pawn move. +static int has_repeated(StateInfo *st) +{ + while (1) { + int i = 4, e = std::min(st->rule50, st->pliesFromNull); + + if (e < i) + return 0; + + StateInfo *stp = st->previous->previous; + + do { + stp = stp->previous->previous; + + if (stp->key == st->key) + return 1; + + i += 2; + } while (i <= e); + + st = st->previous; + } +} + +// Use the DTZ tables to filter out moves that don't preserve the win or draw. +// If the position is lost, but DTZ is fairly high, only keep moves that +// maximise DTZ. +// +// A return value false indicates that not all probes were successful and that +// no moves were filtered out. +bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score) +{ + ProbeState result; + int dtz = probe_dtz(pos, &result); + + if (result == FAIL) + return false; + + StateInfo st; + + // Probe each move + for (size_t i = 0; i < rootMoves.size(); ++i) { + Move move = rootMoves[i].pv[0]; + pos.do_move(move, st); + int v = 0; + + if (pos.checkers() && dtz > 0) { + ExtMove s[MAX_MOVES]; + + if (generate(pos, s) == s) + v = 1; + } + + if (!v) { + if (st.rule50 != 0) { + v = -probe_dtz(pos, &result); + + if (v > 0) + ++v; + else if (v < 0) + --v; + } else { + v = -probe_wdl(pos, &result); + v = dtz_before_zeroing(WDLScore(v)); + } + } + + pos.undo_move(move); + + if (result == FAIL) + return false; + + rootMoves[i].score = (Value)v; + } + + // Obtain 50-move counter for the root position. + // In Stockfish there seems to be no clean way, so we do it like this: + int cnt50 = st.previous->rule50; + + // Use 50-move counter to determine whether the root position is + // won, lost or drawn. + WDLScore wdl = WDLDraw; + + if (dtz > 0) + wdl = (dtz + cnt50 <= 100) ? WDLWin : WDLCursedWin; + else if (dtz < 0) + wdl = (-dtz + cnt50 <= 100) ? WDLLoss : WDLBlessedLoss; + + // Determine the score to report to the user. + score = WDL_to_value[wdl + 2]; + + // If the position is winning or losing, but too few moves left, adjust the + // score to show how close it is to winning or losing. + // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci(). + if (wdl == WDLCursedWin && dtz <= 100) + score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); + else if (wdl == WDLBlessedLoss && dtz >= -100) + score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200); + + // Now be a bit smart about filtering out moves. + size_t j = 0; + + if (dtz > 0) { // winning (or 50-move rule draw) + int best = 0xffff; + + for (size_t i = 0; i < rootMoves.size(); ++i) { + int v = rootMoves[i].score; + + if (v > 0 && v < best) + best = v; + } + + int max = best; + + // If the current phase has not seen repetitions, then try all moves + // that stay safely within the 50-move budget, if there are any. + if (!has_repeated(st.previous) && best + cnt50 <= 99) + max = 99 - cnt50; + + for (size_t i = 0; i < rootMoves.size(); ++i) { + int v = rootMoves[i].score; + + if (v > 0 && v <= max) + rootMoves[j++] = rootMoves[i]; + } + } else if (dtz < 0) { // losing (or 50-move rule draw) + int best = 0; + + for (size_t i = 0; i < rootMoves.size(); ++i) { + int v = rootMoves[i].score; + + if (v < best) + best = v; + } + + // Try all moves, unless we approach or have a 50-move rule draw. + if (-best * 2 + cnt50 < 100) + return true; + + for (size_t i = 0; i < rootMoves.size(); ++i) { + if (rootMoves[i].score == best) + rootMoves[j++] = rootMoves[i]; + } + } else { // drawing + // Try all moves that preserve the draw. + for (size_t i = 0; i < rootMoves.size(); ++i) { + if (rootMoves[i].score == 0) + rootMoves[j++] = rootMoves[i]; + } + } + + rootMoves.resize(j, Search::RootMove(MOVE_NONE)); + + return true; +} + +// Use the WDL tables to filter out moves that don't preserve the win or draw. +// This is a fallback for the case that some or all DTZ tables are missing. +// +// A return value false indicates that not all probes were successful and that +// no moves were filtered out. +bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score) +{ + ProbeState result; + + WDLScore wdl = Tablebases::probe_wdl(pos, &result); + + if (result == FAIL) + return false; + + score = WDL_to_value[wdl + 2]; + + StateInfo st; + + int best = WDLLoss; + + // Probe each move + for (size_t i = 0; i < rootMoves.size(); ++i) { + Move move = rootMoves[i].pv[0]; + pos.do_move(move, st); + WDLScore v = -Tablebases::probe_wdl(pos, &result); + pos.undo_move(move); + + if (result == FAIL) + return false; + + rootMoves[i].score = (Value)v; + + if (v > best) + best = v; + } + + size_t j = 0; + + for (size_t i = 0; i < rootMoves.size(); ++i) { + if (rootMoves[i].score == best) + rootMoves[j++] = rootMoves[i]; + } + + rootMoves.resize(j, Search::RootMove(MOVE_NONE)); + + return true; +} diff --git a/Engines/Windows/mcbrain/src/tbprobe.h b/Engines/Windows/mcbrain/src/tbprobe.h new file mode 100644 index 0000000..0c16662 --- /dev/null +++ b/Engines/Windows/mcbrain/src/tbprobe.h @@ -0,0 +1,79 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (c) 2013 Ronald de Man + Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish 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, see . +*/ + +#ifndef TBPROBE_H +#define TBPROBE_H + +#include + +#include "search.h" + +namespace Tablebases { + +enum WDLScore { + WDLLoss = -2, // Loss + WDLBlessedLoss = -1, // Loss, but draw under 50-move rule + WDLDraw = 0, // Draw + WDLCursedWin = 1, // Win, but draw under 50-move rule + WDLWin = 2, // Win + + WDLScoreNone = -1000 +}; + +// Possible states after a probing operation +enum ProbeState { + FAIL = 0, // Probe failed (missing file table) + OK = 1, // Probe succesful + CHANGE_STM = -1, // DTZ should check the other side + ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) +}; + +extern int MaxCardinality; + +void init(const std::string& paths); +WDLScore probe_wdl(Position& pos, ProbeState* result); +int probe_dtz(Position& pos, ProbeState* result); +bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); +bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score); +void filter_root_moves(Position& pos, Search::RootMoves& rootMoves); + +inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { + + os << (v == WDLLoss ? "Loss" : + v == WDLBlessedLoss ? "Blessed loss" : + v == WDLDraw ? "Draw" : + v == WDLCursedWin ? "Cursed win" : + v == WDLWin ? "Win" : "None"); + + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { + + os << (v == FAIL ? "Failed" : + v == OK ? "Success" : + v == CHANGE_STM ? "Probed opponent side" : + v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None"); + + return os; +} + +} + +#endif diff --git a/Engines/Windows/deepfish/srcD/thread.cpp b/Engines/Windows/mcbrain/src/thread.cpp similarity index 89% rename from Engines/Windows/deepfish/srcD/thread.cpp rename to Engines/Windows/mcbrain/src/thread.cpp index b38bdd6..08059b1 100644 --- a/Engines/Windows/deepfish/srcD/thread.cpp +++ b/Engines/Windows/mcbrain/src/thread.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ #include "search.h" #include "thread.h" #include "uci.h" -#include "syzygy/tbprobe.h" +#include "tbprobe.h" ThreadPool Threads; // Global object @@ -36,8 +36,7 @@ Thread::Thread() { resetCalls = exit = false; maxPly = callsCnt = 0; - history.clear(); - counterMoves.clear(); + tbHits = 0; idx = Threads.size(); // Start from 0 std::unique_lock lk(mutex); @@ -95,6 +94,8 @@ void Thread::start_searching(bool resume) { void Thread::idle_loop() { + WinProcGroup::bindThisThread(idx); + while (!exit) { std::unique_lock lk(mutex); @@ -122,7 +123,7 @@ void Thread::idle_loop() { void ThreadPool::init() { - push_back(new MainThread); + push_back(new MainThread()); read_uci_options(); } @@ -149,7 +150,7 @@ void ThreadPool::read_uci_options() { assert(requested > 0); while (size() < requested) - push_back(new Thread); + push_back(new Thread()); while (size() > requested) delete back(), pop_back(); @@ -158,15 +159,26 @@ void ThreadPool::read_uci_options() { /// ThreadPool::nodes_searched() returns the number of nodes searched -int64_t ThreadPool::nodes_searched() { +uint64_t ThreadPool::nodes_searched() const { - int64_t nodes = 0; + uint64_t nodes = 0; for (Thread* th : *this) nodes += th->rootPos.nodes_searched(); return nodes; } +/// ThreadPool::tb_hits() returns the number of TB hits + +uint64_t ThreadPool::tb_hits() const { + + uint64_t hits = 0; + for (Thread* th : *this) + hits += th->tbHits; + return hits; +} + + /// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop() /// and starts a new search, then returns immediately. @@ -184,7 +196,8 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) rootMoves.push_back(Search::RootMove(m)); - Tablebases::filter_root_moves(pos, rootMoves); + if (!rootMoves.empty()) + Tablebases::filter_root_moves(pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search // and call 'go' again without setting a new position states.get() == NULL. @@ -198,8 +211,11 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : Threads) { th->maxPly = 0; + th->tbHits = 0; th->rootDepth = DEPTH_ZERO; th->rootMoves = rootMoves; + th->nmp_ply = 0; + th->pair = -1; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } diff --git a/Engines/Windows/deepfish/srcD/thread.h b/Engines/Windows/mcbrain/src/thread.h similarity index 91% rename from Engines/Windows/deepfish/srcD/thread.h rename to Engines/Windows/mcbrain/src/thread.h index 8181163..e399e20 100644 --- a/Engines/Windows/deepfish/srcD/thread.h +++ b/Engines/Windows/mcbrain/src/thread.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,22 +55,23 @@ class Thread { void idle_loop(); void start_searching(bool resume = false); void wait_for_search_finished(); - void wait(std::atomic_bool& b); + void wait(std::atomic_bool& condition); Pawns::Table pawnsTable; Material::Table materialTable; Endgames endgames; size_t idx, PVIdx; - int maxPly, callsCnt; + int maxPly, callsCnt, nmp_ply, pair; + uint64_t tbHits; Position rootPos; Search::RootMoves rootMoves; Depth rootDepth; - HistoryStats history; - MoveStats counterMoves; - FromToStats fromTo; Depth completedDepth; std::atomic_bool resetCalls; + MoveStats counterMoves; + HistoryStats history; + CounterMoveHistoryStats counterMoveHistory; }; @@ -97,7 +98,8 @@ struct ThreadPool : public std::vector { MainThread* main() { return static_cast(at(0)); } void start_thinking(Position&, StateListPtr&, const Search::LimitsType&); void read_uci_options(); - int64_t nodes_searched(); + uint64_t nodes_searched() const; + uint64_t tb_hits() const; private: StateListPtr setupStates; diff --git a/Engines/Windows/deepfish/srcD/thread_win32.h b/Engines/Windows/mcbrain/src/thread_win32.h similarity index 97% rename from Engines/Windows/deepfish/srcD/thread_win32.h rename to Engines/Windows/mcbrain/src/thread_win32.h index 47516c6..917563a 100644 --- a/Engines/Windows/deepfish/srcD/thread_win32.h +++ b/Engines/Windows/mcbrain/src/thread_win32.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Engines/Windows/deepfish/srcD/timeman.cpp b/Engines/Windows/mcbrain/src/timeman.cpp similarity index 98% rename from Engines/Windows/deepfish/srcD/timeman.cpp rename to Engines/Windows/mcbrain/src/timeman.cpp index c7c19f4..4ed54cd 100644 --- a/Engines/Windows/deepfish/srcD/timeman.cpp +++ b/Engines/Windows/mcbrain/src/timeman.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,8 +52,8 @@ namespace { } template - int remaining(int myTime, int movesToGo, int ply, int slowMover) - { + int remaining(int myTime, int movesToGo, int ply, int slowMover) { + const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); @@ -81,8 +81,8 @@ namespace { /// inc > 0 && movestogo == 0 means: x basetime + z increment /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment -void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) -{ +void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { + int minThinkingTime = Options["Minimum Thinking Time"]; int moveOverhead = Options["Move Overhead"]; int slowMover = Options["Slow Mover"]; diff --git a/Engines/Windows/deepfish/srcD/timeman.h b/Engines/Windows/mcbrain/src/timeman.h similarity index 96% rename from Engines/Windows/deepfish/srcD/timeman.h rename to Engines/Windows/mcbrain/src/timeman.h index 9930a4b..c22c8c2 100644 --- a/Engines/Windows/deepfish/srcD/timeman.h +++ b/Engines/Windows/mcbrain/src/timeman.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Engines/Windows/deepfish/srcD/tt.cpp b/Engines/Windows/mcbrain/src/tt.cpp similarity index 88% rename from Engines/Windows/deepfish/srcD/tt.cpp rename to Engines/Windows/mcbrain/src/tt.cpp index 13d5d77..f283d0c 100644 --- a/Engines/Windows/deepfish/srcD/tt.cpp +++ b/Engines/Windows/mcbrain/src/tt.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,8 +26,6 @@ TranspositionTable TT; // Our global transposition table -void CREATE_MEM2(void **,uint64_t); -void FREE_MEM(void *); /// TranspositionTable::resize() sets the size of the transposition table, /// measured in megabytes. Transposition table consists of a power of 2 number @@ -42,17 +40,8 @@ void TranspositionTable::resize(size_t mbSize) { clusterCount = newClusterCount; - mem = nullptr; - FREE_MEM(mem); - CREATE_MEM2(&mem, clusterCount * sizeof(Cluster)); - large_use = true; - - if (!mem) - { - free(mem); - mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1); - large_use = false; - } + free(mem); + mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1); if (!mem) { @@ -111,11 +100,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { } -/// Returns an approximation of the hashtable occupation during a search. The -/// hash is x permill full, as per UCI protocol. +/// TranspositionTable::hashfull() returns an approximation of the hashtable +/// occupation during a search. The hash is x permill full, as per UCI protocol. + +int TranspositionTable::hashfull() const { -int TranspositionTable::hashfull() const -{ int cnt = 0; for (int i = 0; i < 1000 / ClusterSize; i++) { diff --git a/Engines/Windows/deepfish/srcD/tt.h b/Engines/Windows/mcbrain/src/tt.h similarity index 95% rename from Engines/Windows/deepfish/srcD/tt.h rename to Engines/Windows/mcbrain/src/tt.h index 98025e2..24045ed 100644 --- a/Engines/Windows/deepfish/srcD/tt.h +++ b/Engines/Windows/mcbrain/src/tt.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -82,10 +82,9 @@ struct TTEntry { /// divide the size of a cache line size, to ensure that clusters never cross /// cache lines. This ensures best cache performance, as the cacheline is /// prefetched, as soon as possible. -extern int large_use; -void FREE_MEM (void *); class TranspositionTable { + static const int CacheLineSize = 64; static const int ClusterSize = 3; @@ -97,8 +96,7 @@ class TranspositionTable { static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect"); public: - void* mem; - ~TranspositionTable() { large_use ? FREE_MEM (mem) : free(mem); } + ~TranspositionTable() { free(mem); } void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound uint8_t generation() const { return generation8; } TTEntry* probe(const Key key, bool& found) const; @@ -114,6 +112,7 @@ class TranspositionTable { private: size_t clusterCount; Cluster* table; + void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 }; diff --git a/Engines/Windows/deepfish/srcD/types.h b/Engines/Windows/mcbrain/src/types.h similarity index 86% rename from Engines/Windows/deepfish/srcD/types.h rename to Engines/Windows/mcbrain/src/types.h index 1440f73..60bfd1f 100644 --- a/Engines/Windows/deepfish/srcD/types.h +++ b/Engines/Windows/mcbrain/src/types.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -115,7 +115,7 @@ const int MAX_PLY = 128; /// any normal move destination square is always different from origin square /// while MOVE_NONE and MOVE_NULL have the same origin and destination square. -enum Move { +enum Move : int { MOVE_NONE, MOVE_NULL = 65 }; @@ -183,13 +183,13 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, - PawnValueMg = 198, PawnValueEg = 258, - KnightValueMg = 817, KnightValueEg = 896, - BishopValueMg = 836, BishopValueEg = 907, - RookValueMg = 1270, RookValueEg = 1356, - QueenValueMg = 2521, QueenValueEg = 2658, + PawnValueMg = 190, PawnValueEg = 250, + KnightValueMg = 761, KnightValueEg = 840, + BishopValueMg = 834, BishopValueEg = 906, + RookValueMg = 1298, RookValueEg = 1385, + QueenValueMg = 2538, QueenValueEg = 2677, - MidgameLimit = 15581, EndgameLimit = 3998 +MidgameLimit = 15411, EndgameLimit = 3954 }; enum PieceType { @@ -205,7 +205,11 @@ enum Piece { PIECE_NB = 16 }; -enum Depth { +const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; +extern Value PieceValue[PHASE_NB][PIECE_NB]; + +enum Depth : int { ONE_PLY = 1, @@ -233,68 +237,67 @@ enum Square { SQUARE_NB = 64, - DELTA_N = 8, - DELTA_E = 1, - DELTA_S = -8, - DELTA_W = -1, - - DELTA_NN = DELTA_N + DELTA_N, - DELTA_NE = DELTA_N + DELTA_E, - DELTA_SE = DELTA_S + DELTA_E, - DELTA_SS = DELTA_S + DELTA_S, - DELTA_SW = DELTA_S + DELTA_W, - DELTA_NW = DELTA_N + DELTA_W + NORTH = 8, + EAST = 1, + SOUTH = -8, + WEST = -1, + + NORTH_EAST = NORTH + EAST, + SOUTH_EAST = SOUTH + EAST, + SOUTH_WEST = SOUTH + WEST, + NORTH_WEST = NORTH + WEST }; -enum File { +enum File : int { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; -enum Rank { +enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; /// Score enum stores a middlegame and an endgame value in a single integer /// (enum). The least significant 16 bits are used to store the endgame value -/// and the upper 16 bits are used to store the middlegame value. +/// and the upper 16 bits are used to store the middlegame value. Take some +/// care to avoid left-shifting a signed int to avoid undefined behavior. enum Score : int { SCORE_ZERO }; inline Score make_score(int mg, int eg) { - return Score((mg << 16) + eg); + return Score((int)((unsigned int)eg << 16) + mg); } /// Extracting the signed lower and upper 16 bits is not so trivial because /// according to the standard a simple cast to short is implementation defined /// and so is a right shift of a signed integer. -inline Value mg_value(Score s) { +inline Value eg_value(Score s) { - union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) }; - return Value(mg.s); + union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) }; + return Value(eg.s); } -inline Value eg_value(Score s) { +inline Value mg_value(Score s) { - union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) }; - return Value(eg.s); + union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) }; + return Value(mg.s); } #define ENABLE_BASE_OPERATORS_ON(T) \ inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ -inline T operator*(int i, T d) { return T(i * int(d)); } \ -inline T operator*(T d, int i) { return T(int(d) * i); } \ inline T operator-(T d) { return T(-int(d)); } \ inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \ -inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \ +inline T operator*(int i, T d) { return T(i * int(d)); } \ +inline T operator*(T d, int i) { return T(int(d) * i); } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } \ inline T operator/(T d, int i) { return T(int(d) / i); } \ inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \ +inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) @@ -326,16 +329,29 @@ inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } -extern Value PieceValue[PHASE_NB][PIECE_NB]; +/// Multiplication of a Score by an integer. We check for overflow in debug mode. +inline Score operator*(Score s, int i) { + Score result = Score(int(s) * i); + + assert(eg_value(result) == (i * eg_value(s))); + assert(mg_value(result) == (i * mg_value(s))); + assert((i == 0) || (result / i) == s ); + + return result; +} inline Color operator~(Color c) { - return Color(c ^ BLACK); + return Color(c ^ BLACK); // Toggle color } inline Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } +inline Piece operator~(Piece pc) { + return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT +} + inline CastlingRight operator|(Color c, CastlingSide s) { return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); } @@ -349,11 +365,11 @@ inline Value mated_in(int ply) { } inline Square make_square(File f, Rank r) { - return Square((r << 3) | f); + return Square((r << 3) + f); } inline Piece make_piece(Color c, PieceType pt) { - return Piece((c << 3) | pt); + return Piece((c << 3) + pt); } inline PieceType type_of(Piece pc) { @@ -395,7 +411,7 @@ inline bool opposite_colors(Square s1, Square s2) { } inline Square pawn_push(Color c) { - return c == WHITE ? DELTA_N : DELTA_S; + return c == WHITE ? NORTH : SOUTH; } inline Square from_sq(Move m) { @@ -415,12 +431,12 @@ inline PieceType promotion_type(Move m) { } inline Move make_move(Square from, Square to) { - return Move(to | (from << 6)); + return Move((from << 6) + to); } template inline Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)); + return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } inline bool is_ok(Move m) { diff --git a/Engines/Windows/mcbrain/src/tzbook.cpp b/Engines/Windows/mcbrain/src/tzbook.cpp new file mode 100644 index 0000000..4052256 --- /dev/null +++ b/Engines/Windows/mcbrain/src/tzbook.cpp @@ -0,0 +1,221 @@ +#include "tzbook.h" +#include "uci.h" +#include "movegen.h" +#include "thread.h" +#include +#include "misc.h" +#include + +TZBook tzbook; // global TZBook + +using namespace std; + +int qsort_compare_int(const void* a, const void* b) +{ + const int int_a = *((const int*)a); + const int int_b = *((const int*)b); + + if (int_a == int_b) return 0; + else if (int_a < int_b) return -1; + else return 1; +} + +TZBook::TZBook() +{ + keycount = 0; + book_move2_probability = 0; + last_position = 0; + akt_position = 0; + last_anz_pieces = 0; + akt_anz_pieces = 0; + search_counter = 0; + tzhash2 = NULL; + do_search = true; + enabled = false; +} + +TZBook::~TZBook() +{ +} + + +void TZBook::init(const std::string& path) +{ + if (path.length() == 0) return; + + const char *p = path.c_str(); + if (strcmp(p, "") == 0) + return; + + FILE *fpt = fopen(p, "rb"); + if (fpt == NULL) + { + sync_cout << "info string Could not open " << path << sync_endl; + return; + } + + if (tzhash2 != NULL) + { + free(tzhash2); + tzhash2 = NULL; + } + + fseek(fpt, 0L, SEEK_END); + int filesize = ftell(fpt); + fseek(fpt, 0L, SEEK_SET); + + keycount = filesize / 8; + tzhash2 = new TZHash2[keycount]; + + fread(tzhash2, 1, filesize, fpt); + fclose(fpt); + + sync_cout << "info string Book loaded: " << path << sync_endl; + + srand((int)time(NULL)); + enabled = true; +} + + +void TZBook::set_book_move2_probability(int book_move2_prob) +{ + book_move2_probability = book_move2_prob; +} + + +Move TZBook::probe2(Position& pos) +{ + Move m = MOVE_NONE; + if (!enabled) return m; + + akt_position = pos.pieces(); + akt_anz_pieces = popcount(akt_position); + + if (do_search == false) + { + Bitboard b = akt_position ^ last_position; + int n2 = popcount(b); + + if (n2 > 4) do_search = true; + if (akt_anz_pieces > last_anz_pieces) do_search = true; + if (akt_anz_pieces < last_anz_pieces - 2) do_search = true; + } + + last_position = akt_position; + last_anz_pieces = akt_anz_pieces; + + if (do_search) + { + TZHash2 *tz = probe2(pos.key()); + if (tz == NULL) + { + search_counter++; + if (search_counter > 2) + { + do_search = false; + search_counter = 0; + } + } + else + { + if (pos.is_draw(64)) + m = get_move_from_draw_position(pos, tz); + else + m = get_move(pos, tz); + } + } + + return m; +} + + +TZHash2 *TZBook::probe2(Key key) +{ + uint32_t key1 = key >> 32; + unsigned short key2 = key >> 16 & 0xFFFF; + + int start = 0; + int end = keycount; + + for (;;) + { + int mid = (end + start) / 2; + + if (tzhash2[mid].key1 < key1) + start = mid; + else + { + if (tzhash2[mid].key1 > key1) + end = mid; + else + { + start = max(mid - 4, 0); + end = min(mid + 4, keycount); + } + } + + if (end - start < 9) + break; + } + + for (int i = start; i < end; i++) + if ((key1 == tzhash2[i].key1) && (key2 == tzhash2[i].key2)) + return &(tzhash2[i]); + + return NULL; +} + +Move TZBook::movenumber_to_move(Position& pos, int n) +{ + const ExtMove *m = MoveList(pos).begin(); + size_t size = MoveList(pos).size(); + Move *mv = new Move[size]; + for (unsigned int i = 0; i < size; i++) + mv[i] = m[i].move; + + qsort(mv, size, sizeof(mv[0]), qsort_compare_int); + + return mv[n]; +} + +bool TZBook::check_draw(Move m, Position& pos) +{ + StateInfo st; + + pos.do_move(m, st, pos.gives_check(m)); + bool draw = pos.is_draw(64); + pos.undo_move(m); + + return draw; +} + + +Move TZBook::get_move_from_draw_position(Position& pos, TZHash2 *tz) +{ + Move m = movenumber_to_move(pos, tz->move_number); + if (!check_draw(m, pos)) + return m; + + if (tz->move_number2 == 255) + return m; + + m = movenumber_to_move(pos, tz->move_number2); + if (!check_draw(m, pos)) + return m; + + return MOVE_NONE; +} + + +Move TZBook::get_move(Position& pos, TZHash2 *tz) +{ + Move m1 = movenumber_to_move(pos, tz->move_number); + if ((book_move2_probability == 0) || (tz->move_number2 == 255)) + return m1; + + Move m2 = movenumber_to_move(pos, tz->move_number2); + if ((book_move2_probability == 100) || (rand() % 100 < book_move2_probability)) + return m2; + + return m1; +} \ No newline at end of file diff --git a/Engines/Windows/mcbrain/src/tzbook.h b/Engines/Windows/mcbrain/src/tzbook.h new file mode 100644 index 0000000..744618a --- /dev/null +++ b/Engines/Windows/mcbrain/src/tzbook.h @@ -0,0 +1,50 @@ +#ifndef TZBOOK_H_INCLUDED +#define TZBOOK_H_INCLUDED + +#include "bitboard.h" +#include "position.h" +#include "string.h" + +struct TZHash2 +{ + uint32_t key1; + uint16_t key2; + unsigned char move_number; + unsigned char move_number2; +}; + +class TZBook +{ + public: + + Bitboard last_position; + Bitboard akt_position; + int last_anz_pieces; + int akt_anz_pieces; + int search_counter; + + bool enabled, do_search; + int book_move2_probability; + + TZBook(); + ~TZBook(); + + void init(const std::string& path); + void set_book_move2_probability(int book_move2_prob); + + Move probe2(Position& pos); + TZHash2 *probe2(Key key); + +private: + + int keycount; + TZHash2 *tzhash2; + bool check_draw(Move m, Position& pos); + Move get_move_from_draw_position(Position& pos, TZHash2 *tz); + Move get_move(Position& pos, TZHash2 *tz); + Move movenumber_to_move(Position& pos, int n); +}; + +extern TZBook tzbook; + +#endif // #ifndef TZBOOK_H_INCLUDED \ No newline at end of file diff --git a/Engines/Windows/deepfish/srcD/uci.cpp b/Engines/Windows/mcbrain/src/uci.cpp similarity index 88% rename from Engines/Windows/deepfish/srcD/uci.cpp rename to Engines/Windows/mcbrain/src/uci.cpp index b195b87..02f0483 100644 --- a/Engines/Windows/deepfish/srcD/uci.cpp +++ b/Engines/Windows/mcbrain/src/uci.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ #include "thread.h" #include "timeman.h" #include "uci.h" +#include "tbprobe.h" using namespace std; @@ -65,7 +66,10 @@ namespace { else if (token == "fen") while (is >> token && token != "moves") fen += token + " "; - else + else if (token == "f") + while (is >> token && token != "moves") + fen += token + " "; + else return; States = StateListPtr(new std::deque(1)); @@ -75,7 +79,7 @@ namespace { while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) { States->push_back(StateInfo()); - pos.do_move(m, States->back(), pos.gives_check(m)); + pos.do_move(m, States->back()); } } @@ -90,7 +94,7 @@ namespace { is >> token; // Consume "name" token // Read option name (can contain spaces) - while (is >> token && token != "value") + while (is >> token && (token != "value" && token != "v")) name += string(" ", name.empty() ? 0 : 1) + token; // Read option value (can contain spaces) @@ -126,10 +130,14 @@ namespace { else if (token == "binc") is >> limits.inc[BLACK]; else if (token == "movestogo") is >> limits.movestogo; else if (token == "depth") is >> limits.depth; + else if (token == "d") is >> limits.depth; + else if (token == "nodes") is >> limits.nodes; else if (token == "movetime") is >> limits.movetime; + else if (token == "mt") is >> limits.movetime; else if (token == "mate") is >> limits.mate; else if (token == "infinite") limits.infinite = 1; + else if (token == "i") limits.infinite = 1; else if (token == "ponder") limits.ponder = 1; Threads.start_thinking(pos, States, limits); @@ -156,7 +164,10 @@ void UCI::loop(int argc, char* argv[]) { do { if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF - cmd = "quit"; + cmd = "quit"; + else if (token == "q") + cmd = "quit"; + istringstream is(cmd); @@ -169,7 +180,9 @@ void UCI::loop(int argc, char* argv[]) { // already ran out of time), otherwise we should continue searching but // switching from pondering to normal search. if ( token == "quit" + || token == "q" || token == "stop" + || token == "?" || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) { Search::Signals.stop = true; @@ -186,16 +199,36 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "ucinewgame") { Search::clear(); + Tablebases::init(Options["SyzygyPath"]); Time.availableNodes = 0; } else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "go") go(pos, is); - else if (token == "position") position(pos, is); + else if (token == "g") go(pos, is); + + else if (token == "q") cmd = "quit"; + + + else if (token == "position") + { + position(pos, is); + if (Options["Clean Search"] == 1) + Search::clear(); + } + else if (token == "p") + { + position(pos, is); + if (Options["Clean Search"] == 1) + Search::clear(); + } else if (token == "setoption") setoption(is); + else if (token == "so") setoption(is); // Additional custom non-UCI commands, useful for debugging else if (token == "flip") pos.flip(); else if (token == "bench") benchmark(pos, is); + else if (token == "b") benchmark(pos, is); + else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "perft") @@ -212,7 +245,7 @@ void UCI::loop(int argc, char* argv[]) { else sync_cout << "Unknown command: " << cmd << sync_endl; - } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour + } while (token != "quit" && token != "q" && argc == 1);// Passed args have one-shot behaviour Threads.main()->wait_for_search_finished(); } diff --git a/Engines/Windows/deepfish/srcD/uci.h b/Engines/Windows/mcbrain/src/uci.h similarity index 94% rename from Engines/Windows/deepfish/srcD/uci.h rename to Engines/Windows/mcbrain/src/uci.h index 4697877..e6b31c5 100644 --- a/Engines/Windows/deepfish/srcD/uci.h +++ b/Engines/Windows/mcbrain/src/uci.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,7 +49,7 @@ class Option { Option(OnChange = nullptr); Option(bool v, OnChange = nullptr); Option(const char* v, OnChange = nullptr); - Option(int v, int min, int max, OnChange = nullptr); + Option(int v, int minv, int maxv, OnChange = nullptr); Option& operator=(const std::string&); void operator<<(const Option&); diff --git a/Engines/Windows/deepfish/srcD/ucioption.cpp b/Engines/Windows/mcbrain/src/ucioption.cpp similarity index 78% rename from Engines/Windows/deepfish/srcD/ucioption.cpp rename to Engines/Windows/mcbrain/src/ucioption.cpp index ab931bb..7dd5012 100644 --- a/Engines/Windows/deepfish/srcD/ucioption.cpp +++ b/Engines/Windows/mcbrain/src/ucioption.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,7 +27,8 @@ #include "thread.h" #include "tt.h" #include "uci.h" -#include "syzygy/tbprobe.h" +#include "tbprobe.h" +#include "tzbook.h" using std::string; @@ -41,6 +42,8 @@ void on_hash_size(const Option& o) { TT.resize(o); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option&) { Threads.read_uci_options(); } void on_tb_path(const Option& o) { Tablebases::init(o); } +void on_brainbook_path(const Option& o) { tzbook.init(o); } +void on_book_move2_prob(const Option& o) { tzbook.set_book_move2_probability(o); } /// Our case insensitive less() function as required by UCI protocol @@ -56,13 +59,36 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048; - - o["Debug Log File"] << Option("", on_logger); - o["Contempt"] << Option(0, -100, 100); - o["Threads"] << Option(1, 1, 128, on_threads); + o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); - o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(false); + o["Threads"] << Option(1, 1, 128, on_threads); + + o["Clear Hash"] << Option(on_clear_hash); + o["Clean Search"] << Option(false); + o["Botvinnik-Markov"] << Option(true); + o["FastPlay"] << Option(false); + o["FindMate"] << Option(true); + o["Futility"] << Option(true); + o["LMR"] << Option(true); + o["NullMove"] << Option(true); + o["ProbCut"] << Option(true); + o["Pruning"] << Option(true); + o["Razoring"] << Option(true); + + o["Variety"] << Option(0, 0, 8); + o["UCI_Limit_Strength"] << Option(false); + o["UCI_Elo_Delay"] << Option(false); + o["UCI_Elo"] << Option(1600, 1200, 2800); + o["Book Move2 Probability"]<< Option(0, 0, 100, on_book_move2_prob); + o["BookPath"] << Option("", on_brainbook_path); + o["Respect"] << Option(10, -100, 100); + o["Tactical"] << Option(0, 0, 8); + // used to setup LMR reduction array based on depth and move count + o["LMRDepth"] << Option(120, 0, 300); + o["LMRDivisor"] << Option(195, 1, 600); + o["LMRMoveCount"] << Option(100, 0, 300); + o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(30, 0, 5000); @@ -74,6 +100,8 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(6, 0, 6); + o["Debug Log File"] << Option("", on_logger); + o["ShowInfo"] << Option(false); } diff --git a/Locale/ar/LC_MESSAGES/lucaschess.mo b/Locale/ar/LC_MESSAGES/lucaschess.mo index fb85dae..7abfaea 100644 Binary files a/Locale/ar/LC_MESSAGES/lucaschess.mo and b/Locale/ar/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/az/LC_MESSAGES/lucaschess.mo b/Locale/az/LC_MESSAGES/lucaschess.mo index 138677e..147660a 100644 Binary files a/Locale/az/LC_MESSAGES/lucaschess.mo and b/Locale/az/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/az/lang.ini b/Locale/az/lang.ini index 12e60c1..2691ad4 100644 --- a/Locale/az/lang.ini +++ b/Locale/az/lang.ini @@ -1,3 +1,3 @@ NAME=Azeri AUTHOR=Shahin Jafarli (shahinjy) -%=94 \ No newline at end of file +%=92 \ No newline at end of file diff --git a/Locale/br/LC_MESSAGES/lucaschess.mo b/Locale/br/LC_MESSAGES/lucaschess.mo index 6089d8d..3fede8c 100644 Binary files a/Locale/br/LC_MESSAGES/lucaschess.mo and b/Locale/br/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/ca/LC_MESSAGES/lucaschess.mo b/Locale/ca/LC_MESSAGES/lucaschess.mo index 5666b43..42707a3 100644 Binary files a/Locale/ca/LC_MESSAGES/lucaschess.mo and b/Locale/ca/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/ca/lang.ini b/Locale/ca/lang.ini index 8d9b2c8..c895aac 100644 --- a/Locale/ca/lang.ini +++ b/Locale/ca/lang.ini @@ -1,4 +1,4 @@ NAME=Català AUTHOR=Salvador Morral i Esteve BASELANG=es -%=84 \ No newline at end of file +%=83 \ No newline at end of file diff --git a/Locale/cs/LC_MESSAGES/lucaschess.mo b/Locale/cs/LC_MESSAGES/lucaschess.mo index 03a661b..ba637ca 100644 Binary files a/Locale/cs/LC_MESSAGES/lucaschess.mo and b/Locale/cs/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/cs/lang.ini b/Locale/cs/lang.ini index 1fa322e..bb8cc75 100644 --- a/Locale/cs/lang.ini +++ b/Locale/cs/lang.ini @@ -1,3 +1,3 @@ NAME=ÄŒeÅ¡tina AUTHOR=Jindrich Skula -%=92 \ No newline at end of file +%=90 \ No newline at end of file diff --git a/Locale/de/LC_MESSAGES/lucaschess.mo b/Locale/de/LC_MESSAGES/lucaschess.mo index 5a23c04..03932cb 100644 Binary files a/Locale/de/LC_MESSAGES/lucaschess.mo and b/Locale/de/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/de/lang.ini b/Locale/de/lang.ini index 47c2d5b..6261fbd 100644 --- a/Locale/de/lang.ini +++ b/Locale/de/lang.ini @@ -1,3 +1,3 @@ NAME=Deutsch AUTHOR=Frank Stender,alfons,Georg Pfefferle -%=96 \ No newline at end of file +%=94 \ No newline at end of file diff --git a/Locale/es/LC_MESSAGES/lucaschess.mo b/Locale/es/LC_MESSAGES/lucaschess.mo index 9d5c2f0..d81b795 100644 Binary files a/Locale/es/LC_MESSAGES/lucaschess.mo and b/Locale/es/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/fi/LC_MESSAGES/lucaschess.mo b/Locale/fi/LC_MESSAGES/lucaschess.mo index 3562ea5..c0cfe9d 100644 Binary files a/Locale/fi/LC_MESSAGES/lucaschess.mo and b/Locale/fi/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/fr/LC_MESSAGES/lucaschess.mo b/Locale/fr/LC_MESSAGES/lucaschess.mo index ca16c19..525e088 100644 Binary files a/Locale/fr/LC_MESSAGES/lucaschess.mo and b/Locale/fr/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/gr/LC_MESSAGES/lucaschess.mo b/Locale/gr/LC_MESSAGES/lucaschess.mo index 1070ce4..8da49bf 100644 Binary files a/Locale/gr/LC_MESSAGES/lucaschess.mo and b/Locale/gr/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/id/LC_MESSAGES/lucaschess.mo b/Locale/id/LC_MESSAGES/lucaschess.mo index bb8222d..f1fc571 100644 Binary files a/Locale/id/LC_MESSAGES/lucaschess.mo and b/Locale/id/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/it/LC_MESSAGES/lucaschess.mo b/Locale/it/LC_MESSAGES/lucaschess.mo index 10e2802..5722161 100644 Binary files a/Locale/it/LC_MESSAGES/lucaschess.mo and b/Locale/it/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/nl/LC_MESSAGES/lucaschess.mo b/Locale/nl/LC_MESSAGES/lucaschess.mo index 76e5fbe..fe54e9e 100644 Binary files a/Locale/nl/LC_MESSAGES/lucaschess.mo and b/Locale/nl/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/nl/lang.ini b/Locale/nl/lang.ini index 2214015..d1922a9 100644 --- a/Locale/nl/lang.ini +++ b/Locale/nl/lang.ini @@ -1,3 +1,3 @@ NAME=Nederlands AUTHOR=Willy Lefebvre -%=98 \ No newline at end of file +%=100 \ No newline at end of file diff --git a/Locale/pl/LC_MESSAGES/lucaschess.mo b/Locale/pl/LC_MESSAGES/lucaschess.mo index 4bf00f0..f8721d6 100644 Binary files a/Locale/pl/LC_MESSAGES/lucaschess.mo and b/Locale/pl/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/po/LC_MESSAGES/lucaschess.mo b/Locale/po/LC_MESSAGES/lucaschess.mo index a4e0a19..e2e2301 100644 Binary files a/Locale/po/LC_MESSAGES/lucaschess.mo and b/Locale/po/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/ro/LC_MESSAGES/lucaschess.mo b/Locale/ro/LC_MESSAGES/lucaschess.mo index 398ae17..2c825c2 100644 Binary files a/Locale/ro/LC_MESSAGES/lucaschess.mo and b/Locale/ro/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/ro/lang.ini b/Locale/ro/lang.ini index a2352e9..7f13531 100644 --- a/Locale/ro/lang.ini +++ b/Locale/ro/lang.ini @@ -1,3 +1,3 @@ NAME=Română AUTHOR=Dan-Alexandru Raportaru -%=16 \ No newline at end of file +%=19 \ No newline at end of file diff --git a/Locale/ru/LC_MESSAGES/lucaschess.mo b/Locale/ru/LC_MESSAGES/lucaschess.mo index 8bf7af3..075c489 100644 Binary files a/Locale/ru/LC_MESSAGES/lucaschess.mo and b/Locale/ru/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/ru/lang.ini b/Locale/ru/lang.ini index 551992b..ceb8274 100644 --- a/Locale/ru/lang.ini +++ b/Locale/ru/lang.ini @@ -1,3 +1,3 @@ NAME=РоÑÑийÑкий -AUTHOR=Reinhard,Vladimir,Slavik Pavlov -%=84 \ No newline at end of file +AUTHOR=Nils Andersson,Reinhard,Vladimir,Slavik Pavlov +%=99 diff --git a/Locale/si/LC_MESSAGES/lucaschess.mo b/Locale/si/LC_MESSAGES/lucaschess.mo index 20a3b6f..d7e71b2 100644 Binary files a/Locale/si/LC_MESSAGES/lucaschess.mo and b/Locale/si/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/sv/LC_MESSAGES/lucaschess.mo b/Locale/sv/LC_MESSAGES/lucaschess.mo index ae04868..d720e75 100644 Binary files a/Locale/sv/LC_MESSAGES/lucaschess.mo and b/Locale/sv/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/sv/lang.ini b/Locale/sv/lang.ini index 9e0a1f1..d4d292c 100644 --- a/Locale/sv/lang.ini +++ b/Locale/sv/lang.ini @@ -1,3 +1,3 @@ NAME=Svenska AUTHOR=Nils Andersson -%=84 \ No newline at end of file +%=99 \ No newline at end of file diff --git a/Locale/tr/LC_MESSAGES/lucaschess.mo b/Locale/tr/LC_MESSAGES/lucaschess.mo index 2b4ead8..4bc2003 100644 Binary files a/Locale/tr/LC_MESSAGES/lucaschess.mo and b/Locale/tr/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/tr/lang.ini b/Locale/tr/lang.ini index d79b3f0..b00b93b 100644 --- a/Locale/tr/lang.ini +++ b/Locale/tr/lang.ini @@ -1,3 +1,3 @@ NAME=Türkçe AUTHOR=Mustafa Nazmi ÖZDEMÄ°R -%=94 \ No newline at end of file +%=98 \ No newline at end of file diff --git a/Locale/vi/LC_MESSAGES/lucaschess.mo b/Locale/vi/LC_MESSAGES/lucaschess.mo index 74f2b30..b4695d2 100644 Binary files a/Locale/vi/LC_MESSAGES/lucaschess.mo and b/Locale/vi/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/zh/LC_MESSAGES/lucaschess.mo b/Locale/zh/LC_MESSAGES/lucaschess.mo index 39525c1..d982463 100644 Binary files a/Locale/zh/LC_MESSAGES/lucaschess.mo and b/Locale/zh/LC_MESSAGES/lucaschess.mo differ diff --git a/Locale/zh/lang.ini b/Locale/zh/lang.ini index 8efe645..6d0a0dd 100644 --- a/Locale/zh/lang.ini +++ b/Locale/zh/lang.ini @@ -1,3 +1,3 @@ NAME=简体中文 AUTHOR=Kevin Sicong Jiang, Stephen Yang -%=94 \ No newline at end of file +%=92 \ No newline at end of file diff --git a/bug.log b/bug.log index 376d461..fed3a10 100644 --- a/bug.log +++ b/bug.log @@ -1 +1 @@ -Version 10.11.9 +Version 10.12