设计思路:
棋盘设计为15×15格,初始状态光标在棋盘的中央,白棋先走,轮流落子,当一方连成五子或下满棋盘时,游戏结束(连成五子的一方获胜,下满棋盘为和棋)。当游戏一方胜利后显示胜利信息,提示信息利用汉字点阵输出。
程序游戏是一个二维平面图,可用二维数组来实现,数组两个下标可以表示棋盘上的位置,数组元素的值代表棋格上的状态,共有三种情况,分别是0代表空格,1代表白棋,2代表黑棋。程序的主要工作是接收棋手按键操作,棋手1用设定四个键控制光标移动,回车键表示落子。棋手2用另四个键控制光标移动,空格键表示落子。接收到回车键或空格键,说明棋手落子,先判断是否是有效位置,即有棋子的位置不能重叠落子。落子成功后,马上判断以该位置为中心的八个方向:上、下、左、右、左上、左下、右上、右下是否有相同颜色的棋子连成五子,如果连成五子,则游戏结束。
#include
#include
#include
#pragma comment(lib, "WINMM.LIB")
#include
#include
#include
#define width 32 //棋盘总宽度
#define high 31 //棋盘总高度
#define MAX_X 15 //棋盘横向格子数
#define MAX_Y 15 //棋盘纵向格子数
#define WIDTH (16+width) //游戏总高度
#define HIGH (high+4) //游戏总高度
#define player1 1 //白子玩家
#define player2 2 //黑子玩家
#define emptyPlayer 0//无子
#define Unplayer -2 //中途退出游戏,无玩家获胜
typedef struct Stack{
//记录下每次落子的坐标
int x[MAX_X*MAX_Y];
int y[MAX_X*MAX_Y];
//相当于栈顶指针
int top;
}Stack;
int pos[MAX_X][MAX_Y];//存储棋盘上各位置处的状态 比如有白子为1, 有黑子为2,无子为0
int px,py; //光标位置
int player = 1;//记录当前玩家 默认玩家从白方开始
int flag1 = 0;//标志游戏开始
int gameOver_player = -1;//判断结束的标志
int pre_px = -1, pre_py = -1;//记录下上一次的坐标位置
void gotoxy(int x,int y);//设置CMD窗口光标位置
void hide_cursor(); //隐藏CMD窗口光标
void map();//打印地图
void game_Description();//打印动态游戏说明
void initMapState();//初始化游戏位置数据
void mapState(int qizi);//数组记录下对应位置的状态
int isGoPlay();//判断是否可以落子
int hasGoPlay(int Player);//以落子处为中心,判断已经落子后的棋盘是否五子相连
void goPlay(int Player, Stack* p);//落子 Player 1 2 0
void yiDongKuang();//移动框
void player1_move();//玩家1_移动
void player2_move();//玩家2_移动
int gameOver();//判断游戏是否结束
Stack* createStack();//创建空栈
void push(Stack* p, int x, int y);//入栈
void color(const unsigned short textColor);//自定义函根据参数改变颜色
//void setColor(unsigned short backColor);//设置游戏背景颜色
void gotoxy(int x,int y)//设置CMD窗口光标位置
{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void hide_cursor() //隐藏CMD窗口光标
{
CONSOLE_CURSOR_INFO cci;
cci.bVisible = FALSE;
cci.dwSize = sizeof(cci);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cci);
}
void color(const unsigned short textColor) //自定义函根据参数改变颜色
{
if(textColor>0 && textColor<=15) //参数在0-15的范围颜色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), textColor); //用一个参数,改变字体颜色
else //默认的字体颜色是白色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
//打印地图
void map()
{
int x, y;
color(02);
/*开始打印棋盘格子*/
//打印横向图格
for(y = 2; y < high - 1; y+=2){
for(x = 1; x < width - 2; x+=2){
gotoxy(x , y);
printf("-+");
}
}
//打印竖向图格
for(y = 1; y < high; y += 2) {
for(x = 2; x < width-2; x += 2){
gotoxy(x , y);
printf("|");
}
}
/*打印棋盘格子结束*/
/*开始打印图框*/
//打印棋盘图框
for (y = 0; y < high; y++)
{
for (x = 0; x < width; x+=2)
{
if (x == 0 || x == width - 2){
gotoxy(x, y);
printf("|");
}
if(y == 0 || y == high - 1 ){
gotoxy(x, y);
printf("--");
}
}
}
//打印右图框
for(y = 0; y < high; y++){
for(x = width; x < WIDTH; x+=2){
if (x == WIDTH - 2){
gotoxy(x, y);
printf("|");
}
if(y == 0 || y == high - 1 ){
gotoxy(x, y);
printf("--");
}
}
}
//打印下图框 y->high ~ HiGH-1 x->0 ~ WIDTH-2
for(y = high; y < HIGH; y++){
for(x = 0; x < WIDTH; x+=2){
if (x == 0 || x == WIDTH - 2){
gotoxy(x, y);
printf("|");
}
if(y == high || y == HIGH - 1 ){
gotoxy(x, y);
printf("--");
}
}
}
//打印下图框内容
gotoxy( 1, high+1);
printf(" 欢迎来到五子棋游戏!");
//打印右图框内容
gotoxy( width-1, 1);
printf("游戏说明:");
gotoxy( width-1, 3);
printf("1)X表示玩家一棋");
gotoxy( width-1, 4);
printf("子,而O表示玩家");
gotoxy( width-1, 5);
printf("二棋子");
gotoxy( width-1, 7);
printf("2)X先、O后,交替");
gotoxy( width-1, 8);
printf("下子,每次只能下");
gotoxy( width-1, 9);
printf("一子");
gotoxy( width-1, 11);
printf("3)棋子下在棋盘");
gotoxy( width-1, 12);
printf("的格子内,棋子下");
gotoxy( width-1, 13);
printf("定后,不得向其它");
gotoxy( width-1, 14);
printf("点移动");
gotoxy( width-1, 16);
printf("4)最先连成五子");
gotoxy( width-1, 17);
printf("的一方即为获胜");
gotoxy( width-1, 19);
printf("玩家一 移动键:");
gotoxy( width-1, 20);
printf("w上 s下 a左 d右");
gotoxy( width-1, 21);
printf("下子: 空格键");
gotoxy( width-1, 23);
printf("玩家二 移动键:");
gotoxy( width-1, 24);
printf("i上 k下 j左 l右");
gotoxy( width-1, 25);
printf("下子:回车键");
gotoxy( width+1, 27);
color(4);
printf("退出键: Y");
gotoxy( width+1, 29);
color(6);
printf("悔棋键: G");
/*打印图框结束*/
color(02);
/*打印棋盘上的四个标记点*/
gotoxy( 3*2+2 , 3*2+2);
printf("*");
gotoxy( (MAX_X-4)*2 , 3*2+2);
printf("*");
gotoxy( 3*2+2 , (MAX_Y-4)*2);
printf("*");
gotoxy( (MAX_X-4)*2 , (MAX_Y-4)*2);
printf("*");
/*打印结束*/
/*开始修边*/
/*gotoxy(width - 1, 0);
printf(" ");
gotoxy(width - 1, high - 1);
printf(" ");*/
/*修边结束*/
/*打印地图完成*/
}
//打印动态游戏说明
void game_Description()
{
//打印下图框内容
gotoxy( 1, high+1);
printf(" ");
if(player == player1){
gotoxy( 1, high+1);
color(2);
printf(" 玩家一下棋中...");
}else if(player == player2){
gotoxy( 1, high+1);
color(2);
printf(" 玩家二下棋中...");
}
}
//初始化游戏位置数据
void initMapState()
{
for(int i = 0 ; i < MAX_Y; i++){
for(int j = 0; j < MAX_X; j++){
pos[i][j] = 0;//初始状态全为空
}
}
//注意 光标的位置和存储在数组中的位置是不同的
px = 7;
py = 7;
gotoxy(py*2+1,px*2+1);//初始位置
}
//数组记录下对应位置的状态
void mapState(int qizi)
{
//2*px+1 = x //2*py+1 = y //px->0~14 //py->0~14
if(px >= MAX_X || px < 0 || py >= MAX_Y || py < 0)
return;//其他情况不可以记录状态
pos[px][py] = qizi;
}
//判断是否可以落子
int isGoPlay()
{
if(px >= MAX_X || px < 0 || py >= MAX_Y || py < 0)
return 0;//其他情况不可以记录状态
if(pos[px][py] == emptyPlayer){
//说明无子
return 1;
}else{
//说明有子
return 0;
}
}
//以落子处为中心,判断已经落子后的棋盘是否五子相连
int hasGoPlay(int Player)
{
//分为两部分,先记录一部分的相同Player的个数
//再记录下另余部分的个数,相加为相连棋子总个数
int port1 = 0, port2 = 0;
int x, y, count;
//上下查找
x = px, y = py-1;
while(pos[x][y]==Player && y >= 0){
++port1;//上部分个数
--y;//上移
}
y = py+1;
while(pos[x][y]==Player && y < MAX_Y){
++port2;//下部分个数
++y;//下移
}
//计算总数
count = port1 + port2 + 1;
if(count >= 5) return 1;
//左右查找
port1 = 0, port2 = 0;
x = px-1, y = py;
while(pos[x][y]==Player && x >= 0){
++port1;//上部分个数
--x;//左移
}
x = px+1;
while(pos[x][y]==Player && x < MAX_X){
++port2;//下部分个数
++x;//右移
}
//计算总数
count = port1 + port2 + 1;
if(count >= 5) return 1;
//左上右下查找
port1 = 0, port2 = 0;
x = px-1, y = py-1;
while(pos[x][y]==Player && x >= 0 && y >= 0){
++port1;//上部分个数
--x;//左移
--y;//上移
}
x = px+1, y = py+1;
while(pos[x][y]==Player && x < MAX_X && y < MAX_Y){
++port2;//下部分个数
++x;//右移
++y;//下移
}
//计算总数
count = port1 + port2 + 1;
if(count >= 5) return 1;
//右上左下查找
port1 = 0, port2 = 0;
x = px+1, y = py-1;
while(pos[x][y]==Player && x < MAX_X && y >= 0){
++port1;//上部分个数
++x;//左移
--y;//上移
}
x = px-1, y = py+1;
while(pos[x][y]==Player && x >= 0 && y < MAX_Y){
++port2;//下部分个数
--x;//右移
++y;//下移
}
//计算总数
count = port1 + port2 + 1;
if(count >= 5) return 1;
return 0;
}
//落子 Player 1 2 0
void goPlay(int Player, Stack* p)
{
if(isGoPlay()){
//说明可以落子
mapState(Player);//将对应位置的情况记录在数组中
if(hasGoPlay(Player)){
//如果五子相连,则 gameover
gameOver_player = Player; //记录此刻胜利玩家,结束游戏
}
/*入栈*/
push(p, px, py);
/*角色切换*/
if(Player == player1){
player = player2;//切换成玩家1
gotoxy(px*2+1, py*2+1 );//将光标移动到对应位置
color(07);
printf("X");//打印玩家1
game_Description();// 动态说明
}else if(Player == player2){
player = player1;//切换成另一个玩家2
gotoxy( px*2+1, py*2+1);//将光标移动到对应位置
color(07);
printf("O");//打印玩家2
game_Description();// 动态说明
}
}
}
//入栈
void push(Stack* p, int x, int y)
{
//将此刻的坐标入栈
int top = p->top;
++p->top;//移动栈针
p->x[top] = x;
p->y[top] = y;
return;
}
//出栈
void pop(Stack* p)
{
int x, y;
//出栈,移动栈顶指针
//如果栈为空,则不弹出
if(p->top <= 0) return;
--p->top;
int top = p->top;
//记录下弹出的位置
x = p->x[top];
y = p->y[top];
//在弹出位置打印空格
gotoxy(x*2+1, y*2+1);
printf(" ");
//抹除记录
pos[x][y] = 0;
}
//移动框
void yiDongKuang()
{
//打印移动框
gotoxy(px*2, py*2+1);
color(11);
printf("[");
gotoxy(px*2+2, py*2+1);
printf("]");
//打印移动框结束
if(pre_px != -1 && pre_py != -1){
if(pre_px > px && pre_py == py){
//当向左移动时
//将上一个位置的右边保持原样
gotoxy(pre_px*2+2, pre_py*2+1);
color(2);
printf("|");
}else if(pre_px < px && pre_py == py){
//当向右移动时
//将上一个位置的左边保持原样
gotoxy(pre_px*2, pre_py*2+1);
color(2);
printf("|");
}else{
//当向上下移动时
//将上一个位置的左右边保持原样
gotoxy(pre_px*2+2, pre_py*2+1);
color(2);
printf("|");
gotoxy(pre_px*2, pre_py*2+1);
color(2);
printf("|");
}
}
pre_px = px;
pre_py = py;
}
//玩家1 移动
void player1_move(Stack* p)
{
char key;
if (_kbhit())//检测是否按键
{
fflush(stdin);
key = _getch();//保存按键
game_Description();//动态说明
}
switch(key)
{
case 'w': py--;yiDongKuang();break;//上
case 'a': px--;yiDongKuang();break;//左
case 'd': px++;yiDongKuang();break;//右
case 's': py++;yiDongKuang();break;//下
case ' ': goPlay(player1, p);break;//落子
case 'y': gameOver_player = -2; gameOver();//退出游戏
case 'Y': gameOver_player = -2; gameOver();//退出游戏
case 'g': pop(p); pop(p); break;//悔棋
case 'G': pop(p); pop(p); break;//悔棋
default: break;
}
//限制光标范围
if(py < 0) py = MAX_Y-1;
else if(py >= MAX_Y) py = 0;
else if(px < 0) px = MAX_X-1;
else if(px >= MAX_X) px = 0;
gotoxy(2*py+1, 2*px+1);
}
//玩家2 移动
void player2_move(Stack* p)
{
char key;
if (_kbhit())//检测是否按键
{
fflush(stdin);
key = _getch();//保存按键
game_Description();//动态说明
}
switch(key)
{
case 'i': py--;yiDongKuang();break;//上
case 'j': px--;yiDongKuang();break;//左
case 'l': px++;yiDongKuang();break;//右
case 'k': py++;yiDongKuang();break;//下
case '\r': goPlay(player2, p);break;//落子
case 'y': gameOver_player = -2; gameOver();//退出游戏
case 'Y': gameOver_player = -2; gameOver();//退出游戏
case 'g': pop(p); pop(p); break;//悔棋
case 'G': pop(p); pop(p); break;//悔棋
default: break;
}
//限制光标范围
if(py < 0) py = MAX_Y-1;
else if(py >= MAX_Y) py = 0;
else if(px < 0) px = MAX_X-1;
else if(px >= MAX_X) px = 0;
gotoxy(2*px+1, 2*py+1);
}
//创建空栈
Stack* createStack(){
//申请空间
Stack* p = (Stack* )malloc(sizeof(Stack));
//如果未申请到空间
if(p==NULL) return NULL;
p->top = 0;//初始化栈顶
return p;
}
//判断游戏是否结束
int gameOver()
{
//gamerOver_player -1 表示继续游戏 1 表示白方胜利 2 表示黑方胜利 0 表示平局
//五子相连 一方取胜 y->high ~ HiGH-1 x->0 ~ WIDTH-2
if(gameOver_player == -1){
return 1;
}else if(gameOver_player == player1){
//白方胜利
gotoxy( 1, high+1);
printf("玩家1胜利!!!");
return 0;
}else if(gameOver_player == player2){
//黑方胜利
gotoxy( 1, high+1);
printf("玩家2胜利!!!");
return 0;
}else if(gameOver_player == emptyPlayer){
//棋盘满棋 平局
gotoxy( 1, high+1);
printf("平局!!!");
return 0;
}else if(gameOver_player == Unplayer){
//中途退出
gotoxy( 1, high+1);
printf("正在退出游戏中...");
exit(1);
}
return 1;
}
int main(){
//调整游戏框
system("mode con cols=48 lines=35");
//打印地图
map();
//初始化游戏位置数据
initMapState();
//创建空栈
Stack* p = createStack();
//隐藏光标
hide_cursor();
//游戏循环 控制移动
while(gameOver()){
//不断调换人物
if(player == player1)
player1_move(p);// 切换玩家1
else if(player == player2)
player2_move(p);// 切换玩家2
}
free(p);
}