Planet

Eventos y movimientos

Más que eventos debería de llamarlo lo que provocan los eventos,  es vital para el juego , es digamos el corazón del juego, lo que determinará su jugabilidad, su robustez  y su extensibilidad .
Vamos a situarnos en contexto, cuando por ejemplo un jugador golpea dandole a un boton este activará un evento por el cual su personaje dara un puñetazo durante un cierto tiempo, por el cual este podrá provocar en su enemigo un evento por el cual será golpeado y esté contrincante perderá el control durante un breve espacio de tiempo.
Todo esto descrito aquí de manera un poco directa y quizás poco clara , deberá estar contenida en una/varias clases, la cual controle las animaciones y sea capaz de bloquear el control del usuario/cpu sobre el personaje y además controlar las colisiones.
Pero meterlo todo en una clase no es mi estilo de programación así que diseñaré varias clases para  así subdividir el trabajo.
Mis esfuerzos estarán centrados en la claridad del código y en que sea lo más extensible posible. Desearme suerte ya ha comenzado lo duro.
Se alcanza el éxito convirtiendo cada paso en una meta y cada meta en un paso.   C.C. Cortez.
      

XMLRPC con ssl (https). Generador de contraseñas y chequeador de fortaleza de las mismas (python)

El proyecto sigue avanzando paso a paso. Hoy he implementado un frontend para el demonio de GECO (gecod) que implementa una interfaz XMLRPC con ssl.
Dado que vamos a mandar información sensible hacia el servidor es necesario que la conexión se haga a través de un canal seguro, por lo tanto he buscado por ahí la forma de tener un servidor xmlrpc en python sobre ssl y lo he
">encontrado
.
A partir de ese código he creado el módulo python secure_xmlrpc. Y básandome en este, crear un servidor XMLRPC sobre ssl es tan fácil como declarar una clase y pasarle una instancia de esta al constructor de la clase EasyServer.

  1. ...
  2. import secure_xmlrpc as sxmlrpc
  3. ...
  4. class frontend:
  5. ...
  6. def auth(self, user, password):
  7. ...
  8.  
  9. def start_server():
  10. sxmlrpc.EasyServer(HOST, PORT, frontend())
  11. ...

Por otra parte, en el lado del cliente, he empezado creando un módulo (utils) el cual ahora mismo tiene un generador de contraseñas "aleatorias" y una función que comprueba la seguridad de una contraseña de forma simple.
Para generar una contraseña aleatoria he usado los módulos random y string. Está parametrizado el conjunto de caracteres a utilizar y el tamaño de la contraseña generada. Con random.choice y una lista por comprensión genero la contraseña a partir del conjunto de caracteres seleccionado.

  1. import random
  2. import string
  3.  
  4. LOWER, UPPER, DIGITS, PUNCT = (string.lowercase,
  5. string.uppercase,
  6. string.digits,
  7. '.:;,!?{}[]<>=-_()+')
  8.  
  9.  
  10. def generate(size=11, lower=True, upper=True, digits=True,
  11. punctuation=False):
  12.  
  13. chars = ''
  14. selection = [lower, upper, digits, punctuation]
  15. strings =(LOWER, UPPER, DIGITS, PUNCT)
  16.  
  17. for opt, v in zip(selection, strings):
  18. if opt:
  19. chars += v
  20.  
  21. return ''.join([random.choice(chars) for _ in xrange(size)])

