/* Generating HTML pages for CWP Copyright (C) 2016 Frans Faase This program is used to generate HTML pages for the Chinese Wooden Puzzles on my website. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. GNU General Public License: http://www.iwriteiam.nl/GNU.txt Details: http://www.iwriteiam.nl/D1601.html#16 */ #include #include #include struct { char name; int size; int nr_neighbours; // = size - 1, except for 'g' } pieces[8] = { { 'o', 5, 4 }, { 'r', 4, 3 }, { 'e', 4, 3 }, { 'b', 4, 3 }, { 'g', 4, 4 }, { 'z', 4, 3 }, { 'w', 3, 2 }, { 'p', 2, 1 } }; int height = 10; int multiplicity = 5; void print_puzzle_name(FILE *f, const char *name, bool capital) { int n = strlen(name); for (int i = 0; i < n; i++) { if (i == 0) ; else if (i < n-1) fprintf(f, ", "); else if (n == 2) fprintf(f, " and "); else fprintf(f, ", and "); switch (name[i]) { case 'g': fprintf(f, "%s", capital ? "Green" : "green"); break; case 'z': fprintf(f, "%s", capital ? "Black" : "black"); break; case 'r': fprintf(f, "%s", capital ? "Red" : "red"); break; case 'b': fprintf(f, "%s", capital ? "Blue" : "blue"); break; case 'w': fprintf(f, "%s", capital ? "White" : "white"); break; case 'p': fprintf(f, "%s", capital ? "Purple" : "purple"); break; case 'o': fprintf(f, "%s", capital ? "Orange" : "orange"); break; case 'e': fprintf(f, "%s", capital ? "Yellow" : "yellow"); break; } capital = false; } } class PrintNumber { public: PrintNumber(long long n, bool capital = false, const char *one = "", const char *more = "") { static const char *numnames[22] = { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; if (n < 0) sprintf(buffer, "ERROR"); else if (n == 0) sprintf(buffer, "%s %s", capital?"No":"no", more); else if (n <= 10) sprintf(buffer, "%s %s", numnames[n+(capital?-1:9)], n == 1 ? one : more); else if (n <= 999) sprintf(buffer, "%lld %s", n, more); else if (n <= 999999L) sprintf(buffer, "%lld,%03lld %s", n/1000, n%1000, more); else if (n <= 999999999LL) sprintf(buffer, "%lld,%03lld,%03lld %s", n/1000000L, (n/1000)%1000, n%1000, more); else sprintf(buffer, "%lld,%03lld,%03lld,%03lld %s", n/1000000000L, (n/1000000L)%1000, (n/1000)%1000, n%1000, more); } const char *val() { return buffer; } private: char buffer[200]; }; int pieces_bv_for(const char *name) { int pieces_bv = 0; const char *s = name; for (int i = 0; i < 8; i++) { if (*s == pieces[i].name) { pieces_bv |= (1 << i); s++; } } return pieces_bv; } int abs_min_neighbours_for(const char *name) { int neighbours = 0; const char *s = name; for (int i = 0; i < 8; i++) { if (*s == pieces[i].name) { neighbours += multiplicity * pieces[i].nr_neighbours + multiplicity - 1; s++; } } return neighbours; } class Puzzle { public: static Puzzle *add(const char *name, int width) { Puzzle *new_puzzle = *ref_last = new Puzzle(name, width); ref_last = &(*ref_last)->next; return new_puzzle; } char name[10]; int width; int pieces_bv; int abs_min_neighbours; int min_neighbours; int nr_min_neighbours; int max_neighbours; int nr_max_neighbours; long long patterns; bool unknown; long long count4; long long count2; long long count1; long long count; long long mix_count; long long guess_count; class Iterator { public: Iterator() : _it(Puzzle::all) {} bool more() const { return _it != 0; } void next() { _it = _it->next; } Puzzle& val() const { return *_it; } private: Puzzle *_it; }; friend class Puzzle::Iterator; private: struct Line { Line(const char *ns, int width) : next(0) { s = new char[strlen(ns)+1]; strcpy(s, ns); neighbours = 0; for (int h = 1; h < width; h++) for (int v = 0; v < height; v++) if (s[v*width+h-1] == s[v*width+h]) neighbours++; for (int h = 0; h < width; h++) for (int v = 1; v < height; v++) if (s[(v-1)*width+h] == s[v*width+h]) neighbours++; } char *s; int neighbours; Line * next; }; public: void addLine(const char *line) { Line* new_line = new Line(line, width); *ref_line = new_line; ref_line = &(*ref_line)->next; if (min_neighbours == 0 || new_line->neighbours < min_neighbours) { min_neighbours = new_line->neighbours; nr_min_neighbours = 1; } else if (new_line->neighbours == min_neighbours) nr_min_neighbours++; if (max_neighbours == 0 || new_line->neighbours > max_neighbours) { max_neighbours = new_line->neighbours; nr_max_neighbours = 1; } else if (new_line->neighbours == max_neighbours) nr_max_neighbours++; } class LineIterator { public: LineIterator(const Puzzle &puzzle) : line(puzzle.lines) {} bool more() const { return line != 0; } void next() { line = line->next; } const char* val() { return line->s; } int neighbours() { return line->neighbours; } private: Line* line; }; friend class Puzzle::LineIterator; private: class Combination { public: Combination(Puzzle* p[], int nr_p, long long c, long long nc, int min_n, int nr_min_n, int max_n, int nr_max_n) : nr_parts(nr_p), count(c), norm_count(nc), next(0), min_neighbours(min_n), nr_min_neighbours(nr_min_n), max_neighbours(max_n), nr_max_neighbours(nr_max_n) { for (int i = 0; i < nr_parts; i++) parts[i] = p[i]; } Puzzle* parts[10]; int nr_parts; long long count; long long norm_count; int min_neighbours; int nr_min_neighbours; int max_neighbours; int nr_max_neighbours; Combination* next; void print(FILE *f) { fprintf(f, "%s (%s) are permutations of ", PrintNumber(count, true, "solution", "solutions").val(), PrintNumber(norm_count, false, "normalized solution", "normalized solutions").val()); for (int i = 0; i < nr_parts; i++) { if (i == 0) ; else if (i < nr_parts) fprintf(f, ", "); else if (nr_parts == 2) fprintf(f, " and "); else fprintf(f, ", and "); fprintf(f, "%s of ", PrintNumber(parts[i]->mix_count, false, "solution", "solutions").val(), parts[i]->name); print_puzzle_name(f, parts[i]->name, false); fprintf(f, ""); } fprintf(f, ".\n"); } }; public: void addCombination(Puzzle* parts[], int nr_parts, long long count, long long norm_count, int min_n, int nr_min_n, int max_n, int nr_max_n) { *ref_combination = new Combination(parts, nr_parts, count, norm_count, min_n, nr_min_n, max_n, nr_max_n); ref_combination = &(*ref_combination)->next; } class CombinationIterator { public: CombinationIterator(const Puzzle &puzzle) : combination(puzzle.combinations) {} bool more() const { return combination != 0; } void next() { combination = combination->next; } Puzzle* part(int i) const { return combination->parts[i]; } Puzzle** parts() const { return combination->parts; } int nr_parts() const { return combination->nr_parts; } long long count() const { return combination->count; } long long norm_count() const { return combination->norm_count; } int min_neighbours() const { return combination->min_neighbours; } int nr_min_neighbours() const { return combination->nr_min_neighbours; } int max_neighbours() const { return combination->max_neighbours; } int nr_max_neighbours() const { return combination->nr_max_neighbours; } void print(FILE *f) { combination->print(f); } private: Combination* combination; }; friend class Puzzle::CombinationIterator; private: Puzzle(const char* n, int w) : width(w), pieces_bv(pieces_bv_for(n)), abs_min_neighbours(abs_min_neighbours_for(n)), min_neighbours(0), nr_min_neighbours(0), patterns(0), unknown(false), count(0), count4(0), count2(0), count1(0), mix_count(0), next(0), lines(0), combinations(0) { strcpy(name, n); ref_line = &lines; ref_combination = &combinations; } Puzzle *next; Line* lines; Line** ref_line; Combination* combinations; Combination** ref_combination; static Puzzle *all; static Puzzle **ref_last; }; Puzzle *Puzzle::all = 0; Puzzle **Puzzle::ref_last = &Puzzle::all; #define SUBSET(A,B) ((A)!=(B)&&((A)|(B))==(B)) void search_combined(int pieces_bv, int width, int depth, Puzzle *parts[], Puzzle &puzzle) { for (Puzzle::Iterator it; it.more(); it.next()) { if (it.val().pieces_bv == pieces_bv) { if (depth > 0 && it.val().mix_count > 0) { parts[depth] = &it.val(); long long count = 1; int min_neighbours = 0; int nr_min_neighbours = 1; int max_neighbours = 0; int nr_max_neighbours = 1; int factor = 2; for (int i = 0; i <= depth; i++) { count *= parts[i]->mix_count * (i+1); if (parts[i]->count > 1) factor = 4; min_neighbours += parts[i]->min_neighbours; nr_min_neighbours *= parts[i]->nr_min_neighbours * (i+1); max_neighbours += parts[i]->max_neighbours; nr_max_neighbours *= parts[i]->nr_max_neighbours * (i+1); } long norm_count = count / factor; //min_neighbours /= factor; //max_neighbours /= factor; puzzle.addCombination(parts, depth+1, count, norm_count, min_neighbours, nr_min_neighbours, max_neighbours, nr_max_neighbours); } break; } if (it.val().mix_count > 0 && it.val().width < width && SUBSET(it.val().pieces_bv, pieces_bv)) { int rem_width = width - it.val().width; int rem_pieces_bv = pieces_bv & ~it.val().pieces_bv; if (rem_width <= it.val().width && (rem_width < it.val().width || rem_pieces_bv < it.val().pieces_bv)) { parts[depth] = &it.val(); search_combined(rem_pieces_bv, rem_width, depth+1, parts, puzzle); } } } } void print_min_max_neighbours(FILE* f, Puzzle& puzzle) { if (puzzle.max_neighbours == puzzle.min_neighbours) return; fprintf(f, "There %s ", puzzle.nr_min_neighbours == 1 ? "is" : "are"); int diff = puzzle.min_neighbours - puzzle.abs_min_neighbours; if (diff == 0) fprintf(f, "%s.\n", PrintNumber(puzzle.nr_min_neighbours, false, "optimal minimal touching pattern", "optimal minimal touching patterns").val()); else fprintf(f, "%s suboptimal with %s extra.\n", PrintNumber(puzzle.nr_min_neighbours, false, "minimal touching pattern, which is", "minimal touching patterns, which are").val(), PrintNumber(diff).val()); fprintf(f, "There %s %s.\n", puzzle.nr_max_neighbours == 1 ? "is" : "are", PrintNumber(puzzle.nr_max_neighbours, false, "maximal touching pattern", "maximal touching patterns").val()); } void iterate_min_neighbours(FILE *f, Puzzle* parts[], int nr_parts, Puzzle::LineIterator* lines[], int depth, int &count) { if (depth == nr_parts) { if (f != 0) { fprintf(f,"p(\""); for (int h = 0; h < height; h++) for (int p = 0; p < nr_parts; p++) { int w = parts[p]->width; for (int i = 0; i < w; i++) fprintf(f, "%c", lines[p]->val()[w*h + i]); } fprintf(f,"\")\n"); } else count++; return; } int min_neighbours = parts[depth]->min_neighbours; for (Puzzle::LineIterator it(*parts[depth]); it.more(); it.next()) if (it.neighbours() == min_neighbours) { lines[depth] = ⁢ iterate_min_neighbours(f, parts, nr_parts, lines, depth+1, count); } } void analyze(const char* fn) { FILE *f = fopen(fn,"rt"); if (f == 0) { printf("File '%s' could not be opened\n", fn); return; } printf("Analyze contests of %s\n", fn); char line[200]; int width; int max_nr = 0; while (fgets(line, 199, f)) { if (strncmp(line, "\"width = ", 9) == 0) { width = atoi(line + 9); printf("width = %d\n", width); } else if (line[0] == '"') { char name[10]; char *r = name; for (char *s = line+1; *s != '"' && *s != '\0';) *r++ = *s++; *r = '\0'; fgets(line, 199, f); long pieces_bv = 0; Puzzle* puzzle = Puzzle::add(name, width); if (line[0] == '?') { printf("Not calculated yet: %s\n", name); fgets(line, 199, f); puzzle->unknown = true; } else { while (line[0] != '"') { puzzle->patterns++; int nr = atoi(line); puzzle->count += nr; if (nr % 4 == 0) puzzle->count4 += nr / 4; else if (nr % 2 == 0) puzzle->count2 += nr / 2; else puzzle->count1 += nr; if (nr > max_nr) max_nr = nr; *strstr(line, "\")") = '\0'; puzzle->addLine(line + 11); fgets(line, 199, f); } printf("- %s %ld %ld", name, puzzle->patterns, puzzle->count); if (puzzle->count1 > 0) { printf(" = 1 x %d", puzzle->count1); if (puzzle->count2 > 0) printf(" + 2 x %d", puzzle->count2); if (puzzle->count4 > 0) printf(" + 4 x %d", puzzle->count4); } else if (puzzle->count2 > 0) { printf(" = 2 x %d", puzzle->count2); if (puzzle->count4 > 0) printf(" + 4 x %d", puzzle->count4); } else if (puzzle->count4 > 0) printf(" = 4 x %d", puzzle->count4); printf("\n"); if (puzzle->count > 0) pieces_bv = puzzle->pieces_bv; } if (pieces_bv != 0) { for (Puzzle::Iterator it; it.more(); it.next()) { if ( it.val().count > 0 && SUBSET(it.val().pieces_bv, pieces_bv)) { long diff = pieces_bv & ~it.val().pieces_bv; Puzzle::Iterator it2; for (; it2.more(); it2.next()) if (it2.val().count > 0 && it2.val().pieces_bv == diff) break; if (it2.more()) { if (it.val().pieces_bv < it2.val().pieces_bv) printf(" - %s %lld %lld + %s %lld %lld\n", it.val().name, it.val().patterns, it.val().count, it2.val().name, it2.val().patterns, it2.val().count); } else { bool larger = false; for (Puzzle::Iterator it2; it2.more(); it2.next()) if ( it2.val().count > 0 && SUBSET(it2.val().pieces_bv, pieces_bv) && SUBSET(it.val().pieces_bv, it2.val().pieces_bv)) { larger = true; break; } if (!larger) printf(" - %s %ld %ld\n", it.val().name, it.val().patterns, it.val().count); } } } } if (line[0] != '"' && line[1] != '"') printf("Unknown line: %s\n", line); } else printf("Unknown line: %s\n", line); } printf("max_nr = %d\n", max_nr); fclose(f); char htmlfn[30]; sprintf(htmlfn, "CWP_N_%d.html", height); f = fopen(htmlfn, "wt"); if (f == 0) return; printf("Generate file %s\n", htmlfn); fprintf(f, "\n"); fprintf(f, "Chinese Wooden Puzzles in N by %d\n", height); fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "

