题目要求:
三连棋是在3X3方格内玩的游戏,两个玩家X和O轮流放棋,先将自己的棋子形成一行、一列或对角线的,则胜出。
使用visual studio 时候 应先下载EasyX插件
主菜单中有玩家对战、人机对战、音乐开、音乐关四个按键,任意鼠标点击即可使用其对应功能。
玩家对战功能是三连棋程序中的双人模式。先手玩家使用○,在九个格子中选择空位点击后落子成功并转换玩家,此时转换棋子图案为×。一局游戏结束会出现三种可能:○选手获胜、×选手获胜以及平局。此时可选择菜单中的再来一局或者返回主菜单。
该功能可以让玩家单人下棋,与电脑对战,玩家先手下棋,程序自动生成棋子。游戏结束后依旧可以选择返回主菜单或者再来一局。
我们的“三连棋”游戏总共分为四大类:棋盘类、游戏对战类、双人类和人机类。其中棋盘类作为基类实现棋盘初始化的基本功能,游戏对战类继承棋盘类实现下棋游戏的基本功能,双人类和人机类分别继承游戏对战类,最后实现各自具体的下棋功能。
面向对象三大特征:封装、继承、多态。
封装:封装的目的是增强安全性和简化编程。
我们在本项目中共封装了四个类,Map棋盘类、Battle游戏对战类、
Playerbattle双人类、Computerbattle人机类。
访问权限包括public,private,protected三类
(1)public(共有),所有类均可访问
(2)protected(保护),可以被自身和子类访问
(3)private(私有),只能自身和友元函数访问
在Map棋盘基类中,我们使用了protected和public,以便就达到对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而增强数据的安全性和简化程序的编写工作。
继承:继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。
在继承时候为了使继承不造成混乱问题我们均使用了单一继承和public的方式。
Battle游戏对战类继承Map棋盘类,使用游戏对战可在棋盘中进行。
Playerbattle双人类继承Battle游戏对战类,继承过来Battle类的的属性和方法来完成自身的双人对战功能。
Computerbattle人机类同Playerbattle双人类一样通过继承Battle游戏对战类的属性和方法,再加上自身的电脑下棋的方法最终实现人机对战。
多态:多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
多态是基于虚函数来实现的。多态实现的两大条件为:
(1)调用函数的对象必须是指针或者引用。
(2)被调用的函数必须是虚函数,且完成了虚函数的重写。
我们在做程序时候Battle游戏对战类中的函数使用virtual定义,以便允许子类Computerbattle人机类和Playerbattle双人类根据自己的实际需求重新定义的成员函数。
代码:
#include
using namespace std;
#include
#include
#include
#include
#include
#pragma comment(lib, "winmm.lib")
class Map {//棋盘基类--定义基本属性
protected:
int map[3][3] =
{
-1, -1, -1,
-1, -1, -1,
-1, -1, -1
};
int x = 0, y = 0; //当前下棋的坐标
int camp = 0; //阵营(自己玩的图案) 0为O图形, 1为X图形
bool resWin[3]; //判断赢得是哪一条线
int winner = 0; //判断赢家 0玩家 1电脑
public:
void initChessboard() {//画棋盘
setlinecolor(BLACK);//这个函数用于设置当前设备画线颜色。
setlinestyle(PS_SOLID, 3);
// 画棋盘
for (int i = 1; i < 3; i++)
{
line(0, i * 100, 300, i * 100);
line(i * 100, 0, i * 100, 300);
}
line(300, 0, 300, 300);
};
// 对行和列以及斜对角进行判断,是否可以连成线
bool ONE_ROW()//第一行
{
if ((map[0][0] != -1 && map[1][0] != -1 && map[2][0] != -1) && \
((map[0][0] == map[1][0]) && (map[1][0] == map[2][0])))
return true;
else
return false;
}
bool TWO_ROW()//第二排
{
if ((map[0][1] != -1 && map[1][1] != -1 && map[2][1] != -1) && \
((map[0][1] == map[1][1]) && (map[1][1] == map[2][1])))
return true;
else
return false;
}
bool THREE_ROW()//第三排
{
if ((map[0][2] != -1 && map[1][2] != -1 && map[2][2] != -1) && \
((map[0][2] == map[1][2]) && (map[1][2] == map[2][2])))
return true;
else
return false;
}
bool ONE_COLUMN()//第一列
{
if ((map[0][0] != -1 && map[0][1] != -1 && map[0][2] != -1) && \
((map[0][0] == map[0][1]) && (map[0][1] == map[0][2])))
return true;
else
return false;
}
bool TWO_COLUMN()//第二列
{
if ((map[1][0] != -1 && map[1][1] != -1 && map[1][2] != -1) && \
((map[1][0] == map[1][1]) && (map[1][1] == map[1][2])))
return true;
else
return false;
}
bool THREE_COLUMN() //第三列
{
if ((map[2][0] != -1 && map[2][1] != -1 && map[2][2] != -1) && \
((map[2][0] == map[2][1]) && (map[2][1] == map[2][2])))
return true;
else
return false;
}
bool LEFT_OBLIQUE()//左斜角
{
if ((map[0][0] != -1 && map[1][1] != -1 && map[2][2] != -1) && \
((map[0][0] == map[1][1]) && (map[1][1] == map[2][2])))
return true;
else
return false;
}
bool RIGHT_OBLIQUE()//右斜角
{
if ((map[2][0] != -1 && map[1][1] != -1 && map[0][2] != -1) && \
((map[2][0] == map[1][1]) && (map[1][1] == map[0][2])))
return true;
else
return false;
}
};
class Battle :public Map {//游戏对战类
public:
void menu();//主页面菜单
void setbutton();//游戏页面菜单
void resetGame();//重整游戏参数
void System();//系统函数
virtual bool End() { return 0; };//游戏结束
virtual void button(){};//按钮
};
class Playerbattle :public Battle {//双人玩家对战类
public:
void playerbattle();//对战函数
void detectMouse(MOUSEMSG& mouse, IMAGE* img);// 检查鼠标事件
bool End();//游戏结束
void button();//按钮
};
class Computerbattle :public Battle {//人机对战类
public:
void computerbattle();//人机对战函数
void computermove(IMAGE* img);//电脑下棋
bool End();//游戏结束
void button();//按钮
};
int main()
{
Battle S;
S.System();
system("pause");
return 0;
}
void Battle::System()//系统函数
{
Playerbattle P;
P.menu();
Computerbattle C;
MOUSEMSG m;//这个结构体用于保存鼠标消息
while (1) {
m = GetMouseMsg();
//进入玩家对战页面
if (m.x >= 50 && m.x <= 250 && m.y >= 50 && m.y <= 130) {
setlinecolor(WHITE);
rectangle(50, 50, 250, 130);
if (m.uMsg == WM_LBUTTONDOWN) { //如果按下鼠标左键实现相应功能.玩家对战
cleardevice();
P.playerbattle();//玩家对战函数调用
break;
}
}
//进入人机对战页面
else if (m.x >= 50 && m.x <= 250 && m.y >= 160 && m.y <= 230) {
setlinecolor(WHITE);
rectangle(50, 160, 250, 230);
if (m.uMsg == WM_LBUTTONDOWN) { //如果按下鼠标左键实现相应功能.人机对战
cleardevice();
C.computerbattle();//人机函数对战
break;
}
}
//音乐开
else if (m.x >= 75 && m.x <= 135 && m.y >= 300 && m.y <= 330)
{
setlinecolor(WHITE);
rectangle(75, 300, 135, 330);
if (m.uMsg == WM_LBUTTONDOWN) {
mciSendString(_T("play 为你我受冷风吹.mp3 repeat"), 0, 0, 0);
System();
break;
}
}
else if (m.x >= 160 && m.x <= 220 && m.y >= 300 && m.y <= 330)//关音乐
{
setlinecolor(WHITE);
rectangle(160, 300, 220, 330);
if (m.uMsg == WM_LBUTTONDOWN)
{
mciSendString(_T("close 为你我受冷风吹.mp3"), 0, 0, 0);
System();
break;
}
}
else { //当鼠标不在目标位置就覆盖之前的蓝色边框.
setlinecolor(CYAN);
rectangle(50, 50, 250, 130);
rectangle(50, 160, 250, 230);
rectangle(75, 300, 135, 330);
rectangle(160, 300, 220, 330);
}
}
}
void Battle::menu()//菜单
{
//MOUSEMSG m;//这个结构体用于保存鼠标消息
initgraph(300, 400);// 初始化绘图窗口
setbkcolor(WHITE);// 设置背景色为白色
cleardevice();// 用背景色清空屏幕
IMAGE background;
loadimage(&background, "111.png", 300, 400, 1);//设置背景图
putimage(0, 0, &background);
setfillcolor(CYAN);
solidrectangle(50, 50, 250, 130);//玩家对战
solidrectangle(50, 160, 250, 230);//人机对战
solidrectangle(75, 300, 135, 330);//音乐开
solidrectangle(160, 300, 220, 330);//音乐关
settextstyle(36, 0, "微软黑体");
setbkmode(TRANSPARENT);
outtextxy(80, 70, "玩家对战");
outtextxy(80, 180, "人机对战");
settextcolor(BLACK);
settextstyle(20, 9, "微软黑体");
outtextxy(20, 350, "@毛成宽-赵世伟 联合开发");
outtextxy(80, 305, "音乐开");
outtextxy(165, 305, "音乐关");
IMAGE musictip2;
loadimage(&musictip2, "音乐符2.png");
putimage(120, 285, &musictip2, SRCAND);
//mciSendString(_T("play 为你我受冷风吹.mp3 repeat"), 0, 0, 0);
}
void Playerbattle::playerbattle()
{
setbutton();
initChessboard(); //画棋盘
IMAGE img[2];
MOUSEMSG mouse;
loadimage(&img[0], _T("o.png"), 0, 0, true); //加载资源图
loadimage(&img[1], _T("x.png"), 0, 0, true);
winner = 0;
while (true)
{
// 检测鼠标位置, 走棋
detectMouse(mouse, img);
End();
}
}
void Battle::setbutton()
{
setfillcolor(CYAN);
solidrectangle(0, 300, 150, 400);//主菜单方框
solidrectangle(150, 300, 300, 400);//再来一局方框
setlinecolor(BLUE);//设置边框的风格和颜色
setlinestyle(PS_SOLID, 1);
rectangle(0, 300, 150, 399);
rectangle(150, 300, 299, 399);
settextstyle(36, 0, "微软黑体");
settextcolor(WHITE);
setbkmode(TRANSPARENT);
outtextxy(10, 330, "主菜单");
outtextxy(155, 330, "再来一局");
}
void Playerbattle::detectMouse(MOUSEMSG& mouse, IMAGE* img)//检测鼠标
{
int front = -1;
mouse = GetMouseMsg();// 获取鼠标信息
if (mouse.x >= 0 && mouse.x <= 300 && mouse.y >= 0 && mouse.y <= 300)
{
x = mouse.x / 100;
y = mouse.y / 100;
if (mouse.uMsg == WM_LBUTTONDOWN && map[x][y] == -1) //判断鼠标是否左键按下
{
front = map[x][y]; //记录当前位置的值
map[x][y] = camp; //鼠标点击的区域
if (front == -1) //-1代表空位, 即可以在当前位置下棋 front == -1
{
putimage(x * 100 + 5, y * 100 + 5, &img[map[x][y]]);
(camp == 1) ? camp = 0 : camp = 1; //交换阵营
}
}
}
if (mouse.x >= 0 && mouse.x <= 150 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
resetGame();
System();
}
}
if (mouse.x >= 150 && mouse.x <= 300 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
cleardevice();
resetGame();
playerbattle();
}
}
}
void Battle::resetGame()//重整游戏参数
{
// 擦除界面,重置map值
setfillcolor(WHITE);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
map[i][j] = -1;
solidrectangle(i * 100 + 5, j * 100 + 5, i * 100 + 95, j * 100 + 95);
}
}
camp = 0;
}
bool Playerbattle::End()
{
setlinecolor(YELLOW); //设置线的颜色
setlinestyle(PS_SOLID, 5); //设置线的风格
settextcolor(BLACK);
if ((resWin[0] = ONE_ROW()) || (resWin[1] = TWO_ROW()) || (resWin[2] = THREE_ROW()))
{
if (resWin[0] == true)
line(5, 45, 295, 45);
else if (resWin[1] == true)
line(5, 155, 295, 155);
else if (resWin[2] == true)
line(5, 245, 295, 245);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
else if ((resWin[0] = ONE_COLUMN()) || (resWin[1] = TWO_COLUMN()) || (resWin[2] = THREE_COLUMN()))
{
if (resWin[0] == true)
line(45, 5, 45, 295);
else if (resWin[1] == true)
line(155, 5, 155, 295);
else if (resWin[2] == true)
line(245, 5, 245, 295);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
else if ((resWin[0] = LEFT_OBLIQUE()) || (resWin[1] = RIGHT_OBLIQUE()))
{
if (resWin[0] == true)
line(5, 5, 295, 295);
else if (resWin[1] == true)
line(295, 5, 5, 295);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
// 判断是否还有空位
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (map[i][j] == -1) return false;
outtextxy(110, 140, "平局!");
button();
return true;
}
void Playerbattle::button()
{
MOUSEMSG mouse;
while (1)
{
mouse = GetMouseMsg();
if (mouse.x >= 0 && mouse.x <= 150 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
resetGame();
System();
}
}
if (mouse.x >= 150 && mouse.x <= 300 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
cleardevice();
resetGame();
playerbattle();
}
}
}
}
void Computerbattle::computerbattle() {//人机对战页面
setbutton();
initChessboard(); //画棋盘
IMAGE img[2];
MOUSEMSG mouse;
loadimage(&img[0], _T("o.png"), 0, 0, true); //加载资源图
loadimage(&img[1], _T("x.png"), 0, 0, true);
mouse = GetMouseMsg();
while (End() != true)
{
mouse = GetMouseMsg(); // 获取鼠标信息
if (mouse.x >= 0 && mouse.x <= 300 && mouse.y >= 0 && mouse.y <= 300)
{
int front = -1;
x = mouse.x / 100;
y = mouse.y / 100;
winner = 0;
if (mouse.uMsg == WM_LBUTTONDOWN && map[x][y] == -1) //判断鼠标是否左键按下
{
front = map[x][y]; //记录当前位置的值
map[x][y] = camp; //鼠标点击的区域
if (front == -1) //-1代表空位, 即可以在当前位置下棋 front == -1
{
putimage(x * 100 + 5, y * 100 + 5, &img[map[x][y]]);
(camp == 1) ? camp = 0 : camp = 1; //交换阵营
if (End()) //判断是否结束游戏了
{
Sleep(500);
cleardevice(); //清除窗口
resetGame(); //重置游戏的参数
cleardevice(); //清除窗口
System(); //返回主界面
}
else {//电脑下棋
winner = 1;
front = -1;
while (1) {
srand((int)time(NULL));
x = rand() % 3;
y = rand() % 3;
if (map[x][y] == -1) //判断鼠标是否左键按下
{
front = map[x][y]; //记录当前位置的值
map[x][y] = camp; //鼠标点击的区域
if (front == -1) //-1代表空位, 即可以在当前位置下棋 front == -1
{
putimage(x * 100 + 5, y * 100 + 5, &img[map[x][y]]);
(camp == 1) ? camp = 0 : camp = 1; //交换阵营
break;
}
}
}
if (End()) //判断是否结束游戏了
{
Sleep(500);
cleardevice(); //清除窗口
resetGame(); //重置游戏的参数
cleardevice(); //清除窗口
System();
}
}
}
}
}
if (mouse.x >= 0 && mouse.x <= 150 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
resetGame();
System();
}
}
if (mouse.x >= 150 && mouse.x <= 300 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
cleardevice();
resetGame();
computerbattle();
}
}
}
}
bool Computerbattle::End()
{
setlinecolor(YELLOW); //设置线的颜色
setlinestyle(PS_SOLID, 5); //设置线的风格
settextcolor(BLACK);
if ((resWin[0] = ONE_ROW()) || (resWin[1] = TWO_ROW()) || (resWin[2] = THREE_ROW()))
{
if (resWin[0] == true)
line(5, 45, 295, 45);
else if (resWin[1] == true)
line(5, 155, 295, 155);
else if (resWin[2] == true)
line(5, 245, 295, 245);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
else if ((resWin[0] = ONE_COLUMN()) || (resWin[1] = TWO_COLUMN()) || (resWin[2] = THREE_COLUMN()))
{
if (resWin[0] == true)
line(45, 5, 45, 295);
else if (resWin[1] == true)
line(155, 5, 155, 295);
else if (resWin[2] == true)
line(245, 5, 245, 295);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
else if ((resWin[0] = LEFT_OBLIQUE()) || (resWin[1] = RIGHT_OBLIQUE()))
{
if (resWin[0] == true)
line(5, 5, 295, 295);
else if (resWin[1] == true)
line(295, 5, 5, 295);
switch (winner)
{
case 0:
outtextxy(110, 140, "你赢了!");
button();
break;
case 1:
outtextxy(110, 140, "你输了!");
button();
break;
default:
break;
}
return true;
}
// 判断是否还有空位
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (map[i][j] == -1) return false;
outtextxy(110, 140, "平局!");
button();
return true;
}
void Computerbattle::button()
{
MOUSEMSG mouse;
while (1)
{
mouse = GetMouseMsg();
if (mouse.x >= 0 && mouse.x <= 150 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
resetGame();
System();
}
}
if (mouse.x >= 150 && mouse.x <= 300 && mouse.y >= 300 && mouse.y <= 400)
{
if (mouse.uMsg == WM_LBUTTONDOWN)
{
cleardevice();
resetGame();
computerbattle();
}
}
}
}
void Computerbattle::computermove(IMAGE* img) {
int front = -1;
while (1) {
srand((int)time(NULL));
x = rand() % 3;
y = rand() % 3;
if (map[x][y] == -1) //判断鼠标是否左键按下
{
front = map[x][y]; //记录当前位置的值
map[x][y] = camp; //鼠标点击的区域
if (front == -1) //-1代表空位, 即可以在当前位置下棋 front == -1
{
putimage(x * 100 + 5, y * 100 + 5, &img[map[x][y]]);
(camp == 1) ? camp = 0 : camp = 1; //交换阵营
break;
}
}
}
}