Para comprobar la seguridad de las contraseñas he implementado una función que comprueba características básicas de las contraseñas y devuelve un valor entre 0 y 1.
Los test que he implementado son:

  • contraseña en una lista de palabras
  • la contraseña es una secuencia ordenada
  • según el tamaño (<=4, <=6, <=8, >=14)
  • la contraseña contiene algún caracter (minusculas, mayusculas, digitos, puntuación)
  1. def strength(password):
  2. '''
  3. return 0..1
  4. '''
  5.  
  6. strength = -5
  7.  
  8. # bad passwords tests (-20 or -15)
  9. bad_passwords = ('', 'qwerty', 'asdf', 'zxcv', '123', '1234')
  10. if password in bad_passwords:
  11. return -20
  12. ord_pass = [ord(i) for i in password]
  13. inverted_pass = ord_pass[:]
  14. inverted_pass.reverse()
  15. if is_sorted(ord_pass) or is_sorted(inverted_pass):
  16. return -15
  17.  
  18. # len tests (min -10, max 5)
  19. if len(password) <= 4:
  20. strength -= 10
  21. elif len(password) <= 6:
  22. strength -= 8
  23. elif len(password) <= 8:
  24. strength -= 5
  25. elif len(password) >= 14:
  26. strength += 5
  27.  
  28. # chars tests (max +20)
  29. for chars_string in LOWER, UPPER, DIGITS, PUNCT:
  30. for j in chars_string:
  31. if j in password:
  32. strength += 5
  33. break
  34.  
  35. return ((strength + 20) / 40.0)
  36.  
  37. def is_sorted(alist):
  38. '''
  39. alist is a list of ints
  40. '''
  41.  
  42. first = alist[0]
  43. i = 1
  44. while i < len(alist):
  45. if first != alist[i] - 1:
  46. return False
  47. first = alist[i]
  48. i += 1
  49. return True

Con esto y un bizcocho, podremos generar contraseñas y verificar su seguridad con suma facilidad.

¿Por qué usar XML como formato de nuestros ficheros?

XML es algo más que un lenguaje, de hecho es un metalenguaje, ya que a través de él podemos describir otros lenguajes. En libgann, guardamos las redes neuronales entrenadas haciendo uso de XML, de forma que cada modelo de red neuronal tendrá un DTD (por ahora pronto serán XSD) asociado que define el lenguaje para guardar éste. La ventaja de este esquema, es que no tenemos que construir nosotros un parser para validar los ficheros guardados, ya que con XML los parser son genéricos, siendo los DTD’s (o XSD’s) los que especializan el parser a cada lenguaje.
Para explotar el uso de XML en Libgann, los fichero XML generados especifican un DTD público, estos DTD’s se encuentran en la dirección:
http://cusl3-libgann.forja.rediris.es/dtd/
De forma que cualquier usuario podrá validar sus archivos ya sea en un validador de internet o en uno local.
Cuando Libgann lee los fichero XML para cargar los distintos modelos de redes neuronales, ignora la especificación del DTD de dicho fichero, utilizando directamente el DTD asociado a ese modelo de red. De no ser así, cualquiera podría cambiar el DTD del fichero, engañando a la biblioteca, de forma que dicho archivo sería valido para ese DTD, pero el DTD no correspondería con ese modelo de red.
Espero no haberos aburrido mucho. Un saludo.
      

Separación de los ejemplos de la biblioteca

En el directorio principal del proyecto existía una carpeta “examples” donde se implementaban un conjunto de ejemplos del uso de la biblioteca. Hemos movido dicha carpeta fuera del proyecto, de tal forma que los ejemplos serán desarrollados independientemente de la biblioteca. Esto tiene varias ventajas:

  • Los ejemplos son desarrollados usando las autotool, esto permitirá a los usuarios ver como se integrán con estas geniales herramientas.
  • Simplificará los ficheros “configure.ac”, ya que antes teníamos que tener en cuenta las dependencias necesarias de los programas de ejemplo dentro del “configure.ac” de la biblioteca, esto suponía algunos problemillas.

Espero que con este cambio les sea más fácil a los usuarios poder comprobar el funcionameinto de Libgann.
Pd: Si queréis ver mejor de que hablo echarle un vistazo a repositorio SVN:
https://forja.rediris.es/plugins/scmsvn/viewcvs.php/?root=cusl3-libgann
Un saludo.
      

Mónadas: documento informativo en la forja

