BIBLIOTECA CURSES

La biblioteca curses se creó para organizar la información que se muestra en el terminal. Nos provee de una serie de funciones que, entre otras cosas, nos permite, colocar caracteres en pantalla mediante coordenadas dadas en filas y columnas o cambiar el color de los caracteres.

Para poder utilizar la biblioteca curses primero debemos incluir su archivo de cabecera en nuestro programa
#include<ncurses.h>

Antes de utilizar las funciones de la librería debemos inicializarla, para ello llamamos a la función:

WINDOW *miVentana = initscr();

A partir de aquí ya podemos utilizar las funciones de la librería según nuestras necesidades y una vez terminemos, la última función llamada debe de ser:

endwin();


ejemplo 1.-


#include<ncurses.h>

int main(void){
initscr();
printw("Hola mundo Curses");
refresh();
getch();
endwin();
return 0;
}


Si guardamos este código en un archivo llamado curses1.c y compilamos con gcc de esta manera:

gcc curses1.c -o curses1Exe

tendríamos la siguiente salida:


Nos da un error al linkar, no reconoce ninguna de las funciones de la biblioteca curses. Esto es por que al compilar debemos decirle a gcc que la incluya de esta manera:

gcc -lncurses curses1.c -o curses1Exe

Ahora si, nuestro programa compila y la salida que deberíamos ver es la frase

Hola mundo Curses

y quedarse esperando a que se pulse una tecla.

Ahora vamos a escribirlo en otra parte de la pantalla. Por defecto comienza a escribir en la esquina superior izquierda. Para colocar el texto en cualquier parte de la pantalla debemos darle coordenadas de escritura mediante la función move(fil, col)


#include<ncurses.h>

int main(void){
initscr();
   
     move(5,5);
printw("Hola mundo Curses");
refresh();
getch();
endwin();
return 0;
}



La entrada de datos por parte del usuario se hace con scanw(), el uso de esta función es exactamente igual que scanf(). Podemos preguntar el nombre al usuario y guardarlo en la memoria:


#include<ncurses.h>

int main(void){

char name[20];
initscr();

     printw("¿Cómo te llamas?");
     move(0,19);
     scanw("%s",&name);
     move(1,0);
printw("Hola %s", name);
refresh();    //Refresca la pantalla, vuelca todos los printw en pantalla
getch();      //Espera a que se pulse una tecla
endwin();
return 0;
}



¿Y los colores?
Primero debemos preguntar si soporta colores y en caso afirmativo, iniciar el sistema de colores.

if(has_colors()){
        start_color();

        init_pair(1,COLOR_RED,COLOR_YELLOW);
        attron(COLOR_PAIR(1));
        printw("Este texto tiene color");
        attroff(COLOR_PAIR(1));
}

has_colors() comprueba si el terminal soporta colores y devuelve cierto o falso
start_color() inicia el modo colores, si no invocamos a esta función, no veremos los colores
init_pair()    asocia pares de colores, en nuestro ejemplo el rojo para el texto y el amarillo para
                    el fondo e identifica el par mediante un número, en este caso el 1.
attron()        activa los colores.
attroff()       desactiva los colores.

Esto nos da mucho juego.


Podríamos necesitar un tamaño de ventana predeterminado para poder ejecutar nuestro programa, Ncurses nos dice el tamaño del terminal con la función getmaxyx()

int dim_x, dim_y;
getmaxyx(stdscr, dim_y, dim_x);

de esta manera comprobaríamos el tamaño actual y el programa puede dar un mensaje al usuario para que modifique el tamaño de la ventana.

Para que escriba la frase Hola Mundo Curses en el centro de la ventana del terminal haríamos algo así:


#include<ncurses.h>

int main(void){

char saludo[]="Hola Mundo Curses";
int dim_x, dim_y;  

     initscr();
     getmaxyx(stdscr, dim_y, dim_x); //Se obtiene el tamaño actual del terminal
     move(dim_y/2, (dim_x/2)-(sizeof(saludo)/2));
     printw("%s", saludo);
    
refresh();    //Refresca la pantalla, vuelca todos los printw en pantalla
getch();      //Espera a que se pulse una tecla
endwin();
return 0;
}


Aquí os  dejo el código del juego Tic Tac Toe o 3 en ralla, es para dos jugadores para compilarlo con gcc

gcc -lncurses tictact2.c -o tictactExe


#include<stdio.h>
#include<ncurses.h>

#define coordScrX 5
#define coordScrY 3
#define despIniY  2
#define despIniX  2

void dibujarTablero(void);
void dibujarCero(int, int);
void dibujarEquis(int, int);
void dibujarFichas(void);
void borrarFicha(int, int);


char cero[3][3]  ={'*','*','*','*','/','*','*','*','*'};
char equis[3][3] ={'\\',' ','/',' ','X',' ','/',' ','\\'};

void dibujarPantalla(void);
void borrarTablero(void);
int hayGanador(void);

char tablero[3][3];
char turnoActual;

