Fundamentos:Solucións:Bomber

De Wiki do Ciclo ASIR do IES de Rodeira

Escribir un Xogo no que participan dous xogadores nun taboleiro de 8x8. En cada turno un xogador pode moverse ou poñer unha bomba, perdendo o xogador que non poda efectuar o seu movemento. Un xogador non poderá mover si a casilla á que se ten que desplazar está ocupada por unha bomba ou por outro xogador. As bombas impiden o paso tanto o xogador contrario como ó que as puxo, e teñen unha duración de 5 turnos.


Índice

Solucións de Xavi

Makefile

Ficheiro de construcción do programa.

FLAGS = `pkg-config --cflags --libs libglade-2.0`
FLAGSC = `pkg-config --cflags libglade-2.0`
 
bomber: bomber.c
	gcc bomber.c -o bomber $(FLAGS) -export-dynamic

Aplicació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 tablero.glade)
Descargar Debuxos (Descomprimir na carpeta do programa)

Pseudocódigo

 Cando se pulse unha tecla de movemento ou click no rato
    Segun a Xogada
         Si e moverse: comprobar que se pode e moverse
         Si e poñer bomba: comprobar que se pode e poñela
    Fin Segun
    Si o xogador efectuou a súa xogada
      Cambio de xogador
      Decrementar as bombas, si procede
      Comprobar que o outro xogador non perdeu
    Fin-Si

Implementación en C

bomber.c

/* Bomber
*/
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glade/glade.h>
 
#ifdef __WINDOWS__
   #define EXPORT __declspec (dllexport)
#else
   #define EXPORT
#endif
 
// Pezas
#define VACIO 	0
#define XOG1	1
#define XOG2	2
#define BOMBA   100
#define BOOM	200
 
// GUI
GladeXML *__gui=NULL;
 
// taboleiro
char __tableiro[8][8];
 
// Xogador ó que lle toca xogar
int __xog=XOG1;
 
// Vida das bombas
int __vida=0;
 
// Nomes dos xogadores
char *nomeXog[]={"","Marron","Azul"};
 
void fichaGui(int fila,int columna,int xogador);
void iniciaTaboleiro(void);
int move_arriba(void);
void obtenCoordenadas(const char *nome,int *f,int *c);
void buscaFicha(int xog,int *f,int *c);
int tenMovemento(int fila,int columna);
void cambioXogador(void);
int move(int df,int dc);
 
EXPORT void on_xogadores_aceptar_clicked(GtkWidget *w,gpointer p);
EXPORT gboolean on_Xogos_key_press_event(GtkWidget *widget,GdkEventKey *event,gpointer user_data);
EXPORT gboolean   on_celda_button_press_event (GtkWidget      *widget,
                                        GdkEventButton *event,
                                        gpointer        user_data);
 
 
/*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);
 
}
 
/*iniciaTaboleiro
  DESCRIPCION: Posición inicial do xogo
*/
void iniciaTaboleiro(void)
{
  int i,j;
  GtkImage *img;
  GtkWidget *sb;
  char nome[8];
  int c;
  int id;
 
  i=0;
  c=1;
  while(i<8)
  {
    j=0;
    while(j<8)
    {
      __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");
      j++;
      c++;
    }
    i++;
  }
  __tableiro[0][0]=XOG1;
  // Poñer ficha no GUI
  fichaGui(0,0,XOG1);
 
  __tableiro[7][7]=XOG2;
  // Poñer ficha no GUI
  fichaGui(7,7,XOG2);
 
  __xog=XOG1;
 
  sb=glade_xml_get_widget(__gui,"nombres");
  gtk_widget_show(sb);
}
 
/* buscaFicha
   RECIBE: Xogador a buscar (__xog) dirección para a fila (f) e para a columna (c)
   DESCRIPCION: Busca a ficha indicada no taboleiro e a almacena en f e c
*/
void buscaFicha(int xog,int *f,int *c)
{
  *f=0;
  *c=0;
  while(((*f)<8)&&(__tableiro[*f][*c]!=xog))
  {
     while(((*c)<8)&&(__tableiro[*f][*c]!=xog))
     {
        (*c)++;
     }
     if ((*c)==8)
     {
       *c=0;
       (*f)++;
     }
  }
}
 
void decrementaBombas(void)
{
   int f,c;
 
   f=0;
   while(f<8)
   {
     c=0;
     while(c<8)
     {
        if (__tableiro[f][c]>BOMBA)
        {
          __tableiro[f][c]--;
          if (__tableiro[f][c]==BOMBA) fichaGui(f,c,BOOM); 
        } 
        else if (__tableiro[f][c]==BOMBA)
             {
               __tableiro[f][c]=VACIO;
               fichaGui(f,c,VACIO); 
             }
        c++;
     }
     f++;
   }
}
 
