You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

游戏加载后舰船攻击判定异常:击中却显示未命中(疑与vector序列化相关)

问题描述

正常游戏时攻击舰船的判定功能正常,但加载存档后,明明击中舰船却显示未命中。

我已实现玩家与对手游戏棋盘的序列化功能,怀疑错误出在vector<Ship5>的序列化环节,但不知道如何处理——这个vector是Board类的受保护成员。

序列化实现代码

ostream &operator<<(ostream &out, const Board &b) {    
    for (int row = 0; row < BOARDSIZE; row++) {        
        for (int col = 0; col < BOARDSIZE; col++) {            
            out << b.board[row][col]<< ' ';        
        }        
        out << '\n';    
    }    
    out << '\n';    
    return out;
} 

istream & operator>>(istream & in, Board &b){    
    for (int row = 0; row < BOARDSIZE; row++) {        
        for (int col = 0; col < BOARDSIZE; col++) {            
            in >> b.board[row][col];        
        }    
    }     
    return in;
}

攻击功能实现

  • getSpaceValue:返回棋盘对应位置的值,例如M代表未命中,X代表击中
  • recordHit:返回布尔值——击中舰船则返回true并将对应位置设为X;未击中则返回false并标记为M
void Game::atack(Board &b){    
    int attemptCounter = 0;    
    int xEntry, yEntry;    
    bool goodMove=false;    
    string entryTemp;    
    while(!goodMove){        
        if(attemptCounter>0)            
            cout << "Wykonales juz ten ruch sprobuj jeszcze raz"<<endl;        
        cout <<"Wpisz lokalizacje statku ktory chcesz zatakowac "<<endl;        
        entryTemp = validLoc();        
        xEntry=static_cast<int>(entryTemp[0]);        
        yEntry=static_cast<int>(entryTemp[1]);        
        cout<<"Get value wartoswc: "<<(b.getSpaceValue(xEntry-A_ASCII,yEntry-N_ASCII)!=HITTED && b.getSpaceValue(xEntry-A_ASCII, yEntry-N_ASCII)!=MISSED)<<endl;        
        if(b.getSpaceValue(xEntry-A_ASCII,yEntry-N_ASCII)!=HITTED && b.getSpaceValue(xEntry-A_ASCII, yEntry-N_ASCII)!=MISSED){            
            b.recordHit(xEntry-A_ASCII, yEntry-N_ASCII);            
            goodMove = true;        
        }        
        attemptCounter++;    
    }
}

recordHit在正常游戏时工作正常,但加载存档后,即使击中舰船也总是返回false:

bool Board::recordHit(int x, int y){    
    for(int i=0; i<SHIPS_NUM;i++){        
        if(ship[i].recordHit(x,y)){            
            board[y][x]=HITTED;            
            return true;        
        }    
    }    
    board[y][x]=MISSED;    
    return false;
}

补充代码

Board构造函数

Board::Board(){    
    for(int i=0; i<BOARDSIZE; i++){        
        for(int j=0; j<BOARDSIZE;j++){            
            board[i][j]=WATER;        
        }    
    }    
    for(int i=0;i<SHIPS_NUM;i++){        
        ship.push_back(Ship5(SHIP_LENGTH[i]));    
    }
}

Board.h

#include <iostream>
#include <vector>
#include "const.h"
#include "ship.h"
using namespace std;

/**
 * Klasa planszy oraz jej metody
 */
class Board{
protected:
    char board[BOARDSIZE][BOARDSIZE];
    vector<Ship5> ship;
public:
    Board();
    Board& operator=(const Board &board1);
    ~Board()= default;
    void printBoard();
    void showUnhiddenBoard();
    bool setShip(int shipID, int x, int y, bool horizontal);
    bool recordHit(int x, int y);
    int HitsNumber();
    int HitsMiss();
    char getSpaceValue(int x,int y);
    friend ostream & operator<<(ostream & out,const Board & b);
    friend istream & operator>>(istream & in, Board &b);
};

棋盘常量定义

const char WATER = '~';
const char SHIP = '-';
const char MISSED = 'M';
const char HITTED = 'X';
const char UNKNOWN = '?';
问题根源与解决方案

你的序列化代码只处理了board二维数组,完全没有保存和恢复vector<Ship5> ship的数据。

加载存档时,Board对象的ship向量只会执行构造函数里的默认初始化(创建SHIPS_NUM个全新的Ship5实例,没有对应棋盘上舰船的实际位置与状态),而非从存档恢复之前游戏中舰船的真实状态。这导致recordHit遍历ship向量时,找不到对应位置的舰船,自然返回false并标记为未命中。

要解决这个问题,必须补上ship向量的序列化逻辑:

  1. 修改输出运算符operator<<
    先输出board数组,再输出舰船数量,最后逐个序列化每个Ship5对象(需要Ship5类实现自身的operator<<,保存位置、朝向、受损状态等关键数据):

    ostream &operator<<(ostream &out, const Board &b) {    
        // 输出board数组
        for (int row = 0; row < BOARDSIZE; row++) {        
            for (int col = 0; col < BOARDSIZE; col++) {            
                out << b.board[row][col]<< ' ';        
            }        
            out << '\n';    
        }    
        // 输出ship向量的大小与每个舰船数据
        out << b.ship.size() << '\n';
        for(const auto& s : b.ship) {
            out << s << '\n';
        }
        return out;
    }
    
  2. 修改输入运算符operator>>
    先读取board数组,再读取舰船数量,最后逐个读取Ship5对象的数据并添加到ship向量中(需要Ship5类实现operator>>):

    istream & operator>>(istream & in, Board &b){    
        // 读取board数组
        for (int row = 0; row < BOARDSIZE; row++) {        
            for (int col = 0; col < BOARDSIZE; col++) {            
                in >> b.board[row][col];        
            }    
        }     
        // 读取ship向量数据
        size_t shipCount;
        in >> shipCount;
        b.ship.clear(); // 清空原有默认初始化的数据
        for(size_t i=0; i<shipCount; i++) {
            Ship5 s;
            in >> s;
            b.ship.push_back(s);
        }
        return in;
    }
    
  3. 实现Ship5类的序列化
    为Ship5类添加operator<<和operator>>,保存并恢复舰船的关键状态。假设Ship5包含x(横坐标)、y(纵坐标)、horizontal(是否水平放置)、hitPositions(受损位置集合),示例代码如下:

    ostream& operator<<(ostream& out, const Ship5& s) {
        out << s.x << ' ' << s.y << ' ' << s.horizontal << ' ';
        // 输出受损位置数量与每个位置
        out << s.hitPositions.size() << ' ';
        for(const auto& pos : s.hitPositions) {
            out << pos.first << ' ' << pos.second << ' ';
        }
        return out;
    }
    
    istream& operator>>(istream& in, Ship5& s) {
        in >> s.x >> s.y >> s.horizontal;
        size_t hitCount;
        in >> hitCount;
        s.hitPositions.clear();
        for(size_t i=0; i<hitCount; i++) {
            int x, y;
            in >> x >> y;
            s.hitPositions.insert({x, y});
        }
        return in;
    }
    

完成以上修改后,加载存档时Board的ship向量会恢复到之前游戏的状态,recordHit就能正确识别击中的舰船,返回正确结果。


内容的提问来源于stack exchange,提问作者klixo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 07:54:22