游戏加载后舰船攻击判定异常:击中却显示未命中(疑与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向量的序列化逻辑:
修改输出运算符
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; }修改输入运算符
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; }实现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

