一个粗糙的二人贪吃蛇联机版实现

联机版贪吃蛇

经过六天的艰苦(抓狂)后,终于完成了一个简单的联机版贪吃蛇制作,我将和大家分享一下制作的流程与感触。

1.题目难点

贪吃蛇是一款很经典的游戏,也是一个难度系数尚可,适宜初学c++者进行挑战的一个项目。在此过程中,主要的难点有两个:

1.如何让蛇动起来

2.如何让两台机器可以随时保持数据的交换

2.基本思路

对于电脑来说,蛇是不可能一点点蠕动的。所以在我的思考中,蛇每移动一步,就等同于在蛇头前面增加一格,同时将尾部删除。食物的产生用srand()和rand()函数来完成(虽然在最后为了保证两台机器食物同步我暂时删除了srand())。至于控制蛇,我就用了电脑的wsad四键,结合_getch()函数完成控制。至于最后的联机部分我们稍后再说。

3.对象&头文件

我总共构建了四个对象

  • 框架

  • 食物

  • 蛇节点

头文件很多,在此对几个特殊的进行介绍。

1.#include <cstdlib>&&#include<ctime> :产生随机数

2.#include<WinSock2.h>&&include <WS2tcpip.h> :联机所用

3.#include<conio.h> :打印和游戏处理所用

4.详解

(因为是初学者操作,为了操作方便,所有类内成员全部为public,类与类之间也全部为友远关系)

1.框架是在控制台打印的东西,也是游戏的界面,我用全局变量二维数组作为整体的框架(42*42),以下是他的基本组成

class Frame {
public:
    friend class snakeNode;
    friend class snake;
    void makeframe();//画出游戏的边框
    void printframe();//打印
};

2.食物是贪吃蛇中必不可少的东西,食物的组成也不算复杂,如下


class Food {
public:
    friend class Frame;
    friend class snake;
    //static int i;
    int fx, fy;//记录事物的坐标
    void randomFood();//产生随机食物
    bool havefood();//判断食物是否被吃掉
    void docter();//防止两个食物随机产生在同一位置
};

3.节点相对于说是一个类,其更像是一个结构,他是蛇整体的基本组成部分,相对于蛇来说更加具体形象。


class snakeNode {
public:
    int x, y;//坐标
    snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) {
        window[ix][iy] = snakeMood;
    }//将节点在frame中表现出来
    snakeNode *next, *prec;//用链表的结构来保存蛇
};

4.蛇是最庞大的类,其内的元素是对蛇的所有操作


class snake {
public:
    string name;
    friend class Frame;
    friend class Food;
    char snakeMood;//蛇的样子
    snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood);
  //为了方便所有蛇都默认从此点出发
    void move();//游戏的关键
    void addHead();//为move服务的函数
    void detail();//为move服务的函数
    enum Direction dir;//蛇运动的方向
    bool block();//判断是否相撞
    bool outofFrame(int h, int w);//判断是否出界
    void changeDir1(char key);//键盘操作转换
};

下面对几个最重要的函数进行详解

1.move()


void snake::move() {
    this->addHead();//先增加一格子
    if (outofFrame(head->x, head->y) || block()) {
    system("cls");
    cout << "Game Over!" << endl;
    system("pause");
    exit(0);
    }
  //因为是由单机版改来的所以这里还没有修改成判断哪一方获胜的函数。。。
    if (!food1.havefood()/*吃了食物*/) {
        food1.randomFood();
    }
    else if (!food2.havefood()/*吃了食物*/) {
        food2.randomFood();
    }
    else/*没吃则将尾部删除*/ {
        detail();
    }
}

2.randomfood()


void Food::randomFood() {
    //  srand((unsigned)time(0) * 1000000);
    bool onSnake = true;
    while (onSnake) {
        onSnake = false;
        fx = rand() % 40 + 1;
        fy = rand() % 40 + 1;
        if (window[fx][fy] == 'm') {
            onSnake = true; break;//若重复了再来
          
        }
      //若在蛇身上,重来
            for (snakeNode *snake = qqq.head; snake; snake = snake->next) {
        if (fx == snake->x && fy == snake->y) {
        onSnake = true; break;
        }
        }
        for (snakeNode *snake = sss.head; snake; snake = snake->next) {
            if (fx == snake->x && fy == snake->y) {
                onSnake = true; break;
            }
        }
    }
    window[fx][fy] = 'm';
}
//PS:qqq和sss为两条蛇名

