Игра Пятнашки

Состояние: Готово

Задача: создать игру Пятнашки;
Использованный API: GTK/GDK;
Среда разработки: Visual Studio 2008;

Перемещение элементов ("квадратиков") осуществляется при нажатии на них левой кнопкой мыши. Производится проверка на наличие 0 в окрестности элемента в матрице (сверху, снизу, слева и справа) и затем, если 0 найден, осуществляется перемещение элемента в данном направлении.

При нажатии на клавишу 'r' (маленькая, английская), производится случайная перестановка элементов и перерисовка состояния. После использования рекомендуется проверить состояние, чтобы убедиться, что данная перестановка элементов имеет решение.

При нажатии на клавишу 'c' (маленькая, английская), производится проверка состояния. Возможные состояния:
1. Done - готово. Сборка завершена.
2. Normal - данное состояние имеет решение.
3. Error!! - данное состояние не имеет решения.
4. Random - только что был произведен случайный обмен.

Проверка состояния осуществляется с помощью подсчета инверсий (элементы матрицы a, такие что ai > aj, но i < j). Затем, количество инверсий суммируется с номером строки в которой стоит 0 (пустой элемент), четность этой суммы и свидетельствует о решаемости данной перестановки:
1. Четная - существует решение.
2. Не четная - решения не существует.

/* Подключаемые модули */
#pragma comment(lib,"gthread-2.0.lib")
#pragma comment(lib,"gtk-win32-2.0.lib")
#pragma comment(lib,"glib-2.0.lib")
#pragma comment(lib,"gobject-2.0.lib")
#pragma comment(lib,"gdk-win32-2.0.lib")
#pragma comment(lib,"gdk_pixbuf-2.0.lib")
#pragma comment(lib,"pango-1.0.lib")
#pragma comment(lib,"pangowin32-1.0.lib")
#pragma comment(lib,"intl.lib")
 
/* Подключаемые библиотеки;
WIDTH - ширина создаваемого окна; 
HEIGHT - высота создаваемого окна;
N - разрядность матрицы ||NxN||;
R - длина стороны квадратика;
shadow - сдвиг, чтобы из квадрата, радиуса R, получить квадрат радиуса чуть меньше;
*/
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#include <time.h>
#define WIDTH N*R
#define HEIGHT N*R
#define N 4
#define R 100
#define shadow 5
 
int X1,Y1,Y2,X2,x1,x2,y1,y2,I,J,vector;
int a[N][N],lent;
char *text;
GdkColor fclrb,fclrw,fclrg,fclrr,fclrp;
GdkGC *gc;
GtkWidget *window;
GdkDrawable *w;
double x_mouse, y_mouse;
 
gboolean boolstart(GtkWidget *widget, int XX1, int YY1, int XX2, int YY2); // перерисовка состояния пятнашек.
gboolean mousemove(GtkWidget *w, GdkEvent *e,gpointer data); // отслеживание перемещение мышки
gboolean on_click(GtkWidget *Window, GdkEventButton *event, gpointer data); //функция, обрабатывает событие click
gboolean on_draw(GtkWidget *widget, GdkEventExpose *event, gpointer data); // задает исходное состояние, и цвета.
gboolean destroy(GtkWidget *widget);
gboolean keypress(GtkWidget *widget, GdkEventKey *e,gpointer data); // обработка нажатия клавиш на клавиатуре.
gint delete_event(GtkWidget *widget);
void gettext(char*,int, int*); // получение строки выводимого текста.
gboolean time_wind(GtkWidget *widget); // перемещение квадратика
void draw_text_in_center(gchar * text, GtkWidget *w, GdkGC *gc, gint xc, gint yc, unsigned t); // вывод текста
 
/* получение строки выводимого текста, преобразуем целое число в стоку символов: */
void gettext(char *t,int a, int* lt)
{
	if(a > 9)
	{
		*lt = 2;
		t[1] = a%10 + '0';
		a = a/10;
		t[0] = a%10 + '0';			
	}
	else
	{
		*lt = 1;
		t[0] = a%10 + '0';
	}
}
 