/* podeMover
   RECIBE: xogador a comprobar
   DEVOLVE: 1 si o xogador pode mover, 0 si non.
*/
int podeMover(int xog)
{ 
  int f,c,ok=0;
 
  buscaFicha(__xog,&f,&c);
  if (tenMovemento(f-1,c)||tenMovemento(f+1,c)||
      tenMovemento(f,c+1)||tenMovemento(f,c-1)||
      tenMovemento(f-1,c-1)||tenMovemento(f-1,c+1)||
      tenMovemento(f+1,c-1)||tenMovemento(f+1,c+1)) ok=1;
  return ok;
}
 
/* tenMovemento
   RECIBE: fila e columna
   DEVOLVE: 1 si se pode mover a esa fila e columna, 0 noutro caso
*/
int tenMovemento(int fila,int columna)
{
   int ok=0;
 
   if ((fila>=0)&&(columna>=0)&&(fila<8)&&(columna<8)&&
      (__tableiro[fila][columna]==VACIO)) ok=1;
   return ok;
}
 
/* Move a ficha do xogador que lle toca 
*/
int move(int df,int dc)
{
  int ok=0;
  int f,c;
 
  /*
    1.- Buscar a ficha (f,c)
    2.- Si pode mover o sitio que lle toca
          Quitar a ficha de donde esta
          Poñela no seu sitio
          ok=1
        Fin-Si
  */
  buscaFicha(__xog,&f,&c);
  if (tenMovemento(f+df,c+dc))
  {
     __tableiro[f][c]=__tableiro[f][c]-__xog;
     fichaGui(f,c,__tableiro[f][c]);
     __tableiro[f+df][c+dc]=__xog;
     fichaGui(f+df,c+dc,__xog);
     ok=1; 
  }
  return ok;
}
 
/* Pon bomba o xogador que lle toca 
*/
int bomba(int df,int dc)
{
  int ok=0;
  int f,c;
 
  /*
    1.- Buscar a ficha (f,c)
    2.- Si pode mover o sitio que lle toca
          Quitar a ficha de donde esta
          Poñela no seu sitio
          ok=1
        Fin-Si
  */
  buscaFicha(__xog,&f,&c);
  if (tenMovemento(f+df,c+dc))
  {
     __tableiro[f+df][c+dc]=BOMBA+__vida;
     fichaGui(f+df,c+dc,BOMBA);
     ok=1; 
  }
  return ok;
}
 
/* Visualiza o turno na barra de estado
*/
void changeStatus(void)
{
  GtkWidget *sb;
  char buffer[128];
  int id;
 
  sb=glade_xml_get_widget(__gui,"statusbar");
  id=gtk_statusbar_get_context_id(GTK_STATUSBAR(sb),"turno");
  sprintf(buffer,"Turno: %s",nomeXog[__xog]);
  gtk_statusbar_push(GTK_STATUSBAR(sb),id,buffer);
 
}
 
/*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;
}
 
/* dialogoFin
*/
void dialogoFin(void)
{
   GtkWidget *fin;
   char buffer[1024];
   int gan,f1,f2;
 
   if (__xog==XOG2) gan=XOG1;
   else gan=XOG2;
   sprintf(buffer,"<big><b>Felicitacións %s</b></big>",nomeXog[gan]);
   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);
}
 
void cambioXogador(void)
{
  int f,c;
 
  if (__xog==XOG1) __xog=XOG2;
  else             __xog=XOG1;
  if (__vida>0) decrementaBombas();
  if (!podeMover(__xog)) 
  {
     buscaFicha(__xog,&f,&c);
     fichaGui(f,c,BOOM);
     dialogoFin();
  }
  changeStatus(); 
}
 
