Commodore 128, Commodore 65 y Mega65

Casi todos los aficionados al C64 conocemos su sucesor natural, el C128:

El C128 se lanzó en 1985 –el C64 en 1982–, y se caracterizaba por:

  • Tener dos procesadores, un MOS 8502 y un Z80.
  • Tener 128 KB de RAM.
  • Soportar BASIC 2.0, BASIC 7.0 y CP/M.

Y, por encima de todo, el C128 era compatible con el C64, en el sentido de que casi todo el software del C64 se podía ejecutar en el C128, entrando éste en “modo C64”.

Menos conocido que el C128 fue el C65. De hecho, el C65 fue un prototipo de finales de los 80 que no llegó al mercado:

Las características principales del C65 eran:

  • Tener un procesador MOS 65CE02.
  • Tener 128 KB de RAM.
  • Soportar BASIC 2.2 y BASIC 10.0.
  • Tener una unidad de disquetes de 3,5 integrada.

El C65 también tenía previsto un “modo 64” para poder ejecutar el software del C64.

Pues bien, aunque ya lleva tiempo por ahí, recientemente he podido conocer un proyecto llamado Mega65 para reproducir el C65 con FPGA, es decir, con circuitos programables. Algo similar a Ultimate 64, pero para el C65.

El proyecto tiene muy buena pinta, y podéis conocer todos sus detalles en su página web https://mega65.org/.

Eso sí, el Mega65 podía tener un precio un poco más ajustado, porque cuesta cerca de los 800 euros. ¡¡Por ese precio te compras varios C64 auténticos de segunda mano!!

Sugerencias para el lector

El proyecto lo damos por terminado con su versión 11 aunque, por supuesto, admite muchas mejoras. Aquí van algunas sugerencias para el lector que tenga interés:

  • Calcular y mostrar unos contadores de podas alfa y beta.
  • Mostrar el registro de jugadas (tabla “game_list”).
  • Guardar el registro de jugadas a cinta o disco, y permitir su carga.
  • Mejorar la función de evaluación con más criterios, por ejemplo, para identificar y evaluar de forma adecuada situaciones de fin de partida.
  • Implementar algoritmos de ordenación de movimientos más eficientes.
  • Añadir un “libro” con comienzos y/o finales de partida.
  • Adaptar las ideas comentadas a otros tipos de juegos de tablero, por ejemplo, Othello, damas, ajedrez o go.
  • Mejorar la interfaz de usuario, utilizando caracteres personalizados para pintar ratón, gatos, casillas, mensajes, etc.
  • Desarrollar sonidos SID para avisar al usuario de que el C64 ha movido, o que el movimiento que pretende hacer el usuario es ilegal.
  • Utilizar los temporizadores y las interrupciones del CIA1 para poner un límite máximo (ej. X minutos/movimiento) al tiempo de juego del C64.
  • Que el C64 “piense” mientras el humano piensa, asumiendo que éste hará una jugada determinada.
  • Hacer un perfilado del programa mediante herramientas como C64 Debugger, de modo que se identifiquen posibles cuellos de botella y el programa se pueda optimizar.

En fin… las posibilidades son casi infinitas… ¡¡a disfrutarlo!!

HVSW.

Colossus Chess

“Colossus Chess” fue una serie de juegos de ajedrez ganadores de muchos premios durante los 80. Lo programó Martin Bryant y se portó a casi todos los equipos de 8 bits de la época.

Jugué muchas veces contra “Colossus Chess”. Ya no recuerdo si conseguí ganarle en alguna ocasión, pero sí recuerdo haberlo puesto contra las cuerdas más de una vez. Al final, casi siempre cometía algún fallo garrafal, se me revolvía, y me daba un buen repaso.

Aquí os dejo un par de pantallas de una partida reciente. Este es el tablero de juego (tenía una vista 2D y otra 3D):

Y este es el registro de la partida y lo que está pensando la máquina:

El juego tenía muchísimas funcionalidades, entre las que destacan:

  • Tablero de juego con vistas 2D y 3D. Se pueden cambiar los colores. Se puede cambiar la orientación del tablero.
  • Registro de jugadas de la partida en notación algebraica, incluyendo los tiempos de juego de cada jugador.
  • Información sobre el análisis de jugadas que está haciendo Colossus (“lookahed”: profundidad de análisis, “positions”: número de tableros analizados, “current line”: variante actual, “best line”: variante principal, etc.).
  • Evaluación de tableros atendiendo a criterios materiales (número y tipos de piezas), de posición (posición de las piezas en el tablero), y otros.
  • El juego no sólo “piensa” durante su turno, sino que también analiza jugadas mientras piensa el contrincante humano. Para ello, hace una hipótesis sobre la jugada que jugará el humano (“assumed”), y analiza jugadas a partir de esa hipótesis.
  • Se permite alterar la posición de las piezas, para así llevar el tablero a una situación X desde la que se quiera continuar una partida.
  • Se permite deshacer movimientos, por ejemplo, para rectificar errores; también se permite repetir movimientos.
  • Guardar partidas a cinta o disco, y cargarlas de nuevo.
  • Cambiar de bando. Pedir a Colossus que haga una jugada por ti.
  • Que Colossus juegue contra sí mismo.
  • Aplicar una base de datos de aperturas y finales de partida.
  • Repetir una partida, en el sentido de reproducir todos los movimientos del registro de jugadas, desde el primero hasta el último.
  • Modificar los parámetros de configuración del juego: si Colossus usa o no la base de datos de aperturas / finales, si Colossus “piensa” o no durante el turno del contrincante, la profundidad de análisis del árbol de jugadas, etc.
  • Etc.