/* Вывод текста на экран */
void draw_text_in_center(gchar * text, GtkWidget *w, GdkGC *gc, gint xc, gint yc, unsigned t = lent)
{
	PangoContext *context=gtk_widget_create_pango_context(w);
	PangoLayout *layout= pango_layout_new(context);
	gint width,height;
	pango_layout_set_text(layout,text,t);
	pango_layout_get_pixel_size(layout,&width,&height);
	gdk_draw_layout (w->window,gc,xc-width/2,yc-height/2,layout);
	g_object_unref(context); g_object_unref(layout);
}
 
/* отслеживание перемещений мышки */
gboolean mousemove(GtkWidget *w, GdkEvent *e,gpointer data)
{
	double a,b;
 
	gdk_event_get_coords(e,&a,&b);
	x_mouse=a;
	y_mouse=b;
 
	return FALSE;
}
 
/* обработка нажатия клавиш на клавиатуре */
gboolean keypress(GtkWidget *widget, GdkEventKey *e,gpointer data)
{
	int st[N*N - 1],i,k,j,s,zero,t1,rdt,t2,tt1,tt2;
 
	switch(e->keyval)
	{
		case (GDK_c): // если нажали английскую 'c', проверяем состояние;
		{
			for(i=0,k=0; i<N; i++) // переписываем элементы в строчку, исключая 0
			{
				for(j=0; j<N; j++)
				{
					if(a[i][j] != 0)
					{
						st[k] = a[i][j];
						k++;
					}
					else
					{
						zero = i + 1;
					}
				}
			}
 
			s = 1; 
			k = 0;
			while(s) // подсчет инверсий
			{
				i = 0;
				s = 0;
				while(i < N*N - 2)
				{
					if(st[i] > st[i+1])
					{
						k++; // +1 инверсия
						s = 1;
						j = st[i];
						st[i] = st[i+1];
						st[i+1] = j;
					}
 
					i++;
				}
			}
 
			if(k == 0 && a[N-1][N-1] == 0) // готово
			{
				gdk_gc_set_rgb_fg_color(gc,&fclrw);
				gdk_draw_rectangle(w,gc,TRUE,5,HEIGHT,WIDTH-10,15);
				gdk_gc_set_rgb_fg_color(gc,&fclrp);
				draw_text_in_center("State: Done",widget,gc,WIDTH/11,HEIGHT + 5,13);
			}
			else
			{
				k = k + zero;
				if(k%2 == 0) // решаемая ситуация
				{
					gdk_gc_set_rgb_fg_color(gc,&fclrw);
					gdk_draw_rectangle(w,gc,TRUE,5,HEIGHT,WIDTH-10,15);
					gdk_gc_set_rgb_fg_color(gc,&fclrg);
					draw_text_in_center("State: Normal",widget,gc,WIDTH/10,HEIGHT + 5,13);
				}
				else // не решаемая ситуация
				{
					gdk_gc_set_rgb_fg_color(gc,&fclrw);
					gdk_draw_rectangle(w,gc,TRUE,5,HEIGHT,WIDTH-10,15);
					gdk_gc_set_rgb_fg_color(gc,&fclrr);
					draw_text_in_center("State: Error!!",widget,gc,WIDTH/10,HEIGHT + 5,14);	
				}
			}
			break;
		}
		case (GDK_r): // если нажали английскую 'r', делаем перестановку элементов;
		{
			rdt = rand()%100 + 20;
			for(i=0; i<rdt; i++)
			{
				t1 = rand()%N;
				t2 = rand()%N;
				tt1 = rand()%(N/2);
				tt2 = rand()%(N/2) + N/2;
 
				j = a[tt1][t1];
				a[tt1][t1] = a[tt2][t2];
				a[tt2][t2] = j;
			}
 
			boolstart(window,0,0,WIDTH,HEIGHT); // перерисовываем состояние пятнашек, после перемещения элементов матрицы.
			break;
		}
	}
 
	return TRUE;
}
 
