第一次自己写这样的大作业,代码风格真的很重要。例如函数的封装性很重要,不然后期很容易无法维护,加一点功能就容易出一堆bug~
在这边完成了简易的图形化,并且其中可以选择人数,自带一个电脑玩家(还挺容易让电脑玩家赢得哈哈哈哈哈哈)写电脑玩家的时候就是按照我自己玩的思维方式写的~还挺有趣的,欢迎尝试。
head.h
#pragma once
#ifndef HEAD_H
#define HEAD_H
#include <conio.h>
#include<graphics.h>
#include <cstdlib>
#include <ctime>
#include<windows.h>
#include<mmsystem.h>
//#pragma comment (lib,"winmm.lib")
class Plane{
public:
int x; //飞机的x坐标
int y; //飞机的y坐标
void init_plane(int player); //初始化飞机的坐标
void draw_plane(int player); //画出飞机的图像
};
Plane player_plane[4][4]; //每个玩家各有四架飞机
int go_pos[4][4] = { {0,0,0,0}, //这个数组记录每架飞机的坐标(也就是所在格子的位置)
{0,0,0,0},
{0,0,0,0},
{0,0,0,0} }; //这个数组用于标记该飞机是否到达终点,若到达终点 那么就不再动这架飞机
int get_end[4][4] = { {0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0} };
Plane plane;
int player = 4; //记录玩家的个数
int num = 0; //记录每轮得到的数字
int turn = 0; //记录摇了多少次骰子
int pos_x = 0; //记录要移动的位置的x坐标
int pos_y = 0; //记录要移动的位置的y坐标
int now_i = -1; //记录当前要移动的飞机是哪一位玩家的
int now_j = -1; //记录当前要移动该玩家的哪一架飞机
int tmp_j = 0; //存储上一轮AI启动的飞机(为第j架)
int flag_3 = 0; //判断上一次AI是否启动了飞机 若启动了飞机则这次只能移动这个飞机
int winner = -1;
bool sixpts = false;
char AInum = ' ';
void Plane::init_plane(int player)//初始化飞机坐标
{
switch (player)
{
case 4:
{
int idx = 0;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
{
player_plane[3][idx].x = 50 + i * 50;
player_plane[3][idx].y = 100 + j * 50;
idx++;
}
}
case 3:
{
int idx = 0;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
{
player_plane[2][idx].x = 50 + i * 50;
player_plane[2][idx].y = 250 + j * 50;
idx++;
}
}
case 2:
{
int idx = 0;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
{
player_plane[1][idx].x = 50 + i * 50;
player_plane[1][idx].y = 400 + j * 50;
idx++;
}
}
case 1:
{
int idx = 0;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
{
player_plane[0][idx].x = 50 + i * 50;
player_plane[0][idx].y = 550 + j * 50;
idx++;
}
}
}
}
void Plane::draw_plane(int player) //画出飞机
{
for (int i = 0; i < 4; i++)
{
setfillcolor(GREEN);
solidcircle(player_plane[0][i].x, player_plane[0][i].y, 25);
rectangle(75, 575, 175, 675);
setfillcolor(RED);
solidcircle(player_plane[1][i].x, player_plane[1][i].y, 25);
rectangle(75, 425, 175, 525);
if (player >= 3)
{
setfillcolor(YELLOW);
solidcircle(player_plane[2][i].x, player_plane[2][i].y, 25);
rectangle(75, 275, 175, 375);
}
if (player == 4)
{
setfillcolor(BLUE);
solidcircle(player_plane[3][i].x, player_plane[3][i].y, 25);
rectangle(75, 125, 175, 225);
}
}
}
class Board {
public:
void init_draw(int player); //画出基础的格子
void draw_button(); //画出Board上的按钮
void draw_event(); //画出Board上的event框架
};
void Board::draw_event() //画出时间对话框 各种事件的显示 放在Setting的show_event里
{
setlinecolor(BLACK);
rectangle(250, 100, 800, 300);
}
void Board::draw_button() //画出两个按钮以及上面的文字
{
setfillcolor(BLACK);
setlinecolor(BLACK);
for (int i = 1; i <= 4; i++)
rectangle(200 + i * 100,600, 300 + i * 100, 700);
rectangle(800, 550, 1000, 750);
//在button上显示文字
setbkmode(TRANSPARENT);
settextstyle(70, 50, "Comic Sans MS");
settextcolor(RED);
outtextxy(325, 615, '2');
settextcolor(GREEN);
outtextxy(425, 615, '3');
settextcolor(BLUE);
outtextxy(525, 615, '4');
settextcolor(BLACK);
settextstyle(45, 20, "Comic Sans MS");
outtextxy(610, 605, "RE");
outtextxy(610, 645, "SET");
settextstyle(55, 25, "Comic Sans MS");
outtextxy(820, 570, "PUSH");
outtextxy(820, 620, "TO");
outtextxy(820, 670, "ROLL");
}
void Board::init_draw(int player) //画出赛道
{
setbkcolor(WHITE);
cleardevice();
setlinecolor(BLACK);
switch (player)
{
case 4: setlinecolor(BLUE);
for (int i = 1; i <= 5; i++)
rectangle(1000+ i * 50, 220, 1050 + i * 50, 160); //终点的五个格子
case 3:setlinecolor(YELLOW);
for (int i = 1; i <= 5; i++)
rectangle(1000 + i * 50, 370, 1050 + i * 50, 310);
case 2:setlinecolor(RED);
for (int i = 1; i <= 5; i++)
rectangle(1000 + i * 50, 520, 1050 + i * 50, 460);
case 1:setlinecolor(GREEN);
for (int i = 1; i <= 5; i++)
rectangle(1000 + i * 50, 670, 1050 + i * 50, 610);
}
setlinecolor(BLACK);
for (int i = 1; i <= 15; i++)
rectangle(200 + i * 50, 365, 250 + i * 50, 465);
}
class Setting :public Board {
public:
bool state = false; //记录游戏启动状态(在未选择玩家以及出现胜利者时为false)
bool button_state = true; //记录button的状态(即是否可以进行点击)使得在一些操作未完成之前不能再点击button
char get_rand(); //获取范围为1~6的随机数
void Get_msg(); //用于获取鼠标点击的event
void Outcome(); //画出这一轮投出的点数
void get_pos(); //重新绘制飞机 (在更新飞机坐标后进行绘制)
void record_pos(int pos, int i, int j); //记录每个格子对应的坐标
bool choose_plane(int x, int y); //在玩家投出点数后,获取鼠标点击的是哪一架飞机
bool plane_state(); //用于判断该玩家是否还有停留在机场的飞机
void get_back(); //将其他飞机踩回机场
bool win(); //判断是否有玩家胜利
void reset_game(); //重置游戏 初始化各个数据和状态
void draw_circle(); //画出圆圈
void show_event(); //画出event框架上的事件
void ai_player(); //封装ai player的下棋基本逻辑 以及各种操作的优先级
bool isback(int pos); //判断 ai player能否把别人踩回机场
void showAInum(); //画出上一轮ai投出的点数
//void backgroudMusic(); //加上游戏的BGM 但未成功
};
void Setting::showAInum()
{
//if (turn % player == 0)
// AInum = num + '0';
if (state)
{
settextstyle(60, 30, "Comic Sans MS");
settextcolor(GREEN);
outtextxy(250, 35, "AI Team get");
outtextxy(650, 35, AInum);
outtextxy(700, 35, "last turn");
}
}
bool Setting::isback(int pos)
{
for (int i = 1; i < player; i++)
{
for (int j = 0; j < 4; j++)
{
if (pos != 0 && pos + num < 17 && go_pos[i][j] == pos + num)
return true;
}
}
return false;
}
void Setting::ai_player()
{
//行动优先级:上一轮已经得到6点 > 无机可动 > 抵达终点 > 可踩回玩家 > 飞机可以起飞 > 不会撞墙反弹的最前方的飞机行动(反弹收益较低)
if (state && turn % player == 0)
{
cleardevice();
init_draw(player);
draw_event();
draw_button();
showAInum();
button_state = false;
Outcome(); //这一部分是为了在界面上做出显示
//_getch();
int flag_1 = 0;
int flag_2 = 0; //判断是否还有下一步不会被反弹的飞机
int max_idx = 0;//记录的是离终点最近的飞机的下标
int judge[4] = { 0,0,0,0 }; //判断是否会反弹 或已经到达终点
for (int i = 0; i < 4; i++)
if (go_pos[0][i] + num > 21 || go_pos[0][i] == 21) //go_pos数组记录的是每个飞机现在的位置 num是这一轮投到的点数
judge[i] = 1;
for (int i = 0; i < 4; i++)
if (!judge[i] && go_pos[0][i]!=0)
{
flag_2 = 1;
max_idx = i;
}
//--------------------------为了找到max_idx 在下面优先级判断中用到-------------------------------------
if (flag_2)
{
for (int i = 0; i < 4; i++)
{
if (!judge[i] && go_pos[0][i] > go_pos[0][max_idx])
{
max_idx = i;
}
}
}
else
{
for (int i = 0; i < 4; i++)
{
if (go_pos[0][i] != 21)
max_idx = i;
}
for (int i = 0; i < 4; i++)
{
if (go_pos[0][i] != 21 && go_pos[0][i] > go_pos[0][max_idx])
max_idx = i;
}
}
//------------------------下面去找rest 看电脑玩家的机场中是否还有飞机 在下面优先级判断中用到--------------------------------------
int rest = -1;
for (int i = 0; i < 4; i++)
{
if (go_pos[0][i] + num == 21)
flag_1 = 1;
if (go_pos[0][i] == 0)
rest = i;
}
//------------------------下面找back_idx 判断是否可以踩回别人的飞机------------------------------------------------
int back_idx = -1;
for (int i = 0; i < 4; i++)
{
if (isback(go_pos[0][i]))
{
back_idx = i;
break;
}
}
//下面为电脑玩家移动优先级的判断
now_i = 0; //now_i = 0 代表当前为电脑玩家的回合
if (flag_3) //flag_3 用于判断上一次AI是否启动了飞机 若启动了飞机则这次只能移动这个飞机
{
flag_3 = 0;
now_j = tmp_j;
go_pos[0][now_j] += num;
get_pos();
}
else if (num != 6 && !plane_state()) //无机可动
{
turn++;
button_state = true;
}
else if (flag_1) //有飞机可以行动并且能够抵达终点
{
for (int i = 0; i < 4; i++)
{
if (go_pos[0][i] + num == 21)
{
go_pos[0][i] += num;
get_end[0][i] = 1;
now_j = i;
get_pos();
}
}
}
else if (back_idx != -1) //可以将其他玩家的飞机踩回去
{
go_pos[0][back_idx] += num;
now_j = back_idx;
get_pos();
}
else if (num == 6 && rest!= -1) //出飞机
{
go_pos[0][rest] = 1;
now_j = rest;
tmp_j = now_j;
flag_3 = 1;
get_pos();
}
else
{
now_j = max_idx;
if (go_pos[0][max_idx] + num < 21)
{
go_pos[0][max_idx] += num;
}
else
{
go_pos[0][max_idx] = 21 - (num + go_pos[0][max_idx] - 21);
}
get_pos();
}
}
}
//void Setting::backgroudMusic()
//{
// mciSendString("open ./Victory.mp3 alias BGM", 0, 0, 0);
// mciSendString("play BGM repeat", 0, 0, 0);
// if (0)
// mciSendString("clsoe BGM", 0, 0, 0);
//}
void Setting::show_event()
{
if (win())
{
settextstyle(50, 30, "Comic Sans MS");
if (winner == 0)
{
settextcolor(GREEN);
outtextxy(280, 150, "Winner :");
outtextxy(280, 200, "Green Team !");
}
else if (winner == 1)
{
settextcolor(RED);
outtextxy(280, 150, "Winner :");
outtextxy(280, 200, "Red Team !");
}
else if (winner == 2)
{
settextcolor(YELLOW);
outtextxy(280, 150, "Winner :");
outtextxy(280, 200, "Yellow Team !");
}
else if (winner == 3)
{
settextcolor(BLUE);
outtextxy(280, 150, "Winner :");
outtextxy(280, 200, "Blue Team !");
}
}
else if (state == false)
{
settextstyle(50, 30, "Comic Sans MS");
outtextxy(270, 140, "Please choose");
outtextxy(270, 200, "Number of player");
}
else if (state == true && turn % player == 0)
{
settextstyle(50, 30, "Comic Sans MS");
settextcolor(GREEN);
outtextxy(280, 150, "Turn :");
outtextxy(280, 200, "Green Team");
}
else if (state == true && turn % player == 1)
{
settextstyle(50, 30, "Comic Sans MS");
settextcolor(RED);
outtextxy(280, 150, "Turn :");
outtextxy(280, 200, "Red Team");
}
else if (state == true && turn % player == 2)
{
settextstyle(50, 30, "Comic Sans MS");
settextcolor(YELLOW);
outtextxy(280, 150, "Turn :");
outtextxy(280, 200, "Yellow Team");
}
else if (state == true && turn % player == 3)
{
settextstyle(50, 30, "Comic Sans MS");
settextcolor(BLUE);
outtextxy(280, 150, "Turn :");
outtextxy(280, 200, "Blue Team");
}
}
void Setting::reset_game()
{
for (int i = 0; i < player; i++)
for (int j = 0; j < 4; j++)
go_pos[i][j] = 0;
for (int i = 0; i < player; i++)
for (int j = 0; j < 4; j++)
get_end[i][j] = 0;
for (int i = 0; i < player; i++)
for (int j = 0; j < 4; j++)
{
record_pos(go_pos[i][j], i, j);
player_plane[i][j].x = pos_x;
player_plane[i][j].y = pos_y;
}
state = true;
turn = 0;
winner = -1;
now_i = 0;
now_j = 0;
cleardevice();
init_draw(player);
draw_event();
draw_button();
}
void Setting::draw_circle()
{
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(0, 0, 50);
}
bool Setting::win()
{
for (int i = 0; i < player; i++)
{
bool iswin = true;
for (int j = 0; j < 4; j++)
{
if (go_pos[i][j] != 21)
iswin = false;
}
if (iswin)
{
winner = i;
state = false; //胜利就让游戏停止
return true;
}
}
return false;
}
void Setting::get_back()
{
for (int i = 0; i < player; i++)
for (int j = 0; j < 4; j++)
{
if (i != now_i && go_pos[now_i][now_j] == go_pos[i][j] && go_pos[now_i][now_j] >= 2 && go_pos[now_i][now_j] <= 16)
go_pos[i][j] = 0;
}
}
bool Setting::plane_state()
{
for (int i = 0; i < 4; i++)
{
if (go_pos[turn % player][i] && go_pos[turn % player][i] != 21)
return true;
}
return false;
}
bool Setting::choose_plane(int x,int y) //鼠标点击的x,y坐标
{
now_i = turn % player;
if (sixpts) //若上次得到6点且启动在机场的飞机 则只能移动刚起飞的飞机
{
for (int j = 0; j < 4; j++)
{
if (go_pos[now_i][j] == 1 && x > player_plane[now_i][j].x - 25 && x<player_plane[now_i][j].x + 25 && y>player_plane[now_i][j].y - 25 && y < player_plane[now_i][j].y + 25)
{
now_j = j;
sixpts = false;
return true;
}
}
}
else
{
for (int j = 0; j < 4; j++)
{
if (go_pos[now_i][j] != 21)
{
if (num == 6)
{
if (x > player_plane[now_i][j].x - 25 && x<player_plane[now_i][j].x + 25 && y>player_plane[now_i][j].y - 25 && y < player_plane[now_i][j].y + 25)
{
if(go_pos[now_i][j] == 0)
sixpts = true; //记录上一轮投到了6 且启动了机场的飞机
now_j = j;
return true;
}
}
else
{
if (go_pos[now_i][j] != 0 && x > player_plane[now_i][j].x - 25 && x<player_plane[now_i][j].x + 25 && y>player_plane[now_i][j].y - 25 && y < player_plane[now_i][j].y + 25)
{
now_j = j;
return true;
}
}
}
}
}
return false;
}
void Setting::record_pos(int go_pos,int i,int j) //记录每个标号对应的xy坐标
{
if (go_pos == 0)
{
if (j < 2) //四个飞机编号为 0 2
pos_x = 100; // 1 3
else
pos_x = 150;
pos_y = 650 - 50 * (j % 2) - 150 * i;
}
else if (go_pos == 1)
{
pos_x = 200;
pos_y = 625 - 150 * i;
}
else if (go_pos >= 2 && go_pos <= 16)
{
pos_x = 225 + 50 * (go_pos - 1);
pos_y = 415;
}
else if (go_pos >= 17)
{
pos_x = 1075 + 50 * (go_pos - 17);
pos_y = 640 - 150 * i;
}
}
void Setting::get_pos() //使得十六架飞机都更新自己的坐标
{
if (turn % player != 0 && now_i == turn % player)
{
if (go_pos[now_i][now_j] == 0 && num == 6)
go_pos[now_i][now_j] = 1;
else if (go_pos[now_i][now_j]!=0 && go_pos[now_i][now_j] + num <= 21)
{
go_pos[now_i][now_j] += num;
if (go_pos[now_i][now_j] == 21)
get_end[now_i][now_j] = 1;
}
else if(go_pos[now_i][now_j] != 0 && !get_end[now_i][now_j])
go_pos[now_i][now_j] = 21 - (num + go_pos[now_i][now_j] - 21);
}
if (turn % player == 0)
button_state = true;
get_back();
cleardevice();
init_draw(player);
draw_event();
draw_button();
showAInum();
turn++;
if (num == 6) //如果摇到了6就再摇一次
{
turn--;
}
for (int i = 0; i < player; i++)
for (int j = 0; j < 4; j++)
{
record_pos(go_pos[i][j], i, j);
player_plane[i][j].x = pos_x;
player_plane[i][j].y = pos_y;
}
}
char Setting::get_rand()
{
num = rand() % 6 + 1;
if (turn % player == 0)
AInum = num + '0';
char str = num + '0';
return str;
}
void Setting::Outcome()
{
char str = get_rand();
if (turn % player == 0)
settextcolor(GREEN);
else if (turn % player == 1)
settextcolor(RED);
else if (turn % player == 2)
settextcolor(YELLOW);
else if (turn % player == 3)
settextcolor(BLUE);
setbkmode(TRANSPARENT);
settextstyle(100, 100, "楷体");
outtextxy(850, 150, str);
}
void Setting:: Get_msg()
{
if (MouseHit())
{
MOUSEMSG msg = GetMouseMsg();
if (msg.uMsg == WM_LBUTTONDOWN)
{
if (button_state == false && ((num != 6 && !plane_state()) || choose_plane(msg.x, msg.y)))
{
get_pos();
button_state = true;
}
//选择玩家个数
else if (msg.x >= 300 && msg.x < 400 && msg.y >= 600 && msg.y <= 700 && state == false)
{
player = 2;
cleardevice();
draw_event();
draw_button();
showAInum();
init_draw(player);
plane.init_plane(player);
state = true;
}
else if (msg.x >= 400 && msg.x < 500 && msg.y >= 600 && msg.y <= 700 && state == false)
{
player = 3;
cleardevice();
draw_event();
draw_button();
showAInum();
init_draw(player);
plane.init_plane(player);
state = true;
}
else if (msg.x >= 500 && msg.x < 600 && msg.y >= 600 && msg.y <= 700 && state == false)
{
player = 4;
cleardevice();
draw_event();
draw_button();
showAInum();
init_draw(player);
plane.init_plane(player);
state = true;
}
else if (msg.x >= 600 && msg.x <= 700 && msg.y >= 600 && msg.y <= 700)
{
reset_game();
}
else if (button_state==true && state == true && msg.x >= 800 && msg.x < 1000 && msg.y >= 550 && msg.y <= 750 )
{
cleardevice();
init_draw(player);
draw_event();
draw_button();
showAInum();
Outcome();
button_state = false;
}
}
}
}
class Game :public Setting{
public:
void run();
};
void Game::run()
{
Board board;
Setting setting;
initgraph(1500, 900);
//setting.backgroudMusic();
setbkcolor(WHITE);
cleardevice();
srand(time(0));
BeginBatchDraw();
while (true)
{
board.draw_button();
board.draw_event();
setting.ai_player();
setting.Get_msg();
plane.draw_plane(player);
setting.show_event();
setting.showAInum();
setting.draw_circle();
FlushBatchDraw();
}
EndBatchDraw();
_getch();
}
#endif
main.cpp
#include "head.h"
int main()
{
Game game;
game.run();
return 0;
}