En definitiva, un juego completísimo e imbatido cuando se midió contra programas similares en otros equipos de 8 bits (Apple II, Spectrum, Atari, etc.).

Ahora que hemos intentado acercarnos un poco a la programación de este tipo de juegos, podemos hacernos una idea de la grandísima dificultad que suponen.

Tablas de historia II

Ahora que ya hemos definido las tablas de historia, y sabemos manejarlas con las rutinas “newHistory”, “getHistory” e “incHistory”, vamos a usarlas. Esto se describen en los apartados siguientes:

Novedades en la rutina “thinkIter”:

En la rutina “thinkIter”, que es la que aplica la profundización iterativa, lo que vamos a hacer es inicializar las tablas de historia, para lo cual llamamos a “newHistory”:

Como se puede observar, después de preparar el proceso de búsqueda con “newPosition”, y antes de empezar la profundización iterativa que va desde X=0 hasta X=max_depth-1, aparece una nueva llamada a “newHistory”. Esta llamada es la que borra las tablas de historia.

Novedades en la rutina “search”:

Por otro lado, en la rutina “search”, tanto en la rama dedicada a la búsqueda del ratón “searchMouse”, como en la rama dedicada a la búsqueda de los gatos “searchCats”, aparecen sendas llamadas a “incHistory”:

La llamada a “incHistory” se produce después de llamar a “addHashPosMouse”, es decir, después de insertar el movimiento (admStart, admDest) en la tabla hash. Esta nueva llamada sirve para incrementar el valor de historia asociado al movimiento elegido, y el incremento puede ser de un punto o de tantos puntos como la profundidad “depth”, que es lo que está programado actualmente.

Novedades en la rutina “addMove”:

Esto ya es lo último, y consiste en generar los movimientos no con puntuación cero, como hasta ahora, sino con una puntuación igual a la de las tablas de historia:

De este modo, cuando los movimientos se desarrollan y prueban en “search”, debido a la llamada a “sort”, primero se desarrollan aquellos que tienen mayor puntuación.

Esta reordenación de movimientos debería tener un efecto positivo en la poda, en el sentido de podar más, pero no debería cambiar los movimientos que se deciden o eligen para cada tablero y para una profundidad dada.

En la práctica, sí puede haber pequeños cambios entre los movimientos que elige la versión del proyecto sin tablas de historia (versión 10) y los movimientos que eligen la nueva versión con tablas de historia (versión 11), pero siempre cambiando entre movimientos que dan lugar a tableros con la misma puntuación. Esto es, simplemente, un pequeño efecto secundario de la reordenación de movimientos.

Otras novedades:

Además de las novedades ya comentadas, hay algunas otras pequeñas novedades relacionadas con las tablas de historia, pero son de entidad menor:

  • En el fichero “Display.asm” hay una nueva rutina “displayHistory” para mostrar el contenido las tablas de historia.
  • En el fichero “Main.asm” hay un nuevo comando especial “.F” para mostrar el contenido de las tablas de historia. Llama a la rutina “displayHistory” anterior, y se usa para depuración.
  • La rutina “addHashMove”, que suma #HASH_SCORE a la puntuación de los movimientos que están en las tablas hash, ahora aplica un tope de 255 ya que, al sumarse en ocasiones también los valores de las tablas de historia, podría superarse ese valor (acarreo).

Mejora en la poda:

El objetivo de todo esto de las tablas de historia es mejorar la ordenación de los movimientos y, por tanto, la poda. Por tanto, no deberíamos concluir esta sección sin comprobar si lo hemos logrado.

En la versión 10 del proyecto, con una profundidad de análisis de 8 niveles, ante el movimiento E1F2, el C64 analizaba $29F8 = 10.744 tableros, lo cual ya era un 40% menos que con la versión 9 ($4535 = 17.717 tableros).

Veamos ahora con las tablas de historia, es decir, con la versión 11 del proyecto:

Es decir, ahora se desarrollan $DCF = 3.535 tableros, lo cual es un 67% menos que con la versión 10 ($29F8 = 10.744) y un 80% menos que con la versión 9 ($4535 = 17.717). Como se puede ver, la reducción es muy notable.


Código del proyecto: RYG3-11