用Turbo C 2.0写五子棋小游戏

这辈子再也不用Turbo C写东西了_(:зゝ∠)_

功能

  • 有比较友好(大概友好吧:) )的界面。
  • 采用贪心算法,能与计算机对弈

流程图

主函数

int main()
{
	int gd = DETECT, gm = 0;
	int key;
	initgraph(&gd, &gm, "c:\\tc");

 	opencartoon(); /*开场动画*/
	init(); /*界面和棋盘初始化*/
	key = get_key();
	while (!quit) { /*只要没有退出就继续*/
		switch (key) {
        case UP: move(0); break; /*下面四个是上下左右的操作*/
        case DOWN: move(1); break;
        case LEFT: move(2); break;
        case RIGHT: move(3); break;
        case ENTER: /*用户下棋*/
            if (!res && userplace(WHITE)) { /*如果游戏没有结束且用户要下棋了*/
                if ((res = judgewinner(1)) == 0) { /*如果用户没赢*/
                    computerplace(RED); /*则轮到电脑下棋*/
                    res = judgewinner(-1); /*判断电脑赢了吗*/
                }
            }
            break;
        case F1: judgerestart(); break; /*重新开始*/
        case ESC: quit = judgequit(); break; /*退出*/
		}
		if (notchoose && res) { /*游戏结束了且用户没有选择是否继续留在游戏里*/
			if (printres(res)) /*显示游戏结果(输赢),并判断用户是否继续留在游戏里*/
				quit = 1; /*用户不愿意留在游戏里,退出*/
			notchoose = 0; /*用户已做出选择*/
		}
		if (!quit) key = get_key(); /*如果没有退出,继续接受键盘事件*/
	}
	
	cleardevice(); /*清屏*/
	goodbyecartoon(); /*退出动画*/
	closegraph();
	return 0;
}

处理键盘响应

  • Turbo C中用bioskey(),会返回两个值:扫描码和ASCII码
  • 扫描码和ASCII码各占8位
  • 有些特殊的键没有ASCII码,只有扫描码,比如F1键。这时候ASCII码为0
/*键盘宏定义*/
#define UP 0x4800 /*方向键上*/
#define DOWN 0x5000 /*方向键下*/
#define LEFT 0x4b00 /*方向键左*/
#define RIGHT 0x4d00 /*方向键右*/
#define ENTER 13
#define ESC 27
#define F1 0x3B00

/*键值的定义*/
typedef union {
	int word;
	char byte;
}keycode;

/*返回按下的键值*/
int get_key() 
{
	keycode key;
	key.word = bioskey(0); /*bioskey(0)会一直等待有键按下*/
	return key.byte ? key.byte : key.word;
}

界面设计

  • 将对话框的显示封装成函数dialog,允许定制对话框的颜色样式(包括边框颜色),定制文本内容
void init()
{
	char *help_text[] = {"1.ESC - Quit", "2.Direct Key - Move", "3.Enter - Put chess", "4.F1 - Restart",
                         "Note: You move first.", "If there is no place", "to put chess, you", "would LOSE!"};
	
	initialize(); /*初始化棋盘*/
	cleardevice(); /*清屏*/
	drawbackgroud(YELLOW);
	n = m = 10; /*棋盘10行10列*/
	cposx = 50; /*棋盘左上角坐标为(50, 80)*/
	cposy = 80;
	drawchessboard(cposx, cposy, n, m, DARKGRAY); /*画出棋盘*/
	drawtitle(cposx - 3, 10, "======Five in a Row======", BLUE); /*写标题*/
	dialog(cposx + (n + 1) * W + 10, 30, 200, 400, "***Help***", help_text, 8,
	       WHITE, BLUE, CYAN, LIGHTGRAY, BLUE, RED); /*帮助文档框*/
	row = 5, col = 5; /*当前准心在第五行第五列*/
	drawtarget(cposx, cposy, row, col, RED); /*绘准心*/
	quit = 0; /*是否退出*/
	res = 0; /*对弈结果*/
	notchoose = 1; /*是否选择留在游戏中*/
	tot = 0; /*下的总步数*/
}

void drawbackgroud(int color)
{
    /*绘制背景*/
	setbkcolor(color);
}

