From 0603befe8b147d21daf97c03545dec7e239eb469 Mon Sep 17 00:00:00 2001 From: lukasmonk Date: Sun, 29 Jul 2018 14:35:18 +0200 Subject: [PATCH] Version 11.10 --- Code/AnalisisIndexes.py | 2 +- Code/Books.py | 13 +- Code/Configuracion.py | 1 - Code/ControlPosicion.py | 71 ++ Code/EngineThread.py | 9 +- Code/EnginesWindows.py | 10 +- Code/Entrenamientos.py | 4 +- Code/Gestor.py | 63 +- Code/GestorAlbum.py | 2 +- Code/GestorBooks.py | 4 +- Code/GestorCompeticion.py | 2 +- Code/GestorElo.py | 8 +- Code/GestorEntMaq.py | 7 +- Code/GestorEntPos.py | 18 +- Code/GestorEverest.py | 2 +- Code/GestorFideFics.py | 2 +- Code/GestorGM.py | 3 +- Code/GestorMate.py | 2 +- Code/GestorMateMap.py | 3 +- Code/GestorMicElo.py | 9 +- Code/GestorOpeningLines.py | 419 +++++++++++- Code/GestorPGN.py | 2 - Code/GestorPlayPGN.py | 2 +- Code/GestorResistance.py | 2 +- Code/GestorRoutes.py | 17 +- Code/GestorTacticas.py | 54 +- Code/GestorTorneo.py | 27 +- Code/GestorWashing.py | 7 +- Code/Init.py | 2 +- Code/Jugada.py | 2 +- Code/Kibitzers.py | 14 +- Code/OpeningLines.py | 407 +++++++++--- Code/PGNreader.py | 10 +- Code/Partida.py | 5 +- Code/Procesador.py | 16 +- Code/QT/Iconos.py | 50 +- Code/QT/InfoBase.py | 2 +- Code/QT/POLAnalisis.py | 2 - Code/QT/POLBoard.py | 2 + Code/QT/Pantalla.py | 4 +- Code/QT/PantallaDirector.py | 2 +- Code/QT/PantallaEntMaq.py | 1 - Code/QT/PantallaGM.py | 1 + Code/QT/PantallaKibitzers.py | 57 +- .../QT/{POLines.py => PantallaOpeningLine.py} | 617 ++++++------------ Code/QT/PantallaOpeningLines.py | 396 +++++++++++ Code/QT/PantallaTorneos.py | 1 - Code/QT/QTUtil.py | 2 + Code/QT/QTUtil2.py | 9 + Code/QT/Tablero.py | 4 - Code/QT/Voyager.py | 4 +- Code/QT/WBG_Players.py | 3 - Code/QT/WBase.py | 4 - Code/RunKibitzer.py | 303 ++++++++- Code/SQL/DBF.py | 1 + Code/Tacticas.py | 29 +- Code/Torneo.py | 2 +- Code/XGestorMotor.py | 2 - GenIconos/Formatos.tema | 11 + GenIconos/windows8/Add_List_32px.png | Bin 0 -> 1210 bytes GenIconos/windows8/Book_32px.png | Bin 0 -> 574 bytes GenIconos/windows8/Download_32px.png | Bin 0 -> 625 bytes GenIconos/windows8/Exposure_32px.png | Bin 0 -> 792 bytes GenIconos/windows8/Knight_32px.png | Bin 0 -> 839 bytes GenIconos/windows8/Knight_32px_1.png | Bin 0 -> 1030 bytes .../windows8/Originals/Download_32px.png | Bin 0 -> 922 bytes GenIconos/windows8/Treadmill_32px.png | Bin 0 -> 1434 bytes IntFiles/Figs/bb.png | Bin 414 -> 419 bytes IntFiles/Figs/bk.png | Bin 352 -> 607 bytes IntFiles/Figs/bn.png | Bin 324 -> 429 bytes IntFiles/Figs/bp.png | Bin 266 -> 264 bytes IntFiles/Figs/bq.png | Bin 306 -> 521 bytes IntFiles/Figs/br.png | Bin 319 -> 453 bytes IntFiles/Figs/wb.png | Bin 515 -> 542 bytes IntFiles/Figs/wk.png | Bin 493 -> 581 bytes IntFiles/Figs/wn.png | Bin 464 -> 572 bytes IntFiles/Figs/wp.png | Bin 360 -> 414 bytes IntFiles/Figs/wq.png | Bin 360 -> 726 bytes IntFiles/Figs/wr.png | Bin 401 -> 474 bytes IntFiles/Iconos.bin | Bin 921141 -> 927645 bytes Locale/es/LC_MESSAGES/lucaschess.mo | Bin 312040 -> 313372 bytes Locale/fr/LC_MESSAGES/lucaschess.mo | Bin 272442 -> 273177 bytes Lucas.py | 1 - bug.log | 2 +- 84 files changed, 2055 insertions(+), 678 deletions(-) rename Code/QT/{POLines.py => PantallaOpeningLine.py} (65%) create mode 100644 Code/QT/PantallaOpeningLines.py create mode 100644 GenIconos/windows8/Add_List_32px.png create mode 100644 GenIconos/windows8/Book_32px.png create mode 100644 GenIconos/windows8/Download_32px.png create mode 100644 GenIconos/windows8/Exposure_32px.png create mode 100644 GenIconos/windows8/Knight_32px.png create mode 100644 GenIconos/windows8/Knight_32px_1.png create mode 100644 GenIconos/windows8/Originals/Download_32px.png create mode 100644 GenIconos/windows8/Treadmill_32px.png diff --git a/Code/AnalisisIndexes.py b/Code/AnalisisIndexes.py index d3334ba..9863f10 100644 --- a/Code/AnalisisIndexes.py +++ b/Code/AnalisisIndexes.py @@ -368,7 +368,7 @@ def genIndexes(partida, elos, elosFORM, alm): # for std, tit in ((Partida.OPENING, _("Opening")), (Partida.MIDDLEGAME, _("Middle game")), (Partida.ENDGAME, _("End game"))): # if elos[None][std]: # txt += plantillaC % ( tit, int(elos[True][std]), int(elos[False][std]), int(elos[None][std])) - # + # txt += plantillaC % ("Elo WITH FORMULA", elosFORM[True][Partida.ALLGAME], elosFORM[False][Partida.ALLGAME], elosFORM[None][Partida.ALLGAME]) # for std, tit in ((Partida.OPENING, _("Opening")), (Partida.MIDDLEGAME, _("Middle game")), (Partida.ENDGAME, _("End game"))): # if elos[None][std]: diff --git a/Code/Books.py b/Code/Books.py index d72fe83..9393920 100644 --- a/Code/Books.py +++ b/Code/Books.py @@ -54,6 +54,12 @@ def porDefecto(self, book=None): return book return self.lista[0] + def buscaLibro(self, nombre): + for book in self.lista: + if book.nombre == nombre: + return book + return None + def cambiaModo(self, apli): if apli in self._modoAnalisis: self._modoAnalisis = self._modoAnalisis.replace(apli, "") @@ -231,7 +237,6 @@ def almListaJugadas(self, fen): return listaJugadas - def eligeJugadaTipo(self, fen, tipo): maxim = 0 liMax = [] @@ -276,7 +281,7 @@ def eligeJugadaTipo(self, fen, tipo): return pv.lower() - def miraListaPV(self, fen, siMax): + def miraListaPV(self, fen, siMax, onlyone=True): li = self.book.lista(self.path, fen) liResp = [] @@ -287,8 +292,8 @@ def miraListaPV(self, fen, siMax): if w > maxim: maxim = w liResp = [entry.pv()] - # elif w == maxim: - # liResp.append(entry.pv()) + elif w == maxim and not onlyone: + liResp.append(entry.pv()) else: for entry in li: liResp.append(entry.pv()) diff --git a/Code/Configuracion.py b/Code/Configuracion.py index ca2dee8..90ed3b6 100644 --- a/Code/Configuracion.py +++ b/Code/Configuracion.py @@ -792,7 +792,6 @@ def lee(self): if not os.path.isdir(self.folderOpenings): self.folderOpenings = self.folderBaseOpenings - for k in dic.keys(): if k.startswith("RIVAL_"): claveK = k[6:] diff --git a/Code/ControlPosicion.py b/Code/ControlPosicion.py index 42eb2aa..eebfb4e 100644 --- a/Code/ControlPosicion.py +++ b/Code/ControlPosicion.py @@ -345,6 +345,24 @@ def siFaltaMaterial(self): return False + def siFaltaMaterialColor(self, siBlancas): + piezas = "" + nb = "nb" + prq = "prq" + if siBlancas: + nb = nb.upper() + prq = prq.upper() + for v in self.casillas.itervalues(): + if v: + if v in prq: + return False + if v in nb: + if piezas: + return False + else: + piezas = v + return False + def numPiezas(self, pieza): if not self.siBlancas: pieza = pieza.lower() @@ -435,5 +453,58 @@ def siPeonCoronando(self, desdeA1H8, hastaA1H8): return True + def aura(self): + lista = [] + + def add(lipos): + for pos in lipos: + lista.append(LCEngine.posA1(pos)) + + def liBR(npos, fi, ci): + fil, col = LCEngine.posFC(npos) + liM = [] + ft = fil + fi + ct = col + ci + while True: + if ft < 0 or ft > 7 or ct < 0 or ct > 7: + break + t = LCEngine.FCpos(ft, ct) + liM.append(t) + + pz = self.casillas[LCEngine.posA1(t)] + if pz: + break + ft += fi + ct += ci + add(liM) + + pzs = "KQRBNP" if self.siBlancas else "kqrbnp" + + for i in range(8): + for j in range(8): + a1 = chr(i + 97) + chr(j + 49) + pz = self.casillas[a1] + if pz and pz in pzs: + pz = pz.upper() + npos = LCEngine.a1Pos(a1) + if pz == "K": + add(LCEngine.liK(npos)) + elif pz == "Q": + for f_i, c_i in ((1, 1), (1, -1), (-1, 1), (-1, -1), (1, 0), (-1, 0), (0, 1), (0, -1)): + liBR(npos, f_i, c_i) + elif pz == "R": + for f_i, c_i in ((1, 0), (-1, 0), (0, 1), (0, -1)): + liBR(npos, f_i, c_i) + elif pz == "B": + for f_i, c_i in ((1, 1), (1, -1), (-1, 1), (-1, -1)): + liBR(npos, f_i, c_i) + elif pz == "N": + add(LCEngine.liN(npos)) + elif pz == "P": + lim, lix = LCEngine.liP(npos, self.siBlancas) + add(lix) + return lista + + def distancia(desde, hasta): return ((ord(desde[0])-ord(hasta[0]))**2 + (ord(desde[1])-ord(hasta[1]))**2)**0.5 diff --git a/Code/EngineThread.py b/Code/EngineThread.py index ab301d9..724a1f1 100644 --- a/Code/EngineThread.py +++ b/Code/EngineThread.py @@ -10,7 +10,8 @@ DEBUG_ENGINE = False -def xpr(line): + +def xpr(exe, line): if DEBUG_ENGINE: t = time.time() prlk("%0.04f %s" % (t - tdbg[0], line)) @@ -29,7 +30,7 @@ def xprli(li): if DEBUG_ENGINE: tdbg = [time.time()] - xpr("DEBUG XMOTOR") + xpr("", "DEBUG XMOTOR") class Priorities: @@ -87,7 +88,7 @@ def cerrar(self): self.working = False def put_line(self, line): - assert xpr("put>>> %s\n" % line) + assert xpr(self.exe, "put>>> %s\n" % line) self.stdin_lock.acquire() self.stdin.write(line + "\n") self.stdin_lock.release() @@ -109,7 +110,7 @@ def xstdout_thread(self, stdout, lock): try: while self.working: line = stdout.readline() - assert xpr(line) + assert xpr(self.exe, line) if not line: break lock.acquire() diff --git a/Code/EnginesWindows.py b/Code/EnginesWindows.py index 8c579e1..ec7defd 100644 --- a/Code/EnginesWindows.py +++ b/Code/EnginesWindows.py @@ -137,7 +137,7 @@ def mas(cm): cm.ponMultiPV(1, 4) mas(cm) - cm = ConfigMotor("cheng", "Martin Sedlák", "4 0.39", "http://www.vlasak.biz/cheng") + cm = ConfigMotor("cheng", "Martin Sedlák".decode("utf-8"), "4 0.39", "http://www.vlasak.biz/cheng") cm.path = "cheng4.exe" cm.elo = 2750 cm.ponMultiPV(20, 256) @@ -200,7 +200,7 @@ def mas(cm): cm.ponMultiPV(20, 100) mas(cm) - cm = ConfigMotor("texel", "Peter Österlund", "1.07 32bit", "http://hem.bredband.net/petero2b/javachess/index.html#texel") + cm = ConfigMotor("texel", "Peter Österlund".decode("utf-8"), "1.07 32bit", "http://hem.bredband.net/petero2b/javachess/index.html#texel") cm.path = "texel32old.exe" cm.elo = 3100 cm.ordenUCI("Hash", "32") @@ -288,7 +288,7 @@ def mas(cm): cm.elo = 2100 mas(cm) - cm = ConfigMotor("andscacs", "Daniel José Queraltó", "0.9032n", "http://www.andscacs.com/") + cm = ConfigMotor("andscacs", "Daniel José Queraltó".decode("utf-8"), "0.9032n", "http://www.andscacs.com/") cm.path = "andscacs32.exe" cm.elo = 3150 mas(cm) @@ -308,12 +308,12 @@ def mas(cm): cm.elo = 2627 mas(cm) - cm = ConfigMotor("spike", "Volker Böhm and Ralf Schäfer", "1.4", "http://spike.lazypics.de/index_en.html") + cm = ConfigMotor("spike", "Volker Böhm and Ralf Schäfer".decode("utf-8"), "1.4", "http://spike.lazypics.de/index_en.html") cm.path = "Spike1.4.exe" cm.elo = 2921 mas(cm) - cm = ConfigMotor("zappa", "Anthony Cozzie", "1.1", "http://www.acoz.net/zappa/") + cm = ConfigMotor("zappa", "Anthony Cozzie", "1.1", "http://www.acoz.net/zappa/") cm.path = "zappa.exe" cm.elo = 2581 cm.removeLog("zappa_log.txt") diff --git a/Code/Entrenamientos.py b/Code/Entrenamientos.py index 9cb6e51..fab613c 100644 --- a/Code/Entrenamientos.py +++ b/Code/Entrenamientos.py @@ -140,7 +140,6 @@ def xopcion(menu, clave, texto, icono, siDeshabilitado=False): xopcion(menu, "wgm", _("Play like a Woman Grandmaster"), Iconos.WGranMaestro()) menu.separador() - # Mate -------------------------------------------------------------------------------------------------- menu1 = menu.submenu(_("Training mates"), Iconos.Mate()) for mate in range(1, 5): @@ -190,7 +189,6 @@ def menuTacticas(submenu, tipo, carpetaBase, lista): menuTacticas(submenu1, tipo, carpeta, lista) return lista - menuTacticas(menu1, "B", "Tactics", []) lista = [] carpetaTacticasP = os.path.join(self.configuracion.dirPersonalTraining, "Tactics") @@ -718,4 +716,4 @@ def xopcion(menu, clave, texto, icono, siDeshabilitado=False): td.reduce() td.menu(menu, xopcion) resp = menu.lanza() - return resp if resp is None else resp[3:] \ No newline at end of file + return resp if resp is None else resp[3:] diff --git a/Code/Gestor.py b/Code/Gestor.py index 2c1717d..c27dbd6 100644 --- a/Code/Gestor.py +++ b/Code/Gestor.py @@ -9,6 +9,7 @@ from Code import AnalisisIndexes from Code import AperturasStd from Code import ControlPGN +from Code import ControlPosicion from Code import DGT from Code import Jugada from Code import Partida @@ -28,6 +29,7 @@ from Code.QT import QTUtil2 from Code.QT import QTVarios from Code.QT import WOpeningGuide +from Code.QT import TabTipos from Code import Util from Code import VarGen from Code import Kibitzers @@ -285,11 +287,12 @@ def showCandidates(): if not self.configuracion.siAtajosRaton: if li_destinos: self.atajosRatonOrigen = a1h8 - if self.atajosRatonDestino and self.atajosRatonDestino in li_destinos: - mueve() - else: - self.atajosRatonDestino = None - showCandidates() + self.atajosRatonDestino = None + # if self.atajosRatonDestino and self.atajosRatonDestino in li_destinos: + # mueve() + # else: + # self.atajosRatonDestino = None + showCandidates() return elif li_origenes: self.atajosRatonDestino = a1h8 @@ -297,6 +300,7 @@ def showCandidates(): mueve() else: self.atajosRatonOrigen = None + self.atajosRatonDestino = None showCandidates() return @@ -520,6 +524,8 @@ def miraKibitzers(self, jg, columnaClave, soloNuevo=False): def paraKibitzers(self): for n, xkibitzer in enumerate(self.liKibitzersActivas): xkibitzer.terminar() #ponFen(None) + self.procesador.quitaKibitzers() + self.liKibitzersActivas = [] def ponPiezasAbajo(self, siBlancas): self.tablero.ponerPiezasAbajo(siBlancas) @@ -829,7 +835,6 @@ def gridRightMouse(self, siShift, siControl, siAlt): self.pgnInformacion() self.pantalla.ajustaTam() - def listado(self, tipo): if tipo == "pgn": return self.pgn.actual() @@ -899,7 +904,7 @@ def cambioTutor(self): self.siAnalizadoTutor = False if self.tipoJuego == kJugEntMaq: - self.analizaTutorInicio() + self.analizaInicio() def siTerminada(self): return self.partida.ultPosicion.siTerminada() @@ -1054,6 +1059,44 @@ def exePulsadoNum(self, siActivar, numero): if self.tablero.flechaSC: self.tablero.flechaSC.show() + elif numero in [2, 7]: + if siActivar: + # Que jugada esta en el tablero + fen = self.fenActivoConInicio() + siBlancas = " w " in fen + if numero == 2: + siMB = siBlancas + else: + siMB = not siBlancas + if siMB != siBlancas: + fen = LCEngine.fenOB(fen) + cp = ControlPosicion.ControlPosicion() + cp.leeFen(fen) + liMovs = cp.aura() + + self.liMarcosTmp = [] + regMarco = TabTipos.Marco() + color = self.tablero.confTablero.flechaActivoDefecto().colorinterior + if color == -1: + color = self.tablero.confTablero.flechaActivoDefecto().color + + st = set() + for h8 in liMovs: + if h8 not in st: + regMarco.a1h8 = h8 + h8 + regMarco.siMovible = True + regMarco.color = color + regMarco.colorinterior = color + regMarco.opacidad = 0.5 + marco = self.tablero.creaMarco(regMarco) + self.liMarcosTmp.append(marco) + st.add(h8) + + else: + for marco in self.liMarcosTmp: + self.tablero.xremoveItem(marco) + self.liMarcosTmp = [] + def exePulsadaLetra(self, siActivar, letra): if siActivar: dic = { 'a':kMoverInicio, @@ -1296,7 +1339,6 @@ def configurar(self, liMasOpciones=None, siCambioTutor=False, siSonidos=False, s modoPosicionBlind = True self.tablero.blindfoldChange(modoPosicionBlind) - elif orden == "conf": self.tablero.blindfoldConfig() @@ -1505,6 +1547,11 @@ def utilidades(self, liMasOpciones=None, siArbol=True): return None + def mensajeEnPGN(self, mens, titulo=None): + p0 = self.pantalla.base.pgn.pos() + p = self.pantalla.mapToGlobal(p0) + QTUtil2.mensajeEnPunto(self.pantalla, mens, titulo, p) + def showAnalisis(self): um = self.procesador.unMomento() elos = self.partida.calc_elos(self.configuracion) diff --git a/Code/GestorAlbum.py b/Code/GestorAlbum.py index b3ed1c1..0e18186 100644 --- a/Code/GestorAlbum.py +++ b/Code/GestorAlbum.py @@ -283,7 +283,7 @@ def ponResultado(self, quien): self.resultado = kTablas self.guardarGanados(quien == kGanamos) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() self.xrival.cerrar() self.pantalla.ponToolBar((k_mainmenu, k_configurar, k_utilidades)) diff --git a/Code/GestorBooks.py b/Code/GestorBooks.py index 77edaa9..f90398d 100644 --- a/Code/GestorBooks.py +++ b/Code/GestorBooks.py @@ -3,7 +3,6 @@ from Code import Gestor from Code import Jugada from Code.QT import PantallaBooks -from Code.QT import QTUtil2 from Code import XMotorRespuesta from Code.Constantes import * @@ -411,5 +410,4 @@ def ponResultado(self): txt = self.txtAciertos() mensaje = "%s\n%s\n" % (_("Line completed"), txt) - - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) diff --git a/Code/GestorCompeticion.py b/Code/GestorCompeticion.py index 5c3a09c..6dd88e0 100644 --- a/Code/GestorCompeticion.py +++ b/Code/GestorCompeticion.py @@ -418,5 +418,5 @@ def ponResultado(self, quien): self.resultado = kTablas self.guardarGanados(quien == kGanamos) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() diff --git a/Code/GestorElo.py b/Code/GestorElo.py index a0d4712..dceaa25 100644 --- a/Code/GestorElo.py +++ b/Code/GestorElo.py @@ -619,9 +619,9 @@ def ponResultado(self, quien): # mensaje = _X( _("Congratulations, you win against %1 on time."), nombreContrario ) # self.resultado = kGanamos - elif quien == kGanaRivalTiempo: - mensaje = _X(_("%1 has won on time."), nombreContrario) - self.resultado = kGanaRival + # elif quien == kGanaRivalTiempo: + # mensaje = _X(_("%1 has won on time."), nombreContrario) + # self.resultado = kGanaRival elo = self.configuracion.eloActivo(self.siCompetitivo) if self.resultado == kGanamos: @@ -646,7 +646,7 @@ def ponResultado(self, quien): mensaje += "

