Fundamentos:Solucións:4 en Raia

De Wiki do Ciclo ASIR do IES de Rodeira

Escribir un programa que permita a dous xogadores xogar o 4 en Raia en un tableiro de 8x8.


Índice

Solucións de Xavi

Makefile

Ficheiro de construcción do programa.

  • Para construir a versión GUI: $make ou $make catroraiagtk
  • Para construir a versión de Consola: $make catroraia

Podes Descargar o Makefile <c> FLAGS = `pkg-config --cflags --libs libglade-2.0` FLAGSC = `pkg-config --cflags libglade-2.0`

catroraiagtk: catroraiagtk.c gcc catroraiagtk.c -o catroraiagtk $(FLAGS) -export-dynamic catroraia: catroraia.c

       gcc catroraia.c -o catroraia

</c>

Versión de Consola

Pseudocódigo

<c> Limpar Taboleiro (posición inicial) Visualizar o Taboleiro xog=Xogador 2 Mentras non remate o xogo

  Si xog==Xogador 1 xog=Xogador 2
  senon   	     xog=Xogador 1
  Pon ficha o Xogador xog
  Visualizar o Taboleiro

Fin Mentras visualizar quen gañou

</c>

Implementación en C

catroraia.c

Podes Descargar o ficheiro

/* Reversi
*/
#include <stdio.h>
#include <stdlib.h>
 
// Direccions
#define ABAESQ 1
#define ABA 2
#define ABADER 3
#define DER 4
 
// Pezas
#define VACIO 	0
#define XOG1	1
#define XOG2	2
 
// taboleiro
char __tableiro[8][8];
 
 
void iniciaTaboleiro(void);
void visualizaTaboleiro(void);
int comproba(int f,int c,int xog,int dir);
int catroRaia(int xog);
int remataXogo(int xog);
int posicionXogada(int c);
 
 
/*iniciaTaboleiro
  DESCRIPCION: Posición inicial do xogo
*/
void iniciaTaboleiro(void)
{
  int i,j;
 
  /*
  i=0;
  while(i<8)
  {
    j=0;
    while(j<8)
    {
      __tableiro[i][j]=VACIO;
      j++;
    }
    i++;
  }
  */
  for(i=0;i<8;i++)
  {
    for(j=0;j<8;j++)
    {
      __tableiro[i][j]=VACIO;
    }
  }
}
 
 
/* visualizaTaboleiro
   DESCRIPCION: Pinta o taboleiro na pantalla
*/
void visualizaTaboleiro(void)
{
   int i,j;
   char *ficha="·X0";
 
   system("clear");
   i=0;
   while(i<8)
   {
     j=0;
     while(j<8)
     {
       printf("%c ",ficha[ __tableiro[i][j]  ]);
       j++;
     }
     printf("\n");
     i++;
   }
}
 
 
/* comproba
   RECIBE:  A posición a comprobar (f,c)
            O xogador (xog) a comprobar e a dirección (dir) a comprobar
   DEVOLVE: 1 si o xogador ten 4 en raia na dirección indicada
*/
int comproba(int f,int c, int xog,int dir)
{
   int ret=0;
   int conta;
 
   conta=0;
   switch(dir)
   {
      case DER: while((c<8)&&(conta<4)&&(__tableiro[f][c]==xog)) 
                {
                  conta++; 
                  c++;
                }
                break;
 
      case ABA: while((f<8)&&(conta<4)&&(__tableiro[f][c]==xog))
                {
                  conta++;
                  f++;
                }
                break;
 
      case ABAESQ: while((f<8)&&(c>=0)&&(conta<4)&&(__tableiro[f][c]==xog))
                   {
                      conta++;
                      f++;
                      c--;
                   }
                   break;
      case ABADER: while((f<8)&&(c<8)&&(conta<4)&&(__tableiro[f][c]==xog))
                   {
                     conta++;
                     f++;
                     c++;
                   }
                   break;
   }
   if (conta==4) ret=1;
   return ret;
}
 
