个人作品

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下

黑白棋游戏(也叫翻转棋、奥赛罗棋)人机对战版(by 自然向日葵) 铜牌收录

黑白棋(也叫翻转棋、奥赛罗棋)的棋盘是一个有8*8方格的棋盘。下棋时将棋下在空格中间,而不是像围棋一样下在交叉点上。开始时在棋盘正中有两白两黑四个棋子交叉放置,黑棋总是先下子。

下子的方法:把自己颜色的棋子放在棋盘的空格上,而当自己放下的棋子在横、竖、斜八个方向内有一个自己的棋子,则被夹在中间的全部翻转会成为自己的棋子。并且,只有在可以翻转棋子的地方才可以下子。

引用了QQ游戏黑白棋的图片。

游戏运行截图如下:

游戏中使用了大量图片、音乐、资源文件,点这里下载该游戏的完整 VC 项目包

游戏代码如下(相关图片资源请下载完整 VC 项目包):

///////////////////////////////////////////////////////////
// 程序名称:黑白棋AI版
// 编译环境:Visual C++ 2010/6.0,EasyX_v20120304(beta)
// 程序编写:自然向日葵 1164359890@qq.com
// 最后更新:2012-3-24
//
////////////////////////////////////////////////////////////
//说明:人机对战版
////////////////////////////////////////////////////////////


#include <graphics.h>	// EasyX_2011惊蛰版
#include <strstream>
#include <ctime>
#pragma comment(lib, "Winmm.lib")
#define T(c) ((c == 'B') ? 'W' : 'B')
using namespace std;

/*******************************定义全局变量*****************************/
const int difficult = 6;	// 难度
const int move[8][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1},
						{-1, -1}, {1, -1}, {1, 1}, {-1, 1}};
							// 八个方向扩展
char map[8][8];				// 棋盘
IMAGE img[5];				// 保存图片
int black, white;			// 双方的棋子数
int X, Y;					// 白棋的下子点

/**********************************函数声明*****************************/
void load(void);				// 加载素材
void print(void);				// 画棋盘
void draw(int, int, char);		// 下当前子
int judge(int, int, char);		// 判断当前是否可以落下
bool baidu(char);				// 判断是否有棋可吃
bool quit(char);				// 判断是否有棋存活
bool ask(void);					// 弹出对话框
int D(char, int);				// 动态规划
void play(void);				// 游戏过程

/**********************************定义函数*****************************/
void load(void)		// 加载素材
{
	// 加载图片
	loadimage(&img[0], "图片\\空位.bmp");
	loadimage(&img[1], "图片\\黑子.bmp");
	loadimage(&img[2], "图片\\白子.bmp");
	loadimage(&img[3], "图片\\黑子1.bmp");
	loadimage(&img[4], "图片\\白子1.bmp");

	// 加载音乐
	mciSendString("open 音乐\\背景音乐.wma", NULL, 0, NULL);
	mciSendString("open 音乐\\和局.wma", NULL, 0, NULL);
	mciSendString("open 音乐\\胜利.wma", NULL, 0, NULL);
	mciSendString("open 音乐\\失败.wma", NULL, 0, NULL);
	mciSendString("open 音乐\\下子.wma", NULL, 0, NULL);

	// 初始化棋盘
	initgraph(340, 340);
	IMAGE qipan;
	loadimage(&qipan, "图片\\棋盘.bmp");
	putimage(0, 0, &qipan);
	setorigin(26, 26);
	SetWindowText(GetHWnd(), "黑白棋AI版");
}

void print(void)	// 画棋盘
{
	int x, y;
	black = white = 0;
	for(x = 0; x < 8; x++)
		for(y = 0; y < 8; y++)
			switch(map[x][y])
			{
				case 0:
						putimage(37 * y, 37 * x, &img[0]);
					break;
				case 'B':
						putimage(37 * y, 37 * x, &img[1]);
					black++;
					break;
				case 'W':
						putimage(37 * y, 37 * x, &img[2]);
					white++;
					break;
			}
}

void draw(int x, int y, char a)	// 下当前子
{
	char b = T(a);									// 敌方子
	int i, x1, y1, x2, y2;
	bool sign;			
	for (i = 0; i < 8; i++)
	{
		sign = false;
		x1 = x + move[i][0];
		y1 = y + move[i][1];
		while (0 <= x1 && x1 < 8 && 0 <= y1 && y1 < 8 && map[x1][y1])
		{
			if(map[x1][y1] == b)
				sign = true;
			else
			{
				if(sign)
				{
					x1 -= move[i][0];
					y1 -= move[i][1];
					x2 = x + move[i][0];
					y2 = y + move[i][1];
					while (((x <= x2 && x2 <= x1) || (x1 <= x2 && x2 <= x)) && ((y <= y2 && y2 <= y1) || (y1 <= y2 && y2 <= y)))
					{
						map[x2][y2] = a;
						x2 += move[i][0];
						y2 += move[i][1];
					}
				}
				break;
			}
			x1 += move[i][0];
			y1 += move[i][1];
		}
	}
	map[x][y] = a;
}

int judge(int x, int y, char a)	// 判断当前是否可以落下,同draw函数
{
	if(map[x][y])						// 如果当前不是空的返回0值
		return 0;
	char b = T(a);
	int i, x1, y1;
	int n = 0, sign;
	for (i = 0; i < 8; i++)
	{
		sign = 0;
		x1 = x + move[i][0];
		y1 = y + move[i][1];
		while (0 <= x1 && x1 < 8 && 0 <= y1 && y1 < 8 && map[x1][y1])
		{
			if(map[x1][y1] == b)
				sign++;
			else
			{
				n += sign;
				break;
			}
			x1 += move[i][0];
			y1 += move[i][1];
		}
	}
	return n;		// 返回可吃棋数
}