int main(int arge, char **argv)
{
	int i,j,k;	
	GdkColor clr;
	gtk_init(&arge,&argv);
	k = 0;
	for(i=0;i<N;i++) // создание начальной матрицы
	{
		for(j=0;j<N;j++)
		{
			k++;
			if(i == N-1 && j == N-1) a[i][j] = 0;
			else a[i][j] = k;
		}
	}
	text = (char*)malloc(sizeof(char)*2);
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request(window,WIDTH,HEIGHT + 50);
	gtk_widget_add_events(window,GDK_POINTER_MOTION_MASK);
	gtk_widget_add_events(window,GDK_BUTTON_PRESS_MASK);
	gtk_widget_add_events(window,GDK_POINTER_MOTION_MASK);
	g_signal_connect(G_OBJECT(window),"motion-notify-event",G_CALLBACK(mousemove),NULL);
	g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(destroy),NULL);
	g_signal_connect(G_OBJECT(window),"delete_event",G_CALLBACK(delete_event),NULL);
	g_signal_connect(G_OBJECT(window),"button-press-event",G_CALLBACK(on_click),NULL);
	g_signal_connect(G_OBJECT(window),"key-press-event",G_CALLBACK(keypress),NULL);
	gdk_color_parse("white",&clr);
	gtk_widget_modify_bg(window,GTK_STATE_NORMAL,&clr);
	g_signal_connect(G_OBJECT(window),"expose_event",G_CALLBACK(on_draw),NULL);
 
	gtk_widget_show_all(window);
	gtk_main();
 
	return 0;
}
 
gboolean destroy(GtkWidget *widget)
{
	return FALSE;
}
 
gint delete_event(GtkWidget *widget)
{
	gtk_main_quit();
	return 1;
}
 
/* при нажатии на левую кнопку мыши, проверяем, можем ли мы сдвинуться
в каком-либо направлении (т.е. есть ли в окрестности 0) 
vector:
1 - влево
2 - вверх
3 - вправо
4 - вниз */
gboolean on_click(GtkWidget *Window, GdkEventButton *event, gpointer data)
{
	int i,j;
	for(i=0; i<N; i++)
	{
		for(j=0; j<N; j++)
		{
			if(x_mouse > j*R && x_mouse < (j+1)*R && y_mouse > i*R && y_mouse < (i+1)*R)
			{
				x1 = j*R;
				y1 = i*R;
				I = i;
				J = j;
			}
		}
	}
 
	vector = 0;
	if(I == 0 || J == 0 || I == N-1 || J == N-1)
	{
		if(I == 0 && J == 0)
		{
			if(a[I][J+1] == 0) vector = 3;
			if(a[I+1][J] == 0) vector = 4;
		}
		else if(I == 0 && J == N-1)
		{
			if(a[I][J-1] == 0) vector = 1;
			if(a[I+1][J] == 0) vector = 4;
		}
		else if(I == N-1 && J == 0)
		{
			if(a[I-1][J] == 0) vector = 2;
			if(a[I][J+1] == 0) vector = 3;
		}
		else if(I == N-1 && J == N-1)
		{
			if(a[I-1][J] == 0) vector = 2;
			if(a[I][J-1] == 0) vector = 1;
		}
		else if(I == 0)
		{
			if(a[I][J-1] == 0) vector = 1;
			if(a[I+1][J] == 0) vector = 4;
			if(a[I][J+1] == 0) vector = 3;
		}
		else if(I == N-1)
		{
			if(a[I-1][J] == 0) vector = 2;
			if(a[I][J-1] == 0) vector = 1;
			if(a[I][J+1] == 0) vector = 3;
		}
		else if(J == 0)
		{
			if(a[I+1][J] == 0) vector = 4;
			if(a[I-1][J] == 0) vector = 2;
			if(a[I][J+1] == 0) vector = 3;
		}
		else if(J == N-1)
		{
			if(a[I-1][J] == 0) vector = 2;
			if(a[I+1][J] == 0) vector = 4;
			if(a[I][J-1] == 0) vector = 1;
		}
	}
	else
	{
		if(a[I][J-1] == 0) vector = 1;
		if(a[I-1][J] == 0) vector = 2;
		if(a[I][J+1] == 0) vector = 3;
		if(a[I+1][J] == 0) vector = 4;
	}
 
	g_timeout_add(20,(GSourceFunc)time_wind,(gpointer)window);
	return TRUE;
}
 