void drawchessboard(int x, int y, int r, int c, int color)
{
    /*绘制棋盘*/
	int height = (r + 1) * W;
	int weight = (c + 1) * W;
	int i;
	setcolor(color);
	line(x, y, x + weight, y);
	line(x, y + height, x + weight, y + height);
	line(x, y, x, y + height);
	line(x + weight, y, x + weight, y + height);
	setlinestyle(DOTTED_LINE, 0, 1); /*虚线*/
	for (i = 1; i <= r; i++)
		line(x, y + i * W, x + weight, y + i * W);
	for (i = 1; i <= c; i++)
		line(x + i * W, y, x + i * W, y + height);
}

void drawtitle(int x, int y, char *title, int color)
{
    /*绘制标题*/
	setcolor(color);
	settextstyle(3, 0, 3);
	outtextxy(x, y, title);
}

void dialog(int x, int y, int width, int height, char *title, char **message, int lines, 
            int bk, int shadow, int bkedge, int shadowedge, int titlec, int messagec)
{
    /*bk对话框颜色、shadow阴影颜色、bkedge边框颜色、shadowedge阴影边框颜色、titlec标题文字颜色、messagec信息文字颜色*/
	int i;
	setcolor(shadowedge);
	setlinestyle(SOLID_LINE, 0, 1);
	rectangle(x + 20, y + 20, x + width + 20, y + height + 20);
	setfillstyle(1, shadow);
	floodfill((2 * x + width + 40) / 2, (2 * y + height + 40) / 2, shadowedge);
	setcolor(bkedge);
	rectangle(x + 10, y + 10, x + width + 10, y + height + 10);
	setfillstyle(1, bk);
	floodfill((2 * x + width) / 2, (2 * y + height) / 2, bkedge);

	setcolor(titlec);
	settextstyle(3, 0, 2);
	outtextxy(x + 20, y + 20, title);

	setcolor(messagec);
	settextstyle(3, 0, 1);
	for (i = 0; i < lines; i++)
		outtextxy(x + 20, y + 20 + 30 * (i + 1), message[i]);
}

用户操作处理

  • 准心移动
  • 用户退出游戏处理和重新开始游戏的处理
/*简化代码*/
const int mover[] = {-1, 1, 0, 0};
const int movec[] = {0, 0, -1, 1};

void move(int type)
{
    /*用户移动准心*/
	int tr = row, tc = col;
	tr += mover[type];
	tc += movec[type];
	
	if (tr == 0 || tr > n || tc == 0 || tc > m) return;
	drawtarget(cposx, cposy, row, col, YELLOW); /*擦除原来的准心*/
	drawtarget(cposx, cposy, tr, tc, RED);
	row = tr;
	col = tc;
}

int judgequit()
{
    /*用户退出操作*/
	int key;
	unsigned size;
	void *buf;
	char *message[] = {"Do you want to quit?(Y/N)"};
	
    /*这里要复制原先的图像,否则如果用户取消就回不去了。。。下面的重新开始游戏也一样*/
	size = imagesize(100, 200, 520, 320);
	buf = malloc(size);
	getimage(100, 200, 520, 320, buf);
	
	dialog(100, 200, 400, 100, "", message, 1, 
	       WHITE, BLUE, LIGHTBLUE, GREEN, RED, BLUE);
	key = get_key();
	while (!(key == 'y' || key == 'n' || key == 'Y' || key == 'N')) {
		key = get_key();
	}
	if (key == 'N' || key == 'n')
		putimage(100, 200, buf, COPY_PUT);
	free(buf);
	return (key == 'y' || key == 'Y') ? 1 : 0;
}

int userplace(int color)
{
    /*绘制用户的棋子*/
	int px = cposx, py = cposy;
	if (chess[row][col] == 0) {
		setcolor(WHITE);
		setlinestyle(SOLID_LINE, 0, 1);
		px += col * W;
		py += row * W;
		circle(px, py, W / 4);
		setfillstyle(1, color);
		floodfill(px, py, WHITE);
		chess[row][col] = 1;
		tot++;
		return 1;
	} else 
		return 0;
}

