基于UDP的网络对战五子棋

网络对战五子棋

介绍

基于UDP的网络对战五子棋,可以在不同电脑上玩,是之前五子棋的升级版

使用说明
  1. 将server.c放到云服务器中(和client放在一起也能玩)
  2. gcc server.c -o server后运行./server
  3. client.c放在自己操作系统中,gcc client.c -o client后运行./client
  4. 先运行的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");
        }

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值