Fundamentos:Solucións:Bomber

De Wiki do Ciclo ASIR do IES de Rodeira
Revisión feita o 26 de xuño de 2014 ás 11:13 por Xavi (conversa | contribucións) (→‎Pseudocódigo)
(dif) ← Revisión máis antiga | Revisión actual (dif) | Revisión máis nova → (dif)
Saltar á navegación Saltar á procura

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.


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();
}