457 lines
12 KiB
C

#include <iostream> // library for printing
#include <string> // support for strings
#include <vector> // support for vectors
#include <unordered_map> // support for hash-tables
#include <assert.h>
#include <fstream> // support for reading files
using namespace std;
// For compiling C++ code
// g++ a.c -o a
// with optimizations
// g++ a.c -O2 -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<Span> 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<Word*> Words; // will store pointers to Words (will not delete by itself)
typedef unordered_map<string, Words> 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; i<num_patterns; i++) {
// cout << " " << i << "\n";
string temp = w->word;
for (int j=0; j<len; j++) {
if ((i >> 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);
if (!line.empty()) {
line = ToUpper(line);
int len = line.length();
if (line[len - 1] == '\r') {
line = line.substr(0, len - 1);
}
if (line.length() <= 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<int> 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];
}
void write_box(const Point& p, char c) {
assert(in_bounds(p));
lines[p.row][p.col] = c;
}
// 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<len; i++) {
Point p = s.GetPoint(i);
char c = box(p);
if (c == '-') {
attr.has_blanks = true;
} else if (c >= 'A' && c <= 'Z') {
attr.has_letters = true;
}
temp[i] = box(p);
}
return temp;
}
void WriteString(const Span& s, const string& t) {
int len = s.len;
assert(t.length() == len);
for (int i=0; i<len; i++) {
Point p = s.GetPoint(i);
write_box(p, t[i]);
}
}
// 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<string> 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<Slot> Slots;
ostream& operator<<(ostream& os, const Slot& s) {
os << s.span << " '" << s.pattern << "'";
return os;
}
// --------------------------------------------------------------------------------
//-Solver
class Solver {
public:
Solver() {}
void Solve(const Grid& grid) { // reference to the grid
cout << "Solving this grid:\n";
grid.Print();
Loop(grid, 0);
}
private:
void Loop(Grid grid, int depth) { // full copy of the grid to allow recursion
depth++;
// if (depth > 3) {
// cout << "Aborting loop because depth=" << depth << "\n";
// return;
// }
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";
// need to check that all words so far are valid!
for (const Slot& s : full_slots) {
// cout << "CHECKING " << s.pattern << " if it is a word\n";
if (!lib.IsWord(s.pattern)) {
// cout << " --> NOT! ABORT\n";
return;
}
}
if (num_partial == 0 && num_empty == 0) {
cout << "SOLUTION!!\n";
grid.Print();
return;
}
assert(num_partial > 0);
CommitSlot(grid, partial_slots[0], depth);
}
void CommitSlot(Grid& grid, const Slot& slot, int depth) {
// 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 << " Committing '" << w->word << "'!\n";
grid.WriteString(slot.span, w->word);
// cout << "New grid is:\n";
// grid.Print();
Loop(grid, depth); // Recursion
}
} else {
// cout << "NO MATCHES to pattern\n";
}
}
};
// --------------------------------------------------------------------------------
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;
solver.Solve(grid);
}