Skip to content

Commit

Permalink
Add IQ mini tiling puzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzm committed Jun 18, 2024
1 parent fe52055 commit fef764f
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 8 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(Boost 1.58 COMPONENTS
find_package(Boost 1.85 COMPONENTS
program_options REQUIRED
)

Expand All @@ -18,7 +18,7 @@ add_executable(dlx
main.cpp
)

target_link_libraries(dlx
target_link_libraries(dlx
common
queens
sudoku
Expand Down
19 changes: 13 additions & 6 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string>
#include <vector>

#include "Iq.hpp"
#include "Pentominoes.hpp"
#include "Pyramid.hpp"
#include "Queens.hpp"
Expand Down Expand Up @@ -55,11 +56,13 @@ int main(int argc, const char *argv[])

po::options_description puzzleOpts("Puzzle options (exactly one required)");
puzzleOpts.add_options()
("pentominoes", po::value<Pentominoes *>(),
("iq", po::bool_switch(),
"IQ mini tiling puzzle (will prompt for peg locations)")
("pentominoes", po::value<Pentominoes *>(),
"pentominoes (one of: 6x10, 5x12, 4x15, or 3x20)")
("pyramid", po::bool_switch(),
"Project Genius 3d pyramid puzzle")
("queens", po::value<int>()->notifier(checkGreaterZero),
("queens", po::value<int>()->notifier(checkGreaterZero),
"n-queens (number of queens, greater than zero)")
("soma", po::bool_switch(),
"Soma cube puzzle")
Expand All @@ -81,7 +84,7 @@ int main(int argc, const char *argv[])

po::variables_map vm;
po::store(po::parse_command_line(argc, argv, opts), vm);
po::notify(vm);
po::notify(vm);

if ((argc == 1) || vm.count("help")) {
cout << "Exact cover puzzle solver using Don Knuth \"Dancing Links\" algorithm" << endl;
Expand All @@ -90,11 +93,12 @@ int main(int argc, const char *argv[])
return 1;
}

if ((vm.count("pentominoes")
if ((vm.count("pentominoes")
+ (vm["pyramid"].defaulted() ? 0 : 1)
+ vm.count("queens")
+ vm.count("queens")
+ (vm["soma"].defaulted() ? 0 : 1)
+ (vm["sudoku"].defaulted() ? 0 : 1))
+ (vm["sudoku"].defaulted() ? 0 : 1)
+ (vm["iq"].defaulted() ? 0 : 1))
!= 1)
{
throw po::error_with_no_option_name("must specify exactly one puzzle option");
Expand All @@ -115,6 +119,9 @@ int main(int argc, const char *argv[])
} else if (vm["sudoku"].as<bool>()) {
auto puzzle = make_unique<Sudoku>();
puzzle->solve(vm["count"].as<bool>());
} else if (vm["iq"].as<bool>()) {
auto puzzle = make_unique<Iq>();
puzzle->solve(vm["count"].as<bool>());
}

return 0;
Expand Down
3 changes: 3 additions & 0 deletions tiling/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
add_library(tiling OBJECT
Iq.cpp
Iq.hpp
Pentominoes.cpp
Pentominoes.hpp
Pyramid.cpp
Expand All @@ -9,6 +11,7 @@ add_library(tiling OBJECT

target_include_directories(tiling PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${Boost_INCLUDE_DIRS}
)

target_link_libraries(tiling PUBLIC
Expand Down
285 changes: 285 additions & 0 deletions tiling/Iq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <vector>

#include "Iq.hpp"

#include "boost/algorithm/string.hpp"

using namespace std;


class IqCell
{
public:

int rOffset;
int cOffset;

IqCell(int row, int col)
: rOffset(row), cOffset(col)
{
}

bool operator<(IqCell const& rhs) const
{
if (rOffset < rhs.rOffset) return true;
if (rOffset > rhs.rOffset) return false;
if (cOffset < rhs.cOffset) return true;
if (cOffset > rhs.cOffset) return false;
return false;
}

std::string name() const
{
ostringstream str;
str << setfill('0') << setw(2) << rOffset << setw(2) << cOffset;
return str.str();
}

};


class IqPiece
{
public:

string name;
vector<IqCell> cells;

bool operator<(IqPiece const& rhs) const
{
if (name < rhs.name) return true;
if (name > rhs.name) return false;
if (cells < rhs.cells) return true;
if (cells > rhs.cells) return false;
return false;
}

};


namespace {

vector<IqPiece> pieces = {
{"T", {{0,0},{0,1},{0,2},{1,1}}},
{"I", {{0,0},{0,1},{0,2}}},
{"L", {{0,0},{0,1},{0,2},{1,0}}},
{"S", {{0,0},{0,1},{1,1},{1,2}}},
{"O", {{0,0},{0,1},{1,0},{1,1}}},
{"R", {{0,0},{0,1},{1,1}}},
{"P0", {{0,0}}},
{"P1", {{0,0}}},
{"P2", {{0,0}}},
};

} // anonymous namespace


void Iq::addPieceAspects(IqPiece const& piece, set<IqPiece>& aspects)
{
auto aspect = piece;
for(int flip=0; flip<2; ++flip) {
for(int rot=0; rot<4; ++rot) {
if (aspectFilter(piece, flip, rot)) continue;
sort(aspect.cells.begin(), aspect.cells.end());
int minrow = numeric_limits<int>::max();
int mincol = numeric_limits<int>::max();
for(auto const& cell: aspect.cells) {
minrow = min(minrow, cell.rOffset);
mincol = min(mincol, cell.cOffset);
}
for(auto& cell: aspect.cells) {
cell.rOffset -= minrow;
cell.cOffset -= mincol;
}
aspects.insert(aspect);
for(auto& cell: aspect.cells) {
auto temp = cell.rOffset;
cell.rOffset = cell.cOffset;
cell.cOffset = -temp;
}
}
for(auto& cell: aspect.cells) {
cell.rOffset = -cell.rOffset;
}
}
}


void Iq::addAspectPlacements(
IqPiece const& aspect,
set<IqCell> const& board,
vector<IqPiece> & placements)
{
for(auto const& pos: board) {
auto placement = aspect;
bool fit = true;
for(auto& cell: placement.cells) {
cell.rOffset += pos.rOffset;
cell.cOffset += pos.cOffset;
if (board.count(cell) == 0) {
fit = false;
break;
}
}
if (fit & !placementFilter(aspect, pos)) {
placements.push_back(placement);
}
}
}


bool Iq::aspectFilter(IqPiece const& piece, int flip, int rot)
{
return false;
}


bool Iq::placementFilter(IqPiece const& aspect, IqCell const& pos)
{
if ((aspect.name == "P0") && ((pos.cOffset != c0) || (pos.rOffset != r0))) {
return true;
} else if ((aspect.name == "P1") && ((pos.cOffset != c1) || (pos.rOffset != r1))) {
return true;
} else if ((aspect.name == "P2") && ((pos.cOffset != c2) || (pos.rOffset != r2))) {
return true;
} else {
return false;
}
}


void Iq::init()
{
cout << "┌───────┬───┬───────┐" << endl;
cout << "│ A B │ │ D E │" << endl;
cout << "│ └───┴───┐ │" << endl;
cout << "│ F G H I │ J │" << endl;
cout << "├───┐ ┌───────┤ │" << endl;
cout << "│ │ L │ M N │ O │" << endl;
cout << "├───┤ │ ┌───┤ │" << endl;
cout << "│ P │ Q │ R │ │ T │" << endl;
cout << "│ └───┘ ├───┘ │" << endl;
cout << "│ U V W │ X Y │" << endl;
cout << "└───────────┴───────┘" << endl;
cout << endl;
cout << "Enter locations for one peg in each of the three tracks, using the legend above: ";

string line;
getline(cin, line);
if (line.length() != 3) throw runtime_error("not 3 characters");
boost::to_upper(line);
sort(line.begin(), line.end());

string p0 = "ABFGHILQ";
string p1 = "DEJOTXY";
string p2 = "MNPRUVW";

string i0;
string i1;
string i2;

set_intersection(line.begin(), line.end(), p0.begin(), p0.end(), back_inserter(i0));
set_intersection(line.begin(), line.end(), p1.begin(), p1.end(), back_inserter(i1));
set_intersection(line.begin(), line.end(), p2.begin(), p2.end(), back_inserter(i2));

if ((i0.size() != 1) || (i1.size() != 1) || (i2.size() != 1)) {
throw runtime_error("not one letter from each track");
}

r0 = (i0[0]-'A') / 5;
c0 = (i0[0]-'A') % 5;
r1 = (i1[0]-'A') / 5;
c1 = (i1[0]-'A') % 5;
r2 = (i2[0]-'A') / 5;
c2 = (i2[0]-'A') % 5;

cout << endl;

subGoals.emplace_back();
auto& sg = subGoals.back();

set<IqPiece> aspects;
for(auto const& piece: pieces) {
addPieceAspects(piece, aspects);
}

set<IqCell> board;
for(auto r=0; r<5; ++r) {
for(auto c=0; c<5; ++c) {
board.insert(IqCell(r, c));
}
}

vector<IqPiece> placements;
for(auto const& aspect: aspects) {
addAspectPlacements(aspect, board, placements);
}

for(auto const& placement: placements) {
auto c = sg.matrix.findColumn(placement.name);
auto e = new Element();
e->insertUD(c);
++sg.elems;
for(auto const& cell: placement.cells) {
auto c2 = sg.matrix.findColumn(cell.name());
auto e2 = new Element();
e2->insertUD(c2);
e2->insertLR(e);
++sg.elems;
++sg.rows;
}
}
}


void Iq::print(vector<Element *>& solution)
{
static int count = 0;

static const string corner[16] = {
"\u0020", "\u2575", "\u2574", "\u2518",
"\u2576", "\u2514", "\u2500", "\u2534",
"\u2577", "\u2502", "\u2510", "\u2524",
"\u250c", "\u251c", "\u252c", "\u253c"
};

map<IqCell, string> board;
int rmax = 0;
int cmax = 0;

for(auto const& re: solution) {
auto pe = re;
for(; pe->col->name.length() > 2; pe=pe->l);
for(auto ce=pe->r; ce!=pe; ce=ce->r) {
int r = stoi(ce->col->name.substr(0,2)) + 1;
int c = stoi(ce->col->name.substr(2,2)) + 1;
rmax = max(rmax, r);
cmax = max(cmax, c);
board[IqCell(r,c)] = pe->col->name;
}
}

cout << "#" << ++count << ":" << endl;
for(int r=1; r<=rmax+1; ++r) {
for(int c=1; c<=cmax+1; ++c) {
cout << corner[
((board[IqCell(r-1, c-1)] != board[IqCell(r-1, c )]) ? 1 : 0) +
((board[IqCell(r-1, c-1)] != board[IqCell(r, c-1)]) ? 2 : 0) +
((board[IqCell(r-1, c )] != board[IqCell(r, c )]) ? 4 : 0) +
((board[IqCell(r, c-1)] != board[IqCell(r, c )]) ? 8 : 0)
];
cout << ((board[IqCell(r-1, c)] != board[IqCell(r, c)])
? "\u2500\u2500\u2500" : "\u0020\u0020\u0020");
}
cout << endl;
for(int c=1; c<=cmax+1; ++c) {
cout << ((board[IqCell(r, c-1)] != board[IqCell(r, c)]) ? "\u2502" : "\u0020");
cout << "\u0020\u0020\u0020";
}
cout << endl;
}
}
Loading

0 comments on commit fef764f

Please sign in to comment.