%s : %d
" % (_("New Lucas-Elo"), self.configuracion.eloActivo(self.siCompetitivo)) self.guardarGanados(quien == kGanamos) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() # def ponFinJuego( self ): diff --git a/Code/GestorEntMaq.py b/Code/GestorEntMaq.py index 2602bb7..9c11a92 100644 --- a/Code/GestorEntMaq.py +++ b/Code/GestorEntMaq.py @@ -659,7 +659,6 @@ def juegaHumano(self, siBlancas): self.timekeeper.start() self.activaColor(siBlancas) - def juegaRival(self): self.pensando(True) self.desactivaTodas() @@ -1031,18 +1030,22 @@ def ponResultado(self, quien): self.resultado = kTablas elif quien == kGanamosTiempo: + if self.partida.ultPosicion.siFaltaMaterialColor(self.siJugamosConBlancas): + return self.ponResultado(kTablasFaltaMaterial) mensaje = _X(_("Congratulations, you win %1 on time."), nombreContrario) self.resultado = kGanamos self.partida.last_jg().comentario = _X(_("%1 has won on time."), self.configuracion.jugador) elif quien == kGanaRivalTiempo: + if self.partida.ultPosicion.siFaltaMaterialColor(not self.siJugamosConBlancas): + return self.ponResultado(kTablasFaltaMaterial) mensaje = _X(_("%1 has won on time."), nombreContrario) self.resultado = kGanaRival self.partida.last_jg().comentario = _X(_("%1 has won on time."), nombreContrario) self.guardarGanados(quien == kGanamos) if quien != kGanaRivalTiempo: # Ya que el mensaje ya se ha usado, para a_adir minutos - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) if QTUtil2.pregunta(self.pantalla, _("Do you want to play again?")): self.reiniciar(False) else: diff --git a/Code/GestorEntPos.py b/Code/GestorEntPos.py index 5e06db9..2acb5e2 100644 --- a/Code/GestorEntPos.py +++ b/Code/GestorEntPos.py @@ -35,7 +35,7 @@ def guardaPosicion(self, posEntreno): db[self.entreno] = data db.close() - def inicio(self, posEntreno, numEntrenos, titEntreno, liEntrenos, siTutorActivado=None, jump=False): + def inicio(self, posEntreno, numEntrenos, titEntreno, liEntrenos, siTutorActivado=None, saltoAutomatico=False): if hasattr(self, "reiniciando"): if self.reiniciando: return @@ -49,7 +49,7 @@ def inicio(self, posEntreno, numEntrenos, titEntreno, liEntrenos, siTutorActivad self.numEntrenos = numEntrenos self.titEntreno = titEntreno self.liEntrenos = liEntrenos - self.jump = jump + self.saltoAutomatico = saltoAutomatico self.liHistorico = [self.posEntreno] @@ -238,7 +238,7 @@ def procesarAccion(self, clave): def reiniciar(self): if self.rivalPensando: return - self.inicio(self.posEntreno, self.numEntrenos, self.titEntreno, self.liEntrenos, self.siTutorActivado, self.jump) + self.inicio(self.posEntreno, self.numEntrenos, self.titEntreno, self.liEntrenos, self.siTutorActivado, self.saltoAutomatico) def ent_siguiente(self, tipo): if not (self.siJuegaHumano or self.estado == kFinJuego): @@ -248,7 +248,7 @@ def ent_siguiente(self, tipo): pos = 1 elif pos == 0: pos = self.numEntrenos - self.inicio(pos, self.numEntrenos, self.titEntreno, self.liEntrenos, self.siTutorActivado, self.jump) + self.inicio(pos, self.numEntrenos, self.titEntreno, self.liEntrenos, self.siTutorActivado, self.saltoAutomatico) def controlTeclado(self, nkey): if nkey in (Qt.Key_Plus, Qt.Key_PageDown): @@ -287,7 +287,7 @@ def atras(self): def siguienteJugada(self): if self.estado == kFinJuego: - if self.siDirigido and self.jump: + if self.siDirigido and self.saltoAutomatico: self.ent_siguiente(k_siguiente) return @@ -396,7 +396,7 @@ def sigue(self): def lineaTerminadaOpciones(self): self.estado = kFinJuego - if self.jump: + if self.saltoAutomatico: self.ent_siguiente(k_siguiente) return False else: @@ -719,9 +719,7 @@ def createTactics(self): Util.dic8ini(nomIni, dicIni) - QTUtil2.mensaje(self.pantalla, _X(_("Tactic training %1 created."), nomDir) + "
" + - _X(_( - "You can access this training from menu Trainings-Learn tactics by repetition-%1"), - nomDir)) + self.mensajeEnPGN(_X(_("Tactic training %1 created."), nomDir) + "
" + + _X(_("You can access this training from menu Trainings-Learn tactics by repetition-%1"), nomDir)) self.procesador.entrenamientos.rehaz() diff --git a/Code/GestorEverest.py b/Code/GestorEverest.py index 989288b..670c8bf 100644 --- a/Code/GestorEverest.py +++ b/Code/GestorEverest.py @@ -314,6 +314,6 @@ def ponResultado(self): mensaje = _("Congratulations you have passed this game.") self.expedition.add_try(True, self.tiempo, self.puntos) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.terminar() diff --git a/Code/GestorFideFics.py b/Code/GestorFideFics.py index ee7ef5a..27f2dca 100644 --- a/Code/GestorFideFics.py +++ b/Code/GestorFideFics.py @@ -459,7 +459,7 @@ def ponResultado(self): mensaje += "

%s : %d
" % (self._newTitulo, self._activo(self.siCompetitivo)) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() # def ponFinJuego( self ): diff --git a/Code/GestorGM.py b/Code/GestorGM.py index 218f87d..5d3be6d 100644 --- a/Code/GestorGM.py +++ b/Code/GestorGM.py @@ -419,7 +419,8 @@ def ponResultado(self): if self.siJuez: mensaje += "

%s = %+d
" % (_("Points accumulated"), self.puntos) - QTUtil2.mensaje(self.pantalla, mensaje, siResalta=False) + # QTUtil2.mensaje(self.pantalla, mensaje, siResalta=False) + self.mensajeEnPGN(mensaje) dbHisto = Util.DicSQL(self.configuracion.ficheroGMhisto) diff --git a/Code/GestorMate.py b/Code/GestorMate.py index 8f7a535..c2d1fdd 100644 --- a/Code/GestorMate.py +++ b/Code/GestorMate.py @@ -360,7 +360,7 @@ def siguienteMate(self): if siRecord: txt += "

%s

" % _("Congratulations you have achieved a new record in this block.") - QTUtil2.mensaje(self.pantalla, txt) + self.mensajeEnPGN(txt) self.finJuego() else: diff --git a/Code/GestorMateMap.py b/Code/GestorMateMap.py index 2d9d324..9ceb8e1 100644 --- a/Code/GestorMateMap.py +++ b/Code/GestorMateMap.py @@ -2,7 +2,6 @@ from Code import Gestor from Code import Jugada from Code.QT import QTUtil -from Code.QT import QTUtil2 from Code.Constantes import * @@ -244,7 +243,7 @@ def ponResultado(self, quien): mensaje = _("Congratulations you have won %s.") % self.workmap.nameAim() self.workmap.winAim(self.partida.pv()) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.desactivaTodas() self.refresh() diff --git a/Code/GestorMicElo.py b/Code/GestorMicElo.py index cdf827a..a859337 100644 --- a/Code/GestorMicElo.py +++ b/Code/GestorMicElo.py @@ -208,7 +208,8 @@ def inicio(self, datosMotor, minutos, segundos, siCompetitivo, aplazamiento=None self.ponPosicionDGT() if not self.siJugamosConBlancas: - QTUtil2.mensaje(self.pantalla, "Press the continue button to start.") + mensaje = _("Press the continue button to start.") + self.mensajeEnPGN(mensaje) self.siguienteJugada() @@ -480,10 +481,14 @@ def ponResultado(self, quien): self.resultado = kTablas elif quien == kGanamosTiempo: + if self.partida.ultPosicion.siFaltaMaterialColor(self.siJugamosConBlancas): + return self.ponResultado(kTablasFaltaMaterial) mensaje = _X(_("Congratulations, you win against %1 on time."), nombreContrario) self.resultado = kGanamos elif quien == kGanaRivalTiempo: + if self.partida.ultPosicion.siFaltaMaterialColor(not self.siJugamosConBlancas): + return self.ponResultado(kTablasFaltaMaterial) mensaje = _X(_("%1 has won on time."), nombreContrario) self.resultado = kGanaRival @@ -524,7 +529,7 @@ def ponResultado(self, quien): self.guardarGanados(quien == kGanamos) self.puestoResultado = True - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() def historial(self, elo, nelo): diff --git a/Code/GestorOpeningLines.py b/Code/GestorOpeningLines.py index cfabcb9..59c7bb2 100644 --- a/Code/GestorOpeningLines.py +++ b/Code/GestorOpeningLines.py @@ -13,6 +13,375 @@ from Code.Constantes import * +class GestorOpeningEngines(Gestor.Gestor): + def inicio(self, pathFichero): + self.tablero.saveVisual() + self.pathFichero = pathFichero + dbop = OpeningLines.Opening(pathFichero) + self.tablero.dbVisual_setFichero(dbop.nomFichero) + self.reinicio(dbop) + + def reinicio(self, dbop): + self.dbop = dbop + self.dbop.open_cache_engines() + self.tipoJuego = kJugOpeningLines + + self.level = self.dbop.getconfig("ENG_LEVEL", 0) + self.numengine = self.dbop.getconfig("ENG_ENGINE", 0) + + self.trainingEngines = self.dbop.trainingEngines() + + liTimes = self.trainingEngines["TIMES"] + liEngines = self.trainingEngines["ENGINES"] + num_engines = len(liEngines) + if self.numengine >= num_engines: + self.level += 1 + self.numengine = 0 + self.dbop.setconfig("ENG_LEVEL", self.level) + self.dbop.setconfig("ENG_ENGINE", 0) + num_levels = len(liTimes) + if self.level >= num_levels: + if QTUtil2.pregunta(self.pantalla, "%s.\n%s" % (_("Training finished"), _("Do you want to reinit?"))): + self.dbop.setconfig("ENG_LEVEL", 0) + self.dbop.setconfig("ENG_ENGINE", 0) + self.reinicio(dbop) + return + + self.time = liTimes[self.level] + self.keyengine = liEngines[self.numengine] + + self.plies_mandatory = self.trainingEngines["MANDATORY"] + self.plies_control = self.trainingEngines["CONTROL"] + self.lost_points = self.trainingEngines["LOST_POINTS"] + + self.siJugamosConBlancas = self.trainingEngines["COLOR"] == "WHITE" + self.siRivalConBlancas = not self.siJugamosConBlancas + + self.siAprobado = False + + rival = self.configuracion.buscaRival(self.keyengine) + self.xrival = self.procesador.creaGestorMotor(rival, self.time, None) + self.xrival.siBlancas = self.siRivalConBlancas + + juez = self.configuracion.buscaRival(self.trainingEngines["ENGINE_CONTROL"]) + self.xjuez = self.procesador.creaGestorMotor(juez, int(self.trainingEngines["ENGINE_TIME"] * 1000), None) + self.xjuez.anulaMultiPV() + + self.li_info = [ + "%s: %d/%d - %s" % (_("Engine"), self.numengine+1, num_engines, self.xrival.nombre), + "%s: %d/%d - %0.1f\"" % (_("Level"), self.level + 1, num_levels, self.time / 1000.0), + ] + + self.dicFENm2 = self.trainingEngines["DICFENM2"] + + self.siAyuda = False + self.tablero.dbVisual_setShowAllways(False) + self.ayudas = 9999 # Para que analice sin problemas + + self.partida = Partida.Partida() + + self.pantalla.ponToolBar((k_mainmenu, k_abandonar, k_reiniciar)) + self.pantalla.activaJuego(True, False, siAyudas=False) + self.ponMensajero(self.mueveHumano) + self.ponPosicion(self.partida.ultPosicion) + self.mostrarIndicador(True) + self.quitaAyudas() + self.ponPiezasAbajo(self.siJugamosConBlancas) + self.pgnRefresh(True) + + self.ponCapInfoPorDefecto() + + self.estado = kJugando + + self.ponPosicionDGT() + + self.errores = 0 + self.ini_time = time.time() + self.muestraInformacion() + self.siguienteJugada() + + def siguienteJugada(self): + self.muestraInformacion() + if self.estado == kFinJuego: + return + + self.estado = kJugando + + self.siJuegaHumano = False + self.ponVista() + + siBlancas = self.partida.ultPosicion.siBlancas + + self.ponIndicador(siBlancas) + self.refresh() + + siRival = siBlancas == self.siRivalConBlancas + + if siRival: + self.desactivaTodas() + + if not self.runcontrol(): + if self.mueveRival(): + self.siguienteJugada() + + else: + self.activaColor(siBlancas) + self.siJuegaHumano = True + + def mueveRival(self): + si_obligatorio = self.partida.numJugadas() <= self.plies_mandatory + si_pensar = True + fenM2 = self.partida.ultPosicion.fenM2() + if si_obligatorio: + moves = self.dicFENm2.get(fenM2, set()) + nmoves = len(moves) + if nmoves == 0: + si_obligatorio = False + elif nmoves == 1: + move = list(moves)[0] + desde, hasta, coronacion = move[:2], move[2:4], move[4:] + si_pensar = False + + if si_pensar: + move = self.dbop.get_cache_engines(self.keyengine, self.time, fenM2) + if move is None: + rmRival = self.xrival.juegaPartida(self.partida) + move = rmRival.movimiento() + self.dbop.set_cache_engines(self.keyengine, self.time, fenM2, move) + desde, hasta, coronacion = move[:2], move[2:4], move[4:] + if si_obligatorio: + if move not in moves: + move = list(moves)[0] + desde, hasta, coronacion = move[:2], move[2:4], move[4:] + + siBien, mens, jg = Jugada.dameJugada(self.partida.ultPosicion, desde, hasta, coronacion) + if siBien: + self.partida.ultPosicion = jg.posicion + + self.masJugada(jg, False) + self.movimientosPiezas(jg.liMovs, True) + + self.error = "" + + return True + else: + self.error = mens + return False + + def mueveHumano(self, desde, hasta, coronacion=""): + jg = self.checkMueveHumano(desde, hasta, coronacion) + if not jg: + return False + + fenM2 = self.partida.ultPosicion.fenM2() + moves = self.dicFENm2.get(fenM2, []) + nmoves = len(moves) + if nmoves > 0: + if jg.movimiento() not in moves: + for move in moves: + self.tablero.creaFlechaMulti(move, False) + self.tablero.creaFlechaMulti(jg.movimiento(), True) + self.mensajeEnPGN(_("This is not the move in the opening lines, you must repeat the game")) + self.ponFinJuego() + return True + + self.movimientosPiezas(jg.liMovs) + + self.masJugada(jg, True) + self.siguienteJugada() + return True + + def masJugada(self, jg, siNuestra): + fenM2 = jg.posicionBase.fenM2() + if fenM2 in self.dicFENm2: + if jg.movimiento() in self.dicFENm2[fenM2]: + jg.criticaDirecta = "!" + self.partida.append_jg(jg) + if self.partida.pendienteApertura: + self.partida.asignaApertura() + + self.ponFlechaSC(jg.desde, jg.hasta) + self.beepExtendido(siNuestra) + + self.pgnRefresh(self.partida.ultPosicion.siBlancas) + self.refresh() + + self.ponPosicionDGT() + + def muestraInformacion(self): + li = [] + li.extend(self.li_info) + + si_obligatorio = self.partida.numJugadas() < self.plies_mandatory + if si_obligatorio and self.estado != kFinJuego: + fenM2 = self.partida.ultPosicion.fenM2() + moves = self.dicFENm2.get(fenM2, []) + if len(moves) > 0: + li.append( "%s: %d/%d" % (_("Mandatory move"), self.partida.numJugadas(), self.plies_mandatory)) + else: + si_obligatorio = False + + if not si_obligatorio and self.estado != kFinJuego: + tm = self.plies_mandatory + self.plies_control - self.partida.numJugadas() + if tm > 0: + li.append("%s: %d" % (_("Moves until the control"), tm)) + + self.ponRotulo1("
".join(li)) + + def runcontrol(self): + numJugadas = self.partida.numJugadas() + if numJugadas == 0: + return False + + def aprobado(): + mens = "%s" % _("Congratulations, goal achieved") + self.li_info.append("") + self.li_info.append(mens) + self.muestraInformacion() + self.dbop.setconfig("ENG_ENGINE", self.numengine + 1) + self.mensajeEnPGN(mens) + self.siAprobado = True + + def suspendido(): + mens = "%s" % _("You must repeat the game") + self.li_info.append("") + self.li_info.append(mens) + self.muestraInformacion() + self.mensajeEnPGN(mens) + + def calcula(fen): + um = self.unMomento() + mrm = self.xjuez.analiza(fen) + um.final() + rm = mrm.mejorMov() + return rm.puntosABS(), rm.mate + + if self.partida.siTerminada(): + self.ponFinJuego() + jg = self.partida.jugada(-1) + if jg.siJaqueMate: + if jg.siBlancas() == self.siJugamosConBlancas: + aprobado() + else: + suspendido() + self.ponFinJuego() + return True + puntosFinal, mateFinal = 0, 0 + + else: + if numJugadas < self.plies_mandatory + self.plies_control: + return False + # Si la ultima jugada es de la linea no se calcula nada + jg = self.partida.jugada(-1) + if jg.criticaDirecta == "!": + puntosFinal, mateFinal = 0, 0 + else: + puntosFinal, mateFinal = calcula(self.partida.ultPosicion.fen()) + puntosFinal, mateFinal = -puntosFinal, -mateFinal + + # Se marcan todas las jugadas que no siguen las lineas + # Y se busca la ultima del color del jugador + fenM2_inicial = None + for njg in range(numJugadas): + jg = self.partida.jugada(njg) + fenM2 = jg.posicionBase.fenM2() + if fenM2 in self.dicFENm2: + moves = self.dicFENm2[fenM2] + if jg.movimiento() not in moves: + jg.criticaDirecta = "?!" + if fenM2_inicial is None and jg.siBlancas() == self.siJugamosConBlancas: + fenM2_inicial = jg.posicion.fenM2() + elif fenM2_inicial is None and jg.siBlancas() == self.siJugamosConBlancas: + fenM2_inicial = jg.posicion.fenM2() + if fenM2_inicial: + puntosInicio, mateInicio = calcula(fenM2_inicial) + puntosInicio, mateInicio = -puntosInicio, -mateInicio + else: + puntosInicio, mateInicio = 0, 0 + + self.li_info.append("%s:" %_("Score")) + template = "    %s: %d" + def appendInfo(label, puntos, mate): + mens = template % (label, puntos) + if mate: + mens += " %s %d" % (_("Mate"), mate) + self.li_info.append(mens) + appendInfo(_("Start"), puntosInicio, mateInicio) + appendInfo(_("End"), puntosFinal, mateFinal) + perdidos = (puntosInicio-puntosFinal) + ok = perdidos < self.lost_points + mens = template % ("(%d)-(%d)" %(puntosInicio, puntosFinal), perdidos) + mens = "%s %s %d" %(mens, "<" if ok else ">", self.lost_points) + self.li_info.append(mens) + + if perdidos > self.lost_points: + suspendido() + else: + aprobado() + self.ponFinJuego() + return True + + def procesarAccion(self, clave): + if clave == k_mainmenu: + self.finPartida() + + elif clave in (k_reiniciar, k_siguiente): + self.reiniciar() + + elif clave == k_peliculaRepetir: + self.dbop.setconfig("ENG_ENGINE", self.numengine) + self.reiniciar() + + elif clave == k_abandonar: + self.ponFinJuego() + + elif clave == k_configurar: + self.configurar(siSonidos=True) + + elif clave == k_utilidades: + liMasOpciones = [] + liMasOpciones.append(("libros", _("Consult a book"), Iconos.Libros())) + if self.siAprobado: + liMasOpciones.append((None, None, None)) + liMasOpciones.append(("add_line", _("Add this line"), Iconos.OpeningLines())) + + resp = self.utilidades(liMasOpciones) + if resp == "libros": + self.librosConsulta(False) + elif resp == "add_line": + self.dbop.append(self.partida) + self.dbop.updateTrainingEngines() + + else: + Gestor.Gestor.rutinaAccionDef(self, clave) + + def finalX(self): + return self.finPartida() + + def finPartida(self): + self.dbop.close() + self.tablero.restoreVisual() + self.procesador.inicio() + self.procesador.openings() + return False + + def reiniciar(self): + self.reinicio(self.dbop) + + def ponFinJuego(self): + self.estado = kFinJuego + self.desactivaTodas() + liOpciones = [k_mainmenu] + if self.siAprobado: + liOpciones.append(k_siguiente) + liOpciones.append(k_peliculaRepetir) + else: + liOpciones.append(k_reiniciar) + liOpciones.append(k_configurar) + liOpciones.append(k_utilidades) + self.pantalla.ponToolBar(liOpciones) + + class GestorOpeningLines(Gestor.Gestor): def inicio(self, pathFichero, modo, num_linea): self.tablero.saveVisual() @@ -125,7 +494,6 @@ def muestraInformacion(self): mens3 += "\n %s" % dicNAGs[valoracion] self.ponRotulo3(mens3 if mens3 else None) - def partidaTerminada(self, siCompleta): self.estado = kFinJuego tm = time.time() - self.ini_time @@ -136,8 +504,8 @@ def partidaTerminada(self, siCompleta): li.append("%s: %d" % (_("Errors"), self.errores)) if siCompleta: - QTUtil2.mensaje(self.pantalla, "\n".join(li)) - + mensaje = "\n".join(li) + self.mensajeEnPGN(mensaje) dictry = { "DATE": Util.hoy(), "TIME": tm, @@ -194,7 +562,7 @@ def procesarAccion(self, clave): if clave == k_mainmenu: self.finPartida() - elif clave == k_reiniciar : + elif clave == k_reiniciar: self.reiniciar() elif clave == k_configurar: @@ -357,6 +725,7 @@ def reinicio(self, dbop): ] self.siAyuda = False + self.siSaltoAutomatico = True cp = ControlPosicion.ControlPosicion() cp.leeFen(self.trposition["FENM2"] + " 0 1") @@ -368,7 +737,7 @@ def reinicio(self, dbop): self.siJugamosConBlancas = self.training["COLOR"] == "WHITE" self.siRivalConBlancas = not self.siJugamosConBlancas - self.pantalla.ponToolBar((k_mainmenu, k_ayuda, k_reiniciar)) + self.pantalla.ponToolBar((k_mainmenu, k_ayuda, k_configurar)) self.pantalla.activaJuego(True, False, siAyudas=False) self.ponMensajero(self.mueveHumano) self.ponPosicion(cp) @@ -392,7 +761,7 @@ def reinicio(self, dbop): def ayuda(self): self.siAyuda = True - self.pantalla.ponToolBar((k_mainmenu, k_reiniciar)) + self.pantalla.ponToolBar((k_mainmenu, k_configurar)) self.muestraAyuda() self.muestraInformacion() @@ -419,13 +788,17 @@ def muestraInformacion(self): def posicionTerminada(self): tm = time.time() - self.ini_time - li = [_("Finished.")] - if self.siAyuda: - li.append(_("Help activated")) - if self.errores > 0: - li.append("%s: %d" % (_("Errors"), self.errores)) - QTUtil2.mensajeTemporal(self.pantalla, "\n".join(li), 1.2) + siSalta = self.siSaltoAutomatico and self.errores == 0 and self.siAyuda == False + + if not siSalta: + li = [_("Finished.")] + if self.siAyuda: + li.append(_("Help activated")) + if self.errores > 0: + li.append("%s: %d" % (_("Errors"), self.errores)) + + QTUtil2.mensajeTemporal(self.pantalla, "\n".join(li), 1.2) dictry = { "DATE": Util.hoy(), @@ -454,11 +827,13 @@ def posicionTerminada(self): if numPosics > salto: liNuevo.extend(self.liTrainPositions[salto:]) self.training["LITRAINPOSITIONS"] = liNuevo - self.pantalla.ponToolBar((k_mainmenu, k_siguiente)) + self.pantalla.ponToolBar((k_mainmenu, k_siguiente, k_configurar)) self.dbop.setTraining(self.training) self.estado = kFinJuego self.muestraInformacion() + if siSalta: + self.reinicio(self.dbop) def muestraAyuda(self): liMoves = self.trposition["MOVES"] @@ -469,11 +844,16 @@ def procesarAccion(self, clave): if clave == k_mainmenu: self.finPartida() - elif clave == k_reiniciar : - self.reiniciar() - elif clave == k_configurar: - self.configurar(siSonidos=True) + base = _("What to do after solving") + if self.siSaltoAutomatico: + liMasOpciones = [("lmo_stop", "%s: %s" % (base, _("Stop")), Iconos.PuntoRojo())] + else: + liMasOpciones = [("lmo_jump", "%s: %s" % (base, _("Jump to the next")), Iconos.PuntoVerde())] + + resp = self.configurar(siSonidos=True, siCambioTutor=False, liMasOpciones=liMasOpciones) + if resp in ("lmo_stop", "lmo_jump"): + self.siSaltoAutomatico = resp == "lmo_jump" elif clave == k_utilidades: self.utilidades() @@ -496,9 +876,6 @@ def finPartida(self): self.procesador.openings() return False - def reiniciar(self): - self.reinicio(self.dbop) - def siguienteJugada(self): self.muestraInformacion() if self.estado == kFinJuego: @@ -551,4 +928,4 @@ def masJugada(self, jg, siNuestra): self.pgnRefresh(self.partida.ultPosicion.siBlancas) self.refresh() - self.ponPosicionDGT() \ No newline at end of file + self.ponPosicionDGT() diff --git a/Code/GestorPGN.py b/Code/GestorPGN.py index ba24e05..4f5b988 100644 --- a/Code/GestorPGN.py +++ b/Code/GestorPGN.py @@ -4,7 +4,6 @@ import sys import random - from Code import Gestor from Code import PGN from Code import Partida @@ -189,7 +188,6 @@ def miniatura(self): self.pgnPaste = txt self.mostrar(pgn, False) - def mostrar(self, pgn, siRepiteFichero, siBlancas=None): self.pensando(True) self.partida.leeOtra(pgn.partida) diff --git a/Code/GestorPlayPGN.py b/Code/GestorPlayPGN.py index 2c60080..c5f9ee0 100644 --- a/Code/GestorPlayPGN.py +++ b/Code/GestorPlayPGN.py @@ -302,7 +302,7 @@ def ponResultado(self): self.beepResultado(quien) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.ponFinJuego() self.guardar() diff --git a/Code/GestorResistance.py b/Code/GestorResistance.py index bd6ae69..345b068 100644 --- a/Code/GestorResistance.py +++ b/Code/GestorResistance.py @@ -237,7 +237,7 @@ def finJuego(self, siFinPartida): txt = "

%s