/* catroRaia
   RECIBE: O último xogador en facer a xogada
   DEVOLVE: 1 si o xogador ten 4 en raia, senon 0
 
   DESCRIPCION: Comproba si xog fixo 4 en raia.
*/
int catroRaia(int xog)
{
  int ret=0;
  int i,j;
 
  i=0;
  while((i<8)&&(ret==0))
  {
    j=0;
    while((j<8)&&(ret==0))
    {
      if ((__tableiro[i][j]==xog)&&(comproba(i,j,xog,DER)||comproba(i,j,xog,ABA)||
          comproba(i,j,xog,ABAESQ)||comproba(i,j,xog,ABADER))) ret=1;
      else j++;
    }
    i++; 
  } 
  return ret;
}
 
 
/* remataXogo
   RECIBE: O xogador a comprobar (xog)
   DEVOLVE: 2 si xog ten 4 en raia, 1 si o tableiro está cheo
            0 en outro caso.
 
   DESCRIPCIÓN: Comproba si o xogo rematou. Si non remata devolve 0
*/
int remataXogo(int xog)
{
  int ret=0;
  int x;
 
  if (catroRaia(xog)) ret=2;
  else
  {
    for(x=0;(x<8)&&(__tableiro[0][x]!=VACIO);x++);
    if (x==8) ret=1;
  }
  return ret;
}
 
 
/* posicionXogada
   RECIBE: columna(c)
   DEVOLVE: -1 si o xogador non pode xogar na columna indicadas
            fila da xogada si o xogador pode xogar na columna indicadas
   DESCRIPCION: Comproba si un xogador pode poñer unha ficha nunha posición
*/
int posicionXogada(int c)
{
  int f=0;
 
  while ((__tableiro[f][c]==VACIO)&&(f<8)) f++;
  return f-1;
}
 
 
 
/* xoga
   RECIBE: O xogador (x) que lle toca xogar
   DESCRIPCION: Pide a xogada, verificando que é correcta e
                efectúa a xogada completa (dando a volta ás fichas
                que o precisen)
 
   Si podeXogar(x)
     Facer 
       Pedir fila e columna
     Mentras non poda poñer a ficha en fila,columna
     Poñer a ficha
     Dar a volta ás fichas que o necesiten
   FinSi
*/
void xoga(int x)
{
   int fila,columna;
 
   do {
        fila=-1;
        printf("columna?:"); scanf("%d",&columna);
        if ((columna>=0)&&(columna<8)) fila=posicionXogada(columna);
   }
   while(fila==-1);
   __tableiro[fila][columna]=x;
}
 
 
/* Principal
 
   Limpar Taboleiro (posición inicial)
   Visualizar o Taboleiro
   xog=Xogador 2
   Mentras non remate o xogo
      Si xog==Xogador 1 xog=Xogador 2
      senon  		xog=Xogador 1
      Pon ficha o Xogador xog
      Visualizar o Taboleiro
   Fin Mentras
   visualizar quen gañou
 
*/
void main(void)
{
  int xog;
  int r;
 
  iniciaTaboleiro();
  visualizaTaboleiro();
  xog=XOG2;
  while (!(r=remataXogo(xog)))
  {
     if (xog==XOG1) 	xog=XOG2;
     else		xog=XOG1;
     xoga(xog);
     visualizaTaboleiro();
  }
  if (r==1) printf("Empate!!!\n");
  else      printf("Gañador xogador %d\n",xog);
}

Versión GTK+

E preciso ter a versión 2.8 das Gtk para que funcione o diálogo de acerca de... Si non se ten o GTK+ 2.8 é necesario comentar as liñas <c>

d=(GtkFileChooser *)glade_xml_get_widget(__gui,"gardar");
gtk_file_chooser_set_do_overwrite_confirmation(d,TRUE);

</c>

Na función main.

Interface

Descargar GUI (ficheiro xml catroraia.glade)
Descargar Debuxos (Descomprimir na carpeta do programa)

Pseudocódigo

<c> Cando fagan click nunha celda

 Si a xogada e correcta
   O xogador efectúa xogada
   Si se enche o taboleiro ou se fixo 4 en raia 
       visualizar resultado
   Se non
       cambiar de xogador
   FinSi
 FinSi

</c>

Implementación en C