//---------------------------------------------------------------------
// Xestores de Sinais
//---------------------------------------------------------------------
EXPORT gboolean   on_celda_button_press_event (GtkWidget      *widget,
                                        GdkEventButton *event,
                                        gpointer        user_data)
{
   const char *nome;
   int f,c,fx,cx;
   int ok=0;
 
   if (event->type==GDK_BUTTON_PRESS)
   {
     // Obtemos as coordenadas do nome
     nome=gtk_widget_get_name(widget);
     obtenCoordenadas(nome,&f,&c);
 
     // Si a casilla está valeira
     if (__tableiro[f][c]==VACIO)
     {
       // Localizamos o xogador
       buscaFicha(__xog,&fx,&cx);
       switch(event->button)
       {
          case 1: // Si o xogador está nunha casilla contigua
                  // arriba, abaixo, na dereita ou na esquerda
                  if ((abs(fx-f)<=1)&&(abs(cx-c)<=1)&&((f==fx)||(c==cx))) 
                  {
                    __tableiro[fx][cx]=__tableiro[fx][cx]-__xog;
                     fichaGui(fx,cx,__tableiro[fx][cx]);
                    __tableiro[f][c]=__xog;
                    fichaGui(f,c,__xog);
                    ok=1; 
                  }
                  break;
          case 3: // Si o xogador está en calquera casilla do redor
                  if ((abs(fx-f)<=1)&&(abs(cx-c)<=1)) 
                  {
                    __tableiro[f][c]=BOMBA+__vida;
                    fichaGui(f,c,BOMBA);
                    ok=1; 
                  }
                  break; 
       }
       if (ok) cambioXogador();
     }
   }
   return FALSE;
}
 
 
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(&__vida,sizeof(__vida),1,f);
      fwrite(nomeXog[1],11,1,f);
      fwrite(nomeXog[2],11,1,f);
      fwrite(__tableiro,sizeof(__tableiro),1,f);
      fclose(f);
      g_free(fn);
   }
   gtk_widget_hide(ad);
}
 
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(&__vida,sizeof(__vida),1,f);
      sb=glade_xml_get_widget(__gui,"vida");
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(sb),__vida);
      // Nome do Xog 1
      fread(msg,11,1,f);
      // Poñelo no entry      
      sb=glade_xml_get_widget(__gui,"xogadores_marron");
      gtk_entry_set_text(GTK_ENTRY(sb),msg);
      nomeXog[1]=(char *)gtk_entry_get_text(GTK_ENTRY(sb));
      // Nome do Xog 2
      fread(msg,11,1,f);
      sb=glade_xml_get_widget(__gui,"xogadores_azul");
      gtk_entry_set_text(GTK_ENTRY(sb),msg);
      nomeXog[2]=(char *)gtk_entry_get_text(GTK_ENTRY(sb));
      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++) 
        {
          if (__vida>0)
          {
            if (__tableiro[i][j]>BOMBA) fichaGui(i,j,BOMBA);
            else if  (__tableiro[i][j]==BOMBA) fichaGui(i,j,BOOM);
                 else  fichaGui(i,j,__tableiro[i][j]);
          } 
          else 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);
}
 
// 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();
}
 
/* Nomes dos xogadores
*/
EXPORT void on_xogadores_aceptar_clicked(GtkWidget *w,gpointer p)
{
  GtkWidget *sb;
 
  sb=glade_xml_get_widget(__gui,"nombres");
  gtk_widget_hide(sb);
  sb=glade_xml_get_widget(__gui,"vida");
  __vida=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(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));
  changeStatus();
}
 
/* 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);
}
 
/* Pulsación de tecla
*/
EXPORT gboolean on_Xogos_key_press_event(GtkWidget *widget,GdkEventKey *event,gpointer user_data)
{
  int ok=0,f,c;
 
  switch(event->keyval)
  {
    case GDK_Up: if ((event->state&1)==0)
                 { 
                   ok=move(-1,0);
                 }
                 else ok=bomba(-1,0);
                 break; 
    case GDK_Down: if ((event->state&1)==0)
                   { 
                     ok=move(1,0);
                   }
                   else ok=bomba(1,0);
                   break; 
    case GDK_Left: if ((event->state&1)==0)
                   { 
                     ok=move(0,-1);
                   }
                   else ok=bomba(0,-1);
                   break;
    case GDK_Right: if ((event->state&1)==0)
                    { 
                      ok=move(0,1);
                    }
                    else ok=bomba(0,1);
                    break;
    case GDK_KP_Page_Down:
    case GDK_Page_Down: if ((event->state&1)==1)
                       {
                         ok=bomba(1,1);
                       }
                       break;
    case GDK_KP_Page_Up:
    case GDK_Page_Up:   if ((event->state&1)==1)
                       {
                         ok=bomba(-1,1);
                       }
                       break;
    case GDK_KP_Home:
    case GDK_Home:     if ((event->state&1)==1)
                       {
                         ok=bomba(-1,-1);
                       }
                       break;
    case GDK_KP_End:
    case GDK_End:      if ((event->state&1)==1)
                       {
                         ok=bomba(1,-1);
                       }
                       break;
  }
  if (ok)
  {
     cambioXogador();
  }
  return FALSE;
}
 
//---------------------------------------------------------------------
// Función Principal
//---------------------------------------------------------------------
int main(int argc,char *argv[])
{
   GtkFileChooser *d;
 
   gtk_init(&argc,&argv);
   __gui=glade_xml_new("tablero.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();
}