4.
Cómo generar números al azar. Un primer juego:
adivinar números.
Contenido
de este apartado:
4.1.
Pautas generales.
Un requisito fundamental en la mayoría de los juegos es que
no sea siempre igual, para que no sea predecible. Si hay cosas al azar,
no bastará con que el jugador memorice, sino que
tendrá que enfrentarse a retos que no serán
siempre los mismos.
Por eso, vamos a ver cómo generar números al
azar. A partir de esos números, haremos el que posiblemente
es el juego más sencillo posible: adivinar un
número oculto, teniendo una cantidad limitada de intentos.
En cuanto a obtener números aleatorios al azar, suele haber
ciertas cosas comunes en casi cualquier lenguaje de
programación: las funciones que nos dan ese
número se llamarán "rand" o algo muy parecido,
porque "aleatorio" en inglés es "random". Por otra parte, en
muchos lenguajes es necesario decirle una "semilla" a partir de la que
empezar a generar los números. Si la semilla fuera siempre
la misma, los números obtenidos, por lo que se suele usar
como semilla el reloj interno del ordenador, porque sería
casi imposible que dos partidas comenzaran exactamente a la misma hora
(con una precisión de centésimas de segundo o
más). Para complicar la situación,
en algunos lenguajes la función "rand" (o como se
llame) nos da un número entre 0 y 1, que nosotros
deberíamos multiplicar si queremos números
más grandes (eso pasa en Pascal y Java); en otros
lenguajes obtenemos un número muy grande, entre 0 y otro
valor (a veces cerca de 32.000, otras cerca de 2.000 millones,
según la biblioteca que usemos), como es el caso de
C, así que para obtener un número más
pequeño lo que haremos es dividir y quedarnos con el resto
de esa división.
Así, en nuestro caso, en la versión de C del
juego haremos (semilla a partir del reloj, y un número
enorme, del que tomamos el resto de la division):
srand(time(0));
numeroAdivinar = rand() % MAXIMONUMERO;
y en Pascal algo como (primero la semilla y luego un número
entre 0 y 1, que multiplicamos)
randomize;
numeroAdivinar := round(random * MAXIMONUMERO);
y en Java (similar a Pascal, pero sin necesidad de pedir que
se cree la semilla):
numeroAdivinar = (int) Math.round(Math.random() *
MAXIMONUMERO);
Por lo que respecta a nuestro juego, la idea de lo que tiene que hacer
(lo que se suele llamar "pseudocódigo") podría
ser algo así:
Generar
un número al azar entre 0 y 99
acertado = FALSO
repetir
pedir número al usuario
si el número tecleado el número al
azar, terminado = VERDADERO
en caso contrario, si el
número tecleado es más pequeño, avisar
en caso contrario, si el
número tecleado es mayor, avisar
incrementar Numero de intentos
hasta que (acertado) o (Numero de intentos = maximo)
si acertado, felicitar
en caso contrario, decir qué número
era el correcto
Pasar de aquí a la práctica no debería
ser difícil, salvo quizá por el hecho de "pedir
número al usuario". Como ya habíamos comentado,
en general no tendremos en modo gráfico rutinas que permitan
leer entradas complejas por teclado. Pero como esa es la
única complicación de este juego, lo podemos
evitar de una forma sencilla: obligaremos a que el usuario tenga que
teclear un número de 2 cifras entre 00 y 99, de modo que nos
bastará con dos órdenes de comprobar una
pulsación de teclado (readkey, getch o similar).
Pasar de las dos letras "1" y "4" al número 14 tampoco es
difícil. Algunos lenguajes nos permitirán
"juntar" (concatenar, hablando más correctamente) las dos
letras para obtener "14" y luego decir que calcule el valor
numérico de este texto. Otra opción es restar la
letra "0" a cada cifra, con lo cual ya tenemos el valor
numérico de cada una, es decir 1 y 4; para obtener el 14
basta con multiplicar el primer número por 10 y sumarle el
segundo. Este es el método que usaremos en C y en Pascal en
este ejemplo, para no necesitar recurrir a bibliotecas externas de
funciones para convertir los valores.
(Un consejo: intenta hacer
cada juego
tú
mismo antes de ver cómo lo he resuelto yo.
El
enfrentarte con los problemas y comparar con
otras
soluciones hará que aprendas mucho
más
que si te limitas a observar)
4.2
adivinar Números Con C y Allegro.
Va a ser muy poco más que seguir el pseudocódigo
de antes, pero adaptado a la sintaxis del lenguaje C y dando alguna
vuelta puntual en casos como el de leer lo que teclea el usuario, que
ya hemos comentado:
/*----------------------------*/
/* Intro a la programac de */
/* juegos, por Nacho Cabanes */
/* */
/* IPJ04C.C */
/* */
/* Cuarto ejemplo: adivinar */
/* un numero - modo gráfico */
/* */
/* Comprobado con: */
/* - MinGW DevStudio 2.05 */
/* (gcc 3.4.2) y Allegro */
/* 4.03, Windows XP */
/*----------------------------*/
#include <allegro.h>
int numeroAdivinar, numeroTecleado;
char tecla1, tecla2;
int acertado;
int intentos;
int lineaEscritura;
#define MAXIMONUMERO 99
#define NUMEROINTENTOS 6
int main()
{
allegro_init();
install_keyboard();
if (set_gfx_mode(GFX_SAFE,320,200,0,0)!=0){
set_gfx_mode(GFX_TEXT,0,0,0,0);
allegro_message(
"Incapaz de entrar a modo grafico\n%s\n",
allegro_error);
return 1;
}
intentos = 0;
lineaEscritura = 50;
srand(time(0));
numeroAdivinar = rand() % MAXIMONUMERO;
acertado = 0;
textout(screen, font, "Adivinar numeros", 10,10,
palette_color[14]);
do {
textout(screen, font, "Teclea dos cifras (00 a 99)", 15,lineaEscritura,
palette_color[13]);
tecla1 = readkey();
textprintf(screen, font, 235,lineaEscritura,
palette_color[13], "%c", tecla1); tecla2 = readkey();
textprintf(screen, font, 243,lineaEscritura,
palette_color[13], "%c", tecla2);
numeroTecleado = (int) (tecla1 - '0') * 10
+ tecla2 - '0';
if (numeroTecleado == numeroAdivinar) acertado = 1;
else if (numeroTecleado < numeroAdivinar) textout(screen, font, "Corto", 260,lineaEscritura,
palette_color[12]);
else if (numeroTecleado > numeroAdivinar) textout(screen, font, "Grande", 260,lineaEscritura,
palette_color[12]);
intentos++;
lineaEscritura += 10;
} while( (!acertado) && (intentos < NUMEROINTENTOS));
if (acertado)
textout(screen, font, "Acertaste!!!", 160,180,
palette_color[15]);
else
textprintf(screen, font, 160,180,
palette_color[15], "Era: %d", numeroAdivinar);
readkey();
}
END_OF_MAIN();
|
que
se vería así:

