Notas de Desarrollo

Introducción

Esta parte está principalmente dedicada a conservar algunos antiguos programas y aplicaciones básicas con cierto interés didáctico o simple curiosidad para el público en general, aunque con una toque sentimental en mi caso, ya que pertenecen a mi historia personal. También se publican aquí entradas con información de tecnologías más o menos recientes, con la característica común de que las uso o he usado. El contenido puede presentarse en tres formatos diferentes: 1) Snippets - que no necesariamente guardan relación entre sí o pertenecen a algún proyecto en particular; 2) Código fuente en ventana independiente - para visualizar un programa completo, comentado, de cierta envergadura 3) Archivo compacto descargable - puede ser en formato de texto, de algún lenguaje interpretado (como Javascript), o ejecutable (.exe). En la tabla de la derecha, se muestran los lenguajes sobre los que se incluye algún tipo de reseña, código o documentación.

Lenguajes

# Lenguaje Paradigma Nivel
1 Pascal Imperativo Alto
2 Ensamblador Imperativo Bajo
3 Javascript POO / Imperativo Alto
4 PHP POO / Imperativo Alto
5 Java POO Alto
6 MySQL Declarativo Alto

El Yam

Una revista semanal de principios de los 90, cuyo nombre no recuerdo, publicaba juegos y utilidades para Basic de categorías diversas, como arcade, juegos de mesa, ofimática... Un ejemplar que compré traía un simple pero adictivo juego de dados llamado "El Yam", con gráficos vía ASCII Art, lógica de negocio sencilla, sin almacenamiento en disco de ningún tipo y unas 200 líneas de código para ejecutar con el intérprete de Basic. La revista se perdió, y por tanto el código, pero no la idea del juego y, a modo de práctica, un compañero y yo decidimos implementar una versión del mismo en Turbo Pascal 6.

Este snippet refleja la técnica que se utilizó para dibujar los dados, almacenando las caras en un array bidimensional.

CONST
  Dado  : ARRAY[1..5] OF SHORTINT = (19,28,37,46,55);
  Caras : ARRAY[0..5,0..2] OF STRING[5] = (('     ','  ·  ','     '),('·    ','     ','    ·'),('    ·','  ·  ','·    '),
                                          ('·   ·','     ','·   ·'),('·   ·','  ·  ','·   ·'),('· · ·','     ','· · ·'));
  RangoJugadas = ['1'..'6','M','N','T','E','F','P','R'];
  RangoDados   = ['1'..'5'];

Para descargar el código fuente completo en formato ZIP, una pequeña descripción que se publicó en la versión anterior de la web y el archivo ejecutable (.EXE), clic en los botones siguientes (IMPORTANTE: Para poder ejecutar el programa en Windows 7+ se necesita un emulador de DOS, yo recomiendo el DOSBox. También, en caso de que el programa "se cuelgue" al mostrar la pantalla gráfica de bienvenida, debe lanzarse de nuevo omitiéndola; esto se consigue mediente el parámetro '/g'. Ejemplo: 'yam /g'):

Código fuente Trivia Ejecutable

Puzle del 15

En esta ocasión tocó hacer una práctica para la asignatura Ampliación de Estructura y Tecnología de los Computadores (UOC), en lenguaje ensamblador. Una vez entregada, dediqué algunas horas a mejorarla, añadiendo nuevas interesantes opciones, y de paso comentar el código, con fines didácticos.

El programa implementa el antiguo juego del “quince”, que es un puzle que consiste en ordenar 15 fichas en un tablero de 4x4 casillas, donde tendremos una casilla vacía que nos permitirá realizar los movimientos para ordenar las fichas. Los movimientos que se podrán realizar son mover una ficha: arriba, abajo, izquierda y derecha (nunca en diagonal). Este movimiento consistirá en desplazar una ficha a la casilla vacía, dejando el lugar donde se encontraba la ficha vacío para el siguiente movimiento. Habrá unas teclas para desplazar el cursor por el tablero, la posición del cursor nos indicará la ficha que queremos mover, y sólo podremos realizar el movimiento si la ficha que pretendemos mover está al lado de la casilla vacía.