" % (_X(_("You have lost %1 points."), str(-self.puntosRival))) if siFinPartida: - QTUtil2.mensaje(self.pantalla, txt) + self.mensajeEnPGN(txt) else: resp = QTUtil2.pregunta(self.pantalla, txt + "
%s" % (_("Do you want to resign or continue playing?")), diff --git a/Code/GestorRoutes.py b/Code/GestorRoutes.py index d3ef4a1..4aaac48 100644 --- a/Code/GestorRoutes.py +++ b/Code/GestorRoutes.py @@ -333,9 +333,10 @@ def lineaTerminada(self): if siwin: if self.route.end_playing(): - QTUtil2.mensaje(self.pantalla, _("Congratulations, you have completed the game.")) + mensaje = _("Congratulations, you have completed the game.") else: - QTUtil2.mensaje(self.pantalla, _("Well done")) + mensaje = _("Well done") + self.mensajeEnPGN(mensaje) else: if self.must_win: QTUtil2.mensError(self.pantalla, _("You must win to pass this step.")) @@ -594,14 +595,17 @@ def lineaTerminada(self): jgUlt = self.partida.last_jg() if jgUlt.siTablas(): - QTUtil2.mensaje(self.pantalla, _("Draw") + "
" + _("You must repeat the puzzle.")) + mensaje = "%s
%s" % (_("Draw"), _("You must repeat the puzzle.")) + self.mensajeEnPGN(mensaje) self.inicio(self.route) elif self.warnings <= self.max_warnings: self.pantalla.ponToolBar([k_mainmenu, k_utilidades]) - QTUtil2.mensaje(self.pantalla, _("Done")) + self.mensajeEnPGN(_("Done")) self.route.end_ending() else: - QTUtil2.mensaje(self.pantalla, _("Done with errors.") + "
" + _("You must repeat the puzzle.")) + QTUtil2.mensaje(self.pantalla, ) + mensaje = "%s
%s" % (_("Done with errors."), _("You must repeat the puzzle.")) + self.mensajeEnPGN(mensaje) self.inicio(self.route) def actualPGN(self): @@ -770,7 +774,8 @@ def lineaTerminada(self): self.refresh() km = self.route.end_tactic() if not self.route.go_fast: - QTUtil2.mensaje(self.pantalla, _("Done") + "
" + _("You have traveled %s") % Routes.km_mi(km, self.route.is_miles)) + mensaje = "%s
%s" % (_("Done"), _("You have traveled %s") % Routes.km_mi(km, self.route.is_miles)) + self.mensajeEnPGN(mensaje) self.siJuegaHumano = False self.estado = kFinJuego if self.route.go_fast: diff --git a/Code/GestorTacticas.py b/Code/GestorTacticas.py index dcc8231..d53ab8d 100644 --- a/Code/GestorTacticas.py +++ b/Code/GestorTacticas.py @@ -30,6 +30,8 @@ def inicio(self, tactica, posSiguiente=None): self.numPosiciones = self.tactica.numPosiciones() self.posActual = self.tactica.posActual() + self.siError = False + numEnt = liOrden[self.posActual] self.siPenalizable = True @@ -37,6 +39,8 @@ def inicio(self, tactica, posSiguiente=None): self.pointView = self.tactica.pointView() + self.siSaltoAutomatico = self.tactica.siSaltoAutomatico() + txtEntreno = self.tactica.unFNS(numEnt) if posSiguiente is None: @@ -141,7 +145,7 @@ def inicio(self, tactica, posSiguiente=None): else: self.siShowText = len(etiDirigido) == 0 - liOpciones = [k_mainmenu] + liOpciones = [k_mainmenu, k_configurar] if not self.siShowText: liOpciones.append(k_showtext) if self.dicEtiquetasPGN: @@ -203,7 +207,16 @@ def procesarAccion(self, clave): self.lanzaVariantes() elif clave == k_configurar: - self.configurar(siSonidos=True, siCambioTutor=False) + base = _("What to do after solving") + if self.siSaltoAutomatico: + liMasOpciones = [("lmo_stop", "%s: %s" %(base, _("Stop")), Iconos.PuntoRojo())] + else: + liMasOpciones = [("lmo_jump", "%s: %s" %(base, _("Jump to the next")), Iconos.PuntoVerde())] + + resp = self.configurar(siSonidos=True, siCambioTutor=False, liMasOpciones=liMasOpciones) + if resp in ("lmo_stop", "lmo_jump"): + self.siSaltoAutomatico = resp == "lmo_jump" + self.tactica.setSaltoAutomatico(self.siSaltoAutomatico) elif clave == k_utilidades: self.utilidades() @@ -254,6 +267,7 @@ def reiniciar(self): self.siguienteJugada() def ent_siguiente(self): + self.siError = False # Para controlar salto, no es automatico si se produce un error if self.posSiguiente == self.numPosiciones: self.finPartida() else: @@ -351,30 +365,33 @@ def siguienteJugada(self): def finLinea(self): self.compruebaComentarios() - QTUtil2.mensajeTemporal(self.pantalla, _("This line training is completed."), 0.7) self.estado = kFinJuego self.desactivaTodas() - liOpciones = [k_mainmenu, k_reiniciar, k_cambiar] + self.tactica.ponPosActual(self.posSiguiente) + if self.tactica.terminada(): + self.finalEntrenamiento() - if not (self.siTerminada() and len(self.liVariantes) == 0): - liOpciones.append(k_variantes) + self.siPenalizable = False - if not self.siShowText: - liOpciones.append(k_showtext) # Si no se ha mostrado ahora es el momento + if self.siSaltoAutomatico and not self.siError: + self.ent_siguiente() + else: + QTUtil2.mensajeTemporal(self.pantalla, _("This line training is completed."), 0.7) + liOpciones = [k_mainmenu, k_reiniciar, k_cambiar] - if self.dicEtiquetasPGN: - liOpciones.append(k_pgnInformacion) + if not (self.siTerminada() and len(self.liVariantes) == 0): + liOpciones.append(k_variantes) - liOpciones.extend([k_configurar, k_utilidades, k_siguiente]) + if not self.siShowText: + liOpciones.append(k_showtext) # Si no se ha mostrado ahora es el momento - self.pantalla.ponToolBar(liOpciones) + if self.dicEtiquetasPGN: + liOpciones.append(k_pgnInformacion) - self.tactica.ponPosActual(self.posSiguiente) - if self.tactica.terminada(): - self.finalEntrenamiento() + liOpciones.extend([k_configurar, k_utilidades, k_siguiente]) - self.siPenalizable = False + self.pantalla.ponToolBar(liOpciones) return @@ -406,6 +423,7 @@ def mueveHumano(self, desde, hasta, coronacion=None): liMovs.append((jg1.desde, jg1.hasta, siMain)) if not siEsta: + self.siError = True self.tactica.nuevoError() self.ponPosicion(self.partida.ultPosicion) if posMain and posMain != movimiento[:2]: @@ -522,8 +540,8 @@ def lanzaVariantes(self): def finalEntrenamiento(self): self.tactica.finalEntrenamiento() - txt = "%s
%s
" % (_("Congratulations goal achieved"), _("Endgame")) - QTUtil2.mensaje(self.pantalla, txt) + mensaje = "%s
%s
" % (_("Congratulations goal achieved"), _("Endgame")) + self.mensajeEnPGN(mensaje) self.finPartida() def guardaVariantes(self): diff --git a/Code/GestorTorneo.py b/Code/GestorTorneo.py index b11ac56..ff4d85a 100644 --- a/Code/GestorTorneo.py +++ b/Code/GestorTorneo.py @@ -110,13 +110,14 @@ def siguienteJuego(self, gm, ngame, numGames): self.refresh() self.finPorTiempo = None + self.finPorError = None + self.finPorErrorMasInfo = "" self.finForce = None while self.siPausa or self.siguienteJugada(): QTUtil.refreshGUI() if self.siPausa: time.sleep(0.1) - self.xmotor[True].terminar() self.xmotor[False].terminar() self.pantalla.paraReloj() @@ -141,6 +142,9 @@ def compruebaFinal(self): if self.finPorTiempo is not None: result = self.finPorTiempo + elif self.finPorError is not None: + result = self.finPorError + elif self.finForce is not None: result = self.finForce @@ -199,6 +203,8 @@ def compruebaFinal(self): termination = "normal" if self.finPorTiempo: termination = "time forfeit" + elif self.finPorError: + termination = self.finPorErrorMasInfo elif self.finForce: termination = "adjudication" elif adjudication: @@ -223,8 +229,6 @@ def siguienteJugada(self): self.ponIndicador(siBlancas) self.refresh() - self.relojStart(siBlancas) - siEncontrada = False analisis = None bk = self.book[siBlancas] @@ -240,8 +244,16 @@ def siguienteJugada(self): segundosJugada = xrival.motorTiempoJugada if self.siTerminar: return False + self.relojStart(siBlancas) mrm = xrival.juegaTiempoTorneo(tiempoBlancas, tiempoNegras, segundosJugada) + self.relojStop(siBlancas) if mrm is None: + if self.tiempo[siBlancas].siAgotado(): + self.finPorTiempo = 2 if siBlancas else 1 + else: + self.finPorError = 2 if siBlancas else 1 + self.finPorErrorMasInfo = "Engine error" + self.compruebaFinal() return False rm = mrm.mejorMov() desde = rm.desde @@ -249,12 +261,17 @@ def siguienteJugada(self): coronacion = rm.coronacion analisis = mrm, 0 - self.relojStop(siBlancas) if self.siTerminar: return False siBien, mens, jg = Jugada.dameJugada(self.partida.ultPosicion, desde, hasta, coronacion) if not jg: + if self.tiempo[siBlancas].siAgotado(): + self.finPorTiempo = 2 if siBlancas else 1 + else: + self.finPorError = 2 if siBlancas else 1 + self.finPorErrorMasInfo = "Engine error: bad move %s-%s%s" %(desde, hasta, coronacion if coronacion else "") + self.compruebaFinal() return False if analisis: jg.analisis = analisis @@ -366,5 +383,3 @@ def assignResult(self): if resp is not None: self.finForce = resp - - diff --git a/Code/GestorWashing.py b/Code/GestorWashing.py index bc5904d..581a690 100644 --- a/Code/GestorWashing.py +++ b/Code/GestorWashing.py @@ -139,10 +139,9 @@ def finPartida(self): if ok: mens = _("Congratulations, this washing is done") - QTUtil2.mensaje(self.pantalla, mens) else: mens = "%s
%s: %d" %( _("Done with errors."), _("Errors"), self.errores) - QTUtil2.mensError(self.pantalla, mens) + self.mensajeEnPGN(mens) def mueveHumano(self, desde, hasta, coronacion=None): jg = self.checkMueveHumano(desde, hasta, coronacion) @@ -384,7 +383,7 @@ def finLinea(self): if self.num_lines == 0: mens = "%s\n%s" % (mens, _("You have solved all puzzles")) - QTUtil2.mensaje(self.pantalla, mens) + self.mensajeEnPGN(mens) else: QTUtil2.mensError(self.pantalla, "%s: %d, %s: %d" % (_("Errors"), self.errores, _("Hints"), self.ayudas)) @@ -869,7 +868,7 @@ def ponResultado(self, quien): self.resultado = kTablas self.guardarGanados(quien == kGanamos) - QTUtil2.mensaje(self.pantalla, mensaje) + self.mensajeEnPGN(mensaje) self.estado = kFinJuego self.desactivaTodas() liOpciones = [k_mainmenu, k_configurar, k_utilidades] diff --git a/Code/Init.py b/Code/Init.py index b7a351a..6ea9291 100644 --- a/Code/Init.py +++ b/Code/Init.py @@ -9,7 +9,7 @@ from Code.Constantes import * DEBUG = False -VERSION = "11.09" +VERSION = "11.10" if DEBUG: prlkn("DEBUG " * 20) diff --git a/Code/Jugada.py b/Code/Jugada.py index 740e671..e9a9be4 100644 --- a/Code/Jugada.py +++ b/Code/Jugada.py @@ -6,7 +6,6 @@ NOABANDONO, ABANDONO, ABANDONORIVAL = "N", "S", "R" - # def creaDicHTML(): # base = '' # dic = {} @@ -15,6 +14,7 @@ # dic[x] = base % ("b", x) # return dic + def creaDicHTML(): base = '%s' ori = "KQRBNPkqrbnp" diff --git a/Code/Kibitzers.py b/Code/Kibitzers.py index a40c926..ef3757e 100644 --- a/Code/Kibitzers.py +++ b/Code/Kibitzers.py @@ -19,7 +19,8 @@ def __init__(self): ("L", _("Best move in one line"), Iconos.pmPuntoMagenta()), ("J", _("Select move"), Iconos.pmPuntoNaranja()), ("C", _("Threats"), Iconos.pmPuntoAzul()), - ("E", _("Stockfish evaluation"), Iconos.pmPuntoAmarillo()) + ("E", _("Stockfish evaluation"), Iconos.pmPuntoAmarillo()), + ("B", _("Polyglot book"), Iconos.pmPuntoEstrella()) ) def combo(self): @@ -223,6 +224,17 @@ def nuevo(self, nombre, motor, tipo, prioridad): self.save() return len(self.lista)-1 + def nuevoPolyglot(self, book): + kib = Kibitzer() + kib.ponHuella(self.lista) + kib.alias = kib.nombre = book.nombre + kib.tipo = "B" + kib.exe = book.path + kib.clave = book.nombre + self.lista.append(kib) + self.save() + return len(self.lista)-1 + def __len__(self): return len(self.lista) diff --git a/Code/OpeningLines.py b/Code/OpeningLines.py index 906f8df..8122cee 100644 --- a/Code/OpeningLines.py +++ b/Code/OpeningLines.py @@ -1,6 +1,9 @@ import os +import shutil import sqlite3 import random +import datetime +import collections import LCEngineV1 as LCEngine @@ -12,6 +15,7 @@ from Code.QT import QTVarios from Code.QT import QTUtil2 + class ListaOpenings: def __init__(self, configuracion): self.folder = configuracion.folderOpenings @@ -124,6 +128,30 @@ def new(self, file, basepv, title): op.close() self.save() + def copy(self, pos): + dicline = dict(self.lista[pos]) + base = dicline["file"][:-3] + if base.split("-")[-1].isdigit(): + li = base.split("-") + base = "-".join(li[:-1]) + filenew = "%s-1.opk" % base + n = 1 + while os.path.isfile(os.path.join(self.folder, filenew)): + filenew = "%s-%d.opk" % (base, n) + n += 1 + try: + shutil.copy(self.filepath(pos), os.path.join(self.folder, filenew)) + except: + return + + dicline["file"] = filenew + dicline["title"] = dicline["title"] + " -%d"%(n-1 if n > 1 else 1) + self.lista.append(dicline) + op = Opening(self.filepath(len(self.lista)-1)) + op.settitle(dicline["title"]) + op.close() + self.save() + def change_title(self, num, title): op = Opening(self.filepath(num)) op.settitle(title) @@ -138,6 +166,13 @@ def add_training_file(self, file): self.save() return + def add_training_engines_file(self, file): + for dicline in self.lista: + if file == dicline["file"]: + dicline["withtrainings_engine"] = True + self.save() + return + class Opening: def __init__(self, nomFichero): @@ -146,20 +181,51 @@ def __init__(self, nomFichero): self._conexion = sqlite3.connect(nomFichero) self.cache = {} - self.max_cache = 4000 + self.max_cache = 14000 self.del_cache = 1000 self.grupo = 0 + self.history = collections.OrderedDict() + self.li_xpv = self.init_database() self.db_config = Util.DicSQL(nomFichero, tabla="CONFIG") self.db_fenvalues = Util.DicSQL(nomFichero, tabla="FENVALUES") + self.db_history = Util.DicSQL(nomFichero, tabla="HISTORY") + self.db_cache_engines = None self.basePV = self.getconfig("BASEPV", "") self.title = self.getconfig("TITLE", os.path.basename(nomFichero).split(".")[0]) self.tablero = None + def open_cache_engines(self): + if self.db_cache_engines is None: + self.db_cache_engines = Util.DicSQL(self.nomFichero, tabla="CACHE_ENGINES") + + def get_cache_engines(self, engine, ms, fenM2): + key = "%s-%d-%s" % (engine, ms, fenM2) + return self.db_cache_engines[key] + + def set_cache_engines(self, engine, ms, fenM2, move): + key = "%s-%d-%s" % (engine, ms, fenM2) + self.db_cache_engines[key] = move + + def init_database(self): + cursor = self._conexion.cursor() + cursor.execute("pragma table_info(LINES)") + if not cursor.fetchall(): + sql = "CREATE TABLE LINES( XPV TEXT PRIMARY KEY );" + cursor.execute(sql) + self._conexion.commit() + li_xpv = [] + else: + sql = "select XPV from LINES ORDER BY XPV" + cursor.execute(sql) + li_xpv = [ raw[0] for raw in cursor.fetchall()] + cursor.close() + return li_xpv + def setdbVisual_Tablero(self, tablero): self.tablero = tablero @@ -189,7 +255,6 @@ def removeAnalisis(self, tmpBP, mensaje): self.setfenvalue(fenM2, dic) self.packAlTerminar() - def getconfig(self, key, default=None): return self.db_config.get(key, default) @@ -200,7 +265,13 @@ def training(self): return self.getconfig("TRAINING") def setTraining(self, reg): - return self.setconfig("TRAINING", reg) + self.setconfig("TRAINING", reg) + + def trainingEngines(self): + return self.getconfig("TRAINING_ENGINES") + + def setTrainingEngines(self, reg): + self.setconfig("TRAINING_ENGINES", reg) def preparaTraining(self, reg, procesador): maxmoves = reg["MAXMOVES"] @@ -221,20 +292,22 @@ def preparaTraining(self, reg, procesador): lilipv[pos] = lipv[:-1] # Quitamos las repetidas - lilipvfinal = [] - nt = len(lilipv) - for x in range(nt-1): - pvmirar = "".join(lilipv[x]) - esta = False - for y in range(nt): - if y != x: - pvotro = "".join(lilipv[y]) - if pvotro.startswith(pvmirar): - esta = True - break - if not esta: - lilipvfinal.append(lilipv[x]) - lilipv = lilipvfinal + dicpv = {} + for lipv in lilipv: + pvmirar = "".join(lipv) + if pvmirar in dicpv: + continue + + siesta = False + for pvotro in dicpv: + if pvotro.startswith(pvmirar): + siesta = True + break + if not siesta: + dicpv[pvmirar] = lipv + li = dicpv.keys() + li.sort() + lilipv = [value for key, value in dicpv.iteritems()] ligamesST = [] ligamesSQ = [] @@ -315,7 +388,83 @@ def preparaTraining(self, reg, procesador): random.shuffle(liTrainPositions) reg["LITRAINPOSITIONS"] = liTrainPositions - def createTraining(self, reg, procesador): + def recalcFenM2(self): + lilipv = [LCEngine.xpv2pv(xpv).split(" ") for xpv in self.li_xpv] + + dicFENm2 = {} + for lipv in lilipv: + LCEngine.setFenInicial() + for pv in lipv: + fen = LCEngine.getFen() + fenM2 = LCEngine.fen2fenM2(fen) + if fenM2 not in dicFENm2: + dicFENm2[fenM2] = set() + dicFENm2[fenM2].add(pv) + LCEngine.makeMove(pv) + return dicFENm2 + + def preparaTrainingEngines(self, reg): + reg["DICFENM2"] = self.recalcFenM2() + reg["TIMES"] = [500, 1000, 2000, 4000, 8000] + reg["ENGINES"] = self.listaEngines()[reg["NUM_LISTA"]] + + def updateTrainingEngines(self): + reg = self.trainingEngines() + reg["DICFENM2"] = self.recalcFenM2() + self.setTrainingEngines(reg) + + def listaEngines(self): + lista = [ + ['irina', 'chispa', 'hamsters', 'zappa', 'demolito', 'wildcat', 'cheng', 'stockfish'], + ['irina', 'arminius', 'rodent', 'toga', 'rodentII', 'gaviota', 'texel', 'gull'], + ['tarrasch', 'cdrill', 'bikjump', 'garbochess', 'ufim', 'amyan', 'delfi', 'spike'], + ['rocinante', 'roce', 'cinnamon', 'gaia', 'greko', 'godel', 'rodent', 'mcbrain'], + ['zappa', 'demolito', 'rhetoric', 'critter', 'houdini', 'gull', 'andscacs', 'stockfish'], + ['clarabit', 'pawny', 'hamsters', 'cheng', 'spike', 'rybka', 'hannibal', 'texel'], + ['bikjump', 'clarabit', 'chispa', 'gaia', 'umko', 'greko', 'wildcat', 'critter'], + ['tarrasch', 'cdrill', 'lime', 'arminius', 'delfi', 'gaviota', 'andscacs', 'mcbrain'], + ['rocinante', 'cinnamon', 'pawny', 'amyan', 'alaric', 'daydreamer', 'godel', 'rodentII'], + ['irina', 'roce', 'demolito', 'rhetoric', 'toga', 'hannibal', 'komodo', 'stockfish'], + ['lime', 'zappa', 'wildcat', 'daydreamer', 'cheng', 'glaurung', 'critter', 'andscacs'], + ['bikjump', 'gaia', 'hamsters', 'umko', 'ufim', 'alaric', 'houdini', 'gull'], + ['cdrill', 'chispa', 'hamsters', 'amyan', 'spike', 'rybka', 'texel', 'komodo'], + ['tarrasch', 'clarabit', 'garbochess', 'ufim', 'delfi', 'fruit', 'rhetoric', 'mcbrain'], + ['rocinante', 'roce', 'cinnamon', 'pawny', 'alaric', 'cheng', 'fruit', 'rodent'], + ['irina', 'lime', 'garbochess', 'zappa', 'demolito', 'daydreamer', 'glaurung', 'spike'], + ['umko', 'greko', 'wildcat', 'glaurung', 'critter', 'gull', 'andscacs', 'stockfish'], + ['bikjump', 'chispa', 'gaia', 'wildcat', 'daydreamer', 'godel', 'fruit', 'toga'], + ['cdrill', 'clarabit', 'lime', 'umko', 'greko', 'amyan', 'delfi', 'texel'], + ['tarrasch', 'rocinante', 'roce', 'cinnamon', 'daydreamer', 'gaviota', 'houdini', 'mcbrain'], + ['irina', 'lime', 'umko', 'alaric', 'arminius', 'glaurung', 'fruit', 'stockfish'], + ['chispa', 'hamsters', 'zappa', 'demolito', 'cheng', 'rhetoric', 'houdini', 'andscacs'], + ['cdrill', 'bikjump', 'gaia', 'ufim', 'arminius', 'fruit', 'texel', 'gull'], + ['tarrasch', 'clarabit', 'simplex', 'amyan', 'delfi', 'godel', 'spike', 'hannibal'], + ['rocinante', 'roce', 'cinnamon', 'pawny', 'greko', 'glaurung', 'rhetoric', 'komodo'], + ['irina', 'lime', 'zappa', 'demolito', 'rodent', 'rodentII', 'gaviota', 'stockfish'], + ['bikjump', 'arminius', 'cheng', 'toga', 'critter', 'houdini', 'gull', 'mcbrain'], + ['chispa', 'gaia', 'umko', 'toga', 'hannibal', 'critter', 'texel', 'andscacs'], + ['cdrill', 'greko', 'godel', 'rodent', 'rhetoric', 'toga', 'rybka', 'hannibal'], + ['tarrasch', 'clarabit', 'arminius', 'delfi', 'rybka', 'gaviota', 'houdini', 'mcbrain'], + ['rocinante', 'roce', 'cinnamon', 'pawny', 'amyan', 'wildcat', 'godel', 'toga'], + ['irina', 'cdrill', 'zappa', 'demolito', 'daydreamer', 'fruit', 'andscacs', 'stockfish'], + ['lime', 'pawny', 'ufim', 'wildcat', 'alaric', 'glaurung', 'rhetoric', 'critter'], + ['chispa', 'gaia', 'amyan', 'arminius', 'rybka', 'hannibal', 'texel', 'gull'], + ['tarrasch', 'bikjump', 'alaric', 'delfi', 'cheng', 'spike', 'gaviota', 'mcbrain'], + ['cinnamon', 'simplex', 'umko', 'delfi', 'godel', 'spike', 'gaviota', 'komodo'], + ['irina', 'rocinante', 'roce', 'clarabit', 'zappa', 'daydreamer', 'houdini', 'stockfish'], + ['tarrasch', 'lime', 'umko', 'cheng', 'rodent', 'rybka', 'hannibal', 'gull'], + ['chispa', 'glaurung', 'spike', 'gaviota', 'critter', 'texel', 'andscacs', 'komodo'], + ['cdrill', 'bikjump', 'clarabit', 'hamsters', 'amyan', 'demolito', 'wildcat', 'mcbrain'], + ['cinnamon', 'godel', 'fruit', 'rhetoric', 'toga', 'hannibal', 'houdini', 'komodo'], + ['irina', 'rocinante', 'roce', 'hamsters', 'godel', 'glaurung', 'rodentII', 'stockfish'], + ['roce', 'umko', 'greko', 'garbochess', 'demolito', 'cheng', 'gull', 'andscacs'], + ['chispa', 'pawny', 'ufim', 'rodentII', 'spike', 'rybka', 'texel', 'komodo'], + ['bikjump', 'clarabit', 'lime', 'gaia', 'simplex', 'greko', 'rybka', 'hannibal'], + ['cdrill', 'rocinante', 'ufim', 'amyan', 'rhetoric', 'toga', 'rybka', 'komodo'], + ] + return lista + + def createTrainingSSP(self, reg, procesador): self.preparaTraining(reg, procesador) reg["DATECREATION"] = Util.hoy() @@ -325,6 +474,17 @@ def createTraining(self, reg, procesador): lo = ListaOpenings(procesador.configuracion) lo.add_training_file(os.path.basename(self.nomFichero)) + def createTrainingEngines(self, reg, procesador): + self.preparaTrainingEngines(reg) + reg["DATECREATION"] = Util.hoy() + self.setTrainingEngines(reg) + + self.setconfig("ENG_LEVEL", 0) + self.setconfig("ENG_ENGINE", 0) + + lo = ListaOpenings(procesador.configuracion) + lo.add_training_engines_file(os.path.basename(self.nomFichero)) + def withTrainings(self): return "TRAINING" in self.db_config @@ -446,29 +606,11 @@ def add_cache(self, xpv, partida): break self.cache[xpv] = partida - def init_database(self): - cursor = self._conexion.cursor() - cursor.execute("pragma table_info(LINES)") - if not cursor.fetchall(): - sql = "CREATE TABLE LINES( XPV TEXT PRIMARY KEY, GRUPO INTEGER, LINE BLOB );" - cursor.execute(sql) - sql = "CREATE INDEX IDX_GRUPO ON LINES( GRUPO );" - cursor.execute(sql) - self._conexion.commit() - li_xpv = [] - else: - sql = "select XPV from LINES ORDER BY XPV" - cursor.execute(sql) - li_xpv = [ raw[0] for raw in cursor.fetchall()] - cursor.close() - return li_xpv - def append(self, partida): xpv = LCEngine.pv2xpv(partida.pv()) - line_blob = partida.save2blob() - sql = "INSERT INTO LINES( XPV, LINE ) VALUES( ?, ? )" + sql = "INSERT INTO LINES( XPV ) VALUES( ? )" cursor = self._conexion.cursor() - cursor.execute(sql, (xpv, line_blob)) + cursor.execute(sql, (xpv,)) cursor.close() self._conexion.commit() self.li_xpv.append(xpv) @@ -504,8 +646,8 @@ def __setitem__(self, num, partida_nue): self.li_xpv.sort() num = self.li_xpv.index(xpv_nue) cursor = self._conexion.cursor() - sql = "UPDATE LINES SET XPV=?, LINE=? WHERE XPV=?" - cursor.execute(sql, (xpv_nue, partida_nue.save2blob(), xpv_ant)) + sql = "UPDATE LINES SET XPV=? WHERE XPV=?" + cursor.execute(sql, (xpv_nue, xpv_ant)) self._conexion.commit() self.add_cache(xpv_nue, partida_nue) cursor.close() @@ -516,14 +658,10 @@ def __getitem__(self, num): if xpv in self.cache: return self.cache[xpv] - sql = "select LINE from LINES where XPV=?" - cursor = self._conexion.cursor() - cursor.execute(sql, (xpv,)) - blob = cursor.fetchone()[0] partida = Partida.Partida() - partida.blob2restore(blob) + pv = LCEngine.xpv2pv(xpv) + partida.leerPV(pv) self.add_cache(xpv, partida) - cursor.close() return partida def __delitem__(self, num): @@ -540,6 +678,54 @@ def __delitem__(self, num): def __len__(self): return len(self.li_xpv) + def removeLines(self, li, label): + self.saveHistory(_("Removing"), label) + li.sort(reverse=True) + cursor = self._conexion.cursor() + for num in li: + xpv = self.li_xpv[num] + sql = "DELETE FROM LINES where XPV=?" + cursor.execute(sql, (xpv,)) + if xpv in self.cache: + del self.cache[xpv] + del self.li_xpv[num] + self._conexion.commit() + cursor.close() + + def lihistory(self): + return self.db_history.keys(siOrdenados=True, siReverse=True) + + def saveHistory(self, *label): + d = datetime.datetime.now() + s = "%s-%s" % (d.strftime("%Y-%m-%d %H:%M:%S"), ",".join(label)) + self.db_history[s] = self.li_xpv[:] + + def rechistory(self, key): + self.saveHistory(_("Recovering"), key) + + stActivo = set(self.li_xpv) + li_xpv_rec = self.db_history[key] + stRecuperar = set(li_xpv_rec) + + cursor = self._conexion.cursor() + + # Borramos los que no estan en Recuperar + sql = "DELETE FROM LINES where XPV=?" + for xpv in stActivo: + if xpv not in stRecuperar: + cursor.execute(sql, (xpv,)) + self._conexion.commit() + + # Mas los que no estan en Activo + sql = "INSERT INTO LINES( XPV ) VALUES( ? )" + for xpv in stRecuperar: + if xpv not in stActivo: + cursor.execute(sql, (xpv, )) + self._conexion.commit() + + cursor.close() + self.li_xpv = li_xpv_rec + def close(self): if self._conexion: conexion = self._conexion @@ -553,16 +739,30 @@ def close(self): self.db_fenvalues.close() self.db_fenvalues = None + if self.db_cache_engines: + self.db_cache_engines.close() + self.db_cache_engines = None + if self.tablero: self.tablero.dbVisual_close() self.tablero = None if si_pack: + if len(self.db_history) > 70: + lik = self.db_history.keys(siOrdenados=True, siReverse=False) + liremove = lik[:len(self.db_history)-50] + for k in liremove: + del self.db_history[k] + self.db_history.close() + cursor = conexion.cursor() cursor.execute("VACUUM") cursor.close() conexion.commit() + else: + self.db_history.close() + conexion.close() def importarPGN(self, owner, partidabase, ficheroPGN, maxDepth): @@ -571,12 +771,14 @@ def importarPGN(self, owner, partidabase, ficheroPGN, maxDepth): dlTmp.hideDuplicados() dlTmp.show() + self.saveHistory(_("Import"), _("PGN with variants"), os.path.basename(ficheroPGN)) + cursor = self._conexion.cursor() base = partidabase.pv() if partidabase else self.getconfig("BASEPV") - sql_insert = "INSERT INTO LINES( XPV, LINE ) VALUES( ?, ? )" - sql_update = "UPDATE LINES SET XPV=?, LINE=? WHERE XPV=?" + sql_insert = "INSERT INTO LINES( XPV ) VALUES( ? )" + sql_update = "UPDATE LINES SET XPV=? WHERE XPV=?" for n, g in enumerate(PGNreader.readGames(ficheroPGN), 1): if not dlTmp.actualiza(n, erroneos, duplicados, importados): @@ -599,18 +801,17 @@ def haz_partida(liMoves): if base and not pv.startswith(base): return xpv = LCEngine.pv2xpv(pv) - line_blob = partida.save2blob() updated = False for npos, xpv_ant in enumerate(self.li_xpv): if xpv_ant.startswith(xpv): return if xpv.startswith(xpv_ant): - cursor.execute(sql_update, (xpv, line_blob, xpv_ant)) + cursor.execute(sql_update, (xpv, xpv_ant)) self.li_xpv[npos] = xpv updated = True break if not updated: - cursor.execute(sql_insert, (xpv, line_blob)) + cursor.execute(sql_insert, (xpv,)) self.li_xpv.append(xpv) for njug, move in enumerate(liMoves): @@ -633,6 +834,9 @@ def haz_partida(liMoves): dlTmp.ponContinuar() def importarPGO(self, partidabase, ficheroPGO, maxDepth): + + self.saveHistory(_("Personal Opening Guide"), os.path.basename(ficheroPGO)) + base = partidabase.pv() if partidabase else self.getconfig("BASEPV") baseXPV = LCEngine.pv2xpv(base) @@ -664,58 +868,75 @@ def importarPGO(self, partidabase, ficheroPGO, maxDepth): cursor = self._conexion.cursor() - sql_insert = "INSERT INTO LINES( XPV, LINE ) VALUES( ?, ? )" - sql_update = "UPDATE LINES SET XPV=?, LINE=? WHERE XPV=?" + sql_insert = "INSERT INTO LINES( XPV ) VALUES( ? )" + sql_update = "UPDATE LINES SET XPV=? WHERE XPV=?" for xpv in stPGO: - pv = LCEngine.xpv2pv(xpv) - partida = Partida.Partida() - partida.leerPV(pv) - line_blob = partida.save2blob() add = True for npos, xpv_ant in enumerate(self.li_xpv): if xpv_ant.startswith(xpv): add = False break if xpv.startswith(xpv_ant): - cursor.execute(sql_update, (xpv, line_blob, xpv_ant)) + cursor.execute(sql_update, (xpv, xpv_ant)) self.li_xpv[npos] = xpv add = False break if add: - cursor.execute(sql_insert, (xpv, line_blob)) + cursor.execute(sql_insert, (xpv, )) self.li_xpv.append(xpv) cursor.close() self.li_xpv.sort() self._conexion.commit() - def guardaPartidas(self, liPartidas, minMoves=0): + def guardaPartidas(self, label, liPartidas, minMoves=0): + self.saveHistory(_("Import"), label) partidabase = self.getpartidabase() - sql_insert = "INSERT INTO LINES( XPV, LINE ) VALUES( ?, ? )" - sql_update = "UPDATE LINES SET XPV=?, LINE=? WHERE XPV=?" + sql_insert = "INSERT INTO LINES( XPV) VALUES( ? )" + sql_update = "UPDATE LINES SET XPV=? WHERE XPV=?" cursor = self._conexion.cursor() for partida in liPartidas: if minMoves <= partida.numJugadas() > partidabase.numJugadas(): xpv = LCEngine.pv2xpv(partida.pv()) if xpv not in self.li_xpv: - line_blob = partida.save2blob() updated = False for npos, xpv_ant in enumerate(self.li_xpv): if xpv.startswith(xpv_ant): - cursor.execute(sql_update, (xpv, line_blob, xpv_ant)) + cursor.execute(sql_update, (xpv, xpv_ant)) self.li_xpv[npos] = xpv updated = True break if not updated: - cursor.execute(sql_insert, (xpv, line_blob)) + cursor.execute(sql_insert, (xpv,)) self.li_xpv.append(xpv) cursor.close() self._conexion.commit() self.li_xpv.sort() - def importarPolyglot(self, ventana, partida, bookW, bookB, titulo, depth, siWhite, minMoves): + def guardaLiXPV(self, label, liXPV): + self.saveHistory(_("Import"), label) + sql_insert = "INSERT INTO LINES( XPV) VALUES( ? )" + sql_update = "UPDATE LINES SET XPV=? WHERE XPV=?" + cursor = self._conexion.cursor() + for xpv in liXPV: + if xpv not in self.li_xpv: + updated = False + for npos, xpv_ant in enumerate(self.li_xpv): + if xpv.startswith(xpv_ant): + cursor.execute(sql_update, (xpv, xpv_ant)) + self.li_xpv[npos] = xpv + updated = True + break + if not updated: + cursor.execute(sql_insert, (xpv,)) + self.li_xpv.append(xpv) + cursor.close() + self._conexion.commit() + self.li_xpv.sort() + + def importarPolyglot(self, ventana, partida, bookW, bookB, titulo, depth, siWhite, onlyone, minMoves): bp = QTUtil2.BarraProgreso1(ventana, titulo) bp.ponTotal(0) bp.ponRotulo(_X(_("Reading %1"), "...")) @@ -734,7 +955,7 @@ def hazFEN(fen, lipv_ant): return siWhite1 = " w " in fen book = bookW if siWhite1 else bookB - liPV = book.miraListaPV(fen, siWhite1 == siWhite) + liPV = book.miraListaPV(fen, siWhite1 == siWhite, onlyone=onlyone) if liPV and len(lipv_ant) < depth: for pv in liPV: setFen(fen) @@ -753,12 +974,13 @@ def hazFEN(fen, lipv_ant): hazFEN(cp.fen(), partida.lipv()) bp.ponRotulo(_("Writing...")) - self.guardaPartidas(liPartidas, minMoves) + + self.guardaPartidas("%s,%s,%s"%(_("Polyglot book"), bookW.nombre, bookB.nombre), liPartidas, minMoves) bp.cerrar() return True - def importarSummary(self, ventana, partidabase, ficheroSummary, depth, siWhite, minMoves): + def importarSummary(self, ventana, partidabase, ficheroSummary, depth, siWhite, onlyone, minMoves): titulo = _("Importing the summary of a database") bp = QTUtil2.BarraProgreso1(ventana, titulo) bp.ponTotal(0) @@ -778,9 +1000,10 @@ def importarSummary(self, ventana, partidabase, ficheroSummary, depth, siWhite, def hazPV(lipv_ant): if bp.siCancelado(): return - siWhite1 = len(lipv_ant) % 2 == 0 + n_ant = len(lipv_ant) + siWhite1 = n_ant % 2 == 0 - pv_ant = " ".join(lipv_ant) + pv_ant = " ".join(lipv_ant) if n_ant else "" liChildren = dbSTAT.children(pv_ant, False) if len(liChildren) == 0 or len(lipv_ant) > depth: @@ -793,24 +1016,26 @@ def hazPV(lipv_ant): return if siWhite1 == siWhite: - alm_max = None tt_max = 0 + limax = [] for alm in liChildren: tt = alm.W + alm.B + alm.O + alm.D if tt > tt_max: tt_max = tt - alm_max = alm - liChildren = [] if tt_max == 0 else [alm_max,] + limax = [alm,] + elif tt == tt_max and not onlyone: + limax.append(alm) + liChildren = limax for alm in liChildren: li = lipv_ant[:] li.append(alm.move) hazPV(li) - hazPV(pvBase.split(" ")) + hazPV(pvBase.split(" ") if pvBase else []) bp.ponRotulo(_("Writing...")) - self.guardaPartidas(liPartidas) + self.guardaPartidas("%s,%s" %(_("Database summary"), os.path.basename(ficheroSummary)), liPartidas) bp.cerrar() return True @@ -819,12 +1044,33 @@ def importarOtra(self, pathFichero, partida): xpvbase = LCEngine.pv2xpv(partida.pv()) tambase = len(xpvbase) otra = Opening(pathFichero) - liPartidas = [] + lista = [] for n, xpv in enumerate(otra.li_xpv): - if xpv.startswith(xpvbase) and tambase < len(xpv): - liPartidas.append(otra[n]) + if xpv.startswith(xpvbase) and len(xpv) > tambase: + if xpv not in self.li_xpv: + lista.append(xpv) otra.close() - self.guardaPartidas(liPartidas) + self.guardaLiXPV("%s,%s"% (_("Other opening lines"), otra.title), lista) + + def exportarPGN(self, ws, result): + liTags = [ + ("Event", self.title.replace('"', "")), + ("Site", ""), + ("Date", Util.hoy().strftime("%Y-%m-%d")) + ] + if result: + liTags.append(("Result", result)) + + for recno in range(len(self)): + partida = self[recno] + + liTags[1] = ("Site", "%s %d" % (_("Line"), recno+1)) + + if recno > 0 or not ws.is_new: + ws.write("\n\n") + tags = "".join(['[%s "%s"]\n' % (k, v) for k, v in liTags]) + ws.write(tags) + ws.write("\n%s" % partida.pgnBase()) def getAllFen(self): stFENm2 = set() @@ -905,4 +1151,3 @@ def listaPV(self): item = item.parent return li[::-1] - diff --git a/Code/PGNreader.py b/Code/PGNreader.py index 520a82a..4ed6ea7 100644 --- a/Code/PGNreader.py +++ b/Code/PGNreader.py @@ -35,7 +35,6 @@ def clona(self): return m - class Moves: def __init__(self): self.liMoves = [] @@ -260,7 +259,14 @@ def readLabels(self, liTxt): for linea in liTxt: li = linea[1:-1].replace('""', '"').split('"') if len(li) == 3: - clave = li[0].strip() + clave = li[0].strip().replace(" ", "") + ok = True + for c in clave: + if not( 33 < ord(c) < 127): + ok = False + break + if not ok: + continue valor = li[1].strip() if clave and valor: if clave.upper() == "FEN": diff --git a/Code/Partida.py b/Code/Partida.py index ac6b101..6055114 100644 --- a/Code/Partida.py +++ b/Code/Partida.py @@ -598,6 +598,7 @@ def pv_pgn(fen, pv): p.leerPV(pv) return p.pgnSP() + def lipv_lipgn(lipv): LCEngine.setFenInicial() li_pgn = [] @@ -606,6 +607,7 @@ def lipv_lipgn(lipv): li_pgn.append(info._san) return li_pgn + def pv_pgn_raw(fen, pv): p = Partida(fen=fen) p.leerPV(pv) @@ -678,7 +680,7 @@ def calc_formula_elo(jg): # , limit=200.0): # dataLG = [] # titLG = [] - # + # def LG(key, value): # titLG.append(key) # dataLG.append(str(value)) @@ -767,7 +769,6 @@ def calc_formula_elo(jg): # , limit=200.0): # LG("elo", int(min(3500, max(0, x)))) # LG("other elo", int(jg.elo)) - # with open("FormulaELO.csv", "ab") as q: # if firstLG[0]: # firstLG[0] = False diff --git a/Code/Procesador.py b/Code/Procesador.py index 74dbf6b..c2df7f2 100644 --- a/Code/Procesador.py +++ b/Code/Procesador.py @@ -65,7 +65,8 @@ from Code.QT import PantallaDatabaseFEN from Code.QT import WOpeningGuide from Code.QT import PantallaKibitzers -from Code.QT import POLines +from Code.QT import PantallaOpeningLines +from Code.QT import PantallaOpeningLine class Procesador: @@ -596,7 +597,6 @@ def usuarios(self): def setPassword(self): PantallaUsuarios.setPassword(self) - def trainingMap(self, mapa): resp = PantallaWorkMap.train_map(self, mapa) if resp: @@ -642,12 +642,12 @@ def menuTools_run(self, resp): self.openings() def openings(self): - dicline = POLines.openingLines(self) + dicline = PantallaOpeningLines.openingLines(self) if dicline: if "TRAIN" in dicline: resp = "tr_%s" % dicline["TRAIN"] else: - resp = POLines.study(self, dicline["file"]) + resp = PantallaOpeningLine.study(self, dicline["file"]) if resp is None: self.openings() else: @@ -658,14 +658,20 @@ def openings(self): self.openingsTrainingStatic(pathFichero) elif resp == "tr_positions": self.openingsTrainingPositions(pathFichero) + elif resp == "tr_engines": + self.openingsTrainingEngines(pathFichero) def openingsTrainingSequential(self, pathFichero): self.gestor = GestorOpeningLines.GestorOpeningLines(self) self.gestor.inicio(pathFichero, "sequential", 0) + def openingsTrainingEngines(self, pathFichero): + self.gestor = GestorOpeningLines.GestorOpeningEngines(self) + self.gestor.inicio(pathFichero) + def openingsTrainingStatic(self, pathFichero): dbop = OpeningLines.Opening(pathFichero) - num_linea = POLines.selectLine(self, dbop) + num_linea = PantallaOpeningLines.selectLine(self, dbop) dbop.close() if num_linea is not None: self.gestor = GestorOpeningLines.GestorOpeningLines(self) diff --git a/Code/QT/Iconos.py b/Code/QT/Iconos.py index afa9252..65cff82 100644 --- a/Code/QT/Iconos.py +++ b/Code/QT/Iconos.py @@ -2380,6 +2380,12 @@ def pmTrainPositions(): def TrainPositions(): return QtGui.QIcon(pmTrainPositions()) +def pmTrainEngines(): + return PM(915971,917405) + +def TrainEngines(): + return QtGui.QIcon(pmTrainEngines()) + def pmError(): return PM(50641,54641) @@ -2387,25 +2393,25 @@ def Error(): return QtGui.QIcon(pmError()) def pmAtajos(): - return PM(915971,917150) + return PM(917405,918584) def Atajos(): return QtGui.QIcon(pmAtajos()) def pmTOLline(): - return PM(917150,918254) + return PM(918584,919688) def TOLline(): return QtGui.QIcon(pmTOLline()) def pmTOLchange(): - return PM(918254,920476) + return PM(919688,921910) def TOLchange(): return QtGui.QIcon(pmTOLchange()) def pmPack(): - return PM(920476,921141) + return PM(921910,922575) def Pack(): return QtGui.QIcon(pmPack()) @@ -2416,3 +2422,39 @@ def pmHome(): def Home(): return QtGui.QIcon(pmHome()) +def pmImport8(): + return PM(922575,923785) + +def Import8(): + return QtGui.QIcon(pmImport8()) + +def pmExport8(): + return PM(923785,924410) + +def Export8(): + return QtGui.QIcon(pmExport8()) + +def pmTablas8(): + return PM(924410,925202) + +def Tablas8(): + return QtGui.QIcon(pmTablas8()) + +def pmBlancas8(): + return PM(925202,926232) + +def Blancas8(): + return QtGui.QIcon(pmBlancas8()) + +def pmNegras8(): + return PM(926232,927071) + +def Negras8(): + return QtGui.QIcon(pmNegras8()) + +def pmBook(): + return PM(927071,927645) + +def Book(): + return QtGui.QIcon(pmBook()) + diff --git a/Code/QT/InfoBase.py b/Code/QT/InfoBase.py index 40edea9..f4c6486 100644 --- a/Code/QT/InfoBase.py +++ b/Code/QT/InfoBase.py @@ -426,7 +426,7 @@ def Programming(self): (_("Audio"), "PyAudio v0.2.4 - MIT License", "http://people.csail.mit.edu/hubert/pyaudio/"), ("psutil", _X(_("Created by %1"), "Giampaolo Rodola"), "http://code.google.com/p/psutil/"), ("chardet", _X(_("Created by %1"), "Ian Cordasco"), "https://github.com/chardet/chardet"), - (_("Polyglot books"), _X(_("Based on work by %1"), "Michel Van den Bergh"), "http://alpha.uhasselt.be/Research/Algebra/Toga/book_format.html"), + (_("Polyglot books"), _X(_("Based on work by %1"), "Michel Van den Bergh"), "https://hardy.uhasselt.be/personal/vdbergh/Members/michel_id.html"), ("Polyglot1.4w", _X(_("Created by %1"), "Fabien Letouzy") + ". " + _X(_("Modified by %1"), "Fonzy Bluemers"), "http://www.geenvis.net/"), ("STS", _X(_("Created by %1"), "Dan Corbit,Swaminathan"), "https://sites.google.com/site/strategictestsuite/about-1"), ("python-chess", _X(_("Created by %1"), "Niklas Fiekas"), "https://github.com/niklasf/python-chess"), diff --git a/Code/QT/POLAnalisis.py b/Code/QT/POLAnalisis.py index 4e49f4d..7b89acd 100644 --- a/Code/QT/POLAnalisis.py +++ b/Code/QT/POLAnalisis.py @@ -335,7 +335,6 @@ def stop(self): self.dbstat.close() - class TreeMoves(QtGui.QTreeWidget): def __init__(self, owner): QtGui.QTreeWidget.__init__(self, owner) @@ -415,7 +414,6 @@ def stop(self): def setData(self, data): pass - def menuContexto(self, position): item = self.tree.currentItem() if not item: diff --git a/Code/QT/POLBoard.py b/Code/QT/POLBoard.py index f4a2801..b1fcf69 100644 --- a/Code/QT/POLBoard.py +++ b/Code/QT/POLBoard.py @@ -37,6 +37,8 @@ def __init__(self, panelOpening, configuracion): self.tablero.dbVisual_setFichero(self.dbop.nomFichero) self.tablero.dbVisual_setShowAllways(True) + self.tablero.ponerPiezasAbajo(self.dbop.getconfig("WHITEBOTTOM", True)) + self.dbop.setdbVisual_Tablero(self.tablero) # To close self.intervalo = 1400 diff --git a/Code/QT/Pantalla.py b/Code/QT/Pantalla.py index 4439f19..8dd3ea7 100644 --- a/Code/QT/Pantalla.py +++ b/Code/QT/Pantalla.py @@ -225,9 +225,9 @@ def ajustaTamH(self): def ponTitulo(self): titulo = _("Lucas Chess") - conf = self.gestor.configuracion + # conf = self.gestor.configuracion - titulo += " - %s" % conf.jugador + # titulo += " - %s" % conf.jugador self.setWindowTitle(titulo) diff --git a/Code/QT/PantallaDirector.py b/Code/QT/PantallaDirector.py index a989326..218b5f4 100644 --- a/Code/QT/PantallaDirector.py +++ b/Code/QT/PantallaDirector.py @@ -1039,4 +1039,4 @@ def mouseReleaseEvent(self, event): def terminar(self): if self.w: self.w.terminar() - self.w = None \ No newline at end of file + self.w = None diff --git a/Code/QT/PantallaEntMaq.py b/Code/QT/PantallaEntMaq.py index 05b2525..0013629 100644 --- a/Code/QT/PantallaEntMaq.py +++ b/Code/QT/PantallaEntMaq.py @@ -185,7 +185,6 @@ def _label(lyG, txt, ly, rutinaCHB=None, siCheck=False): gbThoughtOp = Controles.GB(self, _("Thought of the opponent"), ly) gbThoughtOp.setStyleSheet(gbStyle) - ly = Colocacion.V().espacio(16).control(self.gbTutor).espacio(16).control(gbThoughtOp) ly.espacio(16).control(self.cbAtras).control(self.chbSummary) gb = Controles.GB(self, "", ly) diff --git a/Code/QT/PantallaGM.py b/Code/QT/PantallaGM.py index 533ca73..562edbf 100644 --- a/Code/QT/PantallaGM.py +++ b/Code/QT/PantallaGM.py @@ -691,6 +691,7 @@ def importarGM(ownerGM, siWoman): return False + class SelectGame(QTVarios.WDialogo): def __init__(self, wgm, ogm, siWoman): self.ogm = ogm diff --git a/Code/QT/PantallaKibitzers.py b/Code/QT/PantallaKibitzers.py index 2d9d4e1..3efa503 100644 --- a/Code/QT/PantallaKibitzers.py +++ b/Code/QT/PantallaKibitzers.py @@ -1,7 +1,10 @@ +import os + from PyQt4 import QtGui from Code import Kibitzers from Code import EngineThread +from Code import Books from Code.QT import Colocacion from Code.QT import Columnas from Code.QT import Controles @@ -35,6 +38,7 @@ def __init__(self, wParent, procesador): (_("Copy"), Iconos.Copiar(), self.copy), None, (_("Up"), Iconos.Arriba(), self.up), None, (_("Down"), Iconos.Abajo(), self.down), None, + (_("Polyglot book"), Iconos.Book(), self.polyglot), None, (_("External engines"), Iconos.Motores(), self.ext_engines), None ) tb = Controles.TBrutina(self, liAcciones) @@ -75,6 +79,32 @@ def __init__(self, wParent, procesador): self.gridKibitzers.gotop() + def polyglot(self): + listaLibros = Books.ListaLibros() + listaLibros.recuperaVar(self.configuracion.ficheroBooks) + listaLibros.comprueba() + menu = QTVarios.LCMenu(self) + rondo = QTVarios.rondoPuntos() + for book in listaLibros.lista: + menu.opcion(("book", book), book.nombre, rondo.otro()) + menu.separador() + menu.opcion(("install", None), _("Install new book"), Iconos.Nuevo()) + resp = menu.lanza() + if resp: + orden, book = resp + if orden == "book": + num = self.kibitzers.nuevoPolyglot(book) + self.goto(num) + elif orden == "install": + fbin = QTUtil2.leeFichero(self, listaLibros.path, "bin", titulo=_("Polyglot book")) + if fbin: + listaLibros.path = os.path.dirname(fbin) + nombre = os.path.basename(fbin)[:-4] + book = Books.Libro("P", nombre, fbin, True) + listaLibros.nuevo(book) + listaLibros.guardaVar(self.configuracion.ficheroBooks) + return self.polyglot() + def me_setEditor(self, parent): recno = self.gridValores.recno() key = self.liKibActual[recno][2] @@ -88,7 +118,7 @@ def me_setEditor(self, parent): valor = kibitzer.nombre elif key == "tipo": valor = kibitzer.tipo - if valor == "I": # Indices no se cambian + if valor in "IB": # Indices/books no se cambian return None control = "cb" lista = Kibitzers.Tipos().comboSinIndices() @@ -289,6 +319,7 @@ def actKibitzer(self): return me = self.kibitzers.kibitzer(fila) + tipo = me.tipo self.liKibActual.append((_("Name"), me.nombre, "nombre")) self.liKibActual.append((_("Type"), me.ctipo(), "tipo")) self.liKibActual.append((_("Priority"), me.cpriority(), "prioridad")) @@ -296,19 +327,21 @@ def actKibitzer(self): self.liKibActual.append((_("Analysis of the base position"), str(me.posicionBase), "posicionBase")) self.liKibActual.append((_("Visible in menu"), str(me.visible), "visible")) - self.liKibActual.append((_("Engine"), me.idName, None)) + if tipo != "B": + self.liKibActual.append((_("Engine"), me.idName, None)) - self.liKibActual.append((_("Author"), me.idAuthor, None)) + self.liKibActual.append((_("Author"), me.idAuthor, None)) self.liKibActual.append((_("File"), me.exe, None)) - self.liKibActual.append((_("Information"), me.idInfo, "info")) - - for num, opcion in enumerate(me.liOpciones): - default = opcion.label_default() - label_default = " (%s)" % default if default else "" - valor = str(opcion.valor) - if opcion.tipo in ("check", "button"): - valor = valor.lower() - self.liKibActual.append(("%s%s" % (opcion.nombre, label_default), valor, "opcion,%d" % num)) + if tipo != "B": + self.liKibActual.append((_("Information"), me.idInfo, "info")) + + for num, opcion in enumerate(me.liOpciones): + default = opcion.label_default() + label_default = " (%s)" % default if default else "" + valor = str(opcion.valor) + if opcion.tipo in ("check", "button"): + valor = valor.lower() + self.liKibActual.append(("%s%s" % (opcion.nombre, label_default), valor, "opcion,%d" % num)) class WKibitzerLive(QTVarios.WDialogo): diff --git a/Code/QT/POLines.py b/Code/QT/PantallaOpeningLine.py similarity index 65% rename from Code/QT/POLines.py rename to Code/QT/PantallaOpeningLine.py index cae993e..1506b5a 100644 --- a/Code/QT/POLines.py +++ b/Code/QT/PantallaOpeningLine.py @@ -2,10 +2,8 @@ import os.path import copy - from PyQt4 import QtCore, QtGui -from Code import VarGen from Code import Util from Code import Partida from Code import Analisis @@ -25,287 +23,15 @@ from Code.QT import POLAnalisis from Code.QT import Voyager from Code.QT import FormLayout - - -class WOpeningLines(QTVarios.WDialogo): - def __init__(self, procesador): - - self.procesador = procesador - self.configuracion = procesador.configuracion - self.resultado = None - self.listaOpenings = OpeningLines.ListaOpenings(self.configuracion) - - QTVarios.WDialogo.__init__(self, procesador.pantalla, self.getTitulo(), Iconos.OpeningLines(), "openingLines") - - - oColumnas = Columnas.ListaColumnas() - oColumnas.nueva("TITLE", _("Name"), 240) - oColumnas.nueva("BASEPV", _("First moves"), 280) - oColumnas.nueva("NUMLINES", _("Lines"), 80, siCentrado=True) - oColumnas.nueva("FILE", _("File"), 200) - self.glista = Grid.Grid(self, oColumnas, siSelecFilas=True, siSeleccionMultiple=True) - - sp = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) - - liAcciones = ( - (_("Close"), Iconos.MainMenu(), self.terminar), None, - (_("Edit"), Iconos.Modificar(), self.modificar), None, - (_("New"), Iconos.Nuevo(), self.new), None, - (_("Rename"), Iconos.Modificar(), self.renombrar), None, - (_("Up"), Iconos.Arriba(), self.arriba), - (_("Down"), Iconos.Abajo(), self.abajo), None, - (_("Remove"), Iconos.Borrar(), self.borrar), None, - (_("Reinit"), Iconos.Reiniciar(), self.reiniciar), None, - (_("Folder"), Iconos.File(), self.changeFolder), None, - ) - tb = Controles.TBrutina(self, liAcciones) - - tb.setSizePolicy(sp) - - liAcciones = ( - (_("Sequential"), Iconos.TrainSequential(), self.tr_sequential), None, - (_("Static"), Iconos.TrainStatic(), self.tr_static), None, - (_("Positions"), Iconos.TrainPositions(), self.tr_positions), - ) - tbtrain = Controles.TBrutina(self, liAcciones, siTexto=False) - - lbtrain = Controles.LB(self, _("Trainings")).alinCentrado().ponFondoN("lightgray") - lytrain = Colocacion.V().control(lbtrain).control(tbtrain).margen(0) - self.wtrain = QtGui.QWidget() - self.wtrain.setLayout(lytrain) - - lytb = Colocacion.H().control(tb).control(self.wtrain).margen(0) - wtb = QtGui.QWidget() - wtb.setFixedHeight(62) - wtb.setLayout(lytb) - - # Colocamos - - ly = Colocacion.V().control(wtb).control(self.glista).margen(4) - - self.setLayout(ly) - - self.registrarGrid(self.glista) - self.recuperarVideo(anchoDefecto=self.glista.anchoColumnas()+20) - - self.wtrain.setVisible(False) - self.glista.gotop() - - def getTitulo(self): - return "%s [%s]" % (_("Opening lines"), os.path.relpath(self.listaOpenings.folder)) - - - def tr_(self, tipo): - recno = self.glista.recno() - op = self.listaOpenings[recno] - op["TRAIN"] = tipo - self.resultado = op - self.guardarVideo() - self.accept() - - def tr_sequential(self): - self.tr_("sequential") - - def tr_static(self): - self.tr_("static") - - def tr_positions(self): - self.tr_("positions") - - def reiniciar(self): - self.listaOpenings.reiniciar() - self.glista.refresh() - self.glista.gotop() - if len(self.listaOpenings) == 0: - self.wtrain.setVisible(False) - - def changeFolder(self): - nof = _("New opening folder") - base = self.configuracion.folderBaseOpenings - li = [x for x in os.listdir(base) if os.path.isdir(os.path.join(base, x))] - menu = QTVarios.LCMenu(self) - rondo = QTVarios.rondoPuntos() - menu.opcion("", _("Home folder"), Iconos.Home()) - menu.separador() - for x in li: - menu.opcion(x, x, rondo.otro()) - menu.separador() - menu.opcion(":n", nof, Iconos.Nuevo()) - if VarGen.isWindows: - menu.separador() - menu.opcion(":m", _("Direct maintenance"), Iconos.Configurar()) - - resp = menu.lanza() - if resp is not None: - if resp == ":m": - os.startfile(base) - return - - elif resp == ":n": - name = "" - error = "" - while True: - liGen = [FormLayout.separador] - liGen.append((nof + ":", name)) - if error: - liGen.append(FormLayout.separador) - liGen.append((None, error)) - - resultado = FormLayout.fedit(liGen, title=nof, parent=self, - icon=Iconos.OpeningLines(), anchoMinimo=460) - if resultado: - accion, liResp = resultado - name = liResp[0].strip() - if name: - path = os.path.join(base, name) - try: - os.mkdir(path) - except: - error = _("Unable to create this folder") - continue - if not os.path.isdir(path): - continue - break - else: - return - else: - path = os.path.join(base, resp) - - path = os.path.relpath(path) - self.configuracion.folderOpenings = path - self.configuracion.graba() - self.listaOpenings = OpeningLines.ListaOpenings(self.configuracion) - self.glista.refresh() - self.glista.gotop() - if len(self.listaOpenings) == 0: - self.wtrain.setVisible(False) - self.setWindowTitle(self.getTitulo()) - - def arriba(self): - fila = self.glista.recno() - if self.listaOpenings.arriba(fila): - self.glista.goto(fila - 1, 0) - self.glista.refresh() - - def abajo(self): - fila = self.glista.recno() - if self.listaOpenings.abajo(fila): - self.glista.goto(fila + 1, 0) - self.glista.refresh() - - def modificar(self): - recno = self.glista.recno() - if recno >= 0: - self.resultado = self.listaOpenings[recno] - else: - self.resultado = None - self.guardarVideo() - self.accept() - - def gridDobleClick(self, grid, fila, oColumna): - recno = self.glista.recno() - if recno >= 0: - self.modificar() - - def new(self): - si_expl = len(self.listaOpenings) < 4 - if si_expl: - QTUtil2.mensaje(self, _("First you must select the initial moves.")) - w = PantallaAperturas.WAperturas(self, self.configuracion, None) - if w.exec_(): - ap = w.resultado() - pv = ap.a1h8 if ap else "" - name = ap.nombre if ap else "" - else: - return - - if si_expl: - QTUtil2.mensaje(self, _("Secondly you have to choose a name for this opening studio.")) - - name = self.get_nombre(name) - if name: - file = self.listaOpenings.select_filename(name) - self.listaOpenings.new(file, pv, name) - self.resultado = self.listaOpenings[-1] - self.guardarVideo() - self.accept() - - def get_nombre(self, name): - liGen = [(None, None)] - liGen.append((_("Opening studio name") + ":", name)) - resultado = FormLayout.fedit(liGen, title=_("Opening studio name"), parent=self, icon=Iconos.OpeningLines(), anchoMinimo=460) - if resultado: - accion, liResp = resultado - name = liResp[0].strip() - if name: - return name - return None - - def renombrar(self): - fila = self.glista.recno() - if fila >= 0: - op = self.listaOpenings[fila] - name = self.get_nombre(op["title"]) - if name: - self.listaOpenings.change_title(fila, name) - self.glista.refresh() - - def borrar(self): - li = self.glista.recnosSeleccionados() - if len(li) > 0: - mens = _("Do you want to delete all selected records?") - mens += "\n" - for num, fila in enumerate(li, 1): - mens += "\n%d. %s" % (num, self.listaOpenings[fila]["title"]) - if QTUtil2.pregunta(self, mens): - li.sort(reverse=True) - for fila in li: - del self.listaOpenings[fila] - self.glista.refresh() - - def gridNumDatos(self, grid): - return len(self.listaOpenings) - - def gridDato(self, grid, fila, oColumna): - col = oColumna.clave - op = self.listaOpenings[fila] - if col == "TITLE": - return op["title"] - elif col == "FILE": - return op["file"] - elif col == "NUMLINES": - return op["lines"] - elif col == "BASEPV": - pv = op["pv"] - if pv: - p = Partida.Partida() - p.leerPV(pv) - return p.pgnBaseRAW() - else: - return "" - - def gridCambiadoRegistro(self, grid, fila, columna): - ok = False - if fila >= 0: - op = self.listaOpenings[fila] - ok = op["withtrainings"] - - self.wtrain.setVisible(ok) - - def closeEvent(self, event): # Cierre con X - self.guardarVideo() - - def terminar(self): - self.guardarVideo() - self.reject() +from Code.QT import PantallaSavePGN class WLines(QTVarios.WDialogo): def __init__(self, procesador, dbop): self.dbop = dbop - title = dbop.gettitle() + self.title = dbop.gettitle() - QTVarios.WDialogo.__init__(self, procesador.pantalla, title, Iconos.OpeningLines(), "studyOpening") + QTVarios.WDialogo.__init__(self, procesador.pantalla, self.title, Iconos.OpeningLines(), "studyOpening") self.procesador = procesador self.configuracion = procesador.configuracion @@ -320,7 +46,8 @@ def __init__(self, procesador, dbop): liAcciones = ( (_("Close"), Iconos.MainMenu(), self.terminar), None, (_("Remove"), Iconos.Borrar(), self.borrar), None, - (_("Import"), Iconos.Mezclar(), self.importar), None, + (_("Import"), Iconos.Import8(), self.importar), None, + (_("Export"), Iconos.Export8(), self.exportar), None, (_("Utilities"), Iconos.Utilidades(), self.utilidades), None, (_("Train"), Iconos.Study(), self.train), None, ) @@ -366,6 +93,34 @@ def __init__(self, procesador, dbop): self.recuperarVideo() + self.last_numlines = 0 + self.show_lines() + + def show_lines(self): + numlines = len(self.dbop) + if numlines != self.last_numlines: + self.setWindowTitle( "%s [%d]" %(self.title, numlines)) + self.last_numlines = numlines + + def exportar(self): + menu = QTVarios.LCMenu(self) + submenu = menu.submenu(_("PGN Format"), Iconos.PGN()) + r = "%s %%s" % _("Result") + submenu.opcion("1-0", r % "1-0", Iconos.Blancas8()) + submenu.opcion("0-1", r % "0-1", Iconos.Negras8()) + submenu.opcion("1/2-1/2", r % "1/2-1/2", Iconos.Tablas8()) + submenu.opcion("", _("Without Result"), Iconos.Gris()) + resp = menu.lanza() + if resp is not None: + w = PantallaSavePGN.WSaveVarios(self, self.configuracion) + if w.exec_(): + ws = PantallaSavePGN.FileSavePGN(self, w.dic_result) + if ws.open(): + ws.um() + self.dbop.exportarPGN(ws, resp) + ws.close() + ws.um_final() + def utilidades(self): menu = QTVarios.LCMenu(self) submenu = menu.submenu(_("Analysis"), Iconos.Analizar()) @@ -373,9 +128,29 @@ def utilidades(self): submenu.separador() submenu.opcion(self.ta_remove, _("Delete all previous analysis"), Iconos.Delete()) menu.separador() + lihistory = self.dbop.lihistory() + if lihistory: + submenu = menu.submenu(_("Backups"), Iconos.Copiar()) + rondo = QTVarios.rondoPuntos() + for history in lihistory[:30]: + h = history + if len(h) > 70: + h = h[:70] + "..." + submenu.opcion(history, h, rondo.otro()) + submenu.separador() + + # submenu = menu.submenu(_("History of this session"), Iconos.Copiar()) resp = menu.lanza() if resp: - resp() + if isinstance(resp, (str, unicode)): + if QTUtil2.pregunta(self, _("Are you sure you want to restore backup %s ?" % ("\n%s" % resp))): + um = QTUtil2.unMomento(self, _("Working...")) + self.dbop.rechistory(resp) + self.glines.refresh() + self.glines.gotop() + um.final() + else: + resp() def ta_massive(self): dicVar = self.configuracion.leeVariables("MASSIVE_OLINES") @@ -465,60 +240,69 @@ def ta_remove(self): tmpBP.cerrar() self.glines.refresh() - def train(self): - if self.train_test(): - menu = QTVarios.LCMenu(self) + menu = QTVarios.LCMenu(self) + trSSP, trEng = self.train_test() + if trSSP: menu.opcion("tr_sequential", _("Sequential"), Iconos.TrainSequential()) menu.separador() menu.opcion("tr_static", _("Static"), Iconos.TrainStatic()) menu.separador() menu.opcion("tr_positions", _("Positions"), Iconos.TrainPositions()) menu.separador() - submenu = menu.submenu(_("Configuration"), Iconos.Configurar()) + if trEng: + menu.opcion("tr_engines", _("With engines"), Iconos.TrainEngines()) + menu.separador() + submenu = menu.submenu(_("Configuration"), Iconos.Configurar()) + if trEng or trSSP: submenu.opcion("update", _("Update current trainings"), Iconos.Reindexar()) submenu.separador() - submenu.opcion("new", _("Re-create all trainings"), Iconos.Modificar()) - resp = menu.lanza() - if resp is None: - return - if resp.startswith("tr_"): - self.resultado = resp - self.accept() - elif resp == "new": - self.trainNew() - elif resp == "update": - self.trainUpdate() + submenu1 = submenu.submenu(_("Re-create all trainings"), Iconos.Modificar()) + submenu1.opcion("new_ssp", "%s - %s - %s" %(_("Sequential"), _("Static"), _("Positions")), Iconos.TrainSequential()) + submenu1.opcion("new_eng", "With engines", Iconos.TrainEngines()) + + resp = menu.lanza() + if resp is None: + return + if resp.startswith("tr_"): + self.resultado = resp + self.accept() + elif resp == "new_ssp": + self.trainNewSSP() + elif resp == "new_eng": + self.trainNewEngines() + elif resp == "update": + self.trainUpdateAll() def train_test(self): if len(self.dbop) == 0: - return False + return False, False training = self.dbop.training() - if training is None: - return self.trainNew() - return True + trainingEng = self.dbop.trainingEngines() + return training is not None, trainingEng is not None - def trainNew(self): + def trainNewSSP(self): training = self.dbop.training() - if training is None: - color = "WHITE" - random_order = False - max_moves = 0 - else: + color = "WHITE" + random_order = False + max_moves = 0 + + if training is not None: color = training["COLOR"] random_order = training["RANDOM"] max_moves = training["MAXMOVES"] - liGen = [(None, None)] + separador = FormLayout.separador + liGen = [separador] liJ = [(_("White"), "WHITE"), (_("Black"), "BLACK")] config = FormLayout.Combobox(_("Play with"), liJ) liGen.append((config, color)) - liGen.append((None, None)) + liGen.append(separador) liGen.append((_("Random order"), random_order)) - liGen.append((None, None)) + liGen.append(separador) liGen.append((_("Maximum number of moves (0=all)"), max_moves)) resultado = FormLayout.fedit(liGen, title=_("New training"), parent=self, anchoMinimo=360, icon=Iconos.Study()) @@ -531,12 +315,70 @@ def trainNew(self): reg["COLOR"], reg["RANDOM"], reg["MAXMOVES"] = liResp - self.dbop.createTraining(reg, self.procesador) + self.dbop.createTrainingSSP(reg, self.procesador) QTUtil2.mensaje(self, _("The trainings of this opening has been created")) - def trainUpdate(self): + def trainNewEngines(self): + training = self.dbop.trainingEngines() + color = "WHITE" + mandatory = 5 + control = 10 + lost_points = 20 + engine_control = self.configuracion.tutor.clave + engine_time = 5.0 + num_lista = 0 + + if training is not None: + color = training["COLOR"] + mandatory = training.get("MANDATORY", mandatory) + control = training.get("CONTROL", control) + lost_points = training.get("LOST_POINTS", lost_points) + engine_control = training.get("ENGINE_CONTROL", engine_control) + engine_time = training.get("ENGINE_TIME", engine_time) + num_lista = training.get("NUM_LISTA", num_lista) + + separador = FormLayout.separador + liGen = [separador] + + liJ = [(_("White"), "WHITE"), (_("Black"), "BLACK")] + config = FormLayout.Combobox(_("Play with"), liJ) + liGen.append((config, color)) + + liGen.append((_("Mandatory moves") + ":", mandatory)) + liGen.append(separador) + liGen.append((_("Moves until the control") + ":", control)) + liGen.append(separador) + liGen.append((_("Maximum number of centipawns lost to pass control") + ":", lost_points)) + liGen.append(separador) + + licombo = [("%2d. %s" %(n+1, ",".join(x)), n) for n, x in enumerate(self.dbop.listaEngines())] + config = FormLayout.Combobox(_("Bunch of engines"), licombo) + liGen.append((config, num_lista)) + liGen.append(separador) + + config = FormLayout.Combobox(_("Engine that does the control"), self.configuracion.comboMotoresCompleto()) + liGen.append((config, engine_control)) + liGen.append((_("Duration of analysis (secs)") + ":", float(engine_time))) + + resultado = FormLayout.fedit(liGen, title=_("With engines"), parent=self, anchoMinimo=360, icon=Iconos.Study()) + if resultado is None: + return + + accion, liResp = resultado + + reg = {} + + (reg["COLOR"], reg["MANDATORY"], reg["CONTROL"], reg["LOST_POINTS"], reg["NUM_LISTA"], + reg["ENGINE_CONTROL"], reg["ENGINE_TIME"] ) = liResp + + self.dbop.createTrainingEngines(reg, self.procesador) + + QTUtil2.mensaje(self, _("Created")) + + def trainUpdateAll(self): self.dbop.updateTraining(self.procesador) + self.dbop.updateTrainingEngines() QTUtil2.mensaje(self, _("The trainings have been updated")) def addPartida(self, partida): @@ -550,6 +392,7 @@ def addPartida(self, partida): self.glines.refresh() else: QTUtil2.mensError(self, _X("New line must begin with %1", self.partidabase.pgnSP())) + self.show_lines() def partidaActual(self): partida = Partida.Partida() @@ -567,6 +410,7 @@ def voyager2(self, partida): partida = Partida.Partida() partida.recuperaDeTexto(ptxt) self.addPartida(partida) + self.show_lines() def importar(self): menu = QTVarios.LCMenu(self) @@ -604,7 +448,7 @@ def haz_menu(frommenu, part): if resp is None: return tipo, partida = resp - if tipo == "pgn" : + if tipo == "pgn": self.importarPGN(partida) elif tipo == "polyglot": self.importarPolyglot(partida) @@ -619,6 +463,7 @@ def haz_menu(frommenu, part): elif tipo == "ol": fichero, partida = partida self.importarOtra(fichero, partida) + self.show_lines() def importarOtra(self, fichero, partida): um = QTUtil2.unMomento(self) @@ -637,7 +482,10 @@ def importarApertura(self, partida): partida.leerPV(ap.a1h8) self.addPartida(partida) - def importarLeeParam(self, titulo, dicData): + def importarLeeParam(self, titulo): + dicData = self.dbop.getconfig("IMPORTAR_LEEPARAM") + if not dicData: + dicData = {} liGen = [FormLayout.separador] liGen.append((None, _("Select a maximum number of moves (plies)
to consider from each game"))) @@ -649,15 +497,22 @@ def importarLeeParam(self, titulo, dicData): liGen.append((config, dicData.get("SIWHITE", True))) liGen.append(FormLayout.separador) + li = [(_("Only one best move"), True), (_("All best moves"), False)] + config = FormLayout.Combobox(_("Best move"), li) + liGen.append((config, dicData.get("ONLYONE", True))) + liGen.append(FormLayout.separador) + liGen.append((FormLayout.Spinbox(_("Minimum moves must have each line"), 0, 99, 50), dicData.get("MINMOVES", 0))) resultado = FormLayout.fedit(liGen, title=titulo, parent=self, anchoMinimo=360, icon=Iconos.PuntoNaranja()) if resultado: accion, liResp = resultado - depth, siWhite, minMoves = liResp + depth, siWhite, onlyone, minMoves = liResp dicData["DEPTH"] = depth dicData["SIWHITE"] = siWhite + dicData["ONLYONE"] = onlyone dicData["MINMOVES"] = minMoves + self.dbop.setconfig("IMPORTAR_LEEPARAM", dicData) self.configuracion.escVariables("WBG_MOVES", dicData) return dicData return None @@ -665,12 +520,11 @@ def importarLeeParam(self, titulo, dicData): def importarSummary(self, partida): nomfichgames = QTVarios.selectDB(self, self.configuracion, False, True) if nomfichgames: - previo = self.configuracion.leeVariables("OPENINGLINES") - dicData = self.importarLeeParam(_("Database summary"), previo) + dicData = self.importarLeeParam(_("Database summary")) if dicData: ficheroSummary = nomfichgames + "_s1" - depth, siWhite, minMoves = dicData["DEPTH"], dicData["SIWHITE"], dicData["MINMOVES"] - self.dbop.importarSummary(self, partida, ficheroSummary, depth, siWhite, minMoves) + depth, siWhite, onlyone, minMoves = dicData["DEPTH"], dicData["SIWHITE"], dicData["ONLYONE"], dicData["MINMOVES"] + self.dbop.importarSummary(self, partida, ficheroSummary, depth, siWhite, onlyone, minMoves) self.glines.refresh() self.glines.gotop() @@ -679,20 +533,33 @@ def importarPolyglot(self, partida): listaLibros.recuperaVar(self.configuracion.ficheroBooks) listaLibros.comprueba() + dicData = self.dbop.getconfig("IMPORT_POLYGLOT") + bookW = listaLibros.lista[0] + bookB = listaLibros.lista[0] + if dicData: + book = listaLibros.buscaLibro(dicData["BOOKW"]) + if book: + bookW = book + book = listaLibros.buscaLibro(dicData["BOOKB"]) + if book: + bookB = book + liGen = [FormLayout.separador] - li = [(book.nombre, book) for book in listaLibros.lista] + li = [(bookx.nombre, bookx) for bookx in listaLibros.lista] config = FormLayout.Combobox(_("Book that plays white side"), li) - liGen.append((config, listaLibros.lista[0])) + liGen.append((config, bookW)) liGen.append(FormLayout.separador) config = FormLayout.Combobox(_("Book that plays black side"), li) - liGen.append((config, listaLibros.lista[0])) + liGen.append((config, bookB)) liGen.append(FormLayout.separador) resultado = FormLayout.fedit(liGen, title=_("Polyglot book"), parent=self, anchoMinimo=360, icon=Iconos.Libros()) if resultado: accion, liResp = resultado bookW, bookB = liResp + dicData = {"BOOKW":bookW.nombre, "BOOKB":bookB.nombre} + self.dbop.setconfig("IMPORT_POLYGLOT", dicData) else: return @@ -700,11 +567,10 @@ def importarPolyglot(self, partida): bookB.polyglot() titulo = bookW.nombre if bookW==bookB else "%s/%s" % (bookW.nombre, bookB.nombre) - dicData = self.configuracion.leeVariables("OPENINGLINES") - dicData = self.importarLeeParam(titulo, dicData) + dicData = self.importarLeeParam(titulo) if dicData: - depth, siWhite, minMoves = dicData["DEPTH"], dicData["SIWHITE"], dicData["MINMOVES"] - self.dbop.importarPolyglot(self, partida, bookW, bookB, titulo, depth, siWhite, minMoves) + depth, siWhite, onlyone, minMoves = dicData["DEPTH"], dicData["SIWHITE"], dicData["ONLYONE"], dicData["MINMOVES"] + self.dbop.importarPolyglot(self, partida, bookW, bookB, titulo, depth, siWhite, onlyone, minMoves) self.glines.refresh() self.glines.gotop() @@ -902,6 +768,7 @@ def borrar_move(self): self.dbop[linea] = partida self.goto_finlinea() + self.show_lines() def borrar(self): tam_dbop = len(self.dbop) @@ -922,10 +789,11 @@ def borrar(self): resp = menu.lanza() if resp == "current": + self.dbop.saveHistory(_("Remove line %d") % (current+1,)) del self.dbop[current] self.goto_inilinea() - else: + elif resp is not None: liGen = [FormLayout.separador] config = FormLayout.Editbox("
" + _("Lines") + "
" + _("By example:") + " -5,8-12,14,19-", @@ -952,13 +820,12 @@ def borrar(self): sli.append(cad) cli = "\n".join(sli) if QTUtil2.pregunta(self, _("Do you want to remove the next lines?") + "\n\n" + cli): - li.sort(reverse=True) um = QTUtil2.unMomento(self, _("Working...")) - for num in li: - del self.dbop[num-1] + self.dbop.removeLines([x-1 for x in li], cli) self.glines.refresh() self.goto_inilinea() um.final() + self.show_lines() def goto_inilinea(self): nlines = len(self.dbop) @@ -1028,11 +895,19 @@ def goto_next_lipv(self, lipv): self.glines.goto(fila, ncol) self.glines.refresh() - def terminar(self): + def procesosFinales(self): + self.dbop.setconfig("WHITEBOTTOM", self.pboard.tablero.siBlancasAbajo) self.tabsanalisis.saveConfig() + self.dbop.close() self.guardarVideo() + + def terminar(self): + self.procesosFinales() self.accept() + def closeEvent(self, event): + self.procesosFinales() + def mueveHumano(self, partida): # Estamos en la misma linea ? # recno = self.glines.recno() @@ -1057,91 +932,7 @@ def mueveHumano(self, partida): self.glines.refresh() self.glines.goto(fila, ncol) - - -class WStaticTraining(QTVarios.WDialogo): - def __init__(self, procesador, dbop): - self.training = dbop.training() - self.ligames = self.training["LIGAMES_STATIC"] - self.num_games = len(self.ligames) - self.elems_fila = 10 - if self.num_games < self.elems_fila: - self.elems_fila = self.num_games - self.num_filas = (self.num_games-1) / self.elems_fila + 1 - self.seleccionado = None - - titulo = "%s - %s" % (_("Opening lines"), _("Static training")) - - extparam = "openlines_static_%s" % dbop.nomFichero - - QTVarios.WDialogo.__init__(self, procesador.pantalla, titulo, Iconos.TrainStatic(), extparam) - - lb = Controles.LB(self, dbop.gettitle()) - lb.ponFondoN("#BDDBE8").alinCentrado().ponTipoLetra(puntos=14) - - # Toolbar - tb = Controles.TBrutina(self) - tb.new(_("Close"), Iconos.MainMenu(), self.terminar) - - # Lista - ancho = 42 - oColumnas = Columnas.ListaColumnas() - oColumnas.nueva("FILA", "", 36, siCentrado=True) - for x in range(self.elems_fila): - oColumnas.nueva("COL%d" % x, "%d" % (x+1,), ancho, siCentrado=True, edicion=Delegados.PmIconosWeather()) - - self.grid = Grid.Grid(self, oColumnas, altoFila=ancho, background="white") - self.grid.setAlternatingRowColors(False) - self.grid.tipoLetra(puntos=10, peso=500) - nAnchoPgn = self.grid.anchoColumnas() + 20 - self.grid.setMinimumWidth(nAnchoPgn) - - ly = Colocacion.V().control(lb).control(tb).control(self.grid) - self.setLayout(ly) - - alto = self.num_filas*ancho + 146 - self.recuperarVideo(siTam=True, altoDefecto=alto, anchoDefecto=nAnchoPgn) - - def terminar(self): - - self.guardarVideo() - self.reject() - - def gridNumDatos(self, grid): - return self.num_filas - - def gridDato(self, grid, fila, oColumna): - col = oColumna.clave - if col == "FILA": - return "%d" % fila - elif col.startswith("COL"): - num = fila*self.elems_fila + int(col[3:]) - if num >= self.num_games: - return None - game = self.ligames[num] - sinerror = game["NOERROR"] - return str(sinerror) if sinerror < 4 else "4" - - def gridDobleClick(self, grid, fila, oColumna): - col = oColumna.clave - if col.startswith("COL"): - num = fila*self.elems_fila + int(col[3:]) - if num >= self.num_games: - return - self.seleccionado = num - self.guardarVideo() - self.accept() - - -def selectLine(procesador, dbop): - w = WStaticTraining(procesador, dbop) - w.exec_() - return w.seleccionado - - -def openingLines(procesador): - w = WOpeningLines(procesador) - return w.resultado if w.exec_() else None + self.show_lines() def study(procesador, fichero): diff --git a/Code/QT/PantallaOpeningLines.py b/Code/QT/PantallaOpeningLines.py new file mode 100644 index 0000000..0c569d6 --- /dev/null +++ b/Code/QT/PantallaOpeningLines.py @@ -0,0 +1,396 @@ +import os +import os.path + +from PyQt4 import QtGui + +from Code import VarGen +from Code import Partida +from Code import OpeningLines +from Code.QT import Colocacion +from Code.QT import Columnas +from Code.QT import Controles +from Code.QT import Grid +from Code.QT import Iconos +from Code.QT import PantallaAperturas +from Code.QT import QTUtil2 +from Code.QT import QTVarios +from Code.QT import Delegados +from Code.QT import FormLayout + + +class WOpeningLines(QTVarios.WDialogo): + def __init__(self, procesador): + + self.procesador = procesador + self.configuracion = procesador.configuracion + self.resultado = None + self.listaOpenings = OpeningLines.ListaOpenings(self.configuracion) + + QTVarios.WDialogo.__init__(self, procesador.pantalla, self.getTitulo(), Iconos.OpeningLines(), "openingLines") + + oColumnas = Columnas.ListaColumnas() + oColumnas.nueva("TITLE", _("Name"), 240) + oColumnas.nueva("BASEPV", _("First moves"), 280) + oColumnas.nueva("NUMLINES", _("Lines"), 80, siCentrado=True) + oColumnas.nueva("FILE", _("File"), 200) + self.glista = Grid.Grid(self, oColumnas, siSelecFilas=True, siSeleccionMultiple=True) + + sp = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + + liAcciones = ( + (_("Close"), Iconos.MainMenu(), self.terminar), None, + (_("Edit"), Iconos.Modificar(), self.modificar), None, + (_("New"), Iconos.Nuevo(), self.new), None, + (_("Copy"), Iconos.Copiar(), self.copy), None, + (_("Rename"), Iconos.Modificar(), self.renombrar), None, + (_("Up"), Iconos.Arriba(), self.arriba), + (_("Down"), Iconos.Abajo(), self.abajo), None, + (_("Remove"), Iconos.Borrar(), self.borrar), None, + (_("Reinit"), Iconos.Reiniciar(), self.reiniciar), None, + (_("Folder"), Iconos.File(), self.changeFolder), None, + ) + tb = Controles.TBrutina(self, liAcciones) + + tb.setSizePolicy(sp) + + liAcciones = ( + (_("Sequential"), Iconos.TrainSequential(), self.tr_sequential), None, + (_("Static"), Iconos.TrainStatic(), self.tr_static), None, + (_("Positions"), Iconos.TrainPositions(), self.tr_positions), None, + (_("With engines"), Iconos.TrainEngines(), self.tr_engines), + ) + self.tbtrain = tbtrain = Controles.TBrutina(self, liAcciones, siTexto=False) + + lbtrain = Controles.LB(self, _("Trainings")).alinCentrado().ponFondoN("lightgray") + lytrain = Colocacion.V().control(lbtrain).control(tbtrain).margen(0) + self.wtrain = QtGui.QWidget() + self.wtrain.setLayout(lytrain) + + lytb = Colocacion.H().control(tb).control(self.wtrain).margen(0) + wtb = QtGui.QWidget() + wtb.setFixedHeight(62) + wtb.setLayout(lytb) + + # Colocamos + + ly = Colocacion.V().control(wtb).control(self.glista).margen(4) + + self.setLayout(ly) + + self.registrarGrid(self.glista) + self.recuperarVideo(anchoDefecto=self.glista.anchoColumnas()+20) + + self.wtrain.setVisible(False) + self.glista.gotop() + + def getTitulo(self): + return "%s [%s]" % (_("Opening lines"), os.path.relpath(self.listaOpenings.folder)) + + def tr_(self, tipo): + recno = self.glista.recno() + op = self.listaOpenings[recno] + op["TRAIN"] = tipo + self.resultado = op + self.terminar() + + def tr_sequential(self): + self.tr_("sequential") + + def tr_static(self): + self.tr_("static") + + def tr_positions(self): + self.tr_("positions") + + def tr_engines(self): + self.tr_("engines") + + def reiniciar(self): + self.listaOpenings.reiniciar() + self.glista.refresh() + self.glista.gotop() + if len(self.listaOpenings) == 0: + self.wtrain.setVisible(False) + + def changeFolder(self): + nof = _("New opening folder") + base = self.configuracion.folderBaseOpenings + li = [x for x in os.listdir(base) if os.path.isdir(os.path.join(base, x))] + menu = QTVarios.LCMenu(self) + rondo = QTVarios.rondoPuntos() + menu.opcion("", _("Home folder"), Iconos.Home()) + menu.separador() + for x in li: + menu.opcion(x, x, rondo.otro()) + menu.separador() + menu.opcion(":n", nof, Iconos.Nuevo()) + if VarGen.isWindows: + menu.separador() + menu.opcion(":m", _("Direct maintenance"), Iconos.Configurar()) + + resp = menu.lanza() + if resp is not None: + if resp == ":m": + os.startfile(base) + return + + elif resp == ":n": + name = "" + error = "" + while True: + liGen = [FormLayout.separador] + liGen.append((nof + ":", name)) + if error: + liGen.append(FormLayout.separador) + liGen.append((None, error)) + + resultado = FormLayout.fedit(liGen, title=nof, parent=self, + icon=Iconos.OpeningLines(), anchoMinimo=460) + if resultado: + accion, liResp = resultado + name = liResp[0].strip() + if name: + path = os.path.join(base, name) + try: + os.mkdir(path) + except: + error = _("Unable to create this folder") + continue + if not os.path.isdir(path): + continue + break + else: + return + else: + path = os.path.join(base, resp) + + path = os.path.relpath(path) + self.configuracion.folderOpenings = path + self.configuracion.graba() + self.listaOpenings = OpeningLines.ListaOpenings(self.configuracion) + self.glista.refresh() + self.glista.gotop() + if len(self.listaOpenings) == 0: + self.wtrain.setVisible(False) + self.setWindowTitle(self.getTitulo()) + + def arriba(self): + fila = self.glista.recno() + if self.listaOpenings.arriba(fila): + self.glista.goto(fila - 1, 0) + self.glista.refresh() + + def abajo(self): + fila = self.glista.recno() + if self.listaOpenings.abajo(fila): + self.glista.goto(fila + 1, 0) + self.glista.refresh() + + def modificar(self): + recno = self.glista.recno() + if recno >= 0: + self.resultado = self.listaOpenings[recno] + else: + self.resultado = None + self.guardarVideo() + self.accept() + + def gridDobleClick(self, grid, fila, oColumna): + recno = self.glista.recno() + if recno >= 0: + self.modificar() + + def new(self): + si_expl = len(self.listaOpenings) < 4 + if si_expl: + QTUtil2.mensaje(self, _("First you must select the initial moves.")) + w = PantallaAperturas.WAperturas(self, self.configuracion, None) + if w.exec_(): + ap = w.resultado() + pv = ap.a1h8 if ap else "" + name = ap.nombre if ap else "" + else: + return + + if si_expl: + QTUtil2.mensaje(self, _("Secondly you have to choose a name for this opening studio.")) + + name = self.get_nombre(name) + if name: + file = self.listaOpenings.select_filename(name) + self.listaOpenings.new(file, pv, name) + self.resultado = self.listaOpenings[-1] + self.guardarVideo() + self.accept() + + def copy(self): + recno = self.glista.recno() + if recno >= 0: + self.listaOpenings.copy(recno) + self.glista.refresh() + + def get_nombre(self, name): + liGen = [(None, None)] + liGen.append((_("Opening studio name") + ":", name)) + resultado = FormLayout.fedit(liGen, title=_("Opening studio name"), parent=self, icon=Iconos.OpeningLines(), anchoMinimo=460) + if resultado: + accion, liResp = resultado + name = liResp[0].strip() + if name: + return name + return None + + def renombrar(self): + fila = self.glista.recno() + if fila >= 0: + op = self.listaOpenings[fila] + name = self.get_nombre(op["title"]) + if name: + self.listaOpenings.change_title(fila, name) + self.glista.refresh() + + def borrar(self): + li = self.glista.recnosSeleccionados() + if len(li) > 0: + mens = _("Do you want to delete all selected records?") + mens += "\n" + for num, fila in enumerate(li, 1): + mens += "\n%d. %s" % (num, self.listaOpenings[fila]["title"]) + if QTUtil2.pregunta(self, mens): + li.sort(reverse=True) + for fila in li: + del self.listaOpenings[fila] + recno = self.glista.recno() + self.glista.refresh() + if recno >= len(self.listaOpenings): + self.glista.gobottom() + + def gridNumDatos(self, grid): + return len(self.listaOpenings) + + def gridDato(self, grid, fila, oColumna): + col = oColumna.clave + op = self.listaOpenings[fila] + if col == "TITLE": + return op["title"] + elif col == "FILE": + return op["file"] + elif col == "NUMLINES": + return op["lines"] + elif col == "BASEPV": + pv = op["pv"] + if pv: + p = Partida.Partida() + p.leerPV(pv) + return p.pgnBaseRAW() + else: + return "" + + def gridCambiadoRegistro(self, grid, fila, columna): + ok_ssp = False + ok_eng = False + if fila >= 0: + op = self.listaOpenings[fila] + ok_ssp = op.get("withtrainings", False) + ok_eng = op.get("withtrainings_engine", False) + + if ok_ssp or ok_eng: + self.wtrain.setVisible(True) + self.tbtrain.setAccionVisible(self.tr_sequential, ok_ssp) + self.tbtrain.setAccionVisible(self.tr_static, ok_ssp) + self.tbtrain.setAccionVisible(self.tr_positions, ok_ssp) + self.tbtrain.setAccionVisible(self.tr_engines, ok_eng) + else: + self.wtrain.setVisible(False) + def closeEvent(self, event): # Cierre con X + self.guardarVideo() + + def terminar(self): + self.guardarVideo() + self.accept() + + +class WStaticTraining(QTVarios.WDialogo): + def __init__(self, procesador, dbop): + self.training = dbop.training() + self.ligames = self.training["LIGAMES_STATIC"] + self.num_games = len(self.ligames) + self.elems_fila = 10 + if self.num_games < self.elems_fila: + self.elems_fila = self.num_games + self.num_filas = (self.num_games-1) / self.elems_fila + 1 + self.seleccionado = None + + titulo = "%s - %s" % (_("Opening lines"), _("Static training")) + + extparam = "openlines_static_%s" % dbop.nomFichero + + QTVarios.WDialogo.__init__(self, procesador.pantalla, titulo, Iconos.TrainStatic(), extparam) + + lb = Controles.LB(self, dbop.gettitle()) + lb.ponFondoN("#BDDBE8").alinCentrado().ponTipoLetra(puntos=14) + + # Toolbar + tb = Controles.TBrutina(self) + tb.new(_("Close"), Iconos.MainMenu(), self.terminar) + + # Lista + ancho = 42 + oColumnas = Columnas.ListaColumnas() + oColumnas.nueva("FILA", "", 36, siCentrado=True) + for x in range(self.elems_fila): + oColumnas.nueva("COL%d" % x, "%d" % (x+1,), ancho, siCentrado=True, edicion=Delegados.PmIconosWeather()) + + self.grid = Grid.Grid(self, oColumnas, altoFila=ancho, background="white") + self.grid.setAlternatingRowColors(False) + self.grid.tipoLetra(puntos=10, peso=500) + nAnchoPgn = self.grid.anchoColumnas() + 20 + self.grid.setMinimumWidth(nAnchoPgn) + + ly = Colocacion.V().control(lb).control(tb).control(self.grid) + self.setLayout(ly) + + alto = self.num_filas*ancho + 146 + self.recuperarVideo(siTam=True, altoDefecto=alto, anchoDefecto=nAnchoPgn) + + def terminar(self): + + self.guardarVideo() + self.reject() + + def gridNumDatos(self, grid): + return self.num_filas + + def gridDato(self, grid, fila, oColumna): + col = oColumna.clave + if col == "FILA": + return "%d" % fila + elif col.startswith("COL"): + num = fila*self.elems_fila + int(col[3:]) + if num >= self.num_games: + return None + game = self.ligames[num] + sinerror = game["NOERROR"] + return str(sinerror) if sinerror < 4 else "4" + + def gridDobleClick(self, grid, fila, oColumna): + col = oColumna.clave + if col.startswith("COL"): + num = fila*self.elems_fila + int(col[3:]) + if num >= self.num_games: + return + self.seleccionado = num + self.guardarVideo() + self.accept() + + +def selectLine(procesador, dbop): + w = WStaticTraining(procesador, dbop) + w.exec_() + return w.seleccionado + + +def openingLines(procesador): + w = WOpeningLines(procesador) + return w.resultado if w.exec_() else None + diff --git a/Code/QT/PantallaTorneos.py b/Code/QT/PantallaTorneos.py index f7b5e0f..217ce91 100644 --- a/Code/QT/PantallaTorneos.py +++ b/Code/QT/PantallaTorneos.py @@ -183,7 +183,6 @@ def __init__(self, wParent, torneo): lbBookDepth = Controles.LB(self, _("Max depth of book (0=Maximum)") + ": ") self.sbBookDepth = Controles.SB(self, torneo.bookDepth(), 0, 200) - # Posicion inicial lbFEN = Controles.LB(self, _("Initial position") + ": ") self.fen = torneo.fen() diff --git a/Code/QT/QTUtil.py b/Code/QT/QTUtil.py index d35de5d..f6cd498 100644 --- a/Code/QT/QTUtil.py +++ b/Code/QT/QTUtil.py @@ -119,6 +119,7 @@ def escondeWindow(window): window.move(screen.width()*2, 0) return pos + class EscondeWindow: def __init__(self, window): self.window = window @@ -133,6 +134,7 @@ def __exit__(self, type, value, traceback): self.window.move(self.pos) self.window.show() + def colorIcon(xcolor, ancho, alto): color = QtGui.QColor(xcolor) pm = QtGui.QPixmap(ancho, alto) diff --git a/Code/QT/QTUtil2.py b/Code/QT/QTUtil2.py index 9525e73..6ce54ce 100644 --- a/Code/QT/QTUtil2.py +++ b/Code/QT/QTUtil2.py @@ -508,6 +508,15 @@ def mensErrorSobreControl(owner, mens, control): msgBox.exec_() +def mensajeEnPunto(owner, mens, titulo, point, dif=5): + if titulo is None: + titulo = _("Information") + msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Information, titulo, resalta(mens), parent=owner) + msgBox.addButton(_("Continue"), QtGui.QMessageBox.ActionRole) + msgBox.move(point.x()+dif, point.y()+dif) + msgBox.exec_() + + def pregunta(parent, mens, etiSi=None, etiNo=None, si_top=False): msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, _("Question"), resalta(mens), parent=parent) if etiSi is None: diff --git a/Code/QT/Tablero.py b/Code/QT/Tablero.py index 5db4156..8cc7bb0 100644 --- a/Code/QT/Tablero.py +++ b/Code/QT/Tablero.py @@ -67,7 +67,6 @@ def __init__(self, parent, confTablero, siMenuVisual=True, siDirector=True): self.blindfold = None self.blindfoldModoPosicion = False - self.siInicializado = False self.ultPosicion = None @@ -636,7 +635,6 @@ def dropEvent(self, event): # self.dispatchDrop(a1h8, str(dato)) # event.setDropAction(QtCore.Qt.IgnoreAction) - def showKeys(self): liKeys = [ (_("ALT") + "-F", _("Flip the board")), @@ -1344,7 +1342,6 @@ def setDispatchMove(self, rutina): if siActiva: piezaSC.setDispatchMove(rutina) - def activaTodas(self): self.siActivasPiezas = True for num, una in enumerate(self.liPiezas): @@ -1469,7 +1466,6 @@ def creaFlechaTutor(self, desdeA1h8, hastaA1h8, factor): bf.forma = "1" bf.posicion.orden = kZvalue_pieza + 1 - flecha = self.creaFlecha(bf) self.liFlechas.append(flecha) flecha.show() diff --git a/Code/QT/Voyager.py b/Code/QT/Voyager.py index 4c981a8..7d11749 100644 --- a/Code/QT/Voyager.py +++ b/Code/QT/Voyager.py @@ -328,9 +328,9 @@ def setPosicion(self, posicion): # if self.posicion.siExistePieza("k") != 1: # QTUtil2.mensError(self, _("King") + "-" + _("Black") + "???") # return - # + # self.actPosicion() - # + # self.fen = self.posicion.fen() # Hace control de enroques y EnPassant # if self.fen == ControlPosicion.FEN_INICIAL: # self.fen = "" diff --git a/Code/QT/WBG_Players.py b/Code/QT/WBG_Players.py index 65fb7cb..d8797de 100644 --- a/Code/QT/WBG_Players.py +++ b/Code/QT/WBG_Players.py @@ -41,7 +41,6 @@ def __init__(self, side, rutina): ply4 = Controles.PB(self, _("%d ply") % 4, self.run_p4, plano=False).anchoFijo(ancho) ply5 = Controles.PB(self, _("%d ply") % 5, self.run_p5, plano=False).anchoFijo(ancho) - self.sbply = Controles.SB(self, 0, 0, 100) self.sbply.capturaCambiado(self.run_p) lbply = Controles.LB(self, _("Plies")) @@ -588,11 +587,9 @@ def ordena(empieza, nivel): um.final() - self.data = data self.gridOpeningWhite.refresh() self.gridOpeningBlack.refresh() self.gridMovesWhite.refresh() self.gridMovesBlack.refresh() - diff --git a/Code/QT/WBase.py b/Code/QT/WBase.py index 0727613..06b36b1 100644 --- a/Code/QT/WBase.py +++ b/Code/QT/WBase.py @@ -40,7 +40,6 @@ def __init__(self, parent, gestor): self.conAtajos = True - lyAI = Colocacion.H().relleno(1).control(self.capturas).otroi(lyT).otroi(lyBI).relleno(1).margen(0) ly = Colocacion.V().control(self.tb).relleno().otro(lyAI).relleno().margen(2) @@ -84,7 +83,6 @@ def lanzaAtajosALT(self, key): if self.conAtajos: self.gestor.lanzaAtajosALT(key) - def creaTablero(self): ae = QTUtil.altoEscritorio() mx = 64 if ae >= 750 else 48 @@ -112,7 +110,6 @@ def ponWhiteBlack(self, white, black): oColumnas.liColumnas[1].cabecera = white if white else _("White") oColumnas.liColumnas[2].cabecera = black if black else _("Black") - def creaBloqueInformacion(self): configuracion = self.gestor.configuracion nAnchoPgn = configuracion.anchoPGN @@ -450,7 +447,6 @@ def gridDato(self, grid, fila, oColumna): return pgn, color, info, indicadorInicial, stNAGS - def gridPonValor(self, grid, fila, oColumna, valor): pass diff --git a/Code/RunKibitzer.py b/Code/RunKibitzer.py index 25f24ad..8155d1b 100644 --- a/Code/RunKibitzer.py +++ b/Code/RunKibitzer.py @@ -14,6 +14,11 @@ from Code import Configuracion from Code import Partida from Code import EngineThread +from Code import Util +from Code import VarGen +from Code import XMotorRespuesta +from Code import Kibitzers +from Code import Books from Code.QT import Colocacion from Code.QT import Columnas from Code.QT import Controles @@ -24,11 +29,8 @@ from Code.QT import QTUtil2 from Code.QT import QTVarios from Code.QT import Tablero +from Code.QT import Delegados from Code.QT import PantallaKibitzers -from Code import Util -from Code import VarGen -from Code import XMotorRespuesta -from Code import Kibitzers CONFIGURACION = "C" FEN = "F" @@ -36,6 +38,294 @@ COPYCLIPBOARD = "P" +class VentanaPolyglot(QtGui.QDialog): + def __init__(self, cpu): + QtGui.QDialog.__init__(self) + + self.cpu = cpu + + dicVideo = self.cpu.dic_video + if not dicVideo: + dicVideo = {} + + self.siTop = dicVideo.get("SITOP", True) + self.siShowTablero = dicVideo.get("SHOW_TABLERO", True) + self.posicion = ControlPosicion.ControlPosicion() + + self.fen = "" + self.siPlay = True + + self.setWindowTitle(cpu.titulo) + self.setWindowIcon(Iconos.Book()) + + self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinimizeButtonHint) + + self.setBackgroundRole(QtGui.QPalette.Light) + + configuracion = VarGen.configuracion = cpu.configuracion + + VarGen.todasPiezas = Piezas.TodasPiezas() + confTablero = cpu.configuracion.confTablero("kib" + cpu.kibitzer.huella, 24) + self.tablero = Tablero.Tablero(self, confTablero) + self.tablero.crea() + Delegados.generaPM(self.tablero.piezas) + + book = Books.Libro("P", cpu.kibitzer.nombre, cpu.kibitzer.exe, True) + self.book = book + book.polyglot() + self.li_moves = [] + + self.siFigurines = configuracion.figurinesPGN + + oColumnas = Columnas.ListaColumnas() + delegado = Delegados.EtiquetaPOS(True, siLineas=False) if self.siFigurines else None + for x in range(20): + oColumnas.nueva(x, "", 80, siCentrado=True, edicion = delegado) + self.grid_moves = Grid.Grid(self, oColumnas, siSelecFilas=True, siCabeceraMovible=False, siCabeceraVisible=False) + self.grid_moves.tipoLetra(puntos=configuracion.puntosPGN) + self.grid_moves.ponAltoFila(configuracion.altoFilaPGN) + + liAcciones = ( + (_("Quit"), Iconos.Kibitzer_Terminar(), self.terminar), + (_("Continue"), Iconos.Kibitzer_Continuar(), self.play), + (_("Pause"), Iconos.Kibitzer_Pausa(), self.pause), + (_("Board"), Iconos.Tablero(), self.confTablero), + ("%s: %s" % (_("Enable"), _("window on top")), Iconos.Top(), self.windowTop), + ("%s: %s" % (_("Disable"), _("window on top")), Iconos.Bottom(), self.windowBottom), + ) + self.tb = Controles.TBrutina(self, liAcciones, siTexto=False, tamIcon=16) + self.tb.setAccionVisible(self.play, False) + + ly1 = Colocacion.H().control(self.tb) + ly2 = Colocacion.V().otro(ly1).control(self.grid_moves) + + layout = Colocacion.H().control(self.tablero).otro(ly2) + self.setLayout(layout) + + self.timer = QtCore.QTimer(self) + self.connect(self.timer, QtCore.SIGNAL("timeout()"), cpu.compruebaInput) + self.timer.start(200) + + if not self.siShowTablero: + self.tablero.hide() + self.recuperarVideo(dicVideo) + self.ponFlags() + + def ponFen(self, fen): + self.posicion = ControlPosicion.ControlPosicion() + self.posicion.leeFen(fen) + self.fen = fen + + if self.siPlay: + self.siW = self.posicion.siBlancas + self.centipawns = self.cpu.configuracion.centipawns + + self.tablero.ponPosicion(self.posicion) + self.li_moves = self.book.almListaJugadas(fen) + for alm in self.li_moves: + alm.nivel = 0 + alm.dato = [""] * 20 + alm.dato[0] = alm.pgn + alm.dato[1] = alm.porc + alm.dato[2] = "%d" % alm.weight + self.grid_moves.gotop() + self.grid_moves.refresh() + self.ponFlecha(0) + + def ponFlags(self): + if self.siTop: + flags = self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint + else: + flags = self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint + self.setWindowFlags(flags) + self.tb.setAccionVisible(self.windowTop, not self.siTop) + self.tb.setAccionVisible(self.windowBottom, self.siTop) + self.show() + + def windowTop(self): + self.siTop = True + self.ponFlags() + + def windowBottom(self): + self.siTop = False + self.ponFlags() + + def terminar(self): + self.finalizar() + self.accept() + + def pause(self): + self.siPlay = False + self.tb.setPosVisible(1, True) + self.tb.setPosVisible(2, False) + + def play(self): + self.siPlay = True + self.tb.setPosVisible(1, False) + self.tb.setPosVisible(2, True) + self.ponFen(self.fen) + + def siAnalizar(self): + siW = " w " in self.fen + if not self.siPlay or \ + (siW and (not self.siBlancas)) or \ + ((not siW) and (not self.siNegras)): + return False + return True + + def color(self): + menu = QTVarios.LCMenu(self) + menu.opcion("blancas", _("White"), Iconos.PuntoNaranja()) + menu.opcion("negras", _("Black"), Iconos.PuntoNegro()) + menu.opcion("blancasnegras", "%s + %s" % (_("White"), _("Black")), Iconos.PuntoVerde()) + resp = menu.lanza() + if resp: + self.siNegras = True + self.siBlancas = True + if resp == "blancas": + self.siNegras = False + elif resp == "negras": + self.siBlancas = False + if self.siAnalizar(): + self.ponFen(self.fen) + + def closeEvent(self, event): + self.finalizar() + + def finalizar(self): + self.guardarVideo() + + def guardarVideo(self): + dic = {} + + pos = self.pos() + dic["_POSICION_"] = "%d,%d" % (pos.x(), pos.y()) + + tam = self.size() + dic["_SIZE_"] = "%d,%d" % (tam.width(), tam.height()) + + # dic["SHOW_TABLERO"] = self.siShowTablero + # dic["NARROWS"] = self.nArrows + + dic["SITOP"] = self.siTop + + self.cpu.save_video(dic) + + def recuperarVideo(self, dicVideo): + if dicVideo: + wE, hE = QTUtil.tamEscritorio() + x, y = dicVideo["_POSICION_"].split(",") + x = int(x) + y = int(y) + if not (0 <= x <= (wE - 50)): + x = 0 + if not (0 <= y <= (hE - 50)): + y = 0 + self.move(x, y) + if "_SIZE_" not in dicVideo: + w, h = self.width(), self.height() + for k in dicVideo: + if k.startswith("_TAMA"): + w, h = dicVideo[k].split(",") + else: + w, h = dicVideo["_SIZE_"].split(",") + w = int(w) + h = int(h) + if w > wE: + w = wE + elif w < 20: + w = 20 + if h > hE: + h = hE + elif h < 20: + h = 20 + self.resize(w, h) + + def gridNumDatos(self, grid): + return len(self.li_moves) + + def gridDato(self, grid, fila, oColumna): + mv = self.li_moves[fila] + li = mv.dato + key = int(oColumna.clave) + pgn = li[key] + if self.siFigurines: + siBlancas = " w " in mv.fen + return pgn, siBlancas, None, None, None, None, False, True + else: + return pgn + + def gridDobleClick(self, grid, fila, oColumna): + self.lee_subnivel(fila) + self.grid_moves.refresh() + + def gridBotonDerecho(self, grid, fila, columna, modificadores): + self.borra_subnivel(fila) + self.grid_moves.refresh() + + def gridCambiadoRegistro(self, grid, fila, oColumna): + self.ponFlecha(fila) + + def ponFlecha(self, fila): + if fila < len(self.li_moves): + alm = self.li_moves[fila] + self.tablero.ponFlechaSC(alm.desde, alm.hasta) + + def borra_subnivel(self, fila): + alm = self.li_moves[fila] + nv = alm.nivel + if nv == 0: + return + li = [] + for x in range(fila, 0, -1): + alm1 = self.li_moves[x] + if alm1.nivel < nv: + break + li.append(x) + for x in range(fila+1, len(self.li_moves)): + alm1 = self.li_moves[x] + if alm1.nivel < nv: + break + li.append(x) + li.sort(reverse=True) + for x in li: + del self.li_moves[x] + + def lee_subnivel(self, fila): + alm_base = self.li_moves[fila] + if alm_base.nivel >= 17: + return + LCEngine.setFen(alm_base.fen) + if LCEngine.movePV(alm_base.desde, alm_base.hasta, alm_base.coronacion): + fen = LCEngine.getFen() + for alm in self.book.almListaJugadas(fen): + nv = alm.nivel = alm_base.nivel + 1 + alm.dato = [""] * 20 + alm.dato[nv] = alm.pgn + alm.dato[nv+1] = alm.porc + alm.dato[nv+2] = "%d" % alm.weight + fila += 1 + self.li_moves.insert(fila, alm) + + def confTablero(self): + self.pause() + menu = QTVarios.LCMenu(self) + if self.siShowTablero: + menu.opcion("hide", _("Hide"), Iconos.PuntoNaranja()) + else: + menu.opcion("show", _("Show"), Iconos.PuntoNaranja()) + resp = menu.lanza() + if resp: + if resp == "hide": + self.siShowTablero = False + self.tablero.hide() + elif resp == "show": + self.siShowTablero = True + self.tablero.show() + self.guardarVideo() + self.play() + + class VentanaMultiPV(QtGui.QDialog): def __init__(self, cpu): QtGui.QDialog.__init__(self) @@ -1667,7 +1957,8 @@ def procesa(self, orden): self.titulo = self.kibitzer.nombre - self.configMotor = self.kibitzer.configMotor() + if self.kibitzer.tipo != "B": + self.configMotor = self.kibitzer.configMotor() self.key_video = "KIB%s" % self.kibitzer.huella self.dic_video = self.configuracion.restore_video(self.key_video) @@ -1716,6 +2007,8 @@ def lanzaVentana(self): self.ventana = VentanaMultiPV(self) elif self.tipo == "E": self.ventana = VentanaStockfishEval(self) + elif self.tipo == "B": + self.ventana = VentanaPolyglot(self) self.ventana.show() VarGen.gc = QTUtil.GarbageCollector() diff --git a/Code/SQL/DBF.py b/Code/SQL/DBF.py index 98f5940..d489aee 100644 --- a/Code/SQL/DBF.py +++ b/Code/SQL/DBF.py @@ -442,6 +442,7 @@ def soloGrabar(self, dicNuevo, siCommit=False): values = values[:-1] cSQL = "insert into %s(%s) values(%s)" % (self.ctabla, campos, values) + self.cursor.execute(cSQL, liValues) if siCommit: diff --git a/Code/Tacticas.py b/Code/Tacticas.py index f3689d8..233b10d 100644 --- a/Code/Tacticas.py +++ b/Code/Tacticas.py @@ -8,16 +8,15 @@ class BaseTactica: def __init__(self): - self.PUZZLES = 999 # Max number of puzzles in each block - self.JUMPS = [] # Puzzle repetitions, each puzzle can be repeated, this field determine separations among replicates, more repetitions are separated by comma, eg 3,5,7 + self.PUZZLES = 100 # Max number of puzzles in each block + self.JUMPS = [3, 5, 9, 17] # Puzzle repetitions, each puzzle can be repeated, this field determine separations among replicates, more repetitions are separated by comma, eg 3,5,7 self.FILLEND = 0 # Si los flecos que quedan al final se rellenan con los puzzles mas antiguos,por orden - self.REPEAT = [ - 0, ] # Block repetitions, Repetitions of total of puzzles, comma separated, indicating order of repetition as 0=original, 1=random, 2=previous, eg 1,2,0=3 repetitions, + self.REPEAT = [0, 1] # Block repetitions, Repetitions of total of puzzles, comma separated, indicating order of repetition as 0=original, 1=random, 2=previous, eg 1,2,0=3 repetitions, # first is random, second repeat previous, and third original. Total field in blanck is the same as 0 # Penalties, divided in blocks, by example 1,3,5,7 means first 25% # (100%/4) of puzzles has one position of penalty when error, second 25% # three and so on. Blank means no penalties. - self.PENALIZATION = [] + self.PENALIZATION = [1, 2, 3, 4] self.SHOWTEXT = [1, ] # Text while training, in blocks like penalties, 1,0,0,0, 25% Yes, rest No self.POINTVIEW = 0 # El que corresponda, 1 = White 2 = Black self.REFERENCE = "" @@ -42,7 +41,7 @@ def leeREFERENCE(dic, default=""): def leeJUMPS(dic, default=None): - JUMPS = [] if default is None else default + JUMPS = [3, 5, 9, 17] if default is None else default c = dic.get("JUMPS", None) if c: JUMPS = [int(x) for x in c.replace(" ", "").split(",")] @@ -58,7 +57,7 @@ def leeFILLEND(dic, default=None): def leeREPEAT(dic, default=None): - REPEAT = [0, ] if default is None else default + REPEAT = [0, 1] if default is None else default c = dic.get("REPEAT", None) if c: REPEAT = [int(x) for x in c.replace(" ", "").split(",")] @@ -66,7 +65,7 @@ def leeREPEAT(dic, default=None): def leePENALIZATION(dic, default=None): - PENALIZATION = [] if default is None else default + PENALIZATION = [1, 2, 3, 4] if default is None else default c = dic.get("PENALIZATION", None) if c: PENALIZATION = [int(x) for x in c.replace(" ", "").split(",")] @@ -84,12 +83,12 @@ def leeSHOWTEXT(dic, default=None): def leePUZZLES(dic, default=None): - PUZZLES = 999 if default is None else default + PUZZLES = 100 if default is None else default c = dic.get("PUZZLES", None) if c is not None and c.isdigit(): PUZZLES = int(c) if not PUZZLES: - PUZZLES = 999 + PUZZLES = 100 return PUZZLES @@ -153,7 +152,6 @@ def eligeTactica(self, resp): return Tactica(self, resp) def leeCommon(self): - dic = self.dic["COMMON"] if "COMMON" in self.dic else {} self.PUZZLES = leePUZZLES(dic) @@ -225,6 +223,15 @@ def ponPosActual(self, pos): with self.dbdatos() as db: db["POSACTIVE"] = pos + def siSaltoAutomatico(self): + with self.dbdatos() as db: + siauto = db["AUTOJUMP"] + return False if siauto is None else siauto + + def setSaltoAutomatico(self, siSalto): + with self.dbdatos() as db: + db["AUTOJUMP"] = siSalto + def dbdatos(self): return Util.DicSQL(self.fichero, tabla=self.nombre) diff --git a/Code/Torneo.py b/Code/Torneo.py index 6aa733a..2f4f14a 100644 --- a/Code/Torneo.py +++ b/Code/Torneo.py @@ -371,7 +371,7 @@ def book(self, valor=None): def bookDepth(self, valor=None): if valor is not None: self._bookDepth = valor - return self._bookDepth + return self._bookDepth if self._bookDepth else 0 def fichero(self): if self._nombre: diff --git a/Code/XGestorMotor.py b/Code/XGestorMotor.py index c9df3f5..aaea40c 100644 --- a/Code/XGestorMotor.py +++ b/Code/XGestorMotor.py @@ -365,5 +365,3 @@ def miraListaPV(self, fen, siUna): # lipv = [rm.movimiento() for rm in mrm.liMultiPV] return lipv[0] if siUna else lipv - - diff --git a/GenIconos/Formatos.tema b/GenIconos/Formatos.tema index 70c7791..af81b40 100644 --- a/GenIconos/Formatos.tema +++ b/GenIconos/Formatos.tema @@ -535,6 +535,7 @@ Positions nuvola runMono.png TrainSequential windows8 Running_32px.png TrainStatic windows8 Gymnastics_32px.png TrainPositions windows8 Pullups_32px.png +TrainEngines windows8 Treadmill_32px.png Error gnome 64px-Gnome-process-stop.png @@ -547,3 +548,13 @@ TOLchange gnome edit-redo.png Pack qt4 icon_list-all_circ.png Home nuvola folder_home.png + +Import8 windows8 Add_List_32px.png +Export8 windows8 Download_32px.png + +Tablas8 windows8 Exposure_32px.png + +Blancas8 windows8 Knight_32px_1.png +Negras8 windows8 Knight_32px.png + +Book windows8 Book_32px.png diff --git a/GenIconos/windows8/Add_List_32px.png b/GenIconos/windows8/Add_List_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..c31370efc0f796ce3f751fb3e1dc6d2e69fdd5ce GIT binary patch literal 1210 zcmV;r1V#IaP)vhoxgv(1a{7m5j_eT_V;k z8sZP#pZ#$^h9>)G;w5+snTeWAg_*dJki}?1X67wuT<2V%yA*|?PAKbETCDW;wcqDC zAGVgZv>&mH&3VuF-rpzhd(KIL|M|z{);}`vu1OBb{#?BnSpu{sqOsQojvbyA<4=9^ z0Q3*MzoV|!@@2=4yY;P!NdaDHo_lg5? zTL1_kY!)0KAWUlArDC19Z2%~U1_I*Ay9y26%5fRReX3ku$>q`CzM%b1<-ZhwoKw&=9AC94*P?_gbVW6^Fh543BO6>-{4qCN}vPDT0fUw5{NF)M)a}(a83Dd^yTkX(p ztVxS&enkN=@7|qBu)n(_!gPDy@XqPrBXaNo`JQ-2v!=!QX~wS zR0d9-Qk%`yb-9tE0D$AblUtA*oGbG@k61E}A8%ZQf5s1+*#wnB399#pLkfR*@vUb* z?B`GPz4CE7Ruq7W=H|3N+FG_`_*`Z_5*4Nl8Fq-(TqD_-`EHYS!xRJo_F z96vO4?6H8#`@~4^NJe9at*C6SHVN5)o5U-+JNu#G!6%|Nu zEa^;)bVGrij%^z*{NnB57Wm`DeCS_@M52rSC37~}$6OD0ny4(6PJB0ll$=F_rDj#g zL-o5b_t!kG1iTqN4<2Lg9~J{kp)OG!xH&c^JVfU1Zj7E8&m^J@Jz!F$0-isn(Q|(r zRh1-$45!uJR5^Zm;sserZ{kqla0pK^VbShfeFjn-kLjDg!)mG`Y4aLNLkg0`Xe4^3 zLlgj&%|@J8md%nsnd{5fp;0LX2Rifi+Is~)z1ehDP@?jmi7?Lj&I=EllwlO20MNi< zq>#$V-RA@sdf@2H1%Yg|@G=`gC>|mb5KYE}FWtzk<;nuUYN$qdDnfG#r#<_?Q2?}f zwvIZ+S(bhl%kqD~X0#v@P2iuTG;v9q_0309$>dO30MKfCTDmYfGeg-`L}#Aec4~~o zVz@MWEyyGkg9S%$E3~94_2$z=*(71KVB&I2Oi!c5-k7-&^uT5N(c)kHbUqkO#SaWW z_fjP9-t@oxjuBGDgV{l1_{N4!jat3hG8ww~04s^uop!sr+Gv1;S!ptPhViS<7rXr1 zwP1{6T_5$m`Yp|y2Y@R7EA@SF)S;BhkCHr{JQytlKE+Fz1&&Rgj7!t<807*qoM6N<$f+i_H^Z)<= literal 0 HcmV?d00001 diff --git a/GenIconos/windows8/Book_32px.png b/GenIconos/windows8/Book_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..fad502d2900edd206c6fc0ae6badb853d1164846 GIT binary patch literal 574 zcmV-E0>S->P)jK~z`?V_+ERfDt_a`f}&{GBGjE`TO_37`hy}d?sebKdkI5({j3E z^3ly;K^I_RWSp<7$|@|t%LJDaSC?TBQ_#9>i&L|j7gw#xgKfe8D05O)FF|r{m8`CFFP7GmedWjPI`Qsl0 zBNO8vT#7M5fSZRYYuD!2(|-T{$A?Q3DdLQb4FA~JnWvGWXJn}b04I=$DLU)%8vpb%7 literal 0 HcmV?d00001 diff --git a/GenIconos/windows8/Download_32px.png b/GenIconos/windows8/Download_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..df6f89e4403446dbee03f698f949d31c89c14f9f GIT binary patch literal 625 zcmV-%0*?KOP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0tQJ$K~z{r?UqYR z0$~(}UA1V@PwCh6AKK-%f>J*t^N!U(MLCWsMRt)asG!>-vMWJ1h{!fZbb$z|Nb1!4 zHDi;^OjeT-_`=J4p7WkL&*;LjR0K(F_I1(IL}5>(t_MSQ8VR{FLW8wN9y3o^o`1-Z zksj}FX<;HliEdZUbO=0QY0A@LQlgX?%+E!HT6z5EDjfjI)B1dpq(H0YPi16$(^9)* zRbKlpG@l;te%-m7OA5OiNnZP|asj~VZzR^?*!dHH^xPP&%#85!p8>!Xfd35;?_&H0 z@K86?%xKv(0L+{oZgGEQz5-AI&;=;fqkZnTKb6hsV3bwYbOAOOQ``t8dR+W!R4WEB%BpLc0MUSrj&@dfz{pqG z6WU&$=GS5Xbx&(n@vz{e`EilI+DYD)XATf+V@}Ec`74&>Z=q<=#;^FS22gqtU-O3< zJ&W$9=eU#EIY23jdD-Qp7^LIfG^HXTeiJnSYUm+@tUP9(;(m6OigwcJ!6s$)Hz++j zS^&TqHS~}{Rvt4?LY=j0(96yy!hSm0TPrMAj2e1zFUuk;kC`hD;c;Y(A(Z9l{n~Ox z1&Dz+2zb2TSDxjHgnDar)NQ*+h64oSjGEjokFYT2W9RVqms>0^$S0Ruwu!@200000 LNkvXXu0mjfvE>%7 literal 0 HcmV?d00001 diff --git a/GenIconos/windows8/Exposure_32px.png b/GenIconos/windows8/Exposure_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..e4246c56396472d6f03f03403ead997dd2146a24 GIT binary patch literal 792 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 literal 0 HcmV?d00001 diff --git a/GenIconos/windows8/Treadmill_32px.png b/GenIconos/windows8/Treadmill_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..48e85c0dfcdb1ea19f3f1db3074d0ea33c478be4 GIT binary patch literal 1434 zcmV;L1!ek)P)X*L)b|&B}%OTz@mg~GZXYNtEm-$c)*r{L8DC{hmukOSb69O04}9G z-x0qLpub5nB^uWuyt?g+Icm_onVEttN<-&FDR8(T5IBK#b!JN0DQHXuu%^W`>7Z@A z*!s%KzTP}rj3ft)+^K(+%-FE*8m%`fRV;sXJ_DF!qHyEHo*n0-jt|@d_S2G2Dftx& zST()4)o?o15aAf9;YS=H&ABFKxDTkRS?y(j$7tIc945?;cS%|RC9eTsA5ee+jBUN% zG51AXmlV=%q=wR-wiyx>Lv*YqlZzO1(Q%N=D34kIJG!KdlJ6o%1Z3;+`KFD1J^2-+ z+bFN#EaVsdiZ)}MQn6yi1Zw>{%Y-m+a(Df03r7ZLMBXcqeG33oHTxbbo9ZvTaY5_@ z<7Fy}%rOe(7q&WSTZ)Fe6|pygKMTCucYd-I7SoBa~aR07XbJlCHoB&*Xul> z%N?jr3n*TW$uO-e3j{*|hS&&*H!1lVU?9Z&5l?g6FJ>&Ie?OgLMoxWxVK2w=O!M*T z{qa|g%d)TCE&r!CN3$GUAqfhvXR}!jmbYk4?M=%V87h{)_HzV6hTC1j5YqjDplaww z&+)xqmqh2Ha?}n)9k}cc$-6qtzhSCB2LkC~7*bTYT+)St(bGipvxs9vk6S-ikdAI< zW~Ns_6p`WC0sr7`ZcpR~c7QeHxYp?l2illNa-b6mvb<(c76rbL#7uZsy}Y*Qx!qrK zJ)RH#a;A(K>=L(80kuoZ=j`V=k!>AdEeo9Cp009a;EpHHyXR)J?2L`M z15PVoJcERnKx6InU}!(yDn9n!mdZt7=u=fg%c19fG8L6`?&9yIYB)?^>jtE!r$cE$ zaoT7pFmd9ok3Zx0da22bY+cvAx~Y2%ow+qlOV>2rrRka!4y&T7smK_Mg$n|LEXnll zQa~Xo%OV8*e&`3X4nNc6av_+hm+#DknV?>;=WN@)GYlg;@kE3uiu!}WV7peUJ;5Blq4wAVQ!KQXGm19qLdzM1+Ey zk&Inh9D=y?A1HJW*r`L8P67LOWGX0hQpi#&x`+tk;*XRTOdOKH%dL&J(gSBXobPka zdEbZs9FxfWE4+XMe8KeJ!mscX{b3&B>0RO1=%HUM7Ehxnx_=ysoM84g_&I)!CO4bS zcC}jF2{ze7J_H`51U_Ldj^ndNqw%#?t0|Yu=Y>MyxYOx8!W2%iJp!^RfqBSeGQA{8 zdbwOKn20;y6AFYF#`YCjjqK6GwT0<#h8+ zE{iIa%J+J`zE>X>Tdh{M+wCsiHhsn5XWnAdzz;w05$gulUJ8MUl)xHhkl+R0po~@Q w;sd^+{ih8c;f91VurxEjT&UL4qCh6&;L&KAw5?cBlr0#G%l$ zT=>rUKF)XVx$vLy0C>1gAg|&4z+2!2Z~CdYt*xz>qtQt6 z`Fyi(w>vmGI=Thq)4aJ zMmC$hIyg8G%YR&@Qkgm&4j;e$)L? zkYWCey2olb_sA(2RgR4Qc_i^cb~TJ6j7 z9RQHa0sj_vOVVP~xW;4|p;D+1X&UL@K!QKF?UOvImG7= z;~uPFtyb%*Xb`%@$9B72>vp>{c`!}WS=<-N!Bb9>q?RN} nE%Y{wK@Ybj{2J$BOn(($yr51d+`F_o00000NkvXXu0mjfo;QY0 diff --git a/IntFiles/Figs/bn.png b/IntFiles/Figs/bn.png index 3acfacbf52ecf17903172748023c9eead1dee0f1..2825893b6733906cd052663d829e5317bf4fc87f 100644 GIT binary patch delta 366 zcmV-!0g?X10<8m(NPhuONkl0^SsM>@E#DQKmoi0x9MJ|(|HDr1X@TE zRaINA>nbVX1ZV-8G{~|nYeNkJmXz`e(H9cuc8Oa%DjT9LKu};2pRCwxs7Xo6Rsy)0PE^WE$ruz_ttnD2jrn zX-ipxbZ;Ga$P(zfZiHbNehRZL0V{x>b&${JwPLaOG8hb==CHqu55XE@x4ja`KmY&$ M07*qoM6N<$g7&qkxBvhE delta 260 zcmV+f0sH=~1H=N5NPht4Nkl-d+t|gzCQT4ju!}_q=<;4RDh^EZnE!vn%eRPldj+x<953}%=7%ds;ZlAw|k1!dEjLXF@2t9mSuyoEc;24T%gq? zIQR+&@?;j=!k|I$j*XaLF919T-VQ_X0v>9G=O!Dd3ob)~ty)nJ>v#+Zop;2kIdX-+U@B-e&t0=vK zL{ti;ib_kw(U6Q22O|nQ*~Oa7+WViC;oqx{+_&aFR+yt4Yc64iQ@6q^l`w%CO89*f1WKo1w!}^K&-qUvj z1~1GB7N~}XbO!H|Zc9BQ{m-lDyclrC@<#KUx&jq5D@_GT-i`nO002ovPDHLkV1le6 BUEu%# delta 201 zcmV;)05<=K0*V5VNq?+KL_t(Ijopwj3IZ_@MPC++1jS~(f+7eW#mcjI89PxdEyWX9 zZec05K@>E}{+lr;bf;OX~6N&Wz}J;0dh6QWJ)QVb3$zqImZBwz$wr3j@BA$ zZBInD;RRsMZ~PLt0sRp3w8H$JBBX)t;1H=4h&N<(l zIn1z2OLy2yDqKemqyG_ogolw|<0basCT?sGuEqHb?nKVx$$#(yP4saIm*f6DoE{5g zxQc7h`5Yng1P(_2h{uty;|l6E6u5;JrtuT!FcsI+*o*V{ffINeE8ZMmK@?bsd=h7I z2!|t|ioONB+ujSffDPP@S9pdm_#8PzUlFVM$ByF{mSfOYtj4mJ(Tjpz9NS4?KR#d+ zAx+be=Xt2t>wh7h9-{Dj>>DRdR0P`d^Yag8W@eryNn$u0DvCmqB(f|MLJ&e2wpy+G z&1SPRPO3K1?Ck8TUa$AP)9L8<`$7oHvb46grrYgWUtj+=H#hgDnp3U7%F4>^Mx${g zNfNc%mUfz^%Ca;XjU-8ObZKenBA)Chu(-I`X}8;-21J9w)L<~ki=vo-$;rtw%d(9; z&wGtVW3!r5{W##YI?`WX~()K3H~UcK5#@GyJ1+Z1lvxw3z^0;s`gm3gRT- z47-@3K#dn1;1SO_>GB82BEZfPSFnw(pCks%@V*2v$J-EKWoxY;5YP4)rxtzK@AkPx z*PAZhLy7w#=rz6)>pbNE4znz)^E_|kgQjU_73^wg(+`F$mQ13!8uN6NPhumNklY5Qd+-z0E1aa1j(NgAs`qX|04aB3KA% zw6V&surOt?@@LpZ(Jo*S1ph#q5aL&n#t)FNjSw!kYmwl?KYEcMLv@9!Lcw&qp z3`3MsYxR2lDoR*!K&@6=tyZfyxm<3+alY7EYx?~@&1UnhR4Q%yzW*>50Fck;50z49 zLI`Zz9sRMPj0$$+D<9hwEzGB07*qoM6N<$g89s`pa1{> delta 255 zcmV}>v(o;+fLG;5TX%=;RM)Z zlWPh<1c)FAdZuYUha5%GDPOG9l+U*xREeq?e5E2lTM=vV*n?6Hcr8obR+^>&bbw1i zacYZfLH-wJp66|K9fS~$@B0Rjz~>&!fw2xC<)nb0bq5?!M$JXPE+YT{002ovPDHLk FV1g8@ZPow) diff --git a/IntFiles/Figs/wb.png b/IntFiles/Figs/wb.png index 4c7ba4c3e86bbe94ba13b60ac5916a8ddecc8fac..5c5afd07ec90f50d845d0e0567941eef7d7b7375 100644 GIT binary patch delta 479 zcmV<50U-W^1fB$tNq@jeL_t(IjkS=mi`sA;#b5lTF~N$LT-AV4r#nGJItWK0MJPHu z6~whTId&=dCloq#>2AAg2`>FJ?zC|2l1P+93Ng_kX%f@LLeE|!H}oyPFZ>>l&wIfC zRI}N997Pd{^CaRvE0xOE!C>$mz&}IixPW9bsZkUa0tg=zP=BpfnMR}Wxz%c20l;Rn znT|%I4uIg;doNnM-TpEh4%_SXn(X&`(&=$)zA;!8=A zgj6a8j^kh$27kum@$F{ru)#}}JI(^UcJOKd9vV!Gu`4)hEasvQoj^n&0 zgb>GZ;5ZHlA+&AVZvdoEZUDfgY5FFU$(*IrX#oJX+wBjAVXguEJT_Cm4_THk1wnX~ zNF;JB%VM!uT$an_JOF5#W|m5&Z?5aY_kD9dpMOwQRam=2!1Mg4TrNjjmgRFC=Sz}A z2SIQPfRZHXoa49)-}fJ7v)QL%7|N=u%6A9=6alygOz&|2DHcEy0CRZtd+I-2zX0>( Vp3#ZjEWiK&002ovPDHLkV1l|xsB{|m!2gM~^Zn&wYD2%{CBu~t4D7CzM?|Scj|4vDh zzClaR_!~a(JpAClPe4l93XB6(Diz*lv-A%R{uPh}GHsCr0e=_NDuY9}NbX?M7a#z7 z{mjO3nZ_0vN-nyXK$Ol>-pONC4{rH z&d<*u$gRCYlYhU6L`~dE*(U(R+Rx(H1vH_fMT6M3O><254-7w;VPj*9%Y9uG4$FMm z+C~^Pym>ywLTUmR__&-VpLvPreLn_{|KRswe&Q}QVTJCVT7q>Z)zTodX|NOSAraVR z*F1ye>~mx23d)~=^UY>tc$D)OI~W@X6Kb%?9SGjeZGhH zc^}|Ea{?fAOyE8;djQ}WfX4ua04{D5(MJp}0B8Ux0E=T=9Dl+qisGkCCUXwp&LPpY z4N|F8k>j|CrfDC&UhlnPvG|A(0!`D-SeCuZ^Ze;}JZ?3c%{Ti-RH;;Y)$8?stEx)s z_4-vho&GFI(z;fw{mN#udZ*JN-ENo2vg{NJg_oOXe>tsGDnD%7Cfld$I?3nri(D?Z zTrQVncUCT!zkdM`b|ZL#!Qdr;z}AuHdBo#!pUdR}O<%t?$8le=X>eQP_fRP0IF9oq z5D3u2;Sh?VU@#a^j^j`O;PH6i_xn+;Ru`(OzG%1G=h)8?jYeOKqWCnKOwv5hgJBq~ z*K1gog?_(}Mx!yG&1TPKS$=zK3!~BKgK3&)lgWfu6n_Qba2V6+6h5C19LK>hjLTN5 z_3mJe0KBW!3Jk-55CYRQ!7vP1mWA8xhGkjQp8ke`Vi<-Ti9{YI5{XbO7W2&KbLhJM zQ<9_^fcF5dc0b&3+#t|2eP0lS6Wg|J!!W)8=mA(A(EmGs06SmR>IGG#IsgCw07*qo IM6N<$f?+HF761SM delta 430 zcmV;f0a5(?L_t(IjkQw2N&-<7JvcKt6_aFw1Sx^Esf|M6*7oxaE+omg%DhX;6O&APzxO)lAu*6At@B8m@`k`q%f@}1{srx4&22%@7(jwy*%Kb zu?JxPCjmQyUjq;T5CY%;zyNpz@H#ED{VCr(2!e3tbUM=j_W9POd^{e{6GEO1o>HoE9CvConU}c8FpThhVY4|zQPdSh z(OH(=H0gMb<9`;c3@%cslx&XV_xpulFvyv?TCG+b4u_XkBLF}oo6Y8pxUE)8u2d=| z<6Ny)i#*RCn;V!ZgIXq&S(!{G%biZAAxY9_xm@N5A@yW3`4WvrOXKnQWY+5jLg{q+ zYA_f)nJbsel>ms-V#}}W^?F&m-M(e!JRZ-xmDbAOBPSM%^?g3yxtVM>n@a$C^Wy&+ YUrVpFlz$7PtN;K207*qoM6N<$f_3e~qW}N^ diff --git a/IntFiles/Figs/wn.png b/IntFiles/Figs/wn.png index 589246ac04a8c14905f55faa11344f16da1701ff..7b6b51913e1cadf1ff6e8f945af8a10ff8507afd 100644 GIT binary patch delta 509 zcmVsZ{EwX_|DsUZdadzi&31&t|jP$7nPfy=+;MB!t7^ z2Tt#t@k~*alTN2|pXd4OqtWPzWm&(f)#~d+BC!pPQg#YL2!g@jon09+e!oA=aa>3g z#T%7MMRmfvT#BO5VzKzn?EnBamSueffLt#3s#dGr+qH#dSpYcLCE&obSS$_)gTa?w z0)}C*x~^}W{>oqY0|7u_4|f3IJAku2{J-Hho!H5(-b98M00000NkvXXu0mjfd${~a delta 401 zcmV;C0dD@h1keMJNq=%lL_t(IjkS`oOT$1E#=nMOY(=aHLa@*lTsn1elj1*62n4fb z>=0e5bP<>C{s&G94kAbvTdH(uQ3p#Z4h~YJS~|pJNui1KZjd-=N-e3aiVu$C-M#O3 zkMADvPr>}Pa23EYfVJOtFegdUm8z;`K@d~`%1^oi!!SFYPJictQtDDl-FCZu9gRj0 z0nARKpUz}5MNjN4$8nnRc>DwaI}WgE7{=W&fKuw3rdelMcK16mD~jUTpokFS5JF5Z z&*$?601Kl4a;w$4@rrHRZYGn-%UZ2g_0BBIx(Bf3Ck~`iDb*`945J>2MD_t}=(>J8 z*i|eRJMaUnRDUX!3reZ0X<9iL4DJD#Pp8ubLde@7K$0ZY53nc*LOGkw76EJn2qhAU z(_XLlGHj-zD1}iT%mA1I!0wWxrQAj_Xh5 vsG$hSviuYdho1u<@#V77Xp9}EiQ^rlNdu(afb>iN015yANkvXXu0mjf%x|_E diff --git a/IntFiles/Figs/wp.png b/IntFiles/Figs/wp.png index ac4ceb7ac988c13d031e9ee36b5291d95f9a2128..cbdd9e55024a69abaee65bbf1b5aeb591debe638 100644 GIT binary patch delta 350 zcmV-k0ipir0-ghqNq;~|L_t(Ijm=U$OT$nUJ$|GRq=Sz%qDFj+lUs~~&h3s zhfdCgkRjmc;N+6oACV!!L2z^HA1D+G%^xG=GwvPUpIcSQU>6AvPFe z002S=uj7txw|hGtk0s|^a?WKKh98=y9j%-|Q51_(`d!<$O$@_0TUkI=)yE`BWL`&6 z^p>XS!^#2x6hcS_0E{s-8jbf}ueVddEyDA>t2mB7^ZoM|1i?$Y-QNByfuXAEjpI0* zzlpx@8@jIFFIN??EbH8L-NUkEtyT-BX&wXE``IrzHJi_)LRh-)R0y3R+?h%V|!~g&Q07*qoM6N<$f|7xxbpQYW delta 296 zcmV+@0oVSX1Ly*fNq-7SL_t(IjopyFO2j}Eg|9&&mHB}bc>qONkUT=3z{)PwyoBU6 z1VNEB0n=Db=@QW{ku*y{Bce?T2^viZ&XTzzjE#cGMrKob+IzmkJqO@lYu&c(1J`v! z0N5E5u-7!r3=st(gbtaMKLTcy(#N{4ud*zA8}k8Zns!16Ie!O$?J@jT7=}~M`IBjy zhfC9zWgYi@-yot0g5V4QHkaWiQ4~!PQ6Qq=oImTjezeFqIAR>f?~Jj6F?L%N#f=c+ zWsz@nfWJ?Y1golgRTO03y=NCWWuN~%& ueyjBP?*M2=M@J7!rBW6E=H})K`F#E! z0Js4#^?H3V5D1I{c(%U2{xKX5X8?d82vXCun*gx0vr{=dJlq0+;o;$FUDpQyY+qRs z1mP=9)1yA0Z+~iLW=00!pPZa5rqk*70QxhT%nycP?sFV>HxLMX>B>M9#qVQdW3K=p znM{Tvk;rs38XX9QLVP3=c`z|C5e9%nBJnaFk8gK9s8*{|Hk-ZU^?K9sc-$=rLQWLL z!9*hA9~v5ZEQ%s22*T9D!oqDuQ6xv#;|wP!C-nIE_uJ>$i zZq@*lyBq*8+U<5L7!0Nuh9OPUM61<;VHnUf4YqA#e}BJKEEb=io}O-XWdHzsdwU;b zS^mlMJbzf0h1=~0P1ErE{qXsGD3wZ;gM)+ObdP40}CK*(Nk2F^8?Ls0x-VCLVMkAYeELs@{)k$=%f58wjW z18#xKrMwq#25bROzz9fyXJ7|-04BicXFUcG8entHRsarQwts~Vcm-x_068#S1^BW4 zAE4j%$hPg1AP6RPT@R&{@0Mk~m1VizG)*Ohn8k6tuar6j?mLN6%Ihr4##L1*J(483 z0nYn20NC|?|0)c_ss0c}(I8FJ1EA^y0K`R6jD!$_*2wcb^*rwmI9_fvzl|=WOw$}1 vh9O(Sah#8?#v)aKN7r>1KIAvL_t(Ijm=O!OT$nUJ;}>p)fXrTg<1=O8l*#@hW-GDEE4T( zGjtcHu8yLk_yb&|TNekxQu-GPW@r%whi;*P&^{WXZS!1eP18^W7Y{sm_uPBV{Q&+O z2!MS6bQ-r<5bR(`?zu)gQn@t12Yup4Ys;ZjFWO6N^&qs{`0IgPQT~*an%d)Nkd_)a46-6nf z)9FOF+l|vBlgT6in3iST19*>HK>?76RsX;XukdGl0TfTkju3w!E*}5@002ovPDHLk FV1guow|f8p delta 337 zcmV-X0j~bq1CaxeNq;j*L_t(IjbmUKXuym`jE8}Nfs28G;THn~12>fY8%lFS+5d5x zsIRZTIV~;iFaraFr>d&zwv?2V0}Kod0WvZ&n{sn=_cJgs*x+?iN=nL#PoF;B-nDDj z?kiWW9QyR>)6HGGcI`fQ?%e+C*RLNUXn?l1_S`RDzTE%$^MB`mbUbCslx++Q3|h$Q z8L^nbAt@<2Jv}{LhMSxFFHCUPu3g-_ckeD|U|=|g*9A}z^Xb#4d&ss&MMW*grjUsk z)9?X`4Pe0`F4xh~aZg-a{1!~$=+UE!43Kp2m5`yXhYug#`2!7n|Ni~4mX_9BLax@G zFk!+bY*G00=St82@87?_A}lQ2iqC-dA3uKl#$~|IpFjUkn>K9^15y@14}+J{(a~%1 j*>LgVMP33yKpFr5$w9%Ac_w_x00000NkvXXu0mjfSwg2o diff --git a/IntFiles/Iconos.bin b/IntFiles/Iconos.bin index eeb6ebf7accae4646758ccfbff4c61f7d64c8a63..713d82c7bb4caa844855e8e590479d9be21e298e 100644 GIT binary patch delta 6160 zcmV+r81Lt`oH(7&IDmu!gaU*Egam{Iga(8Mgb0KQgbIWUgbcI|7W$X4A`K#c1y4yt zK~z`?$|F{`N+fOx=`fkC59ABU1s0a$tH2mmgnJl_$&51_wEG9?<< zA-uZni#cl0y_uPUEJ{P?L@98%AP_i#b#-P+*(qpD1+b>YGwGmhyx97G%F4dpJX?$; z2aMdQf0fMGu%5)?yptR<6+7k6Hjbx}=Pf?;=M8Wb5(yrj30)`4yzwD6ilwMy);LF@zLWh#oyF$(1u zwmNBBiiW!tu{+E^u%fAhYo;Nur0TLbMib6+8PB5^0Qesz`wbL-*Xul>%N?jr3n*TW z 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 ME`82mdGA9V(rT#6N9r81cikhWzr;C M<&Q6!wfx-w0lL|jn*aa+ delta 64540 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 z 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 uJLYU!lG(IPPJvUImtT<6