int judgerestart() 
{
    /*重新开始游戏操作*/
	int key;
	unsigned size;
	void *buf;
	char *message[] = {"Do you want to restart?(Y/N)"};
	
	size = imagesize(100, 200, 520, 320);
	buf = malloc(size);
	getimage(100, 200, 520, 320, buf);
	
	dialog(100, 200, 400, 100, "", message, 1, 
	       WHITE, BLUE, LIGHTBLUE, GREEN, RED, BLUE);
	key = get_key();
	while (!(key == 'y' || key == 'n' || key == 'Y' || key == 'N')) {
		key = get_key();
	}
	if (key == 'N' || key == 'n')
		putimage(100, 200, buf, COPY_PUT);
	else
		init();
	free(buf);
	return (key == 'y' || key == 'Y') ? 1 : 0;
}

计算机自动下棋——贪心算法实现

  • 计算机的下棋策略是贪心的,可能不如搜索来的智能,但足以挑战初学者
void computerplace(int color)
{
    /*计算机下棋,绘制*/
	int cx = cposx, cy = cposy;
	computerjudge();
	setcolor(WHITE);
	setlinestyle(SOLID_LINE, 0, 1);
	cx += ccol * W;
	cy += crow * W;
	circle(cx, cy, W / 4);
	setfillstyle(1, color);
	floodfill(cx, cy, WHITE);
	chess[crow][ccol] = -1;
	tot++;
}

void computerjudge()
{
    /*得到棋盘上哪一点是“最优的”*/
	int i, j;
	int tppr, tppc, tpcr, tpcc;
	int pscore[MAXN][MAXN], cscore[MAXN][MAXN];
	int mxp = 0, mxc = 0;
	
	memset(pscore, 0, sizeof(pscore));
	memset(cscore, 0, sizeof(cscore));
	for (i = 1; i <= n; i++) {
		for (j = 1; j <= m; j++) if (chess[i][j] == 0) {
                getscore(i, j, pscore, 1);
                getscore(i, j, cscore, -1);
            }
	}
	for (i = 1; i <= n; i++) {
		for (j = 1; j <= m; j++) {
			if (pscore[i][j] > mxp) {
				mxp = pscore[i][j];
				tppr = i, tppc = j;
			}
			if (cscore[i][j] > mxc) {
				mxc = cscore[i][j];
				tpcr = i, tpcc = j;
			}
		}
	}
	if (mxp >= mxc) {
		crow = tppr, ccol = tppc;
	} else {
		crow = tpcr, ccol = tpcc;
	}
}

void getscore(int rr, int cc, int score[MAXN][MAXN], int who)
{
    /*计算棋盘上每个点的分数*/
	int another = -who;
	int tot = 0;
	int num, l, r;
	int tr, tc;
	int i, j;
	
	for (i = 0; i < 4; i++) {
		num = l = r = 0;
		j = 1;
		while (1) {
			tr = rr + j * dirr[i];
			tc = cc + j * dirc[i];
			if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
				num++;
				j++;
			} else {
				if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == another)
					l = 1;
				break;
			}
		}
		j = 1;
		while (1) {
			tr = rr + j * dirr[i + 4];
			tc = cc + j * dirc[i + 4];
			if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
				num++;
				j++;
			} else {
				if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == another)
					r = 1;
				break;
			}
		}
		tot += judgetype(num, l, r);
	}
	score[rr][cc] = tot;
}

int judgetype(int num, int l, int r)
{
    /*每种“类型”的得分*/
	int sco = 0;
	if (num >= 4)
		sco += 10000;
	else if (num == 3) {
		if (!l && !r)
			sco += 3000;
		else if (!(l && r))
			sco += 900;
	} else if (num == 2) {
		if (!l && !r)
			sco += 460;
		else if (!(l && r))
			sco += 30;
	} else if (num == 1) {
		if (!l && !r)
			sco += 45;
		else if (!(l && r))
			sco += 5;
	} else if (num == 0) {
		if (!l && !r)
			sco += 3;
		else if (!(l && r))
			sco += 1;
	}
	return sco;
}

判断胜负

  • 用户先手。棋盘下完,则计算机赢。
/*简化代码*/
const int dirr[] = {-1, 0, -1, -1, 1, 0, 1, 1};
const int dirc[] = {0, -1, -1, 1, 0, 1, 1, -1};