Chinese Wooden Puzzles in N by %d

\n", height); fprintf(f, "\n"); fprintf(f, "Below the results for the Chinese Wooden Puzzle that fit in a frame of size\n"); fprintf(f, "N by %d for all possible values for N and combination of\n", height); fprintf(f, "kind of pieces, where %d pieces of each selected kind are used.\n", multiplicity); fprintf(f, "This page is generated with the program genCWP10.cpp\n"); width = -1; for (Puzzle::Iterator it; it.more(); it.next()) { Puzzle& puzzle = it.val(); if (puzzle.width > width) { width = puzzle.width; fprintf(f, "\n

Chinese Wooden Puzzles in %d by %d

\n", width, height); } fprintf(f, "\n

", puzzle.name); print_puzzle_name(f, puzzle.name, true); if (puzzle.unknown) { Puzzle* parts[7]; search_combined(puzzle.pieces_bv, width, 0, parts, puzzle); long long nr_comb = 0; long long comb_count = 0; long long comb_norm_count = 0; long long comb_pieces[7]; for (int i = 2; i < 7; i++) comb_pieces[i] = 0; int min_neighbours = 0; int nr_min_neighbours = 0; for (Puzzle::CombinationIterator it(puzzle); it.more(); it.next()) { nr_comb++; comb_count += it.count(); comb_norm_count += it.norm_count(); comb_pieces[it.nr_parts()+1] += it.norm_count(); if (min_neighbours == 0 || it.min_neighbours() < min_neighbours) { min_neighbours = it.min_neighbours(); nr_min_neighbours = it.nr_min_neighbours(); } else if (min_neighbours == it.min_neighbours()) nr_min_neighbours += it.nr_min_neighbours(); } if (comb_count > 0) fprintf(f, ": Has at least %s solutions

