// -*-c++-*- //------------------------------------------------------------------------------ // xword - (http://xword.sourceforge.net) // Copyright 2002 Patrick Crosby //------------------------------------------------------------------------------ // Puzzle.cpp // // $Id: Puzzle.cpp,v 1.11 2002/01/23 19:43:03 pcrosby Exp $ //------------------------------------------------------------------------------ // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., 59 Temple // Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ #include #include "Puzzle.h" #include #include #include #include #include "Error.h" #include "FileUtil.h" #include "String.h" #include "Namespace.h" using namespace std; NAMESPACE_OPEN //------------------------------------------------------------------------------ Puzzle::Puzzle(const std::string& strFilename) : m_strFilename(strFilename), m_nSizeX(0), m_nSizeY(0), m_nMaxClueNum(0) { ParseFile(); m_vecLetters.clear(); for (int i = 0; i < m_nSizeY; i++) { std::string s; for (int j = 0; j < m_nSizeX; j++) { #ifdef HAVE_STRING_PUSH_BACK s.push_back(' '); #else s = s + ' '; #endif } m_vecLetters.push_back(s); } Load(); } //------------------------------------------------------------------------------ Puzzle::~Puzzle() { } //------------------------------------------------------------------------------ void Puzzle::Dump() const { cout << "Puzzle dump" << endl; cout << "------------------------------------------------" << endl; cout << "Filename: " << m_strFilename << endl; cout << "Title: " << m_strTitle << endl; cout << "Author: " << m_strAuthor << endl; cout << "Copyright: " << m_strCopyright << endl; cout << "Size: " << m_nSizeX << " x " << m_nSizeY << endl; cout << "Answer Grid:" << endl; StringVec::const_iterator itr; for (itr = m_vecAnswers.begin(); itr != m_vecAnswers.end(); ++itr) { cout << *itr << endl; } cout << endl << "Puzzle Grid:" << endl; for (itr = m_vecGrid.begin(); itr != m_vecGrid.end(); ++itr) { cout << *itr << endl; } /* cout << endl << "Clue Strings:" << endl; for (itr = m_vecClueStrings.begin(); itr != m_vecClueStrings.end(); ++itr) { cout << *itr << endl; } */ ClueMap::const_iterator citr; cout << endl << "Across Clues:" << endl; for (citr = m_mapCluesAcross.begin(); citr != m_mapCluesAcross.end(); ++citr) { cout << (*citr).first << ": " << (*citr).second.GetText() << endl; } cout << endl << "Down Clues:" << endl; for (citr = m_mapCluesDown.begin(); citr != m_mapCluesDown.end(); ++citr) { cout << (*citr).first << ": " << (*citr).second.GetText() << endl; } } //------------------------------------------------------------------------------ void Puzzle::ParseFile() { FILE* pFile = fopen(m_strFilename.c_str(), "r"); if (pFile == NULL) { throw(runtime_error("couldn't open " + m_strFilename)); } // puzzle size // int nResult = fseek(pFile, 0x2C, SEEK_SET); if (nResult == -1) { perror("fseek"); throw(runtime_error("file seek error")); } m_nSizeX = fgetc(pFile); m_nSizeY = fgetc(pFile); // answer grid // char* pString = (char *)malloc(sizeof(char) * (m_nSizeX + 1)); if (pString == NULL) { throw(runtime_error("malloc error")); } nResult = fseek(pFile, 0x34, SEEK_SET); if (nResult == -1) { perror("fseek"); throw(runtime_error("file seek error")); } for (int i = 0; i < m_nSizeY; i++) { int nRead = fread(pString, 1, m_nSizeX, pFile); pString[m_nSizeX] = '\0'; if (nRead != m_nSizeX) { throw(runtime_error("number of bytes read != number of bytes requested")); } m_vecAnswers.push_back(pString); } // puzzle grid // for (int i = 0; i < m_nSizeY; i++) { int nRead = fread(pString, 1, m_nSizeX, pFile); pString[m_nSizeX] = '\0'; if (nRead != m_nSizeX) { throw(runtime_error("number of bytes read != number of bytes requested")); } m_vecGrid.push_back(pString); } // puzzle headers // GetLine(pFile, m_strTitle); GetLine(pFile, m_strAuthor); GetLine(pFile, m_strCopyright); // clue strings // std::string s; while (GetLine(pFile, s) != EOF) { m_vecClueStrings.push_back(s); } // generate clues from grid // // m_SquareGrid.resize(m_nSizeY); for (int i = 0; i < m_nSizeY; i++) { m_SquareGrid[i].resize(m_nSizeX); } int nClueNum = 1; StringVec::const_iterator itrClueString = m_vecClueStrings.begin(); for (int i = 0; i < m_nSizeY; i++) { std::string s = m_vecGrid[i]; for (int j = 0; j < m_nSizeX; j++) { bool bFoundClue = false; Square square; if (s[j] == '-') { square.SetType(Square::BLANK); } else if (s[j] == '.') { square.SetType(Square::SOLID); } else { throw(runtime_error("unknown square type in grid")); } if (square.GetType() == Square::BLANK) { square.SetAcrossNum(0); square.SetDownNum(0); Clue clue(i, j, *itrClueString); // across clues // // check to see if this is the far left border if (j == 0) { // m_mapCluesAcross[nClueNum] = *itrClueString; m_mapCluesAcross[nClueNum] = clue; ++itrClueString; clue.SetText(*itrClueString); square.SetAcrossNum(nClueNum); bFoundClue = true; } else if (s[j - 1] == '.') { m_mapCluesAcross[nClueNum] = clue; ++itrClueString; clue.SetText(*itrClueString); square.SetAcrossNum(nClueNum); bFoundClue = true; } // down clues // // check to see if this is the top border if (i == 0) { m_mapCluesDown[nClueNum] = clue; ++itrClueString; clue.SetText(*itrClueString); square.SetDownNum(nClueNum); bFoundClue = true; } else { std::string strAbove = m_vecGrid[i - 1]; if (strAbove[j] == '.') { m_mapCluesDown[nClueNum] = clue; ++itrClueString; clue.SetText(*itrClueString); square.SetDownNum(nClueNum); bFoundClue = true; } } if (bFoundClue) { nClueNum++; } if (square.GetAcrossNum() == 0) { if (j > 0) { Square pred = m_SquareGrid[i][j - 1]; if (pred.GetType() != Square::SOLID) { square.SetAcrossNum(pred.GetAcrossNum()); } } } if (square.GetDownNum() == 0) { if (i > 0) { Square pred = m_SquareGrid[i - 1][j]; if (pred.GetType() != Square::SOLID) { square.SetDownNum(pred.GetDownNum()); } } } } m_SquareGrid[i][j] = square; } } m_nMaxClueNum = nClueNum; fclose(pFile); } //------------------------------------------------------------------------------ int Puzzle::GetLine(FILE* pFile, std::string& s) { s = ""; if (pFile == NULL) { return EOF; } char c = fgetc(pFile); while (c != EOF && c != '\n' && c != 0) { #ifdef HAVE_STRING_PUSH_BACK s.push_back(c); #else s = s + c; #endif c = fgetc(pFile); } if (c == EOF) { return EOF; } else { return s.length(); } } //------------------------------------------------------------------------------ void Puzzle::GetClue(int nX, int nY, bool bAcross, std::string& strClue, int& nClue) const { Assert(nX >= 0 && nX < m_nSizeX, "nX invalid"); Assert(nY >= 0 && nY < m_nSizeY, "nY invalid"); strClue = "(unknown)"; Square s = m_SquareGrid[nY][nX]; if (bAcross) { nClue = s.GetAcrossNum(); if (nClue > 0) { ClueMap::const_iterator itr = m_mapCluesAcross.find(nClue); if (itr != m_mapCluesAcross.end()) { strClue = itr->second.GetText(); } } } else { nClue = s.GetDownNum(); if (nClue > 0) { ClueMap::const_iterator itr = m_mapCluesDown.find(nClue); if (itr != m_mapCluesDown.end()) { strClue = itr->second.GetText(); } } } } //------------------------------------------------------------------------------ void Puzzle::SetLetter(int nRow, int nCol, char c) { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); Assert(nCol >= 0 && nCol < m_nSizeX, "invalid col"); m_vecLetters[nRow][nCol] = c; } //------------------------------------------------------------------------------ char Puzzle::GetLetter(int nRow, int nCol) const { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); Assert(nCol >= 0 && nCol < m_nSizeX, "invalid col"); return m_vecLetters[nRow][nCol]; } //------------------------------------------------------------------------------ std::string Puzzle::GetLetterRow(int nRow) const { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); return m_vecLetters[nRow]; } //------------------------------------------------------------------------------ bool Puzzle::IsEmpty(int nRow, int nCol) const { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); Assert(nCol >= 0 && nCol < m_nSizeX, "invalid col"); return (GetLetter(nRow, nCol) == ' '); } //------------------------------------------------------------------------------ bool Puzzle::IsSolid(int nRow, int nCol) const { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); Assert(nCol >= 0 && nCol < m_nSizeX, "invalid col"); return (m_SquareGrid[nRow][nCol].GetType() == Square::SOLID); } //------------------------------------------------------------------------------ bool Puzzle::IsFull(int nRow, int nCol, bool bCountLetters) const { Assert(nRow >= 0 && nRow < m_nSizeY, "invalid row"); Assert(nCol >= 0 && nCol < m_nSizeX, "invalid col"); if (bCountLetters) { return (IsSolid(nRow, nCol) || (!IsEmpty(nRow, nCol))); } else { return (IsSolid(nRow, nCol)); } } //------------------------------------------------------------------------------ int Puzzle::GetClueRow(int nClue, bool bAcross) const { int nRow = -1; if (nClue > 0) { if (bAcross) { ClueMap::const_iterator itr = m_mapCluesAcross.find(nClue); if (itr != m_mapCluesAcross.end()) { nRow = itr->second.GetRow(); } } else { ClueMap::const_iterator itr = m_mapCluesDown.find(nClue); if (itr != m_mapCluesDown.end()) { nRow = itr->second.GetRow(); } } } return nRow; } //------------------------------------------------------------------------------ int Puzzle::GetClueCol(int nClue, bool bAcross) const { int nCol = -1; if (nClue > 0) { if (bAcross) { ClueMap::const_iterator itr = m_mapCluesAcross.find(nClue); if (itr != m_mapCluesAcross.end()) { nCol = itr->second.GetCol(); } } else { ClueMap::const_iterator itr = m_mapCluesDown.find(nClue); if (itr != m_mapCluesDown.end()) { nCol = itr->second.GetCol(); } } } return nCol; } //------------------------------------------------------------------------------ void Puzzle::Save() const { FileUtil::AssertDirectoryExists(Filename("~/.xword/")); std::string strFilename = Filename("~/.xword/") + EncodeFilename(m_strFilename); ofstream os(strFilename.c_str()); if (!os.is_open()) { throw(runtime_error("couldn't open save file: " + strFilename)); } StringVec::const_iterator itr; for (itr = m_vecLetters.begin(); itr != m_vecLetters.end(); ++itr) { os << *itr << endl; } os.close(); } //------------------------------------------------------------------------------ void Puzzle::Load() { std::string strFilename = Filename("~/.xword/") + EncodeFilename(m_strFilename); if (!FileUtil::FileExists(strFilename)) { return; } ifstream is(strFilename.c_str()); m_vecLetters.clear(); std::string strRow; while(getline(is, strRow)) { if ((long)strRow.length() != m_nSizeX) { cerr << "row too short: '" << strRow << "'" << endl; } m_vecLetters.push_back(strRow); } if ((long)m_vecLetters.size() != m_nSizeY) { cerr << "not enough rows: " << m_vecLetters.size() << " (wanted " << m_nSizeY << ")" << endl; } is.close(); } //------------------------------------------------------------------------------ std::string Puzzle::EncodeFilename(const std::string& strIn) const { std::string strResult; /* int nCheckSum = 0; std::string::const_iterator itr; for (itr = strIn.begin(); itr != strIn.end(); ++itr) { nCheckSum += (int)(*itr); } strResult = String(nCheckSum) + ".sav"; */ std::string::size_type nPos = strIn.rfind('/'); if (nPos != std::string::npos) { strResult = strIn.substr(nPos) + ".sav"; } else { strResult = strIn + ".sav"; } return strResult; } //------------------------------------------------------------------------------ NAMESPACE_CLOSE //------------------------------------------------------------------------------