Las mónadas son una clase de tipo de datos abstracto que se utilizan para tratar datos de forma indirecta. Suelen utilizarse 3 operaciones para manejarlos:

  • fmap (o también liftM): introduce una función en una mónada. Cada mónada hará una cosa distinta con la función, pero lo más normal es que la aplique al dato o los datos que encapsula. Ejemplos:
    • fmap (*2) [1,2,7] == [2,4,14]
    • fmap (*2) (Just 5) == Just 10
    • fmap (*2) Nothing == Nothing
  • join: simplifica una mónada que tenga dentro otra mónada del mismo tipo. Su comportamiento se define de forma distinta en cada mónada. Ejemplos:
    • join [[5,4],[7,2,9]] == [5,4,7,2,9]
    • join (Just (Just 5)) == Just 5
    • join (Just (Nothing)) == Nothing
  • (>>=) : se puede definir como m >>= f = join (fmap f m) o de manera directa. Como puede observarse, se parece a fmap (aplica una función f a la monada m), pero con la particularidad de que la función f ha de devolver una mónada. La función f puede verse como un valor monádico que depende de un valor de m; este punto de vista se utiliza para simular programación imperativa, por lo que a veces se dice que >>= es “un punto y coma programable”.

En Principia ya se usa una mónada: Traza. Si envolvemos un valor en una traza, al aplicarle una función se guarda también el valor anterior (en realidad, en Principia Alfa1 no funciona de forma tan automática ni guarda exactamente eso).
Hay otra buena candidata a mónada, aunque necesitaría una buena remodelación, Expresion; pero no le veo la utilidad clara, ni la manera de plantearlo. Creo que es mejor candidata a flecha, un tema que quiero estudiar antes de hacer modificaciones importantes en la estructura de Principia, porque veo muy posible que resulte práctico.
He añadido a la sección Documentos de la forja, una exposición sobre el uso y la creación de mónadas. La he escrito en inglés porque se la envié a alguien con quien me comunico en ese idioma. De todos modos, a medio plazo pienso traducir Principia al inglés, tanto código como documentación; así podré informar a la comunidad que hay en torno a Haskell (que yo sepa, toda ella en inglés) sin que caiga en saco roto .
      

Fechas en multiples formatos

En el proceso de adaptación del sistema de inscripciones de la San Silvestre Segoviana a la estructura de Gesport me he dado cuenta de que usé una función en el Corredor (Runner) para adaptar la fecha de nacimiento del formulario con javascript localizada en castellano 20/06/1981 a la hora de formato iso:
function setBornAt($date){
ereg( “([0-9]{1,2})/([0-9]{1,2})/([0-9]{2,4})”, $date, $mydate);
$final_date=$mydate[3].”-”.$mydate[2].”-”.$mydate[1];
parent::setBornAt($final_date);
}
El problema me viene ahora cuando veo que la localización en Gesport tiene que ser adquirida por el programa desde el navegador (o ip del país desde el que se conecte, aunque veo más fiable la del idioma del navegador usado, porque la mayoría de los usuarios de a pie no saben cambiar la configuración de idioma predeterminado). Como esta función sobreescribe a la original setBornAt de la clase Runner que recibe la fecha en formato americano cuando alguien se conecte desde Estados Unidos cascará. Si sustituyo esta función por una que contemple las traducciones disponibles para gesport (Francés, Inglés y castellano) se dificulta el proceso de traducción basado en ficheros de configuración.
He mirado varias funciones en PHP que podrían serme útiles:
strtotime: coge una cadena de texto que representa una fecha y la tranforma en un objeto “Fecha”. El problema es que solo coge representaciones en inglés.
date: dado un formato de fecha y una marca de tiempo (entero que representa una fecha) nos devuelve la fecha en el formato solicitado. El problema sigue siendo el mismo, yo tengo una cadena de caracteres que representan una fecha pero no se en que localización.
strftime: Coge un objeto fecha y lo transforma en una cadena de caracteres con la localización de la sesión. Esta función es la que venía usando hasta ahora para el proceso de extraer las fechas de la base de datos y mostrarlas o ponerlas predeterminadas en el formulario.
He buscado en las funciones de Strings aparte de en las de fecha y no veo solución a este problema. Considero que es problema de la construcción de la clase base del MVC Symfony que en el proceso de mostrar formulario admite la introducción de fechas localizadas que luego no entiende ni traduce. Buscaré la documentación completa de Symfony a ver si hay solución alguna sin cambiar de versión. Estoy usando symfony 1.0. Es la primera, pero debido al soporte largo de 3 años es la que mayor fecha de caducidad tiene a pesar de haberse publicado Symfony 1.2 hace 30 días. Como quiero que sea un proyecto estable elegí una versión duradera. Consultaré con la documentación de Symfony y los autores si hace falta a ver como podemos resolver esto…

