#include // library for printing #include // support for strings #include // support for vectors #include // support for hash-tables #include #include // support for reading files using namespace std; // For compiling C++ code // g++ a.c -o a string ToUpper(string s) { string s2; for (char c : s) { s2.push_back(toupper(c)); } return s2; } // -------------------------------------------------------------------------------- //-Point struct Point { Point() {} Point(int r, int c) : row(r), col(c) {} friend ostream& operator<<(ostream& os, const Point& p); // overloading the "<<" op int row = 0; // defaults value for the constructor int col = 0; }; ostream& operator<<(ostream& os, const Point& p) { // for printing os << "(" << p.row << "," << p.col << ")"; return os; } // -------------------------------------------------------------------------------- //-Span struct Span { Span(Point p, int l, bool v) : point(p), len(l), vert(v) {} Point GetPoint(int i) const { assert(i >= 0 && i < len); if (vert) { return Point(point.row + i, point.col); } else { return Point(point.row, point.col + i); } } friend ostream& operator<<(ostream& os, const Span& s); Point point; int len; bool vert; }; typedef vector Spans; // No need for pointers, as spans are not so numerous ostream& operator<<(ostream& os, const Span& s) { os << "[" << s.point << " len=" << s.len << " vert=" << s.vert << "]"; return os; } // -------------------------------------------------------------------------------- //-Word struct Word { Word() {} // default empty constructor Word(string s) : word(s) {} // standard way of initialization int len() const { return word.length(); } string word; }; typedef vector Words; // will store pointers to Words (will not delete by itself) typedef unordered_map WordMap; // hash-table from stl // -------------------------------------------------------------------------------- //-Library class Library { public: Library() {} // hash-tables are automatically initialized ~Library() { // destructor of the pointers (instead of the unique pointer) for (Word* w : words_) { delete w; } } // Returns NULL if can't find any matches to the given /pattern const Words* FindWord(const string& s) const { // references are prefered instead of copies auto it = word_map_.find(s); if (it != word_map_.end()) { return &it->second; // address of the vector of words } else { return NULL; } } bool IsWord(string s) const { auto it = word_map_.find(s); // use iterator if (it == word_map_.end()) { return false; // if word is not found on hast-table } else { return true; } //return word_map_.count(s) > 0; // True if word exists } void ComputeStats() { assert(counts_.empty()); counts_.resize(18); for (const Word* w : words_) { // Word is a pointer! int len = w->word.length(); // w is not an actual object (it's a pointer) if (len < 18) { counts_[len]++; } } } void PrintStats() const { cout << "Here are the counts of each word length:\n"; for (int i = 1; i < counts_.size(); i++) { cout << "[" << i << "] " << counts_[i] << "\n"; } } string GetWord(int i) const { assert(i >= 0 && i < words_.size()); return words_[i]->word; } void CreatePatternHash(Word* w) { int len = w->len(); int num_patterns = 1 << len; // create 2^len patterns // cout << "PATTERN HASH on " << w->word << "\n"; for (int i=0; iword; for (int j=0; j> j) & 1) { // get every bit and check if it's 1 temp[j] = '-'; } } // cout << " " << temp << "\n"; word_map_[temp].push_back(w); } } void ReadFromFile(string filename, int max_size) { ifstream f; f.open(filename); while (f.is_open() && !f.eof()) { // check for the file! string line; getline(f, line); // cout << line << "\n"; if (!line.empty()) { line = ToUpper(line); int len = line.length(); if (line[len - 1] == '\r') { line = line.substr(0, len - 1); } if (len <= max_size) { Word* w = new Word(line); words_.push_back(w); // Word would be allocated on the heap CreatePatternHash(w); } } } cout << "Read " << words_.size() << " words from file '" << filename << "'\n"; } void DebugBuckets() const { for (int i = 0; i < word_map_.bucket_count(); i++) { cout << "[" << i << "] " << word_map_.bucket_size(i) << "\n"; } } private: // _ is used to indicate privacy Words words_; // master vector of words WordMap word_map_; // pattern hash vector counts_; }; Library lib; // not ideal, but it is not so bad for this application // -------------------------------------------------------------------------------- //-Attr struct Attr { bool is_empty() const { return has_blanks && !has_letters; } bool is_partial() const { return has_blanks && has_letters; } bool is_full() const { return !has_blanks && has_letters; } bool has_letters = false; bool has_blanks = false; }; // -------------------------------------------------------------------------------- //-Grid struct Grid { Grid(string n) { name = n; } int rows() const { return lines.size(); } int cols() const { if (lines.empty()) { return 0; } else { return lines[0].size(); } } int max_size() const { return max(rows(), cols()); } // Returns character value of the box at point 'p' // 'p' must be in bounds char box(const Point& p) const { assert(in_bounds(p)); return lines[p.row][p.col]; } // Returns true if point p is a '.' "block" in the grid // 'p' must be in bounds bool is_block(const Point& p) const { return box(p) == '.'; } bool is_blank(const Point& p) const { return box(p) == '-'; } bool is_letter(const Point& p) const { char c = box(p); return c >= 'A' && c <= 'Z'; } bool in_bounds(const Point& p) const { return (p.row >= 0 && p.row < rows() && p.col >= 0 && p.col < cols()); } // Fills in attributes of the string string GetString(const Span& s, Attr& attr) const { int len = s.len; string temp; temp.resize(len); for (int i=0; i= 'A' && c <= 'Z') { attr.has_letters = true; } temp[i] = box(p); } return temp; } // Next increments the point across the grid, one box at a time // Returns true if point is still in bounds bool Next(Point& p, bool vert) { if (vert) { p.row++; if (p.row >= rows()) { p.row = 0; p.col++; } } else { p.col++; if (p.col >= cols()) { p.col = 0; p.row++; } } return in_bounds(p); } // NextStopAtWrap is like "Next" except it returns false at every wrap // Returns true if we stay on the same line bool NextStopAtWrap(Point& p, bool vert) { bool wrap = false; if (vert) { p.row++; if (p.row >= rows()) { p.row = 0; p.col++; wrap = true; } } else { p.col++; if (p.col >= cols()) { p.col = 0; p.row++; wrap = true; } } return !wrap; } void FillSpans(bool vert) { Point p; // check all spans while (in_bounds(p)) { // for each span while (in_bounds(p) && is_block(p)) { Next(p, vert); } if (!in_bounds(p)) return; Point startp = p; // cout << "SPAN START: " << p << "\n"; int len = 0; bool keep_going = false; do { keep_going = NextStopAtWrap(p, vert); len++; } while (keep_going && !is_block(p)); //cout << "END OF SPAN!!! len=" << len << "\n"; spans.push_back(Span(startp, len, vert)); } } // Add to 'spans' vector with all viable spans in the grid void FillSpans() { assert(spans.empty()); FillSpans(false); // horiz FillSpans(true); // vert } void LoadFromFile(string filename) { ifstream f; f.open("test"); while (f.is_open() && !f.eof()) { // check for the file string line; getline(f, line); // cout << line << "\n"; if (!line.empty() && line[0] != '#') { lines.push_back(line); } } } void Check() const { for (string s : lines) { assert(s.size() == cols()); } } void Print() const { cout << "Grid: " << name << " (rows=" << rows() << ", cols=" << cols() << ", max_size=" << max_size() << ")\n"; for (string s : lines) { cout << " " << s << "\n"; } } void PrintSpans() const { cout << "Spans:\n"; for (const Span& s : spans) { Attr attr; cout << " " << s << " " << GetString(s, attr) << "\n"; } } string name; // strings are initialized empty vector lines; Spans spans; }; // -------------------------------------------------------------------------------- //-Slot struct Slot { Slot(const Span s, const string& p) : span(s), pattern(p) {} friend ostream& operator<<(ostream& os, const Slot& s); Span span; string pattern; }; typedef vector Slots; ostream& operator<<(ostream& os, const Slot& s) { os << s.span << " '" << s.pattern << "'"; return os; } // -------------------------------------------------------------------------------- //-Solver class Solver { public: Solver(Grid* g) : grid_(g) {} // pointer to the grid void Solve() { cout << "Solving this grid:\n"; grid_->Print(); Loop(); } private: void Loop() { Slots empty_slots; // these are the ones we want to work on Slots partial_slots; Slots full_slots; for (const Span& s : grid_->spans) { Attr attr; string temp = grid_->GetString(s, attr); if (attr.is_empty()) { empty_slots.push_back(Slot(s, temp)); } else if (attr.is_partial()) { partial_slots.push_back(Slot(s, temp)); } else if (attr.is_full()) { full_slots.push_back(Slot(s, temp)); } } int num_empty = empty_slots.size(); int num_partial = partial_slots.size(); int num_full = full_slots.size(); cout << "empty = " << num_empty << "\n"; cout << "partial = " << num_partial << "\n"; cout << "full = " << num_full << "\n"; if (num_partial == 0 && num_empty == 0) { cout << "SOLUTION!!\n"; // FIX what do we do here? } assert(num_partial > 0); CommitSlot(partial_slots[0]); } void CommitSlot(const Slot& slot) { cout << "COMMIT slot " << slot << "\n"; cout << "Possible word choices for this slot are:\n"; const Words* words = lib.FindWord(slot.pattern); if (words) { for (const Word* w : *words) { cout << " " << w->word; } cout << "\n"; } else { cout << "NO MATCHES to pattern\n"; } } Grid* grid_; }; // -------------------------------------------------------------------------------- int main() { Grid grid("MY GRID"); // grid lives on the stack grid.LoadFromFile("test"); grid.Check(); grid.FillSpans(); grid.PrintSpans(); lib.ReadFromFile("top_12000.txt", grid.max_size()); Solver solver(&grid); // address pointer to the grid solver.Solve(); }