\n\n", PrintNumber(comb_count).val()); else fprintf(f, ": Not calculated yet\n\n"); if (nr_comb > 0) { for (Puzzle::CombinationIterator it(puzzle); it.more(); it.next()) it.print(f); fprintf(f, "Which means it has at least %s solutions (%s normalized solutions).\n", PrintNumber(comb_count).val(), PrintNumber(comb_norm_count).val()); puzzle.guess_count = comb_count * 10; int diff = min_neighbours - puzzle.abs_min_neighbours; if (diff == 0) fprintf(f, "There %s at least %s.\n", nr_min_neighbours == 1 ? "is" : "are", PrintNumber(nr_min_neighbours, false, "optimal minimal touching pattern", "optimal minimal touching patterns").val()); else { fprintf(f, "There exists at least minimal touching patterns with %s extra.\n", PrintNumber(diff).val()); fprintf(f, "There %s at least %s.\n", nr_min_neighbours == 1 ? "is" : "are", PrintNumber(nr_min_neighbours, false, "such suboptimal minimal touching pattern", "such suboptimal minimal touching patterns").val()); //fprintf(f, "There %s %s.\n", // puzzle.nr_max_neighbours == 1 ? "is" : "are", // PrintNumber(puzzle.nr_max_neighbours, false, "maximal touching pattern", "maximal touching patterns").val()); } int count = 0; for (Puzzle::CombinationIterator it(puzzle); it.more(); it.next()) if (it.min_neighbours() == min_neighbours) { Puzzle::LineIterator* lines[10]; iterate_min_neighbours(0, it.parts(), it.nr_parts(), lines, 0, count); } int factor = 8; int total_height = 0; for (; factor > 1; factor--) { total_height = factor*height; int sx = 0; for (int i = 0; i < count; i++) { sx++; if (factor*((width+2)*sx + width) >= 600) { sx = 0; total_height += factor*(height+2); } } if (total_height <= 800) break; } if (total_height <= 800) { fprintf(f, "%s:\n", PrintNumber(count, true, "such pattern is", "such patterns are").val()); fprintf(f, "\n