/* После того, как мы сделали случайную перестановку элементов
(при нажатии клавишы 'r', нам нужно вывести на экран результат) */
gboolean boolstart(GtkWidget *widget, int XX1, int YY1, int XX2, int YY2)
{
	int i,j;	
	gdk_gc_set_rgb_fg_color(gc,&fclrb);
	gdk_draw_rectangle(w,gc,TRUE,0,0,XX2,YY2);
	for(i=0; i<N; i++)
	{
		Y1 = i*R;
		for(j=0; j<N; j++)
		{
			X1 = j*R;
			if(a[i][j] != 0)
			{
				gdk_gc_set_rgb_fg_color(gc,&fclrb);
				gdk_draw_rectangle(w,gc,TRUE,X1,Y1,R,R);
				gdk_gc_set_rgb_fg_color(gc,&fclrw);
				gdk_draw_rectangle(w,gc,TRUE,X1+shadow,Y1+shadow,R-2*shadow,R-2*shadow);
				gdk_gc_set_rgb_fg_color(gc,&fclrb);
				gettext(text,a[i][j],&lent);
				draw_text_in_center(text,widget,gc,X1+R/2,Y1+R/2);
			}
		}
	}
 
	gdk_gc_set_rgb_fg_color(gc,&fclrw);
	gdk_draw_rectangle(w,gc,TRUE,5,HEIGHT,WIDTH-10,15);
	gdk_gc_set_rgb_fg_color(gc,&fclrr);
	draw_text_in_center("State: Random",widget,gc,WIDTH/9,HEIGHT + 5,13);
	gdk_gc_set_rgb_fg_color(gc,&fclrb);
 
	return TRUE;
}
 
/* Создаем изображение начального состояния и задаем начальные элементы (цвета, надписи и т.п.) */
gboolean on_draw(GtkWidget *widget,GdkEventExpose *event, gpointer data)
{
	int i, j, XX1, YY1, XX2, YY2;
	XX1 = YY1 = 0; XX2 = WIDTH; YY2 = HEIGHT;
	gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];
	w = widget->window;
	fclrb.pixel = 0;
	fclrb.red = 0x6d00;
	fclrb.green = 0xcf00;
	fclrb.blue = 0xf600;
	fclrw.pixel = 0;
	fclrw.red = 0xFFFF;
	fclrw.green = 0xFFFF;
	fclrw.blue = 0xFFFF;
	fclrg.pixel = 0;
	fclrg.red = 0;
	fclrg.green = 0xFFFF;
	fclrg.blue = 0;
	fclrr.pixel = 0;
	fclrr.red = 0xFFFF;
	fclrr.green = 0;
	fclrr.blue = 0;
	fclrp.pixel = 0;
	fclrp.red = 0xFFFF;
	fclrp.green = 0;
	fclrp.blue = 0xFFFF;
 
	gdk_gc_set_rgb_fg_color(gc,&fclrb);
	gdk_draw_rectangle(w,gc,TRUE,0,0,XX2,YY2);
	for(i=0; i<N; i++)
	{
		Y1 = i*R;
		for(j=0; j<N; j++)
		{
			X1 = j*R;
			if(i != N-1 || j != N-1)
			{
				gdk_gc_set_rgb_fg_color(gc,&fclrb);
				gdk_draw_rectangle(w,gc,TRUE,X1,Y1,R,R);
				gdk_gc_set_rgb_fg_color(gc,&fclrw);
				gdk_draw_rectangle(w,gc,TRUE,X1+shadow,Y1+shadow,R-2*shadow,R-2*shadow);
				gdk_gc_set_rgb_fg_color(gc,&fclrb);
				gettext(text,a[i][j],&lent);
				draw_text_in_center(text,widget,gc,X1+R/2,Y1+R/2);
			}
		}
	}
 
	gdk_gc_set_rgb_fg_color(gc,&fclrw);
	gdk_draw_rectangle(w,gc,TRUE,5,HEIGHT,WIDTH-10,45);
	gdk_gc_set_rgb_fg_color(gc,&fclrp);
	draw_text_in_center("State: Done",widget,gc,WIDTH/11,HEIGHT + 5,13);
	gdk_gc_set_rgb_fg_color(gc,&fclrb);
	draw_text_in_center("Plz press 'c' to check the state",widget,gc,WIDTH/5,HEIGHT + 20,32);
	draw_text_in_center("Plz press 'r', for on randomizer",widget,gc,WIDTH/5,HEIGHT + 35,32);
 
	return TRUE;
}
 