//Función principal main()
int main(int argc, char * argv[])
{
initscr();   /*Inicia curses*/

int fila, columna, movimientos, jugar = 1;
int terminar;
char otraPartida = 's';

while(jugar){       //Bucle principal del juego
terminar = 0;
movimientos = 0;
turnoActual = 'X';
borrarTablero();
dibujarTablero();
mvprintw(19,6,"           ");
mvprintw(24,3,"                                    ");


while(terminar == 0){     //se podría haber puesto   !terminar
if(movimientos >=9) break; //Si se realizan más de 9 movimientos, hay empate
move(21,0);printw("Fila:       ");
scanw("%d",&fila);
move(22,0);printw("Columna:    ");
scanw("%d",&columna);

if(fila > 2) continue;
if(fila == -1)break;  //para finalizar sin terminar la partida

if(tablero[fila][columna] == '_'){  //Casilla vacía??
tablero[fila][columna] = turnoActual;     //Se escribe
terminar = hayGanador();                       //Se comprueba si hay ganador

if(terminar == 0){                                //Si no lo hay
if(turnoActual == 'X') {
turnoActual = 'O';                          //Se cambia el turno
}
else {
turnoActual = 'X';
}
movimientos++;
}
}  

dibujarTablero();
}


move(19,6);
if(movimientos >= 9)
printw("E M P A T E     ");
else
printw("G A N A D O R   %c    ",turnoActual);

mvprintw(24,4,"¿Quieres jugar de nuevo (s/n)?");
scanw("%c",&otraPartida);
if(otraPartida == 'n') jugar = 0;

  }

endwin();
printf("\n\nGracias por jugar\n\n");
return 0;
}

void borrarTablero(void)
{
int i, j;
for(i=0 ; i<3; i++){
for(j=0; j<3; j++){
tablero[i][j] ='_';
borrarFicha(i,j);
}
}
}

int hayGanador(void)
{
int i;
int ganador = 0;

//Horizontal Vertical y Diagonales
for(i=0; i<3; i++){
if(
         ((tablero[i][0] == turnoActual) && (tablero[i][1] == turnoActual) && (tablero[i][2] == turnoActual)) ||
         ((tablero[0][i] == turnoActual) && (tablero[1][i] == turnoActual) && (tablero[2][i] == turnoActual)) ||
         ((tablero[0][0] == turnoActual) && (tablero[1][1] == turnoActual) && (tablero[2][2] == turnoActual)) ||
         ((tablero[2][0] == turnoActual) && (tablero[1][1] == turnoActual) && (tablero[0][2] == turnoActual)))
ganador = 1;

else ganador = 0;

}
return ganador;
}

void dibujarCero(int fila, int column)
{
//Dibuja 0
int i, j, x, y;
for(i=0;i<3;i++){

for(j=0;j<3;j++){
y = coordScrY + despIniY + (fila * 3 + fila);
x = coordScrX + despIniX + (column * 6 + column);
mvprintw(y+i,x+j,"%c",cero[i][j]);
}
printw("\n");
}
}

void dibujarEquis(int fila, int column)
{
//Dibuja X
int i, j, x, y;
for(i=0;i<3;i++){

for(j=0;j<3;j++){
y = coordScrY + despIniY + (fila * 3 + fila);
x = coordScrX + despIniX + (column * 6 + column);
mvprintw(y+i,x+j,"%c",equis[i][j]);
}
printw("\n");
}

}
void dibujarTablero(void)
{
//Tablero
int i,j;
mvprintw(1,7,"TRES EN RALLA");
mvprintw(coordScrY ,5,"   0     1      2");

mvprintw(coordScrY+3,4,"0");
mvprintw(coordScrY+7,4,"1");
mvprintw(coordScrY+11,4,"2");
//Pone las fichas después de las coordenadas
dibujarFichas();

for(i=0;i<25;i++){         //Líneas del borde horizontal
mvprintw(coordScrY - 1, coordScrX - 3 + i,"#");
mvprintw(coordScrY + 14,coordScrX - 3 + i,"#");
if(i<15){
for(j=0;j<25;j++){ //Líneas del borde vertical
if((j==0) || (j==24)){
mvprintw(coordScrY+i,coordScrX+j-3,"#");
}
}
}
}
for(i=1;i<19;i++){
mvprintw(coordScrY + 5, coordScrX + i,"-");  //Divisiones horizontales
mvprintw(coordScrY + 9, coordScrX + i,"-");
if(i<14){
mvprintw(coordScrY + i, coordScrX + 6,"|"); //Divisiones verticales
mvprintw(coordScrY + i, coordScrX + 13,"|");
}
}
mvprintw(coordScrY + 5, coordScrX + 6,"+");
mvprintw(coordScrY + 5, coordScrX + 13,"+");
mvprintw(coordScrY + 9, coordScrX + 6,"+");
mvprintw(coordScrY + 9, coordScrX + 13,"+");
mvprintw(18,12,"Turno %c",turnoActual);

}
void dibujarFichas(void)
{
int i, j;
//recorremos el tablero comprobando si hay fichas puestas
for(i=0;i<3;i++){
for(j=0;j<3;j++){
if(tablero[i][j] == 'O') dibujarCero(i, j);
if(tablero[i][j] == 'X') dibujarEquis(i, j);
}
}
refresh();
}

void borrarFicha(int fila, int column)
{
int i, j, x, y;
for(i=0;i<3;i++){
for(j=0;j<3;j++){
y = coordScrY + despIniY + (fila * 3 + fila);
x = coordScrX + despIniX + (column * 3 + column);
mvprintw(y+i,x+j," ");
}
printw("\n");
}
refresh();
}


Así se ve en el terminal.


La mayor parte del programa es para el tratamiento de la interfaz (tablero, fichas). La entrada de datos y la lógica del juego ocupan relativamente poco.

A modo de ejercicio, se podría dar color a la fichas, por ejemplo, o implementar el modo contra el ordenador. 


Esta es una imagen de otro ejemplo implementado en curse, se trata de una versión libre del Arkanoid


La pelotita se lanza pulsando la barra espaciadora, cada vez que choca con los ladrillos del centro estos se van quitando, la nave se mueve con la flechas del teclado. Al igual que en el TicTacToe, internamente el programa controla un array bidimensional, del que lee buscando ladrillos, paredes, la nave, etc...