Las fichas del tablero serán los dígitos hexadecimales; es decir, desde el 1 al 9 más las letras A,B,C,D y F. Consideraremos que el tablero está ordenado si las fichas están ordenadas según su valor numérico, de 1 a F, de izquierda a derecha y de arriba a abajo, quedando la casilla vacía en la última posición (abajo-derecha). Las funciones que podremos realizar son las siguientes:

  • Mover el cursor: 'i' (arriba), 'j' (izquierda), 'k' (abajo), 'l' (derecha)
  • Mover la ficha donde tenemos el cursor a la casilla vacía que tenga a su lado: <Espacio>
  • Generar puzle aleatorio: 'a' - IMPORTANTE: Esta opción tarda algo más de un minuto, no desesperar
  • Reescribir puzle desde cero: 'z' - Podremos componer nuestro propio puzle (solo con las fichas permitidas)
  • Si hemos pulsado la opción anterior, para volver al menú principal: <Esc>
  • Plantear puzle imposible: 'Ctrl+X' - Este fue el puzle original inventado por el matemático Sam Loyd
  • Salir del programa: 'q', solo está disponible en el menú principal.

Cada vez que se hace un movimiento el programa comprueba si se ha alcanzado la posición ganadora, en cuyo caso sale del mismo emitiendo un mensaje. También se terminará el juego si se agota el tiempo disponible (90 segundos en esta versión), si se plantea un puzle con errores posicionales de tablero (mediante la opción 'z') o si se agotan todos los movimientos de los que disponemos (80). El puzle imposible es puramente ilustrativo, pues de antemano se sabe que se terminará por agotar el tiempo o los movimientos, ya que está matemáticamente demostrado que no puede alcanzarse la solución desde esa posición de partida (Sam Loyd, 1878).

El siguiente código muestra el programa principal, donde se aprecia fácilmente el flujo de control del programa: bucle principal que espera la pulsación de una tecla, tras lo cual comprueba si hay alguna situación de final de partida o salida bajo demanda del programa, y procede según el caso. Los pasos se resumen así:

  1. Inicializamos el entorno: borramos la pantalla e imprimimos el tablero de juego
  2. Ponemos en memoria la posición inicial de la casilla blanca
  3. Colocamos el cursor en la casilla blanca
  4. Instalamos nuestra interrupción de reloj (ISR)
  5. Actualizamos el tablero de juego
  6. Comprobamos si ya hay situacion de final
  7. Si la variable State (estado) es igual a 1 seguimos jugando, si no, salimos mostrando el mensaje pertinente
  8. Desinstalamos nuestra interrupción de reloj (ISR)
  9. Fin de programa, código de error 0
A continuación, el código que implementa el algoritmo que acabamos de ver:

Main:
  STARTUPCODE
  call ClearScreen
  call PrintBoard
  mov Row,11
  mov Col,39
  call PosCursor
  call InstallISR
  call UpdateBoard
MainLoop:
  call ReadKey
  call CheckEnd
  cmp State,1
  je MainLoop
  call PrintMessage
  call UninstallISR
  EXITCODE 0
end Main

El siguiente enlace nos lleva al código completo en un documento HTML, desde el que podemos descargar el archivo en texto plano, listo para ensamblar y enlazar (15.ASM), así como la versión ejecutable del mismo (15.EXE); con la consideración de que está embebido junto con las instrucciones del juego en un archivo ZIP autoextraíble. Al igual que en el caso anterior, para probarlo en Windows 7+ se requiere emulador de DOS, por ejemplo DOSBox:

Documento HTML

Cálculo del día de la semana

Utilidad Javascript que nos da el día de la semana correspondiente a una fecha introducida por teclado. El cálculo principal se realiza en la función "calcular", a la que se le pasa el formulario que recoge los datos numéricos de la fecha; realiza los cálculos y devuelve el resultado (o condición de error) en un campo input debidamente formateado. El código que se muestra a continuación, incluido en nuestra utilidad, maximiza la ventana del navegador al total disponible:

window.moveTo(0,0);
if (document.all) {
	top.window.resizeTo(screen.availWidth, screen.availHeight);
}
else if (document.layers || document.getElementById) {
	if (top.window.outerHeight < screen.availHeight || top.window.outerWidth < screen.availWidth)	{
		top.window.outerHeight = screen.availHeight;
		top.window.outerWidth = screen.availWidth;
	}
}
window.status=''; // Texto que se mostraría en la barra de estado del navegador

En el primer enlace tenemos el código fuente, es decir, los archivos necesarios para que la aplicación funcione en cualquier equipo donde se copien, que son revisables con cualquier editor de texto. El archivo ZIP incluye la librería utilizada en este "proyecto", Nifty Corners (de Alessandro Fulcitini), para dibujar esquinas redondeadas al cuadro de texto del formulario, así como la hoja de estilos. El segundo lanza una demostración del uso del programa.

Código fuente Demostración

Función Hash