/* Перемещение квадратика в направлении найденного вектора (после обработки события: click) */
gboolean time_wind(GtkWidget *widget)
{
	int xx1,yy1;
 
	if(vector == 1)
	{
		xx1 = x1 - R;
		yy1 = y1;
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,x1,y1,R,R);
 
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,xx1,yy1,R,R);
		gdk_gc_set_rgb_fg_color(gc,&fclrw);
		gdk_draw_rectangle(w,gc,TRUE,xx1+shadow,yy1+shadow,R-2*shadow,R-2*shadow);
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gettext(text,a[I][J],&lent);
		draw_text_in_center(text,widget,gc,xx1+R/2,yy1+R/2);
 
		a[I][J-1] = a[I][J];
		a[I][J] = 0;
	}
	else if(vector == 2)
	{
		xx1 = x1;
		yy1 = y1 - R;
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,x1,y1,R,R);
 
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,xx1,yy1,R,R);
		gdk_gc_set_rgb_fg_color(gc,&fclrw);
		gdk_draw_rectangle(w,gc,TRUE,xx1+shadow,yy1+shadow,R-2*shadow,R-2*shadow);
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gettext(text,a[I][J],&lent);
		draw_text_in_center(text,widget,gc,xx1+R/2,yy1+R/2);
 
		a[I-1][J] = a[I][J];
		a[I][J] = 0;
	}
	else if(vector == 3)
	{
		xx1 = x1 + R;
		yy1 = y1;
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,x1,y1,R,R);
 
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,xx1,yy1,R,R);
		gdk_gc_set_rgb_fg_color(gc,&fclrw);
		gdk_draw_rectangle(w,gc,TRUE,xx1+shadow,yy1+shadow,R-2*shadow,R-2*shadow);
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gettext(text,a[I][J],&lent);
		draw_text_in_center(text,widget,gc,xx1+R/2,yy1+R/2);
 
		a[I][J+1] = a[I][J];
		a[I][J] = 0;
	}
	else if(vector == 4)
	{
		xx1 = x1;
		yy1 = y1 + R;
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,x1,y1,R,R);
 
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gdk_draw_rectangle(w,gc,TRUE,xx1,yy1,R,R);
		gdk_gc_set_rgb_fg_color(gc,&fclrw);
		gdk_draw_rectangle(w,gc,TRUE,xx1+shadow,yy1+shadow,R-2*shadow,R-2*shadow);
		gdk_gc_set_rgb_fg_color(gc,&fclrb);
		gettext(text,a[I][J],&lent);
		draw_text_in_center(text,widget,gc,xx1+R/2,yy1+R/2);
 
		a[I+1][J] = a[I][J];
		a[I][J] = 0;
	}
 
	return FALSE;
}

Ключевые слова: 
Игра, пятнашки
ВложениеРазмер
Pyatnashki.rar14.25 кб