5.Tcp联机实现

针对socket内容,我只是停留在理解其用法的地步,现在正在看winsock一书,希望看完后能对其产生更为深刻的理解。

基本服务器和客户端的构建

//服务器
    /*定义相关变量*/
    char temp = 'd';
    int sock_client;
    struct sockaddr_in server_addr;
    int addr_len = sizeof(struct sockaddr_in);
    /*初始化*/
    WSADATA wsaDate;
    WORD wVersionRequested = MAKEWORD(2, 2);
    if (WSAStartup(wVersionRequested, &wsaDate) != 0) {
        cout << "加载失败!\n";
        return 0;
    }
    /*创建套接字*/
    if ((sock_client = socket(AF_INET, SOCK_STREAM, 0))<0) {
        cout << "创建套接字失败! \n";
        WSACleanup();
        return 0;
    }
    /*填写服务器地址*/
    char IP[20];
    cout << "请输入服务器地址:";
    cin >> IP;
    memset((void *)&server_addr, 0, addr_len);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, IP, &server_addr.sin_addr.s_addr);
    /*与服务器建立连接*/
    if (connect(sock_client, (struct sockaddr*)&server_addr, addr_len) != 0) {
        cout << "连接失败,错误代码:" << WSAGetLastError() << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }
//客户端
    char temp1;
    SOCKET sock_server, newsock;
    struct sockaddr_in addr;
    struct sockaddr_in client_addr;
    char msg[] = "Connect succeed. \n";
    /*初始化*/
    WSADATA wsaDate;
    WORD wVersionRequested = MAKEWORD(2, 2);
    if (WSAStartup(wVersionRequested, &wsaDate) != 0) {
        cout << "加载失败!\n";
        return 0;
    }
    /*创建套接字*/
    if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) {
        cout << "创建套接字失败! \n";
        WSACleanup();
        return 0;
    }
    /*添加本地地址*/
    int addr_len = sizeof(struct sockaddr_in);
    memset((void *)&addr, 0, addr_len);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock_server, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
        cout << "地址绑定失败,错误代码:" << WSAGetLastError() << endl;
        WSACleanup();
        return 0;
    }
    //将套接字设置为监听状态 
    if (listen(sock_server, 0) != 0) {
        cout << "listen函数调用失败,错误代码:" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }
    while (true) {
        if ((newsock = accept(sock_server,(struct sockaddr*)&client_addr, &addr_len)) == INVALID_SOCKET) {
            cout << "accept函数调用失败,错误代码:" << WSAGetLastError() << endl;
        }
        else {
            cout << "成功接收到一个连接请求;\n";
            break;
        }
    }
//部分代码来自于winsock编程一书改编而来

相对于socket的基本构造,如何交换数据才是重中之重。windows下的recv()(接受信息函数)是阻塞的(就是在没有信息发来时会一直卡在那里不动)。所以为了保证游戏的连贯与一致,游戏中数据的交换只有蛇的方向。我们要保证信息一直在不停的发送与接收中。在编码过程中,最耗时的部分就是实现如何保证两边总能接收到信息而不发生阻塞,最后我采用了一种规则:先发送再接受,这样根据数学逻辑来讲,在缓存区中存放的数据中的send是大于等于recv的,这样就可以保证总有一端可以收到信息,而在其收到后又会再极短的时间内发出信息,这样自然能保证游戏的流畅。