4.3.
Adivinar números en Pascal.
El juego en Pascal es casi idéntico al de C (salvando la
diferencia de sintaxis, claro):
(*----------------------------*)
(* Intro a la programac de *)
(* juegos, por Nacho Cabanes *)
(* *)
(* IPJ04P.PAS *)
(* *)
(* Cuarto ejemplo: adivinar *)
(* un numero - modo grafico *)
(* *)
(* Comprobado con: *)
(* - FreePascal 1.0.10 -Dos *)
(* - FreePascal 2.0 -Windows *)
(*----------------------------*)
uses crt, graph, sysUtils;
(* Cambiar por "uses wincrt, ..." bajo Windows *)
var
gd,gm, error : integer;
numeroAdivinar, numeroTecleado: integer;
tecla1, tecla2: char;
acertado: boolean;
intentos: integer;
lineaEscritura: integer;
const
MAXIMONUMERO = 99;
NUMEROINTENTOS = 5;
BEGIN
gd := D8BIT;
gm := m320x200; (* Si falla bajo Windows, probar gd:=0; gm:=0; *) initgraph(gd, gm, '');
error := graphResult;
if error <> grOk then
begin
writeLn('No se pudo entrar a modo grafico');
writeLn('Error encontrado: '+ graphErrorMsg(error) );
halt(1);
end;
intentos := 0;
lineaEscritura := 50;
randomize;
numeroAdivinar := round(random * MAXIMONUMERO);
acertado := false;
setColor(14);
outTextXY(10,10, 'Adivinar numeros');
repeat
outTextXY(15,lineaEscritura,'Teclea dos cifras (00 a 99)');
tecla1 := readkey;
outTextXY(235,lineaEscritura,tecla1);
tecla2 := readkey;
outTextXY(243,lineaEscritura,tecla2);
numeroTecleado := (ord(tecla1)-ord('0')) * 10
+ord(tecla2)-ord('0');
if numeroTecleado = numeroAdivinar then acertado := TRUE
else if numeroTecleado < numeroAdivinar then
outTextXY(260, lineaEscritura, 'Corto')
else if numeroTecleado > numeroAdivinar then
outTextXY(260, lineaEscritura, 'Grande');
intentos := intentos + 1;
lineaEscritura := lineaEscritura + 10;
until acertado or (intentos = NUMEROINTENTOS);
setColor(15);
if acertado then
outTextXY(160,180, 'Acertaste!!!')
else
outTextXY(160,180, 'Era: '+intToStr(numeroAdivinar));
readkey;
closeGraph;
END.
|
que
tendría en pantalla una apariencia idéntica a la
versión en C... si compilamos para MsDos.
Eso
sí, existe un problema
en la versión para Windows:
al usar a la vez la biblioteca "crt" para leer del teclado y la "graph"
para dibujar en pantalla, nos aparecen dos ventanas. La que nos muestra
el texto escrito (o los dibujos) no hace caso a las teclas que
pulsemos; la otra hace caso a nuestro teclado, pero no muestra los
dibujos. Así que nos tocaría ir saltando
continuamente de una ventana a otra, para teclear y ver los resultados.
¿Solución? Si vamos a compilar para Windows, basta con
cambiar la línea "uses crt, graph;" por "uses wincrt, graph;".
Sólo con eso ya debería funcionar correctamente bajo
Windows.
4.4.
Adivinar números en Java.
Hay
dos diferencias
importantes con la version en C
- La
forma de acceder al teclado es algo
más incómoda (por ahora, en ciertos casos nos
resultará más práctica esta forma de
trabajar), como ya hemos visto
- Las
"cosas" dentro de programa estan ordenadas de forma un poco
especial: por la forma de funcionar los
Applets, las
rutinas de inicialización aparecen en el método
"init". De igual modo, las rutinas de escritura en pantalla aparecen en
el método "paint" . Finalmente, las operaciones
básicas que hay que
realizar cuando se pulsa una tecla las he incluido en una de las
rutinas de comprobación de teclado (en "keyPressed").
El resto debería ser
fácil
de seguir:
/*----------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* ipj04j.java */ /* */ /* Cuarto ejemplo: adivinar */
/* un numero - modo gráfico */
/* */
/* Comprobado con: */
/* - JDK 1.5.0 */
/*----------------------------*/
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class ipj04j extends Applet implements KeyListener {
int numeroAdivinar, numeroTecleado;
char tecla1='a', tecla2='a';
boolean terminado=false;
int intentos=0;
int lineaEscritura=50;
int i;
final int MAXIMONUMERO = 99;
final int NUMEROINTENTOS = 6;
int respuestas[] = {-1,-1,-1,-1,-1,-1};
char letra;
public void paint(Graphics g) {
// Cartel "principal"
g.setColor(Color.red);
g.drawString("Adivinar numeros",10,10);
// Escribo "n" veces lo que ya ha tecleado
for (i=0; i<=intentos; i++) {
g.setColor(Color.green);
g.drawString("Teclea dos cifras (00 a 99)",
15,lineaEscritura+i*10);
g.setColor(Color.blue); // Sólo escribo la respuesta si realmente la hay
if (respuestas[i] != -1){
g.drawString(""+respuestas[i], 235, lineaEscritura+i*10);
// Y, de paso, compruebo si ha acertado
if (respuestas[i] == numeroAdivinar) {
terminado= true;
g.drawString("Acertaste!!!", 160, 180);
}
// O si todavía hay que ayudarle
else if (respuestas[i] < numeroAdivinar)
g.drawString("Corto", 260, lineaEscritura+i*10);
else
g.drawString("Grande", 260, lineaEscritura+i*10);
}
}
// Si no quedan intentos, digo cual era
if (terminado) g.drawString("Era: "+numeroAdivinar, 160, 190);
}
public void init() {
numeroAdivinar = (int) Math.round(Math.random() * MAXIMONUMERO);
addKeyListener(this);
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (! terminado) {
// Leo la primera tecla si corresponde
if (tecla1 == 'a') {
tecla1 = e.getKeyChar();
tecla2 = 'a';
}
else // Y si no, leo la segunda
{
tecla2 = e.getKeyChar();
// Si era la segunda, calculo el número tecleado
numeroTecleado = new Integer (""+tecla1+tecla2);
respuestas[intentos] = numeroTecleado;
tecla1='a';
intentos++;
if (intentos==NUMEROINTENTOS) {
terminado = true;
intentos --; // Para no salirme en "respuestas[i]"
}
}
}
repaint(); }
}
|
que
tendría como resultado:

 
|