1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565 |
- /*
- * dominosa.c: Domino jigsaw puzzle. Aim to place one of every
- * possible domino within a rectangle in such a way that the number
- * on each square matches the provided clue.
- */
- /*
- * Further possible deduction types in the solver:
- *
- * * possibly an advanced form of deduce_parity via 2-connectedness.
- * We currently deal with areas of the graph with exactly one way
- * in and out; but if you have an area with exactly _two_ routes in
- * and out of it, then you can at least decide on the _relative_
- * parity of the two (either 'these two edges both bisect dominoes
- * or neither do', or 'exactly one of these edges bisects a
- * domino'). And occasionally that can be enough to let you rule
- * out one of the two remaining choices.
- * + For example, if both those edges bisect a domino, then those
- * two dominoes would also be both the same.
- * + Or perhaps between them they rule out all possibilities for
- * some other square.
- * + Or perhaps they themselves would be duplicates!
- * + Or perhaps, on purely geometric grounds, they would box in a
- * square to the point where it ended up having to be an
- * isolated singleton.
- * + The tricky part of this is how you do the graph theory.
- * Perhaps a modified form of Tarjan's bridge-finding algorithm
- * would work, but I haven't thought through the details.
- *
- * * possibly an advanced version of set analysis which doesn't have
- * to start from squares all having the same number? For example,
- * if you have three mutually non-adjacent squares labelled 1,2,3
- * such that the numbers adjacent to each are precisely the other
- * two, then set analysis can work just fine in principle, and
- * tells you that those three squares must overlap the three
- * dominoes 1-2, 2-3 and 1-3 in some order, so you can rule out any
- * placements of those elsewhere.
- * + the difficulty with this is how you avoid it going painfully
- * exponential-time. You can't iterate over all the subsets, so
- * you'd need some kind of more sophisticated directed search.
- * + and the adjacency allowance has to be similarly accounted
- * for, which could get tricky to keep track of.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <ctype.h>
- #include <limits.h>
- #ifdef NO_TGMATH_H
- # include <math.h>
- #else
- # include <tgmath.h>
- #endif
- #include "puzzles.h"
- /* nth triangular number */
- #define TRI(n) ( (n) * ((n) + 1) / 2 )
- /* number of dominoes for value n */
- #define DCOUNT(n) TRI((n)+1)
- /* map a pair of numbers to a unique domino index from 0 upwards. */
- #define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) )
- #define FLASH_TIME 0.13F
- /*
- * 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(X) \
- X(TRIVIAL,Trivial,t) \
- X(BASIC,Basic,b) \
- X(HARD,Hard,h) \
- X(EXTREME,Extreme,e) \
- X(AMBIGUOUS,Ambiguous,a) \
- /* end of list */
- #define ENUM(upper,title,lower) DIFF_ ## upper,
- #define TITLE(upper,title,lower) #title,
- #define ENCODE(upper,title,lower) #lower
- #define CONFIG(upper,title,lower) ":" #title
- enum { DIFFLIST(ENUM) DIFFCOUNT };
- static char const *const dominosa_diffnames[] = { DIFFLIST(TITLE) };
- static char const dominosa_diffchars[] = DIFFLIST(ENCODE);
- #define DIFFCONFIG DIFFLIST(CONFIG)
- enum {
- COL_BACKGROUND,
- COL_TEXT,
- COL_DOMINO,
- COL_DOMINOCLASH,
- COL_DOMINOTEXT,
- COL_EDGE,
- COL_HIGHLIGHT_1,
- COL_HIGHLIGHT_2,
- NCOLOURS
- };
- struct game_params {
- int n;
- int diff;
- };
- struct game_numbers {
- int refcount;
- int *numbers; /* h x w */
- };
- #define EDGE_L 0x100
- #define EDGE_R 0x200
- #define EDGE_T 0x400
- #define EDGE_B 0x800
- struct game_state {
- game_params params;
- int w, h;
- struct game_numbers *numbers;
- int *grid;
- unsigned short *edges; /* h x w */
- bool completed, cheated;
- };
- static game_params *default_params(void)
- {
- game_params *ret = snew(game_params);
- ret->n = 6;
- ret->diff = DIFF_BASIC;
- return ret;
- }
- static const struct game_params dominosa_presets[] = {
- { 3, DIFF_TRIVIAL },
- { 4, DIFF_TRIVIAL },
- { 5, DIFF_TRIVIAL },
- { 6, DIFF_TRIVIAL },
- { 4, DIFF_BASIC },
- { 5, DIFF_BASIC },
- { 6, DIFF_BASIC },
- { 7, DIFF_BASIC },
- { 8, DIFF_BASIC },
- { 9, DIFF_BASIC },
- { 6, DIFF_HARD },
- { 6, DIFF_EXTREME },
- };
- static bool game_fetch_preset(int i, char **name, game_params **params_out)
- {
- game_params *params;
- char buf[80];
- if (i < 0 || i >= lenof(dominosa_presets))
- return false;
- params = snew(game_params);
- *params = dominosa_presets[i]; /* structure copy */
- sprintf(buf, "Order %d, %s", params->n, dominosa_diffnames[params->diff]);
- *name = dupstr(buf);
- *params_out = params;
- 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)
- {
- const char *p = string;
- params->n = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- while (*p) {
- char c = *p++;
- if (c == 'a') {
- /* Legacy encoding from before the difficulty system */
- params->diff = DIFF_AMBIGUOUS;
- } else if (c == 'd') {
- int i;
- params->diff = DIFFCOUNT+1; /* ...which is invalid */
- if (*p) {
- for (i = 0; i < DIFFCOUNT; i++) {
- if (*p == dominosa_diffchars[i])
- params->diff = i;
- }
- p++;
- }
- }
- }
- }
- static char *encode_params(const game_params *params, bool full)
- {
- char buf[80];
- int len = sprintf(buf, "%d", params->n);
- if (full)
- len += sprintf(buf + len, "d%c", dominosa_diffchars[params->diff]);
- return dupstr(buf);
- }
- static config_item *game_configure(const game_params *params)
- {
- config_item *ret;
- char buf[80];
- ret = snewn(3, config_item);
- ret[0].name = "Maximum number on dominoes";
- ret[0].type = C_STRING;
- sprintf(buf, "%d", params->n);
- 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->n = 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->n < 1)
- return "Maximum face number must be at least one";
- if (params->n > INT_MAX - 2 ||
- params->n + 2 > INT_MAX / (params->n + 1))
- return "Maximum face number must not be unreasonably large";
- if (params->diff >= DIFFCOUNT)
- return "Unknown difficulty rating";
- return NULL;
- }
- /* ----------------------------------------------------------------------
- * Solver.
- */
- #ifdef STANDALONE_SOLVER
- #define SOLVER_DIAGNOSTICS
- static bool solver_diagnostics = false;
- #elif defined SOLVER_DIAGNOSTICS
- static const bool solver_diagnostics = true;
- #endif
- struct solver_domino;
- struct solver_placement;
- /*
- * Information about a particular domino.
- */
- struct solver_domino {
- /* The numbers on the domino, and its index in the dominoes array. */
- int lo, hi, index;
- /* List of placements not yet ruled out for this domino. */
- int nplacements;
- struct solver_placement **placements;
- #ifdef SOLVER_DIAGNOSTICS
- /* A textual name we can easily reuse in solver diagnostics. */
- char *name;
- #endif
- };
- /*
- * Information about a particular 'placement' (i.e. specific location
- * that a domino might go in).
- */
- struct solver_placement {
- /* The index of this placement in sc->placements. */
- int index;
- /* The two squares that make up this placement. */
- struct solver_square *squares[2];
- /* The domino that has to go in this position, if any. */
- struct solver_domino *domino;
- /* The index of this placement in each square's placements array,
- * and in that of the domino. */
- int spi[2], dpi;
- /* Whether this is still considered a possible placement. */
- bool active;
- /* Other domino placements that overlap with this one. (Maximum 6:
- * three overlapping each square of the placement.) */
- int noverlaps;
- struct solver_placement *overlaps[6];
- #ifdef SOLVER_DIAGNOSTICS
- /* A textual name we can easily reuse in solver diagnostics. */
- char *name;
- #endif
- };
- /*
- * Information about a particular solver square.
- */
- struct solver_square {
- /* The coordinates of the square, and its index in a normal grid array. */
- int x, y, index;
- /* List of domino placements not yet ruled out for this square. */
- int nplacements;
- struct solver_placement *placements[4];
- /* The number in the square. */
- int number;
- #ifdef SOLVER_DIAGNOSTICS
- /* A textual name we can easily reuse in solver diagnostics. */
- char *name;
- #endif
- };
- struct solver_scratch {
- int n, dc, pc, w, h, wh;
- int max_diff_used;
- struct solver_domino *dominoes;
- struct solver_placement *placements;
- struct solver_square *squares;
- struct solver_placement **domino_placement_lists;
- struct solver_square **squares_by_number;
- struct findloopstate *fls;
- bool squares_by_number_initialised;
- int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch;
- DSF *dsf_scratch;
- };
- static struct solver_scratch *solver_make_scratch(int n)
- {
- int dc = DCOUNT(n), w = n+2, h = n+1, wh = w*h;
- int pc = (w-1)*h + w*(h-1);
- struct solver_scratch *sc = snew(struct solver_scratch);
- int hi, lo, di, x, y, pi, si;
- sc->n = n;
- sc->dc = dc;
- sc->pc = pc;
- sc->w = w;
- sc->h = h;
- sc->wh = wh;
- sc->dominoes = snewn(dc, struct solver_domino);
- sc->placements = snewn(pc, struct solver_placement);
- sc->squares = snewn(wh, struct solver_square);
- sc->domino_placement_lists = snewn(pc, struct solver_placement *);
- sc->fls = findloop_new_state(wh);
- for (di = hi = 0; hi <= n; hi++) {
- for (lo = 0; lo <= hi; lo++) {
- assert(di == DINDEX(hi, lo));
- sc->dominoes[di].hi = hi;
- sc->dominoes[di].lo = lo;
- sc->dominoes[di].index = di;
- #ifdef SOLVER_DIAGNOSTICS
- {
- char buf[128];
- sprintf(buf, "%d-%d", hi, lo);
- sc->dominoes[di].name = dupstr(buf);
- }
- #endif
- di++;
- }
- }
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- struct solver_square *sq = &sc->squares[y*w+x];
- sq->x = x;
- sq->y = y;
- sq->index = y * w + x;
- sq->nplacements = 0;
- #ifdef SOLVER_DIAGNOSTICS
- {
- char buf[128];
- sprintf(buf, "(%d,%d)", x, y);
- sq->name = dupstr(buf);
- }
- #endif
- }
- }
- pi = 0;
- for (y = 0; y < h-1; y++) {
- for (x = 0; x < w; x++) {
- assert(pi < pc);
- sc->placements[pi].squares[0] = &sc->squares[y*w+x];
- sc->placements[pi].squares[1] = &sc->squares[(y+1)*w+x];
- #ifdef SOLVER_DIAGNOSTICS
- {
- char buf[128];
- sprintf(buf, "(%d,%d-%d)", x, y, y+1);
- sc->placements[pi].name = dupstr(buf);
- }
- #endif
- pi++;
- }
- }
- for (y = 0; y < h; y++) {
- for (x = 0; x < w-1; x++) {
- assert(pi < pc);
- sc->placements[pi].squares[0] = &sc->squares[y*w+x];
- sc->placements[pi].squares[1] = &sc->squares[y*w+(x+1)];
- #ifdef SOLVER_DIAGNOSTICS
- {
- char buf[128];
- sprintf(buf, "(%d-%d,%d)", x, x+1, y);
- sc->placements[pi].name = dupstr(buf);
- }
- #endif
- pi++;
- }
- }
- assert(pi == pc);
- /* Set up the full placement lists for all squares, temporarily,
- * so as to use them to calculate the overlap lists */
- for (si = 0; si < wh; si++)
- sc->squares[si].nplacements = 0;
- for (pi = 0; pi < pc; pi++) {
- struct solver_placement *p = &sc->placements[pi];
- for (si = 0; si < 2; si++) {
- struct solver_square *sq = p->squares[si];
- p->spi[si] = sq->nplacements;
- sq->placements[sq->nplacements++] = p;
- }
- }
- /* Actually calculate the overlap lists */
- for (pi = 0; pi < pc; pi++) {
- struct solver_placement *p = &sc->placements[pi];
- p->noverlaps = 0;
- for (si = 0; si < 2; si++) {
- struct solver_square *sq = p->squares[si];
- int j;
- for (j = 0; j < sq->nplacements; j++) {
- struct solver_placement *q = sq->placements[j];
- if (q != p)
- p->overlaps[p->noverlaps++] = q;
- }
- }
- }
- /* Fill in the index field of the placements */
- for (pi = 0; pi < pc; pi++)
- sc->placements[pi].index = pi;
- /* Lazily initialised by particular solver techniques that might
- * never be needed */
- sc->squares_by_number = NULL;
- sc->squares_by_number_initialised = false;
- sc->wh_scratch = NULL;
- sc->pc_scratch = sc->pc_scratch2 = NULL;
- sc->dc_scratch = NULL;
- sc->dsf_scratch = NULL;
- return sc;
- }
- static void solver_free_scratch(struct solver_scratch *sc)
- {
- #ifdef SOLVER_DIAGNOSTICS
- {
- int i;
- for (i = 0; i < sc->dc; i++)
- sfree(sc->dominoes[i].name);
- for (i = 0; i < sc->pc; i++)
- sfree(sc->placements[i].name);
- for (i = 0; i < sc->wh; i++)
- sfree(sc->squares[i].name);
- }
- #endif
- sfree(sc->dominoes);
- sfree(sc->placements);
- sfree(sc->squares);
- sfree(sc->domino_placement_lists);
- sfree(sc->squares_by_number);
- findloop_free_state(sc->fls);
- sfree(sc->wh_scratch);
- sfree(sc->pc_scratch);
- sfree(sc->pc_scratch2);
- sfree(sc->dc_scratch);
- dsf_free(sc->dsf_scratch);
- sfree(sc);
- }
- static void solver_setup_grid(struct solver_scratch *sc, const int *numbers)
- {
- int i, j;
- for (i = 0; i < sc->wh; i++) {
- sc->squares[i].nplacements = 0;
- sc->squares[i].number = numbers[sc->squares[i].index];
- }
- for (i = 0; i < sc->pc; i++) {
- struct solver_placement *p = &sc->placements[i];
- int di = DINDEX(p->squares[0]->number, p->squares[1]->number);
- p->domino = &sc->dominoes[di];
- }
- for (i = 0; i < sc->dc; i++)
- sc->dominoes[i].nplacements = 0;
- for (i = 0; i < sc->pc; i++)
- sc->placements[i].domino->nplacements++;
- for (i = j = 0; i < sc->dc; i++) {
- sc->dominoes[i].placements = sc->domino_placement_lists + j;
- j += sc->dominoes[i].nplacements;
- sc->dominoes[i].nplacements = 0;
- }
- for (i = 0; i < sc->pc; i++) {
- struct solver_placement *p = &sc->placements[i];
- p->dpi = p->domino->nplacements;
- p->domino->placements[p->domino->nplacements++] = p;
- p->active = true;
- }
- for (i = 0; i < sc->wh; i++)
- sc->squares[i].nplacements = 0;
- for (i = 0; i < sc->pc; i++) {
- struct solver_placement *p = &sc->placements[i];
- for (j = 0; j < 2; j++) {
- struct solver_square *sq = p->squares[j];
- p->spi[j] = sq->nplacements;
- sq->placements[sq->nplacements++] = p;
- }
- }
- sc->max_diff_used = DIFF_TRIVIAL;
- sc->squares_by_number_initialised = false;
- }
- /* Given two placements p,q that overlap, returns si such that
- * p->squares[si] is the square also in q */
- static int common_square_index(struct solver_placement *p,
- struct solver_placement *q)
- {
- return (p->squares[0] == q->squares[0] ||
- p->squares[0] == q->squares[1]) ? 0 : 1;
- }
- /* Sort function used to set up squares_by_number */
- static int squares_by_number_cmpfn(const void *av, const void *bv)
- {
- struct solver_square *a = *(struct solver_square *const *)av;
- struct solver_square *b = *(struct solver_square *const *)bv;
- return (a->number < b->number ? -1 : a->number > b->number ? +1 :
- a->index < b->index ? -1 : a->index > b->index ? +1 : 0);
- }
- static void rule_out_placement(
- struct solver_scratch *sc, struct solver_placement *p)
- {
- struct solver_domino *d = p->domino;
- int i, j, si;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics)
- printf(" ruling out placement %s for domino %s\n", p->name, d->name);
- #endif
- p->active = false;
- i = p->dpi;
- assert(d->placements[i] == p);
- if (--d->nplacements != i) {
- d->placements[i] = d->placements[d->nplacements];
- d->placements[i]->dpi = i;
- }
- for (si = 0; si < 2; si++) {
- struct solver_square *sq = p->squares[si];
- i = p->spi[si];
- assert(sq->placements[i] == p);
- if (--sq->nplacements != i) {
- sq->placements[i] = sq->placements[sq->nplacements];
- j = (sq->placements[i]->squares[0] == sq ? 0 : 1);
- sq->placements[i]->spi[j] = i;
- }
- }
- }
- /*
- * If a domino has only one placement remaining, rule out all other
- * placements that overlap it.
- */
- static bool deduce_domino_single_placement(struct solver_scratch *sc, int di)
- {
- struct solver_domino *d = &sc->dominoes[di];
- struct solver_placement *p, *q;
- int oi;
- bool done_something = false;
- if (d->nplacements != 1)
- return false;
- p = d->placements[0];
- for (oi = 0; oi < p->noverlaps; oi++) {
- q = p->overlaps[oi];
- if (q->active) {
- if (!done_something) {
- done_something = true;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics)
- printf("domino %s has unique placement %s\n",
- d->name, p->name);
- #endif
- }
- rule_out_placement(sc, q);
- }
- }
- return done_something;
- }
- /*
- * If a square has only one placement remaining, rule out all other
- * placements of its domino.
- */
- static bool deduce_square_single_placement(struct solver_scratch *sc, int si)
- {
- struct solver_square *sq = &sc->squares[si];
- struct solver_placement *p;
- struct solver_domino *d;
- if (sq->nplacements != 1)
- return false;
- p = sq->placements[0];
- d = p->domino;
- if (d->nplacements <= 1)
- return false; /* we already knew everything this would tell us */
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics)
- printf("square %s has unique placement %s (domino %s)\n",
- sq->name, p->name, p->domino->name);
- #endif
- while (d->nplacements > 1)
- rule_out_placement(
- sc, d->placements[0] == p ? d->placements[1] : d->placements[0]);
- return true;
- }
- /*
- * If all placements for a square involve the same domino, rule out
- * all other placements of that domino.
- */
- static bool deduce_square_single_domino(struct solver_scratch *sc, int si)
- {
- struct solver_square *sq = &sc->squares[si];
- struct solver_domino *d;
- int i;
- /*
- * We only bother with this if the square has at least _two_
- * placements. If it only has one, then a simpler deduction will
- * have handled it already, or will do so the next time round the
- * main solver loop - and we should let the simpler deduction do
- * it, because that will give a less overblown diagnostic.
- */
- if (sq->nplacements < 2)
- return false;
- d = sq->placements[0]->domino;
- for (i = 1; i < sq->nplacements; i++)
- if (sq->placements[i]->domino != d)
- return false; /* not all the same domino */
- if (d->nplacements <= sq->nplacements)
- return false; /* no other placements of d to rule out */
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics)
- printf("square %s can only contain domino %s\n", sq->name, d->name);
- #endif
- for (i = d->nplacements; i-- > 0 ;) {
- struct solver_placement *p = d->placements[i];
- if (p->squares[0] != sq && p->squares[1] != sq)
- rule_out_placement(sc, p);
- }
- return true;
- }
- /*
- * If any placement is overlapped by _all_ possible placements of a
- * given domino, rule that placement out.
- */
- static bool deduce_domino_must_overlap(struct solver_scratch *sc, int di)
- {
- struct solver_domino *d = &sc->dominoes[di];
- struct solver_placement *intersection[6], *p;
- int nintersection = 0;
- int i, j, k;
- /*
- * As in deduce_square_single_domino, we only bother with this
- * deduction if the domino has at least two placements.
- */
- if (d->nplacements < 2)
- return false;
- /* Initialise our set of overlapped placements with all the active
- * ones overlapped by placements[0]. */
- p = d->placements[0];
- for (i = 0; i < p->noverlaps; i++)
- if (p->overlaps[i]->active)
- intersection[nintersection++] = p->overlaps[i];
- /* Now loop over the other placements of d, winnowing that set. */
- for (j = 1; j < d->nplacements; j++) {
- int old_n;
- p = d->placements[j];
- old_n = nintersection;
- nintersection = 0;
- for (k = 0; k < old_n; k++) {
- for (i = 0; i < p->noverlaps; i++)
- if (p->overlaps[i] == intersection[k])
- goto found;
- /* If intersection[k] isn't in p->overlaps, exclude it
- * from our set of placements overlapped by everything */
- continue;
- found:
- intersection[nintersection++] = intersection[k];
- }
- }
- if (nintersection == 0)
- return false; /* no new exclusions */
- for (i = 0; i < nintersection; i++) {
- p = intersection[i];
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("placement %s of domino %s overlaps all placements "
- "of domino %s:", p->name, p->domino->name, d->name);
- for (j = 0; j < d->nplacements; j++)
- printf(" %s", d->placements[j]->name);
- printf("\n");
- }
- #endif
- rule_out_placement(sc, p);
- }
- return true;
- }
- /*
- * If a placement of domino D overlaps the only remaining placement
- * for some square S which is not also for domino D, then placing D
- * here would require another copy of it in S, so we can rule it out.
- */
- static bool deduce_local_duplicate(struct solver_scratch *sc, int pi)
- {
- struct solver_placement *p = &sc->placements[pi];
- struct solver_domino *d = p->domino;
- int i, j;
- if (!p->active)
- return false;
- for (i = 0; i < p->noverlaps; i++) {
- struct solver_placement *q = p->overlaps[i];
- struct solver_square *sq;
- if (!q->active)
- continue;
- /* Find the square of q that _isn't_ part of p */
- sq = q->squares[1 - common_square_index(q, p)];
- for (j = 0; j < sq->nplacements; j++)
- if (sq->placements[j] != q && sq->placements[j]->domino != d)
- goto no;
- /* If we get here, every possible placement for sq is either q
- * itself, or another copy of d. Success! We can rule out p. */
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("placement %s of domino %s would force another copy of %s "
- "in square %s\n", p->name, d->name, d->name, sq->name);
- }
- #endif
- rule_out_placement(sc, p);
- return true;
- no:;
- }
- return false;
- }
- /*
- * If placement P overlaps one placement for each of two squares S,T
- * such that all the remaining placements for both S and T are the
- * same domino D (and none of those placements joins S and T to each
- * other), then P can't be placed, because it would leave S,T each
- * having to be a copy of D, i.e. duplicates.
- */
- static bool deduce_local_duplicate_2(struct solver_scratch *sc, int pi)
- {
- struct solver_placement *p = &sc->placements[pi];
- int i, j, k;
- if (!p->active)
- return false;
- /*
- * Iterate over pairs of placements qi,qj overlapping p.
- */
- for (i = 0; i < p->noverlaps; i++) {
- struct solver_placement *qi = p->overlaps[i];
- struct solver_square *sqi;
- struct solver_domino *di = NULL;
- if (!qi->active)
- continue;
- /* Find the square of qi that _isn't_ part of p */
- sqi = qi->squares[1 - common_square_index(qi, p)];
- /*
- * Identify the unique domino involved in all possible
- * placements of sqi other than qi. If there isn't a unique
- * one (either too many or too few), move on and try the next
- * qi.
- */
- for (k = 0; k < sqi->nplacements; k++) {
- struct solver_placement *pk = sqi->placements[k];
- if (sqi->placements[k] == qi)
- continue; /* not counting qi itself */
- if (!di)
- di = pk->domino;
- else if (di != pk->domino)
- goto done_qi;
- }
- if (!di)
- goto done_qi;
- /*
- * Now find an appropriate qj != qi.
- */
- for (j = 0; j < p->noverlaps; j++) {
- struct solver_placement *qj = p->overlaps[j];
- struct solver_square *sqj;
- bool found_di = false;
- if (j == i || !qj->active)
- continue;
- sqj = qj->squares[1 - common_square_index(qj, p)];
- /*
- * As above, we want the same domino di to be the only one
- * sqj can be if placement qj is ruled out. But also we
- * need no placement of sqj to overlap sqi.
- */
- for (k = 0; k < sqj->nplacements; k++) {
- struct solver_placement *pk = sqj->placements[k];
- if (pk == qj)
- continue; /* not counting qj itself */
- if (pk->domino != di)
- goto done_qj; /* found a different domino */
- if (pk->squares[0] == sqi || pk->squares[1] == sqi)
- goto done_qj; /* sqi,sqj can be joined to each other */
- found_di = true;
- }
- if (!found_di)
- goto done_qj;
- /* If we get here, then every placement for either of sqi
- * and sqj is a copy of di, except for the ones that
- * overlap p. Success! We can rule out p. */
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("placement %s of domino %s would force squares "
- "%s and %s to both be domino %s\n",
- p->name, p->domino->name,
- sqi->name, sqj->name, di->name);
- }
- #endif
- rule_out_placement(sc, p);
- return true;
- done_qj:;
- }
- done_qi:;
- }
- return false;
- }
- struct parity_findloop_ctx {
- struct solver_scratch *sc;
- struct solver_square *sq;
- int i;
- };
- static int parity_neighbour(int vertex, void *vctx)
- {
- struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx;
- struct solver_placement *p;
- if (vertex >= 0) {
- ctx->sq = &ctx->sc->squares[vertex];
- ctx->i = 0;
- } else {
- assert(ctx->sq);
- }
- if (ctx->i >= ctx->sq->nplacements) {
- ctx->sq = NULL;
- return -1;
- }
- p = ctx->sq->placements[ctx->i++];
- return p->squares[0]->index + p->squares[1]->index - ctx->sq->index;
- }
- /*
- * Look for dominoes whose placement would disconnect the unfilled
- * area of the grid into pieces with odd area. Such a domino can't be
- * placed, because then the area on each side of it would be
- * untileable.
- */
- static bool deduce_parity(struct solver_scratch *sc)
- {
- struct parity_findloop_ctx pflctx;
- bool done_something = false;
- int pi;
- /*
- * Run findloop, aka Tarjan's bridge-finding algorithm, on the
- * graph whose vertices are squares, with two vertices separated
- * by an edge iff some not-yet-ruled-out domino placement covers
- * them both. (So each edge itself corresponds to a domino
- * placement.)
- *
- * The effect is that any bridge in this graph is a domino whose
- * placement would separate two previously connected areas of the
- * unfilled squares of the grid.
- *
- * Placing that domino would not just disconnect those areas from
- * each other, but also use up one square of each. So if we want
- * to avoid leaving two odd areas after placing the domino, it
- * follows that we want to avoid the bridge having an _even_
- * number of vertices on each side.
- */
- pflctx.sc = sc;
- findloop_run(sc->fls, sc->wh, parity_neighbour, &pflctx);
- for (pi = 0; pi < sc->pc; pi++) {
- struct solver_placement *p = &sc->placements[pi];
- int size0, size1;
- if (!p->active)
- continue;
- if (!findloop_is_bridge(
- sc->fls, p->squares[0]->index, p->squares[1]->index,
- &size0, &size1))
- continue;
- /* To make a deduction, size0 and size1 must both be even,
- * i.e. after placing this domino decrements each by 1 they
- * would both become odd and untileable areas. */
- if ((size0 | size1) & 1)
- continue;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("placement %s of domino %s would create two odd-sized "
- "areas\n", p->name, p->domino->name);
- }
- #endif
- rule_out_placement(sc, p);
- done_something = true;
- }
- return done_something;
- }
- /*
- * Try to find a set of squares all containing the same number, such
- * that the set of possible dominoes for all the squares in that set
- * is small enough to let us rule out placements of those dominoes
- * elsewhere.
- */
- static bool deduce_set(struct solver_scratch *sc, bool doubles)
- {
- struct solver_square **sqs, **sqp, **sqe;
- int num, nsq, i, j;
- unsigned long domino_sets[16], adjacent[16];
- struct solver_domino *ds[16];
- bool done_something = false;
- if (!sc->squares_by_number)
- sc->squares_by_number = snewn(sc->wh, struct solver_square *);
- if (!sc->wh_scratch)
- sc->wh_scratch = snewn(sc->wh, int);
- if (!sc->squares_by_number_initialised) {
- /*
- * If this is the first call to this function for a given
- * grid, start by sorting the squares by their containing
- * number.
- */
- for (i = 0; i < sc->wh; i++)
- sc->squares_by_number[i] = &sc->squares[i];
- qsort(sc->squares_by_number, sc->wh, sizeof(*sc->squares_by_number),
- squares_by_number_cmpfn);
- }
- sqp = sc->squares_by_number;
- sqe = sc->squares_by_number + sc->wh;
- for (num = 0; num <= sc->n; num++) {
- unsigned long squares;
- unsigned long squares_done;
- /* Find the bounds of the subinterval of squares_by_number
- * containing squares with this particular number. */
- sqs = sqp;
- while (sqp < sqe && (*sqp)->number == num)
- sqp++;
- nsq = sqp - sqs;
- /*
- * Now sqs[0], ..., sqs[nsq-1] are the squares containing 'num'.
- */
- if (nsq > lenof(domino_sets)) {
- /*
- * Abort this analysis if we're trying to enumerate all
- * the subsets of a too-large base set.
- *
- * This _shouldn't_ happen, at the time of writing this
- * code, because the largest puzzle we support is only
- * supposed to have 10 instances of each number, and part
- * of our input grid validation checks that each number
- * does appear the right number of times. But just in case
- * weird test input makes its way to this function, or the
- * puzzle sizes are expanded later, it's easy enough to
- * just rule out doing this analysis for overlarge sets of
- * numbers.
- */
- continue;
- }
- /*
- * Index the squares in wh_scratch, which we're using as a
- * lookup table to map the official index of a square back to
- * its value in our local indexing scheme.
- */
- for (i = 0; i < nsq; i++)
- sc->wh_scratch[sqs[i]->index] = i;
- /*
- * For each square, make a bit mask of the dominoes that can
- * overlap it, by finding the number at the other end of each
- * one.
- *
- * Also, for each square, make a bit mask of other squares in
- * the current list that might occupy the _same_ domino
- * (because a possible placement of a double overlaps both).
- * We'll need that for evaluating whether sets are properly
- * exhaustive.
- */
- for (i = 0; i < nsq; i++)
- adjacent[i] = 0;
- for (i = 0; i < nsq; i++) {
- struct solver_square *sq = sqs[i];
- unsigned long mask = 0;
- for (j = 0; j < sq->nplacements; j++) {
- struct solver_placement *p = sq->placements[j];
- int othernum = p->domino->lo + p->domino->hi - num;
- mask |= 1UL << othernum;
- ds[othernum] = p->domino; /* so we can find them later */
- if (othernum == num) {
- /*
- * Special case: this is a double, so it gives
- * rise to entries in adjacent[].
- */
- int i2 = sc->wh_scratch[p->squares[0]->index +
- p->squares[1]->index - sq->index];
- adjacent[i] |= 1UL << i2;
- adjacent[i2] |= 1UL << i;
- }
- }
- domino_sets[i] = mask;
- }
- squares_done = 0;
- for (squares = 0; squares < (1UL << nsq); squares++) {
- unsigned long dominoes = 0;
- int bitpos, nsquares, ndominoes;
- bool got_adj_squares = false;
- bool reported = false;
- bool rule_out_nondoubles;
- int min_nused_for_double;
- #ifdef SOLVER_DIAGNOSTICS
- const char *rule_out_text;
- #endif
- /*
- * We don't do set analysis on the same square of the grid
- * more than once in this loop. Otherwise you generate
- * pointlessly overcomplicated diagnostics for simpler
- * follow-up deductions. For example, suppose squares
- * {A,B} must go with dominoes {X,Y}. So you rule out X,Y
- * elsewhere, and then it turns out square C (from which
- * one of those was eliminated) has only one remaining
- * possibility Z. What you _don't_ want to do is
- * triumphantly report a second case of set elimination
- * where you say 'And also, squares {A,B,C} have to be
- * {X,Y,Z}!' You'd prefer to give 'now C has to be Z' as a
- * separate deduction later, more simply phrased.
- */
- if (squares & squares_done)
- continue;
- nsquares = 0;
- /* Make the set of dominoes that these squares can inhabit. */
- for (bitpos = 0; bitpos < nsq; bitpos++) {
- if (!(1 & (squares >> bitpos)))
- continue; /* this bit isn't set in the mask */
- if (adjacent[bitpos] & squares)
- got_adj_squares = true;
- dominoes |= domino_sets[bitpos];
- nsquares++;
- }
- /* Count them. */
- ndominoes = 0;
- for (bitpos = 0; bitpos < nsq; bitpos++)
- ndominoes += 1 & (dominoes >> bitpos);
- /*
- * Do the two sets have the right relative size?
- */
- if (!got_adj_squares) {
- /*
- * The normal case, in which every possible domino
- * placement involves at most _one_ of these squares.
- *
- * This is exactly analogous to the set analysis
- * deductions in many other puzzles: if our N squares
- * between them have to account for N distinct
- * dominoes, with exactly one of those dominoes to
- * each square, then all those dominoes correspond to
- * all those squares and we can rule out any
- * placements of the same dominoes appearing
- * elsewhere.
- */
- if (ndominoes != nsquares)
- continue;
- rule_out_nondoubles = true;
- min_nused_for_double = 1;
- #ifdef SOLVER_DIAGNOSTICS
- rule_out_text = "all of them elsewhere";
- #endif
- } else {
- if (!doubles)
- continue; /* not at this difficulty level */
- /*
- * But in Dominosa, there's a special case if _two_
- * squares in this set can possibly both be covered by
- * the same double domino. (I.e. if they are adjacent,
- * and moreover, the double-domino placement
- * containing both is not yet ruled out.)
- *
- * In that situation, the simple argument doesn't hold
- * up, because the N squares might be covered by N-1
- * dominoes - or, put another way, if you list the
- * containing domino for each of the squares, they
- * might not be all distinct.
- *
- * In that situation, we can still do something, but
- * the details vary, and there are two further cases.
- */
- if (ndominoes == nsquares-1) {
- /*
- * Suppose there is one _more_ square in our set
- * than there are dominoes it can involve. For
- * example, suppose we had four '0' squares which
- * between them could contain only the 0-0, 0-1
- * and 0-2 dominoes.
- *
- * Then that can only work at all if the 0-0
- * covers two of those squares - and in that
- * situation that _must_ be what's happened.
- *
- * So we can rule out the 0-1 and 0-2 dominoes (in
- * this example) in any placement that doesn't use
- * one of the squares in this set. And we can rule
- * out a placement of the 0-0 even if it uses
- * _one_ square from this set: in this situation,
- * we have to insist on it using _two_.
- */
- rule_out_nondoubles = true;
- min_nused_for_double = 2;
- #ifdef SOLVER_DIAGNOSTICS
- rule_out_text = "all of them elsewhere "
- "(including the double if it fails to use both)";
- #endif
- } else if (ndominoes == nsquares) {
- /*
- * A restricted form of the deduction is still
- * possible if we have the same number of dominoes
- * as squares.
- *
- * If we have _three_ '0' squares none of which
- * can be any domino other than 0-0, 0-1 and 0-2,
- * and there's still a possibility of an 0-0
- * domino using up two of them, then we can't rule
- * out 0-1 or 0-2 anywhere else, because it's
- * possible that these three squares only use two
- * of the dominoes between them.
- *
- * But we _can_ rule out the double 0-0, in any
- * placement that uses _none_ of our three
- * squares. Because we do know that _at least one_
- * of our squares must be involved in the 0-0, or
- * else the three of them would only have the
- * other two dominoes left.
- */
- rule_out_nondoubles = false;
- min_nused_for_double = 1;
- #ifdef SOLVER_DIAGNOSTICS
- rule_out_text = "the double elsewhere";
- #endif
- } else {
- /*
- * If none of those cases has happened, then our
- * set admits no deductions at all.
- */
- continue;
- }
- }
- /* Skip sets of size 1, or whose complement has size 1.
- * Those can be handled by a simpler analysis, and should
- * be, for more sensible solver diagnostics. */
- if (ndominoes <= 1 || ndominoes >= nsq-1)
- continue;
- /*
- * We've found a set! That means we can rule out any
- * placement of any domino in that set which would leave
- * the squares in the set with too few dominoes between
- * them.
- *
- * We may or may not actually end up ruling anything out
- * here. But even if we don't, we should record that these
- * squares form a self-contained set, so that we don't
- * pointlessly report a superset of them later which could
- * instead be reported as just the other ones.
- *
- * Or rather, we do that for the main cases that let us
- * rule out lots of dominoes. We only do this with the
- * borderline case where we can only rule out a double if
- * we _actually_ rule something out. Otherwise we'll never
- * even _find_ a larger set with the same number of
- * dominoes!
- */
- if (rule_out_nondoubles)
- squares_done |= squares;
- for (bitpos = 0; bitpos < nsq; bitpos++) {
- struct solver_domino *d;
- if (!(1 & (dominoes >> bitpos)))
- continue;
- d = ds[bitpos];
- for (i = d->nplacements; i-- > 0 ;) {
- struct solver_placement *p = d->placements[i];
- int si, nused;
- /* Count how many of our squares this placement uses. */
- for (si = nused = 0; si < 2; si++) {
- struct solver_square *sq2 = p->squares[si];
- if (sq2->number == num &&
- (1 & (squares >> sc->wh_scratch[sq2->index])))
- nused++;
- }
- /* See if that's too many to rule it out. */
- if (d->lo == d->hi) {
- if (nused >= min_nused_for_double)
- continue;
- } else {
- if (nused > 0 || !rule_out_nondoubles)
- continue;
- }
- if (!reported) {
- reported = true;
- done_something = true;
- /* In case we didn't do this above */
- squares_done |= squares;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- int b;
- const char *sep;
- printf("squares {");
- for (sep = "", b = 0; b < nsq; b++)
- if (1 & (squares >> b)) {
- printf("%s%s", sep, sqs[b]->name);
- sep = ",";
- }
- printf("} can contain only dominoes {");
- for (sep = "", b = 0; b < nsq; b++)
- if (1 & (dominoes >> b)) {
- printf("%s%s", sep, ds[b]->name);
- sep = ",";
- }
- printf("}, so rule out %s", rule_out_text);
- printf("\n");
- }
- #endif
- }
- rule_out_placement(sc, p);
- }
- }
- }
- }
- return done_something;
- }
- static int forcing_chain_dup_cmp(const void *av, const void *bv, void *ctx)
- {
- struct solver_scratch *sc = (struct solver_scratch *)ctx;
- int a = *(const int *)av, b = *(const int *)bv;
- int ac, bc;
- ac = sc->pc_scratch[a];
- bc = sc->pc_scratch[b];
- if (ac != bc) return ac > bc ? +1 : -1;
- ac = sc->placements[a].domino->index;
- bc = sc->placements[b].domino->index;
- if (ac != bc) return ac > bc ? +1 : -1;
- return 0;
- }
- static int forcing_chain_sq_cmp(const void *av, const void *bv, void *ctx)
- {
- struct solver_scratch *sc = (struct solver_scratch *)ctx;
- int a = *(const int *)av, b = *(const int *)bv;
- int ac, bc;
- ac = sc->placements[a].domino->index;
- bc = sc->placements[b].domino->index;
- if (ac != bc) return ac > bc ? +1 : -1;
- ac = sc->pc_scratch[a];
- bc = sc->pc_scratch[b];
- if (ac != bc) return ac > bc ? +1 : -1;
- return 0;
- }
- static bool deduce_forcing_chain(struct solver_scratch *sc)
- {
- int si, pi, di, j, k, m;
- bool done_something = false;
- if (!sc->wh_scratch)
- sc->wh_scratch = snewn(sc->wh, int);
- if (!sc->pc_scratch)
- sc->pc_scratch = snewn(sc->pc, int);
- if (!sc->pc_scratch2)
- sc->pc_scratch2 = snewn(sc->pc, int);
- if (!sc->dc_scratch)
- sc->dc_scratch = snewn(sc->dc, int);
- if (!sc->dsf_scratch)
- sc->dsf_scratch = dsf_new_flip(sc->pc);
- /*
- * Start by identifying chains of placements which must all occur
- * together if any of them occurs. We do this by making
- * dsf_scratch a flip dsf binding the placements into an equivalence
- * class for each entire forcing chain, with the two possible sets
- * of dominoes for the chain listed as inverses.
- */
- dsf_reinit(sc->dsf_scratch);
- for (si = 0; si < sc->wh; si++) {
- struct solver_square *sq = &sc->squares[si];
- if (sq->nplacements == 2)
- dsf_merge_flip(sc->dsf_scratch,
- sq->placements[0]->index,
- sq->placements[1]->index, true);
- }
- /*
- * Now read out the whole dsf into pc_scratch, flattening its
- * structured data into a simple integer id per chain of dominoes
- * that must occur together.
- *
- * The integer ids have the property that any two that differ only
- * in the lowest bit (i.e. of the form {2n,2n+1}) represent
- * complementary chains, each of which rules out the other.
- */
- for (pi = 0; pi < sc->pc; pi++) {
- bool inv;
- int c = dsf_canonify_flip(sc->dsf_scratch, pi, &inv);
- sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0);
- }
- /*
- * Identify chains that contain a duplicate domino, and rule them
- * out. We do this by making a list of the placement indices in
- * pc_scratch2, sorted by (chain id, domino id), so that dupes
- * become adjacent.
- */
- for (pi = 0; pi < sc->pc; pi++)
- sc->pc_scratch2[pi] = pi;
- arraysort(sc->pc_scratch2, sc->pc, forcing_chain_dup_cmp, sc);
- for (j = 0; j < sc->pc ;) {
- struct solver_domino *duplicated_domino = NULL;
- /*
- * This loop iterates once per contiguous segment of the same
- * value in pc_scratch2, i.e. once per chain.
- */
- int ci = sc->pc_scratch[sc->pc_scratch2[j]];
- int climit, cstart = j;
- while (j < sc->pc && sc->pc_scratch[sc->pc_scratch2[j]] == ci)
- j++;
- climit = j;
- /*
- * Now look for a duplicate domino within that chain.
- */
- for (k = cstart; k + 1 < climit; k++) {
- struct solver_placement *p = &sc->placements[sc->pc_scratch2[k]];
- struct solver_placement *q = &sc->placements[sc->pc_scratch2[k+1]];
- if (p->domino == q->domino) {
- duplicated_domino = p->domino;
- break;
- }
- }
- if (!duplicated_domino)
- continue;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("domino %s occurs more than once in forced chain:",
- duplicated_domino->name);
- for (k = cstart; k < climit; k++)
- printf(" %s", sc->placements[sc->pc_scratch2[k]].name);
- printf("\n");
- }
- #endif
- for (k = cstart; k < climit; k++)
- rule_out_placement(sc, &sc->placements[sc->pc_scratch2[k]]);
- done_something = true;
- }
- if (done_something)
- return true;
- /*
- * A second way in which a whole forcing chain can be ruled out is
- * if it contains all the dominoes that can occupy some other
- * square, so that if the domnioes in the chain were all laid, the
- * other square would be left without any choices.
- *
- * To detect this, we sort the placements again, this time by
- * (domino index, chain index), so that we can easily find a
- * sorted list of chains per domino. That allows us to iterate
- * over the squares and check for a chain id common to all the
- * placements of that square.
- */
- for (pi = 0; pi < sc->pc; pi++)
- sc->pc_scratch2[pi] = pi;
- arraysort(sc->pc_scratch2, sc->pc, forcing_chain_sq_cmp, sc);
- /* Store a lookup table of the first entry in pc_scratch2
- * corresponding to each domino. */
- for (di = j = 0; j < sc->pc; j++) {
- while (di <= sc->placements[sc->pc_scratch2[j]].domino->index) {
- assert(di < sc->dc);
- sc->dc_scratch[di++] = j;
- }
- }
- assert(di == sc->dc);
- for (si = 0; si < sc->wh; si++) {
- struct solver_square *sq = &sc->squares[si];
- int listpos = 0, listsize = 0, listout = 0;
- int exclude[4];
- int n_exclude;
- if (sq->nplacements < 2)
- continue; /* too simple to be worth trying */
- /*
- * Start by checking for chains this square can actually form
- * part of. We won't consider those. (The aim is to find a
- * completely _different_ square whose placements are all
- * ruled out by a chain.)
- */
- assert(sq->nplacements <= lenof(exclude));
- for (j = n_exclude = 0; j < sq->nplacements; j++)
- exclude[n_exclude++] = sc->pc_scratch[sq->placements[j]->index];
- for (j = 0; j < sq->nplacements; j++) {
- struct solver_domino *d = sq->placements[j]->domino;
- listout = listpos = 0;
- for (k = sc->dc_scratch[d->index];
- k < sc->pc && sc->placements[sc->pc_scratch2[k]].domino == d;
- k++) {
- int chain = sc->pc_scratch[sc->pc_scratch2[k]];
- bool keep;
- if (!sc->placements[sc->pc_scratch2[k]].active)
- continue;
- if (j == 0) {
- keep = true;
- } else {
- while (listpos < listsize &&
- sc->wh_scratch[listpos] < chain)
- listpos++;
- keep = (listpos < listsize &&
- sc->wh_scratch[listpos] == chain);
- }
- for (m = 0; m < n_exclude; m++)
- if (chain == exclude[m])
- keep = false;
- if (keep)
- sc->wh_scratch[listout++] = chain;
- }
- listsize = listout;
- if (listsize == 0)
- break; /* ruled out all chains; terminate loop early */
- }
- for (listpos = 0; listpos < listsize; listpos++) {
- int chain = sc->wh_scratch[listpos];
- /*
- * We've found a chain we can rule out.
- */
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("all choices for square %s would be ruled out "
- "by forced chain:", sq->name);
- for (pi = 0; pi < sc->pc; pi++)
- if (sc->pc_scratch[pi] == chain)
- printf(" %s", sc->placements[pi].name);
- printf("\n");
- }
- #endif
- for (pi = 0; pi < sc->pc; pi++)
- if (sc->pc_scratch[pi] == chain)
- rule_out_placement(sc, &sc->placements[pi]);
- done_something = true;
- }
- }
- /*
- * Another thing you can do with forcing chains, besides ruling
- * out a whole one at a time, is to look at each pair of chains
- * that overlap each other. Each such pair gives you two sets of
- * domino placements, such that if either set is not placed, then
- * the other one must be.
- *
- * This means that any domino which has a placement in _both_
- * chains of a pair must occupy one of those two placements, i.e.
- * we can rule that domino out anywhere else it might appear.
- */
- for (di = 0; di < sc->dc; di++) {
- struct solver_domino *d = &sc->dominoes[di];
- if (d->nplacements <= 2)
- continue; /* not enough placements to rule one out */
- for (j = 0; j+1 < d->nplacements; j++) {
- int ij = d->placements[j]->index;
- int cj = sc->pc_scratch[ij];
- for (k = j+1; k < d->nplacements; k++) {
- int ik = d->placements[k]->index;
- int ck = sc->pc_scratch[ik];
- if ((cj ^ ck) == 1) {
- /*
- * Placements j,k of domino d are in complementary
- * chains, so we can rule out all the others.
- */
- int i;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- printf("domino %s occurs in both complementary "
- "forced chains:", d->name);
- for (i = 0; i < sc->pc; i++)
- if (sc->pc_scratch[i] == cj)
- printf(" %s", sc->placements[i].name);
- printf(" and");
- for (i = 0; i < sc->pc; i++)
- if (sc->pc_scratch[i] == ck)
- printf(" %s", sc->placements[i].name);
- printf("\n");
- }
- #endif
- for (i = d->nplacements; i-- > 0 ;)
- if (i != j && i != k)
- rule_out_placement(sc, d->placements[i]);
- done_something = true;
- goto done_this_domino;
- }
- }
- }
- done_this_domino:;
- }
- return done_something;
- }
- /*
- * Run the solver until it can't make any more progress.
- *
- * Return value is:
- * 0 = no solution exists (puzzle clues are unsatisfiable)
- * 1 = unique solution found (success!)
- * 2 = multiple possibilities remain (puzzle is ambiguous or solver is not
- * smart enough)
- */
- static int run_solver(struct solver_scratch *sc, int max_diff_allowed)
- {
- int di, si, pi;
- bool done_something;
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- int di, j;
- printf("Initial possible placements:\n");
- for (di = 0; di < sc->dc; di++) {
- struct solver_domino *d = &sc->dominoes[di];
- printf(" %s:", d->name);
- for (j = 0; j < d->nplacements; j++)
- printf(" %s", d->placements[j]->name);
- printf("\n");
- }
- }
- #endif
- do {
- done_something = false;
- for (di = 0; di < sc->dc; di++)
- if (deduce_domino_single_placement(sc, di))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_TRIVIAL);
- continue;
- }
- for (si = 0; si < sc->wh; si++)
- if (deduce_square_single_placement(sc, si))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_TRIVIAL);
- continue;
- }
- if (max_diff_allowed <= DIFF_TRIVIAL)
- continue;
- for (si = 0; si < sc->wh; si++)
- if (deduce_square_single_domino(sc, si))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
- continue;
- }
- for (di = 0; di < sc->dc; di++)
- if (deduce_domino_must_overlap(sc, di))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
- continue;
- }
- for (pi = 0; pi < sc->pc; pi++)
- if (deduce_local_duplicate(sc, pi))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
- continue;
- }
- for (pi = 0; pi < sc->pc; pi++)
- if (deduce_local_duplicate_2(sc, pi))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
- continue;
- }
- if (deduce_parity(sc))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_BASIC);
- continue;
- }
- if (max_diff_allowed <= DIFF_BASIC)
- continue;
- if (deduce_set(sc, false))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_HARD);
- continue;
- }
- if (max_diff_allowed <= DIFF_HARD)
- continue;
- if (deduce_set(sc, true))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_EXTREME);
- continue;
- }
- if (deduce_forcing_chain(sc))
- done_something = true;
- if (done_something) {
- sc->max_diff_used = max(sc->max_diff_used, DIFF_EXTREME);
- continue;
- }
- } while (done_something);
- #ifdef SOLVER_DIAGNOSTICS
- if (solver_diagnostics) {
- int di, j;
- printf("Final possible placements:\n");
- for (di = 0; di < sc->dc; di++) {
- struct solver_domino *d = &sc->dominoes[di];
- printf(" %s:", d->name);
- for (j = 0; j < d->nplacements; j++)
- printf(" %s", d->placements[j]->name);
- printf("\n");
- }
- }
- #endif
- for (di = 0; di < sc->dc; di++)
- if (sc->dominoes[di].nplacements == 0)
- return 0;
- for (di = 0; di < sc->dc; di++)
- if (sc->dominoes[di].nplacements > 1)
- return 2;
- return 1;
- }
- /* ----------------------------------------------------------------------
- * Functions for generating a candidate puzzle (before we run the
- * solver to check it's soluble at the right difficulty level).
- */
- struct alloc_val;
- struct alloc_loc;
- struct alloc_scratch {
- /* Game parameters. */
- int n, w, h, wh, dc;
- /* The domino layout. Indexed by squares in the usual y*w+x raster
- * order: layout[i] gives the index of the other square in the
- * same domino as square i. */
- int *layout;
- /* The output array, containing a number in every square. */
- int *numbers;
- /* List of domino values (i.e. number pairs), indexed by DINDEX. */
- struct alloc_val *vals;
- /* List of domino locations, indexed arbitrarily. */
- struct alloc_loc *locs;
- /* Preallocated scratch spaces. */
- int *wh_scratch; /* size wh */
- int *wh2_scratch; /* size 2*wh */
- };
- struct alloc_val {
- int lo, hi;
- bool confounder;
- };
- struct alloc_loc {
- int sq[2];
- };
- static struct alloc_scratch *alloc_make_scratch(int n)
- {
- struct alloc_scratch *as = snew(struct alloc_scratch);
- int lo, hi;
- as->n = n;
- as->w = n+2;
- as->h = n+1;
- as->wh = as->w * as->h;
- as->dc = DCOUNT(n);
- as->layout = snewn(as->wh, int);
- as->numbers = snewn(as->wh, int);
- as->vals = snewn(as->dc, struct alloc_val);
- as->locs = snewn(as->dc, struct alloc_loc);
- as->wh_scratch = snewn(as->wh, int);
- as->wh2_scratch = snewn(as->wh * 2, int);
- for (hi = 0; hi <= n; hi++)
- for (lo = 0; lo <= hi; lo++) {
- struct alloc_val *v = &as->vals[DINDEX(hi, lo)];
- v->lo = lo;
- v->hi = hi;
- }
- return as;
- }
- static void alloc_free_scratch(struct alloc_scratch *as)
- {
- sfree(as->layout);
- sfree(as->numbers);
- sfree(as->vals);
- sfree(as->locs);
- sfree(as->wh_scratch);
- sfree(as->wh2_scratch);
- sfree(as);
- }
- static void alloc_make_layout(struct alloc_scratch *as, random_state *rs)
- {
- int i, pos;
- domino_layout_prealloc(as->w, as->h, rs,
- as->layout, as->wh_scratch, as->wh2_scratch);
- for (i = pos = 0; i < as->wh; i++) {
- if (as->layout[i] > i) {
- struct alloc_loc *loc;
- assert(pos < as->dc);
- loc = &as->locs[pos++];
- loc->sq[0] = i;
- loc->sq[1] = as->layout[i];
- }
- }
- assert(pos == as->dc);
- }
- static void alloc_trivial(struct alloc_scratch *as, random_state *rs)
- {
- int i;
- for (i = 0; i < as->dc; i++)
- as->wh_scratch[i] = i;
- shuffle(as->wh_scratch, as->dc, sizeof(*as->wh_scratch), rs);
- for (i = 0; i < as->dc; i++) {
- struct alloc_val *val = &as->vals[as->wh_scratch[i]];
- struct alloc_loc *loc = &as->locs[i];
- int which_lo = random_upto(rs, 2), which_hi = 1 - which_lo;
- as->numbers[loc->sq[which_lo]] = val->lo;
- as->numbers[loc->sq[which_hi]] = val->hi;
- }
- }
- /*
- * Given a domino location in the form of two square indices, compute
- * the square indices of the domino location that would lie on one
- * side of it. Returns false if the location would be outside the
- * grid, or if it isn't actually a domino in the layout.
- */
- static bool alloc_find_neighbour(
- struct alloc_scratch *as, int p0, int p1, int *n0, int *n1)
- {
- int x0 = p0 % as->w, y0 = p0 / as->w, x1 = p1 % as->w, y1 = p1 / as->w;
- int dy = y1-y0, dx = x1-x0;
- int nx0 = x0 + dy, ny0 = y0 - dx, nx1 = x1 + dy, ny1 = y1 - dx;
- int np0, np1;
- if (!(nx0 >= 0 && nx0 < as->w && ny0 >= 0 && ny0 < as->h &&
- nx1 >= 1 && nx1 < as->w && ny1 >= 1 && ny1 < as->h))
- return false; /* out of bounds */
- np0 = ny0 * as->w + nx0;
- np1 = ny1 * as->w + nx1;
- if (as->layout[np0] != np1)
- return false; /* not a domino */
- *n0 = np0;
- *n1 = np1;
- return true;
- }
- static bool alloc_try_unique(struct alloc_scratch *as, random_state *rs)
- {
- int i;
- for (i = 0; i < as->dc; i++)
- as->wh_scratch[i] = i;
- shuffle(as->wh_scratch, as->dc, sizeof(*as->wh_scratch), rs);
- for (i = 0; i < as->dc; i++)
- as->wh2_scratch[i] = i;
- shuffle(as->wh2_scratch, as->dc, sizeof(*as->wh2_scratch), rs);
- for (i = 0; i < as->wh; i++)
- as->numbers[i] = -1;
- for (i = 0; i < as->dc; i++) {
- struct alloc_val *val = &as->vals[as->wh_scratch[i]];
- struct alloc_loc *loc = &as->locs[as->wh2_scratch[i]];
- int which_lo, which_hi;
- bool can_lo_0 = true, can_lo_1 = true;
- int n0, n1;
- /*
- * This is basically the same strategy as alloc_trivial:
- * simply iterate through the locations and values in random
- * relative order and pair them up. But we make sure to avoid
- * the most common, and also simplest, cause of a non-unique
- * solution:two dominoes side by side, sharing a number at
- * opposite ends. Any section of that form automatically leads
- * to an alternative solution:
- *
- * +-------+ +---+---+
- * | 1 2 | | 1 | 2 |
- * +-------+ <-> | | |
- * | 2 3 | | 2 | 3 |
- * +-------+ +---+---+
- *
- * So as we place each domino, we check for a neighbouring
- * domino on each side, and if there is one, rule out any
- * placement of _this_ domino that places a number diagonally
- * opposite the same number in the neighbour.
- *
- * Sometimes this can fail completely, if a domino on each
- * side is already placed and between them they rule out all
- * placements of this one. But it happens rarely enough that
- * it's fine to just abort and try the layout again.
- */
- if (alloc_find_neighbour(as, loc->sq[0], loc->sq[1], &n0, &n1) &&
- (as->numbers[n0] == val->hi || as->numbers[n1] == val->lo))
- can_lo_0 = false;
- if (alloc_find_neighbour(as, loc->sq[1], loc->sq[0], &n0, &n1) &&
- (as->numbers[n0] == val->hi || as->numbers[n1] == val->lo))
- can_lo_1 = false;
- if (!can_lo_0 && !can_lo_1)
- return false; /* layout failed */
- else if (can_lo_0 && can_lo_1)
- which_lo = random_upto(rs, 2);
- else
- which_lo = can_lo_0 ? 0 : 1;
- which_hi = 1 - which_lo;
- as->numbers[loc->sq[which_lo]] = val->lo;
- as->numbers[loc->sq[which_hi]] = val->hi;
- }
- return true;
- }
- static bool alloc_try_hard(struct alloc_scratch *as, random_state *rs)
- {
- int i, x, y, hi, lo, vals, locs, confounders_needed;
- bool ok;
- for (i = 0; i < as->wh; i++)
- as->numbers[i] = -1;
- /*
- * Shuffle the location indices.
- */
- for (i = 0; i < as->dc; i++)
- as->wh2_scratch[i] = i;
- shuffle(as->wh2_scratch, as->dc, sizeof(*as->wh2_scratch), rs);
- /*
- * Start by randomly placing the double dominoes, to give a
- * starting instance of every number to try to put other things
- * next to.
- */
- for (i = 0; i <= as->n; i++)
- as->wh_scratch[i] = DINDEX(i, i);
- shuffle(as->wh_scratch, i, sizeof(*as->wh_scratch), rs);
- for (i = 0; i <= as->n; i++) {
- struct alloc_loc *loc = &as->locs[as->wh2_scratch[i]];
- as->numbers[loc->sq[0]] = as->numbers[loc->sq[1]] = i;
- }
- /*
- * Find all the dominoes that don't yet have a _wrong_ placement
- * somewhere in the grid.
- */
- for (i = 0; i < as->dc; i++)
- as->vals[i].confounder = false;
- for (y = 0; y < as->h; y++) {
- for (x = 0; x < as->w; x++) {
- int p = y * as->w + x;
- if (as->numbers[p] == -1)
- continue;
- if (x+1 < as->w) {
- int p1 = y * as->w + (x+1);
- if (as->layout[p] != p1 && as->numbers[p1] != -1)
- as->vals[DINDEX(as->numbers[p], as->numbers[p1])]
- .confounder = true;
- }
- if (y+1 < as->h) {
- int p1 = (y+1) * as->w + x;
- if (as->layout[p] != p1 && as->numbers[p1] != -1)
- as->vals[DINDEX(as->numbers[p], as->numbers[p1])]
- .confounder = true;
- }
- }
- }
- for (i = confounders_needed = 0; i < as->dc; i++)
- if (!as->vals[i].confounder)
- confounders_needed++;
- /*
- * Make a shuffled list of all the unplaced dominoes, and go
- * through it trying to find a placement for each one that also
- * fills in at least one of the needed confounders.
- */
- vals = 0;
- for (hi = 0; hi <= as->n; hi++)
- for (lo = 0; lo < hi; lo++)
- as->wh_scratch[vals++] = DINDEX(hi, lo);
- shuffle(as->wh_scratch, vals, sizeof(*as->wh_scratch), rs);
- locs = as->dc;
- while (vals > 0) {
- int valpos, valout, oldvals = vals;
- for (valpos = valout = 0; valpos < vals; valpos++) {
- int validx = as->wh_scratch[valpos];
- struct alloc_val *val = &as->vals[validx];
- struct alloc_loc *loc;
- int locpos, si, which_lo;
- for (locpos = 0; locpos < locs; locpos++) {
- int locidx = as->wh2_scratch[locpos];
- int wi, flip;
- loc = &as->locs[locidx];
- if (as->numbers[loc->sq[0]] != -1)
- continue; /* this location is already filled */
- flip = random_upto(rs, 2);
- /* Try this location both ways round. */
- for (wi = 0; wi < 2; wi++) {
- int n0, n1;
- which_lo = wi ^ flip;
- /* First, do the same check as in alloc_try_unique, to
- * avoid making an obviously insoluble puzzle. */
- if (alloc_find_neighbour(as, loc->sq[which_lo],
- loc->sq[1-which_lo], &n0, &n1) &&
- (as->numbers[n0] == val->hi ||
- as->numbers[n1] == val->lo))
- break; /* can't place it this way round */
- if (confounders_needed == 0)
- goto place_ok;
- /* Look to see if we're adding at least one
- * previously absent confounder. */
- for (si = 0; si < 2; si++) {
- int x = loc->sq[si] % as->w, y = loc->sq[si] / as->w;
- int n = (si == which_lo ? val->lo : val->hi);
- int d;
- for (d = 0; d < 4; d++) {
- int dx = d==0 ? +1 : d==2 ? -1 : 0;
- int dy = d==1 ? +1 : d==3 ? -1 : 0;
- int x1 = x+dx, y1 = y+dy, p1 = y1 * as->w + x1;
- if (x1 >= 0 && x1 < as->w &&
- y1 >= 0 && y1 < as->h &&
- as->numbers[p1] != -1 &&
- !(as->vals[DINDEX(n, as->numbers[p1])]
- .confounder)) {
- /*
- * Place this domino.
- */
- goto place_ok;
- }
- }
- }
- }
- }
- /* If we get here without executing 'goto place_ok', we
- * didn't find anywhere useful to put this domino. Put it
- * back on the list for the next pass. */
- as->wh_scratch[valout++] = validx;
- continue;
- place_ok:;
- /* We've found a domino to place. Place it, and fill in
- * all the confounders it adds. */
- as->numbers[loc->sq[which_lo]] = val->lo;
- as->numbers[loc->sq[1 - which_lo]] = val->hi;
- for (si = 0; si < 2; si++) {
- int p = loc->sq[si];
- int n = as->numbers[p];
- int x = p % as->w, y = p / as->w;
- int d;
- for (d = 0; d < 4; d++) {
- int dx = d==0 ? +1 : d==2 ? -1 : 0;
- int dy = d==1 ? +1 : d==3 ? -1 : 0;
- int x1 = x+dx, y1 = y+dy, p1 = y1 * as->w + x1;
- if (x1 >= 0 && x1 < as->w && y1 >= 0 && y1 < as->h &&
- p1 != loc->sq[1-si] && as->numbers[p1] != -1) {
- int di = DINDEX(n, as->numbers[p1]);
- if (!as->vals[di].confounder)
- confounders_needed--;
- as->vals[di].confounder = true;
- }
- }
- }
- }
- vals = valout;
- if (oldvals == vals)
- break;
- }
- ok = true;
- for (i = 0; i < as->dc; i++)
- if (!as->vals[i].confounder)
- ok = false;
- for (i = 0; i < as->wh; i++)
- if (as->numbers[i] == -1)
- ok = false;
- return ok;
- }
- static char *new_game_desc(const game_params *params, random_state *rs,
- char **aux, bool interactive)
- {
- int n = params->n, w = n+2, h = n+1, wh = w*h, diff = params->diff;
- struct solver_scratch *sc;
- struct alloc_scratch *as;
- int i, j, k, len;
- char *ret;
- #ifndef OMIT_DIFFICULTY_CAP
- /*
- * Cap the difficulty level for small puzzles which would
- * otherwise become impossible to generate.
- *
- * Under an #ifndef, to make it easy to remove this cap for the
- * purpose of re-testing what it ought to be.
- */
- if (diff != DIFF_AMBIGUOUS) {
- if (n == 1 && diff > DIFF_TRIVIAL)
- diff = DIFF_TRIVIAL;
- if (n == 2 && diff > DIFF_BASIC)
- diff = DIFF_BASIC;
- }
- #endif /* OMIT_DIFFICULTY_CAP */
- /*
- * Allocate space in which to lay the grid out.
- */
- sc = solver_make_scratch(n);
- as = alloc_make_scratch(n);
- /*
- * I haven't been able to think of any particularly clever
- * techniques for generating instances of Dominosa with a
- * unique solution. Many of the deductions used in this puzzle
- * are based on information involving half the grid at a time
- * (`of all the 6s, exactly one is next to a 3'), so a strategy
- * of partially solving the grid and then perturbing the place
- * where the solver got stuck seems particularly likely to
- * accidentally destroy the information which the solver had
- * used in getting that far. (Contrast with, say, Mines, in
- * which most deductions are local so this is an excellent
- * strategy.)
- *
- * Therefore I resort to the basest of brute force methods:
- * generate a random grid, see if it's solvable, throw it away
- * and try again if not. My only concession to sophistication
- * and cleverness is to at least _try_ not to generate obvious
- * 2x2 ambiguous sections (see comment below in the domino-
- * flipping section).
- *
- * During tests performed on 2005-07-15, I found that the brute
- * force approach without that tweak had to throw away about 87
- * grids on average (at the default n=6) before finding a
- * unique one, or a staggering 379 at n=9; good job the
- * generator and solver are fast! When I added the
- * ambiguous-section avoidance, those numbers came down to 19
- * and 26 respectively, which is a lot more sensible.
- */
- while (1) {
- alloc_make_layout(as, rs);
- if (diff == DIFF_AMBIGUOUS) {
- /* Just assign numbers to each domino completely at random. */
- alloc_trivial(as, rs);
- } else if (diff < DIFF_HARD) {
- /* Try to rule out the most common case of a non-unique solution */
- if (!alloc_try_unique(as, rs))
- continue;
- } else {
- /*
- * For Hard puzzles and above, we'd like there not to be
- * any easy toehold to start with.
- *
- * Mostly, that's arranged by alloc_try_hard, which will
- * ensure that no domino starts off with only one
- * potential placement. But a few other deductions
- * possible at Basic level can still sneak through the
- * cracks - for example, if the only two placements of one
- * domino overlap in a square, and you therefore rule out
- * some other domino that can use that square, you might
- * then find that _that_ domino now has only one
- * placement, and you've made a start.
- *
- * Of course, the main difficulty-level check will still
- * guarantee that you have to do a harder deduction
- * _somewhere_ in the grid. But it's more elegant if
- * there's nowhere obvious to get started at all.
- */
- int di;
- bool ok;
- if (!alloc_try_hard(as, rs))
- continue;
- solver_setup_grid(sc, as->numbers);
- if (run_solver(sc, DIFF_BASIC) < 2)
- continue;
- ok = true;
- for (di = 0; di < sc->dc; di++)
- if (sc->dominoes[di].nplacements <= 1) {
- ok = false;
- break;
- }
- if (!ok) {
- continue;
- }
- }
- if (diff != DIFF_AMBIGUOUS) {
- int solver_result;
- solver_setup_grid(sc, as->numbers);
- solver_result = run_solver(sc, diff);
- if (solver_result > 1)
- continue; /* puzzle couldn't be solved at this difficulty */
- if (sc->max_diff_used < diff)
- continue; /* puzzle _could_ be solved at easier difficulty */
- }
- break;
- }
- #ifdef GENERATION_DIAGNOSTICS
- for (j = 0; j < h; j++) {
- for (i = 0; i < w; i++) {
- putchar('0' + as->numbers[j*w+i]);
- }
- putchar('\n');
- }
- putchar('\n');
- #endif
- /*
- * Encode the resulting game state.
- *
- * Our encoding is a string of digits. Any number greater than
- * 9 is represented by a decimal integer within square
- * brackets. We know there are n+2 of every number (it's paired
- * with each number from 0 to n inclusive, and one of those is
- * itself so that adds another occurrence), so we can work out
- * the string length in advance.
- */
- /*
- * To work out the total length of the decimal encodings of all
- * the numbers from 0 to n inclusive:
- * - every number has a units digit; total is n+1.
- * - all numbers above 9 have a tens digit; total is max(n+1-10,0).
- * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0).
- * - and so on.
- */
- len = n+1;
- for (i = 10; i <= n; i *= 10)
- len += max(n + 1 - i, 0);
- /* Now add two square brackets for each number above 9. */
- len += 2 * max(n + 1 - 10, 0);
- /* And multiply by n+2 for the repeated occurrences of each number. */
- len *= n+2;
- /*
- * Now actually encode the string.
- */
- ret = snewn(len+1, char);
- j = 0;
- for (i = 0; i < wh; i++) {
- k = as->numbers[i];
- if (k < 10)
- ret[j++] = '0' + k;
- else
- j += sprintf(ret+j, "[%d]", k);
- assert(j <= len);
- }
- assert(j == len);
- ret[j] = '\0';
- /*
- * Encode the solved state as an aux_info.
- */
- {
- char *auxinfo = snewn(wh+1, char);
- for (i = 0; i < wh; i++) {
- int v = as->layout[i];
- auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' :
- v == i+w ? 'T' : v == i-w ? 'B' : '.');
- }
- auxinfo[wh] = '\0';
- *aux = auxinfo;
- }
- solver_free_scratch(sc);
- alloc_free_scratch(as);
- return ret;
- }
- static const char *validate_desc(const game_params *params, const char *desc)
- {
- int n = params->n, w = n+2, h = n+1, wh = w*h;
- int *occurrences;
- int i, j;
- const char *ret;
- ret = NULL;
- occurrences = snewn(n+1, int);
- for (i = 0; i <= n; i++)
- occurrences[i] = 0;
- for (i = 0; i < wh; i++) {
- if (!*desc) {
- ret = ret ? ret : "Game description is too short";
- } else {
- if (*desc >= '0' && *desc <= '9')
- j = *desc++ - '0';
- else if (*desc == '[') {
- desc++;
- j = atoi(desc);
- while (*desc && isdigit((unsigned char)*desc)) desc++;
- if (*desc != ']')
- ret = ret ? ret : "Missing ']' in game description";
- else
- desc++;
- } else {
- j = -1;
- ret = ret ? ret : "Invalid syntax in game description";
- }
- if (j < 0 || j > n)
- ret = ret ? ret : "Number out of range in game description";
- else
- occurrences[j]++;
- }
- }
- if (*desc)
- ret = ret ? ret : "Game description is too long";
- if (!ret) {
- for (i = 0; i <= n; i++)
- if (occurrences[i] != n+2)
- ret = "Incorrect number balance in game description";
- }
- sfree(occurrences);
- return ret;
- }
- static game_state *new_game(midend *me, const game_params *params,
- const char *desc)
- {
- int n = params->n, w = n+2, h = n+1, wh = w*h;
- game_state *state = snew(game_state);
- int i, j;
- state->params = *params;
- state->w = w;
- state->h = h;
- state->grid = snewn(wh, int);
- for (i = 0; i < wh; i++)
- state->grid[i] = i;
- state->edges = snewn(wh, unsigned short);
- for (i = 0; i < wh; i++)
- state->edges[i] = 0;
- state->numbers = snew(struct game_numbers);
- state->numbers->refcount = 1;
- state->numbers->numbers = snewn(wh, int);
- for (i = 0; i < wh; i++) {
- assert(*desc);
- if (*desc >= '0' && *desc <= '9')
- j = *desc++ - '0';
- else {
- assert(*desc == '[');
- desc++;
- j = atoi(desc);
- while (*desc && isdigit((unsigned char)*desc)) desc++;
- assert(*desc == ']');
- desc++;
- }
- assert(j >= 0 && j <= n);
- state->numbers->numbers[i] = j;
- }
- state->completed = false;
- state->cheated = false;
- return state;
- }
- static game_state *dup_game(const game_state *state)
- {
- int n = state->params.n, w = n+2, h = n+1, wh = w*h;
- game_state *ret = snew(game_state);
- ret->params = state->params;
- ret->w = state->w;
- ret->h = state->h;
- ret->grid = snewn(wh, int);
- memcpy(ret->grid, state->grid, wh * sizeof(int));
- ret->edges = snewn(wh, unsigned short);
- memcpy(ret->edges, state->edges, wh * sizeof(unsigned short));
- ret->numbers = state->numbers;
- ret->numbers->refcount++;
- ret->completed = state->completed;
- ret->cheated = state->cheated;
- return ret;
- }
- static void free_game(game_state *state)
- {
- sfree(state->grid);
- sfree(state->edges);
- if (--state->numbers->refcount <= 0) {
- sfree(state->numbers->numbers);
- sfree(state->numbers);
- }
- sfree(state);
- }
- static char *solution_move_string(struct solver_scratch *sc)
- {
- char *ret;
- int retlen, retsize;
- int i, pass;
- /*
- * First make a pass putting in edges for -1, then make a pass
- * putting in dominoes for +1.
- */
- retsize = 256;
- ret = snewn(retsize, char);
- retlen = sprintf(ret, "S");
- for (pass = 0; pass < 2; pass++) {
- char type = "ED"[pass];
- for (i = 0; i < sc->pc; i++) {
- struct solver_placement *p = &sc->placements[i];
- char buf[80];
- int extra;
- if (pass == 0) {
- /* Emit a barrier if this placement is ruled out for
- * the domino. */
- if (p->active)
- continue;
- } else {
- /* Emit a domino if this placement is the only one not
- * ruled out. */
- if (!p->active || p->domino->nplacements > 1)
- continue;
- }
- extra = sprintf(buf, ";%c%d,%d", type,
- p->squares[0]->index, p->squares[1]->index);
- if (retlen + extra + 1 >= retsize) {
- retsize = retlen + extra + 256;
- ret = sresize(ret, retsize, char);
- }
- strcpy(ret + retlen, buf);
- retlen += extra;
- }
- }
- return ret;
- }
- static char *solve_game(const game_state *state, const game_state *currstate,
- const char *aux, const char **error)
- {
- int n = state->params.n, w = n+2, h = n+1, wh = w*h;
- char *ret;
- int retlen, retsize;
- int i;
- char buf[80];
- int extra;
- if (aux) {
- retsize = 256;
- ret = snewn(retsize, char);
- retlen = sprintf(ret, "S");
- for (i = 0; i < wh; i++) {
- if (aux[i] == 'L')
- extra = sprintf(buf, ";D%d,%d", i, i+1);
- else if (aux[i] == 'T')
- extra = sprintf(buf, ";D%d,%d", i, i+w);
- else
- continue;
- if (retlen + extra + 1 >= retsize) {
- retsize = retlen + extra + 256;
- ret = sresize(ret, retsize, char);
- }
- strcpy(ret + retlen, buf);
- retlen += extra;
- }
- } else {
- struct solver_scratch *sc = solver_make_scratch(n);
- solver_setup_grid(sc, state->numbers->numbers);
- run_solver(sc, DIFFCOUNT);
- ret = solution_move_string(sc);
- solver_free_scratch(sc);
- }
- return ret;
- }
- static bool game_can_format_as_text_now(const game_params *params)
- {
- return params->n < 1000;
- }
- static void draw_domino(char *board, int start, char corner,
- int dshort, int nshort, char cshort,
- int dlong, int nlong, char clong)
- {
- int go_short = nshort*dshort, go_long = nlong*dlong, i;
- board[start] = corner;
- board[start + go_short] = corner;
- board[start + go_long] = corner;
- board[start + go_short + go_long] = corner;
- for (i = 1; i < nshort; ++i) {
- int j = start + i*dshort, k = start + i*dshort + go_long;
- if (board[j] != corner) board[j] = cshort;
- if (board[k] != corner) board[k] = cshort;
- }
- for (i = 1; i < nlong; ++i) {
- int j = start + i*dlong, k = start + i*dlong + go_short;
- if (board[j] != corner) board[j] = clong;
- if (board[k] != corner) board[k] = clong;
- }
- }
- static char *game_text_format(const game_state *state)
- {
- int w = state->w, h = state->h, r, c;
- int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh;
- char *board = snewn(len + 1, char);
- memset(board, ' ', len);
- for (r = 0; r < h; ++r) {
- for (c = 0; c < w; ++c) {
- int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2;
- int i = r*w + c, num = state->numbers->numbers[i];
- if (num < 100) {
- board[center] = '0' + num % 10;
- if (num >= 10) board[center - 1] = '0' + num / 10;
- } else {
- board[center+1] = '0' + num % 10;
- board[center] = '0' + num / 10 % 10;
- board[center-1] = '0' + num / 100;
- }
- if (state->edges[i] & EDGE_L) board[center - cw/2] = '|';
- if (state->edges[i] & EDGE_R) board[center + cw/2] = '|';
- if (state->edges[i] & EDGE_T) board[center - gw] = '-';
- if (state->edges[i] & EDGE_B) board[center + gw] = '-';
- if (state->grid[i] == i) continue; /* no domino pairing */
- if (state->grid[i] < i) continue; /* already done */
- assert (state->grid[i] == i + 1 || state->grid[i] == i + w);
- if (state->grid[i] == i + 1)
- draw_domino(board, cell, '+', gw, ch, '|', +1, 2*cw, '-');
- else if (state->grid[i] == i + w)
- draw_domino(board, cell, '+', +1, cw, '-', gw, 2*ch, '|');
- }
- board[r*ch*gw + gw - 1] = '\n';
- board[r*ch*gw + gw + gw - 1] = '\n';
- }
- board[len - 1] = '\n';
- board[len] = '\0';
- return board;
- }
- struct game_ui {
- int cur_x, cur_y, highlight_1, highlight_2;
- bool cur_visible;
- };
- static game_ui *new_ui(const game_state *state)
- {
- game_ui *ui = snew(game_ui);
- ui->cur_x = ui->cur_y = 0;
- ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
- ui->highlight_1 = ui->highlight_2 = -1;
- return ui;
- }
- static void free_ui(game_ui *ui)
- {
- sfree(ui);
- }
- static void game_changed_state(game_ui *ui, const game_state *oldstate,
- const game_state *newstate)
- {
- if (!oldstate->completed && newstate->completed)
- ui->cur_visible = false;
- }
- static const char *current_key_label(const game_ui *ui,
- const game_state *state, int button)
- {
- if (IS_CURSOR_SELECT(button)) {
- int d1, d2, w = state->w;
- if (!((ui->cur_x ^ ui->cur_y) & 1))
- return ""; /* must have exactly one dimension odd */
- d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
- d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
- /* We can't mark an edge next to any domino. */
- if (button == CURSOR_SELECT2 &&
- (state->grid[d1] != d1 || state->grid[d2] != d2))
- return "";
- if (button == CURSOR_SELECT) {
- if (state->grid[d1] == d2) return "Remove";
- return "Place";
- } else {
- int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B;
- if (state->edges[d1] & edge) return "Remove";
- return "Line";
- }
- }
- return "";
- }
- #define PREFERRED_TILESIZE 32
- #define TILESIZE (ds->tilesize)
- #define BORDER (TILESIZE * 3 / 4)
- #define DOMINO_GUTTER (TILESIZE / 16)
- #define DOMINO_RADIUS (TILESIZE / 8)
- #define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS)
- #define CURSOR_RADIUS (TILESIZE / 4)
- #define COORD(x) ( (x) * TILESIZE + BORDER )
- #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
- struct game_drawstate {
- int w, h, tilesize;
- unsigned long *visible;
- };
- static char *interpret_move(const game_state *state, game_ui *ui,
- const game_drawstate *ds,
- int x, int y, int button)
- {
- int w = state->w, h = state->h;
- char buf[80];
- /*
- * A left-click between two numbers toggles a domino covering
- * them. A right-click toggles an edge.
- */
- if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
- int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx;
- int dx, dy;
- int d1, d2;
- if (tx < 0 || tx >= w || ty < 0 || ty >= h)
- return MOVE_UNUSED;
- /*
- * Now we know which square the click was in, decide which
- * edge of the square it was closest to.
- */
- dx = 2 * (x - COORD(tx)) - TILESIZE;
- dy = 2 * (y - COORD(ty)) - TILESIZE;
- if (abs(dx) > abs(dy) && dx < 0 && tx > 0)
- d1 = t - 1, d2 = t; /* clicked in right side of domino */
- else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w)
- d1 = t, d2 = t + 1; /* clicked in left side of domino */
- else if (abs(dy) > abs(dx) && dy < 0 && ty > 0)
- d1 = t - w, d2 = t; /* clicked in bottom half of domino */
- else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h)
- d1 = t, d2 = t + w; /* clicked in top half of domino */
- else
- return MOVE_NO_EFFECT; /* clicked precisely on a diagonal */
- /*
- * We can't mark an edge next to any domino.
- */
- if (button == RIGHT_BUTTON &&
- (state->grid[d1] != d1 || state->grid[d2] != d2))
- return MOVE_NO_EFFECT;
- ui->cur_visible = false;
- sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2);
- return dupstr(buf);
- } else if (IS_CURSOR_MOVE(button)) {
- ui->cur_visible = true;
- move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false);
- return MOVE_UI_UPDATE;
- } else if (IS_CURSOR_SELECT(button)) {
- int d1, d2;
- if (!((ui->cur_x ^ ui->cur_y) & 1))
- return MOVE_NO_EFFECT; /* must have exactly one dimension odd */
- d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
- d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
- /*
- * We can't mark an edge next to any domino.
- */
- if (button == CURSOR_SELECT2 &&
- (state->grid[d1] != d1 || state->grid[d2] != d2))
- return MOVE_NO_EFFECT;
- sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2);
- return dupstr(buf);
- } else if (isdigit(button)) {
- int n = state->params.n, num = button - '0';
- if (num > n) {
- return MOVE_UNUSED;
- } else if (ui->highlight_1 == num) {
- ui->highlight_1 = -1;
- } else if (ui->highlight_2 == num) {
- ui->highlight_2 = -1;
- } else if (ui->highlight_1 == -1) {
- ui->highlight_1 = num;
- } else if (ui->highlight_2 == -1) {
- ui->highlight_2 = num;
- } else {
- return MOVE_NO_EFFECT;
- }
- return MOVE_UI_UPDATE;
- }
- return MOVE_UNUSED;
- }
- static game_state *execute_move(const game_state *state, const char *move)
- {
- int n = state->params.n, w = n+2, h = n+1, wh = w*h;
- int d1, d2, d3, p;
- game_state *ret = dup_game(state);
- while (*move) {
- if (move[0] == 'S') {
- int i;
- ret->cheated = true;
- /*
- * Clear the existing edges and domino placements. We
- * expect the S to be followed by other commands.
- */
- for (i = 0; i < wh; i++) {
- ret->grid[i] = i;
- ret->edges[i] = 0;
- }
- move++;
- } else if (move[0] == 'D' &&
- sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
- d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
- (d2 - d1 == 1 || d2 - d1 == w)) {
- /*
- * Toggle domino presence between d1 and d2.
- */
- if (ret->grid[d1] == d2) {
- assert(ret->grid[d2] == d1);
- ret->grid[d1] = d1;
- ret->grid[d2] = d2;
- } else {
- /*
- * Erase any dominoes that might overlap the new one.
- */
- d3 = ret->grid[d1];
- if (d3 != d1)
- ret->grid[d3] = d3;
- d3 = ret->grid[d2];
- if (d3 != d2)
- ret->grid[d3] = d3;
- /*
- * Place the new one.
- */
- ret->grid[d1] = d2;
- ret->grid[d2] = d1;
- /*
- * Destroy any edges lurking around it.
- */
- if (ret->edges[d1] & EDGE_L) {
- assert(d1 - 1 >= 0);
- ret->edges[d1 - 1] &= ~EDGE_R;
- }
- if (ret->edges[d1] & EDGE_R) {
- assert(d1 + 1 < wh);
- ret->edges[d1 + 1] &= ~EDGE_L;
- }
- if (ret->edges[d1] & EDGE_T) {
- assert(d1 - w >= 0);
- ret->edges[d1 - w] &= ~EDGE_B;
- }
- if (ret->edges[d1] & EDGE_B) {
- assert(d1 + 1 < wh);
- ret->edges[d1 + w] &= ~EDGE_T;
- }
- ret->edges[d1] = 0;
- if (ret->edges[d2] & EDGE_L) {
- assert(d2 - 1 >= 0);
- ret->edges[d2 - 1] &= ~EDGE_R;
- }
- if (ret->edges[d2] & EDGE_R) {
- assert(d2 + 1 < wh);
- ret->edges[d2 + 1] &= ~EDGE_L;
- }
- if (ret->edges[d2] & EDGE_T) {
- assert(d2 - w >= 0);
- ret->edges[d2 - w] &= ~EDGE_B;
- }
- if (ret->edges[d2] & EDGE_B) {
- assert(d2 + 1 < wh);
- ret->edges[d2 + w] &= ~EDGE_T;
- }
- ret->edges[d2] = 0;
- }
- move += p+1;
- } else if (move[0] == 'E' &&
- sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
- d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
- ret->grid[d1] == d1 && ret->grid[d2] == d2 &&
- (d2 - d1 == 1 || d2 - d1 == w)) {
- /*
- * Toggle edge presence between d1 and d2.
- */
- if (d2 == d1 + 1) {
- ret->edges[d1] ^= EDGE_R;
- ret->edges[d2] ^= EDGE_L;
- } else {
- ret->edges[d1] ^= EDGE_B;
- ret->edges[d2] ^= EDGE_T;
- }
- move += p+1;
- } else {
- free_game(ret);
- return NULL;
- }
- if (*move) {
- if (*move != ';') {
- free_game(ret);
- return NULL;
- }
- move++;
- }
- }
- /*
- * After modifying the grid, check completion.
- */
- if (!ret->completed) {
- int i, ok = 0;
- bool *used = snewn(TRI(n+1), bool);
- memset(used, 0, TRI(n+1));
- for (i = 0; i < wh; i++)
- if (ret->grid[i] > i) {
- int n1, n2, di;
- n1 = ret->numbers->numbers[i];
- n2 = ret->numbers->numbers[ret->grid[i]];
- di = DINDEX(n1, n2);
- assert(di >= 0 && di < TRI(n+1));
- if (!used[di]) {
- used[di] = true;
- ok++;
- }
- }
- sfree(used);
- if (ok == DCOUNT(n))
- ret->completed = true;
- }
- return ret;
- }
- /* ----------------------------------------------------------------------
- * Drawing routines.
- */
- static void game_compute_size(const game_params *params, int tilesize,
- const game_ui *ui, int *x, int *y)
- {
- int n = params->n, w = n+2, h = n+1;
- /* Ick: fake up `ds->tilesize' for macro expansion purposes */
- struct { int tilesize; } ads, *ds = &ads;
- ads.tilesize = tilesize;
- *x = w * TILESIZE + 2*BORDER;
- *y = h * TILESIZE + 2*BORDER;
- }
- 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_TEXT * 3 + 0] = 0.0F;
- ret[COL_TEXT * 3 + 1] = 0.0F;
- ret[COL_TEXT * 3 + 2] = 0.0F;
- ret[COL_DOMINO * 3 + 0] = 0.0F;
- ret[COL_DOMINO * 3 + 1] = 0.0F;
- ret[COL_DOMINO * 3 + 2] = 0.0F;
- ret[COL_DOMINOCLASH * 3 + 0] = 0.5F;
- ret[COL_DOMINOCLASH * 3 + 1] = 0.0F;
- ret[COL_DOMINOCLASH * 3 + 2] = 0.0F;
- ret[COL_DOMINOTEXT * 3 + 0] = 1.0F;
- ret[COL_DOMINOTEXT * 3 + 1] = 1.0F;
- ret[COL_DOMINOTEXT * 3 + 2] = 1.0F;
- ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3;
- ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3;
- ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 3;
- ret[COL_HIGHLIGHT_1 * 3 + 0] = 0.85;
- ret[COL_HIGHLIGHT_1 * 3 + 1] = 0.20;
- ret[COL_HIGHLIGHT_1 * 3 + 2] = 0.20;
- ret[COL_HIGHLIGHT_2 * 3 + 0] = 0.30;
- ret[COL_HIGHLIGHT_2 * 3 + 1] = 0.85;
- ret[COL_HIGHLIGHT_2 * 3 + 2] = 0.20;
- *ncolours = NCOLOURS;
- return ret;
- }
- static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
- {
- struct game_drawstate *ds = snew(struct game_drawstate);
- int i;
- ds->w = state->w;
- ds->h = state->h;
- ds->visible = snewn(ds->w * ds->h, unsigned long);
- ds->tilesize = 0; /* not decided yet */
- for (i = 0; i < ds->w * ds->h; i++)
- ds->visible[i] = 0xFFFF;
- return ds;
- }
- static void game_free_drawstate(drawing *dr, game_drawstate *ds)
- {
- sfree(ds->visible);
- sfree(ds);
- }
- enum {
- TYPE_L,
- TYPE_R,
- TYPE_T,
- TYPE_B,
- TYPE_BLANK,
- TYPE_MASK = 0x0F
- };
- /* These flags must be disjoint with:
- * the above enum (TYPE_*) [0x000 -- 0x00F]
- * EDGE_* [0x100 -- 0xF00]
- * and must fit into an unsigned long (32 bits).
- */
- #define DF_HIGHLIGHT_1 0x10
- #define DF_HIGHLIGHT_2 0x20
- #define DF_FLASH 0x40
- #define DF_CLASH 0x80
- #define DF_CURSOR 0x01000
- #define DF_CURSOR_USEFUL 0x02000
- #define DF_CURSOR_XBASE 0x10000
- #define DF_CURSOR_XMASK 0x30000
- #define DF_CURSOR_YBASE 0x40000
- #define DF_CURSOR_YMASK 0xC0000
- #define CEDGE_OFF (TILESIZE / 8)
- #define IS_EMPTY(s,x,y) ((s)->grid[(y)*(s)->w+(x)] == ((y)*(s)->w+(x)))
- static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
- int x, int y, int type, int highlight_1, int highlight_2)
- {
- int w = state->w /*, h = state->h */;
- int cx = COORD(x), cy = COORD(y);
- int nc;
- char str[80];
- int flags;
- clip(dr, cx, cy, TILESIZE, TILESIZE);
- draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND);
- flags = type &~ TYPE_MASK;
- type &= TYPE_MASK;
- if (type != TYPE_BLANK) {
- int i, bg;
- /*
- * Draw one end of a domino. This is composed of:
- *
- * - two filled circles (rounded corners)
- * - two rectangles
- * - a slight shift in the number
- */
- if (flags & DF_CLASH)
- bg = COL_DOMINOCLASH;
- else
- bg = COL_DOMINO;
- nc = COL_DOMINOTEXT;
- if (flags & DF_FLASH) {
- int tmp = nc;
- nc = bg;
- bg = tmp;
- }
- if (type == TYPE_L || type == TYPE_T)
- draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET,
- DOMINO_RADIUS, bg, bg);
- if (type == TYPE_R || type == TYPE_T)
- draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET,
- DOMINO_RADIUS, bg, bg);
- if (type == TYPE_L || type == TYPE_B)
- draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET,
- DOMINO_RADIUS, bg, bg);
- if (type == TYPE_R || type == TYPE_B)
- draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET,
- cy+TILESIZE-1-DOMINO_COFFSET,
- DOMINO_RADIUS, bg, bg);
- for (i = 0; i < 2; i++) {
- int x1, y1, x2, y2;
- x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET);
- y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER);
- x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET);
- y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER);
- if (type == TYPE_L)
- x2 = cx + TILESIZE + TILESIZE/16;
- else if (type == TYPE_R)
- x1 = cx - TILESIZE/16;
- else if (type == TYPE_T)
- y2 = cy + TILESIZE + TILESIZE/16;
- else if (type == TYPE_B)
- y1 = cy - TILESIZE/16;
- draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg);
- }
- } else {
- if (flags & EDGE_T)
- draw_rect(dr, cx+DOMINO_GUTTER, cy,
- TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
- if (flags & EDGE_B)
- draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1,
- TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
- if (flags & EDGE_L)
- draw_rect(dr, cx, cy+DOMINO_GUTTER,
- 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
- if (flags & EDGE_R)
- draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER,
- 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
- nc = COL_TEXT;
- }
- if (flags & DF_CURSOR) {
- int curx = ((flags & DF_CURSOR_XMASK) / DF_CURSOR_XBASE) & 3;
- int cury = ((flags & DF_CURSOR_YMASK) / DF_CURSOR_YBASE) & 3;
- int ox = cx + curx*TILESIZE/2;
- int oy = cy + cury*TILESIZE/2;
- draw_rect_corners(dr, ox, oy, CURSOR_RADIUS, nc);
- if (flags & DF_CURSOR_USEFUL)
- draw_rect_corners(dr, ox, oy, CURSOR_RADIUS+1, nc);
- }
- if (flags & DF_HIGHLIGHT_1) {
- nc = COL_HIGHLIGHT_1;
- } else if (flags & DF_HIGHLIGHT_2) {
- nc = COL_HIGHLIGHT_2;
- }
- sprintf(str, "%d", state->numbers->numbers[y*w+x]);
- draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
- ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str);
- draw_update(dr, cx, cy, TILESIZE, TILESIZE);
- unclip(dr);
- }
- 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 n = state->params.n, w = state->w, h = state->h, wh = w*h;
- int x, y, i;
- unsigned char *used;
- /*
- * See how many dominoes of each type there are, so we can
- * highlight clashes in red.
- */
- used = snewn(TRI(n+1), unsigned char);
- memset(used, 0, TRI(n+1));
- for (i = 0; i < wh; i++)
- if (state->grid[i] > i) {
- int n1, n2, di;
- n1 = state->numbers->numbers[i];
- n2 = state->numbers->numbers[state->grid[i]];
- di = DINDEX(n1, n2);
- assert(di >= 0 && di < TRI(n+1));
- if (used[di] < 2)
- used[di]++;
- }
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- int n = y*w+x;
- int n1, n2, di;
- unsigned long c;
- if (state->grid[n] == n-1)
- c = TYPE_R;
- else if (state->grid[n] == n+1)
- c = TYPE_L;
- else if (state->grid[n] == n-w)
- c = TYPE_B;
- else if (state->grid[n] == n+w)
- c = TYPE_T;
- else
- c = TYPE_BLANK;
- n1 = state->numbers->numbers[n];
- if (c != TYPE_BLANK) {
- n2 = state->numbers->numbers[state->grid[n]];
- di = DINDEX(n1, n2);
- if (used[di] > 1)
- c |= DF_CLASH; /* highlight a clash */
- } else {
- c |= state->edges[n];
- }
- if (n1 == ui->highlight_1)
- c |= DF_HIGHLIGHT_1;
- if (n1 == ui->highlight_2)
- c |= DF_HIGHLIGHT_2;
- if (flashtime != 0)
- c |= DF_FLASH; /* we're flashing */
- if (ui->cur_visible) {
- unsigned curx = (unsigned)(ui->cur_x - (2*x-1));
- unsigned cury = (unsigned)(ui->cur_y - (2*y-1));
- if (curx < 3 && cury < 3) {
- c |= (DF_CURSOR |
- (curx * DF_CURSOR_XBASE) |
- (cury * DF_CURSOR_YBASE));
- if ((ui->cur_x ^ ui->cur_y) & 1)
- c |= DF_CURSOR_USEFUL;
- }
- }
- if (ds->visible[n] != c) {
- draw_tile(dr, ds, state, x, y, c,
- ui->highlight_1, ui->highlight_2);
- ds->visible[n] = c;
- }
- }
- sfree(used);
- }
- 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)
- {
- ui->highlight_1 = ui->highlight_2 = -1;
- 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->cur_visible)
- {
- *x = BORDER + ((2 * ui->cur_x + 1) * TILESIZE) / 4;
- *y = BORDER + ((2 * ui->cur_y + 1) * TILESIZE) / 4;
- *w = *h = TILESIZE / 2 + 2;
- }
- }
- 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;
- /*
- * I'll use 6mm squares by default.
- */
- game_compute_size(params, 600, 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->w, h = state->h;
- int c, x, y;
- /* Ick: fake up `ds->tilesize' for macro expansion purposes */
- game_drawstate ads, *ds = &ads;
- game_set_size(dr, ds, NULL, tilesize);
- c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
- c = print_mono_colour(dr, 0); assert(c == COL_TEXT);
- c = print_mono_colour(dr, 0); assert(c == COL_DOMINO);
- c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH);
- c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT);
- c = print_mono_colour(dr, 0); assert(c == COL_EDGE);
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- int n = y*w+x;
- unsigned long c;
- if (state->grid[n] == n-1)
- c = TYPE_R;
- else if (state->grid[n] == n+1)
- c = TYPE_L;
- else if (state->grid[n] == n-w)
- c = TYPE_B;
- else if (state->grid[n] == n+w)
- c = TYPE_T;
- else
- c = TYPE_BLANK;
- draw_tile(dr, ds, state, x, y, c, -1, -1);
- }
- }
- #ifdef COMBINED
- #define thegame dominosa
- #endif
- const struct game thegame = {
- "Dominosa", "games.dominosa", "dominosa",
- 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,
- NULL, NULL, /* get_prefs, set_prefs */
- new_ui,
- free_ui,
- NULL, /* encode_ui */
- NULL, /* decode_ui */
- NULL, /* 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 */
- 0, /* flags */
- };
- #ifdef STANDALONE_SOLVER
- int main(int argc, char **argv)
- {
- game_params *p;
- game_state *s, *s2;
- char *id = NULL, *desc;
- int maxdiff = DIFFCOUNT;
- const char *err;
- bool grade = false, diagnostics = false;
- struct solver_scratch *sc;
- int retd;
- while (--argc > 0) {
- char *p = *++argv;
- if (!strcmp(p, "-v")) {
- diagnostics = true;
- } else if (!strcmp(p, "-g")) {
- grade = true;
- } else if (!strncmp(p, "-d", 2) && p[2] && !p[3]) {
- int i;
- bool bad = true;
- for (i = 0; i < lenof(dominosa_diffchars); i++)
- if (dominosa_diffchars[i] != DIFF_AMBIGUOUS &&
- dominosa_diffchars[i] == p[2]) {
- bad = false;
- maxdiff = i;
- break;
- }
- if (bad) {
- fprintf(stderr, "%s: unrecognised difficulty `%c'\n",
- argv[0], p[2]);
- return 1;
- }
- } else if (*p == '-') {
- fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
- return 1;
- } else {
- id = p;
- }
- }
- if (!id) {
- fprintf(stderr, "usage: %s [-v | -g] <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);
- solver_diagnostics = diagnostics;
- sc = solver_make_scratch(p->n);
- solver_setup_grid(sc, s->numbers->numbers);
- retd = run_solver(sc, maxdiff);
- if (retd == 0) {
- printf("Puzzle is inconsistent\n");
- } else if (grade) {
- printf("Difficulty rating: %s\n",
- dominosa_diffnames[sc->max_diff_used]);
- } else {
- char *move, *text;
- move = solution_move_string(sc);
- s2 = execute_move(s, move);
- text = game_text_format(s2);
- sfree(move);
- fputs(text, stdout);
- sfree(text);
- free_game(s2);
- if (retd > 1)
- printf("Could not deduce a unique solution\n");
- }
- solver_free_scratch(sc);
- free_game(s);
- free_params(p);
- return 0;
- }
- #endif
- /* vim: set shiftwidth=4 :set textwidth=80: */
|