Fundamentos:Solucións:Bomber
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.
<c> 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 </c>
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
<c>
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
</c>
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();
}