123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235 |
- /*
- * towers.c: the puzzle also known as 'Skyscrapers'.
- *
- * Possible future work:
- *
- * - Relax the upper bound on grid size at 9?
- * + I'd need TOCHAR and FROMCHAR macros a bit like group's, to
- * be used wherever this code has +'0' or -'0'
- * + the pencil marks in the drawstate would need a separate
- * word to live in
- * + the clues outside the grid would have to cope with being
- * multi-digit, meaning in particular that the text formatting
- * would become more unpleasant
- * + most importantly, though, the solver just isn't fast
- * enough. Even at size 9 it can't really do the solver_hard
- * factorial-time enumeration at a sensible rate. Easy puzzles
- * higher than that would be possible, but more latin-squarey
- * than skyscrapery, as it were.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <ctype.h>
- #ifdef NO_TGMATH_H
- # include <math.h>
- #else
- # include <tgmath.h>
- #endif
- #include "puzzles.h"
- #include "latin.h"
- /*
- * Difficulty levels. I do some macro ickery here to ensure that my
- * enum and the various forms of my name list always match up.
- */
- #define DIFFLIST(A) \
- A(EASY,Easy,solver_easy,e) \
- A(HARD,Hard,solver_hard,h) \
- A(EXTREME,Extreme,NULL,x) \
- A(UNREASONABLE,Unreasonable,NULL,u)
- #define ENUM(upper,title,func,lower) DIFF_ ## upper,
- #define TITLE(upper,title,func,lower) #title,
- #define ENCODE(upper,title,func,lower) #lower
- #define CONFIG(upper,title,func,lower) ":" #title
- enum { DIFFLIST(ENUM) DIFFCOUNT };
- static char const *const towers_diffnames[] = { DIFFLIST(TITLE) };
- static char const towers_diffchars[] = DIFFLIST(ENCODE);
- #define DIFFCONFIG DIFFLIST(CONFIG)
- enum {
- COL_BACKGROUND,
- COL_GRID,
- COL_USER,
- COL_HIGHLIGHT,
- COL_ERROR,
- COL_PENCIL,
- COL_DONE,
- NCOLOURS
- };
- struct game_params {
- int w, diff;
- };
- struct clues {
- int refcount;
- int w;
- /*
- * An array of 4w integers, of which:
- * - the first w run across the top
- * - the next w across the bottom
- * - the third w down the left
- * - the last w down the right.
- */
- int *clues;
- /*
- * An array of w*w digits.
- */
- digit *immutable;
- };
- /*
- * Macros to compute clue indices and coordinates.
- */
- #define STARTSTEP(start, step, index, w) do { \
- if (index < w) \
- start = index, step = w; \
- else if (index < 2*w) \
- start = (w-1)*w+(index-w), step = -w; \
- else if (index < 3*w) \
- start = w*(index-2*w), step = 1; \
- else \
- start = w*(index-3*w)+(w-1), step = -1; \
- } while (0)
- #define CSTARTSTEP(start, step, index, w) \
- STARTSTEP(start, step, (((index)+2*w)%(4*w)), w)
- #define CLUEPOS(x, y, index, w) do { \
- if (index < w) \
- x = index, y = -1; \
- else if (index < 2*w) \
- x = index-w, y = w; \
- else if (index < 3*w) \
- x = -1, y = index-2*w; \
- else \
- x = w, y = index-3*w; \
- } while (0)
- #ifdef STANDALONE_SOLVER
- static const char *const cluepos[] = {
- "above column", "below column", "left of row", "right of row"
- };
- #endif
- struct game_state {
- game_params par;
- struct clues *clues;
- bool *clues_done;
- digit *grid;
- int *pencil; /* bitmaps using bits 1<<1..1<<n */
- bool completed, cheated;
- };
- static game_params *default_params(void)
- {
- game_params *ret = snew(game_params);
- ret->w = 5;
- ret->diff = DIFF_EASY;
- return ret;
- }
- static const struct game_params towers_presets[] = {
- { 4, DIFF_EASY },
- { 5, DIFF_EASY },
- { 5, DIFF_HARD },
- { 6, DIFF_EASY },
- { 6, DIFF_HARD },
- { 6, DIFF_EXTREME },
- { 6, DIFF_UNREASONABLE },
- };
- static bool game_fetch_preset(int i, char **name, game_params **params)
- {
- game_params *ret;
- char buf[80];
- if (i < 0 || i >= lenof(towers_presets))
- return false;
- ret = snew(game_params);
- *ret = towers_presets[i]; /* structure copy */
- sprintf(buf, "%dx%d %s", ret->w, ret->w, towers_diffnames[ret->diff]);
- *name = dupstr(buf);
- *params = ret;
- return true;
- }
- static void free_params(game_params *params)
- {
- sfree(params);
- }
- static game_params *dup_params(const game_params *params)
- {
- game_params *ret = snew(game_params);
- *ret = *params; /* structure copy */
- return ret;
- }
- static void decode_params(game_params *params, char const *string)
- {
- char const *p = string;
- params->w = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- if (*p == 'd') {
- int i;
- p++;
- params->diff = DIFFCOUNT+1; /* ...which is invalid */
- if (*p) {
- for (i = 0; i < DIFFCOUNT; i++) {
- if (*p == towers_diffchars[i])
- params->diff = i;
- }
- p++;
- }
- }
- }
- static char *encode_params(const game_params *params, bool full)
- {
- char ret[80];
- sprintf(ret, "%d", params->w);
- if (full)
- sprintf(ret + strlen(ret), "d%c", towers_diffchars[params->diff]);
- return dupstr(ret);
- }
- static config_item *game_configure(const game_params *params)
- {
- config_item *ret;
- char buf[80];
- ret = snewn(3, config_item);
- ret[0].name = "Grid size";
- ret[0].type = C_STRING;
- sprintf(buf, "%d", params->w);
- ret[0].u.string.sval = dupstr(buf);
- ret[1].name = "Difficulty";
- ret[1].type = C_CHOICES;
- ret[1].u.choices.choicenames = DIFFCONFIG;
- ret[1].u.choices.selected = params->diff;
- ret[2].name = NULL;
- ret[2].type = C_END;
- return ret;
- }
- static game_params *custom_params(const config_item *cfg)
- {
- game_params *ret = snew(game_params);
- ret->w = atoi(cfg[0].u.string.sval);
- ret->diff = cfg[1].u.choices.selected;
- return ret;
- }
- static const char *validate_params(const game_params *params, bool full)
- {
- if (params->w < 3 || params->w > 9)
- return "Grid size must be between 3 and 9";
- if (params->diff >= DIFFCOUNT)
- return "Unknown difficulty rating";
- return NULL;
- }
- /* ----------------------------------------------------------------------
- * Solver.
- */
- struct solver_ctx {
- int w, diff;
- bool started;
- int *clues;
- long *iscratch;
- int *dscratch;
- };
- static int solver_easy(struct latin_solver *solver, void *vctx)
- {
- struct solver_ctx *ctx = (struct solver_ctx *)vctx;
- int w = ctx->w;
- int c, i, j, n, m, furthest;
- int start, step, cstart, cstep, clue, pos, cpos;
- int ret = 0;
- #ifdef STANDALONE_SOLVER
- char prefix[256];
- #endif
- if (!ctx->started) {
- ctx->started = true;
- /*
- * One-off loop to help get started: when a pair of facing
- * clues sum to w+1, it must mean that the row consists of
- * two increasing sequences back to back, so we can
- * immediately place the highest digit by knowing the
- * lengths of those two sequences.
- */
- for (c = 0; c < 3*w; c = (c == w-1 ? 2*w : c+1)) {
- int c2 = c + w;
- if (ctx->clues[c] && ctx->clues[c2] &&
- ctx->clues[c] + ctx->clues[c2] == w+1) {
- STARTSTEP(start, step, c, w);
- CSTARTSTEP(cstart, cstep, c, w);
- pos = start + (ctx->clues[c]-1)*step;
- cpos = cstart + (ctx->clues[c]-1)*cstep;
- if (solver->cube[cpos*w+w-1]) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working) {
- printf("%*sfacing clues on %s %d are maximal:\n",
- solver_recurse_depth*4, "",
- c>=2*w ? "row" : "column", c % w + 1);
- printf("%*s placing %d at (%d,%d)\n",
- solver_recurse_depth*4, "",
- w, pos%w+1, pos/w+1);
- }
- #endif
- latin_solver_place(solver, pos%w, pos/w, w);
- ret = 1;
- } else {
- ret = -1;
- }
- }
- }
- if (ret)
- return ret;
- }
- /*
- * Go over every clue doing reasonably simple heuristic
- * deductions.
- */
- for (c = 0; c < 4*w; c++) {
- clue = ctx->clues[c];
- if (!clue)
- continue;
- STARTSTEP(start, step, c, w);
- CSTARTSTEP(cstart, cstep, c, w);
- /* Find the location of each number in the row. */
- for (i = 0; i < w; i++)
- ctx->dscratch[i] = w;
- for (i = 0; i < w; i++)
- if (solver->grid[start+i*step])
- ctx->dscratch[solver->grid[start+i*step]-1] = i;
- n = m = 0;
- furthest = w;
- for (i = w; i >= 1; i--) {
- if (ctx->dscratch[i-1] == w) {
- break;
- } else if (ctx->dscratch[i-1] < furthest) {
- furthest = ctx->dscratch[i-1];
- m = i;
- n++;
- }
- }
- if (clue == n+1 && furthest > 1) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working)
- sprintf(prefix, "%*sclue %s %d is nearly filled:\n",
- solver_recurse_depth*4, "",
- cluepos[c/w], c%w+1);
- else
- prefix[0] = '\0'; /* placate optimiser */
- #endif
- /*
- * We can already see an increasing sequence of the very
- * highest numbers, of length one less than that
- * specified in the clue. All of those numbers _must_ be
- * part of the clue sequence, so the number right next
- * to the clue must be the final one - i.e. it must be
- * bigger than any of the numbers between it and m. This
- * allows us to rule out small numbers in that square.
- *
- * (This is a generalisation of the obvious deduction
- * that when you see a clue saying 1, it must be right
- * next to the largest possible number; and similarly,
- * when you see a clue saying 2 opposite that, it must
- * be right next to the second-largest.)
- */
- j = furthest-1; /* number of small numbers we can rule out */
- for (i = 1; i <= w && j > 0; i++) {
- if (ctx->dscratch[i-1] < w && ctx->dscratch[i-1] >= furthest)
- continue; /* skip this number, it's elsewhere */
- j--;
- if (solver->cube[cstart*w+i-1]) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working) {
- printf("%s%*s ruling out %d at (%d,%d)\n",
- prefix, solver_recurse_depth*4, "",
- i, start%w+1, start/w+1);
- prefix[0] = '\0';
- }
- #endif
- solver->cube[cstart*w+i-1] = 0;
- ret = 1;
- }
- }
- }
- if (ret)
- return ret;
- #ifdef STANDALONE_SOLVER
- if (solver_show_working)
- sprintf(prefix, "%*slower bounds for clue %s %d:\n",
- solver_recurse_depth*4, "",
- cluepos[c/w], c%w+1);
- else
- prefix[0] = '\0'; /* placate optimiser */
- #endif
- i = 0;
- for (n = w; n > 0; n--) {
- /*
- * The largest number cannot occur in the first (clue-1)
- * squares of the row, or else there wouldn't be space
- * for a sufficiently long increasing sequence which it
- * terminated. The second-largest number (not counting
- * any that are known to be on the far side of a larger
- * number and hence excluded from this sequence) cannot
- * occur in the first (clue-2) squares, similarly, and
- * so on.
- */
- if (ctx->dscratch[n-1] < w) {
- for (m = n+1; m < w; m++)
- if (ctx->dscratch[m] < ctx->dscratch[n-1])
- break;
- if (m < w)
- continue; /* this number doesn't count */
- }
- for (j = 0; j < clue - i - 1; j++)
- if (solver->cube[(cstart + j*cstep)*w+n-1]) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working) {
- int pos = start+j*step;
- printf("%s%*s ruling out %d at (%d,%d)\n",
- prefix, solver_recurse_depth*4, "",
- n, pos%w+1, pos/w+1);
- prefix[0] = '\0';
- }
- #endif
- solver->cube[(cstart + j*cstep)*w+n-1] = 0;
- ret = 1;
- }
- i++;
- }
- }
- if (ret)
- return ret;
- return 0;
- }
- static int solver_hard(struct latin_solver *solver, void *vctx)
- {
- struct solver_ctx *ctx = (struct solver_ctx *)vctx;
- int w = ctx->w;
- int c, i, j, n, best, clue, start, step, ret;
- long bitmap;
- #ifdef STANDALONE_SOLVER
- char prefix[256];
- #endif
- /*
- * Go over every clue analysing all possibilities.
- */
- for (c = 0; c < 4*w; c++) {
- clue = ctx->clues[c];
- if (!clue)
- continue;
- CSTARTSTEP(start, step, c, w);
- for (i = 0; i < w; i++)
- ctx->iscratch[i] = 0;
- /*
- * Instead of a tedious physical recursion, I iterate in the
- * scratch array through all possibilities. At any given
- * moment, i indexes the element of the box that will next
- * be incremented.
- */
- i = 0;
- ctx->dscratch[i] = 0;
- best = n = 0;
- bitmap = 0;
- while (1) {
- if (i < w) {
- /*
- * Find the next valid value for cell i.
- */
- int limit = (n == clue ? best : w);
- int pos = start + step * i;
- for (j = ctx->dscratch[i] + 1; j <= limit; j++) {
- if (bitmap & (1L << j))
- continue; /* used this one already */
- if (!solver->cube[pos*w+j-1])
- continue; /* ruled out already */
- /* Found one. */
- break;
- }
- if (j > limit) {
- /* No valid values left; drop back. */
- i--;
- if (i < 0)
- break; /* overall iteration is finished */
- bitmap &= ~(1L << ctx->dscratch[i]);
- if (ctx->dscratch[i] == best) {
- n--;
- best = 0;
- for (j = 0; j < i; j++)
- if (best < ctx->dscratch[j])
- best = ctx->dscratch[j];
- }
- } else {
- /* Got a valid value; store it and move on. */
- bitmap |= 1L << j;
- ctx->dscratch[i++] = j;
- if (j > best) {
- best = j;
- n++;
- }
- ctx->dscratch[i] = 0;
- }
- } else {
- if (n == clue) {
- for (j = 0; j < w; j++)
- ctx->iscratch[j] |= 1L << ctx->dscratch[j];
- }
- i--;
- bitmap &= ~(1L << ctx->dscratch[i]);
- if (ctx->dscratch[i] == best) {
- n--;
- best = 0;
- for (j = 0; j < i; j++)
- if (best < ctx->dscratch[j])
- best = ctx->dscratch[j];
- }
- }
- }
- #ifdef STANDALONE_SOLVER
- if (solver_show_working)
- sprintf(prefix, "%*sexhaustive analysis of clue %s %d:\n",
- solver_recurse_depth*4, "",
- cluepos[c/w], c%w+1);
- else
- prefix[0] = '\0'; /* placate optimiser */
- #endif
- ret = 0;
- for (i = 0; i < w; i++) {
- int pos = start + step * i;
- for (j = 1; j <= w; j++) {
- if (solver->cube[pos*w+j-1] &&
- !(ctx->iscratch[i] & (1L << j))) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working) {
- printf("%s%*s ruling out %d at (%d,%d)\n",
- prefix, solver_recurse_depth*4, "",
- j, pos/w+1, pos%w+1);
- prefix[0] = '\0';
- }
- #endif
- solver->cube[pos*w+j-1] = 0;
- ret = 1;
- }
- }
- /*
- * Once we find one clue we can do something with in
- * this way, revert to trying easier deductions, so as
- * not to generate solver diagnostics that make the
- * problem look harder than it is.
- */
- if (ret)
- return ret;
- }
- }
- return 0;
- }
- #define SOLVER(upper,title,func,lower) func,
- static usersolver_t const towers_solvers[] = { DIFFLIST(SOLVER) };
- static bool towers_valid(struct latin_solver *solver, void *vctx)
- {
- struct solver_ctx *ctx = (struct solver_ctx *)vctx;
- int w = ctx->w;
- int c, i, n, best, clue, start, step;
- for (c = 0; c < 4*w; c++) {
- clue = ctx->clues[c];
- if (!clue)
- continue;
- STARTSTEP(start, step, c, w);
- n = best = 0;
- for (i = 0; i < w; i++) {
- if (solver->grid[start+i*step] > best) {
- best = solver->grid[start+i*step];
- n++;
- }
- }
- if (n != clue) {
- #ifdef STANDALONE_SOLVER
- if (solver_show_working)
- printf("%*sclue %s %d is violated\n",
- solver_recurse_depth*4, "",
- cluepos[c/w], c%w+1);
- #endif
- return false;
- }
- }
- return true;
- }
- static int solver(int w, int *clues, digit *soln, int maxdiff)
- {
- int ret;
- struct solver_ctx ctx;
- ctx.w = w;
- ctx.diff = maxdiff;
- ctx.clues = clues;
- ctx.started = false;
- ctx.iscratch = snewn(w, long);
- ctx.dscratch = snewn(w+1, int);
- ret = latin_solver(soln, w, maxdiff,
- DIFF_EASY, DIFF_HARD, DIFF_EXTREME,
- DIFF_EXTREME, DIFF_UNREASONABLE,
- towers_solvers, towers_valid, &ctx, NULL, NULL);
- sfree(ctx.iscratch);
- sfree(ctx.dscratch);
- return ret;
- }
- /* ----------------------------------------------------------------------
- * Grid generation.
- */
- static char *new_game_desc(const game_params *params, random_state *rs,
- char **aux, bool interactive)
- {
- int w = params->w, a = w*w;
- digit *grid, *soln, *soln2;
- int *clues, *order;
- int i, ret;
- int diff = params->diff;
- char *desc, *p;
- /*
- * Difficulty exceptions: some combinations of size and
- * difficulty cannot be satisfied, because all puzzles of at
- * most that difficulty are actually even easier.
- *
- * Remember to re-test this whenever a change is made to the
- * solver logic!
- *
- * I tested it using the following shell command:
- for d in e h x u; do
- for i in {3..9}; do
- echo -n "./towers --generate 1 ${i}d${d}: "
- perl -e 'alarm 30; exec @ARGV' ./towers --generate 1 ${i}d${d} >/dev/null \
- && echo ok
- done
- done
- * Of course, it's better to do that after taking the exceptions
- * _out_, so as to detect exceptions that should be removed as
- * well as those which should be added.
- */
- if (diff > DIFF_HARD && w <= 3)
- diff = DIFF_HARD;
- grid = NULL;
- clues = snewn(4*w, int);
- soln = snewn(a, digit);
- soln2 = snewn(a, digit);
- order = snewn(max(4*w,a), int);
- while (1) {
- /*
- * Construct a latin square to be the solution.
- */
- sfree(grid);
- grid = latin_generate(w, rs);
- /*
- * Fill in the clues.
- */
- for (i = 0; i < 4*w; i++) {
- int start, step, j, k, best;
- STARTSTEP(start, step, i, w);
- k = best = 0;
- for (j = 0; j < w; j++) {
- if (grid[start+j*step] > best) {
- best = grid[start+j*step];
- k++;
- }
- }
- clues[i] = k;
- }
- /*
- * Remove the grid numbers and then the clues, one by one,
- * for as long as the game remains soluble at the given
- * difficulty.
- */
- memcpy(soln, grid, a);
- if (diff == DIFF_EASY && w <= 5) {
- /*
- * Special case: for Easy-mode grids that are small
- * enough, it's nice to be able to find completely empty
- * grids.
- */
- memset(soln2, 0, a);
- ret = solver(w, clues, soln2, diff);
- if (ret > diff)
- continue;
- }
- for (i = 0; i < a; i++)
- order[i] = i;
- shuffle(order, a, sizeof(*order), rs);
- for (i = 0; i < a; i++) {
- int j = order[i];
- memcpy(soln2, grid, a);
- soln2[j] = 0;
- ret = solver(w, clues, soln2, diff);
- if (ret <= diff)
- grid[j] = 0;
- }
- if (diff > DIFF_EASY) { /* leave all clues on Easy mode */
- for (i = 0; i < 4*w; i++)
- order[i] = i;
- shuffle(order, 4*w, sizeof(*order), rs);
- for (i = 0; i < 4*w; i++) {
- int j = order[i];
- int clue = clues[j];
- memcpy(soln2, grid, a);
- clues[j] = 0;
- ret = solver(w, clues, soln2, diff);
- if (ret > diff)
- clues[j] = clue;
- }
- }
- /*
- * See if the game can be solved at the specified difficulty
- * level, but not at the one below.
- */
- memcpy(soln2, grid, a);
- ret = solver(w, clues, soln2, diff);
- if (ret != diff)
- continue; /* go round again */
- /*
- * We've got a usable puzzle!
- */
- break;
- }
- /*
- * Encode the puzzle description.
- */
- desc = snewn(40*a, char);
- p = desc;
- for (i = 0; i < 4*w; i++) {
- if (i)
- *p++ = '/';
- if (clues[i])
- p += sprintf(p, "%d", clues[i]);
- }
- for (i = 0; i < a; i++)
- if (grid[i])
- break;
- if (i < a) {
- int run = 0;
- *p++ = ',';
- for (i = 0; i <= a; i++) {
- int n = (i < a ? grid[i] : -1);
- if (!n)
- run++;
- else {
- if (run) {
- while (run > 0) {
- int thisrun = min(run, 26);
- *p++ = thisrun - 1 + 'a';
- run -= thisrun;
- }
- } else {
- /*
- * If there's a number in the very top left or
- * bottom right, there's no point putting an
- * unnecessary _ before or after it.
- */
- if (i > 0 && n > 0)
- *p++ = '_';
- }
- if (n > 0)
- p += sprintf(p, "%d", n);
- run = 0;
- }
- }
- }
- *p++ = '\0';
- desc = sresize(desc, p - desc, char);
- /*
- * Encode the solution.
- */
- *aux = snewn(a+2, char);
- (*aux)[0] = 'S';
- for (i = 0; i < a; i++)
- (*aux)[i+1] = '0' + soln[i];
- (*aux)[a+1] = '\0';
- sfree(grid);
- sfree(clues);
- sfree(soln);
- sfree(soln2);
- sfree(order);
- return desc;
- }
- /* ----------------------------------------------------------------------
- * Gameplay.
- */
- static const char *validate_desc(const game_params *params, const char *desc)
- {
- int w = params->w, a = w*w;
- const char *p = desc;
- int i, clue;
- /*
- * Verify that the right number of clues are given, and that
- * they're in range.
- */
- for (i = 0; i < 4*w; i++) {
- if (!*p)
- return "Too few clues for grid size";
- if (i > 0) {
- if (*p != '/')
- return "Expected commas between clues";
- p++;
- }
- if (isdigit((unsigned char)*p)) {
- clue = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- if (clue <= 0 || clue > w)
- return "Clue number out of range";
- }
- }
- if (*p == '/')
- return "Too many clues for grid size";
- if (*p == ',') {
- /*
- * Verify that the right amount of grid data is given, and
- * that any grid elements provided are in range.
- */
- int squares = 0;
- p++;
- while (*p) {
- int c = *p++;
- if (c >= 'a' && c <= 'z') {
- squares += c - 'a' + 1;
- } else if (c == '_') {
- /* do nothing */;
- } else if (c > '0' && c <= '9') {
- int val = atoi(p-1);
- if (val < 1 || val > w)
- return "Out-of-range number in grid description";
- squares++;
- while (*p && isdigit((unsigned char)*p)) p++;
- } else
- return "Invalid character in game description";
- }
- if (squares < a)
- return "Not enough data to fill grid";
- if (squares > a)
- return "Too much data to fit in grid";
- }
- if (*p) return "Rubbish at end of game description";
- return NULL;
- }
- static key_label *game_request_keys(const game_params *params, int *nkeys)
- {
- int i;
- int w = params->w;
- key_label *keys = snewn(w+1, key_label);
- *nkeys = w + 1;
- for (i = 0; i < w; i++) {
- if (i<9) keys[i].button = '1' + i;
- else keys[i].button = 'a' + i - 9;
- keys[i].label = NULL;
- }
- keys[w].button = '\b';
- keys[w].label = NULL;
- return keys;
- }
- static game_state *new_game(midend *me, const game_params *params,
- const char *desc)
- {
- int w = params->w, a = w*w;
- game_state *state = snew(game_state);
- const char *p = desc;
- int i;
- state->par = *params; /* structure copy */
- state->clues = snew(struct clues);
- state->clues->refcount = 1;
- state->clues->w = w;
- state->clues->clues = snewn(4*w, int);
- state->clues->immutable = snewn(a, digit);
- state->grid = snewn(a, digit);
- state->clues_done = snewn(4*w, bool);
- state->pencil = snewn(a, int);
- for (i = 0; i < a; i++) {
- state->grid[i] = 0;
- state->pencil[i] = 0;
- }
- memset(state->clues->immutable, 0, a);
- memset(state->clues_done, 0, 4*w*sizeof(bool));
- for (i = 0; i < 4*w; i++) {
- if (i > 0) {
- assert(*p == '/');
- p++;
- }
- if (*p && isdigit((unsigned char)*p)) {
- state->clues->clues[i] = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- } else
- state->clues->clues[i] = 0;
- }
- if (*p == ',') {
- int pos = 0;
- p++;
- while (*p) {
- int c = *p++;
- if (c >= 'a' && c <= 'z') {
- pos += c - 'a' + 1;
- } else if (c == '_') {
- /* do nothing */;
- } else if (c > '0' && c <= '9') {
- int val = atoi(p-1);
- assert(val >= 1 && val <= w);
- assert(pos < a);
- state->grid[pos] = state->clues->immutable[pos] = val;
- pos++;
- while (*p && isdigit((unsigned char)*p)) p++;
- } else
- assert(!"Corrupt game description");
- }
- assert(pos == a);
- }
- assert(!*p);
- state->completed = false;
- state->cheated = false;
- return state;
- }
- static game_state *dup_game(const game_state *state)
- {
- int w = state->par.w, a = w*w;
- game_state *ret = snew(game_state);
- ret->par = state->par; /* structure copy */
- ret->clues = state->clues;
- ret->clues->refcount++;
- ret->grid = snewn(a, digit);
- ret->pencil = snewn(a, int);
- ret->clues_done = snewn(4*w, bool);
- memcpy(ret->grid, state->grid, a*sizeof(digit));
- memcpy(ret->pencil, state->pencil, a*sizeof(int));
- memcpy(ret->clues_done, state->clues_done, 4*w*sizeof(bool));
- ret->completed = state->completed;
- ret->cheated = state->cheated;
- return ret;
- }
- static void free_game(game_state *state)
- {
- sfree(state->grid);
- sfree(state->pencil);
- sfree(state->clues_done);
- if (--state->clues->refcount <= 0) {
- sfree(state->clues->immutable);
- sfree(state->clues->clues);
- sfree(state->clues);
- }
- sfree(state);
- }
- static char *solve_game(const game_state *state, const game_state *currstate,
- const char *aux, const char **error)
- {
- int w = state->par.w, a = w*w;
- int i, ret;
- digit *soln;
- char *out;
- if (aux)
- return dupstr(aux);
- soln = snewn(a, digit);
- memcpy(soln, state->clues->immutable, a);
- ret = solver(w, state->clues->clues, soln, DIFFCOUNT-1);
- if (ret == diff_impossible) {
- *error = "No solution exists for this puzzle";
- out = NULL;
- } else if (ret == diff_ambiguous) {
- *error = "Multiple solutions exist for this puzzle";
- out = NULL;
- } else {
- out = snewn(a+2, char);
- out[0] = 'S';
- for (i = 0; i < a; i++)
- out[i+1] = '0' + soln[i];
- out[a+1] = '\0';
- }
- sfree(soln);
- return out;
- }
- static bool game_can_format_as_text_now(const game_params *params)
- {
- return true;
- }
- static char *game_text_format(const game_state *state)
- {
- int w = state->par.w /* , a = w*w */;
- char *ret;
- char *p;
- int x, y;
- int total;
- /*
- * We have:
- * - a top clue row, consisting of three spaces, then w clue
- * digits with spaces between (total 2*w+3 chars including
- * newline)
- * - a blank line (one newline)
- * - w main rows, consisting of a left clue digit, two spaces,
- * w grid digits with spaces between, two spaces and a right
- * clue digit (total 2*w+6 chars each including newline)
- * - a blank line (one newline)
- * - a bottom clue row (same as top clue row)
- * - terminating NUL.
- *
- * Total size is therefore 2*(2*w+3) + 2 + w*(2*w+6) + 1
- * = 2w^2+10w+9.
- */
- total = 2*w*w + 10*w + 9;
- ret = snewn(total, char);
- p = ret;
- /* Top clue row. */
- *p++ = ' '; *p++ = ' ';
- for (x = 0; x < w; x++) {
- *p++ = ' ';
- *p++ = (state->clues->clues[x] ? '0' + state->clues->clues[x] : ' ');
- }
- *p++ = '\n';
- /* Blank line. */
- *p++ = '\n';
- /* Main grid. */
- for (y = 0; y < w; y++) {
- *p++ = (state->clues->clues[y+2*w] ? '0' + state->clues->clues[y+2*w] :
- ' ');
- *p++ = ' ';
- for (x = 0; x < w; x++) {
- *p++ = ' ';
- *p++ = (state->grid[y*w+x] ? '0' + state->grid[y*w+x] : ' ');
- }
- *p++ = ' '; *p++ = ' ';
- *p++ = (state->clues->clues[y+3*w] ? '0' + state->clues->clues[y+3*w] :
- ' ');
- *p++ = '\n';
- }
- /* Blank line. */
- *p++ = '\n';
- /* Bottom clue row. */
- *p++ = ' '; *p++ = ' ';
- for (x = 0; x < w; x++) {
- *p++ = ' ';
- *p++ = (state->clues->clues[x+w] ? '0' + state->clues->clues[x+w] :
- ' ');
- }
- *p++ = '\n';
- *p++ = '\0';
- assert(p == ret + total);
- return ret;
- }
- struct game_ui {
- /*
- * These are the coordinates of the currently highlighted
- * square on the grid, if hshow = 1.
- */
- int hx, hy;
- /*
- * This indicates whether the current highlight is a
- * pencil-mark one or a real one.
- */
- bool hpencil;
- /*
- * This indicates whether or not we're showing the highlight
- * (used to be hx = hy = -1); important so that when we're
- * using the cursor keys it doesn't keep coming back at a
- * fixed position. When hshow = 1, pressing a valid number
- * or letter key or Space will enter that number or letter in the grid.
- */
- bool hshow;
- /*
- * This indicates whether we're using the highlight as a cursor;
- * it means that it doesn't vanish on a keypress, and that it is
- * allowed on immutable squares.
- */
- bool hcursor;
- /*
- * User preference option which can be set to FALSE to disable the
- * 3D graphical style, and instead just display the puzzle as if
- * it was a Sudoku variant, i.e. each square just has a digit in
- * it.
- *
- * I was initially a bit uncertain about whether the 3D style
- * would be the right thing, on the basis that it uses up space in
- * the cells and makes it hard to use many pencil marks. Actually
- * nobody seems to have complained, but having put in the option
- * while I was still being uncertain, it seems silly not to leave
- * it in just in case.
- */
- int three_d;
- };
- static void legacy_prefs_override(struct game_ui *ui_out)
- {
- static bool initialised = false;
- static int three_d = -1;
- if (!initialised) {
- initialised = true;
- three_d = getenv_bool("TOWERS_2D", -1);
- }
- if (three_d != -1)
- ui_out->three_d = three_d;
- }
- static game_ui *new_ui(const game_state *state)
- {
- game_ui *ui = snew(game_ui);
- ui->hx = ui->hy = 0;
- ui->hpencil = false;
- ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
- ui->three_d = true;
- legacy_prefs_override(ui);
- return ui;
- }
- static void free_ui(game_ui *ui)
- {
- sfree(ui);
- }
- static config_item *get_prefs(game_ui *ui)
- {
- config_item *ret;
- ret = snewn(2, config_item);
- ret[0].name = "Puzzle appearance";
- ret[0].kw = "appearance";
- ret[0].type = C_CHOICES;
- ret[0].u.choices.choicenames = ":2D:3D";
- ret[0].u.choices.choicekws = ":2d:3d";
- ret[0].u.choices.selected = ui->three_d;
- ret[1].name = NULL;
- ret[1].type = C_END;
- return ret;
- }
- static void set_prefs(game_ui *ui, const config_item *cfg)
- {
- ui->three_d = cfg[0].u.choices.selected;
- }
- static void game_changed_state(game_ui *ui, const game_state *oldstate,
- const game_state *newstate)
- {
- int w = newstate->par.w;
- /*
- * We prevent pencil-mode highlighting of a filled square, unless
- * we're using the cursor keys. So if the user has just filled in
- * a square which we had a pencil-mode highlight in (by Undo, or
- * by Redo, or by Solve), then we cancel the highlight.
- */
- if (ui->hshow && ui->hpencil && !ui->hcursor &&
- newstate->grid[ui->hy * w + ui->hx] != 0) {
- ui->hshow = false;
- }
- }
- static const char *current_key_label(const game_ui *ui,
- const game_state *state, int button)
- {
- if (ui->hshow && (button == CURSOR_SELECT))
- return ui->hpencil ? "Ink" : "Pencil";
- return "";
- }
- #define PREFERRED_TILESIZE 48
- #define TILESIZE (ds->tilesize)
- #define BORDER (TILESIZE * 9 / 8)
- #define COORD(x) ((x)*TILESIZE + BORDER)
- #define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1)
- /* These always return positive values, though y offsets are actually -ve */
- #define X_3D_DISP(height, w) ((height) * TILESIZE / (8 * (w)))
- #define Y_3D_DISP(height, w) ((height) * TILESIZE / (4 * (w)))
- #define FLASH_TIME 0.4F
- #define DF_PENCIL_SHIFT 16
- #define DF_CLUE_DONE 0x10000
- #define DF_ERROR 0x8000
- #define DF_HIGHLIGHT 0x4000
- #define DF_HIGHLIGHT_PENCIL 0x2000
- #define DF_IMMUTABLE 0x1000
- #define DF_PLAYAREA 0x0800
- #define DF_DIGIT_MASK 0x00FF
- struct game_drawstate {
- int tilesize;
- long *tiles; /* (w+2)*(w+2) temp space */
- long *drawn; /* (w+2)*(w+2)*4: current drawn data */
- bool *errtmp;
- };
- static bool check_errors(const game_state *state, bool *errors)
- {
- int w = state->par.w /*, a = w*w */;
- int W = w+2, A = W*W; /* the errors array is (w+2) square */
- int *clues = state->clues->clues;
- digit *grid = state->grid;
- int i, x, y;
- bool errs = false;
- int tmp[32];
- assert(w < lenof(tmp));
- if (errors)
- for (i = 0; i < A; i++)
- errors[i] = false;
- for (y = 0; y < w; y++) {
- unsigned long mask = 0, errmask = 0;
- for (x = 0; x < w; x++) {
- unsigned long bit = 1UL << grid[y*w+x];
- errmask |= (mask & bit);
- mask |= bit;
- }
- if (mask != (1L << (w+1)) - (1L << 1)) {
- errs = true;
- errmask &= ~1UL;
- if (errors) {
- for (x = 0; x < w; x++)
- if (errmask & (1UL << grid[y*w+x]))
- errors[(y+1)*W+(x+1)] = true;
- }
- }
- }
- for (x = 0; x < w; x++) {
- unsigned long mask = 0, errmask = 0;
- for (y = 0; y < w; y++) {
- unsigned long bit = 1UL << grid[y*w+x];
- errmask |= (mask & bit);
- mask |= bit;
- }
- if (mask != (1 << (w+1)) - (1 << 1)) {
- errs = true;
- errmask &= ~1UL;
- if (errors) {
- for (y = 0; y < w; y++)
- if (errmask & (1UL << grid[y*w+x]))
- errors[(y+1)*W+(x+1)] = true;
- }
- }
- }
- for (i = 0; i < 4*w; i++) {
- int start, step, j, n, best;
- STARTSTEP(start, step, i, w);
- if (!clues[i])
- continue;
- best = n = 0;
- for (j = 0; j < w; j++) {
- int number = grid[start+j*step];
- if (!number)
- break; /* can't tell what happens next */
- if (number > best) {
- best = number;
- n++;
- }
- }
- if (n > clues[i] || (best == w && n < clues[i]) ||
- (best < w && n == clues[i])) {
- if (errors) {
- int x, y;
- CLUEPOS(x, y, i, w);
- errors[(y+1)*W+(x+1)] = true;
- }
- errs = true;
- }
- }
- return errs;
- }
- static int clue_index(const game_state *state, int x, int y)
- {
- int w = state->par.w;
- if (x == -1 || x == w)
- return w * (x == -1 ? 2 : 3) + y;
- else if (y == -1 || y == w)
- return (y == -1 ? 0 : w) + x;
- return -1;
- }
- static bool is_clue(const game_state *state, int x, int y)
- {
- int w = state->par.w;
- if (((x == -1 || x == w) && y >= 0 && y < w) ||
- ((y == -1 || y == w) && x >= 0 && x < w))
- {
- if (state->clues->clues[clue_index(state, x, y)] & DF_DIGIT_MASK)
- return true;
- }
- return false;
- }
- static char *interpret_move(const game_state *state, game_ui *ui,
- const game_drawstate *ds,
- int x, int y, int button)
- {
- int w = state->par.w;
- bool shift_or_control = button & (MOD_SHFT | MOD_CTRL);
- int tx, ty;
- char buf[80];
- button &= ~MOD_MASK;
- tx = FROMCOORD(x);
- ty = FROMCOORD(y);
- if (ui->three_d) {
- /*
- * In 3D mode, just locating the mouse click in the natural
- * square grid may not be sufficient to tell which tower the
- * user clicked on. Investigate the _tops_ of the nearby
- * towers to see if a click on one grid square was actually
- * a click on a tower protruding into that region from
- * another.
- */
- int dx, dy;
- for (dy = 0; dy <= 1; dy++)
- for (dx = 0; dx >= -1; dx--) {
- int cx = tx + dx, cy = ty + dy;
- if (cx >= 0 && cx < w && cy >= 0 && cy < w) {
- int height = state->grid[cy*w+cx];
- int bx = COORD(cx), by = COORD(cy);
- int ox = bx + X_3D_DISP(height, w);
- int oy = by - Y_3D_DISP(height, w);
- if (/* on top face? */
- (x - ox >= 0 && x - ox < TILESIZE &&
- y - oy >= 0 && y - oy < TILESIZE) ||
- /* in triangle between top-left corners? */
- (ox > bx && x >= bx && x <= ox && y <= by &&
- (by-y) * (ox-bx) <= (by-oy) * (x-bx)) ||
- /* in triangle between bottom-right corners? */
- (ox > bx && x >= bx+TILESIZE && x <= ox+TILESIZE &&
- y >= oy+TILESIZE &&
- (by-y+TILESIZE)*(ox-bx) >= (by-oy)*(x-bx-TILESIZE))) {
- tx = cx;
- ty = cy;
- }
- }
- }
- }
- if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
- if (button == LEFT_BUTTON) {
- if (tx == ui->hx && ty == ui->hy &&
- ui->hshow && !ui->hpencil) {
- ui->hshow = false;
- } else {
- ui->hx = tx;
- ui->hy = ty;
- ui->hshow = !state->clues->immutable[ty*w+tx];
- ui->hpencil = false;
- }
- ui->hcursor = false;
- return MOVE_UI_UPDATE;
- }
- if (button == RIGHT_BUTTON) {
- /*
- * Pencil-mode highlighting for non filled squares.
- */
- if (state->grid[ty*w+tx] == 0) {
- if (tx == ui->hx && ty == ui->hy &&
- ui->hshow && ui->hpencil) {
- ui->hshow = false;
- } else {
- ui->hpencil = true;
- ui->hx = tx;
- ui->hy = ty;
- ui->hshow = true;
- }
- } else {
- ui->hshow = false;
- }
- ui->hcursor = false;
- return MOVE_UI_UPDATE;
- }
- } else if (button == LEFT_BUTTON) {
- if (is_clue(state, tx, ty)) {
- sprintf(buf, "%c%d,%d", 'D', tx, ty);
- return dupstr(buf);
- }
- }
- if (IS_CURSOR_MOVE(button)) {
- if (shift_or_control) {
- int x = ui->hx, y = ui->hy;
- switch (button) {
- case CURSOR_LEFT: x = -1; break;
- case CURSOR_RIGHT: x = w; break;
- case CURSOR_UP: y = -1; break;
- case CURSOR_DOWN: y = w; break;
- }
- if (is_clue(state, x, y)) {
- sprintf(buf, "%c%d,%d", 'D', x, y);
- return dupstr(buf);
- }
- return NULL;
- }
- move_cursor(button, &ui->hx, &ui->hy, w, w, false);
- ui->hshow = true;
- ui->hcursor = true;
- return MOVE_UI_UPDATE;
- }
- if (ui->hshow &&
- (button == CURSOR_SELECT)) {
- ui->hpencil = !ui->hpencil;
- ui->hcursor = true;
- return MOVE_UI_UPDATE;
- }
- if (ui->hshow &&
- ((button >= '0' && button <= '9' && button - '0' <= w) ||
- button == CURSOR_SELECT2 || button == '\b')) {
- int n = button - '0';
- if (button == CURSOR_SELECT2 || button == '\b')
- n = 0;
- /*
- * Can't make pencil marks in a filled square. This can only
- * become highlighted if we're using cursor keys.
- */
- if (ui->hpencil && state->grid[ui->hy*w+ui->hx])
- return NULL;
- /*
- * Can't do anything to an immutable square.
- */
- if (state->clues->immutable[ui->hy*w+ui->hx])
- return NULL;
- /*
- * If you ask to fill a square with what it already contains,
- * or blank it when it's already empty, that has no effect...
- */
- if ((!ui->hpencil || n == 0) && state->grid[ui->hy*w+ui->hx] == n &&
- state->pencil[ui->hy*w+ui->hx] == 0) {
- /* ... expect to remove the cursor in mouse mode. */
- if (!ui->hcursor) {
- ui->hshow = false;
- return MOVE_UI_UPDATE;
- }
- return NULL;
- }
- sprintf(buf, "%c%d,%d,%d",
- (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
- if (!ui->hcursor) ui->hshow = false;
- return dupstr(buf);
- }
- if (button == 'M' || button == 'm')
- return dupstr("M");
- return NULL;
- }
- static game_state *execute_move(const game_state *from, const char *move)
- {
- int w = from->par.w, a = w*w;
- game_state *ret = dup_game(from);
- int x, y, i, n;
- if (move[0] == 'S') {
- ret->completed = ret->cheated = true;
- for (i = 0; i < a; i++) {
- if (move[i+1] < '1' || move[i+1] > '0'+w)
- goto badmove;
- ret->grid[i] = move[i+1] - '0';
- ret->pencil[i] = 0;
- }
- if (move[a+1] != '\0')
- goto badmove;
- return ret;
- } else if ((move[0] == 'P' || move[0] == 'R') &&
- sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
- x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) {
- if (from->clues->immutable[y*w+x])
- goto badmove;
- if (move[0] == 'P' && n > 0) {
- ret->pencil[y*w+x] ^= 1L << n;
- } else {
- ret->grid[y*w+x] = n;
- ret->pencil[y*w+x] = 0;
- if (!ret->completed && !check_errors(ret, NULL))
- ret->completed = true;
- }
- return ret;
- } else if (move[0] == 'M') {
- /*
- * Fill in absolutely all pencil marks everywhere. (I
- * wouldn't use this for actual play, but it's a handy
- * starting point when following through a set of
- * diagnostics output by the standalone solver.)
- */
- for (i = 0; i < a; i++) {
- if (!ret->grid[i])
- ret->pencil[i] = (1L << (w+1)) - (1L << 1);
- }
- return ret;
- } else if (move[0] == 'D' && sscanf(move+1, "%d,%d", &x, &y) == 2 &&
- is_clue(from, x, y)) {
- int index = clue_index(from, x, y);
- ret->clues_done[index] = !ret->clues_done[index];
- return ret;
- }
- badmove:
- /* couldn't parse move string */
- free_game(ret);
- return NULL;
- }
- /* ----------------------------------------------------------------------
- * Drawing routines.
- */
- #define SIZE(w) ((w) * TILESIZE + 2*BORDER)
- static void game_compute_size(const game_params *params, int tilesize,
- const game_ui *ui, int *x, int *y)
- {
- /* Ick: fake up `ds->tilesize' for macro expansion purposes */
- struct { int tilesize; } ads, *ds = &ads;
- ads.tilesize = tilesize;
- *x = *y = SIZE(params->w);
- }
- static void game_set_size(drawing *dr, game_drawstate *ds,
- const game_params *params, int tilesize)
- {
- ds->tilesize = tilesize;
- }
- static float *game_colours(frontend *fe, int *ncolours)
- {
- float *ret = snewn(3 * NCOLOURS, float);
- frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
- ret[COL_GRID * 3 + 0] = 0.0F;
- ret[COL_GRID * 3 + 1] = 0.0F;
- ret[COL_GRID * 3 + 2] = 0.0F;
- ret[COL_USER * 3 + 0] = 0.0F;
- ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
- ret[COL_USER * 3 + 2] = 0.0F;
- ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
- ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
- ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
- ret[COL_ERROR * 3 + 0] = 1.0F;
- ret[COL_ERROR * 3 + 1] = 0.0F;
- ret[COL_ERROR * 3 + 2] = 0.0F;
- ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
- ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
- ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
- ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
- ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
- ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
- *ncolours = NCOLOURS;
- return ret;
- }
- static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
- {
- int w = state->par.w /*, a = w*w */;
- struct game_drawstate *ds = snew(struct game_drawstate);
- int i;
- ds->tilesize = 0;
- ds->tiles = snewn((w+2)*(w+2), long);
- ds->drawn = snewn((w+2)*(w+2)*4, long);
- for (i = 0; i < (w+2)*(w+2)*4; i++)
- ds->drawn[i] = -1;
- ds->errtmp = snewn((w+2)*(w+2), bool);
- return ds;
- }
- static void game_free_drawstate(drawing *dr, game_drawstate *ds)
- {
- sfree(ds->errtmp);
- sfree(ds->tiles);
- sfree(ds->drawn);
- sfree(ds);
- }
- static void draw_tile(drawing *dr, game_drawstate *ds, const game_ui *ui,
- struct clues *clues, int x, int y, long tile)
- {
- int w = clues->w /* , a = w*w */;
- int tx, ty, bg;
- char str[64];
- tx = COORD(x);
- ty = COORD(y);
- bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
- /* draw tower */
- if (ui->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
- int coords[8];
- int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
- int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
- /* left face of tower */
- coords[0] = tx;
- coords[1] = ty - 1;
- coords[2] = tx;
- coords[3] = ty + TILESIZE - 1;
- coords[4] = coords[2] + xoff;
- coords[5] = coords[3] - yoff;
- coords[6] = coords[0] + xoff;
- coords[7] = coords[1] - yoff;
- draw_polygon(dr, coords, 4, bg, COL_GRID);
- /* bottom face of tower */
- coords[0] = tx + TILESIZE;
- coords[1] = ty + TILESIZE - 1;
- coords[2] = tx;
- coords[3] = ty + TILESIZE - 1;
- coords[4] = coords[2] + xoff;
- coords[5] = coords[3] - yoff;
- coords[6] = coords[0] + xoff;
- coords[7] = coords[1] - yoff;
- draw_polygon(dr, coords, 4, bg, COL_GRID);
- /* now offset all subsequent drawing to the top of the tower */
- tx += xoff;
- ty -= yoff;
- }
- /* erase background */
- draw_rect(dr, tx, ty, TILESIZE, TILESIZE, bg);
- /* pencil-mode highlight */
- if (tile & DF_HIGHLIGHT_PENCIL) {
- int coords[6];
- coords[0] = tx;
- coords[1] = ty;
- coords[2] = tx+TILESIZE/2;
- coords[3] = ty;
- coords[4] = tx;
- coords[5] = ty+TILESIZE/2;
- draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
- }
- /* draw box outline */
- if (tile & DF_PLAYAREA) {
- int coords[8];
- coords[0] = tx;
- coords[1] = ty - 1;
- coords[2] = tx + TILESIZE;
- coords[3] = ty - 1;
- coords[4] = tx + TILESIZE;
- coords[5] = ty + TILESIZE - 1;
- coords[6] = tx;
- coords[7] = ty + TILESIZE - 1;
- draw_polygon(dr, coords, 4, -1, COL_GRID);
- }
- /* new number needs drawing? */
- if (tile & DF_DIGIT_MASK) {
- int color;
- str[1] = '\0';
- str[0] = (tile & DF_DIGIT_MASK) + '0';
- if (tile & DF_ERROR)
- color = COL_ERROR;
- else if (tile & DF_CLUE_DONE)
- color = COL_DONE;
- else if (x < 0 || y < 0 || x >= w || y >= w)
- color = COL_GRID;
- else if (tile & DF_IMMUTABLE)
- color = COL_GRID;
- else
- color = COL_USER;
- draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, FONT_VARIABLE,
- (tile & DF_PLAYAREA ? TILESIZE/2 : TILESIZE*2/5),
- ALIGN_VCENTRE | ALIGN_HCENTRE, color, str);
- } else {
- int i, j, npencil;
- int pl, pr, pt, pb;
- float bestsize;
- int pw, ph, minph, pbest, fontsize;
- /* Count the pencil marks required. */
- for (i = 1, npencil = 0; i <= w; i++)
- if (tile & (1L << (i + DF_PENCIL_SHIFT)))
- npencil++;
- if (npencil) {
- minph = 2;
- /*
- * Determine the bounding rectangle within which we're going
- * to put the pencil marks.
- */
- /* Start with the whole square, minus space for impinging towers */
- pl = tx + (ui->three_d ? X_3D_DISP(w,w) : 0);
- pr = tx + TILESIZE;
- pt = ty;
- pb = ty + TILESIZE - (ui->three_d ? Y_3D_DISP(w,w) : 0);
- /*
- * We arrange our pencil marks in a grid layout, with
- * the number of rows and columns adjusted to allow the
- * maximum font size.
- *
- * So now we work out what the grid size ought to be.
- */
- bestsize = 0.0;
- pbest = 0;
- /* Minimum */
- for (pw = 3; pw < max(npencil,4); pw++) {
- float fw, fh, fs;
- ph = (npencil + pw - 1) / pw;
- ph = max(ph, minph);
- fw = (pr - pl) / (float)pw;
- fh = (pb - pt) / (float)ph;
- fs = min(fw, fh);
- if (fs > bestsize) {
- bestsize = fs;
- pbest = pw;
- }
- }
- assert(pbest > 0);
- pw = pbest;
- ph = (npencil + pw - 1) / pw;
- ph = max(ph, minph);
- /*
- * Now we've got our grid dimensions, work out the pixel
- * size of a grid element, and round it to the nearest
- * pixel. (We don't want rounding errors to make the
- * grid look uneven at low pixel sizes.)
- */
- fontsize = min((pr - pl) / pw, (pb - pt) / ph);
- /*
- * Centre the resulting figure in the square.
- */
- pl = pl + (pr - pl - fontsize * pw) / 2;
- pt = pt + (pb - pt - fontsize * ph) / 2;
- /*
- * Now actually draw the pencil marks.
- */
- for (i = 1, j = 0; i <= w; i++)
- if (tile & (1L << (i + DF_PENCIL_SHIFT))) {
- int dx = j % pw, dy = j / pw;
- str[1] = '\0';
- str[0] = i + '0';
- draw_text(dr, pl + fontsize * (2*dx+1) / 2,
- pt + fontsize * (2*dy+1) / 2,
- FONT_VARIABLE, fontsize,
- ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
- j++;
- }
- }
- }
- }
- static void game_redraw(drawing *dr, game_drawstate *ds,
- const game_state *oldstate, const game_state *state,
- int dir, const game_ui *ui,
- float animtime, float flashtime)
- {
- int w = state->par.w /*, a = w*w */;
- int i, x, y;
- check_errors(state, ds->errtmp);
- /*
- * Work out what data each tile should contain.
- */
- for (i = 0; i < (w+2)*(w+2); i++)
- ds->tiles[i] = 0; /* completely blank square */
- /* The clue squares... */
- for (i = 0; i < 4*w; i++) {
- long tile = state->clues->clues[i];
- CLUEPOS(x, y, i, w);
- if (ds->errtmp[(y+1)*(w+2)+(x+1)])
- tile |= DF_ERROR;
- else if (state->clues_done[i])
- tile |= DF_CLUE_DONE;
- ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
- }
- /* ... and the main grid. */
- for (y = 0; y < w; y++) {
- for (x = 0; x < w; x++) {
- long tile = DF_PLAYAREA;
- if (state->grid[y*w+x])
- tile |= state->grid[y*w+x];
- else
- tile |= (long)state->pencil[y*w+x] << DF_PENCIL_SHIFT;
- if (ui->hshow && ui->hx == x && ui->hy == y)
- tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
- if (state->clues->immutable[y*w+x])
- tile |= DF_IMMUTABLE;
- if (flashtime > 0 &&
- (flashtime <= FLASH_TIME/3 ||
- flashtime >= FLASH_TIME*2/3))
- tile |= DF_HIGHLIGHT; /* completion flash */
- if (ds->errtmp[(y+1)*(w+2)+(x+1)])
- tile |= DF_ERROR;
- ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
- }
- }
- /*
- * Now actually draw anything that needs to be changed.
- */
- for (y = 0; y < w+2; y++) {
- for (x = 0; x < w+2; x++) {
- long tl, tr, bl, br;
- int i = y*(w+2)+x;
- tr = ds->tiles[y*(w+2)+x];
- tl = (x == 0 ? 0 : ds->tiles[y*(w+2)+(x-1)]);
- br = (y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+x]);
- bl = (x == 0 || y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+(x-1)]);
- if (ds->drawn[i*4] != tl || ds->drawn[i*4+1] != tr ||
- ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
- clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
- draw_tile(dr, ds, ui, state->clues, x-1, y-1, tr);
- if (x > 0)
- draw_tile(dr, ds, ui, state->clues, x-2, y-1, tl);
- if (y <= w)
- draw_tile(dr, ds, ui, state->clues, x-1, y, br);
- if (x > 0 && y <= w)
- draw_tile(dr, ds, ui, state->clues, x-2, y, bl);
- unclip(dr);
- draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
- ds->drawn[i*4] = tl;
- ds->drawn[i*4+1] = tr;
- ds->drawn[i*4+2] = bl;
- ds->drawn[i*4+3] = br;
- }
- }
- }
- }
- static float game_anim_length(const game_state *oldstate,
- const game_state *newstate, int dir, game_ui *ui)
- {
- return 0.0F;
- }
- static float game_flash_length(const game_state *oldstate,
- const game_state *newstate, int dir, game_ui *ui)
- {
- if (!oldstate->completed && newstate->completed &&
- !oldstate->cheated && !newstate->cheated)
- return FLASH_TIME;
- return 0.0F;
- }
- static void game_get_cursor_location(const game_ui *ui,
- const game_drawstate *ds,
- const game_state *state,
- const game_params *params,
- int *x, int *y, int *w, int *h)
- {
- if(ui->hshow) {
- *x = COORD(ui->hx);
- *y = COORD(ui->hy);
- *w = *h = TILESIZE;
- }
- }
- static int game_status(const game_state *state)
- {
- return state->completed ? +1 : 0;
- }
- static void game_print_size(const game_params *params, const game_ui *ui,
- float *x, float *y)
- {
- int pw, ph;
- /*
- * We use 9mm squares by default, like Solo.
- */
- game_compute_size(params, 900, ui, &pw, &ph);
- *x = pw / 100.0F;
- *y = ph / 100.0F;
- }
- static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
- int tilesize)
- {
- int w = state->par.w;
- int ink = print_mono_colour(dr, 0);
- int i, x, y;
- /* Ick: fake up `ds->tilesize' for macro expansion purposes */
- game_drawstate ads, *ds = &ads;
- game_set_size(dr, ds, NULL, tilesize);
- /*
- * Border.
- */
- print_line_width(dr, 3 * TILESIZE / 40);
- draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, w*TILESIZE, ink);
- /*
- * Main grid.
- */
- for (x = 1; x < w; x++) {
- print_line_width(dr, TILESIZE / 40);
- draw_line(dr, BORDER+x*TILESIZE, BORDER,
- BORDER+x*TILESIZE, BORDER+w*TILESIZE, ink);
- }
- for (y = 1; y < w; y++) {
- print_line_width(dr, TILESIZE / 40);
- draw_line(dr, BORDER, BORDER+y*TILESIZE,
- BORDER+w*TILESIZE, BORDER+y*TILESIZE, ink);
- }
- /*
- * Clues.
- */
- for (i = 0; i < 4*w; i++) {
- char str[128];
- if (!state->clues->clues[i])
- continue;
- CLUEPOS(x, y, i, w);
- sprintf (str, "%d", state->clues->clues[i]);
- draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2,
- BORDER + y*TILESIZE + TILESIZE/2,
- FONT_VARIABLE, TILESIZE/2,
- ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
- }
- /*
- * Numbers for the solution, if any.
- */
- for (y = 0; y < w; y++)
- for (x = 0; x < w; x++)
- if (state->grid[y*w+x]) {
- char str[2];
- str[1] = '\0';
- str[0] = state->grid[y*w+x] + '0';
- draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2,
- BORDER + y*TILESIZE + TILESIZE/2,
- FONT_VARIABLE, TILESIZE/2,
- ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
- }
- }
- #ifdef COMBINED
- #define thegame towers
- #endif
- const struct game thegame = {
- "Towers", "games.towers", "towers",
- default_params,
- game_fetch_preset, NULL,
- decode_params,
- encode_params,
- free_params,
- dup_params,
- true, game_configure, custom_params,
- validate_params,
- new_game_desc,
- validate_desc,
- new_game,
- dup_game,
- free_game,
- true, solve_game,
- true, game_can_format_as_text_now, game_text_format,
- get_prefs, set_prefs,
- new_ui,
- free_ui,
- NULL, /* encode_ui */
- NULL, /* decode_ui */
- game_request_keys,
- game_changed_state,
- current_key_label,
- interpret_move,
- execute_move,
- PREFERRED_TILESIZE, game_compute_size, game_set_size,
- game_colours,
- game_new_drawstate,
- game_free_drawstate,
- game_redraw,
- game_anim_length,
- game_flash_length,
- game_get_cursor_location,
- game_status,
- true, false, game_print_size, game_print,
- false, /* wants_statusbar */
- false, NULL, /* timing_state */
- REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
- };
- #ifdef STANDALONE_SOLVER
- #include <stdarg.h>
- int main(int argc, char **argv)
- {
- game_params *p;
- game_state *s;
- char *id = NULL, *desc;
- const char *err;
- bool grade = false;
- int ret, diff;
- bool really_show_working = false;
- while (--argc > 0) {
- char *p = *++argv;
- if (!strcmp(p, "-v")) {
- really_show_working = true;
- } else if (!strcmp(p, "-g")) {
- grade = true;
- } else if (*p == '-') {
- fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
- return 1;
- } else {
- id = p;
- }
- }
- if (!id) {
- fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
- return 1;
- }
- desc = strchr(id, ':');
- if (!desc) {
- fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
- return 1;
- }
- *desc++ = '\0';
- p = default_params();
- decode_params(p, id);
- err = validate_desc(p, desc);
- if (err) {
- fprintf(stderr, "%s: %s\n", argv[0], err);
- return 1;
- }
- s = new_game(NULL, p, desc);
- /*
- * When solving an Easy puzzle, we don't want to bother the
- * user with Hard-level deductions. For this reason, we grade
- * the puzzle internally before doing anything else.
- */
- ret = -1; /* placate optimiser */
- solver_show_working = 0;
- for (diff = 0; diff < DIFFCOUNT; diff++) {
- memcpy(s->grid, s->clues->immutable, p->w * p->w);
- ret = solver(p->w, s->clues->clues, s->grid, diff);
- if (ret <= diff)
- break;
- }
- if (really_show_working) {
- /*
- * Now run the solver again at the last difficulty level we
- * tried, but this time with diagnostics enabled.
- */
- solver_show_working = really_show_working;
- memcpy(s->grid, s->clues->immutable, p->w * p->w);
- ret = solver(p->w, s->clues->clues, s->grid,
- diff < DIFFCOUNT ? diff : DIFFCOUNT-1);
- }
- if (diff == DIFFCOUNT) {
- if (grade)
- printf("Difficulty rating: ambiguous\n");
- else
- printf("Unable to find a unique solution\n");
- } else {
- if (grade) {
- if (ret == diff_impossible)
- printf("Difficulty rating: impossible (no solution exists)\n");
- else
- printf("Difficulty rating: %s\n", towers_diffnames[ret]);
- } else {
- if (ret != diff)
- printf("Puzzle is inconsistent\n");
- else
- fputs(game_text_format(s), stdout);
- }
- }
- return 0;
- }
- #endif
- /* vim: set shiftwidth=4 tabstop=8: */
|