Fundamentos:Solucións:4 en Raia
Escribir un programa que permita a dous xogadores xogar o 4 en Raia en un tableiro de 8x8.
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();
}