system("mode con cols=100 lines=50");
    Frame frame;
    char key;
    char sended[1000], geted[1000];
    frame.makeframe();
    food1.randomFood();
    food2.randomFood();
    cout << "请输入sss的方向:";
    cin >> key;
    sended[0] = key;
    sss.changeDir1(key);
    qqq.snakeMood = '*';
    sss.snakeMood = 'o';
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos; pos.X = 0, pos.Y = 0;
    cout << "等待链接…" << endl;
    send(sock_client, sended, sizeof(sended), 0);
    while (true) {
        int size = recv(sock_client, geted, sizeof(geted), 0);
        if (size > 0)break;
    }
    char key1 = geted[0];
    cout << key1;
    qqq.changeDir1(key1);
    while (true) {
        if (sss.dir != NONE&&qqq.dir != NONE) {
            break;
        }
    }//通过此手段,给蛇初始方向赋值,同时也能保证两台机器上游戏时间的同步
    cout << "链接成功…" << "\n";
    cout << "请调至英文输入进行游戏" << "\n";
    cout << "游戏将在三秒后开始";
    Sleep(3000);
    system("cls");//清屏函数
    //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏
    while (true) {
        while (_kbhit()) {
            key = _getch();
            sss.changeDir1(key);
            sended[0] = key;
            send(sock_client, sended, sizeof(sended), 0);
            recv(sock_client, geted, sizeof(geted), 0);
            char key1 = geted[0];
            qqq.changeDir1(key1);
            qqq.move();
            sss.move();
            determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
            break;
        }
        while (!_kbhit()) {     
            send(sock_client, sended, sizeof(sended), 0);
            recv(sock_client, geted, sizeof(geted), 0);
            char key1 = geted[0];
            qqq.changeDir1(key1);
                    qqq.move();
            sss.move();
                determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
        }
    }

可以从代码中看出,其实每台机器都是独立的,他们只是同时开始,开始方向相同,每一方的玩家控制一条蛇的dir,仅此而已。这也是现在很多大型游戏所采用的方法,将大部分内容本地运行,只将玩家之间的交互数据上传到服务器上,从而使得网速要求没有那么苛刻。

6.全部代码