El hash de contraseñas es una de las consideraciones de seguridad más elementales que se deben llevar a la práctica al diseñar una aplicación que acepte contraseñas de los usuarios. Sin hashing, cualquier contraseña que se almacene en la base de datos de la aplicación podrá ser robada si la base de datos se ve comprometida, con lo que inmediatamente no sólo estaría comprometida la aplicación, sino también las cuentas de otros servicios de nuestros usuarios, siempre y cuando no utilicen contraseñas distintas (Fuente: PHP.net). En este ejemplo sencillo, vemos el uso de password_hash de PHP para calcular un hash de contraseña. La sintaxis es:

password_hash ( string $password , integer $algo [, array $options ] ) : string

El documento HTML contiene un formulario que solicitará un nombre de usuario y una contraseña, que procesará mediante la anterior función de hash incluida en el archivo PHP, devolviendo el resultado en pantalla. Este es el código del HTML:

<!DOCTYPE html>
<html lang="es">
<head>
	<title>Hash de contraseña</title>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
	<center> <!-- La etiqueta center está deprecada, la usamos para evitar crear hojas de estilo -->
	<h1>Hash de contraseña</h1>
	<hr/>
	<form action="algo.php" method="post" >
	<label>Nombre de usuario:</label><br>
	<input name="username" type="text" id="username" required>
	<br><br>
	<label>Contraseña:</label><br>
	<input name="password" type="password" id="password" required>
	<br><br>
	<input type="submit" name="Submit" value="Generar Hash">
	</form>
	<hr/>
	<footer>(L) 2016-2017 djmm</footer>
	</center>
	</body>
</html>

El algoritmo PHP que procesa los datos del formulario (se le pasa en el atributo action) es el siguiente:

<?php
	/* Hash de contraseña uando el algoritmo DEFAULT actual (BCRYPT), que dará un resultado de 60 caracteres
	(esto podría variar con el tiempo) */
	mb_internal_encoding ('UTF-8'); // Establecemos la codificación para las llamadas y respuestas HTTP
	echo '<html><head><title>Hash de contraseña</title></head>';
	echo '<body><center><h2>Hash de contraseña</h2></center>';
	echo '<center><table border="0"><tr><td>';
	echo "<br><u>RESULTADO</u><br/><br/>";
	echo "Usuario: <span style='color:blue'>", $_POST['username']."</span><br/>";
	echo "Contraseña: <span style='color:blue'>", $_POST['password']."</span><br/><br/>";
	echo "Hash generado: <span style='color:red'>", password_hash($_POST['password'], PASSWORD_DEFAULT)."</span>";
	echo '</td></tr></table></center></body></html>';
?>

Para descargar la utilidad en formato ZIP clic en el primer enlace, en el segundo podemos lanzar una prueba o demostración de la misma preinstalada en el servidor donde reside esta página web:

Código fuente Demostración

La clase BigInteger de Java

En ocasiones la capacidad de los tipos numéricos predefidos de Java se queda corta. Un típico ejemplo de ello sería el cálculo del factorial de un número "grande", por ejemplo 100. Sí, el factorial de un número en apariencia bajo como 100 provoca desbordamiento si se trata como Integer. De hecho, éste es un número de 158 cifras, y la clase Integer va desde el intervalo -9.223.372.036.854.775.808 a +9.223.372.036.854.775.807. Ni siquiera la clase Double puede ayudarnos aquí, ya que va desde 1,40129846432481707e-45 a 3,40282346638528860e+38, suficiente para la mayoría de las operaciones cotidianas, pero no para este caso. Para cubrir esta necesidad tenemos a nuestra disposición la clase BigInteger, cuyo uso vamos a ver en el siguiente código, que calcula el factorial de 100:

package djmm.pruebas;
import java.math.BigInteger;
public class PruebaFactorial {
	public static void main(String[] args) {
		int i = 100;
		BigInteger bi = new BigInteger("100");
		System.out.println("El sumatorio de " + i + " es: " + sumatorio(i));
		System.out.println("El factorial de " + bi + " es: " + factorial(bi));
	}
	public static int sumatorio(int n) {
		int contador = 0;
		int suma = 0;
		while (contador <= n) {
			suma += contador;
			contador++;
		}
		return suma;
	}
	public static BigInteger factorial(BigInteger n) {
		if (n.compareTo(BigInteger.ZERO) == -1) throw new RuntimeException("No se pueden procesar valores negativos");
		BigInteger resultado = BigInteger.ONE;
		while (!n.equals(BigInteger.ZERO)) {
			resultado = resultado.multiply(n);
			n = n.subtract(BigInteger.ONE);
		}
		return resultado;
	}
}

Desde los enlaces siguientes podemos descargar el snippet con el código, así como un fichero de texto plano con la salida que devolvería la ejecución del mismo, el sumatorio y el factorial, en Eclipse Photon 4.8, plataforma JavaSE-1.8.

