Beispiel #1
0
char_t Square::ToGrid(int ch)
{
    if (ch == Black[0] || ch == puzT('[') || ch == puzT('*'))
        return 0;

#if PUZ_UNICODE
    return iswprint(ch) ? towupper(ch) : 0;
#else
    return isprint(ch) ? toupper(ch) : 0;
#endif // PUZ_UNICODE
}
Beispiel #2
0
// Default constructor
// Square is white and blank.
Square::Square()
    : m_col(-1),
      m_row(-1),
      m_flag(FLAG_CLEAR),
      m_number(),
      m_red(255),
      m_green(255),
      m_blue(255)
{
    SetText(puzT(""));
    SetSolution(puzT(""));

    // These will be filled in by the grid
    m_next.clear();
}
Beispiel #3
0
// From CamelCase to snake_case
std::string CamelCase(const string_t & name)
{
    // Replace '_' + a lowercase letter with an upper case letter
    string_t str;
    str.reserve(name.size());
    for (size_t i = 0; i < name.size(); ++i)
    {
        if (i == 0 || name[i] == puzT('_'))
        {
            if (i > 0)
                ++i;
            if (i < name.size())
            {
            #if PUZ_UNICODE
                if (::iswlower(name[i]))
                    str.push_back(::towupper(name[i]));
            #else // ! PUZ_UNICODE
                if (::islower(name[i]))
                    str.push_back(::toupper(name[i]));
            #endif // PUZ_UNICODE/! PUZ_UNICODE
                else // Not lower case: so push both the underscore and letter
                {
                    if (i > 0)
                        str.push_back(name[i-1]);
                    str.push_back(name[i]);
                }
            }
            else if (i > 0) // At the end of the string: push the underscore
                str.push_back(name[i-1]);
        }
        else // Not a hypen: push the letter
            str.push_back(name[i]);
    }
    return encode_utf8(str);
}
Beispiel #4
0
string_t Square::GetHtmlColor() const
{
    char_t ret[] = {
        puzT('#'),
        hexDigits[(m_red & 0xf0) >> 4],
        hexDigits[m_red & 0x0f],
        hexDigits[(m_green & 0xf0) >> 4],
        hexDigits[m_green & 0x0f],
        hexDigits[(m_blue & 0xf0) >> 4],
        hexDigits[m_blue & 0x0f],
    };
Beispiel #5
0
static puz::string_t luapuz_checkstring_t(lua_State * L, int index)
{
    try
    {
        return puz::decode_utf8(luaL_checkstring(L, index));
    }
    catch (...)
    {
        luapuz_handleExceptions(L);
    }
    lua_error(L);
    return puzT(""); // This is just to stop the warnings.
}
Beispiel #6
0
void Square::SetColor(const string_t & hexcolor)
{
    size_t len = hexcolor.size();
    if (len > 0 && hexcolor[0] == puzT('#'))
    {
        SetColor(hexcolor.substr(1));
        return;
    }
    try {
        if (len == 3)
            SetColor(ParseHex(hexcolor[0], hexcolor[0]),
                     ParseHex(hexcolor[1], hexcolor[1]),
                     ParseHex(hexcolor[2], hexcolor[2]));
        else if (len == 6)
            SetColor(ParseHex(hexcolor[0], hexcolor[1]),
                     ParseHex(hexcolor[2], hexcolor[3]),
                     ParseHex(hexcolor[4], hexcolor[5]));
    }
    catch (Exception &) {
        // Don't set the color if we can't parse the hex value
    }
}
Beispiel #7
0
//-----------------------------------------------------------------------------
// SaveXPF
//-----------------------------------------------------------------------------
void SaveXPF(Puzzle * puz, const std::string & filename, void * /* dummy */)
{
    const Grid & grid = puz->GetGrid();

    if (grid.IsScrambled())
        throw ConversionError("XPF does not support scrambled puzzles");
    if (! grid.HasSolution())
        throw ConversionError("XPF does not support puzzles without a solution");
    for (const Square * square = puz->GetGrid().First();
         square != NULL;
         square = square->Next())
    {
        if (square->HasImage())
            throw ConversionError("XPF does not support puzzles with background images.");
    }

    xml::document doc;
    xml::node puzzles = doc.append_child("Puzzles");
    puzzles.append_attribute("Version") = "1.0";
    xml::node puzzle = puzzles.append_child("Puzzle");
    // Metadata
    puz::Puzzle::metamap_t & meta = puz->GetMetadata();
    puz::Puzzle::metamap_t::iterator it;
    for (it = meta.begin(); it != meta.end(); ++it)
    {
        if (it->first == puzT("notes")) // "notes" -> "Notepad"
            xml::Append(puzzle, "Notepad", it->second);
        else
            xml::Append(puzzle, xml::CamelCase(it->first).c_str(), it->second);
    }

    // Grid
    if (grid.GetType() == TYPE_DIAGRAMLESS)
        xml::Append(puzzle, "Type", "diagramless");

    // Grid Size
    xml::node size = puzzle.append_child("Size");
    xml::Append(size, "Rows", puz::ToString(grid.GetHeight()));
    xml::Append(size, "Cols", puz::ToString(grid.GetWidth()));

    // Answers
    {
        xml::node grid_node = puzzle.append_child("Grid");
        xml::node row;
        std::string row_text;
        row_text.reserve(grid.GetWidth());
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->IsFirst(ACROSS))
                row = grid_node.append_child("Row");
            if (square->IsMissing())
                row_text.append(1, '~');
            else if (square->IsBlack())
                row_text.append(1, char(Square::Black[0]));
            else if (square->IsSolutionBlank())
                row_text.append(1, ' ');
            else
                row_text.append(1, square->GetPlainSolution());
            if (square->IsLast(ACROSS))
            {
                xml::SetText(row, row_text.c_str());
                row_text.clear();
            }
        }
    }

    // Circles
    {
        xml::node circles = puzzle.append_child("Circles");
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->HasCircle())
            {
                xml::node circle = circles.append_child("Circle");
                circle.append_attribute("Row") = square->GetRow() + 1;
                circle.append_attribute("Col") = square->GetCol() + 1;
            }
        }
        if (circles.empty())
            puzzle.remove_child(circles);
    }

    // Rebus
    {
        xml::node rebus = puzzle.append_child("RebusEntries");
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->HasSolutionRebus())
            {
                xml::node entry = rebus.append_child("Rebus");
                entry.append_attribute("Row") = square->GetRow() + 1;
                entry.append_attribute("Col") = square->GetCol() + 1;
                entry.append_attribute("Short") =
                    std::string(1, square->GetPlainSolution()).c_str();
                xml::SetText(entry, square->GetSolution());
            }
        }
        if (rebus.empty())
            puzzle.remove_child(rebus);
    }

    // Shades
    {
        xml::node shades = puzzle.append_child("Shades");
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->HasColor())
            {
                xml::node shade = shades.append_child("Shade");
                shade.append_attribute("Row") = square->GetRow() + 1;
                shade.append_attribute("Col") = square->GetCol() + 1;
                if (square->HasHighlight())
                    xml::SetText(shade, "gray");
                else
                    xml::SetText(shade, square->GetHtmlColor());
            }
        }
        if (shades.empty())
            puzzle.remove_child(shades);
    }

    // Clues
    {
        xml::node clues_node = puzzle.append_child("Clues");
        Clues & clues = puz->GetClues();
        Clues::iterator clues_it;
        for (clues_it = clues.begin(); clues_it != clues.end(); ++clues_it)
        {
            ClueList & cluelist = clues_it->second;
            ClueList::iterator it;
            for (it = cluelist.begin(); it != cluelist.end(); ++it)
            {
                xml::node clue = clues_node.append_child("Clue");
                // Find the clue direction
                if (! puz->IsDiagramless())
                {
                    const Word & word = it->GetWord();
                    switch (word.GetDirection())
                    {
                    case ACROSS:
                        clue.append_attribute("Dir") = "Across";
                        break;
                    case DOWN:
                        clue.append_attribute("Dir") = "Down";
                        break;
                    case DIAGONAL_SW:
                        clue.append_attribute("Dir") = "Diagonal";
                        break;
                    default:
                        throw ConversionError("XPF clues must be Across, Down, or Diagonal");
                        break;
                    }
                    clue.append_attribute("Row") = word.front()->GetRow() + 1;
                    clue.append_attribute("Col") = word.front()->GetCol() + 1;
                }
                else // diagramless
                {
                    puz::string_t title = cluelist.GetTitle();
                    if (title == puzT("Across"))
                        clue.append_attribute("Dir") = "Across";
                    else if (title == puzT("Down"))
                        clue.append_attribute("Dir") = "Down";
                    else if (title == puzT("Diagonal"))
                        clue.append_attribute("Dir") = "Diagonal";
                    else
                        throw ConversionError("XPF clues must be Across, Down, or Diagonal");
                }
                clue.append_attribute("Num") = encode_utf8(it->GetNumber()).c_str();
                // Clue formatting needs to be escaped if it is XHTML.
                // xml::SetInnerXML(clue, it->GetText());
                xml::SetText(clue, it->GetText());
            }
        }
    }

    // User Grid
    {
        xml::node usergrid = puzzle.append_child("UserGrid");
        xml::node row;
        std::string row_text;
        row_text.reserve(grid.GetWidth());
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->IsFirst(ACROSS))
                row = usergrid.append_child("Row");
            if (square->IsMissing())
                row_text.append(1, '~');
            else if (square->IsBlack())
                row_text.append(1, char(Square::Black[0]));
            else if (square->IsBlank())
                row_text.append(1, ' ');
            else
                row_text.append(1, square->GetPlainText());
            if (square->IsLast(ACROSS))
            {
                xml::SetText(row, row_text.c_str());
                row_text.clear();
            }
        }
        if (usergrid.empty())
            puzzle.remove_child(usergrid);
    }

    // User Rebus
    {
        xml::node rebus = puzzle.append_child("UserRebusEntries");
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->HasTextRebus())
            {
                xml::node entry = rebus.append_child("Rebus");
                entry.append_attribute("Row") = square->GetRow() + 1;
                entry.append_attribute("Col") = square->GetCol() + 1;
                entry.append_attribute("Short") =
                    std::string(1, square->GetPlainText()).c_str();
                xml::SetText(entry, square->GetText());
            }
        }
        if (rebus.empty())
            puzzle.remove_child(rebus);
    }

    // Square Flags
    {
        xml::node flags = puzzle.append_child("SquareFlags");
        const Square * square;
        for (square = grid.First(); square; square = square->Next())
        {
            if (square->GetFlag() != 0)
            {
                xml::node entry = flags.append_child("Flag");
                entry.append_attribute("Row") = square->GetRow() + 1;
                entry.append_attribute("Col") = square->GetCol() + 1;
                if (square->HasFlag(FLAG_PENCIL))
                    entry.append_attribute("Pencil") = "true";
                if (square->HasFlag(FLAG_BLACK))
                    entry.append_attribute("Checked") = "true";
                if (square->HasFlag(FLAG_REVEALED))
                    entry.append_attribute("Revealed") = "true";
                if (square->HasFlag(FLAG_X))
                    entry.append_attribute("Incorrect") = "true";
                if (square->HasFlag(FLAG_CORRECT))
                    entry.append_attribute("Correct") = "true";
            }
        }
        if (flags.empty())
            puzzle.remove_child(flags);
    }

    // Timer
    {
        if (puz->GetTime() != 0)
        {
            xml::node timer = puzzle.append_child("Timer");
            timer.append_attribute("Seconds") = puz->GetTime();
            if (puz->IsTimerRunning())
                timer.append_attribute("Running") = "true";
        }
    }

    doc.save_file(filename.c_str());
}
Beispiel #8
0
    try {
        if (len == 3)
            SetColor(ParseHex(hexcolor[0], hexcolor[0]),
                     ParseHex(hexcolor[1], hexcolor[1]),
                     ParseHex(hexcolor[2], hexcolor[2]));
        else if (len == 6)
            SetColor(ParseHex(hexcolor[0], hexcolor[1]),
                     ParseHex(hexcolor[2], hexcolor[3]),
                     ParseHex(hexcolor[4], hexcolor[5]));
    }
    catch (Exception &) {
        // Don't set the color if we can't parse the hex value
    }
}

static const char_t hexDigits [] = puzT("0123456789abcdef");

string_t Square::GetHtmlColor() const
{
    char_t ret[] = {
        puzT('#'),
        hexDigits[(m_red & 0xf0) >> 4],
        hexDigits[m_red & 0x0f],
        hexDigits[(m_green & 0xf0) >> 4],
        hexDigits[m_green & 0x0f],
        hexDigits[(m_blue & 0xf0) >> 4],
        hexDigits[m_blue & 0x0f],
    };
    return string_t(ret, 7);
}
Beispiel #9
0
 explicit Clue(int num_, const string_t & text_ = puzT(""))
 {
     SetNumber(num_);
     SetText(text_);
 }