网络版五子棋

初学期间,在校学习了三子棋的编写,在学习网络编程之后,趋于兴趣,又实现了单机版的五子棋,之后就试图实现网络版五子棋,实现跨机对战。

我在单机版的五子棋上加入了socket,建立c/s/模型进行网络编程,然后实现客户端和服务器端的坐标数据互通,在数据互通之前在自己的终端下进行一系列判断,将合法的坐标传输至对方客户端,然后同时落子,双方同步落子,以此实现了最简单的双人网络版五子棋。

client.c

#include <stdio.h> 
#include <arpa/inet.h> 
#include <stdlib.h> 
#include <sys/types.h>           
#include "game.h" 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <sys/types.h>   
#include <netinet/in.h> 
#include <string.h> 
#include <unistd.h> 
void SocketCommon(int sock, struct sockaddr_in* server) 
#include <stdlib.h> 
void Menu() 
{ 
    char buf[1024]; 
    struct sockaddr_in peer; 
    while(1) 
    { 
        socklen_t len = sizeof(peer); 
        printf("Please Enter# \n"); 
        fflush(stdout); 
        ssize_t s = read(0,buf,sizeof(buf)-1); 
        if(s > 0) 
        { 
            buf[s-1] = 0; 
            sendto(sock,buf,strlen(buf),0,(struct sockaddr*)server,sizeof(*server)); 
            ssize_t _s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr&peer,&len); 
            if(_s > 0) 
            { 
                buf[_s] = 0; 
                printf("server echo# %s\n",buf); 
            } 
    printf("==================================\n"); 
    printf("||  1.开始游戏   0.退出游戏     ||\n"); 
    printf("==================================\n"); 
} 
void Game(int sock) 
{ 
    Point point_server,point_client; 
    char board[ROWS][COLS]; 
    InitBoard(board,ROWS,COLS); 
    PrintBoard(board,ROWS,COLS); 
    ssize_t s; 
    int x,y; 
    while(1){ 
        printf("请下子(输入坐标)> "); 
        scanf("%d%d",&x,&y); 
        point_client.row = x-1; 
        point_client.col = y-1; 
        ClientMove(board,&point_client);         
        PrintBoard(board,ROWS,COLS);         
        write(sock,(void*)&point_client,sizeof(point_client)); 
        if(GameState(board,&point_client) == 'X') 
        { 
            printf("You win!\n"); 
            break; 
        } 
        else if(GameState(board,&point_client) == 'p') 
        { 
            printf("平局!\n"); 
            break; 
        } 
        s = read(sock,(void*)&point_server,sizeof(point_server)); 
        if(s == 0) 
        { 
            printf("Server quit\n"); 
            return; 
        } 
        ServerMove(board,&point_server); 
        PrintBoard(board,ROWS,COLS); 
        if(GameState(board,&point_server) == 'O') 
        { 
            printf("Server win!\n"); 
            break; 
        } 
        else if(GameState(board,&point_server) == 'p') 
        { 
            printf("平局!\n"); 
            break; 
        } 
    } 
} 
int main(int argc, char* argv[]) 
{ 
    if(argc < 3) 
    if(argc != 3) 
    { 
        perror("argc"); 
        printf("Usage: ./server [ip] [port]\n"); 
        exit(1); 
    } 
    //创建 socket 文件描述符 
    //AF_INET    ipv4地址类型 
    //SOCK_DGRAM udp类型 
    //0          默认使用SOCK_DGRAM类型 
    int sock = socket(AF_INET,SOCK_DGRAM,0);      
    int sock = socket(AF_INET,SOCK_STREAM,0); 
    if(sock < 0) 
    { 
        perror("socket"); 
        exit(2); 
        exit(1); 
    } 
    struct sockaddr_in server; 
    server.sin_family = AF_INET;//ipv4地址类型 
    server.sin_port = htons(atoi(argv[2]));//端口号 
    server.sin_addr.s_addr = inet_addr(argv[1]);//ipv4地址转换 因为输入的是点分十进制  
    struct sockaddr_in client; 
    client.sin_family = AF_INET; 
    client.sin_addr.s_addr = inet_addr(argv[1]); 
    client.sin_port = htons(atoi(argv[2])); 
    int ret = connect(sock,(const struct sockaddr*)&client,sizeof(client)); 
    if(ret < 0) 
    { 
        perror("connect"); 
        exit(2); 
    } 
    Menu(); 
    int state; 
    while(1) 
    { 
        printf("请做出你的选择> "); 
        scanf("%d",&state); 
        if(state == 1) 
        { 
            Game(sock); 
        }else if(state == 0) 
        { 
            break; 
        } 
        else 
        { 
            printf("选择错误\n"); 
            Menu(); 
            sleep(2); 
        } 
    } 
    SocketCommon(sock,&server); 
    close(sock); 
    return 0; 
} 

game.c

#include<stdio.h>
#define ROWS 11
#define COLS 13
struct Point
{
	int row;
	int col;
};
//初始化棋盘
void InitBoard(char board[ROWS][COLS])
{
	memset(board, ' ', ROWS*COLS*sizeof(char));//每个坐标的初始值我用空格代替
}
//打印棋盘
void PrintBoard(char board[ROWS][COLS])//打印棋盘
{
	int  i = 0;
	printf("   ");
	for (i = 1; i < COLS; i++)
		printf(" %2d ",i);
	printf(" %2d \n",i);
	for (i = 0; i < ROWS; i++)
	{
		printf("%2d ", i + 1);
		int j = 0;
		int col = COLS;
		for (; j < COLS; j++)
		{
			if (j == COLS - 1)
			{
				printf(" %c \n", board[i][j]);
				if (i != ROWS - 1){
					printf("   ");
					while (--col)
						printf("---|");
					printf("---\n");
				}
				break;
			}
			printf(" %c ", board[i][j]);
			printf("|");
		}

	}
}
//判断棋盘是否满了 
int IsFall(char board[ROWS][COLS])
{
	size_t i = 0;
	for (; i < ROWS; i++)
	{
		size_t j = 0;
		for (; j < COLS; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
//落子
void Move(char board[ROWS][COLS],struct Point* point, char ch)
{
	if (point == NULL)
	{
		return;
	}
	int x = point->row;
	int y = point->col;
	//如果棋盘满了的话就退出循环,也就是不能再落子了
	while (1)
	{
		if (x >= 0 && x < ROWS && y >= 0 && y < COLS){
			if (board[x][y] == ' ')
			{
				board[x][y] = ch;
				point->row = x;
				point->col = y;
				break;
			}
			else
			{
				printf("该坐标已落子,请重新输入坐标: ");
				scanf("%d%d", &x, &y);
				x--;
				y--;
			}
		}
		else{
			printf("坐标输入有误,请重新输入坐标: ");
			scanf("%d%d", &x, &y);
			x--;
			y--;
		}
	}
}
//列齐 
int Cols(char board[ROWS][COLS], struct Point* point)
{
	int x = point->row;
	int y = point->col;
	int count = 1;
	while (x - 1 >= 0)
	{
		if (board[x][y] != board[x - 1][y])
			break;
		count++;
		if (count == 5)
		{
			//如果往上遍历已经够了五个,说明已经赢了,直接返回 
			return count;
		}
		x--;
	}
	//到这里说明当前点往上走是不满足条件的 
	//但是已经走到了最上面的点,所以从临界点往相反方向找 
	//必须要找到五个连续的才算满足条件 
	count = 1;//count 重新被设定为1 
	while (x + 1 <= ROWS)
	{
		if (board[x][y] == board[x + 1][y])
		{
			count++;
			if (count == 5)
			{
 		 		return count;
			}
			x++;
		}
		else
		{
			//n 代表当前状态未满足赢的条件 
			return 0;
		}
	}
	return 0;
}
//行齐 
int Rows(char board[ROWS][COLS], struct Point* point)
{
	int x = point->row;
	int y = point->col;
	int count = 1;
	while (y - 1 >= 0)
	{
		if (board[x][y] != board[x][y - 1])
			break;
		count++;
		if (count == 5)
		{
			//如果往左遍历已经够了五个,说明已经赢了,直接返回 
			return count;
		}
		y--;
	}
	//到这里说明当前点往左走是不满足条件的 
	//但是已经走到了最左面的点,所以从临界点往相反方向找 
	//必须要找到五个连续的才算满足条件 
	count = 1;//count 重新被设定为1 
	while (y + 1 <= COLS)
	{
		if (board[x][y] == board[x][y + 1])
		{
			count++;
			if (count == 5)
			{
				return count;
			}
			y++;
		}
		else
		{
			//n 代表当前状态未满足赢的条件 
			return 0;
		}
	}
	return 0;
}
//左上到右下的对角线 
int UpLeft(char board[ROWS][COLS], struct Point* point)
{
	int x = point->row;
	int y = point->col;
	int count = 1;
	while (x - 1 >= 0 && y - 1 >= 0)
	{
		if (board[x][y] != board[x - 1][y - 1])
			break;
		count++;
		if (count == 5)
		{
			//如果往左上遍历已经够了五个,说明已经赢了,直接返回 
			return count;
		}
		x--;
		y--;
	}
	//到这里说明当前点往左上走是不满足条件的 
	//但是已经走到了最左上的点,所以从临界点往相反方向找 
	//必须要找到五个连续的才算满足条件 
	count = 1;//count 重新被设定为1 
	while (x + 1 <= ROWS && y + 1 <= COLS)
	{
		if (board[x][y] == board[x + 1][y + 1])
		{
			count++;
			if (count == 5)
			{
				return count;
			}
			x++;
			y++;
		}
		else
		{
			//n 代表当前状态未满足赢的条件 
			return 0;
		}
	}
	return 0;
}
//右上到左下的对角线 
int UpRight(char board[ROWS][COLS],struct Point* point)
{
	int x = point->row;
	int y = point->col;
	int count = 1;
	while (x - 1 >= 0 && y + 1 <= COLS)
	{
		if (board[x][y] != board[x - 1][y + 1])
			break;
		count++;
		if (count == 5)
		{
			//如果往右上遍历已经够了五个,说明已经赢了,直接返回 
			return count;
		}
		x--;
		y++;
	}
	//到这里说明当前点往右上走是不满足条件的 
	//但是已经走到了最右上面的点,所以从临界点往相反方向找 
	//必须要找到五个连续的才算满足条件 
	count = 1;//count 重新被设定为1 
	while (x + 1 <= ROWS && y - 1 <= COLS)
	{
		if (board[x][y] == board[x + 1][y - 1])
		{
			count++;
			if (count == 5)
			{
				return count;
			}
			x++;
			y--;
		}
		else
		{
			//n 代表当前状态未满足赢的条件 
			return 0;
		}
	}
	return 0;
}
//返回游戏进行状态/平局/输赢/正在进行// 
char Iswin(char board[ROWS][COLS], struct Point* point)
{
	if (point == NULL)
	{
		return 'e';
	}
	//判断当前点所在列是否是连续五个子儿 
	if (Cols(board, point) == 5)
	{
		return board[point->row][point->col];
	}
	//判断当前点所在行是否是连续五个子儿 
	else if (Rows(board, point) == 5)
	{
		return board[point->row][point->col];
	}
	//判断当前点所在左上与右下的对角线是否满足条件 
	else if (UpLeft(board, point) == 5)
	{
		return board[point->row][point->col];
	}
	//判断当前点所在左上与右下的对角线是否满足条件 
	else if (UpRight(board, point) == 5)
	{
		return board[point->row][point->col];
	}
	else if (IsFall(board))
	{
		//平局 
		return 'p';
	}
	//当前没有输赢,继续游戏 
	return 'g';
}

server.c

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <arpa/inet.h> 
#include <sys/types.h>           
#include "game.h" 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <sys/types.h>   
#include <netinet/in.h> 
void SocketCommon(int sock) 
{ 
    char buf[1024]; 
    struct sockaddr_in client; 
    while(1) 
    { 
        socklen_t len = sizeof(client); 
        ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&len); 
        if(s > 0) 
        { 
            buf[s] = 0; 
            printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),nto(client.sin_port),buf); 
            fflush(stdout); 
            sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client)); 
        } 
    } 
} 
int main(int argc, char* argv[]) 
{ 
    if(argc < 3) 
    if(argc != 3) 
    { 
        perror("argc"); 
        printf("Usage: ./server [ip] [port]\n"); 
        exit(1); 
    } 
    //创建 socket 文件描述符 
    //AF_INET    ipv4地址类型 
    //SOCK_DGRAM udp类型 
    //0          默认使用SOCK_DGRAM类型 
    int sock = socket(AF_INET,SOCK_DGRAM,0);      
    int sock = socket(AF_INET,SOCK_STREAM,0); 
    if(sock < 0) 
    { 
        perror("socket"); 
        exit(2); 
        exit(1); 
    } 
 
    struct sockaddr_in local; 
    local.sin_family = AF_INET;//ipv4地址类型 
    local.sin_port = htons(atoi(argv[2]));//端口号 
    local.sin_addr.s_addr = inet_addr(argv[1]);//ipv4地址转换 因为输入的是点分十进制 
    struct sockaddr_in server; 
    server.sin_family = AF_INET; 
    server.sin_addr.s_addr = inet_addr(argv[1]); 
    server.sin_port = htons(atoi(argv[2])); 
 
    if(bind(sock,(const struct sockaddr*)&local,sizeof(local)) < 0) 
    int ret = bind(sock,(const struct sockaddr*)&server,sizeof(server)); 
    if(ret < 0) 
    { 
        perror("bind"); 
        exit(2); 
    } 
    ret = listen(sock,5); 
    if(ret < 0) 
    { 
        perror("listen"); 
        exit(3); 
    } 
 
    SocketCommon(sock); 
    for(;;) 
    { 
        struct sockaddr_in client; 
        socklen_t len; 
        int client_sock = accept(sock,(struct sockaddr*)&client,&len); 
        if(client_sock < 0) 
        { 
            continue; 
        } 
        ssize_t s; 
        Point point_server,point_client; 
        char board[ROWS][COLS]; 
        InitBoard(board,ROWS,COLS); 
        int x,y; 
        while(1){ 
            s = read(client_sock,(void*)&point_client,sizeof(point_client)); 
            if(s == 0) 
            { 
                printf("Client quit...\n"); 
                close(client_sock); 
            } 
            ClientMove(board,&point_client); 
            PrintBoard(board,ROWS,COLS); 
            if(GameState(board,&point_client) == 'X') 
            { 
                printf("Client win!\n"); 
                break; 
            } 
            else if(GameState(board,&point_client) == 'p') 
            { 
                printf("平局!\n"); 
                break; 
            } 
            printf("请下子(输入坐标)> "); 
            scanf("%d%d",&x,&y); 
            point_server.row = x-1; 
            point_server.col = y-1; 
            ServerMove(board,&point_server); 
            PrintBoard(board,ROWS,COLS); 
            write(client_sock,(void*)&point_server,sizeof(point_server)); 
            if(GameState(board,&point_client) == 'O') 
            { 
                printf("You win!\n"); 
                break; 
            } 
            else if(GameState(board,&point_client) == 'p') 
            { 
                printf("平局!\n"); 
                break; 
            } 
          }//游戏下完了 
    } 
    close(sock); 
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值