En este tema vamos a ver cómo acceder a la pantalla de texto, y algunos "bonus". :-)
Este tema va a ser específico de Turbo Pascal para DOS.
Algunas de las cosas que veremos aparecen en otras versiones de Turbo Pascal
(la 3.0 para CP/M, por ejemplo), pero no todas, y de cualquier modo, nada
de esto es Pascal estándar. Aun así, como muchas versiones de Pascal posteriores (Tmt Pascal, Virtual Pascal, Free Pascal, etc) buscan una cierta compatibilidad con Turbo Pascal, es fácil que funcione con muchos compiladores recientes.
Me centraré primero en cómo se haría con las versiones 5.0 y superiores de Turbo Pascal. Luego comentaré cómo se haría con Turbo Pascal 3.01.
Vamos allá... En la mayoría de los lenguajes de programación, existen "bibliotecas" (en inglés, "library") con funciones y procedimientos nuevos, que permiten ampliar el lenguaje. En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades" (UNIT), y existen a partir de la versión 5.
Veremos cómo crearlas un poco más adelante, pero de momento nos va a interesar saber cómo acceder a ellas, porque "de fábrica" ;-) Turbo Pascal incorpora unidades que aportan mayores posibilidades de manejo de la pantalla en modo texto o gráfico, de acceso a funciones del DOS, de manejo de la impresora, etc.
Así, la primera unidad que trataremos es la encargada de gestionar (entre otras cosas) la pantalla en modo texto. Esta unidad se llama CRT.
Para acceder a cualquier unidad, se emplea la sentencia "uses" justo después de "program" y antes de las declaraciones de variables:
program prueba;
uses crt;
var
[...]
Voy a mencionar algunos de los procedimientos y funciones más importantes. Al final de esta lección resumo todos los que hay y para qué sirven.
Algunos "extras" no relacionados con la pantalla son:
Comentarios generales, la mayoría "triviales"
:
Aquí va un programita de ejemplo que maneja todo esto...
o casi
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Ejemplo de la unidad }
{ CRT: acceso a panta- }
{ lla en modo texto TP }
{ EJCRT.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Turbo Pascal 7.0 }
{ - Tmt Pascal Lt 1.01 }
{--------------------------}
program PruebaDeCRT;
uses crt;
var
bucle : byte;
tecla : char;
begin
ClrScr; { Borra la pantalla }
TextColor( Yellow ); { Color amarillo }
TextBackground( Red ); { Fondo rojo }
GotoXY( 40, 13 ); { Vamos al centro de la pantalla }
Write(' Hola '); { Saludamos ;-) }
Delay( 1000 ); { Esperamos un segundo }
Window ( 1, 15, 80, 23 ); { Ventana entre las filas 15 y 23 }
TextBackground ( Blue ); { Con fondo azul }
ClrScr; { La borramos para que se vea }
for bucle := 1 to 100
do WriteLn( bucle ); { Escribimos del 1 al 100 }
WriteLn( 'Pulse una tecla..');
tecla := ReadKey; { Esperamos que se pulse una tecla }
Window( 1, 1, 80, 25 ); { Restauramos ventana original }
GotoXY( 1, 24 ); { Vamos a la penúltima línea }
Write( 'Ha pulsado ', tecla ); { Pos eso }
Sound( 220 ); { Sonido de frecuencia 220 Hz }
Delay( 500 ); { Durante medio segundo }
NoSound; { Se acabó el sonido }
Delay( 2000 ); { Pausa antes de acabar }
TextColor( LightGray ); { Colores por defecto del DOS }
TextBackground( Black ); { Y borramos la pantalla }
ClrScr;
end.
Finalmente, veamos los cambios para Turbo Pascal 3.01: En TP3
no existen unidades, por lo que la línea "uses crt;" no existiría.
La otra diferencia es que para leer una tecla se hace con "read(kbd,
tecla);" (leer de un dispositivio especial, el teclado, denotado
con kbd) en vez de con "tecla := readkey". Con estos dos cambios,
el programa anterior funciona perfectamente.
Con estas consideraciones, el programa (que aun así se parece)
queda:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Pantalla de texto }
{ con Surpas }
{ EJCRTSP.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Surpas 1.00 }
{--------------------------}
program PruebaDeCRT;
{ ----- Aqui empiezan las definiciones que hacen que SurPas
maneje la pantalla de forma similar a Turbo Pascal ------ }
{ Para comprender de donde ha salido esto, consulta el
fichero IBMPC.DOC que acompaña a SURPAS }
const
Black = 0;
Blue = 1;
Green = 2;
Cyan = 3;
Red = 4;
Magenta = 5;
Brown = 6;
LightGray = 7;
DarkGray = 8;
LightBlue = 9;
LightGreen = 10;
LightCyan = 11;
LightRed = 12;
LightMagenta= 13;
Yellow = 14;
White = 15;
procedure ClrScr;
begin
gotoxy(0,0);
write( CLREOS );
end;
procedure TextColor(n: byte);
begin
write( chr(27), 'b', chr(n) );
end;
procedure TextBackground(n: byte);
begin
write( chr(27), 'c', chr(n) );
end;
{ Se podrían añadir otras posibilidades, como TextMode, HighVideo y
LowVideo, etc, siguiendo este esquema, si se cree necesario }
{ ----- Final del añadido ----- }
var
bucle : byte;
tecla : char;
begin
ClrScr; { Borra la pantalla }
TextColor( Yellow ); { Color amarillo }
TextBackground( Red ); { Fondo rojo }
GotoXY( 30, 13 ); { Vamos al centro de la pantalla }
Write(' Hola. Pulse una tecla... '); { Saludamos ;-) }
read(kbd,tecla); { Esperamos que se pulse una tecla }
TextBackground ( Blue ); { Con fondo azul }
ClrScr; { La borramos para que se vea }
for bucle := 1 to 100
do Write( bucle, ' ' ); { Escribimos del 1 al 100 }
WriteLn; { Avanzamos una línea }
WriteLn( 'Pulse otra tecla..');
read(kbd,tecla); { Esperamos que se pulse una tecla }
GotoXY( 0, 12 ); { Vamos al centro de la pantalla }
Write( 'Ha pulsado ', tecla ); { Pos eso }
TextColor( LightGray ); { Colores por defecto del DOS }
TextBackground( Black ); { Y borramos la pantalla }
GotoXY( 0, 23 ); { Vamos a la penúltima línea }
end.
También tenemos las variables siguientes (cuyo valor podemos leer o cambiar):
Y como constantes (por ejemplo, para poder escribir el nombre de un color en vez de recordar qué número indica ese color, como hemos hecho en los ejemplos anteriores):
Las constantes que indican los modos de pantalla son:
Y por compatibilidad con la versión 3.0:
Pues hala, a experimentar...
Antes de dejar el tema, un ejemplo sencillo que ponga a prueba algunas
de las cosas que hemos visto: vamos a hacer el juego del ahorcado:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Juego del Ahorcado }
{ AHORCA.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Tmt Pascal Lt 1.20 }
{--------------------------}
Program Ahorcado;
Uses crt;
Var
palabra, intento, letras:string; { La palabra a adivinar, la que }
{ el jugador 2 va consiguiendo y }
{ las letras que se han probado }
oportunidades: integer; { El número de intentos permitido }
letra: char; { Cada letra que prueba el jug. dos }
i: integer; { Para mirar cada letra, con "for" }
acertado: boolean; { Si ha acertado alguna letra }
begin
clrscr; { Valores iniciales, del jugador 1 }
gotoxy (10,5);
write ('Jugador 1: ¿Que frase hay que adivinar? ');
readln (palabra);
gotoxy (10,7);
write ('¿En cuantos intentos? ');
readln (oportunidades);
intento := ''; { Relleno con _ y " " lo que ve Jug. 2 }
for i:=1 to length(palabra) do
if palabra[i]= ' ' then
intento:=intento+' '
else
intento:=intento+'_';
repeat
clrscr;
gotoxy (10,6); { Digo cuantos intentos le quedan }
writeln('Te quedan ',oportunidades,' intentos');
gotoxy (10,8); { Le muestro cómo va }
writeln(intento);
gotoxy (10,10); { Y le pido otra letra }
write('Letras intentadas: ', letras);
gotoxy (10,12); { Y le pido otra letra }
write('¿Qué letra? ');
letra := readkey;
letras := letras + letra;
acertado := false; { Miro a ver si ha acertado }
for i:=1 to length(palabra) do
if letra=palabra[i] then
begin
intento[i]:=palabra[i];
acertado := true;
end;
if acertado = false then { Si falló, le queda un intento menos }
oportunidades := oportunidades-1;
until (intento=palabra) { Hasta que acierte }
or (oportunidades=0); { o gaste sus oportunidades }
gotoxy(10, 15); { Le felicito o le digo cual era }
if intento=palabra then
writeln('¡Acertaste!')
else
writeln('Lo siento. Era: ', palabra);
end.
Esto es muy mejorable. La primera mejora será que no haya necesidad
de que un primer jugador sea el que escriba la palabra a adivinar y el
número de intentos, sino que el número de intentos esté
prefijado en el programa, y exista una serie de palabras de las que el
ordenador escoja una al azar (para lo que usaremos "random" y "randomize",
que se ven con más detalle en la Ampliación 1):
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Juego del Ahorcado }
{ (segunda version) }
{ AHORCA2.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Tmt Pascal Lt 1.20 }
{--------------------------}
Program Ahorcado2;
Uses crt;
Const
NUMPALABRAS = 10;
MAXINTENTOS = 2;
datosPalabras: array[1..NUMPALABRAS] of string =
(
'Alicante','Barcelona','Guadalajara','Madrid',
'Toledo','Malaga','Zaragoza','Sevilla',
'Valencia','Valladolid'
);
Var
palabra, intento, letras:string; { La palabra a adivinar, la que }
{ el jugador 2 va consiguiendo y }
{ las letras que se han probado }
numeroPalabra: word;
oportunidades: integer; { El numero de intentos permitido }
letra: char; { Cada letra que prueba el jug. dos }
i: integer; { Para mirar cada letra, con "for" }
acertado: boolean; { Si ha acertado alguna letra }
begin
randomize; { Comienzo a generar numeros aleatorios }
numeroPalabra := { Tomo una palabra al azar }
random(NUMPALABRAS);
palabra := datosPalabras[numeroPalabra+1];
oportunidades := MAXINTENTOS;
intento := ''; { Relleno con _ y " " lo que ve Jug. 2 }
for i:=1 to length(palabra) do
if palabra[i]= ' ' then
intento:=intento+' '
else
intento:=intento+'*';
repeat
clrscr;
gotoxy (10,6); { Digo cuantos intentos le quedan }
writeln('Te quedan ',oportunidades,' intentos');
gotoxy (10,8); { Le muestro como va }
writeln(intento);
gotoxy (10,10); { Y le pido otra letra }
write('Letras intentadas: ', letras);
gotoxy (10,12); { Y le pido otra letra }
write('¿Qué letra? ');
letra := upcase(readkey); { Convierto a mayusculas }
letras := letras + letra;
acertado := false; { Miro a ver si ha acertado }
for i:=1 to length(palabra) do
if letra=upcase(palabra[i]) then
begin { Comparo en mayusculas }
intento[i]:=palabra[i];
acertado := true;
end;
if acertado = false then { Si falla, le queda un intento menos }
oportunidades := oportunidades-1;
until (intento=palabra) { Hasta que acierte }
or (oportunidades=0); { o gaste sus oportunidades }
gotoxy(10, 15); { Le felicito o le digo cual era }
if intento=palabra then
begin
gotoxy (10,8);
writeln(intento);
gotoxy(10, 15);
writeln('¡Acertaste!')
end
else
writeln('Lo siento. Era: ', palabra);
end.
Una segunda mejora podría ser que realmente "se dibujara" el ahorcado en pantalla en vez de limitarse a decirnos cuantos intentos nos quedan. Como todavía no sabemos manejar la pantalla en modo gráfico, dibujaremos de un modo rudimentario, empleando letras. El resultado será "feo", algo parecido a esto:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Juego del Ahorcado }
{ (tercera version) }
{ AHORCA3.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Tmt Pascal Lt 1.20 }
{--------------------------}
Program Ahorcado3;
Uses crt;
Const
NUMPALABRAS = 10;
datosPalabras: array[1..NUMPALABRAS] of string =
(
'Alicante','Barcelona','Guadalajara','Madrid',
'Toledo','Malaga','Zaragoza','Sevilla',
'Valencia','Valladolid'
);
MAXINTENTOS = 5; { No debe ser modificado: vamos a "dibujar" 5 cosas }
Var
palabra, intento, letras:string; { La palabra a adivinar, la que }
{ el jugador 2 va consiguiendo y }
{ las letras que se han probado }
numeroPalabra: word;
oportunidades: integer; { El numero de intentos permitido }
letra: char; { Cada letra que prueba el jug. dos }
i: integer; { Para mirar cada letra, con "for" }
acertado: boolean; { Si ha acertado alguna letra }
procedure PrimerFallo; { Primer fallo: }
var
j: byte; { Dibujamos la "plataforma" }
begin
for j := 50 to 60 do
begin
gotoxy(j,20);
write('-');
end;
end;
procedure SegundoFallo; { Segundo fallo: }
var
j: byte; { Dibujamos el "palo vertical" }
begin
for j := 14 to 19 do
begin
gotoxy(53,j);
write('|');
end;
end;
procedure TercerFallo; { Tercer fallo: }
var
j: byte; { Dibujamos el "palo superior" }
begin
for j := 53 to 57 do
begin
gotoxy(j,14);
write('-');
end;
end;
procedure CuartoFallo; { Cuarto fallo: }
var
j: byte; { Dibujamos la "plataforma" }
begin
gotoxy(57,15);
write('|');
end;
procedure QuintoFallo; { Quinto fallo: }
var
j: byte; { Dibujamos la "plataforma" }
begin
gotoxy(56,16);
write(' O');
gotoxy(56,17);
write('/|\');
gotoxy(56,18);
write('/ \');
for j := 50 to 60 do
begin
gotoxy(j,20);
write('-');
end;
end;
begin
randomize; { Comienzo a generar numeros aleatorios }
numeroPalabra := { Tomo una palabra al azar }
random(NUMPALABRAS);
palabra := datosPalabras[numeroPalabra+1];
oportunidades := MAXINTENTOS;
intento := ''; { Relleno con _ y " " lo que ve Jug. 2 }
for i:=1 to length(palabra) do
if palabra[i]= ' ' then
intento:=intento+' '
else
intento:=intento+'*';
repeat
clrscr;
{ Dibujo lo que corresponde del "patibulo" }
if oportunidades <= 4 then PrimerFallo;
if oportunidades <= 3 then SegundoFallo;
if oportunidades <= 2 then TercerFallo;
if oportunidades <= 1 then CuartoFallo;
gotoxy (10,6); { Digo cuantos intentos le quedan }
writeln('Te quedan ',oportunidades,' intentos');
gotoxy (10,8); { Le muestro como va }
writeln(intento);
gotoxy (10,10); { Y le pido otra letra }
write('Letras intentadas: ', letras);
gotoxy (10,12); { Y le pido otra letra }
write('¿Qué letra? ');
letra := upcase(readkey); { Convierto a mayusculas }
letras := letras + letra;
acertado := false; { Miro a ver si ha acertado }
for i:=1 to length(palabra) do
if letra=upcase(palabra[i]) then
begin { Comparo en mayusculas }
intento[i]:=palabra[i];
acertado := true;
end;
if acertado = false then { Si falla, le queda un intento menos }
oportunidades := oportunidades-1;
until (intento=palabra) { Hasta que acierte }
or (oportunidades=0); { o gaste sus oportunidades }
{ Le felicito o le digo cual era }
if intento=palabra then
begin
gotoxy (10,8);
writeln(intento);
gotoxy(10, 15);
writeln('¡Acertaste!')
end
else
begin
QuintoFallo;
gotoxy(10, 15);
writeln('Lo siento. Era: ', palabra);
end;
end.
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Entrada mejorada de }
{ datos }
{ ENTRMEJ.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Turbo Pascal 7.0 }
{ - Turbo Pascal 5.0 }
{--------------------------}
{
PIDEXY: Entrada mejorada de datos.
- En cualquier posición de la pantalla.
- Muestra el tamaño máximo y lo limita.
- Permite usar las flechas, Inicio, Fin, Ctrl+Flechas, Supr.
- Modo de inserción o sobreescritura.
-- Esto es parte de CUPAS, curso de Pascal por Nacho Cabanes --
-- Este ejemplo sólo funcionará en Turbo Pascal 5.0 o superior --
Mejoras posibles no incluidas:
- Cambiar la forma del cursor según se esté en modo de inserción o
sobreescritura.
- Quitar los espacios sobrantes a la derecha de la palabra.
- Permitir la edición en varias líneas.
- Seguro que alguna más... :-)
}
uses crt;
FUNCTION SPC(n:byte):string; {Espacios en blanco}
var b:string; c:byte;
begin
b:='';
for c:=1 to n do b:=b+' ';
spc:=b
end;
PROCEDURE PIDEXY(var valor:string; long:byte; x,y:byte; nuevo:boolean);
{ Valor: la variable en la que se va a devolver }
{ Long: longitud máxima permitida }
{ x,y: posición en pantalla }
{ Nuevo: ¿borrar el contenido anterior de "valor"? }
var
i,fila,posix,posiy: byte; {bucles, fila, posición, inicial, final}
inicial,final:byte; { inicial, final }
tecla:char; { tecla que se pulsa }
insertar:boolean; { ¿modo de inserción? }
begin
if nuevo=true then valor:='';
gotoxy(x,y);
fila:=wherey;
write('['); ;
for i:=1 to long do write('·'); { Para que se vea mejor }
write(']'); ;
for i:=length(valor) to long-1 do valor:=valor+'·';
insertar:=false;
posix:=1;
posiy:=y;
repeat
gotoxy(x+1,y);
write(valor);
gotoxy(posix+x,y);
tecla:=readkey; { Lee una pulsación }
if ord(tecla)=0 then { Si es tecla de función }
case ord(readkey) of
{ Flecha izquierda }
75: if posix>1 then posix:=posix-1;
{ Flecha derecha }
77: if posix' ')
then posix:=i+1;
if posix=inicial then posix:=1
end;
{ Ctrl + -> }
116: for i:=long-1 downto posix do
if (valor[i]=' ') and (valor[i+1]<>' ') then posix:=i+1;
end
else { Si es carácter imprimible }
if tecla>=' ' then
if not insertar then { Si no hay que insertar }
begin
valor[posix]:=tecla; { Reemplaza }
if posix1) then
begin
for i:=posix-1 to long-1 do valor[i]:=valor[i+1];
valor[long]:='·';
posix:=posix-1
end;
until ord(tecla)=13; { Hasta pulsar INTRO }
for i:=1 to length(valor) do { Al terminar, pone espacios }
if valor[i]='·' then valor[i]:=' ';
gotoxy(x,y); write(' ' + valor + ' '); { Y muestra el resultado }
end;
{ --- Mini-Programa de prueba --- }
var
nombre: string;
begin
clrscr;
gotoxy(3,3); write( '¿Cómo te llamas?' );
pidexy( nombre, 20, 3,4, TRUE );
gotoxy(3, 10); writeln( 'Tu nombre es: ', nombre );
readln;
end.