int judgewinner(int who)
{
	int posr, posc;
	int dir[8] = {0};
	int i, j;
	int tr, tc;
	if (who == 1) {
		posr = row;
		posc = col;
	} else {
		posr = crow;
		posc = ccol;
	}
	for (i = 0; i < 8; i++) {
		j = 1;
		while (1) {
			tr = posr + j * dirr[i];
			tc = posc + j * dirc[i];
			if (tr > 0 && tc > 0 && tr <= n && tc <= m && chess[tr][tc] == who) {
				dir[i]++;
				j++;
			} else 
				break;
		}
	}
	for (i = 0; i < 4; i++) {
		if (dir[i] + dir[i + 4] + 1 >= 5) 
			return who;
	}
	if (tot == n * m)
		return -1;
	return 0;
}

退出和进入游戏的动画

  • 这一段代码几乎是抄一个博主写的,但我忘记是哪个博客给的了。。。(五子棋的代码本来也想复制一波的,但这位博主的代码实在不堪入目,自己重写了代码,并加入了计算机自动下棋功能)
  • 凑代码量的东西,很简单:)
/*以下代码只贴“见”的字模*/
char jian64L[]={
/* 以下是 '见' 的 64点阵隶书 字模,512 byte */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x40,0x00,0x00,0x06,0x00,0x00,
    0x00,0x00,0xF0,0x3F,0xFE,0x1F,0x00,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x80,0x00,
    0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0x01,0xF8,0x00,0x00,0x1F,0x00,0x00,
    0x00,0x01,0xF0,0x00,0x00,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x00,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x0E,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1F,0x00,0x0F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x00,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x08,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x1C,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x1C,0x1F,0x00,0x00,
    0x00,0x00,0xF0,0x1E,0x38,0x1F,0x80,0x00,
    0x00,0x01,0xF0,0x3C,0x38,0x1F,0x80,0x00,
    0x00,0x00,0xF0,0x3C,0x38,0x1F,0x00,0x00,
    0x00,0x00,0x00,0x3C,0x70,0x0E,0x00,0x00,
    0x00,0x00,0x00,0x78,0x70,0x00,0x00,0x00,
    0x00,0x00,0x00,0xF8,0x70,0x00,0x00,0x00,
    0x00,0x00,0x01,0xF0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x03,0xE0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x0F,0xC0,0xF8,0x00,0x00,0x00,
    0x00,0x00,0x3F,0x80,0xFE,0x00,0x00,0x04,
    0x00,0x00,0xFF,0x00,0x7F,0xC0,0x00,0x3C,
    0x00,0x07,0xFE,0x00,0x3F,0xFF,0xFF,0xF8,
    0x00,0x7F,0xF8,0x00,0x1F,0xFF,0xFF,0xF8,
    0x0F,0xFF,0xE0,0x00,0x0F,0xFF,0xFF,0xF0,
    0x1F,0xFF,0x80,0x00,0x03,0xFF,0xFF,0xF0,
    0x0F,0xFC,0x00,0x00,0x00,0x7F,0xFF,0xE0,
    0x07,0xE0,0x00,0x00,0x00,0x07,0xFF,0xC0,
    0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void tim(int p)
{
	int i;
	for(i = 0; i < p; i++)
		delay(500);
}

void drawmat(char *mat,int matsize,int x,int y,int color)
/*依次:字模指针、点阵大小、起始坐标(x,y)、颜色*/
{
	int i, j, k, n;
	n = (matsize - 1) / 8 + 1;
	for(j = 0; j < matsize; j++) {
		for(i = 0; i < n; i++) {
			for(k = 0;k < 8; k++) {
				if(mat[j * n + i] & (0x80 >> k))  /*测试为1的位则显示*/
					putpixel(x + i * 8 + k, y + j, color);
			}
		}
	}
}

void opencartoon()
{
	drawmat(huan64L, 64, 149, 200, 1); tim(1);
	drawmat(ying64L, 64, 249, 200, 2); tim(1);
	drawmat(shang64L, 64, 349, 200, 12); tim(1);
	drawmat(wan64L, 64, 449, 200, 4); tim(1);
}

void goodbyecartoon()
{
	drawmat(zai64L, 64, 200, 200, 13); tim(1);
	drawmat(jian64L, 64, 376, 200, 2); tim(1);
}
posted @ 2018-06-25 22:33  RawFisher  阅读(303)  评论(0编辑  收藏  举报