catroraiagtk.c

Podes Descargar o ficheiro

/* 4 raia
*/
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
 
#ifdef __WINDOWS__
   #define EXPORT __declspec (dllexport)
#else
   #define EXPORT
#endif
 
// Direccions
#define ABAESQ 1
#define ABA 2
#define ABADER 3
#define DER 4
 
// Pezas
#define VACIO 	0
#define XOG1	1
#define XOG2	2
 
// GUI
GladeXML *__gui=NULL;
 
// taboleiro
char __tableiro[8][8];
 
// xogador
int __xog=XOG1;
 
// Nomes dos xogadores
char *nomeXog[]={"Empate","Marron","Azul"};
 
void iniciaTaboleiro(void);
void obtenCoordenadas(const char *nome,int *f,int *c);
void fichaGui(int fila,int columna,int xogador);
void dialogoFin(int r);
int posicionXogada(int c);
int comproba(int f,int c, int xog,int dir);
int catroRaia(int xog);
int remataXogo(int xog);
 
 
EXPORT gboolean   on_celda_button_press_event (GtkWidget      *widget,
                                        GdkEventButton *event,
                                        gpointer        user_data);
EXPORT void on_novo_clicked(GtkWidget *w,gpointer p);
EXPORT void on_acerca_de_activate(GtkWidget *w,gpointer p);
EXPORT void on_gardar_activate(GtkWidget *w,gpointer p);
EXPORT void on_abrir_activate(GtkWidget *w,gpointer p);
EXPORT void on_xogadores_aceptar_clicked(GtkWidget *w,gpointer p);
 
/*iniciaTaboleiro
  DESCRIPCION: Posición inicial do xogo
*/
void iniciaTaboleiro(void)
{
  int i,j,c;
  GtkImage *img;
  GtkWidget *sb;
  char nome[8];
 
  __xog=XOG1;
  c=1;
  for(i=0;i<8;i++)
  {
    for(j=0;j<8;j++)
    {
      __tableiro[i][j]=VACIO;
      sprintf(nome,"image%d",c);
      img=(GtkImage *) glade_xml_get_widget(__gui,nome);
      gtk_image_set_from_file(img,"xog0.png");
      c++;
    }
  }
  sb=glade_xml_get_widget(__gui,"nombres");
  gtk_widget_show(sb);
}
 
/*obtenCoordenadas
  RECIBE: O nome da casilla
          As direccións de memoria onde deixar a fila (f) e a columna (c)
  DESCRIPCION: Obten as coordenadas da casilla a partir do nome
*/
void obtenCoordenadas(const char *nome,int *f,int *c)
{
   int num;
 
   // Obter o número da casilla do nome
   num=atoi(&nome[8])-1;
   // Calcular fila e columna
   *f=num/8;
   *c=num%8;
}
 
/*fichaGui
  RECIBE: fila, columna, xogador
  DESCRIPCION: Pon no gui a ficha do xogador na fila e columna
               indicadas.
*/
void fichaGui(int fila,int columna,int xogador)
{
  GtkImage *img;
  char buffer[12];
 
  // Obtemos a imaxe da ficha
  sprintf(buffer,"image%d",fila*8+columna+1);
  img=(GtkImage *) glade_xml_get_widget(__gui,buffer);
 
  // Poñemos ficha na posición
  sprintf(buffer,"xog%d.png",xogador);
  gtk_image_set_from_file(img,buffer);
 
}
 
/* posicionXogada
   RECIBE: columna(c)
   DEVOLVE: -1 si o xogador non pode xogar na columna indicadas
            fila da xogada si o xogador pode xogar na columna indicadas
   DESCRIPCION: Comproba si un xogador pode poñer unha ficha nunha posición
*/
int posicionXogada(int c)
{
  int f=0;
 
  while ((__tableiro[f][c]==VACIO)&&(f<8)) f++;
  return f-1;
}
 