\n

\n", puzzle.name, total_height); fprintf(f, "This text is displayed if your browser does not support HTML5 Canvas.\n"); fprintf(f, "\n"); fprintf(f, "
\n"); } } else { long long count = 0; for (Puzzle::Iterator it; it.more(); it.next()) { if ( it.val().count > 0 && SUBSET(it.val().pieces_bv, puzzle.pieces_bv)) { bool larger = false; for (Puzzle::Iterator it2; it2.more(); it2.next()) if ( it2.val().count > 0 && SUBSET(it2.val().pieces_bv, puzzle.pieces_bv) && SUBSET(it.val().pieces_bv, it2.val().pieces_bv)) { larger = true; break; } if (!larger) { fprintf(f, "", it.val().name); print_puzzle_name(f, it.val().name, true); fprintf(f, " in %d by %d has %s solutions in %s patterns.\n", it.val().width, height, PrintNumber(it.val().count).val(), PrintNumber(it.val().patterns).val()); count += it.val().count * 4 * width / it.val().width; } } } puzzle.guess_count = count; } } else if (puzzle.patterns == 0) fprintf(f, ": No solutions\n\n"); else { fprintf(f, ": %s in %s\n\n", PrintNumber(puzzle.count, true, "solution", "solutions").val(), PrintNumber(puzzle.patterns, false, "pattern", "patterns").val()); if (puzzle.count1 + puzzle.count2 + puzzle.count4 == 1) { if (puzzle.count1 == 1) fprintf(f, "The one solution is symmetric in two directions.\n"); else if (puzzle.count2 == 1) fprintf(f, "The one normalized solution is symmetric in one direction.\n"); else fprintf(f, "The one normalized solution is not symmetric in any direction.\n"); } else { if (puzzle.count1 > 0) fprintf(f, "%s symmetric in two directions.\n", PrintNumber(puzzle.count1, true, "normalized solution is", "normalized solutions are").val()); if (puzzle.count2 > 0) fprintf(f, "%s symmetric in one direction.\n", PrintNumber(puzzle.count2, true, "normalized solution is", "normalized solutions are").val()); if (puzzle.count4 > 0) fprintf(f, "%s not symmetric in any direction.\n", PrintNumber(puzzle.count4, true, "normalized solution is", "normalized solutions are").val()); } int pieces_bv = puzzle.pieces_bv; Puzzle* parts[7]; search_combined(puzzle.pieces_bv, width, 0, parts, puzzle); long long nr_comb = 0; long long comb_count = 0; long long comb_norm_count = 0; long long comb_pieces[7]; for (int i = 2; i < 7; i++) comb_pieces[i] = 0; for (Puzzle::CombinationIterator it(puzzle); it.more(); it.next()) { nr_comb++; comb_count += it.count(); comb_norm_count += it.norm_count(); comb_pieces[it.nr_parts()+1] += it.norm_count(); it.print(f); } puzzle.mix_count = puzzle.count - comb_count; if (nr_comb == 1) { if (puzzle.mix_count == 0) fprintf(f, "There are no mix solutions.\n"); else fprintf(f, "This leaves %s.\n", PrintNumber(puzzle.mix_count, false, "mix solution","mix solutions").val()); } else if (nr_comb > 1) fprintf(f, "There are a total of %s (%s) that are permutations, leaving %s.\n", PrintNumber(comb_count, false, "solution", "solutions").val(), PrintNumber(comb_norm_count, false, "normalized solution", "normalized solutions").val(), PrintNumber(puzzle.mix_count, false, "mix solution", "mix solutions").val()); int horz_split[10]; for (int i = 0; i < 10; i++) horz_split[i] = 0; for (Puzzle::LineIterator it(puzzle); it.more(); it.next()) { int nr = 0; for (int h = 1; h < width; h++) { bool split = true; for (int v = 0; v < height; v++) if (it.val()[v*width+h-1] == it.val()[v*width+h]) { split = false; break; } if (split) nr++; } horz_split[nr]++; } if (horz_split[0] == 0) { int t = 0; for (int i = 1; i < 10; i++) if (horz_split[i] > 0) t++; if (t > 1) fprintf(f, "All patterns are a combination of smaller patterns.\n"); } bool some_split = false; for (int i = 1; i < 10; i++) if (horz_split[i] == puzzle.patterns) fprintf(f, "All patterns are a combination of %s smaller patterns.\n", PrintNumber(i+1).val()); else if (horz_split[i] > 0) { fprintf(f, "%s a combination of %s smaller patterns.\n", PrintNumber(horz_split[i], true, "pattern is", "patterns are").val(), PrintNumber(i+1).val()); some_split = true; } if (puzzle.patterns > 1 && horz_split[0] > 0 && some_split) fprintf(f, "%s a mix patterns.\n", PrintNumber(horz_split[0], true, "pattern is", "patterns are").val()); int factor = 8; int total_height = 0; for (; factor > 1; factor--) { total_height = factor*height; int sx = 0; for (Puzzle::LineIterator it(puzzle); it.more(); it.next()) { sx++; if (factor*((width+2)*sx + width) >= 600) { sx = 0; total_height += factor*(height+2); } } if (total_height <= 800) break; } if (total_height <= 800) { print_min_max_neighbours(f, puzzle); if (puzzle.patterns == 1) fprintf(f, "The one pattern is:"); else fprintf(f, "The patterns are:"); fprintf(f, "\n

