网络对战五子棋
介绍
基于UDP的网络对战五子棋,可以在不同电脑上玩,是之前五子棋的升级版
使用说明
- 将server.c放到云服务器中(和client放在一起也能玩)
- gcc server.c -o server后运行./server
- client.c放在自己操作系统中,gcc client.c -o client后运行./client
- 先运行的client是先手,后运行的是后手,目前只能两个人玩
server代码
#include <stdio.h>
#include <getch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUF_SIZE (4096)
typedef struct sockaddr* SAP;
int main()
{
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(0 > sock)
{
perror("socket");
return EXIT_FAILURE;
}
struct sockaddr_in addr = {}, from_addr_1 = {}, from_addr_2 = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(10077);
addr.sin_addr.s_addr = inet_addr("172.20.10.3"); //172.20.10.3 47.110.244.211
socklen_t addrlen = sizeof(addr);
if(bind(sock,(SAP)&addr,addrlen))
{
perror("bind");
close(sock);
return EXIT_FAILURE;
}
char buf[BUF_SIZE] = {};
//接收先手棋手
recvfrom(sock,buf,BUF_SIZE,0,(SAP)&from_addr_1,&addrlen);
printf("%s\n",buf);
sprintf(buf,"F:你是先手,请等待下一位选手链接");
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_1,addrlen);
//等待后手棋手
recvfrom(sock,buf,BUF_SIZE,0,(SAP)&from_addr_2,&addrlen);
printf("%s\n",buf);
sprintf(buf,"B:你是后手");
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_2,addrlen);
//游戏开始
sprintf(buf,"双方就绪,3s后游戏开始");
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_1,addrlen);
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_2,addrlen);
int ret_size;
for(;;)
{
ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&from_addr_1,&addrlen);
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_2,addrlen);
recvfrom(sock,buf,BUF_SIZE,0,(SAP)&from_addr_2,&addrlen);
sendto(sock,buf,strlen(buf)+1,0,(SAP)&from_addr_1,addrlen);
}
}
client代码
#include <stdio.h>
#include <getch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//定义发送数据缓冲区
#define BUF_SIZE (4096)
//地址类型重定义
typedef struct sockaddr* SAP;
//棋盘15*15
char board[15][15] = {};
//当前正在落子角色
char piece = '@';
//落子位置
int cnt = 0,x =7,y=7;
void drop();
void show_board();
int check();
int main(int argc,const char* argv[])
{
//网络地址初始化
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(0 > sock)
{
perror("socket");
return EXIT_FAILURE;
}
struct sockaddr_in dest_addr = {};
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(10077);
dest_addr.sin_addr.s_addr = inet_addr("172.20.10.3");//172.20.10.3 47.110.244.211
socklen_t addrlen = sizeof(dest_addr);
char buf[BUF_SIZE] = {};
//初始化棋盘
for(int i = 0; i < 15; i++)
for(int j = 0;j < 15;j++)
board[i][j] = '+';
//链接服务端
sprintf(buf,"客户端%d链接",sock);
int ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
if(0 >= ret_size)
{
perror("sendto");
return EXIT_FAILURE;
}
//接收服务端信息,判断是先手/后手
ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
printf("%s\n",buf);
char flag = buf[0];
//等待游戏开始
ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
printf("%s\n",buf);
usleep(3000000);
//如果是先手,则先手落子
if(flag == 'F')
{
piece = '#';
//显示棋盘
show_board();
//落子
drop();
//发送落子地点
sprintf(buf,"%d %d",x,y);
ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
if(0 >= ret_size || 0 == strcmp(buf,"quit"))
{
perror("sendto");
return EXIT_FAILURE;
}
}
for(;;)
{
show_board();
//接收
ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
if(0 >= ret_size)
{
perror("recvfrom");
return EXIT_FAILURE;
}
sscanf(buf,"%d %d",&x,&y);
//切换成对方棋子
piece = (piece=='@')?'#':'@';
board[x][y] = piece;
//判断是否游戏结束
if(check(1,0) || check(0,1) || check(1,1) || check(1,-1))
{
show_board();
printf("对方胜利\n");
return 0;
}
//切换回己方棋子
piece = (piece=='@')?'#':'@';
show_board();
//落子
drop();
//发送
sprintf(buf,"%d %d",x,y);
int ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
if(0 >= ret_size)
{
perror("sendto");
return EXIT_FAILURE;
}
//判断是否游戏结束
if(check(1,0) || check(0,1) || check(1,1) || check(1,-1))
{
show_board();
printf("%c棋手胜利",piece);
return 0;
}
}
}
//检查落子位置是否五子连通(判断上下左右以及斜向)
int check(int x1,int y1)
{
cnt = 0;
int m_x = x1,m_y = y1;
//先判断一个方向(例如输入(1,0),下方向)
for(int i = 0; i < 4; i++)
{
if(board[x+m_x][y+m_y] == piece)
{
cnt++;
m_x += x1;
m_y += y1;
}
else
break;
}
m_x = x1;m_y =y1;
//再判断一个方向(输入的是(1,0),上方向)
for(int j = 0; j < 4; j++)
{
if(board[x-m_x][y-m_y] == piece)
{
cnt++;
m_x += x1;
m_y += y1;
}
else
break;
}
//如果一条线上有4个子以上相同相同,则返回1,否则返回0
return cnt/4;
}
//落子函数
void drop()
{
for(;;)
{
//显示当前光标的位置
printf("\33[%d;%dH",x+1,(y+1)*2);
//获取键盘输入
switch(getch())
{
//183-186为上下左右,10为回车
case 183:x>0 && x--;break;
case 184:x<14 && x++;break;
case 185:y<14 && y++;break;
case 186:y>0 && y--;break;
case 10:
//当光标位置没有子时,可以落子
if(board[x][y] == '+')
{
board[x][y] = piece;
return;
}
break;
}
}
}
//显示棋盘函数
void show_board()
{
system("clear");
for(int i=0;i<15;i++)
{
for(int j = 0;j<15;j++)
{
printf(" %c",board[i][j]);
}
printf("\n");
}
}