服务端
#include<iostream>
#include<ctime>
#include<conio.h>
#include <cstdlib>  
#include<WinSock2.h>
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
enum Direction { NONE,UP, DOWN, LEFT, RIGHT};
char window[42][42];
class Frame {
public:
    friend class snakeNode;
    friend class snake;
    void makeframe();
    void printframe();
}frame;
void Frame::makeframe() {
    for (int i = 0; i <= 41; i++) {
        for (int j = 0; j <= 41; j++)
            window[i][j] = ' ';
    }
    for (int i = 0; i <= 41; i++) {
        window[i][0] = '#';
        window[i][41] = '#';
    }
    for (int i = 0; i <= 41; i++) {
        window[0][i] = '#';
        window[41][i] = '#';
    }
}
void Frame::printframe() {
    for (int i = 0; i <= 41; i++) {
        for (int j = 0; j <= 41; j++)
            cout << window[i][j] << ' ';
        cout << endl;
    }
}
class snakeNode {
public:
    int x, y;
    snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) {
        window[ix][iy] = snakeMood;
    }
    snakeNode *next, *prec;
};
class snake {
public:
    string name;
    friend class Frame;
    friend class Food;
    char snakeMood;
    snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood);
    void move();
    void addHead();
    void detail();
    enum Direction dir;
    bool block();
    bool outofFrame(int h, int w);
    void changeDir1(char key);
};
snake qqq;
snake sss;
class Food {
public:
    friend class Frame;
    friend class snake;
    //static int i;
    int fx, fy;
    void randomFood();
    bool havefood();
    void docter();
};
Food food1, food2;
void Food::docter() {
    if (food1.fx == food2.fx&&food2.fx == food1.fx) {
        food1.randomFood();
    }
}
bool Food::havefood() {
    if (window[fx][fy] == 'm')return true;
    return false;
}
void Food::randomFood() {
//  srand((unsigned)time(0) * 1000000);
    bool onSnake = true;
    while (onSnake) {
        onSnake = false;
        fx = rand() % 40 + 1;
        fy = rand() % 40 + 1;
        if (window[fx][fy] == 'm') {
            onSnake = true; break;
        }
    for (snakeNode *snake = qqq.head; snake; snake = snake->next) {
            if (fx == snake->x && fy == snake->y) {
                onSnake = true; break;
            }
        }
        for (snakeNode *snake = sss.head; snake; snake = snake->next) {
            if (fx == snake->x && fy == snake->y) {
                onSnake = true; break;
            }
        }
    }
    window[fx][fy] = 'm';
}
void snake::changeDir1(char key) {
    switch (key)
    {
    case 'w':if (dir != DOWN)dir = UP; break;
    case's':if (dir != UP)dir = DOWN; break;
    case 'a':if (dir != RIGHT)dir = LEFT; break;
    case 'd':if (dir != LEFT)dir = RIGHT; break;
    case'z':system("pause"); break;
    default:
        break;
    }
}
bool snake::outofFrame(int h, int w) {
    return h < 41 && h > 0 && w < 41 && w > 0 ? false : true;
}
bool snake::block() {
    for (snakeNode* snake1 = head; snake1; snake1 = snake1->next) {
        for (snakeNode* snake2 = (snake1->next); snake2; snake2 = snake2->next) {
            if (snake1->x == snake2->x&&snake1->y == snake2->y)return true;
        }
    }
    return false;
}
void snake::addHead() {
    int x1 = head->x;
    int y1 = head->y;
    switch (dir)
    {
    case UP:x1--;
        break;
    case DOWN:x1++;
        break;
    case LEFT:y1--;
        break;
    case RIGHT:y1++;
        break;
    default:
        break;
    }

    snakeNode* head1 = new snakeNode(x1, y1, head, nullptr, snakeMood);
    head->prec = head1;
    head = head1;
}
void snake::detail() {
    snakeNode* node = head;
    for (; node; node = node->next) {
        if (node->next == nullptr) {
            window[node->x][node->y] = ' ';
            node->prec->next = nullptr;
        }
    }
}
void snake::move() {
    this->addHead();
    //若_getch还没触发一次操作是再嗯下一次getch会导致蛇回头从而gameover
    if (outofFrame(head->x, head->y) || block()) {
        system("cls");
        cout << "Game Over!" << endl;
        system("pause");
        exit(0);
    }
    if (!food1.havefood()) {
        food1.randomFood();
    }
    else if (!food2.havefood()) {
        food2.randomFood();
    }
    else {
        detail();
    }
}
void HideCursor()
{
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
bool qqqwin() {
    for (snakeNode* snake1 = qqq.head; snake1->next; snake1 = snake1->next) {
        if (snake1->next->x == sss.head->x&&snake1->next->y == sss.head->y)return true;
    }
    return false;
}
bool ssswin() {
    for (snakeNode* snake1 = sss.head; snake1->next; snake1 = snake1->next) {
        if (snake1->next->x == qqq.head->x&&snake1->next->y == qqq.head->y)return true;
    }
    return false;
}
bool equal() {
    if (qqq.head->x == sss.head->x&&qqq.head->y == sss.head->y)return true;
    return false;
}
void determination() {
    if (qqqwin()) {
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD pos; pos.X = 40, pos.Y = 20;
        SetConsoleCursorPosition(hOut, pos);
        cout << "qqq win" << endl;
        system("pause");
        exit(0);
    }
    if (ssswin()) {
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD pos; pos.X = 40, pos.Y = 20;
        SetConsoleCursorPosition(hOut, pos);
        cout << "sss win" << endl;
        system("pause");
        exit(0);
    }
    if (equal()) {
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD pos; pos.X = 40, pos.Y = 20;
        SetConsoleCursorPosition(hOut, pos);
        cout << "Draw" << endl;
        system("pause");
        exit(0);
    }
}
int main() {

    /*定义相关变量*/
    char temp1;
    SOCKET sock_server, newsock;
    struct sockaddr_in addr;
    struct sockaddr_in client_addr;
    char msg[] = "Connect succeed. \n";
    /*初始化*/
    WSADATA wsaDate;
    WORD wVersionRequested = MAKEWORD(2, 2);
    if (WSAStartup(wVersionRequested, &wsaDate) != 0) {
        cout << "加载失败!\n";
        return 0;
    }
    /*创建套接字*/
    if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) {
        cout << "创建套接字失败! \n";
        WSACleanup();
        return 0;
    }
    /*添加本地地址*/
    int addr_len = sizeof(struct sockaddr_in);
    memset((void *)&addr, 0, addr_len);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock_server, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
        cout << "地址绑定失败,错误代码:" << WSAGetLastError() << endl;
        WSACleanup();
        return 0;
    }
    //将套接字设置为监听状态 
    if (listen(sock_server, 0) != 0) {
        cout << "listen函数调用失败,错误代码:" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }
    while (true) {
        if ((newsock = accept(sock_server,(struct sockaddr*)&client_addr, &addr_len)) == INVALID_SOCKET) {
            cout << "accept函数调用失败,错误代码:" << WSAGetLastError() << endl;
        }
        else {
            cout << "成功接收到一个连接请求;\n";
            break;
        }
    }


    system("mode con cols=100 lines=50");
    Frame frame;
    char sended[1000], geted[1000];
    frame.makeframe();
    food1.randomFood();
    food2.randomFood();
        cout << "请输入qqq的方向:";
        cin >> temp1;
        qqq.changeDir1(temp1);
        sended[0] = temp1;
        qqq.snakeMood = '*';
    sss.snakeMood = 'o';
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos; pos.X = 0, pos.Y = 0;
    cout << "等待链接…" << endl;
    send(newsock, sended, sizeof(sended), 0);
    while (true) {
        int size = recv(newsock, geted, sizeof(geted), 0);
        if (size > 0)break;
    }
    char key1 = geted[0]; 
    sss.changeDir1(key1);
    while (true) {
        if (sss.dir != NONE&&qqq.dir != NONE) {
            break;
        }
    }
    cout << "链接成功…" << "\n";

    cout << "请调至英文输入进行游戏" << "\n";
    cout << "游戏将在五秒后开始";
    Sleep(3000);
    system("cls");
    //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏
    while (true) {
        while (_kbhit()) {
            temp1 = _getch();
            qqq.changeDir1(temp1);
            sended[0] = temp1;
            send(newsock, sended, sizeof(sended), 0);
            recv(newsock, geted, sizeof(geted), 0);
            char key1 = geted[0];
            sss.changeDir1(key1);
            qqq.move();
            sss.move();
            determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
            break;
        }
        while (!_kbhit()) {     //  key = _getch();
            //  sended[0] = key;
            //  send(newsock, sended, sizeof(sended), 0);
            //qqq.changeDir1(key);
            send(newsock, sended, sizeof(sended), 0);
            recv(newsock, geted, sizeof(geted), 0);
            char key1 = geted[0];
            sss.changeDir1(key1);
            qqq.move();
            sss.move();
            determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
        }
        //连续getch掉头的问题还没解决= =
    }
    return 0;
}
客户端
#include<iostream>
#include<ctime>
#include<conio.h>
#include <cstdlib>  
#include<WinSock2.h>
#include <WS2tcpip.h>
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
enum Direction { NONE, UP, DOWN, LEFT, RIGHT };
char window[42][42];
class Frame {
public:
    friend class snakeNode;
    friend class snake;
    void makeframe();
    void printframe();
}frame;
void Frame::makeframe() {
    for (int i = 0; i <= 41; i++) {
        for (int j = 0; j <= 41; j++)
            window[i][j] = ' ';
    }
    for (int i = 0; i <= 41; i++) {
        window[i][0] = '#';
        window[i][41] = '#';
    }
    for (int i = 0; i <= 41; i++) {
        window[0][i] = '#';
        window[41][i] = '#';
    }
}
void Frame::printframe() {
    for (int i = 0; i <= 41; i++) {
        for (int j = 0; j <= 41; j++)
            cout << window[i][j] << ' ';
        cout << endl;
    }
}
class snakeNode {
public:
    int x, y;
    snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) {
        window[ix][iy] = snakeMood;
    }
    snakeNode *next, *prec;
};
class snake {
public:
    string name;
    friend class Frame;
    friend class Food;
    char snakeMood;
    snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood);
    void move();
    void addHead();
    void detail();
    enum Direction dir;
    bool block();
    bool outofFrame(int h, int w);
    void changeDir1(char key);
};
snake qqq;
snake sss;
class Food {
public:
    friend class Frame;
    friend class snake;
    //static int i;
    int fx, fy;
    void randomFood();
    bool havefood();
    void docter();
};
Food food1, food2;
void Food::docter() {
    if (food1.fx == food2.fx&&food2.fx == food1.fx) {
        food1.randomFood();
    }
}
bool Food::havefood() {
    if (window[fx][fy] == 'm')return true;
    return false;
}
void Food::randomFood() {
    //  srand((unsigned)time(0) * 1000000);
    bool onSnake = true;
    while (onSnake) {
        onSnake = false;
        fx = rand() % 40 + 1;
        fy = rand() % 40 + 1;
        if (window[fx][fy] == 'm') {
            onSnake = true; break;
        }
            for (snakeNode *snake = qqq.head; snake; snake = snake->next) {
        if (fx == snake->x && fy == snake->y) {
        onSnake = true; break;
        }
        }
        for (snakeNode *snake = sss.head; snake; snake = snake->next) {
            if (fx == snake->x && fy == snake->y) {
                onSnake = true; break;
            }
        }
    }
    window[fx][fy] = 'm';
}
void snake::changeDir1(char key) {
    switch (key)
    {
    case 'w':if (dir != DOWN)dir = UP; break;
    case's':if (dir != UP)dir = DOWN; break;
    case 'a':if (dir != RIGHT)dir = LEFT; break;
    case 'd':if (dir != LEFT)dir = RIGHT; break;
    case'z':system("pause"); break;
    default:
        break;
    }
}
bool snake::outofFrame(int h, int w) {
    return h < 41 && h > 0 && w < 41 && w > 0 ? false : true;
}
bool snake::block() {
    for (snakeNode* snake1 = head; snake1; snake1 = snake1->next) {
        for (snakeNode* snake2 = (snake1->next); snake2; snake2 = snake2->next) {
            if (snake1->x == snake2->x&&snake1->y == snake2->y)return true;
        }
    }
    return false;
}
void snake::addHead() {
    int x1 = head->x;
    int y1 = head->y;
    switch (dir)
    {
    case UP:x1--;
        break;
    case DOWN:x1++;
        break;
    case LEFT:y1--;
        break;
    case RIGHT:y1++;
        break;
    default:
        break;
    }

    snakeNode* head1 = new snakeNode(x1, y1, head, nullptr, snakeMood);
    head->prec = head1;
    head = head1;
}
void snake::detail() {
    snakeNode* node = head;
    for (; node; node = node->next) {
        if (node->next == nullptr) {
            window[node->x][node->y] = ' ';
            node->prec->next = nullptr;
        }
    }
}
void snake::move() {
    this->addHead();
    //若_getch还没触发一次操作是再嗯下一次getch会导致蛇回头从而gameover
    if (outofFrame(head->x, head->y) || block()) {
    system("cls");
    cout << "Game Over!" << endl;
    system("pause");
    exit(0);
    }
    if (!food1.havefood()) {
        food1.randomFood();
    }
    else if (!food2.havefood()) {
        food2.randomFood();
    }
    else {
        detail();
    }
}
void HideCursor()
{
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
bool qqqwin() {
for (snakeNode* snake1 = qqq.head; snake1->next; snake1 = snake1->next) {
if (snake1->next->x == sss.head->x&&snake1->next->y == sss.head->y)return true;
}
return false;
}
bool ssswin() {
for (snakeNode* snake1 = sss.head; snake1->next; snake1 = snake1->next) {
if (snake1->next->x == qqq.head->x&&snake1->next->y == qqq.head->y)return true;
}
return false;
}
bool equal() {
if (qqq.head->x == sss.head->x&&qqq.head->y == sss.head->y)return true;
return false;
}
void determination() {
if (qqqwin()) {
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos; pos.X = 40, pos.Y = 20;
SetConsoleCursorPosition(hOut, pos);
cout << "qqq win" << endl;
system("pause");
exit(0);
}
if (ssswin()) {
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos; pos.X = 40, pos.Y = 20;
SetConsoleCursorPosition(hOut, pos);
cout << "sss win" << endl;
system("pause");
exit(0);
}
if (equal()) {
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos; pos.X = 40, pos.Y = 20;
SetConsoleCursorPosition(hOut, pos);
cout << "Draw" << endl;
system("pause");
exit(0);
}
}
int main() {
    /*定义相关变量*/
    int sock_client;
    struct sockaddr_in server_addr;
    int addr_len = sizeof(struct sockaddr_in);
    /*初始化*/
    WSADATA wsaDate;
    WORD wVersionRequested = MAKEWORD(2, 2);
    if (WSAStartup(wVersionRequested, &wsaDate) != 0) {
        cout << "加载失败!\n";
        return 0;
    }
    /*创建套接字*/
    if ((sock_client = socket(AF_INET, SOCK_STREAM, 0))<0) {
        cout << "创建套接字失败! \n";
        WSACleanup();
        return 0;
    }
    /*填写服务器地址*/
    char IP[20];
    cout << "请输入服务器地址:";
    cin >> IP;
    memset((void *)&server_addr, 0, addr_len);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, IP, &server_addr.sin_addr.s_addr);
    /*与服务器建立连接*/
    if (connect(sock_client, (struct sockaddr*)&server_addr, addr_len) != 0) {
        cout << "连接失败,错误代码:" << WSAGetLastError() << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }


    system("mode con cols=100 lines=50");
    Frame frame;
    char key;
    char sended[1000], geted[1000];
    frame.makeframe();
    food1.randomFood();
    food2.randomFood();
    cout << "请输入sss的方向:";
    cin >> key;
    sended[0] = key;
    sss.changeDir1(key);
    qqq.snakeMood = '*';
    sss.snakeMood = 'o';
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos; pos.X = 0, pos.Y = 0;
    cout << "等待链接…" << endl;
    send(sock_client, sended, sizeof(sended), 0);
    while (true) {
        int size = recv(sock_client, geted, sizeof(geted), 0);
        if (size > 0)break;
    }
    char key1 = geted[0];
    cout << key1;
    qqq.changeDir1(key1);
    while (true) {
        if (sss.dir != NONE&&qqq.dir != NONE) {
            break;
        }
    }
    cout << "链接成功…" << "\n";
    cout << "请调至英文输入进行游戏" << "\n";
    cout << "游戏将在五秒后开始";
    Sleep(3000);
    system("cls");
    //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏
    while (true) {
        while (_kbhit()) {
            key = _getch();
            sss.changeDir1(key);
            sended[0] = key;
            send(sock_client, sended, sizeof(sended), 0);
            recv(sock_client, geted, sizeof(geted), 0);
            char key1 = geted[0];
            qqq.changeDir1(key1);
            qqq.move();
            sss.move();
            determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
            break;
        }
        while (!_kbhit()) {     
            send(sock_client, sended, sizeof(sended), 0);
            recv(sock_client, geted, sizeof(geted), 0);
            char key1 = geted[0];
            qqq.changeDir1(key1);
                    qqq.move();
            sss.move();
                determination();
            food1.docter();
            SetConsoleCursorPosition(hOut, pos);
            HideCursor();
            frame.printframe();
            Sleep(300);
        }
        //连续getch掉头的问题还没解决= =
    }
    return 0;
}

总结

毕竟初学。。。可能很多地方都是在意思路多于在意代码,在其中还有很多定义了却未使用,或者重复定义的地方

在制作过程中也参考了网上的一些函数的源码,他们使得我思路更清晰明了

最近学业可能十分繁忙,没有太多时间进行代码的重编译和修改

不过在以后空闲时间,我一定会将其修改完成,顺便尝试将服务器分离,使得游戏变成多人而非二人模式

  • 11
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值