bool baidu(char c)	// 判断是否有棋可吃
{
	int x, y;
	for(x = 0; x < 8; x++)
		for(y = 0; y < 8; y++)
			if(judge(x, y, c))
				return true;
	return false;
}

bool quit(char c)	// 判断是否有棋存活
{
	int x, y;
	bool b = false, w = false;
	for(x = 0; x < 8; x++)
		for(y = 0; y < 8; y++)
		{
			if(map[x][y] == c)
				return false;
		}
	return true;
}

bool ask(void)	// 弹出对话框
{
	HWND wnd = GetHWnd();
	int key;
	char str[50];
	ostrstream strout(str, 50);
	strout <<"黑:" <<black <<"  白:" <<white <<endl;
	if (black == white)
		strout <<"世界和平";
	else if(black > white)
		strout <<"恭喜你赢了!";
	else
		strout <<"小样,还想赢我。";
	strout <<"\n再来一局吗?" <<ends;
	if(black == white)
		key = MessageBox(wnd, str, "和局", MB_YESNO | MB_ICONQUESTION);
	else if(black > white)
		key = MessageBox(wnd, str, "黑胜", MB_YESNO | MB_ICONQUESTION);
	else
		key = MessageBox(wnd, str, "白胜", MB_YESNO | MB_ICONQUESTION);
	if(key == IDYES)
		return true;
	else
		return false;
}

int D(char c, int step)
{
	// 判断是否结束递归
	if (step > difficult)	// 约束步数之内
		return 0;
	if (!baidu(c))
	{
		if (baidu(T(c)))
			return -D(T(c), step);
		else
			return 0;
	}

	int i, j, max = 0, temp, x, y;
	bool ans = false;

	// 建立临时数组
	char **t = new char *[8];
	for (i = 0; i < 8; i++)
		t[i] = new char [8];
	for (i = 0; i < 8; i++)
		for (j = 0; j < 8; j++)
			t[i][j] = map[i][j];

	// 搜索解法
	for (i = 0; i < 8; i++)
		for (j = 0; j < 8; j++)
			if (temp = judge(i, j, c))
			{
				draw(i, j, c);
				temp -= D(T(c), step + 1);
				if (temp > max || !ans)
				{
					max = temp;
					x = i;
					y = j;
					ans = true;
				}
				for (int k = 0; k < 8; k++)
					for (int l = 0; l < 8; l++)
						map[k][l] = t[k][l];
			}

	// 撤销空间
	for (i = 0; i < 8; i++)
		delete [] t[i];
	delete [] t;

	// 如果是第一步则标识白棋下子点
	if (step == 1)
	{
		X = x;
		Y = y;
	}

	return max;	// 返会最优解
}

void play(void)			// 游戏过程
{
	MOUSEMSG m;
	int x, y;

	// 初始化棋子
	for(x = 0; x < 8; x++)
		for(y = 0; y < 8; y++)
			map[x][y] = 0;
	map[3][4] = map[4][3] = 'B';
	map[3][3] = map[4][4] = 'W';

	// 开始游戏
	print();
	mciSendString("play 音乐\\背景音乐.wma from 0 repeat", NULL, 0, NULL);
	do
	{
		if (baidu('B'))										// 如果玩家有下子位置							
		{
			A:
			while(true)
			{
				m = GetMouseMsg();							// 获取鼠标消息
				if(m.uMsg == WM_LBUTTONDOWN && m.x - 26 < 37 * 8 && m.y - 26 < 37 * 8)
															// 如果左键点击
					break;
			}
			x = (m.y - 26) / 37;
			y = (m.x - 26) / 37;
			if(judge(x, y, 'B'))							// 如果当前位置有效
			{
				draw(x, y, 'B');							// 下子
				mciSendString("play 音乐\\下子.wma from 0", NULL, 0, NULL);
				print();
				putimage(37 * y, 37 * x, &img[3]);			// 标识下子点
			}
			else
				goto A;
			if (quit('W'))									// 计算机是否失败
				break;
		}
		if (baidu('W'))										// 如果计算机有下子位置
		{
			clock_t start;
			start = clock();
			D('W', 1);										// 搜索解法
			while (clock() - start < CLOCKS_PER_SEC);
			draw(X, Y, 'W');
			print();
			mciSendString("play 音乐\\下子.wma from 0", NULL, 0, NULL);
			putimage(37 * Y, 37 * X, &img[4]);	// 标识下子点
			if (quit('B'))									// 玩家是否失败
				break;
		}
	}while (baidu('B') || baidu ('W'));

	// 播放庆祝音乐
	mciSendString("stop 音乐\\背景音乐.wma", NULL, 0, NULL);
	if (black > white)
		mciSendString("play 音乐\\胜利.wma from 0", NULL, 0, NULL);
	else if (black < white)
		mciSendString("play 音乐\\失败.wma from 0", NULL, 0, NULL);
	else
		mciSendString("play 音乐\\和局.wma from 0", NULL, 0, NULL);
}

// 主函数
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	load();
	do
	{
		play();
	} while(ask());

	// 关闭音乐
	mciSendString("close 音乐\\背景音乐.wma", NULL, 0, NULL);
	mciSendString("close 音乐\\和局.wma", NULL, 0, NULL);
	mciSendString("close 音乐\\胜利.wma", NULL, 0, NULL);
	mciSendString("close 音乐\\失败.wma", NULL, 0, NULL);
	mciSendString("close 音乐\\下子.wma", NULL, 0, NULL);
	closegraph();
	return 0;
}

/***********************************THE END************************************/

作者:自然向日葵
邮箱:1164359890@qq.com

添加评论