Código fuente Salida del programa

Operativas MySQL

Cuando trabajamos con bases de datos, a veces es necesario "echar mano" de ciertos trucos que nos facilitan la gestión de las mismas, o bien nos sirven para un propósito concreto en el marco de alguna aplicación o script. Las sentencias más comunes salen solas, pero es bueno tener alguna "chuleta" con otras algo más complejas, o de uso menos habitual. A continuación, ofrezco una selección de algunas que considero interesantes.

Conocer el último número de una secuencia AUTO_INCREMENT, así como reiniciarla: Para ello, necesitaremos acceder a un schema de MySQL llamado 'information_schema', tal como figura en el código siguiente:

/* Para saber el último número de la secuencia AUTO_INCREMENT */
SELECT AUTO_INCREMENT AS UltimoId FROM information_schema.tables WHERE TABLE_SCHEMA='nombre_bd' AND TABLE_NAME='nombre_tabla';
/* Para poner el AUTO_INCREMENT a 0 (reiniciar la secuencia) */
ALTER TABLE nombre_tabla AUTO_INCREMENT = 0;

Añadir un campo TIMESTAMP a posteriori: Si se nos pasa hacerlo cuando es debido o bien queremos cambiar el evento que dispara el TIMESTAMP, tenemos una forma de conseguirlo mediante la sentencia ALTER TABLE, como ilustran los ejemplos:

/* Para añadir un timestamp de creación de campo */
ALTER TABLE nombre_tabla MODIFY fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON CREATE CURRENT_TIMESTAMP;
/* Para añadir un timestamp de modificación de campo */
ALTER TABLE nombre_tabla MODIFY fecha_publicacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Asignar privilegios a usuario para volcar una tabla a un fichero: Esto puede ser útil para migración de servidores MySQL, copias de seguridad locales, o creación de réplicas para servidores MySQL, muy común esto en entornos corporativos. Un ejemplo en el siguiente código:

/* Asignar privilegios a usuario para volcar tabla a fichero */
GRANT ALL PRIVILEGES ON schema.* to 'usuario'@'localhost' identified by 'mi_password';
FLUSH PRIVILEGES;
GRANT FILE ON *.* TO 'usuario'@'localhost';
SELECT * FROM tabla INTO OUTFILE 'D:/fichero.txt'  FIELDS TERMINATED BY ';' ESCAPED BY '\\' ENCLOSED BY '"' LINES TERMINATED BY '\r\n';
echo "select * from tabla" | mysql --host=localhost --port=3306 --user=usuario --password=mi_password database.schema > salida.txt

Reemplazo de una cadena por otra en un campo de una tabla MySQL: Difícil encontrar un administrador de bases de datos que no haya tenido que cambiar masivamente el contenido de un campo por otro, bien total o solo parcialmente (un trozo o subcadena de dicho campo por otra distinta). La forma de lograr esto fácilmente en MySQL es mediante el uso de la sentencia UPDATE. Dicho esto, cabe añadir que el uso de las expresiones regulares potencia sumamente esta característica, consiguiendo resultados verdaderamente sorprendentes con muy poco código. En los snippets de ejemplo siguientes, veremos en primer lugar la sustitución de una cadena completa por otra, seguido de tres ejemplos que ilustran el uso de REGEXP en las selects:

/* Cambiamos en nombre_campo la cadena antigua (que podría ser el todo valor del campo) por la cadena nueva */
UPDATE nombre_tabla SET nombre_campo = REPLACE ( nombre_campo, 'cadena_antigua', 'cadena_nueva' );
/* Muestra todo aquello que comienza por la cadena '<p><code>' y termina por '</code></p>' */
SELECT nombre_campo FROM nombre_tabla WHERE nombre_campo REGEXP '^<p><code>.*</code></p>$';
/* Muestra todo aquello que comienza por la cadena '<pre>' (sin apóstrofe) */
SELECT * FROM nombre_tabla WHERE nombre_campo REGEXP '^<pre>';
/* Muestra el resultado de reemplazar '<code>' por '<nabo>' en todos los registros cuyo campo descripción contiene '^<p><code>' */
SELECT REPLACE ( nombre_campo, '<code>', '<nabo>' ) FROM nombre_tabla WHERE nombre_campo REGEXP '^<p><code>';

Desde el enlace siguiente podemos descargar en un archivo de texto plano (extensión SQL) los ejemplos citados anteriormente. Todos y cada uno de ellos han sido probados satisfactoriamente en una base de datos sobre un servidor de MariaDB 10.3 (fork estable de MySQL):

Código fuente