Base de datos (gecod)

Ya he creado la primera versión de la base de datos, con sqlalchemy, para el demonio del gestor de contraseñas GECO.
La base de datos es necesaria para almacenar las contraseñas ya cifradas, así como los usuarios que están registrados en ese demonio.
En principio sólo tenía pensado crear tres tablas, la de los usuarios, la de las contraseñas y la de los ficheros de configuración, pero he añadido una cuarta (cookies).
Las tres primeras están más o menos justificadas dada la aplicación que me he propuesto desarrollar. Necesito guardar los usuarios registrados. Para cada usuario tendré que almacenar las contraseñas y ficheros de configuración que gestione con la aplicación.
La cuarta viene dada por la forma en la que estoy implementando el backend del demonio. La idea es que el frontend sea totalmente "independiente" del backend, sólo haga llamadas a una serie de funciones. He pensado que una buena (y simple) manera de hacer esto es mediante un sistema de cookies similar al que utilizan los navegadores, para así evitar que el frontend esté continuamente pasandole la contraseña del usuario (aunque sólo sea un hash de la contraseña real) al backend (aunque no haya transferencia por la red).
Por lo tanto con el sistema de cookies que he implementado, el frontend llamará a la función autenticar (que admitirá más de un método de autenticación, actualmente sólo está implementado por usuario y contraseña) y esta función devolverá una cookie. Para cada petición que quiera hacer el frontend al backend, en lugar de pasar usuario y contraseña, sólo tiene que pasar la cookie.


De momento hay poco código, pero el sistema va tomando forma, y estoy intentando mantenerlo lo más simple posible. Tengo que escribir un poco más de código y de documentación para poder recibir colaboraciones, dado que de momento la idea está en mi cabeza, por lo que no hay una forma sencilla de colaborar en el proyecto.
Estoy manteniendo tanto el repositorio en la forja de rediris como en mi propio servidor, y para ello estoy utilizando bzr-svn, con lo que con un "bzr push" hago un commit en la forja de rediris.

JAI-JDK integrado con Eclipse

Tal y como comentamos la solución de usar JAI como una biblioteca linkada no nos parecía una solución aceptable pudiendo hacer que esta forme parte de la máquina virtual. Por ello, decidimos emplear el tiempo necesario para solucionar este problema.
Al final y tras varios días de esfuerzo hemos conseguido utilizar JAI estando instalado en el JDK.
      

Agradecimiento

Tengo que agradecer especialmente la aportación que están haciendo al proyecto dos amigos y compañeros de facultad, ya que, gracias a ellos, voy descubriendo los fallos y problemas que va teniendo el proyecto. Ellos son Rafael Gómez García y Víctor Ramírez. Contribuyen haciendo de “testers”, es decir, prueban el proyecto con sus propias cuentas de e-mail de vez en cuando y me van comentando los resultados. Me han ayudado desde el principio del proyecto, lo cual me ha venido muy bien para ir mejorándolo poco a poco.
      

Resumen hasta el día de hoy

Puesto que hace algun tiempo que no actualizamos el blog, vamos a dar una visión general del estado del proyecto.
A día de hoy, tenemos parte de la aplicación funcionando, podemos decir, que practicamente el diseño principal está terminado. Ya tenemos que empezar a hacerla más robusta y a añadirle mayor funcionalidad a la aplicación.
Por otro lado, tal y como dijimos en post’s anteriores estamos usando la filosofía Scrum. Pero al ser un proyecto de dos personas, no podemos llevar Scrum al pie de la letra, aunque si estamos usando algunos de sus principios. Estamos usando un product Backlog que alberga la lista de tareas que tenemos hasta el momento. De esta lista, nos fijamos unas cuantas tareas a realizar en cada spring. Estos spring son de tiempo variable, al no estar dedicados al 100% al proyecto, y las reuniones las solemos hacer cada 2 o 3 días.
Para mantener el product Backlog estamos usando la tecnología de Google Bloc de Notas que cubre todas nuestras necesidades.
Por otro lado, la documentación, la estamos intentando desarrollar conforme avanzamos con la aplicación.
      

Distribuir contenido