Last active
December 16, 2016 06:35
-
-
Save andrew-pa/a78f59ae0e158ed1cb010fb04027ff12 to your computer and use it in GitHub Desktop.
A silly and broken crossword generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################################################### | |
# Set default behavior to automatically normalize line endings. | |
############################################################################### | |
* text=auto | |
############################################################################### | |
# Set default behavior for command prompt diff. | |
# | |
# This is need for earlier builds of msysgit that does not have it on by | |
# default for csharp files. | |
# Note: This is only used by command line | |
############################################################################### | |
#*.cs diff=csharp | |
############################################################################### | |
# Set the merge driver for project and solution files | |
# | |
# Merging from the command prompt will add diff markers to the files if there | |
# are conflicts (Merging from VS is not affected by the settings below, in VS | |
# the diff markers are never inserted). Diff markers may cause the following | |
# file extensions to fail to load in VS. An alternative would be to treat | |
# these files as binary and thus will always conflict and require user | |
# intervention with every merge. To do so, just uncomment the entries below | |
############################################################################### | |
#*.sln merge=binary | |
#*.csproj merge=binary | |
#*.vbproj merge=binary | |
#*.vcxproj merge=binary | |
#*.vcproj merge=binary | |
#*.dbproj merge=binary | |
#*.fsproj merge=binary | |
#*.lsproj merge=binary | |
#*.wixproj merge=binary | |
#*.modelproj merge=binary | |
#*.sqlproj merge=binary | |
#*.wwaproj merge=binary | |
############################################################################### | |
# behavior for image files | |
# | |
# image files are treated as binary by default. | |
############################################################################### | |
#*.jpg binary | |
#*.png binary | |
#*.gif binary | |
############################################################################### | |
# diff behavior for common document formats | |
# | |
# Convert binary document formats to text before diffing them. This feature | |
# is only available from the command line. Turn it on by uncommenting the | |
# entries below. | |
############################################################################### | |
#*.doc diff=astextplain | |
#*.DOC diff=astextplain | |
#*.docx diff=astextplain | |
#*.DOCX diff=astextplain | |
#*.dot diff=astextplain | |
#*.DOT diff=astextplain | |
#*.pdf diff=astextplain | |
#*.PDF diff=astextplain | |
#*.rtf diff=astextplain | |
#*.RTF diff=astextplain |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Ignore Visual Studio temporary files, build results, and | |
## files generated by popular Visual Studio add-ons. | |
# User-specific files | |
*.suo | |
*.user | |
*.userosscache | |
*.sln.docstates | |
# User-specific files (MonoDevelop/Xamarin Studio) | |
*.userprefs | |
# Build results | |
[Dd]ebug/ | |
[Dd]ebugPublic/ | |
[Rr]elease/ | |
[Rr]eleases/ | |
[Xx]64/ | |
[Xx]86/ | |
[Bb]uild/ | |
bld/ | |
[Bb]in/ | |
[Oo]bj/ | |
# Visual Studio 2015 cache/options directory | |
.vs/ | |
# Uncomment if you have tasks that create the project's static files in wwwroot | |
#wwwroot/ | |
# MSTest test Results | |
[Tt]est[Rr]esult*/ | |
[Bb]uild[Ll]og.* | |
# NUNIT | |
*.VisualState.xml | |
TestResult.xml | |
# Build Results of an ATL Project | |
[Dd]ebugPS/ | |
[Rr]eleasePS/ | |
dlldata.c | |
# DNX | |
project.lock.json | |
artifacts/ | |
*_i.c | |
*_p.c | |
*_i.h | |
*.ilk | |
*.meta | |
*.obj | |
*.pch | |
*.pdb | |
*.pgc | |
*.pgd | |
*.rsp | |
*.sbr | |
*.tlb | |
*.tli | |
*.tlh | |
*.tmp | |
*.tmp_proj | |
*.log | |
*.vspscc | |
*.vssscc | |
.builds | |
*.pidb | |
*.svclog | |
*.scc | |
# Chutzpah Test files | |
_Chutzpah* | |
# Visual C++ cache files | |
ipch/ | |
*.aps | |
*.ncb | |
*.opendb | |
*.opensdf | |
*.sdf | |
*.cachefile | |
*.VC.db | |
# Visual Studio profiler | |
*.psess | |
*.vsp | |
*.vspx | |
*.sap | |
# TFS 2012 Local Workspace | |
$tf/ | |
# Guidance Automation Toolkit | |
*.gpState | |
# ReSharper is a .NET coding add-in | |
_ReSharper*/ | |
*.[Rr]e[Ss]harper | |
*.DotSettings.user | |
# JustCode is a .NET coding add-in | |
.JustCode | |
# TeamCity is a build add-in | |
_TeamCity* | |
# DotCover is a Code Coverage Tool | |
*.dotCover | |
# NCrunch | |
_NCrunch_* | |
.*crunch*.local.xml | |
nCrunchTemp_* | |
# MightyMoose | |
*.mm.* | |
AutoTest.Net/ | |
# Web workbench (sass) | |
.sass-cache/ | |
# Installshield output folder | |
[Ee]xpress/ | |
# DocProject is a documentation generator add-in | |
DocProject/buildhelp/ | |
DocProject/Help/*.HxT | |
DocProject/Help/*.HxC | |
DocProject/Help/*.hhc | |
DocProject/Help/*.hhk | |
DocProject/Help/*.hhp | |
DocProject/Help/Html2 | |
DocProject/Help/html | |
# Click-Once directory | |
publish/ | |
# Publish Web Output | |
*.[Pp]ublish.xml | |
*.azurePubxml | |
# TODO: Un-comment the next line if you do not want to checkin | |
# your web deploy settings because they may include unencrypted | |
# passwords | |
#*.pubxml | |
*.publishproj | |
# NuGet Packages | |
*.nupkg | |
# The packages folder can be ignored because of Package Restore | |
**/packages/* | |
# except build/, which is used as an MSBuild target. | |
!**/packages/build/ | |
# Uncomment if necessary however generally it will be regenerated when needed | |
#!**/packages/repositories.config | |
# NuGet v3's project.json files produces more ignoreable files | |
*.nuget.props | |
*.nuget.targets | |
# Microsoft Azure Build Output | |
csx/ | |
*.build.csdef | |
# Microsoft Azure Emulator | |
ecf/ | |
rcf/ | |
# Windows Store app package directory | |
AppPackages/ | |
BundleArtifacts/ | |
# Visual Studio cache files | |
# files ending in .cache can be ignored | |
*.[Cc]ache | |
# but keep track of directories ending in .cache | |
!*.[Cc]ache/ | |
# Others | |
ClientBin/ | |
[Ss]tyle[Cc]op.* | |
~$* | |
*~ | |
*.dbmdl | |
*.dbproj.schemaview | |
*.pfx | |
*.publishsettings | |
node_modules/ | |
orleans.codegen.cs | |
# RIA/Silverlight projects | |
Generated_Code/ | |
# Backup & report files from converting an old project file | |
# to a newer Visual Studio version. Backup files are not needed, | |
# because we have git ;-) | |
_UpgradeReport_Files/ | |
Backup*/ | |
UpgradeLog*.XML | |
UpgradeLog*.htm | |
# SQL Server files | |
*.mdf | |
*.ldf | |
# Business Intelligence projects | |
*.rdl.data | |
*.bim.layout | |
*.bim_*.settings | |
# Microsoft Fakes | |
FakesAssemblies/ | |
# GhostDoc plugin setting file | |
*.GhostDoc.xml | |
# Node.js Tools for Visual Studio | |
.ntvs_analysis.dat | |
# Visual Studio 6 build log | |
*.plg | |
# Visual Studio 6 workspace options file | |
*.opt | |
# Visual Studio LightSwitch build output | |
**/*.HTMLClient/GeneratedArtifacts | |
**/*.DesktopClient/GeneratedArtifacts | |
**/*.DesktopClient/ModelManifest.xml | |
**/*.Server/GeneratedArtifacts | |
**/*.Server/ModelManifest.xml | |
_Pvt_Extensions | |
# LightSwitch generated files | |
GeneratedArtifacts/ | |
ModelManifest.xml | |
# Paket dependency manager | |
.paket/paket.exe | |
# FAKE - F# Make | |
.fake/ | |
/Project1 | |
/puzzle.svg | |
/test.svg | |
/.test.svg.swp | |
/.puzzle.svg.swp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <fstream> | |
#include <string> | |
#include <vector> | |
#include <list> | |
#include <stack> | |
#include <algorithm> | |
#include <memory> | |
#include <random> | |
#include <cctype> | |
#include <locale> | |
#include <iomanip> | |
using namespace std; | |
#include "svg.h" | |
// trim from start | |
inline std::string <rim(std::string &s) { | |
s.erase(s.begin(), std::find_if(s.begin(), s.end(), | |
std::not1(std::ptr_fun<int, int>(std::isspace)))); | |
return s; | |
} | |
// trim from end | |
inline std::string &rtrim(std::string &s) { | |
s.erase(std::find_if(s.rbegin(), s.rend(), | |
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); | |
return s; | |
} | |
// trim from both ends | |
inline std::string &trim(std::string &s) { | |
return ltrim(rtrim(s)); | |
} | |
enum class direction { across, down }; | |
struct ivec2 { | |
int x, y; | |
ivec2(int x = 0, int y = 0) | |
: x(x), y(y) {} | |
inline ivec2 operator +(const ivec2& a) const { return ivec2(x+a.x,y+a.y); } | |
inline ivec2 operator +=(const ivec2& a) { return *this = (*this+a); } | |
inline bool operator ==(const ivec2& a) const { return x==a.x && y==a.y; } | |
}; | |
inline ivec2 direction_to_dp(direction d) { | |
if(d == direction::across) return ivec2(1, 0); | |
else if(d == direction::down) return ivec2(0, 1); | |
else throw; | |
} | |
inline direction ppdl(direction d) { | |
if(d == direction::across) return direction::down; | |
else if(d == direction::down) return direction::across; | |
else throw; | |
} | |
inline ivec2 swap_for_direction(direction d, ivec2 i) { | |
if(d == direction::across) return i; | |
else if(d == direction::down) return ivec2(i.y, i.x); | |
else throw; | |
} | |
struct placed_word { | |
shared_ptr<placed_word> parent; | |
const ivec2 pos; | |
const string word; | |
const direction dr; | |
vector< shared_ptr<placed_word> > children; | |
placed_word(shared_ptr<placed_word> par, ivec2 p, const string& w, direction d) : | |
word(w), dr(d), pos(p), children(w.size(), nullptr), parent(par) {} | |
inline void debug_print(ostream& os, int lvl = 0) { | |
for(int i = 0; i < lvl; ++i) os << "\t"; | |
os << word << endl; | |
for(auto c : children) { | |
if(c) c->debug_print(os,lvl+1); else { | |
// for(int i = 0; i < lvl+1; ++i) os << "\t"; | |
// os << "***" << endl; | |
} | |
} | |
} | |
}; | |
bool verbose = false; | |
class puzzle { | |
struct cell { | |
char c; | |
list<placed_word*> owners; | |
cell(char c = ' ', placed_word* w = nullptr) : c(c), owners{w} {} | |
inline void append_owner(placed_word* w) { owners.push_back(w); } | |
}; | |
vector<vector<cell>> grid; | |
shared_ptr<placed_word> root_word; | |
inline void place_word_in_grid(shared_ptr<placed_word> word) { | |
ivec2 p = word->pos; | |
for(int i = 0; i < word->word.size(); ++i) { | |
if(grid[p.y][p.x].c == word->word[i]) | |
grid[p.y][p.x].append_owner(word.get()); | |
else | |
grid[p.y][p.x] = cell(word->word[i], word.get()); | |
p += direction_to_dp(word->dr); | |
} | |
} | |
inline bool check_grid(shared_ptr<placed_word> parent, ivec2 p, direction d, const string& w) { | |
if(verbose) cout << "checking word " << w << " for placement" << endl; | |
for(int i = 0; i < w.size(); ++i) { | |
if(p.x < 0 || p.x >= grid[0].size() || p.y < 0 || p.y >= grid.size()) return false; | |
if(verbose) cout << "w = " << w[i] << " g = " << grid[p.y][p.x].c << endl; | |
if(grid[p.y][p.x].c != ' ' && grid[p.y][p.x].c != w[i]) return false; | |
for(int dy = -1; dy <= 1; ++dy) | |
for(int dx = -1; dx <= 1; ++dx) { | |
if(p.x+dx < 0 || p.x+dx >= grid[0].size() || p.y+dy < 0 || p.y+dy >= grid.size()) continue; | |
if (grid[p.y + dy][p.x + dx].c != ' ') {// && grid[p.y + dy][p.x + dx].c != w[i + (d == direction::across ? dx : dy)]) { | |
if(verbose) cout << ">> " << w[i+(d == direction::across ? dx : dy)] << " " << grid[p.y+dy][p.x+dx].c << endl; | |
auto o = grid[p.y+dy][p.x+dx].owners; | |
auto pi = find(o.begin(), o.end(), parent.get()); | |
if(pi == o.end()) return false; | |
} | |
} | |
p += direction_to_dp(d); | |
} | |
return true; | |
} | |
inline void remove_word_from_grid(shared_ptr<placed_word> w) { | |
if(verbose) cout << "removing word " << w->word << endl; | |
auto f = find(w->parent->children.begin(), w->parent->children.end(), w); | |
if(f != w->parent->children.end()) *f = nullptr; | |
ivec2 p = w->pos; | |
for(int i = 0; i < w->word.size(); ++i) { | |
// cout << "[ "; | |
// for(auto o : grid[p.y][p.x].owners) cout << o << " "; | |
// cout << "]"; | |
if(grid[p.y][p.x].owners.size() == 1) | |
grid[p.y][p.x] = cell(); | |
else { | |
auto f = find(grid[p.y][p.x].owners.begin(), grid[p.y][p.x].owners.end(), w.get()); | |
if(f == grid[p.y][p.x].owners.end()) throw; | |
grid[p.y][p.x].owners.erase(f); | |
} | |
p += direction_to_dp(w->dr); | |
} | |
} | |
inline bool complete() { | |
// check that we have the proper number of words down and across | |
stack<shared_ptr<placed_word>> s; | |
s.push(root_word); | |
size_t words_down = 0, words_across = 0; | |
while(!s.empty()) { | |
auto n = s.top(); s.pop(); | |
for(auto c : n->children) if(c != nullptr) s.push(c); | |
if(n->dr == direction::across) words_across++; else if(n->dr == direction::down) words_down++; | |
} | |
return words_down >= 15 && words_across >= 15; | |
} | |
public: | |
puzzle(const vector<string>& words) : | |
grid(18+rand()%4, vector<cell>(18+rand()%4, cell())) | |
{ | |
/**** Puzzle generation algorithm ****/ | |
stack<shared_ptr<placed_word>> last_placed_word_stack; | |
list<string> words_to_place(words.begin(), words.end()); | |
const auto grid_width = grid[0].size(); | |
const auto grid_height = grid.size(); | |
root_word = make_shared<placed_word>(nullptr, ivec2(1,5), words_to_place.front(), direction::across); | |
place_word_in_grid(root_word); | |
words_to_place.pop_front(); | |
//For each word in dictionary | |
while(!words_to_place.empty()) { | |
if(complete()) break; | |
const string word = words_to_place.front(); words_to_place.pop_front(); | |
if(verbose) cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl; | |
if(verbose) cout << "trying to place word: " << word << endl; | |
stack<shared_ptr<placed_word>> search_stack; | |
//Start at the root word | |
search_stack.push(root_word); | |
while(true) { | |
if(verbose) print_grid(cout); | |
if(verbose) root_word->debug_print(cout); | |
if(verbose) cout << "~~" << endl; | |
if(verbose) getchar(); | |
//*1* Pop a placed word out of the search stack for the current placed word | |
//If the stack is in fact empty: | |
if(search_stack.empty()) { | |
//At this point a placement for the word could not be found. This means that some backtracking must be necessary | |
//If there are no placed words left, this word could not be placed, so restart puzzle generation | |
if(last_placed_word_stack.empty()) { | |
if(verbose) cout << "could not place word " << word << endl; | |
search_stack.push(root_word); | |
break; | |
} | |
//Pop the last word placed off the last placed word stack and remove it, then start at the root word and attempt to place the current word to be placed | |
remove_word_from_grid(last_placed_word_stack.top()); | |
words_to_place.push_back(last_placed_word_stack.top()->word); | |
last_placed_word_stack.pop(); | |
search_stack.push(root_word); | |
continue; | |
} | |
auto current_placed_word = search_stack.top(); search_stack.pop(); | |
if(verbose) cout << "checking word: " << current_placed_word->word << endl; | |
//Search the current placed word for a letter that it has in common with the word to be placed | |
bool placed = false; | |
for(int chr = 0; chr < word.size(); ++chr) { | |
auto nxt_ltr = current_placed_word->word.find_first_of(word[chr]); | |
if(verbose) cout << "checking letter " << word[chr] << endl; | |
for(; nxt_ltr != string::npos; nxt_ltr = current_placed_word->word.find_first_of(word[chr], nxt_ltr+1)) { | |
if (chr == 0 && nxt_ltr == 0) continue; | |
if(verbose) cout << "intersection @ " << chr << ", " << nxt_ltr << " letter=" << word[chr] << endl; | |
//calculate placement of word at this matching letter | |
auto D = ppdl(current_placed_word->dr); | |
auto pos = current_placed_word->pos + swap_for_direction(current_placed_word->dr, ivec2(nxt_ltr, -chr)); | |
//check the grid to see if that placement would be valid | |
if(check_grid(current_placed_word, pos, D, word)) { | |
//If the placement is valid: | |
//place the word in the grid and move on to the next word to be placed | |
auto wrd = make_shared<placed_word>(current_placed_word, pos, word, D); | |
place_word_in_grid(wrd); | |
current_placed_word->children[nxt_ltr] = wrd; | |
//push the placed word in the last word placed stack | |
last_placed_word_stack.push(wrd); | |
placed = true; | |
if(verbose) cout << "!"; | |
break; | |
} | |
//Else: | |
//continue on to the next common letter | |
} | |
if(placed) break; | |
} | |
//Else if you don't find any common letters: | |
if(!placed) { | |
if(verbose) cout << "couldn't place word " << word << endl; | |
//push all the children on to the search stack and repeat from *1* | |
for(const auto ch : current_placed_word->children) { | |
if(ch == nullptr) continue; | |
if(verbose) cout << "searching: " << ch->word << endl; | |
search_stack.push(ch); | |
} | |
} else { | |
if(verbose) cout << "placed word " << word << endl; | |
break; | |
} | |
} | |
} | |
/*************************************/ | |
} | |
inline void print_grid(ostream& os) const { | |
for(const auto& v : grid) { | |
for(auto c : v) | |
os << c.c; | |
os << endl; | |
} | |
} | |
inline void print_final(ostream& os) const { | |
stack<shared_ptr<placed_word>> s; | |
s.push(root_word); | |
vector<shared_ptr<placed_word>> down,across; | |
while(!s.empty()) { | |
auto n = s.top(); s.pop(); | |
for(auto c : n->children) if(c != nullptr) s.push(c); | |
if(n->dr == direction::across) across.push_back(n); else if(n->dr == direction::down) down.push_back(n); | |
} | |
for(size_t y = 0; y < grid.size(); ++y) { | |
for(size_t x = 0; x < grid[y].size(); ++x) | |
cout << "+---"; | |
cout << "+" << endl; | |
for(size_t x = 0; x < grid[y].size(); ++x) { | |
cout << "|"; | |
if(grid[y][x].c != ' ') { | |
bool found_head = false; | |
for(auto ow : grid[y][x].owners) { | |
if(ow->pos == ivec2(x,y)) { | |
vector<shared_ptr<placed_word>>* wl = nullptr; | |
if(ow->dr == direction::across) | |
wl = &across; | |
else if(ow->dr == direction::down) | |
wl = &down; | |
auto f = find_if(wl->begin(), wl->end(), | |
[&ow](shared_ptr<placed_word> w) { | |
return w.get() == ow; | |
}); | |
cout << setw(2) << distance(wl->begin(), f)+1; | |
found_head = true; | |
break; | |
} | |
} | |
if(!found_head) cout << " "; | |
} else { | |
cout << " "; | |
} | |
cout << grid[y][x].c; | |
} | |
cout << "|" << endl; | |
} | |
for(size_t x = 0; x < grid[0].size(); ++x) | |
cout << "+---"; | |
cout << "+" << endl; | |
cout << "down:" << endl; | |
for(int i = 0; i < down.size(); ++i) { | |
cout << (i+1) << ". " << down[i]->word << endl; | |
} | |
cout << "across:" << endl; | |
for(int i = 0; i < across.size(); ++i) { | |
cout << (i+1) << ". " << across[i]->word << endl; | |
} | |
} | |
inline float fill_ratio() const { | |
float filled_cells = 0.f; | |
for(int y = 0; y < grid.size(); ++y) | |
for(int x = 0; x < grid[y].size(); ++x) { | |
if(grid[y][x].c != ' ') filled_cells++; | |
} | |
return filled_cells/((float)(grid.size()*grid[0].size())); | |
} | |
inline shared_ptr<svg::shape> generate_puzzle_svg(bool is_key = true) { | |
vector<shared_ptr<svg::shape>> cells; | |
stack<shared_ptr<placed_word>> s; | |
s.push(root_word); | |
vector<shared_ptr<placed_word>> down,across; | |
while(!s.empty()) { | |
auto n = s.top(); s.pop(); | |
for(auto c : n->children) if(c != nullptr) s.push(c); | |
if(n->dr == direction::across) across.push_back(n); else if(n->dr == direction::down) down.push_back(n); | |
} | |
double cell_w = 32.0, cell_h = 32.0; | |
for(size_t y = 0; y < grid.size(); ++y) { | |
for(size_t x = 0; x < grid[y].size(); ++x) { | |
cells.push_back(make_shared<svg::rectangle>(5+x*cell_w,5+y*cell_h,cell_w,cell_h,svg::color(svg::rgba_colors::black, 2.0))); | |
if(grid[y][x].c != ' ') { | |
for(auto ow : grid[y][x].owners) { | |
if(ow->pos == ivec2(x,y)) { | |
vector<shared_ptr<placed_word>>* wl = nullptr; | |
if(ow->dr == direction::across) | |
wl = &across; | |
else if(ow->dr == direction::down) | |
wl = &down; | |
auto f = find_if(wl->begin(), wl->end(), | |
[&ow](shared_ptr<placed_word> w) { | |
return w.get() == ow; | |
}); | |
auto ix = distance(wl->begin(), f)+1; | |
cells.push_back(make_shared<svg::text>(5+x*cell_w+cell_w*0.1, 5+y*cell_h+cell_w*0.3, to_string(ix), svg::font("", 10, 600))); | |
break; | |
} | |
} | |
} | |
if(grid[y][x].c != ' ' && is_key) cells.push_back(make_shared<svg::text>(5+x*cell_w+cell_w*0.3, 5+y*cell_h+cell_w*0.7, | |
string(1,grid[y][x].c))); | |
} | |
} | |
double listy = 18; | |
cells.push_back(make_shared<svg::text>(grid.size()*cell_w + 5, listy, "down:", svg::font("", 16, 900))); | |
//listy += 16; | |
for(int i = 0; i < down.size(); ++i) { | |
ostringstream oss; | |
oss << (i+1) << ". " << down[i]->word << endl; | |
cells.push_back(make_shared<svg::text>(grid.size()*cell_w + 5, listy += 16, oss.str())); | |
} | |
listy += 32; | |
cells.push_back(make_shared<svg::text>(grid.size()*cell_w + 5, listy+=16, "across:", svg::font("", 16, 900))); | |
for(int i = 0; i < across.size(); ++i) { | |
ostringstream oss; | |
oss << (i+1) << ". " << across[i]->word << endl; | |
cells.push_back(make_shared<svg::text>(grid.size()*cell_w + 5, listy+=16, oss.str())); | |
} | |
return make_shared<svg::group>(cells); | |
} | |
}; | |
ostream& operator<<(ostream& os, const puzzle& p) { | |
p.print_final(os); | |
return os; | |
} | |
//need some way to backtrack | |
//probably need to be able to take words out of the puzzle and restart | |
//need to be able to not place words so that they make gibberish words with their neghbors | |
int main(int argc, char* argv[]) { | |
if(argc < 2) { | |
cout << "no arguments" << endl; | |
return -1; | |
} | |
vector<string> words; | |
{ | |
vector<string> words_raw; | |
ifstream wordsf(argv[1]); | |
while(wordsf) { | |
string s; getline(wordsf, s); | |
s = trim(s); | |
if(s.empty()) continue; | |
words_raw.push_back(s); | |
} | |
random_device rd; | |
mt19937 g(rd()); | |
shuffle(words_raw.begin(), words_raw.end(), g); | |
words = words_raw; | |
} | |
if(argc > 2 && *argv[2] == 'v') verbose = true; | |
puzzle p(words); | |
cout << p << endl; | |
cout << "puzzle fill ratio: " << p.fill_ratio() << endl; | |
getchar(); | |
ofstream svgo("puzzle.svg"); | |
svgo << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"3000px\" height=\"3000px\">"; | |
auto pv = p.generate_puzzle_svg(); | |
pv->generate_svg(svgo); | |
svgo << "</svg>"; | |
svgo.flush(); | |
getchar(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <iostream> | |
#include <string> | |
#include <sstream> | |
#include <vector> | |
#include <functional> | |
#include <iomanip> | |
#include <memory> | |
using namespace std; | |
namespace svg { | |
ostream& operator <<(ostream& os, function<void(ostream& os)> f) { | |
f(os); | |
return os; | |
} | |
enum class rgba_colors : unsigned int { | |
transparent = 0x0000'0000, | |
black = 0x0000'00ff, | |
white = 0xffff'ffff, | |
red = 0xff00'00ff, | |
green = 0x00ff'00ff, | |
blue = 0x0000'ffff | |
}; | |
union RGBAvalue { | |
unsigned int v; unsigned char a, r, g, b; | |
RGBAvalue(unsigned int V) : v(V) {} | |
RGBAvalue(rgba_colors c) : v((unsigned int)c) {} | |
}; | |
ostream& operator <<(ostream& os, RGBAvalue v) { | |
os << "#" << setfill('0') << setw(8) << hex << v.v << dec; | |
return os; | |
} | |
struct color { | |
RGBAvalue fill; | |
RGBAvalue stroke; | |
double stroke_width; | |
color(RGBAvalue fill = rgba_colors::black) : fill(fill), stroke(0), stroke_width(0) {} | |
color(RGBAvalue stroke, double stroke_width) : fill(0), stroke(stroke), stroke_width(stroke_width) {} | |
color(RGBAvalue fill, RGBAvalue stroke, double stroke_width) : fill(fill), stroke(stroke), stroke_width(stroke_width) {} | |
virtual void generate_svg(ostream& os) const { | |
os << " fill=\"" << fill << "\" "; | |
if(stroke.v > 0) os << "stroke=\"" << stroke << "\" "; | |
if(stroke_width > 0) os << "stroke-width=\"" << stroke_width << "\""; | |
} | |
}; | |
struct shape { | |
color col; | |
shape(color c = color()) : col(c) {} | |
virtual void generate_svg(ostream& os) const = 0; | |
protected: | |
void generate_common_svg(ostream& os, const string& elmnm, const vector<pair<string,string>>& attrb) const { | |
os << "<" << elmnm; | |
for(const auto& atr : attrb) { | |
os << " " << atr.first << " =\"" << atr.second << "\""; | |
} | |
os << [&](ostream& os) { col.generate_svg(os); } << "/>"; | |
} | |
}; | |
struct rectangle : public shape { | |
double x, y, width, height; | |
rectangle(double x, double y, double w, double h, color c) | |
: x(x), y(y), width(w), height(h), shape(c) {} | |
void generate_svg(ostream& os) const override { | |
generate_common_svg(os, "rect", { | |
{"x", to_string(x)}, | |
{"y", to_string(y)}, | |
{"width", to_string(width)}, | |
{"height", to_string(height)}, | |
}); | |
/*os << "<rect x=\"" << x << "\" y=\"" << y << | |
"\" width=\"" << width << "\" height=\"" << height << "\"" << | |
[&](ostream& os){col.generate_svg(os);} << "/>"; */ | |
} | |
}; | |
struct group : public shape { | |
vector<shared_ptr<shape>> children; | |
group(const vector<shared_ptr<shape>>& ch = {}, color c = color()) : children(ch), shape(c) {} | |
void generate_svg(ostream& os) const override { | |
os << "<g" << [&](ostream& os) { col.generate_svg(os); } << ">"; | |
for(const auto& s : children) { | |
s->generate_svg(os); | |
} | |
os << "</g>"; | |
} | |
}; | |
struct font { | |
double size; | |
double weight; | |
string family; | |
font(const string& f = "", double s = -1.0, double w = -1.0) : size(s), weight(w), family(f) {} | |
void generate_svg(ostream& os) const { | |
if(size > 0.0) os << " font-size=\"" << size << "\" "; | |
if(weight > 0.0) os << " font-weight=\"" << weight << "\" "; | |
if(!family.empty()) os << " font-family=\"" << family << "\" "; | |
} | |
}; | |
struct text : public shape { | |
string chars; | |
double x,y; | |
font fnt; | |
text(double x, double y, const string& ch, const font& f = font(), color c = color()) | |
: chars(ch), x(x), y(y), fnt(f), shape(c) {} | |
void generate_svg(ostream& os) const override { | |
os << "<text x=\"" << x << "\" y=\"" << y << "\"" | |
<< [&](ostream& os) { fnt.generate_svg(os); } | |
<< [&](ostream& os) { col.generate_svg(os); } << ">"; | |
os << chars; | |
os << "</text>"; | |
} | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "svg.h" | |
#include <fstream> | |
using namespace svg; | |
int main() { | |
ofstream out("out.svg"); | |
vector<shared_ptr<shape>> grid_squares; | |
for(int y = 0; y < 5; ++y) { | |
for(int x = 0; x < 5; ++x) { | |
grid_squares.push_back(make_shared<rectangle>(50.0*x + 5.0, 50.0*y + 5.0, 50.0, 50.0, color(0x000000ff, 1.0))); | |
} | |
} | |
grid_squares.push_back(make_shared<text>(25, 100, "Hello, World!", font("impact", 32), color(0xff00aaff, 0x000000ff, 2.0))); | |
//rectangle r(2.0, 2.0, 100.0, 100.0, color(0xff0000ff, 1.0)); | |
group tlg(grid_squares); | |
out << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"300px\" height=\"300px\">"; | |
tlg.generate_svg(out); | |
out << "</svg>"; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
density | |
contact | |
fall | |
hike | |
aid | |
stab | |
terrify | |
error | |
cook | |
wage | |
suggest | |
mole | |
memory | |
sausage | |
prevent | |
awful | |
poem | |
coast | |
combine | |
total | |
minor | |
energy | |
pack | |
pray | |
witch | |
dose | |
misery | |
back | |
voucher | |
flavor | |
beat | |
king | |
miracle | |
brag | |
crutch | |
honest | |
license | |
strong | |
subway | |
cart | |
stride | |
essay | |
tumour | |
donor | |
bird | |
retreat | |
analyst | |
grind | |
excess | |
shift | |
social | |
race | |
ceiling | |
real | |
genetic | |
report | |
copper | |
tank | |
diet | |
context | |
justice | |
gutter | |
behead | |
notice | |
acute | |
prize | |
poison | |
sample | |
have | |
adopt | |
climb | |
salt | |
wild | |
active | |
breathe | |
brink | |
ankle | |
flash | |
power | |
monarch | |
button | |
joint | |
comfort | |
decide | |
gallon | |
insure | |
colon | |
rare | |
halt | |
yard | |
control | |
section | |
model | |
metal | |
belt | |
throat | |
harmful | |
rocket | |
air |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment