Historial de desarrollo de Spock!
Descripción del contenido de la página
Historial de desarrollo de Spock!, versión plurilingüe del juego Piedra, papel, tijera, lagarto, Spock escrito en Forth, para un jugador.
2012-03-30
Primeros cambios en el código de Piedra, papel, tijeras, que sirve de base. El objetivo es probar el lenguage 4tH, un pariente cercano de Forth, aparentemente muy parecido pero con algunas peculiaridades que lo alejan del espíritu de Forth, empezando por el hecho de que es un compilador. Versión A-00.
2012-03-31
Más cambios.
Eliminadas las estructuras CASE
: el preprocesador de 4tH las convierte en equivalentes con IF
antes de compilar, y por ello unas tablas de búsqueda son mejor solución, como recomienda el manual. 4tH provee herramientas para crear y usar este tipo de tablas fácilmente.
Primer borrador del sistema de selección de lengua y de los textos plurilingües.
El desarrollo con 4tH es demasiado lento porque las herramientas de depuración son limitadas, los mensajes de error son de poca ayuda (no indican la línea ni el contexto, solo el motivo y el número de palabra, lo que no permite encontrar fácilmente el punto del código que causó el error). La falta de intérprete impide la interactividad, tan útil en Forth durante el desarrollo y depuración. Debido a todo ello, empiezo a añadir el código necesario para que el programa corra también en Gforth...
2012-04-01
Tablas plurilingües para los resultados de cada enfrentamiento (solo para Gforth), incluidos los empates. La lengua inglesa se divide en dos variantes: británica y usamericana, pues algunos de los textos son diferentes.
Finalmente elimino todo el código para 4tH: Es demasiado lento depurar dos versiones del código, especialmente cuando las estructuras de datos deben ser tan diferentes. Versión A-01, solo para Gforth.
2012-04-02
Nuevo sistema para los empates
El sistema empleado hasta ahora para añadir a la tabla de resultados los empates creaba una entrada en la tabla para cada combinación de comandos iguales, por tanto sin distinguir entre empates y el resto. Primero, la palabra versus
calculaba un resultado único para cada combinación de comandos:
: versus ( u1 u2 | u2 u1 | u1 u1 -- u3 )
\ u1 u2 = Commands.
\ u3 = Identifier of the commands combination.
2dup min commands * >r max r> +
;
commands 1- dup versus constant max_versus
Segundo, los textos que anuncian empate eran mantenidos en una tabla propia:
h" Draw" \ British English.
h" Tie" \ American English.
h" Remiso" \ Esperanto.
h" Empate" \ Spanish.
create tie_messages languages,
: 'tie_message ( u -- a )
\ u = Language.
\ a = Message's addresss.
tie_messages at @
;
languages,
simplemente compila en el diccionario tantos elementos de la pila como lenguas:
: ,s ( x1..xu u -- ) 0 do , loop ;
: languages, ( x..x' -- )
\ Compile elements from the stack, one for every language.
languages ,s
;
Y por último, era necesario una capa adicional, sobre la creación de los mensajes de resultados, para completar la tabla de resultados con los resultados de empate:
: tie_explanation! ( u -- )
\ Store a tie explanation of a commands combination in a language.
\ u = Language.
dup 'tie_message swap explanation!
;
: (tie_explanations) ( u -- )
\ Create tie explanations of a command in every language.
\ u = Command.
dup explanations: \ Store the combination of a tie command.
languages 0 do i tie_explanation! loop
;
: tie_explanations ( -- )
\ Create all tie explanations.
commands 0 do i (tie_explanations) loop
;
tie_explanations
Para simplificar el método, primero hay que modificar versus
para que devuelva un único valor en todos los casos de empate, y evitar que coincida con cualquier otra combinación posible. Lo más práctico es usar cero para los empates:
: (versus) ( u1 u2 | u2 u1 -- u3 )
\ u1 u2 = Commands.
\ u3 = Identifier of the commands combination.
2dup min commands * >r max r> + 1+
;
: versus ( u1 u2 | u2 u1 | u1 u1 -- u3 )
\ u1 u2 = Commands.
\ u3 = Identifier of the commands combination.
2dup = if 2drop 0 else (versus) then
;
commands 1- dup 1- versus constant max_versus
De esa manera, es posible crear los mensajes de empate con una única entrada en la tabla de resultados, cuyos dos primeros elementos se muestran a continuación:
false dup explanations: \ Valid for any pair of equal commands.
h" Empate" spanish explanation!
h" Remiso" esperanto explanation!
h" Tie" american_english explanation!
h" Draw" british_english explanation!
rock scissors explanations:
h" La piedra rompe la tijera." spanish explanation!
h" La ŝtono rompas la tondilon." esperanto explanation!
h" The rock crushes the scissors." dup american_english explanation!
british_english explanation!
paper rock explanations:
h" El papel envuelve la piedra." spanish explanation!
h" La papero envolvas la ŝtonon." esperanto explanation!
h" The paper covers the rock." dup american_english explanation!
british_english explanation!
scissors paper explanations:
h" La tijera corta el papel." spanish explanation!
h" La tondilo tondas la paperon." esperanto explanation!
h" The scissors cut the paper." dup american_english explanation!
british_english explanation!
lizard spock explanations:
h" El lagarto envenena a Spock." spanish explanation!
h" La lacerto venenas Spock-on." esperanto explanation!
h" The lizard poisons Spock." dup american_english explanation!
british_english explanation!
rock lizard explanations:
h" La piedra aplasta el lagarto." spanish explanation!
h" La ŝtono premplatigas la lacerton." esperanto explanation!
h" The rock crushes the lizard." dup american_english explanation!
british_english explanation!
scissors lizard explanations:
h" La tijera decapita el lagarto." spanish explanation!
h" La tondilo senkapigas la lacerton." esperanto explanation!
h" The scissors decapitate the lizard." dup american_english explanation!
british_english explanation!
lizard paper explanations:
h" El lagarto come el papel." spanish explanation!
h" La lacerto manĝas la paperon." esperanto explanation!
h" The lizard eats the paper." dup american_english explanation!
british_english explanation!
spock rock explanations:
h" Spock vaporiza la piedra." spanish explanation!
h" Spock vaporigas la ŝtonon." esperanto explanation!
h" Spock vaporizes the rock." dup american_english explanation!
british_english explanation!
paper spock explanations:
h" El papel desautoriza a Spock." spanish explanation!
h" La papero senrajtigas Spock-on." esperanto explanation!
h" The paper disproves Spock." dup american_english explanation!
british_english explanation!
spock scissors explanations:
h" Spock rompe la tijera." spanish explanation!
h" Spock rompas la tondilon." esperanto explanation!
h" Spock smashes the scissors." dup american_english explanation!
british_english explanation!
La palabra h"
simplemente compila la cadena en el diccionario pero antes deja su dirección en la pila: : h" ( "text
.
Las herramientas para crear y consultar la tabla de resultados son:
create explanations max_versus languages * cells allot
variable >explanations \ Pointer in the EXPLANATIONS table.
: explanations: ( u1 u2 -- )
\ u1 u2 = Commands the following explanations definitions refers to.
versus >explanations !
;
: explanation! ( a u -- )
\ a = Address of the explanation string.
\ u = Language of the explanation string.
>explanations @ languages * + explanations at !
;
: explanation ( u -- a1 u1 )
\ u = Identifier of a commands combination.
\ a1 u1 = Explanation in the current language.
languages * language @ + explanations at s@
;
: .explanation ( u -- )
\ u = Identifier of a commands combination.
explanation type
;
: .result ( -- )
result .explanation
;
Nuevo sistema para la comprobación del ganador
La palabra que comprueba si la computadora ha ganado su turno necesita ahora diez comprobaciones, en lugar de las tres del juego original Piedra, papel, tijeras:
: computer_wins_round? ( -- f )
\ Rewrite with table!!!
choices rock scissors d=
choices rock lizard d= or
choices scissors paper d= or
choices scissors lizard d= or
choices paper rock d= or
choices paper spock d= or
choices lizard paper d= or
choices lizard spock d= or
choices spock rock d= or
choices spock scissors d= or
;
La palabra choices
simplemente deja en la pila los comandos de ambos jugadores, que en algunas operaciones, como la anterior, son tratados como un solo número doble:
: choices ( -- u1 u2 )
computer_choice @ human_choice @
;
En lugar de hacer diez comprobaciones de números dobles para calcular un resultado es más elegante, aunque necesite más código, crear una tabla de búsqueda lista para ser utilizada en tiempo de ejecución:
: command_combinations ( -- u )
commands dup *
;
create winners command_combinations allot
: >winner ( u1 u2 -- a )
\ u1 = Computer's command.
\ u2 = Human's command.
commands * + winners +
;
: winner ( u1 u2 -- )
\ u1 = Computer's command.
\ u2 = Human's command.
>winner true swap c!
;
: winner? ( u1 u2 -- f )
\ u1 = Computer's command.
\ u2 = Human's command.
>winners c@
;
: clear_winners ( -- )
winners command_combinations erase
;
: init_winners ( -- )
clear_winners
rock scissors winner
rock lizard winner
scissors paper winner
scissors lizard winner
paper rock winner
paper spock winner
lizard paper winner
lizard spock winner
spock rock winner
spock scissors winner
;
Para dejar la tabla preparada bastará llamar a init_winners
en la palabra de inicialización general previa a todas las partidas. Durante el juego, la comprobación necesaria quedará reducida a esta simple palabra:
: computer_wins_round? ( -- f )
choices winner?
;
La versión A-02 usa este método. Es la primera versión funcional, aunque los textos de la interfaz solo están en castellano.
Integración de las definiciones de resultados y de combinaciones ganadoras
La palabra init_winners
mostrada antes es prescindible, pues su trabajo puede hacerlo explanations:
, que recibe los mismos parámetros (durante la creación de la tabla de resultados mostrada antes):
: explanations: ( u1 u2 -- )
\ u1 u2 = Commands the following explanations definitions refers to.
versus >explanations !
;
Lo único que hace falta (aparte de limpiar la tabla en tiempo de compilación) es distinguir el caso de los resultados de empate (que solo se usa para crear un elemento en la tabla) del resto del casos, los ganadores:
: explanations: ( u1 u2 -- )
\ u1 u2 = Commands the following explanations definitions refers to.
versus >explanations !
;
: winner_explanations: ( u1 u2 -- )
\ u1 u2 = Commands the following explanations definitions refers to.
2dup winner explanations:
;
2012-04-07
Traducción de los mensajes genéricos, con tablas que guardan las direcciones de las versiones de una cadena en cada lengua. Por ejemplo:
h" I win." dup
h" Mi venkis."
h" Yo gano."
create i_win languages,
: i_win$ ( -- a u )
i_win language_string
;
language_string
forma parte del nivel inferior desde la implementación de las traducciones y está definida así:
: th ( a1 n -- a2 ) cells + ;
: s@ ( a1 -- a2 u2 ) @ count ;
: language_string ( a1 -- a2 u2 )
\ a1 = Address of a table of multilingual strings.
\ a2 u2 = String in the current language.
language @ th s@
;
Primeros cambios para implementar descripciones variables del resultado de cada partida, con textos aleatorios. Para ello la tabla de explicaciones pasará de guardar las direcciones de compilación de los textos a las direcciones de ejecución de las palabras que los crearán.
Pantalla de presentación. Implementado el sistema para echar otra partida o salir del programa:
h" Press space to play again; any other key to finish." dup
h" Premu spacon por reludi; alian klavon por fini."
h" Pulsa espacio para jugar de nuevo; otra tecla para terminar."
create press_space languages,
: press_space$ ( -- a u ) press_space language_string ;
: .press_space cr press_space$ type ;
h" Bye!" dup h" Adiaŭ!" h" ¡Adiós!"
create good_bye_ languages,
: good_bye$ ( -- a u ) good_bye_ language_string ;
: .good_bye good_bye$ type cr ;
: enough? ( -- ff ) .press_space .prompt key bl <> ;
: final_pause 2 seconds ;
: good_bye page .good_bye final_pause bye ;
Lo cual implica la modificación de las las palabras que forman el bucle principal:
: i_play my_choice my_command ! ;
: you_play your_choice your_command ! ;
: turn page i_play you_play .results ;
: (game) init_game begin turn game_over? until ;
: game init_once (game) game_over ;
: run splash begin game enough? until good_bye ;
Completadas las últimas traducciones pendientes. Versión A-03.
2012-04-08
Implementado el sistema de selección de lengua, con una opción de menú similar a la utilizada en el programa Alien-B. Primero definimos los textos de la opción de menú, la tecla que se usará y la palabra que rotará entre las lenguas:
\ Menu option to change the current language:
h" Not in American English."
h" Not in British English."
h" Ne en Esperanto."
h" No en castellano."
create not_in_language languages,
: not_in_language$ ( -- a u ) not_in_language language_string ;
: .not_in_language not_in_language$ type ;
char + constant language_key \ Char used to change the current language.
: +language language @ 1+ dup languages < and language ! ;
Después ampliamos el menú de opciones existente, para incluir la nueva opción y comprobar la nueva tecla, de modo que el menú se reescriba al cambiar de idioma:
char 0 constant "0"
: digit ( u -- c ) "0" + ;
: option ( c -- u ) "0" - ;
: command_key? ( c -- ff ) "0" commands 1- digit between ;
: language_key? ( c -- ff ) language_key = dup if +language then ;
: valid_key? ( c -- ff ) dup language_key? swap command_key? or ;
: your_key ( -- c ) 0 begin drop key dup valid_key? until ;
: .choice ( u -- ) .command_name [char] . emit ;
: .command_option ( u -- ) dup cr . ." = " .choice ;
: .language_option cr cr language_key emit ." = " .not_in_language ;
: .options
cr commands 0 do i .command_option loop .language_option
;
h" Choose:" dup h" Elektu:" h" Elige:"
create choose languages,
: choose$ ( -- a u ) choose language_string ;
: .choose choose$ type ;
: menu page .long_title .choose .options .prompt ;
: your_choice ( -- u)
0 begin drop menu your_key
dup command_key? until option
;
Implementado el control del acusativo en la impresión de los comandos en esperanto: en el menú deben aparecer en nominativo, y en la frase que recuerda las opciones elegidas deben aparecer en acusativo. La impresión debe hacerse en la palabra .choice
, comprobando si la lengua actual es el esperanto y si el indicador de acusativo está activado:
variable accusative \ Show the Esperanto commands in accusative?
: esperanto? ( -- ff ) language @ esperanto = ;
: accusative? ( -- ff ) esperanto? accusative @ and ;
: (.accusative) ( u -- ) spock = if ." -on" else ." n" then ;
: .accusative ( u -- ) accusative? if (.accusative) else drop then ;
: .choice ( u -- ) dup .command_name .accusative [char] . emit ;
Después el indicador debe desactivarse para imprimir el menú:
: .language_option cr cr language_key emit ." = " .not_in_language ;
: .options cr accusative off .command_options .language_option ;
Pero activarse para imprimir la opción elegida por el jugador:
: .your_choice .you_choosed your_command @ accusative on .choice ;
Versión A-04.
2012-04-10
Revisión general. Cambios y ampliaciones en comentarios.
Versión B-00, primera beta publicable.
2012-04-11
Añadido un borrado final de pantalla antes de salir del juego.
2012-04-14
Método más sencillo y versátil para imprimir el ganador del juego. El antiguo sistema, incómodo para añadirle más lenguas, era el siguiente:
\ This method is not suitable for languages that change the verb form
\ in the needed sentences. In order to make it easier to add new
\ languages, a different method is used.
h" You " dup h" Vi " h" Has "
create you_ languages,
: you_$ ( -- a u ) you_ language_string ;
h" I " dup h" Mi " h" He "
create i_ languages,
: i_$ ( -- a u ) i_ language_string ;
h" won the game." dup h" venkis la ludon." h" ganado la partida."
create won_what languages,
: won_what$ ( -- a u ) won_what language_string ;
: .won_what won_what$ type ;
: winner_who$ ( -- a u ) you_win_game? if you_$ else i_$ then ;
: .winner_who winner_who$ type ;
: .game_winner .winner_who .won_what ;
El nuevo método guarda las frases completas:
h" I won the game." dup
h" Mi venkis la ludon."
h" He ganado la partida."
create (i_won_game$) languages,
: i_won_game$ ( -- a u) (i_won_game$) language_string ;
h" You won the game." dup
h" Vi venkis la ludon."
h" Has ganado la partida."
create (you_won_game$) languages,
: you_won_game$ ( -- a u) (you_won_game$) language_string ;
: game_winner$ ( -- a u )
you_win_game? if you_won_game$ else i_won_game$ then
;
: .game_winner game_winner$ type ;
2012-04-15
init_once
es movido a main
para que la lengua predeterminada esté activa antes de imprimir el título y los créditos.
Pequeña simplificación del bucle principal.
Añadida la mención a la licencia GPL en about
.
2012-04-16
Cambiado el formato de los nombres de los textos plurilingües. Modificadas las pausas en la impresión de los créditos y la licencia.
2012-05-02
Primera beta publicada.