五子棋是一种比较流行的棋类游戏,也是中国传统的智力运动之一。这个游戏通常是两个人玩,一方执黑棋,一方执白棋,双方轮流在棋盘上落子,先把自己的五个棋子连成一条直线的一方获胜。五子棋因其简单易学、趣味性强、对智力和观察能力的培养有益等特点而在国内广受欢迎。
本次小项目,使用二维数组的知识点进行棋盘和旗子的打印。进入游戏前需要对先后手进行判断,以便客户端操作。客户端循环对服务器发送落子坐标和接受对手落子坐标直到有一方胜利。服务器接受客户端发来的信息并将客户端额的地址信息保存到链表中,接受第一个服务器的落点位置并将落点位置发送给另一个服务器。
服务器:
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <sqlite3.h>
#include <dirent.h>
#include <fcntl.h>
#define ROWS 10
#define COLS 10
#define ERRLOG(errmsg) do{\
printf("%s %s %id\n", __FILE__, __func__, __LINE__);\
perror(errmsg);\
exit(-1);\
}while(0)
int f=1;
typedef struct {
char name[30];
int type;
int row;
int col;
int flag;
}MSG;
typedef struct {
int row;
int col;
}TP;
typedef struct node{
MSG point;
struct sockaddr_in addr;
struct node * next;
}*linklist,linknode;
linklist list_create(){
linklist H=(linklist)malloc(sizeof(linknode));
if(NULL==H){
printf("create fail\n");
return NULL;
}
H->next=NULL;
return H;
}
void insert_linklist(MSG msg,struct sockaddr_in cliaddr,linklist p){
linklist t=p;
while(t->next!=NULL){
t=t->next;
}
linklist new=(linklist)malloc(sizeof(linknode));
new->addr=cliaddr;
new->point=msg;
new->next=NULL;
t->next=new;
printf("%s\n",new->point.name);
printf("插入链表成功\n");
return;
}
void shunxu(int fd,MSG msg,struct sockaddr_in cliaddr,int len){
printf("%s\n",msg.data);
memset(msg.data,0,100);
msg.flag=f;
printf("%d\n",f);
if(msg.flag==1)
{
sprintf(msg.data,"你是先手");
}
else{
sprintf(msg.data,"你是后手");
}
if(sendto(fd,&msg,sizeof(MSG),0,(struct sockaddr * )&cliaddr,len)==-1)
{
perror("send error");
exit(-1);
}
f++;
}
void game(int fd,MSG msg,linklist p,struct sockaddr_in cliaddr,int len){
printf("进入game\n");
TP tp;
linklist q;
q=p->next;
if(q==NULL)
{
perror("linklist NULL");
exit(-1);
}
tp.row=msg.row;
tp.col=msg.col;
while(q!=NULL)
{
if(strcmp(q->point.name,msg.name)==0){
printf("linknext\n");
q=q->next;
}
else{
printf("send game\n");
if(sendto(fd,&tp,sizeof(TP),0,(struct sockaddr *)&q->addr,sizeof(q->addr)) < 0)
{
perror("sendto\n");
exit(-1);
}
else{
break;
}
}
}
}
int main(int argc, char* argv[])
{
MSG msg;
if(argc != 3)
{
printf("Usage: ./server [ip] [port]\n");
exit(1);
}
//创建用户数据报套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == fd){
ERRLOG("socket error");
}
//填充网络信息结构体
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
//3.绑定
if(-1 == bind(fd, (struct sockaddr *)&serveraddr, serveraddr_len)){
ERRLOG("bind error");
}
//定义结构体保存客户端的信息
//如果使用UDP时不保存客户端的网络信息结构体
//可以接受到客户端发来的数据 但是没法回信给客户端 因为sendto的后两个参数需要用到
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
linklist p=list_create();
while(1){
int ret =recvfrom(fd,&msg,sizeof(MSG), 0, (struct sockaddr *)&cliaddr, &len);
printf("%d",ret);
if(ret>0)
{
printf("shoudao xinxi\n");
printf("%s\n",msg.data);
switch(msg.type)
{
case 1:
shunxu(fd,msg,cliaddr,len);
insert_linklist(msg,cliaddr,p);
break;
case 2:
game(fd,msg,p,cliaddr,len);
break;
defalut:
printf("recv error\n");
}
}
}
close(fd);
return 0;
}
客户端:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ROWS 10
#define COLS 10
#define ERRLOG(errmsg) do{\
printf("%s %s %id\n", __FILE__, __func__, __LINE__);\
perror(errmsg);\
exit(-1);\
}while(0)
typedef struct {
char name[30];
int type;
int row;
int col;
int flag;
char data[100];
}MSG;
typedef struct {
int row;
int col;
}TP;
//初始化棋盘
void Initmap(char map[ROWS][COLS],int row, int col);
//打印棋盘
void Printmap(char map[ROWS][COLS], int row, int col);
void CommonMove(char map[ROWS][COLS],MSG* msg,char ch);
//客户端移动
void Cli1Move(char map[ROWS][COLS],MSG* msg);
//服务器移动
void Cli2Move(char map[ROWS][COLS],MSG* msg);
//判断输赢
char GameState(char map[ROWS][COLS],MSG* msg);
//初始化棋盘
void Initmap(char map[ROWS][COLS],int row, int col)
{
memset(map,' ',row*col*sizeof(char));
}
//打印棋盘
void Printmap(char map[ROWS][COLS],int row,int col)
{
int i = 0;
printf(" 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10\n");
for(; i < row;i++)
{
printf("%2d ",i+1);
int j = 0;
for(; j < col;j++)
{
if( j == col-1 )
{
printf(" %c \n",map[i][j]);
if(i != row-1)
printf(" ---|---|---|---|---|---|---|---|---|---\n");
break;
}
printf(" %c ",map[i][j]);
printf("|");
}
}
}
//判断棋盘是否满了
int IsFall(char map[ROWS][COLS])
{
size_t i = 0;
for(; i < ROWS;i++)
{
size_t j = 0;
for(; j < COLS; j++)
{
if(map[i][j] == ' ')
return 0;
}
}
return 1;
}
//移动
void CommonMove(char map[ROWS][COLS],MSG* msg,char ch)
{
if(msg == NULL)
{
return;
}
int x = msg->row;
int y = msg->col;
//如果棋盘满了的话就退出循环,也就是不能再下子了
while(1)
{
if(x >= 0 && x < ROWS && y >= 0 && y < COLS){
if(map[x][y] == ' ')
{
map[x][y] = ch;
msg->row = x;
msg->col = y;
break;
}
else
{
printf("坐标输入有误,请重新输入坐标: ");
scanf("%d%d",&x,&y);
x--;
y--;
}
}
else{
printf("坐标输入有误,请重新输入坐标: ");
scanf("%d%d",&x,&y);
x--;
y--;
}
}
}
//客户端移动
void Cli1Move(char map[ROWS][COLS],MSG* msg)
{
CommonMove(map,msg,'@');
}
//服务器移动
void Cli2Move(char map[ROWS][COLS],MSG* msg)
{
CommonMove(map,msg,'O');
}
//列齐
int ColState(char map[ROWS][COLS],MSG* msg)
{
int x = msg->row;
int y = msg->col;
int count = 1;
while(x-1 >= 0)
{
if(map[x][y] != map[x-1][y])
break;
count++;
if(count == 5)
{
//如果往上遍历已经够了五个,说明已经赢了,直接返回
return count;
}
x--;
}
//到这里说明当前点往上走是不满足条件的
//但是已经走到了最上面的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while(x+1 <= ROWS)
{
if(map[x][y] == map[x+1][y])
{
count++;
if(count == 5)
{
return count;
}
x++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//行齐
int RowState(char map[ROWS][COLS],MSG* msg)
{
int x = msg->row;
int y = msg->col;
int count = 1;
while(y-1 >= 0)
{
if(map[x][y] != map[x][y-1])
break;
count++;
if(count == 5)
{
//如果往左遍历已经够了五个,说明已经赢了,直接返回
return count;
}
y--;
}
//到这里说明当前点往左走是不满足条件的
//但是已经走到了最左面的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while(y+1 <= COLS)
{
if(map[x][y] == map[x][y+1])
{
count++;
if(count == 5)
{
return count;
}
y++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//左上到右下的对角线
int UpLeftState(char map[ROWS][COLS],MSG* msg)
{
int x = msg->row;
int y = msg->col;
int count = 1;
while(x-1 >= 0 && y-1 >= 0 )
{
if(map[x][y] != map[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(map[x][y] == map[x+1][y+1])
{
count++;
if(count == 5)
{
return count;
}
x++;
y++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//右上到左下的对角线
int UpRightState(char map[ROWS][COLS], MSG* msg)
{
int x = msg->row;
int y = msg->col;
int count = 1;
while(x-1 >= 0 && y+1 <= COLS)
{
if(map[x][y] != map[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(map[x][y] == map[x+1][y-1])
{
count++;
if(count == 5)
{
return count;
}
x++;
y--;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//返回游戏进行状态
char GameState(char map[ROWS][COLS],MSG* msg)
{
if(msg == NULL)
{
return 'e';
}
//判断当前点所在列是否是连续五个子儿
if(ColState(map,msg) == 5)
{
return map[msg->row][msg->col];
}
//判断当前点所在行是否是连续五个子儿
else if(RowState(map,msg) == 5)
{
return map[msg->row][msg->col];
}
//判断当前点所在左上与右下的对角线是否满足条件
else if(UpLeftState(map,msg) == 5)
{
return map[msg->row][msg->col];
}
//判断当前点所在左上与右下的对角线是否满足条件
else if(UpRightState(map,msg) == 5)
{
return map[msg->row][msg->col];
}
else if(IsFall(map))
{
//平局
return 'p';
}
//当前没有输赢,继续游戏
return 'g';
}
void Menu()
{
printf("=========================================\n");
printf("||1.查看先手后手 2.开始游戏 3.退出游戏 ||\n");
printf("=========================================\n");
}
void Game(int fd,MSG*msg,struct sockaddr_in saddr,int len,int flag)
{
msg->type=2;
char map[ROWS][COLS];
TP tp;
Initmap(map,ROWS,COLS);
Printmap(map,ROWS,COLS);
int x,y=0;
if(flag==1){
while(1){
msg->row=0;
msg->col=0;
printf("你是先手请下子@(输入坐标)> ");
scanf("%d%d",&x,&y);
getchar();
msg->row = x-1;
msg->col = y-1;
Cli1Move(map,msg);
Printmap(map,ROWS,COLS);
if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
{
perror("send error");
exit(-1);
}
if(GameState(map,msg) == '@')
{
printf("你赢啦!\n");
break;
}
else if(GameState(map,msg) == 'p')
{
printf("平局!\n");
break;
}
printf("等待对方输入... \n");
if(recvfrom(fd,&tp,sizeof(TP),0,(struct sockaddr*)&saddr,&len)==-1)
{
perror("recv 失败");
exit(-1);
}
msg->row=tp.row;
msg->col=tp.col;
Cli2Move(map,msg);
Printmap(map,ROWS,COLS);
if(GameState(map,msg) == 'O')
{
printf("你输啦!\n");
break;
}
else if(GameState(map,msg) == 'p')
{
printf("平局!\n");
break;
}
}
}
else if(flag==2) {
while(1){
msg->row=0;
msg->col=0;
printf("你是后手等待对方输入... \n");
if(recvfrom(fd,&tp,sizeof(TP),0,(struct sockaddr*)&saddr,&len)==-1)
{
perror("recv 失败");
exit(-1);
}
msg->row=tp.row;
msg->col=tp.col;
printf("收到服务器消息\n");
Cli1Move(map,msg);
Printmap(map,ROWS,COLS);
if(GameState(map,msg) == '@')
{
printf("你输啦!\n");
break;
}
else if(GameState(map,msg) == 'p')
{
printf("平局!\n");
break;
}
printf("到你下棋了请下子O(输入坐标)> ");
scanf("%d%d",&x,&y);
getchar();
msg->row = x-1;
msg->col = y-1;
Cli2Move(map,msg);
Printmap(map,ROWS,COLS);
if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr * )&saddr,len)==-1)
{
perror("send error");
exit(-1);
}
if(GameState(map,msg) == 'O')
{
printf("你赢啦!\n");
break;
}
else if(GameState(map,msg) == 'p')
{
printf("平局!\n");
break;
}
}
}
else{
printf("房间已满\n");
}
}
int f=-1;
void shunxu(int fd,MSG*msg,struct sockaddr_in seraddr,int len){
msg->type=1;
printf("intput name\n");
fgets(msg->name,100,stdin);
msg->name[strlen(msg->name)-1]='\0';
if(sendto(fd,msg,sizeof(MSG),0,(struct sockaddr *)&seraddr,len)==-1)
{
perror("send error");
exit(-1);
}
if(recvfrom(fd,msg,sizeof(MSG),0,(struct sockaddr*)&seraddr,&len)==-1)
{
perror("recv 失败");
exit(-1);
}
f=msg->flag;
printf("%s\n",msg->data);
}
int main(int argc, char* argv[])
{
MSG msg;
if(3 != argc){
printf("Usage : %s <IP> <PORT>\n", argv[0]);
exit(-1);
}
//1.创建用户数据报套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == fd){
ERRLOG("socket error");
}
//2.填充服务器的网络信息结构体
struct sockaddr_in seraddr;
memset(&seraddr, 0, sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[2]));
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
int len = sizeof(seraddr);
int state=0;
while(1)
{
Menu();
printf("请做出你的选择> \n");
int sc=scanf("%d",&state);
if(sc==-1){
perror("scanf\n");
exit(-1);
}
getchar();
switch(state)
{
case 1:
shunxu(fd,&msg,seraddr,len);
break;
case 2:
Game(fd,&msg,seraddr,len,f);
break;
case 3:
printf("\033[1;34m感谢使用,欢迎下次再来\n");
close(fd);
exit(0);
default:
printf("\033[1;34m输入错了请重新输入\n");
}
}
return 0;
}