12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293 |
- /*
- * mines.c: Minesweeper clone with sophisticated grid generation.
- *
- * Still TODO:
- *
- * - think about configurably supporting question marks.
- */
- #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 "tree234.h"
- #include "puzzles.h"
- enum {
- COL_BACKGROUND, COL_BACKGROUND2,
- COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8,
- COL_MINE, COL_BANG, COL_CROSS, COL_FLAG, COL_FLAGBASE, COL_QUERY,
- COL_HIGHLIGHT, COL_LOWLIGHT,
- COL_WRONGNUMBER,
- COL_CURSOR,
- NCOLOURS
- };
- #define PREFERRED_TILE_SIZE 20
- #define TILE_SIZE (ds->tilesize)
- #ifdef SMALL_SCREEN
- #define BORDER 8
- #else
- #define BORDER (TILE_SIZE * 3 / 2)
- #endif
- #define HIGHLIGHT_WIDTH (TILE_SIZE / 10 ? TILE_SIZE / 10 : 1)
- #define OUTER_HIGHLIGHT_WIDTH (BORDER / 10 ? BORDER / 10 : 1)
- #define COORD(x) ( (x) * TILE_SIZE + BORDER )
- #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
- #define FLASH_FRAME 0.13F
- struct game_params {
- int w, h, n;
- bool unique;
- };
- struct mine_layout {
- /*
- * This structure is shared between all the game_states for a
- * given instance of the puzzle, so we reference-count it.
- */
- int refcount;
- bool *mines;
- /*
- * If we haven't yet actually generated the mine layout, here's
- * all the data we will need to do so.
- */
- int n;
- bool unique;
- random_state *rs;
- midend *me; /* to give back the new game desc */
- };
- struct game_state {
- int w, h, n;
- bool dead, won, used_solve;
- struct mine_layout *layout; /* real mine positions */
- signed char *grid; /* player knowledge */
- /*
- * Each item in the `grid' array is one of the following values:
- *
- * - 0 to 8 mean the square is open and has a surrounding mine
- * count.
- *
- * - -1 means the square is marked as a mine.
- *
- * - -2 means the square is unknown.
- *
- * - -3 means the square is marked with a question mark
- * (FIXME: do we even want to bother with this?).
- *
- * - 64 means the square has had a mine revealed when the game
- * was lost.
- *
- * - 65 means the square had a mine revealed and this was the
- * one the player hits.
- *
- * - 66 means the square has a crossed-out mine because the
- * player had incorrectly marked it.
- */
- };
- static game_params *default_params(void)
- {
- game_params *ret = snew(game_params);
- ret->w = ret->h = 9;
- ret->n = 10;
- ret->unique = true;
- return ret;
- }
- static const struct game_params mines_presets[] = {
- {9, 9, 10, true},
- {9, 9, 35, true},
- {16, 16, 40, true},
- {16, 16, 99, true},
- #ifndef SMALL_SCREEN
- {30, 16, 99, true},
- {30, 16, 170, true},
- #endif
- };
- static bool game_fetch_preset(int i, char **name, game_params **params)
- {
- game_params *ret;
- char str[80];
- if (i < 0 || i >= lenof(mines_presets))
- return false;
- ret = snew(game_params);
- *ret = mines_presets[i];
- sprintf(str, "%dx%d, %d mines", ret->w, ret->h, ret->n);
- *name = dupstr(str);
- *params = ret;
- return true;
- }
- static void free_params(game_params *params)
- {
- sfree(params);
- }
- static game_params *dup_params(const game_params *params)
- {
- game_params *ret = snew(game_params);
- *ret = *params; /* structure copy */
- return ret;
- }
- static void decode_params(game_params *params, char const *string)
- {
- char const *p = string;
- params->w = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- if (*p == 'x') {
- p++;
- params->h = atoi(p);
- while (*p && isdigit((unsigned char)*p)) p++;
- } else {
- params->h = params->w;
- }
- if (*p == 'n') {
- p++;
- params->n = atoi(p);
- while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
- } else {
- if (params->h > 0 && params->w > 0 &&
- params->w <= INT_MAX / params->h)
- params->n = params->w * params->h / 10;
- }
- while (*p) {
- if (*p == 'a') {
- p++;
- params->unique = false;
- } else
- p++; /* skip any other gunk */
- }
- }
- static char *encode_params(const game_params *params, bool full)
- {
- char ret[400];
- int len;
- len = sprintf(ret, "%dx%d", params->w, params->h);
- /*
- * Mine count is a generation-time parameter, since it can be
- * deduced from the mine bitmap!
- */
- if (full)
- len += sprintf(ret+len, "n%d", params->n);
- if (full && !params->unique)
- ret[len++] = 'a';
- assert(len < lenof(ret));
- ret[len] = '\0';
- return dupstr(ret);
- }
- static config_item *game_configure(const game_params *params)
- {
- config_item *ret;
- char buf[80];
- ret = snewn(5, config_item);
- ret[0].name = "Width";
- ret[0].type = C_STRING;
- sprintf(buf, "%d", params->w);
- ret[0].u.string.sval = dupstr(buf);
- ret[1].name = "Height";
- ret[1].type = C_STRING;
- sprintf(buf, "%d", params->h);
- ret[1].u.string.sval = dupstr(buf);
- ret[2].name = "Mines";
- ret[2].type = C_STRING;
- sprintf(buf, "%d", params->n);
- ret[2].u.string.sval = dupstr(buf);
- ret[3].name = "Ensure solubility";
- ret[3].type = C_BOOLEAN;
- ret[3].u.boolean.bval = params->unique;
- ret[4].name = NULL;
- ret[4].type = C_END;
- return ret;
- }
- static game_params *custom_params(const config_item *cfg)
- {
- game_params *ret = snew(game_params);
- ret->w = atoi(cfg[0].u.string.sval);
- ret->h = atoi(cfg[1].u.string.sval);
- ret->n = atoi(cfg[2].u.string.sval);
- if (strchr(cfg[2].u.string.sval, '%'))
- ret->n = ret->n * (ret->w * ret->h) / 100;
- ret->unique = cfg[3].u.boolean.bval;
- return ret;
- }
- static const char *validate_params(const game_params *params, bool full)
- {
- /*
- * Lower limit on grid size: each dimension must be at least 3.
- * 1 is theoretically workable if rather boring, but 2 is a
- * real problem: there is often _no_ way to generate a uniquely
- * solvable 2xn Mines grid. You either run into two mines
- * blocking the way and no idea what's behind them, or one mine
- * and no way to know which of the two rows it's in. If the
- * mine count is even you can create a soluble grid by packing
- * all the mines at one end (so that when you hit a two-mine
- * wall there are only as many covered squares left as there
- * are mines); but if it's odd, you are doomed, because you
- * _have_ to have a gap somewhere which you can't determine the
- * position of.
- */
- if (full && params->unique && (params->w <= 2 || params->h <= 2))
- return "Width and height must both be greater than two";
- if (params->w < 1 || params->h < 1)
- return "Width and height must both be at least one";
- if (params->w > SHRT_MAX || params->h > SHRT_MAX)
- return "Neither width nor height may be unreasonably large";
- /*
- * We use random_upto() to place mines, and its maximum limit is 2^28-1.
- */
- #if (1<<28)-1 < INT_MAX
- if (params->w > ((1<<28)-1) / params->h)
- #else
- if (params->w > INT_MAX / params->h)
- #endif
- return "Width times height must not be unreasonably large";
- if (params->n < 0)
- return "Mine count may not be negative";
- if (params->n > params->w * params->h - 9)
- return "Too many mines for grid size";
- /*
- * FIXME: Need more constraints here. Not sure what the
- * sensible limits for Minesweeper actually are. The limits
- * probably ought to change, however, depending on uniqueness.
- */
- return NULL;
- }
- /* ----------------------------------------------------------------------
- * Minesweeper solver, used to ensure the generated grids are
- * solvable without having to take risks.
- */
- /*
- * Count the bits in a word. Only needs to cope with 16 bits.
- */
- static int bitcount16(int inword)
- {
- unsigned int word = inword;
- word = ((word & 0xAAAA) >> 1) + (word & 0x5555);
- word = ((word & 0xCCCC) >> 2) + (word & 0x3333);
- word = ((word & 0xF0F0) >> 4) + (word & 0x0F0F);
- word = ((word & 0xFF00) >> 8) + (word & 0x00FF);
- return (int)word;
- }
- /*
- * We use a tree234 to store a large number of small localised
- * sets, each with a mine count. We also keep some of those sets
- * linked together into a to-do list.
- */
- struct set {
- short x, y, mask, mines;
- bool todo;
- struct set *prev, *next;
- };
- static int setcmp(void *av, void *bv)
- {
- struct set *a = (struct set *)av;
- struct set *b = (struct set *)bv;
- if (a->y < b->y)
- return -1;
- else if (a->y > b->y)
- return +1;
- else if (a->x < b->x)
- return -1;
- else if (a->x > b->x)
- return +1;
- else if (a->mask < b->mask)
- return -1;
- else if (a->mask > b->mask)
- return +1;
- else
- return 0;
- }
- struct setstore {
- tree234 *sets;
- struct set *todo_head, *todo_tail;
- };
- static struct setstore *ss_new(void)
- {
- struct setstore *ss = snew(struct setstore);
- ss->sets = newtree234(setcmp);
- ss->todo_head = ss->todo_tail = NULL;
- return ss;
- }
- /*
- * Take two input sets, in the form (x,y,mask). Munge the first by
- * taking either its intersection with the second or its difference
- * with the second. Return the new mask part of the first set.
- */
- static int setmunge(int x1, int y1, int mask1, int x2, int y2, int mask2,
- bool diff)
- {
- /*
- * Adjust the second set so that it has the same x,y
- * coordinates as the first.
- */
- if (abs(x2-x1) >= 3 || abs(y2-y1) >= 3) {
- mask2 = 0;
- } else {
- while (x2 > x1) {
- mask2 &= ~(4|32|256);
- mask2 <<= 1;
- x2--;
- }
- while (x2 < x1) {
- mask2 &= ~(1|8|64);
- mask2 >>= 1;
- x2++;
- }
- while (y2 > y1) {
- mask2 &= ~(64|128|256);
- mask2 <<= 3;
- y2--;
- }
- while (y2 < y1) {
- mask2 &= ~(1|2|4);
- mask2 >>= 3;
- y2++;
- }
- }
- /*
- * Invert the second set if `diff' is set (we're after A &~ B
- * rather than A & B).
- */
- if (diff)
- mask2 ^= 511;
- /*
- * Now all that's left is a logical AND.
- */
- return mask1 & mask2;
- }
- static void ss_add_todo(struct setstore *ss, struct set *s)
- {
- if (s->todo)
- return; /* already on it */
- #ifdef SOLVER_DIAGNOSTICS
- printf("adding set on todo list: %d,%d %03x %d\n",
- s->x, s->y, s->mask, s->mines);
- #endif
- s->prev = ss->todo_tail;
- if (s->prev)
- s->prev->next = s;
- else
- ss->todo_head = s;
- ss->todo_tail = s;
- s->next = NULL;
- s->todo = true;
- }
- static void ss_add(struct setstore *ss, int x, int y, int mask, int mines)
- {
- struct set *s;
- assert(mask != 0);
- /*
- * Normalise so that x and y are genuinely the bounding
- * rectangle.
- */
- while (!(mask & (1|8|64)))
- mask >>= 1, x++;
- while (!(mask & (1|2|4)))
- mask >>= 3, y++;
- /*
- * Create a set structure and add it to the tree.
- */
- s = snew(struct set);
- assert(SHRT_MIN <= x && x <= SHRT_MAX);
- s->x = x;
- assert(SHRT_MIN <= y && y <= SHRT_MAX);
- s->y = y;
- s->mask = mask;
- s->mines = mines;
- s->todo = false;
- if (add234(ss->sets, s) != s) {
- /*
- * This set already existed! Free it and return.
- */
- sfree(s);
- return;
- }
- /*
- * We've added a new set to the tree, so put it on the todo
- * list.
- */
- ss_add_todo(ss, s);
- }
- static void ss_remove(struct setstore *ss, struct set *s)
- {
- struct set *next = s->next, *prev = s->prev;
- #ifdef SOLVER_DIAGNOSTICS
- printf("removing set %d,%d %03x\n", s->x, s->y, s->mask);
- #endif
- /*
- * Remove s from the todo list.
- */
- if (prev)
- prev->next = next;
- else if (s == ss->todo_head)
- ss->todo_head = next;
- if (next)
- next->prev = prev;
- else if (s == ss->todo_tail)
- ss->todo_tail = prev;
- s->todo = false;
- /*
- * Remove s from the tree.
- */
- del234(ss->sets, s);
- /*
- * Destroy the actual set structure.
- */
- sfree(s);
- }
- /*
- * Return a dynamically allocated list of all the sets which
- * overlap a provided input set.
- */
- static struct set **ss_overlap(struct setstore *ss, int x, int y, int mask)
- {
- struct set **ret = NULL;
- int nret = 0, retsize = 0;
- int xx, yy;
- for (xx = x-3; xx < x+3; xx++)
- for (yy = y-3; yy < y+3; yy++) {
- struct set stmp, *s;
- int pos;
- /*
- * Find the first set with these top left coordinates.
- */
- assert(SHRT_MIN <= xx && xx <= SHRT_MAX);
- stmp.x = xx;
- assert(SHRT_MIN <= yy && yy <= SHRT_MAX);
- stmp.y = yy;
- stmp.mask = 0;
- if (findrelpos234(ss->sets, &stmp, NULL, REL234_GE, &pos)) {
- while ((s = index234(ss->sets, pos)) != NULL &&
- s->x == xx && s->y == yy) {
- /*
- * This set potentially overlaps the input one.
- * Compute the intersection to see if they
- * really overlap, and add it to the list if
- * so.
- */
- if (setmunge(x, y, mask, s->x, s->y, s->mask, false)) {
- /*
- * There's an overlap.
- */
- if (nret >= retsize) {
- retsize = nret + 32;
- ret = sresize(ret, retsize, struct set *);
- }
- ret[nret++] = s;
- }
- pos++;
- }
- }
- }
- ret = sresize(ret, nret+1, struct set *);
- ret[nret] = NULL;
- return ret;
- }
- /*
- * Get an element from the head of the set todo list.
- */
- static struct set *ss_todo(struct setstore *ss)
- {
- if (ss->todo_head) {
- struct set *ret = ss->todo_head;
- ss->todo_head = ret->next;
- if (ss->todo_head)
- ss->todo_head->prev = NULL;
- else
- ss->todo_tail = NULL;
- ret->next = ret->prev = NULL;
- ret->todo = false;
- return ret;
- } else {
- return NULL;
- }
- }
- struct squaretodo {
- int *next;
- int head, tail;
- };
- static void std_add(struct squaretodo *std, int i)
- {
- if (std->tail >= 0)
- std->next[std->tail] = i;
- else
- std->head = i;
- std->tail = i;
- std->next[i] = -1;
- }
- typedef int (*open_cb)(void *, int, int);
- static void known_squares(int w, int h, struct squaretodo *std,
- signed char *grid,
- open_cb open, void *openctx,
- int x, int y, int mask, bool mine)
- {
- int xx, yy, bit;
- bit = 1;
- for (yy = 0; yy < 3; yy++)
- for (xx = 0; xx < 3; xx++) {
- if (mask & bit) {
- int i = (y + yy) * w + (x + xx);
- /*
- * It's possible that this square is _already_
- * known, in which case we don't try to add it to
- * the list twice.
- */
- if (grid[i] == -2) {
- if (mine) {
- grid[i] = -1; /* and don't open it! */
- } else {
- grid[i] = open(openctx, x + xx, y + yy);
- assert(grid[i] != -1); /* *bang* */
- }
- std_add(std, i);
- }
- }
- bit <<= 1;
- }
- }
- /*
- * This is data returned from the `perturb' function. It details
- * which squares have become mines and which have become clear. The
- * solver is (of course) expected to honourably not use that
- * knowledge directly, but to efficently adjust its internal data
- * structures and proceed based on only the information it
- * legitimately has.
- */
- struct perturbation {
- int x, y;
- int delta; /* +1 == become a mine; -1 == cleared */
- };
- struct perturbations {
- int n;
- struct perturbation *changes;
- };
- /*
- * Main solver entry point. You give it a grid of existing
- * knowledge (-1 for a square known to be a mine, 0-8 for empty
- * squares with a given number of neighbours, -2 for completely
- * unknown), plus a function which you can call to open new squares
- * once you're confident of them. It fills in as much more of the
- * grid as it can.
- *
- * Return value is:
- *
- * - -1 means deduction stalled and nothing could be done
- * - 0 means deduction succeeded fully
- * - >0 means deduction succeeded but some number of perturbation
- * steps were required; the exact return value is the number of
- * perturb calls.
- */
- typedef struct perturbations *(*perturb_cb) (void *, signed char *, int, int, int);
- static int minesolve(int w, int h, int n, signed char *grid,
- open_cb open,
- perturb_cb perturb,
- void *ctx, random_state *rs)
- {
- struct setstore *ss = ss_new();
- struct set **list;
- struct squaretodo astd, *std = &astd;
- int x, y, i, j;
- int nperturbs = 0;
- /*
- * Set up a linked list of squares with known contents, so that
- * we can process them one by one.
- */
- std->next = snewn(w*h, int);
- std->head = std->tail = -1;
- /*
- * Initialise that list with all known squares in the input
- * grid.
- */
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- i = y*w+x;
- if (grid[i] != -2)
- std_add(std, i);
- }
- }
- /*
- * Main deductive loop.
- */
- while (1) {
- bool done_something = false;
- struct set *s;
- /*
- * If there are any known squares on the todo list, process
- * them and construct a set for each.
- */
- while (std->head != -1) {
- i = std->head;
- #ifdef SOLVER_DIAGNOSTICS
- printf("known square at %d,%d [%d]\n", i%w, i/w, grid[i]);
- #endif
- std->head = std->next[i];
- if (std->head == -1)
- std->tail = -1;
- x = i % w;
- y = i / w;
- if (grid[i] >= 0) {
- int dx, dy, mines, bit, val;
- #ifdef SOLVER_DIAGNOSTICS
- printf("creating set around this square\n");
- #endif
- /*
- * Empty square. Construct the set of non-known squares
- * around this one, and determine its mine count.
- */
- mines = grid[i];
- bit = 1;
- val = 0;
- for (dy = -1; dy <= +1; dy++) {
- for (dx = -1; dx <= +1; dx++) {
- #ifdef SOLVER_DIAGNOSTICS
- printf("grid %d,%d = %d\n", x+dx, y+dy, grid[i+dy*w+dx]);
- #endif
- if (x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= h)
- /* ignore this one */;
- else if (grid[i+dy*w+dx] == -1)
- mines--;
- else if (grid[i+dy*w+dx] == -2)
- val |= bit;
- bit <<= 1;
- }
- }
- if (val)
- ss_add(ss, x-1, y-1, val, mines);
- }
- /*
- * Now, whether the square is empty or full, we must
- * find any set which contains it and replace it with
- * one which does not.
- */
- {
- #ifdef SOLVER_DIAGNOSTICS
- printf("finding sets containing known square %d,%d\n", x, y);
- #endif
- list = ss_overlap(ss, x, y, 1);
- for (j = 0; list[j]; j++) {
- int newmask, newmines;
- s = list[j];
- /*
- * Compute the mask for this set minus the
- * newly known square.
- */
- newmask = setmunge(s->x, s->y, s->mask, x, y, 1, true);
- /*
- * Compute the new mine count.
- */
- newmines = s->mines - (grid[i] == -1);
- /*
- * Insert the new set into the collection,
- * unless it's been whittled right down to
- * nothing.
- */
- if (newmask)
- ss_add(ss, s->x, s->y, newmask, newmines);
- /*
- * Destroy the old one; it is actually obsolete.
- */
- ss_remove(ss, s);
- }
- sfree(list);
- }
- /*
- * Marking a fresh square as known certainly counts as
- * doing something.
- */
- done_something = true;
- }
- /*
- * Now pick a set off the to-do list and attempt deductions
- * based on it.
- */
- if ((s = ss_todo(ss)) != NULL) {
- #ifdef SOLVER_DIAGNOSTICS
- printf("set to do: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
- #endif
- /*
- * Firstly, see if this set has a mine count of zero or
- * of its own cardinality.
- */
- if (s->mines == 0 || s->mines == bitcount16(s->mask)) {
- /*
- * If so, we can immediately mark all the squares
- * in the set as known.
- */
- #ifdef SOLVER_DIAGNOSTICS
- printf("easy\n");
- #endif
- known_squares(w, h, std, grid, open, ctx,
- s->x, s->y, s->mask, (s->mines != 0));
- /*
- * Having done that, we need do nothing further
- * with this set; marking all the squares in it as
- * known will eventually eliminate it, and will
- * also permit further deductions about anything
- * that overlaps it.
- */
- continue;
- }
- /*
- * Failing that, we now search through all the sets
- * which overlap this one.
- */
- list = ss_overlap(ss, s->x, s->y, s->mask);
- for (j = 0; list[j]; j++) {
- struct set *s2 = list[j];
- int swing, s2wing, swc, s2wc;
- /*
- * Find the non-overlapping parts s2-s and s-s2,
- * and their cardinalities.
- *
- * I'm going to refer to these parts as `wings'
- * surrounding the central part common to both
- * sets. The `s wing' is s-s2; the `s2 wing' is
- * s2-s.
- */
- swing = setmunge(s->x, s->y, s->mask, s2->x, s2->y, s2->mask,
- true);
- s2wing = setmunge(s2->x, s2->y, s2->mask, s->x, s->y, s->mask,
- true);
- swc = bitcount16(swing);
- s2wc = bitcount16(s2wing);
- /*
- * If one set has more mines than the other, and
- * the number of extra mines is equal to the
- * cardinality of that set's wing, then we can mark
- * every square in the wing as a known mine, and
- * every square in the other wing as known clear.
- */
- if (swc == s->mines - s2->mines ||
- s2wc == s2->mines - s->mines) {
- known_squares(w, h, std, grid, open, ctx,
- s->x, s->y, swing,
- (swc == s->mines - s2->mines));
- known_squares(w, h, std, grid, open, ctx,
- s2->x, s2->y, s2wing,
- (s2wc == s2->mines - s->mines));
- continue;
- }
- /*
- * Failing that, see if one set is a subset of the
- * other. If so, we can divide up the mine count of
- * the larger set between the smaller set and its
- * complement, even if neither smaller set ends up
- * being immediately clearable.
- */
- if (swc == 0 && s2wc != 0) {
- /* s is a subset of s2. */
- assert(s2->mines > s->mines);
- ss_add(ss, s2->x, s2->y, s2wing, s2->mines - s->mines);
- } else if (s2wc == 0 && swc != 0) {
- /* s2 is a subset of s. */
- assert(s->mines > s2->mines);
- ss_add(ss, s->x, s->y, swing, s->mines - s2->mines);
- }
- }
- sfree(list);
- /*
- * In this situation we have definitely done
- * _something_, even if it's only reducing the size of
- * our to-do list.
- */
- done_something = true;
- } else if (n >= 0) {
- /*
- * We have nothing left on our todo list, which means
- * all localised deductions have failed. Our next step
- * is to resort to global deduction based on the total
- * mine count. This is computationally expensive
- * compared to any of the above deductions, which is
- * why we only ever do it when all else fails, so that
- * hopefully it won't have to happen too often.
- *
- * If you pass n<0 into this solver, that informs it
- * that you do not know the total mine count, so it
- * won't even attempt these deductions.
- */
- int minesleft, squaresleft;
- int nsets, cursor;
- bool setused[10];
- /*
- * Start by scanning the current grid state to work out
- * how many unknown squares we still have, and how many
- * mines are to be placed in them.
- */
- squaresleft = 0;
- minesleft = n;
- for (i = 0; i < w*h; i++) {
- if (grid[i] == -1)
- minesleft--;
- else if (grid[i] == -2)
- squaresleft++;
- }
- #ifdef SOLVER_DIAGNOSTICS
- printf("global deduction time: squaresleft=%d minesleft=%d\n",
- squaresleft, minesleft);
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- int v = grid[y*w+x];
- if (v == -1)
- putchar('*');
- else if (v == -2)
- putchar('?');
- else if (v == 0)
- putchar('-');
- else
- putchar('0' + v);
- }
- putchar('\n');
- }
- #endif
- /*
- * If there _are_ no unknown squares, we have actually
- * finished.
- */
- if (squaresleft == 0) {
- assert(minesleft == 0);
- break;
- }
- /*
- * First really simple case: if there are no more mines
- * left, or if there are exactly as many mines left as
- * squares to play them in, then it's all easy.
- */
- if (minesleft == 0 || minesleft == squaresleft) {
- for (i = 0; i < w*h; i++)
- if (grid[i] == -2)
- known_squares(w, h, std, grid, open, ctx,
- i % w, i / w, 1, minesleft != 0);
- continue; /* now go back to main deductive loop */
- }
- /*
- * Failing that, we have to do some _real_ work.
- * Ideally what we do here is to try every single
- * combination of the currently available sets, in an
- * attempt to find a disjoint union (i.e. a set of
- * squares with a known mine count between them) such
- * that the remaining unknown squares _not_ contained
- * in that union either contain no mines or are all
- * mines.
- *
- * Actually enumerating all 2^n possibilities will get
- * a bit slow for large n, so I artificially cap this
- * recursion at n=10 to avoid too much pain.
- */
- nsets = count234(ss->sets);
- if (nsets <= lenof(setused)) {
- /*
- * Doing this with actual recursive function calls
- * would get fiddly because a load of local
- * variables from this function would have to be
- * passed down through the recursion. So instead
- * I'm going to use a virtual recursion within this
- * function. The way this works is:
- *
- * - we have an array `setused', such that setused[n]
- * is true if set n is currently in the union we
- * are considering.
- *
- * - we have a value `cursor' which indicates how
- * much of `setused' we have so far filled in.
- * It's conceptually the recursion depth.
- *
- * We begin by setting `cursor' to zero. Then:
- *
- * - if cursor can advance, we advance it by one. We
- * set the value in `setused' that it went past to
- * true if that set is disjoint from anything else
- * currently in `setused', or to false otherwise.
- *
- * - If cursor cannot advance because it has
- * reached the end of the setused list, then we
- * have a maximal disjoint union. Check to see
- * whether its mine count has any useful
- * properties. If so, mark all the squares not
- * in the union as known and terminate.
- *
- * - If cursor has reached the end of setused and the
- * algorithm _hasn't_ terminated, back cursor up to
- * the nearest true entry, reset it to false, and
- * advance cursor just past it.
- *
- * - If we attempt to back up to the nearest 1 and
- * there isn't one at all, then we have gone
- * through all disjoint unions of sets in the
- * list and none of them has been helpful, so we
- * give up.
- */
- struct set *sets[lenof(setused)];
- for (i = 0; i < nsets; i++)
- sets[i] = index234(ss->sets, i);
- cursor = 0;
- while (1) {
- if (cursor < nsets) {
- bool ok = true;
- /* See if any existing set overlaps this one. */
- for (i = 0; i < cursor; i++)
- if (setused[i] &&
- setmunge(sets[cursor]->x,
- sets[cursor]->y,
- sets[cursor]->mask,
- sets[i]->x, sets[i]->y, sets[i]->mask,
- false)) {
- ok = false;
- break;
- }
- if (ok) {
- /*
- * We're adding this set to our union,
- * so adjust minesleft and squaresleft
- * appropriately.
- */
- minesleft -= sets[cursor]->mines;
- squaresleft -= bitcount16(sets[cursor]->mask);
- }
- setused[cursor++] = ok;
- } else {
- #ifdef SOLVER_DIAGNOSTICS
- printf("trying a set combination with %d %d\n",
- squaresleft, minesleft);
- #endif /* SOLVER_DIAGNOSTICS */
- /*
- * We've reached the end. See if we've got
- * anything interesting.
- */
- if (squaresleft > 0 &&
- (minesleft == 0 || minesleft == squaresleft)) {
- /*
- * We have! There is at least one
- * square not contained within the set
- * union we've just found, and we can
- * deduce that either all such squares
- * are mines or all are not (depending
- * on whether minesleft==0). So now all
- * we have to do is actually go through
- * the grid, find those squares, and
- * mark them.
- */
- for (i = 0; i < w*h; i++)
- if (grid[i] == -2) {
- bool outside = true;
- y = i / w;
- x = i % w;
- for (j = 0; j < nsets; j++)
- if (setused[j] &&
- setmunge(sets[j]->x, sets[j]->y,
- sets[j]->mask, x, y, 1,
- false)) {
- outside = false;
- break;
- }
- if (outside)
- known_squares(w, h, std, grid,
- open, ctx,
- x, y, 1, minesleft != 0);
- }
- done_something = true;
- break; /* return to main deductive loop */
- }
- /*
- * If we reach here, then this union hasn't
- * done us any good, so move on to the
- * next. Backtrack cursor to the nearest 1,
- * change it to a 0 and continue.
- */
- while (--cursor >= 0 && !setused[cursor]);
- if (cursor >= 0) {
- assert(setused[cursor]);
- /*
- * We're removing this set from our
- * union, so re-increment minesleft and
- * squaresleft.
- */
- minesleft += sets[cursor]->mines;
- squaresleft += bitcount16(sets[cursor]->mask);
- setused[cursor++] = false;
- } else {
- /*
- * We've backtracked all the way to the
- * start without finding a single 1,
- * which means that our virtual
- * recursion is complete and nothing
- * helped.
- */
- break;
- }
- }
- }
- }
- }
- if (done_something)
- continue;
- #ifdef SOLVER_DIAGNOSTICS
- /*
- * Dump the current known state of the grid.
- */
- printf("solver ran out of steam, ret=%d, grid:\n", nperturbs);
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- int v = grid[y*w+x];
- if (v == -1)
- putchar('*');
- else if (v == -2)
- putchar('?');
- else if (v == 0)
- putchar('-');
- else
- putchar('0' + v);
- }
- putchar('\n');
- }
- {
- struct set *s;
- for (i = 0; (s = index234(ss->sets, i)) != NULL; i++)
- printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
- }
- #endif
- /*
- * Now we really are at our wits' end as far as solving
- * this grid goes. Our only remaining option is to call
- * a perturb function and ask it to modify the grid to
- * make it easier.
- */
- if (perturb) {
- struct perturbations *ret;
- struct set *s;
- nperturbs++;
- /*
- * Choose a set at random from the current selection,
- * and ask the perturb function to either fill or empty
- * it.
- *
- * If we have no sets at all, we must give up.
- */
- if (count234(ss->sets) == 0) {
- #ifdef SOLVER_DIAGNOSTICS
- printf("perturbing on entire unknown set\n");
- #endif
- ret = perturb(ctx, grid, 0, 0, 0);
- } else {
- s = index234(ss->sets, random_upto(rs, count234(ss->sets)));
- #ifdef SOLVER_DIAGNOSTICS
- printf("perturbing on set %d,%d %03x\n", s->x, s->y, s->mask);
- #endif
- ret = perturb(ctx, grid, s->x, s->y, s->mask);
- }
- if (ret) {
- assert(ret->n > 0); /* otherwise should have been NULL */
- /*
- * A number of squares have been fiddled with, and
- * the returned structure tells us which. Adjust
- * the mine count in any set which overlaps one of
- * those squares, and put them back on the to-do
- * list. Also, if the square itself is marked as a
- * known non-mine, put it back on the squares-to-do
- * list.
- */
- for (i = 0; i < ret->n; i++) {
- #ifdef SOLVER_DIAGNOSTICS
- printf("perturbation %s mine at %d,%d\n",
- ret->changes[i].delta > 0 ? "added" : "removed",
- ret->changes[i].x, ret->changes[i].y);
- #endif
- if (ret->changes[i].delta < 0 &&
- grid[ret->changes[i].y*w+ret->changes[i].x] != -2) {
- std_add(std, ret->changes[i].y*w+ret->changes[i].x);
- }
- list = ss_overlap(ss,
- ret->changes[i].x, ret->changes[i].y, 1);
- for (j = 0; list[j]; j++) {
- list[j]->mines += ret->changes[i].delta;
- ss_add_todo(ss, list[j]);
- }
- sfree(list);
- }
- /*
- * Now free the returned data.
- */
- sfree(ret->changes);
- sfree(ret);
- #ifdef SOLVER_DIAGNOSTICS
- /*
- * Dump the current known state of the grid.
- */
- printf("state after perturbation:\n");
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- int v = grid[y*w+x];
- if (v == -1)
- putchar('*');
- else if (v == -2)
- putchar('?');
- else if (v == 0)
- putchar('-');
- else
- putchar('0' + v);
- }
- putchar('\n');
- }
- {
- struct set *s;
- for (i = 0; (s = index234(ss->sets, i)) != NULL; i++)
- printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
- }
- #endif
- /*
- * And now we can go back round the deductive loop.
- */
- continue;
- }
- }
- /*
- * If we get here, even that didn't work (either we didn't
- * have a perturb function or it returned failure), so we
- * give up entirely.
- */
- break;
- }
- /*
- * See if we've got any unknown squares left.
- */
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++)
- if (grid[y*w+x] == -2) {
- nperturbs = -1; /* failed to complete */
- break;
- }
- /*
- * Free the set list and square-todo list.
- */
- {
- struct set *s;
- while ((s = delpos234(ss->sets, 0)) != NULL)
- sfree(s);
- freetree234(ss->sets);
- sfree(ss);
- sfree(std->next);
- }
- return nperturbs;
- }
- /* ----------------------------------------------------------------------
- * Grid generator which uses the above solver.
- */
- struct minectx {
- bool *grid;
- int w, h;
- int sx, sy;
- bool allow_big_perturbs;
- random_state *rs;
- };
- static int mineopen(void *vctx, int x, int y)
- {
- struct minectx *ctx = (struct minectx *)vctx;
- int i, j, n;
- assert(x >= 0 && x < ctx->w && y >= 0 && y < ctx->h);
- if (ctx->grid[y * ctx->w + x])
- return -1; /* *bang* */
- n = 0;
- for (i = -1; i <= +1; i++) {
- if (x + i < 0 || x + i >= ctx->w)
- continue;
- for (j = -1; j <= +1; j++) {
- if (y + j < 0 || y + j >= ctx->h)
- continue;
- if (i == 0 && j == 0)
- continue;
- if (ctx->grid[(y+j) * ctx->w + (x+i)])
- n++;
- }
- }
- return n;
- }
- /* Structure used internally to mineperturb(). */
- struct square {
- int x, y, type, random;
- };
- static int squarecmp(const void *av, const void *bv)
- {
- const struct square *a = (const struct square *)av;
- const struct square *b = (const struct square *)bv;
- if (a->type < b->type)
- return -1;
- else if (a->type > b->type)
- return +1;
- else if (a->random < b->random)
- return -1;
- else if (a->random > b->random)
- return +1;
- else if (a->y < b->y)
- return -1;
- else if (a->y > b->y)
- return +1;
- else if (a->x < b->x)
- return -1;
- else if (a->x > b->x)
- return +1;
- return 0;
- }
- /*
- * Normally this function is passed an (x,y,mask) set description.
- * On occasions, though, there is no _localised_ set being used,
- * and the set being perturbed is supposed to be the entirety of
- * the unreachable area. This is signified by the special case
- * mask==0: in this case, anything labelled -2 in the grid is part
- * of the set.
- *
- * Allowing perturbation in this special case appears to make it
- * guaranteeably possible to generate a workable grid for any mine
- * density, but they tend to be a bit boring, with mines packed
- * densely into far corners of the grid and the remainder being
- * less dense than one might like. Therefore, to improve overall
- * grid quality I disable this feature for the first few attempts,
- * and fall back to it after no useful grid has been generated.
- */
- static struct perturbations *mineperturb(void *vctx, signed char *grid,
- int setx, int sety, int mask)
- {
- struct minectx *ctx = (struct minectx *)vctx;
- struct square *sqlist;
- int x, y, dx, dy, i, n, nfull, nempty;
- struct square **tofill, **toempty, **todo;
- int ntofill, ntoempty, ntodo, dtodo, dset;
- struct perturbations *ret;
- int *setlist;
- if (!mask && !ctx->allow_big_perturbs)
- return NULL;
- /*
- * Make a list of all the squares in the grid which we can
- * possibly use. This list should be in preference order, which
- * means
- *
- * - first, unknown squares on the boundary of known space
- * - next, unknown squares beyond that boundary
- * - as a very last resort, known squares, but not within one
- * square of the starting position.
- *
- * Each of these sections needs to be shuffled independently.
- * We do this by preparing list of all squares and then sorting
- * it with a random secondary key.
- */
- sqlist = snewn(ctx->w * ctx->h, struct square);
- n = 0;
- for (y = 0; y < ctx->h; y++)
- for (x = 0; x < ctx->w; x++) {
- /*
- * If this square is too near the starting position,
- * don't put it on the list at all.
- */
- if (abs(y - ctx->sy) <= 1 && abs(x - ctx->sx) <= 1)
- continue;
- /*
- * If this square is in the input set, also don't put
- * it on the list!
- */
- if ((mask == 0 && grid[y*ctx->w+x] == -2) ||
- (x >= setx && x < setx + 3 &&
- y >= sety && y < sety + 3 &&
- mask & (1 << ((y-sety)*3+(x-setx)))))
- continue;
- sqlist[n].x = x;
- sqlist[n].y = y;
- if (grid[y*ctx->w+x] != -2) {
- sqlist[n].type = 3; /* known square */
- } else {
- /*
- * Unknown square. Examine everything around it and
- * see if it borders on any known squares. If it
- * does, it's class 1, otherwise it's 2.
- */
- sqlist[n].type = 2;
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (x+dx >= 0 && x+dx < ctx->w &&
- y+dy >= 0 && y+dy < ctx->h &&
- grid[(y+dy)*ctx->w+(x+dx)] != -2) {
- sqlist[n].type = 1;
- break;
- }
- }
- /*
- * Finally, a random number to cause qsort to
- * shuffle within each group.
- */
- sqlist[n].random = random_bits(ctx->rs, 31);
- n++;
- }
- qsort(sqlist, n, sizeof(struct square), squarecmp);
- /*
- * Now count up the number of full and empty squares in the set
- * we've been provided.
- */
- nfull = nempty = 0;
- if (mask) {
- for (dy = 0; dy < 3; dy++)
- for (dx = 0; dx < 3; dx++)
- if (mask & (1 << (dy*3+dx))) {
- assert(setx+dx <= ctx->w);
- assert(sety+dy <= ctx->h);
- if (ctx->grid[(sety+dy)*ctx->w+(setx+dx)])
- nfull++;
- else
- nempty++;
- }
- } else {
- for (y = 0; y < ctx->h; y++)
- for (x = 0; x < ctx->w; x++)
- if (grid[y*ctx->w+x] == -2) {
- if (ctx->grid[y*ctx->w+x])
- nfull++;
- else
- nempty++;
- }
- }
- /*
- * Now go through our sorted list until we find either `nfull'
- * empty squares, or `nempty' full squares; these will be
- * swapped with the appropriate squares in the set to either
- * fill or empty the set while keeping the same number of mines
- * overall.
- */
- ntofill = ntoempty = 0;
- if (mask) {
- tofill = snewn(9, struct square *);
- toempty = snewn(9, struct square *);
- } else {
- tofill = snewn(ctx->w * ctx->h, struct square *);
- toempty = snewn(ctx->w * ctx->h, struct square *);
- }
- for (i = 0; i < n; i++) {
- struct square *sq = &sqlist[i];
- if (ctx->grid[sq->y * ctx->w + sq->x])
- toempty[ntoempty++] = sq;
- else
- tofill[ntofill++] = sq;
- if (ntofill == nfull || ntoempty == nempty)
- break;
- }
- /*
- * If we haven't found enough empty squares outside the set to
- * empty it into _or_ enough full squares outside it to fill it
- * up with, we'll have to settle for doing only a partial job.
- * In this case we choose to always _fill_ the set (because
- * this case will tend to crop up when we're working with very
- * high mine densities and the only way to get a solvable grid
- * is going to be to pack most of the mines solidly around the
- * edges). So now our job is to make a list of the empty
- * squares in the set, and shuffle that list so that we fill a
- * random selection of them.
- */
- if (ntofill != nfull && ntoempty != nempty) {
- int k;
- assert(ntoempty != 0);
- setlist = snewn(ctx->w * ctx->h, int);
- i = 0;
- if (mask) {
- for (dy = 0; dy < 3; dy++)
- for (dx = 0; dx < 3; dx++)
- if (mask & (1 << (dy*3+dx))) {
- assert(setx+dx <= ctx->w);
- assert(sety+dy <= ctx->h);
- if (!ctx->grid[(sety+dy)*ctx->w+(setx+dx)])
- setlist[i++] = (sety+dy)*ctx->w+(setx+dx);
- }
- } else {
- for (y = 0; y < ctx->h; y++)
- for (x = 0; x < ctx->w; x++)
- if (grid[y*ctx->w+x] == -2) {
- if (!ctx->grid[y*ctx->w+x])
- setlist[i++] = y*ctx->w+x;
- }
- }
- assert(i > ntoempty);
- /*
- * Now pick `ntoempty' items at random from the list.
- */
- for (k = 0; k < ntoempty; k++) {
- int index = k + random_upto(ctx->rs, i - k);
- int tmp;
- tmp = setlist[k];
- setlist[k] = setlist[index];
- setlist[index] = tmp;
- }
- } else
- setlist = NULL;
- /*
- * Now we're pretty much there. We need to either
- * (a) put a mine in each of the empty squares in the set, and
- * take one out of each square in `toempty'
- * (b) take a mine out of each of the full squares in the set,
- * and put one in each square in `tofill'
- * depending on which one we've found enough squares to do.
- *
- * So we start by constructing our list of changes to return to
- * the solver, so that it can update its data structures
- * efficiently rather than having to rescan the whole grid.
- */
- ret = snew(struct perturbations);
- if (ntofill == nfull) {
- todo = tofill;
- ntodo = ntofill;
- dtodo = +1;
- dset = -1;
- sfree(toempty);
- } else {
- /*
- * (We also fall into this case if we've constructed a
- * setlist.)
- */
- todo = toempty;
- ntodo = ntoempty;
- dtodo = -1;
- dset = +1;
- sfree(tofill);
- }
- ret->n = 2 * ntodo;
- ret->changes = snewn(ret->n, struct perturbation);
- for (i = 0; i < ntodo; i++) {
- ret->changes[i].x = todo[i]->x;
- ret->changes[i].y = todo[i]->y;
- ret->changes[i].delta = dtodo;
- }
- /* now i == ntodo */
- if (setlist) {
- int j;
- assert(todo == toempty);
- for (j = 0; j < ntoempty; j++) {
- ret->changes[i].x = setlist[j] % ctx->w;
- ret->changes[i].y = setlist[j] / ctx->w;
- ret->changes[i].delta = dset;
- i++;
- }
- sfree(setlist);
- } else if (mask) {
- for (dy = 0; dy < 3; dy++)
- for (dx = 0; dx < 3; dx++)
- if (mask & (1 << (dy*3+dx))) {
- int currval = (ctx->grid[(sety+dy)*ctx->w+(setx+dx)] ? +1 : -1);
- if (dset == -currval) {
- ret->changes[i].x = setx + dx;
- ret->changes[i].y = sety + dy;
- ret->changes[i].delta = dset;
- i++;
- }
- }
- } else {
- for (y = 0; y < ctx->h; y++)
- for (x = 0; x < ctx->w; x++)
- if (grid[y*ctx->w+x] == -2) {
- int currval = (ctx->grid[y*ctx->w+x] ? +1 : -1);
- if (dset == -currval) {
- ret->changes[i].x = x;
- ret->changes[i].y = y;
- ret->changes[i].delta = dset;
- i++;
- }
- }
- }
- assert(i == ret->n);
- sfree(sqlist);
- sfree(todo);
- /*
- * Having set up the precise list of changes we're going to
- * make, we now simply make them and return.
- */
- for (i = 0; i < ret->n; i++) {
- int delta;
- x = ret->changes[i].x;
- y = ret->changes[i].y;
- delta = ret->changes[i].delta;
- /*
- * Check we're not trying to add an existing mine or remove
- * an absent one.
- */
- assert((delta < 0) ^ (ctx->grid[y*ctx->w+x] == 0));
- /*
- * Actually make the change.
- */
- ctx->grid[y*ctx->w+x] = (delta > 0);
- /*
- * Update any numbers already present in the grid.
- */
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (x+dx >= 0 && x+dx < ctx->w &&
- y+dy >= 0 && y+dy < ctx->h &&
- grid[(y+dy)*ctx->w+(x+dx)] != -2) {
- if (dx == 0 && dy == 0) {
- /*
- * The square itself is marked as known in
- * the grid. Mark it as a mine if it's a
- * mine, or else work out its number.
- */
- if (delta > 0) {
- grid[y*ctx->w+x] = -1;
- } else {
- int dx2, dy2, minecount = 0;
- for (dy2 = -1; dy2 <= +1; dy2++)
- for (dx2 = -1; dx2 <= +1; dx2++)
- if (x+dx2 >= 0 && x+dx2 < ctx->w &&
- y+dy2 >= 0 && y+dy2 < ctx->h &&
- ctx->grid[(y+dy2)*ctx->w+(x+dx2)])
- minecount++;
- grid[y*ctx->w+x] = minecount;
- }
- } else {
- if (grid[(y+dy)*ctx->w+(x+dx)] >= 0)
- grid[(y+dy)*ctx->w+(x+dx)] += delta;
- }
- }
- }
- #ifdef GENERATION_DIAGNOSTICS
- {
- int yy, xx;
- printf("grid after perturbing:\n");
- for (yy = 0; yy < ctx->h; yy++) {
- for (xx = 0; xx < ctx->w; xx++) {
- int v = ctx->grid[yy*ctx->w+xx];
- if (yy == ctx->sy && xx == ctx->sx) {
- assert(!v);
- putchar('S');
- } else if (v) {
- putchar('*');
- } else {
- putchar('-');
- }
- }
- putchar('\n');
- }
- printf("\n");
- }
- #endif
- return ret;
- }
- static bool *minegen(int w, int h, int n, int x, int y, bool unique,
- random_state *rs)
- {
- bool *ret = snewn(w*h, bool);
- bool success;
- int ntries = 0;
- do {
- success = false;
- ntries++;
- memset(ret, 0, w*h);
- /*
- * Start by placing n mines, none of which is at x,y or within
- * one square of it.
- */
- {
- int *tmp = snewn(w*h, int);
- int i, j, k, nn;
- /*
- * Write down the list of possible mine locations.
- */
- k = 0;
- for (i = 0; i < h; i++)
- for (j = 0; j < w; j++)
- if (abs(i - y) > 1 || abs(j - x) > 1)
- tmp[k++] = i*w+j;
- /*
- * Now pick n off the list at random.
- */
- nn = n;
- while (nn-- > 0) {
- i = random_upto(rs, k);
- ret[tmp[i]] = true;
- tmp[i] = tmp[--k];
- }
- sfree(tmp);
- }
- #ifdef GENERATION_DIAGNOSTICS
- {
- int yy, xx;
- printf("grid after initial generation:\n");
- for (yy = 0; yy < h; yy++) {
- for (xx = 0; xx < w; xx++) {
- int v = ret[yy*w+xx];
- if (yy == y && xx == x) {
- assert(!v);
- putchar('S');
- } else if (v) {
- putchar('*');
- } else {
- putchar('-');
- }
- }
- putchar('\n');
- }
- printf("\n");
- }
- #endif
- /*
- * Now set up a results grid to run the solver in, and a
- * context for the solver to open squares. Then run the solver
- * repeatedly; if the number of perturb steps ever goes up or
- * it ever returns -1, give up completely.
- *
- * We bypass this bit if we're not after a unique grid.
- */
- if (unique) {
- signed char *solvegrid = snewn(w*h, signed char);
- struct minectx actx, *ctx = &actx;
- int solveret, prevret = -2;
- ctx->grid = ret;
- ctx->w = w;
- ctx->h = h;
- ctx->sx = x;
- ctx->sy = y;
- ctx->rs = rs;
- ctx->allow_big_perturbs = (ntries > 100);
- while (1) {
- memset(solvegrid, -2, w*h);
- solvegrid[y*w+x] = mineopen(ctx, x, y);
- assert(solvegrid[y*w+x] == 0); /* by deliberate arrangement */
- solveret =
- minesolve(w, h, n, solvegrid, mineopen, mineperturb, ctx, rs);
- if (solveret < 0 || (prevret >= 0 && solveret >= prevret)) {
- success = false;
- break;
- } else if (solveret == 0) {
- success = true;
- break;
- }
- }
- sfree(solvegrid);
- } else {
- success = true;
- }
- } while (!success);
- return ret;
- }
- static char *describe_layout(bool *grid, int area, int x, int y,
- bool obfuscate)
- {
- char *ret, *p;
- unsigned char *bmp;
- int i;
- /*
- * Set up the mine bitmap and obfuscate it.
- */
- bmp = snewn((area + 7) / 8, unsigned char);
- memset(bmp, 0, (area + 7) / 8);
- for (i = 0; i < area; i++) {
- if (grid[i])
- bmp[i / 8] |= 0x80 >> (i % 8);
- }
- if (obfuscate)
- obfuscate_bitmap(bmp, area, false);
- /*
- * Now encode the resulting bitmap in hex. We can work to
- * nibble rather than byte granularity, since the obfuscation
- * function guarantees to return a bit string of the same
- * length as its input.
- */
- ret = snewn((area+3)/4 + 100, char);
- p = ret + sprintf(ret, "%d,%d,%s", x, y,
- obfuscate ? "m" : "u"); /* 'm' == masked */
- for (i = 0; i < (area+3)/4; i++) {
- int v = bmp[i/2];
- if (i % 2 == 0)
- v >>= 4;
- *p++ = "0123456789abcdef"[v & 0xF];
- }
- *p = '\0';
- sfree(bmp);
- return ret;
- }
- static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique,
- random_state *rs, char **game_desc)
- {
- bool *grid = minegen(w, h, n, x, y, unique, rs);
- if (game_desc)
- *game_desc = describe_layout(grid, w * h, x, y, true);
- return grid;
- }
- static char *new_game_desc(const game_params *params, random_state *rs,
- char **aux, bool interactive)
- {
- /*
- * We generate the coordinates of an initial click even if they
- * aren't actually used. This has the effect of harmonising the
- * random number usage between interactive and batch use: if
- * you use `mines --generate' with an explicit random seed, you
- * should get exactly the same results as if you type the same
- * random seed into the interactive game and click in the same
- * initial location. (Of course you won't get the same grid if
- * you click in a _different_ initial location, but there's
- * nothing to be done about that.)
- */
- int x = random_upto(rs, params->w);
- int y = random_upto(rs, params->h);
- if (!interactive) {
- /*
- * For batch-generated grids, pre-open one square.
- */
- bool *grid;
- char *desc;
- grid = new_mine_layout(params->w, params->h, params->n,
- x, y, params->unique, rs, &desc);
- sfree(grid);
- return desc;
- } else {
- char *rsdesc, *desc;
- rsdesc = random_state_encode(rs);
- desc = snewn(strlen(rsdesc) + 100, char);
- sprintf(desc, "r%d,%c,%s", params->n, (char)(params->unique ? 'u' : 'a'), rsdesc);
- sfree(rsdesc);
- return desc;
- }
- }
- static const char *validate_desc(const game_params *params, const char *desc)
- {
- int wh = params->w * params->h;
- int x, y;
- if (*desc == 'r') {
- desc++;
- if (!*desc || !isdigit((unsigned char)*desc))
- return "No initial mine count in game description";
- if (atoi(desc) > wh - 9)
- return "Too many mines for grid size";
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over mine count */
- if (*desc != ',')
- return "No ',' after initial x-coordinate in game description";
- desc++;
- if (*desc != 'u' && *desc != 'a')
- return "No uniqueness specifier in game description";
- desc++;
- if (*desc != ',')
- return "No ',' after uniqueness specifier in game description";
- /* now ignore the rest */
- } else {
- if (*desc && isdigit((unsigned char)*desc)) {
- x = atoi(desc);
- if (x < 0 || x >= params->w)
- return "Initial x-coordinate was out of range";
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over x coordinate */
- if (*desc != ',')
- return "No ',' after initial x-coordinate in game description";
- desc++; /* eat comma */
- if (!*desc || !isdigit((unsigned char)*desc))
- return "No initial y-coordinate in game description";
- y = atoi(desc);
- if (y < 0 || y >= params->h)
- return "Initial y-coordinate was out of range";
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over y coordinate */
- if (*desc != ',')
- return "No ',' after initial y-coordinate in game description";
- desc++; /* eat comma */
- }
- /* eat `m' for `masked' or `u' for `unmasked', if present */
- if (*desc == 'm' || *desc == 'u')
- desc++;
- /* now just check length of remainder */
- if (strlen(desc) != (wh+3)/4)
- return "Game description is wrong length";
- }
- return NULL;
- }
- static int open_square(game_state *state, int x, int y)
- {
- int w = state->w, h = state->h;
- int xx, yy, nmines, ncovered;
- if (!state->layout->mines) {
- /*
- * We have a preliminary game in which the mine layout
- * hasn't been generated yet. Generate it based on the
- * initial click location.
- */
- char *desc, *privdesc;
- state->layout->mines = new_mine_layout(w, h, state->layout->n,
- x, y, state->layout->unique,
- state->layout->rs,
- &desc);
- /*
- * Find the trailing substring of the game description
- * corresponding to just the mine layout; we will use this
- * as our second `private' game ID for serialisation.
- */
- privdesc = desc;
- while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++;
- if (*privdesc == ',') privdesc++;
- while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++;
- if (*privdesc == ',') privdesc++;
- assert(*privdesc == 'm');
- midend_supersede_game_desc(state->layout->me, desc, privdesc);
- sfree(desc);
- random_free(state->layout->rs);
- state->layout->rs = NULL;
- }
- if (state->layout->mines[y*w+x]) {
- /*
- * The player has landed on a mine. Bad luck. Expose the
- * mine that killed them, but not the rest (in case they
- * want to Undo and carry on playing).
- */
- state->dead = true;
- state->grid[y*w+x] = 65;
- return -1;
- }
- /*
- * Otherwise, the player has opened a safe square. Mark it to-do.
- */
- state->grid[y*w+x] = -10; /* `todo' value internal to this func */
- /*
- * Now go through the grid finding all `todo' values and
- * opening them. Every time one of them turns out to have no
- * neighbouring mines, we add all its unopened neighbours to
- * the list as well.
- *
- * FIXME: We really ought to be able to do this better than
- * using repeated N^2 scans of the grid.
- */
- while (1) {
- bool done_something = false;
- for (yy = 0; yy < h; yy++)
- for (xx = 0; xx < w; xx++)
- if (state->grid[yy*w+xx] == -10) {
- int dx, dy, v;
- assert(!state->layout->mines[yy*w+xx]);
- v = 0;
- for (dx = -1; dx <= +1; dx++)
- for (dy = -1; dy <= +1; dy++)
- if (xx+dx >= 0 && xx+dx < state->w &&
- yy+dy >= 0 && yy+dy < state->h &&
- state->layout->mines[(yy+dy)*w+(xx+dx)])
- v++;
- state->grid[yy*w+xx] = v;
- if (v == 0) {
- for (dx = -1; dx <= +1; dx++)
- for (dy = -1; dy <= +1; dy++)
- if (xx+dx >= 0 && xx+dx < state->w &&
- yy+dy >= 0 && yy+dy < state->h &&
- state->grid[(yy+dy)*w+(xx+dx)] == -2)
- state->grid[(yy+dy)*w+(xx+dx)] = -10;
- }
- done_something = true;
- }
- if (!done_something)
- break;
- }
- /* If the player has already lost, don't let them win as well. */
- if (state->dead) return 0;
- /*
- * Finally, scan the grid and see if exactly as many squares
- * are still covered as there are mines. If so, set the `won'
- * flag and fill in mine markers on all covered squares.
- */
- nmines = ncovered = 0;
- for (yy = 0; yy < h; yy++)
- for (xx = 0; xx < w; xx++) {
- if (state->grid[yy*w+xx] < 0)
- ncovered++;
- if (state->layout->mines[yy*w+xx])
- nmines++;
- }
- assert(ncovered >= nmines);
- if (ncovered == nmines) {
- for (yy = 0; yy < h; yy++)
- for (xx = 0; xx < w; xx++) {
- if (state->grid[yy*w+xx] < 0)
- state->grid[yy*w+xx] = -1;
- }
- state->won = true;
- }
- return 0;
- }
- static game_state *new_game(midend *me, const game_params *params,
- const char *desc)
- {
- game_state *state = snew(game_state);
- int i, wh, x, y;
- bool masked;
- unsigned char *bmp;
- state->w = params->w;
- state->h = params->h;
- state->n = params->n;
- state->dead = state->won = false;
- state->used_solve = false;
- wh = state->w * state->h;
- state->layout = snew(struct mine_layout);
- memset(state->layout, 0, sizeof(struct mine_layout));
- state->layout->refcount = 1;
- state->grid = snewn(wh, signed char);
- memset(state->grid, -2, wh);
- if (*desc == 'r') {
- desc++;
- state->layout->n = atoi(desc);
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over mine count */
- if (*desc) desc++; /* eat comma */
- if (*desc == 'a')
- state->layout->unique = false;
- else
- state->layout->unique = true;
- desc++;
- if (*desc) desc++; /* eat comma */
- state->layout->mines = NULL;
- state->layout->rs = random_state_decode(desc);
- state->layout->me = me;
- } else {
- state->layout->rs = NULL;
- state->layout->me = NULL;
- state->layout->mines = snewn(wh, bool);
- if (*desc && isdigit((unsigned char)*desc)) {
- x = atoi(desc);
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over x coordinate */
- if (*desc) desc++; /* eat comma */
- y = atoi(desc);
- while (*desc && isdigit((unsigned char)*desc))
- desc++; /* skip over y coordinate */
- if (*desc) desc++; /* eat comma */
- } else {
- x = y = -1;
- }
- if (*desc == 'm') {
- masked = true;
- desc++;
- } else {
- if (*desc == 'u')
- desc++;
- /*
- * We permit game IDs to be entered by hand without the
- * masking transformation.
- */
- masked = false;
- }
- bmp = snewn((wh + 7) / 8, unsigned char);
- memset(bmp, 0, (wh + 7) / 8);
- for (i = 0; i < (wh+3)/4; i++) {
- int c = desc[i];
- int v;
- assert(c != 0); /* validate_desc should have caught */
- if (c >= '0' && c <= '9')
- v = c - '0';
- else if (c >= 'a' && c <= 'f')
- v = c - 'a' + 10;
- else if (c >= 'A' && c <= 'F')
- v = c - 'A' + 10;
- else
- v = 0;
- bmp[i / 2] |= v << (4 * (1 - (i % 2)));
- }
- if (masked)
- obfuscate_bitmap(bmp, wh, true);
- memset(state->layout->mines, 0, wh * sizeof(bool));
- for (i = 0; i < wh; i++) {
- if (bmp[i / 8] & (0x80 >> (i % 8)))
- state->layout->mines[i] = true;
- }
- if (x >= 0 && y >= 0)
- open_square(state, x, y);
- sfree(bmp);
- }
- return state;
- }
- static game_state *dup_game(const game_state *state)
- {
- game_state *ret = snew(game_state);
- ret->w = state->w;
- ret->h = state->h;
- ret->n = state->n;
- ret->dead = state->dead;
- ret->won = state->won;
- ret->used_solve = state->used_solve;
- ret->layout = state->layout;
- ret->layout->refcount++;
- ret->grid = snewn(ret->w * ret->h, signed char);
- memcpy(ret->grid, state->grid, ret->w * ret->h);
- return ret;
- }
- static void free_game(game_state *state)
- {
- if (--state->layout->refcount <= 0) {
- sfree(state->layout->mines);
- if (state->layout->rs)
- random_free(state->layout->rs);
- sfree(state->layout);
- }
- sfree(state->grid);
- sfree(state);
- }
- static char *solve_game(const game_state *state, const game_state *currstate,
- const char *aux, const char **error)
- {
- if (!state->layout->mines) {
- *error = "Game has not been started yet";
- return NULL;
- }
- return dupstr("S");
- }
- static bool game_can_format_as_text_now(const game_params *params)
- {
- return true;
- }
- static char *game_text_format(const game_state *state)
- {
- char *ret;
- int x, y;
- ret = snewn((state->w + 1) * state->h + 1, char);
- for (y = 0; y < state->h; y++) {
- for (x = 0; x < state->w; x++) {
- int v = state->grid[y*state->w+x];
- if (v == 0)
- v = '-';
- else if (v >= 1 && v <= 8)
- v = '0' + v;
- else if (v == -1)
- v = '*';
- else if (v == -2 || v == -3)
- v = '?';
- else if (v >= 64)
- v = '!';
- ret[y * (state->w+1) + x] = v;
- }
- ret[y * (state->w+1) + state->w] = '\n';
- }
- ret[(state->w + 1) * state->h] = '\0';
- return ret;
- }
- struct game_ui {
- int hx, hy, hradius; /* for mouse-down highlights */
- int validradius;
- bool flash_is_death;
- int deaths;
- bool completed;
- int cur_x, cur_y;
- bool cur_visible;
- };
- static game_ui *new_ui(const game_state *state)
- {
- game_ui *ui = snew(game_ui);
- ui->hx = ui->hy = -1;
- ui->hradius = ui->validradius = 0;
- ui->deaths = 0;
- ui->completed = false;
- ui->flash_is_death = false; /* *shrug* */
- ui->cur_x = ui->cur_y = 0;
- ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
- return ui;
- }
- static void free_ui(game_ui *ui)
- {
- sfree(ui);
- }
- static char *encode_ui(const game_ui *ui)
- {
- char buf[80];
- /*
- * The deaths counter and completion status need preserving
- * across a serialisation.
- */
- sprintf(buf, "D%d", ui->deaths);
- if (ui->completed)
- strcat(buf, "C");
- return dupstr(buf);
- }
- static void decode_ui(game_ui *ui, const char *encoding,
- const game_state *state)
- {
- int p= 0;
- sscanf(encoding, "D%d%n", &ui->deaths, &p);
- if (encoding[p] == 'C')
- ui->completed = true;
- }
- static void game_changed_state(game_ui *ui, const game_state *oldstate,
- const game_state *newstate)
- {
- if (newstate->won)
- ui->completed = true;
- }
- static const char *current_key_label(const game_ui *ui,
- const game_state *state, int button)
- {
- int cx = ui->cur_x, cy = ui->cur_y;
- int v = state->grid[cy * state->w + cx];
- if (state->dead || state->won || !ui->cur_visible) return "";
- if (button == CURSOR_SELECT2) {
- if (v == -2) return "Mark";
- if (v == -1) return "Unmark";
- return "";
- }
- if (button == CURSOR_SELECT) {
- int dy, dx, n = 0;
- if (v == -2 || v == -3) return "Uncover";
- if (v == 0) return "";
- /* Count mine markers. */
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (cx+dx >= 0 && cx+dx < state->w &&
- cy+dy >= 0 && cy+dy < state->h) {
- if (state->grid[(cy+dy)*state->w+(cx+dx)] == -1)
- n++;
- }
- if (n == v) return "Clear";
- }
- return "";
- }
- struct game_drawstate {
- int w, h, tilesize, bg;
- bool started;
- signed char *grid;
- /*
- * Items in this `grid' array have all the same values as in
- * the game_state grid, and in addition:
- *
- * - -10 means the tile was drawn `specially' as a result of a
- * flash, so it will always need redrawing.
- *
- * - -22 and -23 mean the tile is highlighted for a possible
- * click.
- */
- int cur_x, cur_y; /* -1, -1 for no cursor displayed. */
- };
- static char *interpret_move(const game_state *from, game_ui *ui,
- const game_drawstate *ds,
- int x, int y, int button)
- {
- int cx, cy;
- char buf[256];
- if (from->dead || from->won)
- return NULL; /* no further moves permitted */
- cx = FROMCOORD(x);
- cy = FROMCOORD(y);
- if (IS_CURSOR_MOVE(button)) {
- move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, false);
- ui->cur_visible = true;
- return MOVE_UI_UPDATE;
- }
- if (IS_CURSOR_SELECT(button)) {
- int v = from->grid[ui->cur_y * from->w + ui->cur_x];
- if (!ui->cur_visible) {
- ui->cur_visible = true;
- return MOVE_UI_UPDATE;
- }
- if (button == CURSOR_SELECT2) {
- /* As for RIGHT_BUTTON; only works on covered square. */
- if (v != -2 && v != -1)
- return MOVE_NO_EFFECT;
- sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y);
- return dupstr(buf);
- }
- /* Otherwise, treat as LEFT_BUTTON, for a single square. */
- if (v == -2 || v == -3) {
- if (from->layout->mines &&
- from->layout->mines[ui->cur_y * from->w + ui->cur_x])
- ui->deaths++;
- sprintf(buf, "O%d,%d", ui->cur_x, ui->cur_y);
- return dupstr(buf);
- }
- cx = ui->cur_x; cy = ui->cur_y;
- ui->validradius = 1;
- goto uncover;
- }
- if (button == LEFT_BUTTON || button == LEFT_DRAG ||
- button == MIDDLE_BUTTON || button == MIDDLE_DRAG) {
- if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
- return MOVE_UNUSED;
- /*
- * Mouse-downs and mouse-drags just cause highlighting
- * updates.
- */
- ui->hx = cx;
- ui->hy = cy;
- ui->hradius = (from->grid[cy*from->w+cx] >= 0 ? 1 : 0);
- if (button == LEFT_BUTTON)
- ui->validradius = ui->hradius;
- else if (button == MIDDLE_BUTTON)
- ui->validradius = 1;
- ui->cur_visible = false;
- return MOVE_UI_UPDATE;
- }
- if (button == RIGHT_BUTTON) {
- if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
- return MOVE_UNUSED;
- /*
- * Right-clicking only works on a covered square, and it
- * toggles between -1 (marked as mine) and -2 (not marked
- * as mine).
- *
- * FIXME: question marks.
- */
- if (from->grid[cy * from->w + cx] != -2 &&
- from->grid[cy * from->w + cx] != -1)
- return MOVE_NO_EFFECT;
- sprintf(buf, "F%d,%d", cx, cy);
- return dupstr(buf);
- }
- if (button == LEFT_RELEASE || button == MIDDLE_RELEASE) {
- ui->hx = ui->hy = -1;
- ui->hradius = 0;
- /*
- * At this stage we must never return MOVE_UNUSED or
- * MOVE_NO_EFFECT: we have adjusted the ui, so at worst we
- * return MOVE_UI_UPDATE.
- */
- if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
- return MOVE_UI_UPDATE;
- /*
- * Left-clicking on a covered square opens a tile. Not
- * permitted if the tile is marked as a mine, for safety.
- * (Unmark it and _then_ open it.)
- */
- if (button == LEFT_RELEASE &&
- (from->grid[cy * from->w + cx] == -2 ||
- from->grid[cy * from->w + cx] == -3) &&
- ui->validradius == 0) {
- /* Check if you've killed yourself. */
- if (from->layout->mines && from->layout->mines[cy * from->w + cx])
- ui->deaths++;
- sprintf(buf, "O%d,%d", cx, cy);
- return dupstr(buf);
- }
- goto uncover;
- }
- return MOVE_UNUSED;
- uncover:
- {
- /*
- * Left-clicking or middle-clicking on an uncovered tile:
- * first we check to see if the number of mine markers
- * surrounding the tile is equal to its mine count, and if
- * so then we open all other surrounding squares.
- */
- if (from->grid[cy * from->w + cx] > 0 && ui->validradius == 1) {
- int dy, dx, n;
- /* Count mine markers. */
- n = 0;
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (cx+dx >= 0 && cx+dx < from->w &&
- cy+dy >= 0 && cy+dy < from->h) {
- if (from->grid[(cy+dy)*from->w+(cx+dx)] == -1)
- n++;
- }
- if (n == from->grid[cy * from->w + cx]) {
- /*
- * Now see if any of the squares we're clearing
- * contains a mine (which will happen iff you've
- * incorrectly marked the mines around the clicked
- * square). If so, we open _just_ those squares, to
- * reveal as little additional information as we
- * can.
- */
- char *p = buf;
- const char *sep = "";
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (cx+dx >= 0 && cx+dx < from->w &&
- cy+dy >= 0 && cy+dy < from->h) {
- if (from->grid[(cy+dy)*from->w+(cx+dx)] != -1 &&
- from->layout->mines &&
- from->layout->mines[(cy+dy)*from->w+(cx+dx)]) {
- p += sprintf(p, "%sO%d,%d", sep, cx+dx, cy+dy);
- sep = ";";
- }
- }
- if (p > buf) {
- ui->deaths++;
- } else {
- sprintf(buf, "C%d,%d", cx, cy);
- }
- return dupstr(buf);
- }
- }
- return MOVE_UI_UPDATE;
- }
- }
- static game_state *execute_move(const game_state *from, const char *move)
- {
- int cy, cx;
- game_state *ret;
- if (!strcmp(move, "S")) {
- int yy, xx;
- if (!from->layout->mines) return NULL; /* Game not started. */
- ret = dup_game(from);
- if (!ret->dead) {
- /*
- * If the player is still alive at the moment of pressing
- * Solve, expose the entire grid as if it were a completed
- * solution.
- */
- for (yy = 0; yy < ret->h; yy++)
- for (xx = 0; xx < ret->w; xx++) {
- if (ret->layout->mines[yy*ret->w+xx]) {
- ret->grid[yy*ret->w+xx] = -1;
- } else {
- int dx, dy, v;
- v = 0;
- for (dx = -1; dx <= +1; dx++)
- for (dy = -1; dy <= +1; dy++)
- if (xx+dx >= 0 && xx+dx < ret->w &&
- yy+dy >= 0 && yy+dy < ret->h &&
- ret->layout->mines[(yy+dy)*ret->w+(xx+dx)])
- v++;
- ret->grid[yy*ret->w+xx] = v;
- }
- }
- } else {
- /*
- * If the player pressed Solve _after dying_, show a full
- * corrections grid in the style of standard Minesweeper.
- * Players who don't like Mines's behaviour on death of
- * only showing the mine that killed you (so that in case
- * of a typo you can undo and carry on without the rest of
- * the grid being spoiled) can use this to get the display
- * that ordinary Minesweeper would have given them.
- */
- for (yy = 0; yy < ret->h; yy++)
- for (xx = 0; xx < ret->w; xx++) {
- int pos = yy*ret->w+xx;
- if ((ret->grid[pos] == -2 || ret->grid[pos] == -3) &&
- ret->layout->mines[pos]) {
- ret->grid[pos] = 64;
- } else if (ret->grid[pos] == -1 &&
- !ret->layout->mines[pos]) {
- ret->grid[pos] = 66;
- }
- }
- }
- ret->used_solve = true;
- return ret;
- } else {
- /* Dead players should stop trying to move. */
- if (from->dead)
- return NULL;
- ret = dup_game(from);
- while (*move) {
- if (move[0] == 'F' &&
- sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
- cx >= 0 && cx < from->w && cy >= 0 && cy < from->h &&
- (ret->grid[cy * from->w + cx] == -1 ||
- ret->grid[cy * from->w + cx] == -2)) {
- ret->grid[cy * from->w + cx] ^= (-2 ^ -1);
- } else if (move[0] == 'O' &&
- sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
- cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
- open_square(ret, cx, cy);
- } else if (move[0] == 'C' &&
- sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
- cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
- int dx, dy;
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++)
- if (cx+dx >= 0 && cx+dx < ret->w &&
- cy+dy >= 0 && cy+dy < ret->h &&
- (ret->grid[(cy+dy)*ret->w+(cx+dx)] == -2 ||
- ret->grid[(cy+dy)*ret->w+(cx+dx)] == -3))
- open_square(ret, cx+dx, cy+dy);
- } else {
- free_game(ret);
- return NULL;
- }
- while (*move && *move != ';') move++;
- if (*move) move++;
- }
- return ret;
- }
- }
- /* ----------------------------------------------------------------------
- * Drawing routines.
- */
- static void game_compute_size(const game_params *params, int tilesize,
- const game_ui *ui, int *x, int *y)
- {
- /* Ick: fake up `ds->tilesize' for macro expansion purposes */
- struct { int tilesize; } ads, *ds = &ads;
- ads.tilesize = tilesize;
- *x = BORDER * 2 + TILE_SIZE * params->w;
- *y = BORDER * 2 + TILE_SIZE * params->h;
- }
- 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_BACKGROUND2 * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 19.0F / 20.0F;
- ret[COL_BACKGROUND2 * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 19.0F / 20.0F;
- ret[COL_BACKGROUND2 * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 19.0F / 20.0F;
- ret[COL_1 * 3 + 0] = 0.0F;
- ret[COL_1 * 3 + 1] = 0.0F;
- ret[COL_1 * 3 + 2] = 1.0F;
- ret[COL_2 * 3 + 0] = 0.0F;
- ret[COL_2 * 3 + 1] = 0.5F;
- ret[COL_2 * 3 + 2] = 0.0F;
- ret[COL_3 * 3 + 0] = 1.0F;
- ret[COL_3 * 3 + 1] = 0.0F;
- ret[COL_3 * 3 + 2] = 0.0F;
- ret[COL_4 * 3 + 0] = 0.0F;
- ret[COL_4 * 3 + 1] = 0.0F;
- ret[COL_4 * 3 + 2] = 0.5F;
- ret[COL_5 * 3 + 0] = 0.5F;
- ret[COL_5 * 3 + 1] = 0.0F;
- ret[COL_5 * 3 + 2] = 0.0F;
- ret[COL_6 * 3 + 0] = 0.0F;
- ret[COL_6 * 3 + 1] = 0.5F;
- ret[COL_6 * 3 + 2] = 0.5F;
- ret[COL_7 * 3 + 0] = 0.0F;
- ret[COL_7 * 3 + 1] = 0.0F;
- ret[COL_7 * 3 + 2] = 0.0F;
- ret[COL_8 * 3 + 0] = 0.5F;
- ret[COL_8 * 3 + 1] = 0.5F;
- ret[COL_8 * 3 + 2] = 0.5F;
- ret[COL_MINE * 3 + 0] = 0.0F;
- ret[COL_MINE * 3 + 1] = 0.0F;
- ret[COL_MINE * 3 + 2] = 0.0F;
- ret[COL_BANG * 3 + 0] = 1.0F;
- ret[COL_BANG * 3 + 1] = 0.0F;
- ret[COL_BANG * 3 + 2] = 0.0F;
- ret[COL_CROSS * 3 + 0] = 1.0F;
- ret[COL_CROSS * 3 + 1] = 0.0F;
- ret[COL_CROSS * 3 + 2] = 0.0F;
- ret[COL_FLAG * 3 + 0] = 1.0F;
- ret[COL_FLAG * 3 + 1] = 0.0F;
- ret[COL_FLAG * 3 + 2] = 0.0F;
- ret[COL_FLAGBASE * 3 + 0] = 0.0F;
- ret[COL_FLAGBASE * 3 + 1] = 0.0F;
- ret[COL_FLAGBASE * 3 + 2] = 0.0F;
- ret[COL_QUERY * 3 + 0] = 0.0F;
- ret[COL_QUERY * 3 + 1] = 0.0F;
- ret[COL_QUERY * 3 + 2] = 0.0F;
- ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
- ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
- ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
- ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
- ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
- ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
- ret[COL_WRONGNUMBER * 3 + 0] = 1.0F;
- ret[COL_WRONGNUMBER * 3 + 1] = 0.6F;
- ret[COL_WRONGNUMBER * 3 + 2] = 0.6F;
- /* Red tinge to a light colour, for the cursor. */
- ret[COL_CURSOR * 3 + 0] = ret[COL_HIGHLIGHT * 3 + 0];
- ret[COL_CURSOR * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F;
- ret[COL_CURSOR * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F;
- *ncolours = NCOLOURS;
- return ret;
- }
- static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
- {
- struct game_drawstate *ds = snew(struct game_drawstate);
- ds->w = state->w;
- ds->h = state->h;
- ds->started = false;
- ds->tilesize = 0; /* not decided yet */
- ds->grid = snewn(ds->w * ds->h, signed char);
- ds->bg = -1;
- ds->cur_x = ds->cur_y = -1;
- memset(ds->grid, -99, ds->w * ds->h);
- return ds;
- }
- static void game_free_drawstate(drawing *dr, game_drawstate *ds)
- {
- sfree(ds->grid);
- sfree(ds);
- }
- static void draw_tile(drawing *dr, game_drawstate *ds,
- int x, int y, int v, int bg)
- {
- if (v < 0) {
- int coords[12];
- int hl = 0;
- if (v == -22 || v == -23) {
- v += 20;
- /*
- * Omit the highlights in this case.
- */
- draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
- bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg);
- draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
- draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
- } else {
- /*
- * Draw highlights to indicate the square is covered.
- */
- coords[0] = x + TILE_SIZE - 1;
- coords[1] = y + TILE_SIZE - 1;
- coords[2] = x + TILE_SIZE - 1;
- coords[3] = y;
- coords[4] = x;
- coords[5] = y + TILE_SIZE - 1;
- draw_polygon(dr, coords, 3, COL_LOWLIGHT ^ hl, COL_LOWLIGHT ^ hl);
- coords[0] = x;
- coords[1] = y;
- draw_polygon(dr, coords, 3, COL_HIGHLIGHT ^ hl,
- COL_HIGHLIGHT ^ hl);
- draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
- TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
- bg);
- }
- if (v == -1) {
- /*
- * Draw a flag.
- */
- #define SETCOORD(n, dx, dy) do { \
- coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \
- coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \
- } while (0)
- SETCOORD(0, 0.6F, 0.35F);
- SETCOORD(1, 0.6F, 0.7F);
- SETCOORD(2, 0.8F, 0.8F);
- SETCOORD(3, 0.25F, 0.8F);
- SETCOORD(4, 0.55F, 0.7F);
- SETCOORD(5, 0.55F, 0.35F);
- draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
- SETCOORD(0, 0.6F, 0.2F);
- SETCOORD(1, 0.6F, 0.5F);
- SETCOORD(2, 0.2F, 0.35F);
- draw_polygon(dr, coords, 3, COL_FLAG, COL_FLAG);
- #undef SETCOORD
- } else if (v == -3) {
- /*
- * Draw a question mark.
- */
- draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
- FONT_VARIABLE, TILE_SIZE * 6 / 8,
- ALIGN_VCENTRE | ALIGN_HCENTRE,
- COL_QUERY, "?");
- }
- } else {
- /*
- * Clear the square to the background colour, and draw thin
- * grid lines along the top and left.
- *
- * Exception is that for value 65 (mine we've just trodden
- * on), we clear the square to COL_BANG.
- */
- if (v & 32) {
- bg = COL_WRONGNUMBER;
- v &= ~32;
- }
- draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
- (v == 65 ? COL_BANG :
- bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg));
- draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
- draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
- if (v > 0 && v <= 8) {
- /*
- * Mark a number.
- */
- char str[2];
- str[0] = v + '0';
- str[1] = '\0';
- draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
- FONT_VARIABLE, TILE_SIZE * 7 / 8,
- ALIGN_VCENTRE | ALIGN_HCENTRE,
- (COL_1 - 1) + v, str);
- } else if (v >= 64) {
- /*
- * Mark a mine.
- */
- {
- int cx = x + TILE_SIZE / 2;
- int cy = y + TILE_SIZE / 2;
- int r = TILE_SIZE / 2 - 3;
- draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE);
- draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE);
- draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE);
- draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT);
- }
- if (v == 66) {
- /*
- * Cross through the mine.
- */
- int dx;
- for (dx = -1; dx <= +1; dx++) {
- draw_line(dr, x + 3 + dx, y + 2,
- x + TILE_SIZE - 3 + dx,
- y + TILE_SIZE - 2, COL_CROSS);
- draw_line(dr, x + TILE_SIZE - 3 + dx, y + 2,
- x + 3 + dx, y + TILE_SIZE - 2,
- COL_CROSS);
- }
- }
- }
- }
- draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
- }
- 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 x, y;
- int mines, markers, closed, bg;
- int cx = -1, cy = -1;
- bool cmoved;
- if (flashtime) {
- int frame = (int)(flashtime / FLASH_FRAME);
- if (frame % 2)
- bg = (ui->flash_is_death ? COL_BACKGROUND : COL_LOWLIGHT);
- else
- bg = (ui->flash_is_death ? COL_BANG : COL_HIGHLIGHT);
- } else
- bg = COL_BACKGROUND;
- if (!ds->started) {
- int coords[10];
- /*
- * Recessed area containing the whole puzzle.
- */
- coords[0] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1;
- coords[1] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1;
- coords[2] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1;
- coords[3] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
- coords[4] = coords[2] - TILE_SIZE;
- coords[5] = coords[3] + TILE_SIZE;
- coords[8] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
- coords[9] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1;
- coords[6] = coords[8] + TILE_SIZE;
- coords[7] = coords[9] - TILE_SIZE;
- draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
- coords[1] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
- coords[0] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
- draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
- ds->started = true;
- }
- if (ui->cur_visible) cx = ui->cur_x;
- if (ui->cur_visible) cy = ui->cur_y;
- cmoved = (cx != ds->cur_x || cy != ds->cur_y);
- /*
- * Now draw the tiles. Also in this loop, count up the number
- * of mines, mine markers, and closed squares.
- */
- mines = markers = closed = 0;
- for (y = 0; y < ds->h; y++)
- for (x = 0; x < ds->w; x++) {
- int v = state->grid[y*ds->w+x];
- bool cc = false;
- if (v < 0)
- closed++;
- if (v == -1)
- markers++;
- if (state->layout->mines && state->layout->mines[y*ds->w+x])
- mines++;
- if (v >= 0 && v <= 8) {
- /*
- * Count up the flags around this tile, and if
- * there are too _many_, highlight the tile.
- */
- int dx, dy, flags = 0;
- for (dy = -1; dy <= +1; dy++)
- for (dx = -1; dx <= +1; dx++) {
- int nx = x+dx, ny = y+dy;
- if (nx >= 0 && nx < ds->w &&
- ny >= 0 && ny < ds->h &&
- state->grid[ny*ds->w+nx] == -1)
- flags++;
- }
- if (flags > v)
- v |= 32;
- }
- if ((v == -2 || v == -3) &&
- (abs(x-ui->hx) <= ui->hradius && abs(y-ui->hy) <= ui->hradius))
- v -= 20;
- if (cmoved && /* if cursor has moved, force redraw of curr and prev pos */
- ((x == cx && y == cy) || (x == ds->cur_x && y == ds->cur_y)))
- cc = true;
- if (ds->grid[y*ds->w+x] != v || bg != ds->bg || cc) {
- draw_tile(dr, ds, COORD(x), COORD(y), v,
- (x == cx && y == cy) ? COL_CURSOR : bg);
- ds->grid[y*ds->w+x] = v;
- }
- }
- ds->bg = bg;
- ds->cur_x = cx; ds->cur_y = cy;
- if (!state->layout->mines)
- mines = state->layout->n;
- /*
- * Update the status bar.
- */
- {
- char statusbar[512];
- if (state->dead) {
- sprintf(statusbar, "DEAD!");
- } else if (state->won) {
- if (state->used_solve)
- sprintf(statusbar, "Auto-solved.");
- else
- sprintf(statusbar, "COMPLETED!");
- } else {
- int safe_closed = closed - mines;
- sprintf(statusbar, "Marked: %d / %d", markers, mines);
- if (safe_closed > 0 && safe_closed <= 9) {
- /*
- * In the situation where there's a very small number
- * of _non_-mine squares left unopened, it's helpful
- * to mention that number in the status line, to save
- * the player from having to count it up
- * painstakingly. This is particularly important if
- * the player has turned up the mine density to the
- * point where game generation resorts to its weird
- * pathological fallback of a very dense mine area
- * with a clearing in the middle, because that often
- * leads to a deduction you can only make by knowing
- * that there is (say) exactly one non-mine square to
- * find, and it's a real pain to have to count up two
- * large numbers of squares and subtract them to get
- * that value of 1.
- *
- * The threshold value of 8 for displaying this
- * information is because that's the largest number of
- * non-mine squares that might conceivably fit around
- * a single central square, and the most likely way to
- * _use_ this information is to observe that if all
- * the remaining safe squares are adjacent to _this_
- * square then everything else can be immediately
- * flagged as a mine.
- */
- if (safe_closed == 1) {
- sprintf(statusbar + strlen(statusbar),
- " (1 safe square remains)");
- } else {
- sprintf(statusbar + strlen(statusbar),
- " (%d safe squares remain)", safe_closed);
- }
- }
- }
- if (ui->deaths)
- sprintf(statusbar + strlen(statusbar),
- " Deaths: %d", ui->deaths);
- status_bar(dr, statusbar);
- }
- }
- 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->used_solve || newstate->used_solve)
- return 0.0F;
- if (dir > 0 && !oldstate->dead && !oldstate->won) {
- if (newstate->dead) {
- ui->flash_is_death = true;
- return 3 * FLASH_FRAME;
- }
- if (newstate->won) {
- ui->flash_is_death = false;
- return 2 * FLASH_FRAME;
- }
- }
- 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 = COORD(ui->cur_x);
- *y = COORD(ui->cur_y);
- *w = *h = TILE_SIZE;
- }
- }
- static int game_status(const game_state *state)
- {
- /*
- * We report the game as lost only if the player has used the
- * Solve function to reveal all the mines. Otherwise, we assume
- * they'll undo and continue play.
- */
- return state->won ? (state->used_solve ? -1 : +1) : 0;
- }
- static bool game_timing_state(const game_state *state, game_ui *ui)
- {
- if (state->dead || state->won || ui->completed || !state->layout->mines)
- return false;
- return true;
- }
- #ifdef COMBINED
- #define thegame mines
- #endif
- const struct game thegame = {
- "Mines", "games.mines", "mines",
- 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,
- encode_ui,
- decode_ui,
- NULL, /* game_request_keys */
- game_changed_state,
- current_key_label,
- interpret_move,
- execute_move,
- PREFERRED_TILE_SIZE, 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,
- false, false, NULL, NULL, /* print_size, print */
- true, /* wants_statusbar */
- true, game_timing_state,
- BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON,
- };
- #ifdef STANDALONE_OBFUSCATOR
- /*
- * Vaguely useful stand-alone program which translates between
- * obfuscated and clear Mines game descriptions. Pass in a game
- * description on the command line, and if it's clear it will be
- * obfuscated and vice versa. The output text should also be a
- * valid game ID describing the same game. Like this:
- *
- * $ ./mineobfusc 9x9:4,4,mb071b49fbd1cb6a0d5868
- * 9x9:4,4,004000007c00010022080
- * $ ./mineobfusc 9x9:4,4,004000007c00010022080
- * 9x9:4,4,mb071b49fbd1cb6a0d5868
- */
- int main(int argc, char **argv)
- {
- game_params *p;
- game_state *s;
- char *id = NULL, *desc;
- const char *err;
- int y, x;
- while (--argc > 0) {
- char *p = *++argv;
- if (*p == '-') {
- fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
- return 1;
- } else {
- id = p;
- }
- }
- if (!id) {
- fprintf(stderr, "usage: %s <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);
- x = atoi(desc);
- while (*desc && *desc != ',') desc++;
- if (*desc) desc++;
- y = atoi(desc);
- while (*desc && *desc != ',') desc++;
- if (*desc) desc++;
- printf("%s:%s\n", id, describe_layout(s->layout->mines,
- p->w * p->h,
- x, y,
- (*desc != 'm')));
- return 0;
- }
- #endif
- /* vim: set shiftwidth=4 tabstop=8: */
|