From bc9bd893fccfa0952401fb1eeb333ad0fc362e0b Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Fri, 29 Sep 2023 19:29:41 -0700 Subject: [PATCH 1/2] Improve handling of puzzles without solutions. - Support IPUZ/JPZ files without solutions, leveraging the existing PUZ grid flag. For IPUZ files, we can look at whether the solution grid is present. For JPZ files, we look at whether any letter squares have non-empty solutions. - Respect the "no solution" grid flag when evaluating the completion status. This prevents us from thinking that the grid is correctly filled when it is fully empty. --- puz/formats/ipuz/load_ipuz.cpp | 4 ++++ puz/formats/jpz/load_jpz.cpp | 9 ++++++++- src/XGridCtrl.cpp | 18 +++++++++--------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/puz/formats/ipuz/load_ipuz.cpp b/puz/formats/ipuz/load_ipuz.cpp index 9a54a0ad..e8452116 100644 --- a/puz/formats/ipuz/load_ipuz.cpp +++ b/puz/formats/ipuz/load_ipuz.cpp @@ -309,6 +309,10 @@ bool ipuzParser::DoLoadPuzzle(Puzzle * puz, json::Value * root) } }); } + else + { + puz->GetGrid().SetFlag(FLAG_NO_SOLUTION); + } // User grid if (doc->Contains(puzT("saved"))) diff --git a/puz/formats/jpz/load_jpz.cpp b/puz/formats/jpz/load_jpz.cpp index f991ff3c..8d7859e4 100644 --- a/puz/formats/jpz/load_jpz.cpp +++ b/puz/formats/jpz/load_jpz.cpp @@ -226,6 +226,7 @@ bool jpzParser::DoLoadPuzzle(Puzzle * puz, xml::document & doc) } // Grid cells + bool has_solution = false; xml::node cell = RequireChild(grid_node, "cell"); for (; cell; cell = cell.next_sibling("cell")) { @@ -260,7 +261,10 @@ bool jpzParser::DoLoadPuzzle(Puzzle * puz, xml::document & doc) else // type == puzT("letter") || type == puzT("clue") { square->SetMissing(false); - square->SetSolution(GetAttribute(cell, "solution")); + string_t solution = GetAttribute(cell, "solution"); + square->SetSolution(solution); + if (!solution.empty()) + has_solution = true; square->SetText(GetAttribute(cell, "solve-state")); if (type == puzT("clue")) square->SetAnnotation(true); @@ -314,6 +318,9 @@ bool jpzParser::DoLoadPuzzle(Puzzle * puz, xml::document & doc) image.child_value("encoded-image")); } } + + if (!has_solution) + grid.SetFlag(FLAG_NO_SOLUTION); } // Words diff --git a/src/XGridCtrl.cpp b/src/XGridCtrl.cpp index ad70e4c8..9d385975 100644 --- a/src/XGridCtrl.cpp +++ b/src/XGridCtrl.cpp @@ -340,24 +340,24 @@ XGridCtrl::UnscrambleSolution(unsigned short key) // Helper for IsCorrect and GetStats CorrectStatus GetCorrectStatus(const puz::Grid * grid, bool correct) { - // We *can* test scrambled puzzles! Not sure why I didn't think of - // this before. (Inspired by Alex Boisvert: - // http://alexboisvert.com/software.html#check) - if (correct) + if (!grid->HasSolution()) + { + return UNCHECKABLE_PUZZLE; + } + else if (correct) { return CORRECT_PUZZLE; } else if (grid->IsScrambled()) { + // We *can* test scrambled puzzles! Not sure why I didn't think of + // this before. (Inspired by Alex Boisvert: + // http://alexboisvert.com/software.html#check) if (grid->CheckScrambledGrid()) return CORRECT_PUZZLE; else return INCORRECT_PUZZLE; } - else if (! grid->HasSolution()) - { - return UNCHECKABLE_PUZZLE; - } else { return INCORRECT_PUZZLE; @@ -403,7 +403,7 @@ XGridCtrl::GetStats(GridStats * stats) const if (square->IsBlank()) { ++stats->blank; - if (square->IsSolutionBlank()) + if (m_grid->HasSolution() && square->IsSolutionBlank()) ++stats->blank_correct; } // If the puzzle is correct so far, and without blanks (that do From a1898a4881c61552b890351cba98b57db9d95df3 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Thu, 5 Oct 2023 16:04:03 -0700 Subject: [PATCH 2/2] Support saving JPZ files without solutions. - Omit the (empty) solution attribute from all cells - Disable check/reveal actions - Show completion message regardless of grid correctness --- puz/formats/jpz/save_jpz.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/puz/formats/jpz/save_jpz.cpp b/puz/formats/jpz/save_jpz.cpp index cb7eda70..3342fb3b 100644 --- a/puz/formats/jpz/save_jpz.cpp +++ b/puz/formats/jpz/save_jpz.cpp @@ -61,8 +61,6 @@ void SaveJpz(Puzzle * puz, const std::string & filename, void * /* dummy */) if (grid.IsScrambled()) throw ConversionError("Jpz does not support scrambled puzzles"); - if (! grid.HasSolution()) - throw ConversionError("Jpz does not support puzzles without a solution"); if (grid.GetType() == TYPE_DIAGRAMLESS) throw ConversionError("Can't save a diagramless puzzle as jpz."); @@ -88,16 +86,21 @@ void SaveJpz(Puzzle * puz, const std::string & filename, void * /* dummy */) xml::node actions = settings.append_child("actions"); actions.append_attribute("buttons-layout") = "below"; - actions.append_child("check").append_attribute("label") = "Check"; - actions.append_child("reveal-letter").append_attribute("label") = - "Reveal Letter"; - actions.append_child("reveal-word").append_attribute("label") = - "Reveal Word"; actions.append_child("revert").append_attribute("label") = "Clear All"; - actions.append_child("solution").append_attribute("label") = "Solution"; - xml::node completion = settings.append_child("completion"); - completion.append_attribute("only-if-correct") = "true"; + if (grid.HasSolution()) { + actions.append_child("check").append_attribute("label") = "Check"; + actions.append_child("reveal-letter").append_attribute("label") = + "Reveal Letter"; + actions.append_child("reveal-word").append_attribute("label") = + "Reveal Word"; + actions.append_child("solution").append_attribute("label") = "Solution"; + completion.append_attribute("only-if-correct") = "true"; + } + else { + completion.append_attribute("only-if-correct") = "false"; + } + string_t message = puz->GetMeta(puzT("completion")); if (message.empty()) { message = puzT("Congratulations, you have solved the puzzle!"); @@ -172,8 +175,9 @@ void SaveJpz(Puzzle * puz, const std::string & filename, void * /* dummy */) { if (square->IsAnnotation()) cell.append_attribute("type") = "clue"; - cell.append_attribute("solution") = - encode_utf8(square->GetSolution()).c_str(); + if (grid.HasSolution()) + cell.append_attribute("solution") = + encode_utf8(square->GetSolution()).c_str(); if (square->HasNumber()) cell.append_attribute("number") = encode_utf8(square->GetNumber()).c_str();