\n

\n", puzzle.name, total_height); fprintf(f, "This text is displayed if your browser does not support HTML5 Canvas.\n"); fprintf(f, "\n"); fprintf(f, "
\n"); } else { int factor = 8; int total_height = 0; for (; factor > 1; factor--) { total_height = factor*height; int sx = 0; for (int i = 0; i < puzzle.nr_min_neighbours; i++) { sx++; if (factor*((width+2)*sx + width) >= 600) { sx = 0; total_height += factor*(height+2); } } if (total_height <= 800) break; } if (total_height <= 800) { fprintf(f, "Too many patterns to display them all.\n"); int diff = puzzle.min_neighbours - puzzle.abs_min_neighbours; if (diff == 0) fprintf(f, "The %s:", PrintNumber(puzzle.nr_min_neighbours, false, "optimal minimal touching pattern is", "optimal minimal touching patterns are").val()); else fprintf(f, "The %s suboptimal (%s extra) minimal touching %s:", PrintNumber(puzzle.nr_min_neighbours).val(), PrintNumber(diff).val(), puzzle.nr_min_neighbours == 1 ? "pattern is" : "patterns are"); fprintf(f, "\n

\n

\n", puzzle.name, total_height); fprintf(f, "This text is displayed if your browser does not support HTML5 Canvas.\n"); fprintf(f, "\n"); fprintf(f, "
\n"); } else { fprintf(f, "Too many patterns to be displayed.\n"); print_min_max_neighbours(f, puzzle); } } } } fprintf(f, "\n"); fprintf(f, "


\n"); fprintf(f, "
\n"); fprintf(f, "Home and email\n"); fprintf(f, "| Chinese Wooden Puzzle\n"); fprintf(f, "
\n"); fprintf(f, "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    fprintf(f, "
\n"); fprintf(f, "\n"); } int main(int argc, char *argv[]) { if (argc > 1) height = atoi(argv[1]); multiplicity = height/2; char infn[40]; sprintf(infn, "cwp_sols_%d.txt", multiplicity); analyze(infn); return 0; }