/* comproba
   RECIBE:  A posición a comprobar (f,c)
            O xogador (xog) a comprobar e a dirección (dir) a comprobar
   DEVOLVE: 1 si o xogador ten 4 en raia na dirección indicada
*/
int comproba(int f,int c, int xog,int dir)
{
   int ret=0;
   int conta;
 
   conta=0;
   switch(dir)
   {
      case DER: while((c<8)&&(conta<4)&&(__tableiro[f][c]==xog)) 
                {
                  conta++; 
                  c++;
                }
                break;
 
      case ABA: while((f<8)&&(conta<4)&&(__tableiro[f][c]==xog))
                {
                  conta++;
                  f++;
                }
                break;
 
      case ABAESQ: while((f<8)&&(c>=0)&&(conta<4)&&(__tableiro[f][c]==xog))
                   {
                      conta++;
                      f++;
                      c--;
                   }
                   break;
      case ABADER: while((f<8)&&(c<8)&&(conta<4)&&(__tableiro[f][c]==xog))
                   {
                     conta++;
                     f++;
                     c++;
                   }
                   break;
   }
   if (conta==4) ret=1;
   return ret;
}
 
/* catroRaia
   RECIBE: O último xogador en facer a xogada
   DEVOLVE: 1 si o xogador ten 4 en raia, senon 0
 
   DESCRIPCION: Comproba si xog fixo 4 en raia.
*/
int catroRaia(int xog)
{
  int ret=0;
  int i,j;
 
  i=0;
  while((i<8)&&(ret==0))
  {
    j=0;
    while((j<8)&&(ret==0))
    {
      if ((__tableiro[i][j]==xog)&&(comproba(i,j,xog,DER)||comproba(i,j,xog,ABA)||
          comproba(i,j,xog,ABAESQ)||comproba(i,j,xog,ABADER))) ret=1;
      else j++;
    }
    i++; 
  } 
  return ret;
}
 
/* remataXogo
   RECIBE: O xogador a comprobar (xog)
   DEVOLVE: 2 si xog ten 4 en raia, 1 si o tableiro está cheo
            0 en outro caso.
 
   DESCRIPCIÓN: Comproba si o xogo rematou. Si non remata devolve 0
*/
int remataXogo(int xog)
{
  int ret=0;
  int x;
 
  if (catroRaia(xog)) ret=2;
  else
  {
    for(x=0;(x<8)&&(__tableiro[0][x]!=VACIO);x++);
    if (x==8) ret=1;
  }
  return ret;
}
 
/* dialogoFin
*/
void dialogoFin(int r)
{
   GtkWidget *fin;
   char buffer[1024];
   int gan,f1,f2;
 
   if (r==1) strcpy(buffer,"<big><b>Empate!!!</b></big>");
   else sprintf(buffer,"<big><b>Felicitacións %s</b></big>",nomeXog[__xog]);
   fin=glade_xml_get_widget(__gui,"msg");
   gtk_label_set_markup(GTK_LABEL(fin),buffer);
   fin=glade_xml_get_widget(__gui,"fin");
   gtk_widget_show(fin);
}
 
// Nomes dos xogadores
//
EXPORT void on_xogadores_aceptar_clicked(GtkWidget *w,gpointer p)
{
  GtkWidget *sb;
  char buffer[128];
  int id;
 
  sb=glade_xml_get_widget(__gui,"nombres");
  gtk_widget_hide(sb);
  sb=glade_xml_get_widget(__gui,"xogadores_marron");
  nomeXog[1]=(char *)gtk_entry_get_text(GTK_ENTRY(sb));
  sb=glade_xml_get_widget(__gui,"xogadores_azul");
  nomeXog[2]=(char *)gtk_entry_get_text(GTK_ENTRY(sb));
  sb=glade_xml_get_widget(__gui,"statusbar");
  id=gtk_statusbar_get_context_id(GTK_STATUSBAR(sb),"turno");
  sprintf(buffer,"Turno: %s",nomeXog[1]);
  gtk_statusbar_push(GTK_STATUSBAR(sb),id,buffer);
}
 
// Novo Xogo
//
EXPORT void on_novo_clicked(GtkWidget *w,gpointer p)
{
  GtkWidget *fin;
 
  fin=glade_xml_get_widget(__gui,"fin");
  gtk_widget_hide(fin);
  iniciaTaboleiro();
}
 
// Acerca de ...
EXPORT void on_acerca_de_activate(GtkWidget *w,gpointer p)
{
   GtkWidget *ad;
 
   ad=glade_xml_get_widget(__gui,"acercade");
   gtk_widget_show(ad);
}
 
// Gardar Xogo
//
EXPORT void on_gardar_activate(GtkWidget *w,gpointer p)
{
   GtkWidget *ad;
   gchar *fn;
   FILE *f;
 
   ad=glade_xml_get_widget(__gui,"gardar");
   if (gtk_dialog_run (GTK_DIALOG(ad)) == GTK_RESPONSE_OK) 
   {
      fn=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ad));
      f=fopen(fn,"w");
      fwrite(&__xog,sizeof(__xog),1,f);
      fwrite(__tableiro,sizeof(__tableiro),1,f);
      fclose(f);
      g_free(fn);
   }
   gtk_widget_hide(ad);
}
 
// Cargar Xogo
//
EXPORT void on_abrir_activate(GtkWidget *w,gpointer p)
{
   GtkWidget *ad,*sb;
   gchar *fn;
   FILE *f;
   char msg[128];
   int i,j;
 
   ad=glade_xml_get_widget(__gui,"abrir");
   if (gtk_dialog_run (GTK_DIALOG(ad)) == GTK_RESPONSE_OK) 
   {
      // Recuperar o ficheiro
      fn=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ad));
      f=fopen(fn,"r");
      fread(&__xog,sizeof(__xog),1,f);
      fread(__tableiro,sizeof(__tableiro),1,f);
      fclose(f);
      // Poñer as fichas no gui
      for(i=0;i<8;i++)
        for(j=0;j<8;j++) fichaGui(i,j,__tableiro[i][j]);
      // Indicar a que xogador lle toca
      sb=glade_xml_get_widget(__gui,"statusbar");
      i=gtk_statusbar_get_context_id(GTK_STATUSBAR(sb),"turno");
      gtk_statusbar_pop(GTK_STATUSBAR(sb),i);
      sprintf(msg,"Turno do Xogador %s",nomeXog[__xog]);
      gtk_statusbar_push(GTK_STATUSBAR(sb),i,msg);
      g_free(fn);
   }
   gtk_widget_hide(ad);
}
 
// Pulsada unha celda: XOGADA
//
EXPORT gboolean   on_celda_button_press_event (GtkWidget      *widget,
                                        GdkEventButton *event,
                                        gpointer        user_data)
{
   const char *nome;
   int fila,columna;
 
   char msg[128];
   GtkWidget *sb;
   int id;
   int r;
 
   // Obtemos as coordenadas do nome
   nome=gtk_widget_get_name(widget);
   obtenCoordenadas(nome,&fila,&columna);
   fila=posicionXogada(columna);
   if (fila!=-1)
   {
      __tableiro[fila][columna]=__xog;
      fichaGui(fila,columna,__xog);
      r=remataXogo(__xog);
      if (r) dialogoFin(r);
      else 
      {
        if (__xog==XOG1) 	__xog=XOG2;
        else  			__xog=XOG1;
        // Mensaxe do xogador ó que lle toca
        sb=glade_xml_get_widget(__gui,"statusbar");
        id=gtk_statusbar_get_context_id(GTK_STATUSBAR(sb),"turno");
        gtk_statusbar_pop(GTK_STATUSBAR(sb),id);
        sprintf(msg,"Turno: %s",nomeXog[__xog]);
        gtk_statusbar_push(GTK_STATUSBAR(sb),id,msg);
      }
   }
}
 
//---------------------------------------------------------------------
// Función Principal
//---------------------------------------------------------------------
int main(int argc,char *argv[])
{
   GtkFileChooser *d;
 
   gtk_init(&argc,&argv);
   __gui=glade_xml_new("catroraia.glade",NULL,NULL);
   glade_xml_signal_autoconnect(__gui);
   d=(GtkFileChooser *)glade_xml_get_widget(__gui,"gardar");
   gtk_file_chooser_set_do_overwrite_confirmation(d,TRUE);
   iniciaTaboleiro();
   gtk_main();
}