diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..779f99a12b5c71692ad6d6908d6d2fc3bdba485d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/android/gradle.properties b/android/gradle.properties index 2bfb2682ee7566b8366b9e1f2acae8cdd80b55ac..a390467502f50e6247a22ba092f145f009ade412 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.1.3 -app.versionCode=52 +app.versionName=0.1.4 +app.versionCode=53 diff --git a/assets/files/templates.json b/assets/files/templates.json index ca12c150946a42156f69feb606fda3bb35a64534..3a37ebd141958e36c6ccbb97e146eacae2f75497 100644 --- a/assets/files/templates.json +++ b/assets/files/templates.json @@ -2,308 +2,353 @@ "templates": { "2x2": { "easy": [ - "0000300200000423", - "0002004010003200", - "0002200432000100", - "0010000010300400", - "0040010200230304", - "0040401301003000", - "0120024020100000", - "0200400324300040", - "0200430020000040", - "0201014200031304", - "0310004310300000", - "0400000241230200", - "0401000440000143", - "1000021004000040", - "1004003004000043", - "2003042000020000", - "2004340000030010", - "3001000000004020", - "3200002304002310", - "3400000042000302" + "4020204332141032", + "0302043100133124", + "3240412303000432", + "4231014200230304", + "0310012332001032", + "2300143041200201", + "2401310042130020", + "3040142020044302", + "4031304020131004", + "4231030401400402", + "4302120004210103", + "0000421324010102", + "0010310042301320", + "0302420021340400", + "2400132002030102", + "3204002023004032", + "4013310010420001", + "4132030102001003", + "0000302110404210", + "0001300002131302" ], "medium": [ - "0000104000000302", - "0000200000014003", - "0000420000403000", - "0003400010020000", - "0004300002004002", - "0020000010003001", - "0030000030100200", - "0030000040003002", - "0030020000004001", - "0120000000000043", - "0204000030010000", - "1000000240000003", - "2000000201000030", - "2000100400030000", - "2001040000000030", - "2100400000100040", - "4000000302000001", - "4000010002100000", - "4010030400003000", - "4030000001040000" + "2403000000201000", + "0000001004323000", + "0000103000203004", + "0000201010030040", + "0001403000130000", + "0002030101030000", + "0003004041002000", + "0003204030000400", + "0004003003022000", + "0004100040200200", + "0004402010000200", + "0021000040002030", + "0024000030000403", + "0024000030004100", + "0030010000000314", + "0032000003404000", + "0102001000204000", + "0140000003041000", + "0203000101004000", + "0300120000020040" ], "hard": [ - "0000004302004020", - "0000020140000100", - "0000040000200310", - "0000204001200004", - "0001000030202003", - "0003000130004000", - "0003002042000100", - "0003400002000020", - "0004100000303001", - "0020300100001000", - "0100000200030400", - "0204401000000100", - "0430010000000004", - "1000004000033010", - "1040040200000020", - "2000400100030000", - "3000021000200000", - "3002000040000001", - "3004002014000000", - "4200100000010030" + "1000040300200000", + "1000200000020003", + "1000300200040000", + "1000400000200010", + "1000400100000020", + "1400000030200000", + "2000001000403000", + "2000400100400000", + "2000400300000040", + "2030002000000100", + "2100000000300200", + "3000002000001040", + "3002000000140000", + "3002040000030000", + "3100000000000204", + "4000000000302010", + "4000000020010003", + "4000000200300300", + "4000300200000030", + "4000300400000020" ], "nightmare": [ - "0000000301020301", - "0000020000010420", - "0000042000001300", - "0000100302040000", - "0000200331020000", - "0001004041002000", - "0003001001200000", - "0004403000233000", - "0010000010400200", - "0010400000000302", - "0012000000010300", - "0020030102001000", - "0020300000020400", - "0100000200030040", - "0104000000002003", - "0200040000200010", - "0240000010000003", - "0301000000030020", - "3000000423000000", - "3000001020000400" + "1000200300040000", + "1002000300004000", + "1004000200003000", + "1400000000002030", + "2000100200000030", + "2000104000300000", + "2001030000020000", + "2010000000000403", + "2010000043000000", + "2030000000004001", + "2300000010000002", + "3000002000040100", + "3001000000002400", + "3002040000000003", + "3020000000000403", + "4000003000101000", + "4000031000300000", + "4000204000000003", + "4002000000000103", + "4300000000200400" ] }, "3x2": { "easy": [ - "000004040010054120301006000230213460", - "000560050030016000000002500306403100", - "004126012034100352300600401060200000", - "006400450060310520060300230004640035", - "016200204060500002000504600000001006", - "020016060040005460000500450602602000", - "020300035421213504400130060200000003", - "030000150036200143300265000021021000", - "143506006014320061000200030000000050", - "154236230145420503503420345000012004", - "300100561340006200430010000003003020", - "300152052040004030620010465300030064", - "350041004250500030243165000006005004", - "405003000000610032500006000320002651", - "420053530040015064003025052006004010", - "450200023600264510531060610304345126", - "500000260001403106100045645013000050", - "600432000060126054030200300041410020" + "425361136520241053003142350016610230", + "123405506012002530354126230641461050", + "215460640001050612160350501246426035", + "143560526041410023362054201030034010", + "314065056314461023500106030051005632", + "620000305106504362263014136005052631", + "015204064315400051106003523040041532", + "020306346152050010614200061403432061", + "065400342000534201620304206143403605", + "305010421635106503530401650002204006", + "342016106003403650020301064100531064", + "413625265300120403050010002164600502", + "512406300512200054030620154200603140", + "000040540231461050230164154020600015", + "040203032045050026261534600052420001", + "160052020060536010240035050320312540", + "210036630050542603063024300145400060", + "241530500004050312002405400253025601", + "301500050040432600610230560123123400", + "614205003016040301136542400003060054" ], "medium": [ - "000003400060005001020000006150100020", - "004600200014000430000000305100610003", - "005000600100064005030200000630000000", - "010062000130100320035006053000000000", - "020014000600000003000540530400200000", - "023000010000000400301002002106005240", - "025461000000512000400002004000000506", - "032005004006005004100600000000000400", - "050010601005004000200000000003123400", - "060503000002002050000400620100014200", - "200400000060001000000650650040042000", - "240005100200000620004300502060403500", - "261005400120010030002050123000000000", - "400200002060030104504300000030000040", - "400501500340100000003600010050302000", - "603250200000005300100000000046006002", - "610000530200050030000006300600000302", - "620030540001200610000000000005005100" + "050000000251001300040102410500300004", + "460001005042000153000000530004620000", + "000000500063040256060000423005100002", + "004600000000102300506420050060021500", + "005000041000500120210503400005003002", + "005000300004000006003020002015150243", + "120000540000250064600100000020002015", + "200010400600001520500160000040040206", + "450300300240020000040502200006600020", + "504000310400043050100000030506060001", + "002006000502325400140300050000200004", + "600013000050000100025060060230201500", + "000040100523020000600050002100301002", + "000560005040000230600015530000410000", + "040035000106520000300004062000405000", + "050010000003104300020060005100001230", + "056002200005002001400050000600023010", + "060000010062026310050006005400000003", + "461000000001010520602000003260000100", + "500040146035450000001000005000030100" ], "hard": [ - "000000500006403210002064000000200400", - "000000600324002460000010003000015630", - "000001301006204603000050500000006100", - "000003000126000060000501460200052610", - "000006206015052004000000135000000000", - "000100000004006005024000400020060030", - "001005020006002100030000510600040001", - "003250005063100020300000000100060000", - "004010002450000530056000205000010000", - "005300002040000060200000060200403650", - "006512010006600000000065200004000230", - "024006000000001054006000000400405120", - "060053450200000504000000100300040001", - "060300504600000510001004020100000032", - "062003000201004302000050300600400000", - "100005000040420006600003014000000030", - "340000600000500021000003050300000206", - "401050000030520600040000000460600000", - "600000400010000005302000003100504002" + "060210050300003450000000020100001000", + "104020002000000200013050500301000000", + "210000004300020540000001060000001400", + "300260500000002006000504000001010040", + "400000001640000002500034100000003010", + "400610200340004001000200050000300000", + "500100000260000004052000000030100450", + "502000000000004360000005030240006050", + "600001042006000000060003000500001604", + "602000000004000163010000501400000001", + "000000602000000005401300300621000030", + "000006100004020030000061030102005000", + "000020000615150000003000046003000400", + "000046000000206004003000100032000601", + "000400000003000006410500003650065000", + "000500050601003006040100004000300400", + "003000000602000001000250300005420003", + "006401000500000300100000020050001603", + "010000006000302400000000403100160003", + "010403000001040002000500300000065200" ], "nightmare": [ - "000300500140120006030500601000000000", - "000600650400060000430000300105012000", - "000650056000004010000340401005305000", - "001000060005600030305060500140102356", - "001006006530002140000000000260060005", - "001540002306000400160000000004003000", - "006000320000000510000436000150103000", - "006010040206050001001005204000010400", - "006302205406300501000000502003040000", - "006500012600031006000120000001000300", - "020016064030005064006200000601000000", - "042300060024010405004003006000000000", - "060050001000200100100040002304304006", - "100025000060003210002054000000350000", - "205004001500040600000003000000050140", - "260100000000010006300020001004050000", - "640100001000020001064000000000410263" + "000006020500050003100002300000500600", + "000060600500000040031000025000060003", + "001024000300630000000003000000065001", + "003000000005060010100402014000000001", + "004000063000600002000050001030200004", + "004001000600200050041030020000100000", + "006000040100002003600002000536000000", + "006100000030000600000040054300030050", + "030045020000006000003010500002000006", + "060000000503604000000002105030000001", + "060000102050000042000010005000000034", + "305004000010000200050000046050000002", + "400000001032000020000500030040500001", + "406300300100000000000513004006000000", + "502010000004000000010020030000100540", + "600000050000006004200005000150010060", + "600010003000065000000000200106000520", + "000000005002000000431000002040000305", + "003001020000400005060400600000001000", + "200016000000006000000002000500604300" ] }, "3x3": { "easy": [ - "000000308038402076629300150064700800000001040015004039040050203000000500053087091", - "000060200203000806856930000782603015039054728045020690020010570560040082074080900", - "000800060030104005004573082026418057009000010400057230090705021870021000100000570", - "004690710800231045003705809936027581582319476001856302079082164615970238420163950", - "007005028300089100400007900203800006598246713716093402029150000070900841000000000", - "008072040479000008253001679041500006800300590690200480780006050030840007006029004", - "008630027013900000406800350607090085000270030530006091060008072000329500000000840", - "064305092050029000298000105500006014002904000647000280085090041901048573076531008", - "070950803823647000560310702400096007218000900706201000002069078607425301940083600", - "100870920008029064502060803670401382913280457284300691327910546459630718801740209", - "204980650306205089058376140000810004500700306040003008723108000861452907005030021", - "390700008064392750072405630040059803600100205018004000720060084050800000083040060", - "452380100018026057706109208001000023284700690570062000160870002320090500800210076", - "500090030960150820010076400630827041000305070027000563000040380086500019200701000", - "604570381950001040080302050890407263345000000206100405068214530002605078519730624", - "610500008040006000530080200390718002872405319056093784705000923003800601000032000", - "897245306065081904130796258742638190306409082589000040608900531403002069951803027", - "908150624006803715105426309050702460342560971017000052701249530293605147500001006" + "751283946482769351936451827629074138045398672378610095217046583803025764564837210", + "560810042803764150010320876438671025256400731791502684379006518645187093182953467", + "054283906007915032239674001390167008745020169601459327923846715406501203518732694", + "840257069026039478000846302362510907700362540154098603235681794017920836680473215", + "281600947960271508070894012010946375496730081530128496629017853140082009750069124", + "710290485490517602260384970047650298829470100651928743986045307170809504504702809", + "070081395814035620953276814200158743045369081138704069060847152521000070487512030", + "401975026060128309289306175305009714790512083816030592143000968972063451650400237", + "100398465305104800890002371632700598009603710471985206253410907746039102918057643", + "548009210910426005007581394401963782826174950379850460105007600694218537000695108", + "010428009920653800480017623863079245594206730271534960700860300008042190102795486", + "459203017387415290000798054062307900731984600040020030674139082198572403523800179", + "700009816980016720651872409460208351025347608308165004006924187814050002279081043", + "400057210728031459500402670000389026800165934963720085317900560284006301609203847", + "050980043197643285048572196589307402000000010710068359435120900971835020820700031", + "410000050926570108350410729030045960895067402004032810271308604083094271049720583", + "597631004128907305600508179002059046480763002906200730735492618204806053000010427", + "020359174035187060791406800000534006600802401300761598268910347510600082073048610", + "028569470043180650507204018800400720090608031200703006480070195015942387079805264", + "095007641640589020073006859104203590307095006580614702950060070418730960736001084" ], "medium": [ - "000000706060001208308406005036100000890352600150000004010094080080010460073008090", - "000020008000408605070056000000007103760105200810000000037509000059012806002603070", - "010309060020050100906000024800000300005097000100060980453200690000910000291400000", - "020400105043600700090008030004000000000904573306700000400270306000586007500040982", - "050070149009000000071508000820000950937000061005907080603150000010340805504080600", - "070200068053600140009000000006920501002158070045700289698500007510090826007000095", - "308910504070050090060004000000000053000070002000005701080000300205000069794060010", - "400908060029060040086000705000001000030506800000400000000600304503100620907043500", - "439700000582091700000480005003200160000300097901500030016030000040802300098050006", - "490750800630080275080000140100590684000613002700000500048205961070000000000460027", - "609000040053001000107209000001000087270000000006030104790305006000090002400607308", - "700301000000600080320048100050079200800000000073005019502004070098007020010080900", - "706090003000050000039200058090081046401000800005070009000903002912040307000700900" + "043700080007085321000360700500890070080547263024610058405130890208074036369058010", + "060300010549000026071900548654130709093460050102570604425603870800050200907201405", + "304008765859000300627453000793800002000100430146325007275030640038060001401580973", + "475631002016940300903870014080106740040003006702584139004009078090008401608417900", + "020196030790080206306702100070430028060020301083671050807300060012057093935264010", + "280315009360070500540869320070000031026138975138050200700500692650001700093720004", + "574023098239080064106947003860204031000000605340006280412309050000062010093805470", + "602503010030740080417086003854012006096070501020605000279008164040160278008427005", + "703004058090065047840070190671509800000406725254300619026908401010040563000600902", + "920451030045026079061080045170208400294500600008040912412070308050830001703160090", + "014095070020140050635280140700514900000903867093608500002439785300050002070860430", + "207006158891574620000000700028030471053420900900081205002040569039100047486700300", + "304027095501043200200000010892070140615400720000281956006010472708002501123050800", + "320608047700420010060197000573001008000530071416089020008302000297815364630970002", + "905624083000038070320001406457092001209500748100070009092147360736000910510360000", + "147080526030070000980254071300460907004013602008705010462897005859000004700046200", + "470308605950260304003050000200005070005603142300192506530000000104586709086034251", + "804250931090830046170040582020080400000702059300094020010520094508610003002408165", + "843106057126050400700004060580600721001205094279010036300001005000902018918503600", + "946010350001000002730040186250481093090700020604030875028000500105608234060023708" ], "hard": [ - "000006000013700000004082500000000600090300807208010005006070009001903008400865030", - "000100000000876493008000027087901000932060010600003000100389076000025000200017840", - "000620500001500027003000004400870102006401708100950000300009670000000905040000003", - "000800000503970014801005009000390407000000200039064000284009050005742600370580000", - "010365009320079000005001000874600103250000600003090004500748000000020401092006030", - "900007000510390604060502301280079400000423060400060709600900807070000000000000045", - "920031040000000018050006720080014203400000050237085004810253407364090002000068930" + "000050000020190070036740000060400800503008704004009100601900230209007600008020917", + "504700003810093200023061800701026009002300008380000501000030000049100050208000090", + "006300809780590023003008670172849365000050000050000007000080730000205010000060090", + "050040107900817605071600900000060700000080003300002500020401006607200409090036800", + "070408000001000900836010020007002830100500762000607100760000210508701003003000079", + "083009001651000008700518000007986100860000429019000006500040000906053010000007300", + "200568703009173004003200000907000512000005400032700800080000001300600240720410000", + "370280106009700020000000073068070000590430060700010490850090000930108507046000000", + "509040078401600020700005600070000460180070500056412300042000800890000003005008700", + "635000010017030040804000000963100200000042090040000871001053960000007400006429030", + "470350009000008247809470500000000726000926850000700000708000105305810902000000070", + "600905030200067800097400020024050001000080950809010003000096002480300019030040700", + "140000083027005140600000900700236051403050069000014000000700002000090610094000530", + "700520400001708000060000100050000678000480210608005000000130894300040000149057002", + "907000060620000501043069008070004000090057000400908070350000010060091052100006904", + "513740600020160005000080300700000000458900026000054097004000060082006500000400738", + "000000200000300081200080396071020038090400107000003509000034000807209000023750810", + "000782030214305090000109500058201000439600000007000600300410007900000403700003002", + "003900000047080000956002378000065014800730500005200000000129000090070420021300060", + "005093100690080740128060000000008000061400050709600010904007020080002007050000481" ], "nightmare": [ - "000007958004080060800090073210504000600800000070060000060908030000630590930040000", - "000867439090000000006394010103245008050600070269700000810000390605030000000028051", - "001079583205400000708601200000740150012005007060012090000000400370100000850000060", - "002080507100000000000002300219806740580009600060004800620040908700060000801205003", - "037009008002000006568300020090010045003007001000593000000030502000100400009000063", - "038200007240001890070405020002003000013000580000008970507000040000090005069507230", - "050809003000603500732004060000090801097300004128006070340200100201900000900000730", - "060070900040009500530086024010003097354007010700640000026300000003518000005000009", - "200460300000002009300090010053009006008500000020040705016203040049600020002080050", - "300000004000060000020400679030906020108024003206003145000050080503070401002049007", - "940008060006000729000006040025000000010000030400021980000009800760850402508702300" + "058001093200030004000200071905420000000007050000100000080304060703900005006810700", + "060140000720000000040875060070000493092007800004050000000090740000060025058004001", + "068070000003004000000180370900407601000901007000030040009043108057000000030602090", + "005208010000000809128690004000020000006900430009040208080014000004002700000009600", + "009007400030600000000485000200030107000070020170204600546700092800000040010000050", + "020500603000300004009024750050200006008000200012603000034000000507000001280900070", + "090304005000200490050086302300009250900000060070050130063000710020000500010000000", + "390007801508360020040950607000000000800003910700040005000030700010000086000200040", + "012034050000009640030600000784001300003080060000300078000002030005006000068000907", + "000000000050078200000590870600200100007004500004003008500000723040000006276030401", + "002000008070040031500890007009200000000503040040000150610000000000368700780150003", + "800000650300000080005007002108790400600050100052040060003000200000019000500003748", + "800004305000000790001000000010805000090100000200600184700200041402006070000037026", + "000000012240601009000300050600100020000700003320040605460008001900000008000036900", + "000409003750060040000000206000031079000000832500702600400007000000314060006000001", + "000000500009106430060090000008000050004000982300980007000035610017000090000000270", + "073002600200105008000000000729306100040000900000008004000003000007560001098200006", + "800003000050001079006079003000200001900700060000010305032090506000002000087000040", + "000800000039605400008000603005390000700006000380170004002900100004020306010000000", + "200000090500206700816700003400100000000050030900084520000000807000620050000000062" ] }, "4x4": { "easy": [ - "004AG065287001BD00G000BE4D0F70C0D00040C00010360E5F021D00A00E49G8B9E0A1G03280C704G4AC57926BF0DE80827000D00AE00B00605000E47C0GA02143FDCG0190002A70702064FBD1080G3C956GD07000400000C01B0529F7G00D400134EBAC80D00267A005704G0F208CE92G069F5000C7001AEC072618B5A0G4DF", - "006003500E0800G0003FA8200009001C0AC50D90420030070EBG0F6003A1520470006A400D0C08FEF2000E009G000A6330080B0CA016G0D00DA60905F800107B0089ECDA060F703006000470E135090A0F200130790040B0E370900F0004000000G0B5E41C00000F600387020F40EB0DD5FE0GC08B6A27011042F6A0300E8GC0", - "02E6G00530B7F0890514860E0DG27C0AF7D9000208E016G38CB00A97146F2D050697E0B002300804BA4E19237080DGC0CD20745GA00631FE5F31680C0040B0026EADC37000F092181B00506029D300074G03F2190A0E6B00290FDG8B0710E03C03009C4D01AG057001C0070603584E00D4G50F00BE7CA0607800A5004629C30G", - "041050D000F0CAG00520BA0F107G000003AG01806DB5924F0070004G009A5DB10050C0G4008DE0A0A800D0B10963GF70200B000950006800D00000600A00B00070BA9C05G640D300500E1B3D00000GF0F030G4A0D5C8172B4GD10000B00F05000090867C0GA40BEDEBFC0G0002070018G0070E03C050F09A0A42FD9B00E0005G", - "0809BE6F0D37AGC4B1000ADG6482090FFGAD90000BCE010274C01520F0A0DB00107029E0304B0D05E90A000C056F02075D3000487E0GC69A8640705029DCF0130A600000DF20B70E9B0E0F0000538CD10FD80102G700300600176430E00820F9D09040CBA8E50F0G478GD0000100E30060F5G0A1C27D004BA0B007FE93G4050D", - "10GC705F2D390068A050E0G8000F70D3E0091D3A060000004D379B620GEA0F5CF612CED0030004000900A2000800613DGED30180FC0000000008000019000CF034A0B570900GF602BC91364E00F2D8GA0G00F0AC4B0D0E01D000201G30005B9420BG50E7DF90C006030D8G04CE0002100F8AD3C062010500C5046F0107A38DB9", - "24F539A870DE0610C100507098A0D2E3307GCE0010628095E98D2F60B00000A70BC000F4017A9080F61A80005E007DC47G030AD6008C2B50D000750B00F60000430197050D28CABE00506B00E0C300DG0EA7D80CG501640200DCA02E0B070809000FG0030700002A100B020AC6E439000C00BD1000G90008073800092F5D010B", - "28EF46090DA531C0534AC01G709F000DGCD6FB3510420E7A01B920A83GE0F546D610A09E507G0CBF4FAE10B7028960G097GC0400EBFA5812320B0C8F410009E71A8GD06B2000700004607AF0G5000290EB00004CF060GAD1F5978GE0DA1B0634AEF400G060B027856905E82DA4G703FB0D0367C48F01EGA08G00BF0A9003D40C", - "36FC1705004DAB82G1E5A20B00039F4DA0089ED40G0253C1D429C08F0B007EG09CD7G64AE350281F5BG00D9C00010703804105E2G0AFBC09006A0831C7D9G4051902E457DF6G0A0B080D0009000CF05G0036DGC8152AE0047G542AF3B9E0100C2D8G5C76FA0431B0001B0920080ED500C5730F0E0DB04G9A4E0F3BAD5107C628", - "4000DG59C000A00F15C0000E490FD00B0F00C023681B549G8020000BD5GA6C7E2D7080G05E014000B1400D900FA7G8005G0F620A00B01ED79C00040180D0BA050A0C706G1D4529E80952480D0003701A0ED00000F00C0BG600G100E070960054003720B801C0F50DC2FE3906B75D0040A00051D40G0007BCDB050CF0E00800A0", - "480E01CD67G32B000A0D0065C091G0700G00000705B080D65730G40200A00E003070A609G200B8100F007008000AE50G10EG0F4B780C000A65802E010030C04D700F80540300AGE0AB4030EG01C9D0F000D509268AFG4CB026G1CD0AE00059308DA0600E0C04000B900C0G70060D005E0E60D01C0F500A800407B083200E0D9C", - "59A2600E0300F07BF6C098127B4AD0030G083CFD19E0A426001DB5A70F2680C0006A09540007CEBF72E0063A540C000D0034DF7CB6000200C0052EBG9001400705D672084EB000001F80000B079D204500B7004581020000204900D6005G7B08002FAG83E01B060000G10B0908D00750D003070FAC6408108B0CED6120093FA0", - "5A0000C0D0200300F300E0700B80AD2500C70050000080G000263D0A570410BF00G20F6803DCB74E0F000207400A3861AB800E0G000000CD00740C008000GA5201FEG020A04056907G00D0AEB050248CC4D805012006EGABB0A06480GC70D0036E4A08B0109072DG0000F006E0A0C030000000ED6800000000B10A9502CD6FE8", - "607E0DC95100A340D90GA001C00370F80AF03G026408D1E92310F078090A006C70G3000010C0F4A6000A0F06E0240B130B26DC1000008007F1548B0A006000CD570F109D2B063C0G02010007800C96000400000G3000007A00D0C8000F07425BG69D004C0A80E03FB0C8000000016D000E37028B460D0A9040A0600F7030G0B0", - "60E70C20038A000D029008F00754A000418093AGE02005BCAG0CD740B1F9028EC002000458A7BED35E6B0D0040C3G01F8001CB030FG069000D0GAF9E2000C050060405000AEG03C0100D02090070EB0408053E7C004B1D60E00060BA0C3DF8200030B1E006D00G09000A09327EB58601942E706D0G105C0B0F008G05349200E0", - "68024GA0CD000790FAE9681C4G57B0D0C005DE0700F9G6A80D7G29FB0A804C1504D0BF23189A750C7030A1C6D4E00G8B0090G450F37B00ED5B81709E0C2GAF4300009BEG0F1C0D5406FC537DA90008BG8E0B00400000C970495D80607B00002F0F2015B0G6A0040090CA070000BF0206B003008F27D01009D71436009E0050F0", - "703G0AD0C8E0014B090BC0E0D034G00085DC0F4B02000E370E04120G9075D0000A900520300100BC4CG006F0090E70D20D57000E00F0390100EF00900DA00804G0C39E0FB0601000E0800C01F007060G51B2G470E0D000A8006008B2G14A00E5281600G04EC0A070C00E00601A038D5003000D100F0B6GCEA0F0E08C7G560019", - "70A10426DE8FB0G0D8F05BCA231G470626G0D3704C0B810ACB4501F89A7623EDE01039070D24GA084A63000257G0F009BG000A4FC931D25E092D0G8BE6FA0417A00078003G00104F6DC01E34AF97582B0004006000B0AGD30200B5A0014DE97C3F9082D5006C0EB007E200B0F0D0053485D047091B03000210BC06E370000D0G", - "A3FB2CD8G41006902008A619C537BE0G5C670BEG829FD1A49E0074006ADB23C8F5CA83204EB60D1909G20D5A37C8400F07ED0G40F95A02836834C00BD021E0570F0E400C508D39B0D17CF20E0349A5060B050963A1E2FC7D3609D5B17FG084E072B65E3F98A01G0C80419A02EC6G5000CA90B8G00DF5672E0G0F61C00B73984A", - "ADC0263F00G00450086000G009500FA2F0G970B5C42A08D34E20C089FD3007100270B0E018AG590F00009527DC6F8A001FB0400A05000E609A5610F0E7B0200D0GE08C1D40020006C092EFA00010D307001A396B0ED0F0C08030527490FC100E09D30158BGE74C0000A100005F0036E0058EFB4300C00D7GGCF4A7900680B105", - "BA026005F401D0EG376F0B1A09D0C425G90C700E8000B006540E0009B002708A0E01000F6540A70DF000A0D12E80650C000408000D0B920020CDB05G300A800098B00003000000C00G2A090070F00E004D0700A0C81030F000F3C582A0B0G0970C480AE000000G596F053DC00000087000EB06G807004C03D0000F2700C00601", - "C060940B503A712007A00360G01F004B54E0G70F000BA306030F0D0064009G0800900E50A08D00G03B0G00D0CE90800AA80E00G91603BD72D0074A30F000091EE0D63BCA81G9F457050B0F700000G2894FG0028E7B500A008C70019G3F00EB000DC300A629F700E570F0E843006GDCA10E05290000A167B36A14C0B0E0D02F0G", - "E0G764A0910C5F0090083FE0BGA2C07D213AG7BCF65D049EC6BF9D250E870GA1FGA305960401E7B074DE2CFG60B8A310109B083EAD0005646800B0047903DCGF837CAG601040B005BA0GE90280F501C049018B0730D6G2EAD0200140009B68F7A7100EG058290B3C5009F2CAD0107E46GFC45370EB001D0232ED000BCF009A50", - "EC0A0000F10687000B238017040950A089F0A0003D0BG01001G00000A5000000GE31080F7B9A2DC0B6C8E7D01005009A00A960BC400000G000050A30206D0001C0E200010G040907A36D008050720100000FCB0600304GD200B403796FDCAE5030000008D740B0E0621GB0F4E008007D54D0790AC200608G0A80350000BF1240", - "G0E0689D50F3B000270D530B0AG48E690085407AB6C0F30GA63B021FDE987C040408210900EFG7B390B1043765AGED0F0D7E0G56243B980AC3GABF0E09814520004C0900E86ADB31E0203D8CF1B954G70F007B010250A00E0159A6EG0307C0F8000GE0F83B16200C48071500AF0D69E00B02DC609G7E10856E109A000C453G7D", - "G23CFE48010A5B76EB15790G438CD2FA8070352AFBEG94C0F94AD1CB7060083003GFB481EDC9265791EBCGA524763F8DC502E37D0GFB4A1970D82F96A503ECBG0G276C193ED4F5A85F8942070AG10EDC4EC38A009657B1G216A05BGECF28039438B407FC09A21DE00C5E98B4173DA02F2A9G1DE3504FC76B0001A652GCBE8943" + "00B6483AE20FDC91GA8F0E659D3172B42D9CGF71406B538E03E42B9D8C57GFA6C2G06184DFEA9B7596105GE724C3FAD8A45DF32B6709E1CGEF78A9DCB01G64327860BC423GAE19FD30AED51F7040CG2BBGF13000C9D20507DC02E7G9F1B508634EC9825613FDB7GA81D79AFG00263E4CFB3G1D00A0042659650A74B30E9C8D1F", + "G8D13540972EFB0A3BC980EDA61457G2AF5721B683CGED0404267AG9DBF53018C7381BAFEGD92645BGA40EC7056018F95DF098647CA1GE3B916E532008BFC07D4EGA091BC20D0F8385B00732G906D4C16C1D0G00B473A92E297300DEF18060B0F38CG09A5047B1E602E5B6831A9C40DF064BED050FG093ACDA9G40F16E3B8257", + "48F0071693BC5DEA613CD5E80G0AB42029EA3C4B8051F0600D5B9F0G0426831C0C057634129E0B0G9218GB05F0A0E6430060E82C4530D9F10E431AF9D86GC75213865907GFE24ACD0000C1G36A842590C000FD6A5003700E059F4E82CB7D0106E40D625107G0003B3B790GCD014F6EA05601B47F3EC9G2D88FG2A09EB0D51C74", + "0381D0GA9FB06C20G0600842E03CFD50EFC5003BD42A1070A24D0F05800G3B9E00B30CFG19AE24D6800C041070F0G9EB4E0F00B902G8C531091G2DE35C4B708F0030A28CG617DEF0DC780B5F2E94AG130006E0D70A83024C2GAE439005D006B71D0230C40BE690A894F7G16EA852B0CDC6EAB9203D714FG5380BF7AD0G09E162", + "1DEF0A5C47209B865B7948D20E003AG000000763D0005E4F6003BE0085C0120D01026500E3D480C0030D841EC0F9002B8F9C020BAG15E364E64G093002B8AD51B0D8A3460157FC9GCGF19D853406200A40362FC7BA9GD01E92A0EGB1FC8D6430D80B7194GF3EC5A2GAC406285D700003F9253BEG680041D730105CAD9B42G608", + "G9D05382CEA06BF445EC19D0G06F0780F08070A0451D009EA700G4FE9B830C0D6C159B40AFG0D8E783B020107000FA0020FDA00059C4B163740AED6008300905C0A0F694D0EG851BE64080201C5B73G95130C0G7FA904D20DG9831B52046CEAF1D700208E40A960C382E00C96D75AFB1BFC407E031295GD89A56DF018GBCE472", + "0DC63B47F058G20AF0102E06DBAGC30032A009FD06E048500GE8005A3490DB6F2AFCE0005G01B0085047AGCFB230169E1E905003008A0700B63G1D78E9F45A2CAF736000C5198GDB600298DGA740E0F300B9FA3008DE05160C8E7500036F29A44900D3A00EB561C78705BFE90A0600G2E3008C600DG790B0C00147009F23A08D", + "AE3G6B980120DF0C0C007040900D06EA97185D0F00642BG3460523CAE0B0098004500F120EC798A6F06AD00E0G980254C829B076F403GD0E70E100A96052C3B0D04007GC50E60A910F8796B14A3CE5D2EA0600000710B4000193EA8020DF6C702DGC0057864EF13B15B0CEF0327A8G09037F0820D9G100C589AE106305F04720", + "08030C4B0D102A096F940E0580A203071A000F023G4B8C000B000A300F0E0645EC7B24509068F03DA08GB7CF03D402E0D26918A3G0EFB40000F00D9EB00C08GA0105C3D968FAEG727EAC06B020034D9036D2A5F4E9700B0189GFE20004BDA0635GC600EAD2893714FD3A518C4E07692B900E4G20A631500804180076F0C50E0G", + "0900FGE8071A24606G502D9B00CF73A10F17A0408D23G0B02400510000008E0FGBFAE2C0D1050843C8040AF630001000769ED5310080F2GAD03187040F60C09EA10G30020507D084534D0E0G20009AF682CF04DA0E00050097B6C85F030DE12GFCG9400E7AD856024A63GC2DF901B7E80D726F000C4E0G00BE8519A06230400D", + "D0B09E8G203610C4716GF024B590380D00980D0CE0FG0075ECA205B7400060G9A00EB0060GD00407002D005FC6478B30G45602030EB0C0D0073B48ED02A9F05G0G000C0ED47093868D07003A91EFG50B02437098GC5BE0AF0EF5G4D0380A712C56E103A90F0007B000D080016B205G933000D06279C5A0F12B79EFG5A3104C68", + "0E000648F0C1D352000F02GD00B507016000F000D340G0BE0000C105A090F846930C671B2AGD0F850A8004F2EC03961B00D1G8AC0564E200702459030F18CGA0G4530EB0C0D7092F8DB0430051290AG00F090C2G00AB5DE727CA0D0680FGB000E9060F81GB30250AD1A030C9420F7E680G7025640DE03BF935F2AB0E79801400", + "2BD7G0F0C0010590A419E67B50F3G2CD8305D00C7400BE10CF0641020B00A3780030AF0G490DE020E904B706F0008GD0FD205000G0081B07B68G300D007A49050548706F3E0BCDG0DEBA180527GC3F46706FC0E3A004918B90C000D0168F57EA0A0BFD006C1970303C0295B080470A0E007080000F3E2C59089E6037D2A0F4B1", + "032E0B061AC89DG4G0400C012B5930006B80290GE04D51C090C5084D0G0F002BADE090020613C8054G685FAE02BC07D300000DG4F50E0B000259601008D4000018A4G07FC936E20DE60214905D07GCAF5F0G0203A400867909D0E0680F2G4531000C008A07050002207601398EG0D4FAD0GB462501FA79008A0100D70C62B050", + "FGE427ACB09056D15C72BDE9G416A00000B6F301720EG490091064000C0D27EB010CA00240GB3079089A0070053162G06E07810B0D29000F20FGE50307001CBD8B20000006D4931A03A19000EB007D0540C01E0A39780G629760003520AG0EC0E54072B09A60D13GC600G91F8302E05770G930CD1F05002000835000DGB7C0F6", + "008A3259016FG0D4CD5070063E9A028134960G0807D00FE00F20D0BE0G08960061EFA54G0372D8C9G3ABFC20608901705040E890AF1D030G0970B163ECG0000006C809021030FE0A0010C03A042G7050F0G0500189AEC040A0B0GE8F7DC6291300D003AC48000590903C00E5G60184AD000590G400E31C0B0A0E801B905C00F0", + "16G20E8C50A00F490FD03190G70405007E390D450C00A2G8C040A6F028091D7E008G07C9A0406B53B0564GE0D00098000A20001B69750G004C006250EGB0D100A20FC9G6850BE431G8C12B7D009006FA000005AEF0G07082596EF400721000DBDG0000600E210A0C9500GFB70386001D60089A21CD50F0B0031BE0049AFG8705", + "247301050B000FDCCD800GBE450F10066F0007C908GD003EG90E00400C360080AC541E9B00D708FG90G27A0061080BC0D0300C800A5G00E701F7G600E40B30000B08092GF000CD64106FEB0A8D4C000052ED0413090AF0704309F006G200AEB1FA90000C57B4830DE8DC05A03G92704F302140F0C0AE0009B04693G7DF81EC2A", + "A064E087DFC3B29190080A0CEB106400G00093D460700F0AF00E006000A0D85758BC64F0A0E900G004G7B1A3F856C92E13900075B40D06AFE6FA800D312C754BB056A010C00G3D742A0G30BF0E009C6881E90020000F00B50703D590060000E2C00D263A40075B0039050B4800DEA0F0700F0D01906B00800G400FC01A852ED3", + "00B0070800E605FD800000G1039D0C0200D5FB49000C600130G9C05DF0B0870E0286B09FE5G731C40701AC649D8300009DC4G870100006500F0B1205006008975G02D31C04FA097019FC0EBA27DG5308030009F0B6C01DEG7B0D048G0E19CA2F0C0G70AEDB302F10059E41320000GB8CB0170006G20000D30A2380DB00407060" ], "medium": [ - "001G40805320B0FDFB8DG1760AC9000EA460000000BD80G70E950F0210G76A4C4GB000A0003C96E090ECFD0B800542A3D3A0942E0G0BC71000008063000E0G0F004906EG00D3F80A5AC30B08G64FE0010DF032C1095AG0646000DA040CE835090F302G006081700B0070093FCE0250D0005408BC9D0G1F06G90607100B04A000", - "030E9A00C0020405G2058C17009F00A3070B2FED0500C000C8D00040EGA0B7004068D0700E05G000F90G0E0310B758007DE215FG93060B005A03C800G0240D000500A20E003D0006A69D540F00GE0038E00C0D300060900034216G085CF00E7D8050G02060EB30000070E00030D06F540G340756F80A200C200000D009500A00", - "0E00900030800F009A0070E601FB0G000F4D8B300006900E600304025000780B008CA0G0205400E0030408500000F0007DB210906C0050A3F05A0D000300G008002F407305BD010CD530F00E090C204G0G000C0A00E1D307AC1002D00073E0F0000100C0B70000GDC4D02000000G8E0F07A03F0B9D0E0C200B0ED00GC8206090", - "0G5EF00C070180B38041B06900D00G000007G100A6EC0D0FAD000042080G107656B20C01007F3E0A037DE00400G000084AE000GF02C5900B10GF05D63E8B002C0060027089F00CA02E0A080500000B90GF15D90AC4B208E7970BC4EG1A560F30B0DG0A83EF60C21000095607DC20B000F004000D0BA8000900064000G0105080", - "49800E00603C572A5000600C00700E0DC0D74A2150E0B068006E0000800D0G13020001D0050764CGB07C0G6028000309DF1903050000208BAG46800009B3E5D0ECF52080430B90A10402ED907C0106B0169GB0A40E087030780DC0030090G04E002409000B0500GC9D00F000G04EA000FB5137G8C06A09E40ECA1050908F0B06", - "50F0008GED020607030E04000B00DFG0D8203A070000000BC9GB600D040015806007F13A0C0B000E100CG7040E00500F800G090B050A6104A0E9000000107020E07510D32800C4B62010E04050000008G698A20C4FD3E7050003050810002DF0BGC00DAF39750061350D0610B00FG279070053G982E1B0C090A070C2DG600050", - "70960BD02000A0050E0B0002900000465024EF8910G6007C0C00A60400000900B900007050F0D4026F70C59GAD008030300CB02869705010GD00000E380C9A600200073AB500160F06B5D8C000370GA40710000006083CBDA3F8600B04C07E000000006D0019C0F3000020A187E30000C00A00F3406BG00100E0G000CF0D6290", - "9EC0A08F6000G07003006B00G94008005G0640000B8000AD8102G5EC30A700097C08E00640B30D0F00E0F1A30G7542C6065F08201EC0AG071004C000A6FD0E006090BF0GDC3E0152C0B072680004D00E080G300070006000003150C0280G790B04601EG5B0080F00G71E800B0300902400002C3AF0G60000000C94D051E20000", - "9GACD05002806000BD28009A50C0F7EG0E0108000A9000D500400000000D00981000832G00E000A0F080CD056G400329D000E0000013800B0300F1600002E50078006F0009300B0A42000008E05600F0050G004DF820CE1609001500A40B02072A00560000BE307006FB720103A0400001004ED000F0A600C4059A3B006001G0", - "C2070D000840G00095B60030F0G000010E0F6G2095DC000BG000800CB301F02E00401F028060B0002090370040C0D0000015CBE6A70D203GA00004900B03E0F006207AG03900008CE00400007D586200B97350006G0000A458GDF000C00407037109A600502F000D005200CFG68730000G0B2879DC0000606308D500000900E0", - "DF00C98030517BG00B8700000CF0020490014000BG000A80A3E000F080600900670000302BGF18D0G800A290000300500C0386G5000D27000210700D0006C00G000001C0E0B00G0500G200A0F50CE6170060F500D00G030B000EG0627130000004500G0F6782B1A31A3D980BGFE56070F070530040CB00E220B0604C03DA050F", - "G76CD0402FE5B80A0B500007A30C601E8403200E916B00G710E00F068GD00C20E07F12D040B630C5AD2067F43C5EG10B41B0E00CF07G06A20C30B00A1000FE7005009E0D60000B000ACE780BD2900456029B346070010ADG68D4F0A150G0200000070D30000095010F02A600C51D000000G08010000000FCC3A0400FB8097G6D", - "GCD4FE000700000A58A03000000F7400760104008CBD00F03F0967810000020D0000GF08BD12900EAD9F5370060020G00100D094E30GA80FE08G00B20FA7D00407009AE05B460G019048000F00CE000310068000FG70E092D5BE200718006F00800000F3040000E000GDC010350B4A00603C4BGA7008F9050B570200A9F1G0C0" + "00B6483AE20FDC91GA8F0E659D3172B42D9CGF71406B538E03E42B9D8C57GFA6C2G06184DFEA9B7596105GE724C3FAD8A45DF32B6709E1CGEF78A9DCB01G64327860BC423GAE19FD30AED51F7040CG2BBGF13000C9D20507DC02E7G9F1B508634EC9825613FDB7GA81D79AFG00263E4CFB3G1D00A0042659650A74B30E9C8D1F", + "G8D13540972EFB0A3BC980EDA61457G2AF5721B683CGED0404267AG9DBF53018C7381BAFEGD92645BGA40EC7056018F95DF098647CA1GE3B916E532008BFC07D4EGA091BC20D0F8385B00732G906D4C16C1D0G00B473A92E297300DEF18060B0F38CG09A5047B1E602E5B6831A9C40DF064BED050FG093ACDA9G40F16E3B8257", + "48F0071693BC5DEA613CD5E80G0AB42029EA3C4B8051F0600D5B9F0G0426831C0C057634129E0B0G9218GB05F0A0E6430060E82C4530D9F10E431AF9D86GC75213865907GFE24ACD0000C1G36A842590C000FD6A5003700E059F4E82CB7D0106E40D625107G0003B3B790GCD014F6EA05601B47F3EC9G2D88FG2A09EB0D51C74", + "0381D0GA9FB06C20G0600842E03CFD50EFC5003BD42A1070A24D0F05800G3B9E00B30CFG19AE24D6800C041070F0G9EB4E0F00B902G8C531091G2DE35C4B708F0030A28CG617DEF0DC780B5F2E94AG130006E0D70A83024C2GAE439005D006B71D0230C40BE690A894F7G16EA852B0CDC6EAB9203D714FG5380BF7AD0G09E162", + "1DEF0A5C47209B865B7948D20E003AG000000763D0005E4F6003BE0085C0120D01026500E3D480C0030D841EC0F9002B8F9C020BAG15E364E64G093002B8AD51B0D8A3460157FC9GCGF19D853406200A40362FC7BA9GD01E92A0EGB1FC8D6430D80B7194GF3EC5A2GAC406285D700003F9253BEG680041D730105CAD9B42G608", + "G9D05382CEA06BF445EC19D0G06F0780F08070A0451D009EA700G4FE9B830C0D6C159B40AFG0D8E783B020107000FA0020FDA00059C4B163740AED6008300905C0A0F694D0EG851BE64080201C5B73G95130C0G7FA904D20DG9831B52046CEAF1D700208E40A960C382E00C96D75AFB1BFC407E031295GD89A56DF018GBCE472", + "0DC63B47F058G20AF0102E06DBAGC30032A009FD06E048500GE8005A3490DB6F2AFCE0005G01B0085047AGCFB230169E1E905003008A0700B63G1D78E9F45A2CAF736000C5198GDB600298DGA740E0F300B9FA3008DE05160C8E7500036F29A44900D3A00EB561C78705BFE90A0600G2E3008C600DG790B0C00147009F23A08D", + "AE3G6B980120DF0C0C007040900D06EA97185D0F00642BG3460523CAE0B0098004500F120EC798A6F06AD00E0G980254C829B076F403GD0E70E100A96052C3B0D04007GC50E60A910F8796B14A3CE5D2EA0600000710B4000193EA8020DF6C702DGC0057864EF13B15B0CEF0327A8G09037F0820D9G100C589AE106305F04720", + "08030C4B0D102A096F940E0580A203071A000F023G4B8C000B000A300F0E0645EC7B24509068F03DA08GB7CF03D402E0D26918A3G0EFB40000F00D9EB00C08GA0105C3D968FAEG727EAC06B020034D9036D2A5F4E9700B0189GFE20004BDA0635GC600EAD2893714FD3A518C4E07692B900E4G20A631500804180076F0C50E0G", + "0900FGE8071A24606G502D9B00CF73A10F17A0408D23G0B02400510000008E0FGBFAE2C0D1050843C8040AF630001000769ED5310080F2GAD03187040F60C09EA10G30020507D084534D0E0G20009AF682CF04DA0E00050097B6C85F030DE12GFCG9400E7AD856024A63GC2DF901B7E80D726F000C4E0G00BE8519A06230400D", + "D0B09E8G203610C4716GF024B590380D00980D0CE0FG0075ECA205B7400060G9A00EB0060GD00407002D005FC6478B30G45602030EB0C0D0073B48ED02A9F05G0G000C0ED47093868D07003A91EFG50B02437098GC5BE0AF0EF5G4D0380A712C56E103A90F0007B000D080016B205G933000D06279C5A0F12B79EFG5A3104C68" ], "hard": [ - "001G40805320B0FDFB8DG1760AC9000EA460000000BD80G70E950F0210G76A4C4GB000A0003C96E090ECFD0B800542A3D3A0942E0G0BC71000008063000E0G0F004906EG00D3F80A5AC30B08G64FE0010DF032C1095AG0646000DA040CE835090F302G006081700B0070093FCE0250D0005408BC9D0G1F06G90607100B04A000", - "030E9A00C0020405G2058C17009F00A3070B2FED0500C000C8D00040EGA0B7004068D0700E05G000F90G0E0310B758007DE215FG93060B005A03C800G0240D000500A20E003D0006A69D540F00GE0038E00C0D300060900034216G085CF00E7D8050G02060EB30000070E00030D06F540G340756F80A200C200000D009500A00", - "0E00900030800F009A0070E601FB0G000F4D8B300006900E600304025000780B008CA0G0205400E0030408500000F0007DB210906C0050A3F05A0D000300G008002F407305BD010CD530F00E090C204G0G000C0A00E1D307AC1002D00073E0F0000100C0B70000GDC4D02000000G8E0F07A03F0B9D0E0C200B0ED00GC8206090", - "0G5EF00C070180B38041B06900D00G000007G100A6EC0D0FAD000042080G107656B20C01007F3E0A037DE00400G000084AE000GF02C5900B10GF05D63E8B002C0060027089F00CA02E0A080500000B90GF15D90AC4B208E7970BC4EG1A560F30B0DG0A83EF60C21000095607DC20B000F004000D0BA8000900064000G0105080", - "49800E00603C572A5000600C00700E0DC0D74A2150E0B068006E0000800D0G13020001D0050764CGB07C0G6028000309DF1903050000208BAG46800009B3E5D0ECF52080430B90A10402ED907C0106B0169GB0A40E087030780DC0030090G04E002409000B0500GC9D00F000G04EA000FB5137G8C06A09E40ECA1050908F0B06", - "50F0008GED020607030E04000B00DFG0D8203A070000000BC9GB600D040015806007F13A0C0B000E100CG7040E00500F800G090B050A6104A0E9000000107020E07510D32800C4B62010E04050000008G698A20C4FD3E7050003050810002DF0BGC00DAF39750061350D0610B00FG279070053G982E1B0C090A070C2DG600050", - "70960BD02000A0050E0B0002900000465024EF8910G6007C0C00A60400000900B900007050F0D4026F70C59GAD008030300CB02869705010GD00000E380C9A600200073AB500160F06B5D8C000370GA40710000006083CBDA3F8600B04C07E000000006D0019C0F3000020A187E30000C00A00F3406BG00100E0G000CF0D6290", - "9EC0A08F6000G07003006B00G94008005G0640000B8000AD8102G5EC30A700097C08E00640B30D0F00E0F1A30G7542C6065F08201EC0AG071004C000A6FD0E006090BF0GDC3E0152C0B072680004D00E080G300070006000003150C0280G790B04601EG5B0080F00G71E800B0300902400002C3AF0G60000000C94D051E20000", - "9GACD05002806000BD28009A50C0F7EG0E0108000A9000D500400000000D00981000832G00E000A0F080CD056G400329D000E0000013800B0300F1600002E50078006F0009300B0A42000008E05600F0050G004DF820CE1609001500A40B02072A00560000BE307006FB720103A0400001004ED000F0A600C4059A3B006001G0", - "C2070D000840G00095B60030F0G000010E0F6G2095DC000BG000800CB301F02E00401F028060B0002090370040C0D0000015CBE6A70D203GA00004900B03E0F006207AG03900008CE00400007D586200B97350006G0000A458GDF000C00407037109A600502F000D005200CFG68730000G0B2879DC0000606308D500000900E0", - "DF00C98030517BG00B8700000CF0020490014000BG000A80A3E000F080600900670000302BGF18D0G800A290000300500C0386G5000D27000210700D0006C00G000001C0E0B00G0500G200A0F50CE6170060F500D00G030B000EG0627130000004500G0F6782B1A31A3D980BGFE56070F070530040CB00E220B0604C03DA050F", - "G76CD0402FE5B80A0B500007A30C601E8403200E916B00G710E00F068GD00C20E07F12D040B630C5AD2067F43C5EG10B41B0E00CF07G06A20C30B00A1000FE7005009E0D60000B000ACE780BD2900456029B346070010ADG68D4F0A150G0200000070D30000095010F02A600C51D000000G08010000000FCC3A0400FB8097G6D", - "GCD4FE000700000A58A03000000F7400760104008CBD00F03F0967810000020D0000GF08BD12900EAD9F5370060020G00100D094E30GA80FE08G00B20FA7D00407009AE05B460G019048000F00CE000310068000FG70E092D5BE200718006F00800000F3040000E000GDC010350B4A00603C4BGA7008F9050B570200A9F1G0C0" + "B329800F0140D0A0G700E0290500B6316058G01D2F00C079010CB005G00928FE00BG000E00230D073DF009G00C08001480E002F107G0A39C0A1034DB695F00G27000A08C90010000000B0E325G871006AF010090D000GCE820GED1B60AF07903F0CD96E0701405B000801D50020G074004302008FD9561CG0G754FC030E09000", + "G000070A9008C00BD040C02B603GE9050200G3091004D8A000690105E0A0F0326002A0D050EC00FG8FA06017300D5C0E0050F9B3000A20063G905E000600B08D1030BC0D20F0GE602E00958FD00700C4900F060EBA8372015086027004009BDF00F0ED9000C1602046D01AF070928000B92G705CF30610EA01E02036A8D54FG0", + "00G05A0E300000009C0A0004G201705EF07E1000A6B50G40010870DF904CB00A2D6430G510CE000FAE0G0180042FC0D3CF05AB42000D6E017010E0FC000024B50000C00025E40D86G9C10F0B006045E250ED2038B1G7900C0A0200E0FCD93B1700A3G000CF0B0804000C80B0E00000908B90F0C100A0023D1G200300D856EC7B", + "08B007E90F03042D0040830G00D2E015950DAC0B08013600E0F750D09C04B08G8000007102009E6CD000BE300706080A00E000A639087001700A28900G4E003B60C0028E0590A0D03A8E95CF4D17600204090B1D0A3G8C0EF1000A632000570400600050G02000E04E920FG0C65D1BA0C0031628B4EA0F0010A5ED04007002C6", + "10F3000C0A600502A0C8F9B320106D70B0G0052D0EF70000D2004600C09B00E000B0000G9C00A6585C6A3840ED009B07009F6B00A15402D33GD2AF50B780E40C8F30009460BA100EG500B3000901082600A9G00000CEF00B000001F0G340D9A003007EDF14028A60900714365F0C2EBGF42G0A8B000057016A002CG08070000D", + "CF004000906020107428000900010600D0050000230E70409310800B7C4DEFG5514003BC00800G6F000079F0G4250A000ED76A8G00FC35B0G8A0E4000B03927039040502000AG0010DF10G98300467A06A0GD743000B080080020F0EC09G04D0F5BD30E040G9A000420C9BGFE0108D50A08E521607CF0B30179608040500FCEG", + "30400000070005000FC84050B000D970E0D003A04000F6GB0050E9B8CF0G010000B005E4800300909E36B100F02D075A000400000000E30170A090236E00C400276E001B5G349F8054002E3FD006G000ADFC000609B70E148003D479A10E52600C8G300596721A00F31A0B00G0E57C4606E010GC30AF2800D07260FE0840B035", + "0B0G017A00C080390390D240700BC0G01420000CGA900F5000CF0GB00328040ADA30000000G7000FB1560AF08D009740G040CD07250F00A079F0G004301A00D8C074B0D86E300190060BA0C900D150000010F70G00A548B6008A0E01B042000C80A1E06000B00DC702000C005000G084476520GD0C03F001FCBD48A31G0E2965", + "193580400A6DF200AEG010B0040758D68B0DF50C129G4007020706005008100AF60C7D2A050304E0750B010F4009030800025C90670E0D0F409AEB800C0F200568002A7000F1350GD009B3F000C46080B3040G00A002E00DG001000836D007C05C700010D9008A0E0006A802000C0F549AF80EC700500G0000DG30560E8A00B1", + "8076FGE9500DB1C0B2A934100000807004050B0C900A0306F000006A38BG940537600C82DB04G009090B0000760C0003D08049300E02670C0G04E6700980DA02000008G000091F5790070001BF650D48100000B0200700A00A0E00FD83C12B9G08900FC0G00B0260A650GD080700EC0B41B06200C50EA8DFC0DFBA53004800G1", + "C4D0FB83050200060G105000D009A70B89050070310600CDE000D0028CA0300001C2070D089A6400400000A90D0EB0006009004B00F38C000D0E6F0804C19G2A0800CD07900B56020F000805001D003C060DB020CE05F8099000E3GA4680D07105A4700F100C0EB8FC080254E0D7136G070G81BEA3040F95000B309C0G580DA0", + "0000AE000B0264080960000070C01A00B3080074E1A0050D10F0DG006895B0E70G0BC13D2F007605000086EB045AC2008C0300451009FD0E00E5F9A70C0301000B4920C600000G73F60000095738000AE8GD070002460C907030E41809GC0B6F0F709360400BE0D004B0700E0A00300G000G1C020360A00B310C0A0F9GED0006", + "0G0BC01D72000405000860320GD0BE7C0100G500AF00093D407D0EF830C01200F0070009GD010080004A80E0900F2D510200D0B0003ACG0F1C00020G8600009A001638705E0DG0B0083FE00C6B9G5A10A00G0B00F002360427B50DG6000C980E0A049F0E01G30500700E006A00004BC380920G00B0F0D1E6CD00730B040EA0G9", + "95AE0008GF7D000B80005200B4EC0FA323C00FD090600408B004GE1C30280D00600020CE008795F0020C0050003F00D0F0E70B0020153A400A9304076CDB80020008E104079A560G3B0FD0028146EC00A149000650BED83FE050B80F00000214C0F2000510A00G0E403A002G00000007G06089E07BF000C07E1B0003C0G20900", + "B1C047820003056900000903060B0E0C08030065D29C1GA79000FACD18G00043870CA5160E304FBGG20F74300A00C800A03BD0F864C090020001200B7G80A03000G000B05002000D09BD630A01E07080000050700CD6GA0B00E090D083B0005F0D4000A00000F0GE2F003D57G90EB000E0700000ADF139003GA68F9EC0205701", + "G0DA30004010085004080000305000E0002085E060A94BG00390704B8000A26DEB0900F003006A08A8F50B040090D301207DC090000850BE063C005D1GE09F720500B20AE14D398619034EC0062A000BD00EG300B07F010507A6D00F003CE00G7064000002B0000A0C1264AEF08G05070G00003750000C0F900F2G0C7ED08004", + "00700300A0DC000F960C85EF04G230A03A0109D200070000800GC00A0F530ED00036700190F00040008F040G75EA9D36000E0B39002D7C00270008F043C051BE00000000F000C0250D03027C0005B010C002010BE064807G14005080C2BG00E0F0C00000D00E6791794A0DB5001F00C0E2001F6750A000G36310EC940G78F05B", + "0F0B206D000085CA0170B00000280G00G80400EF05007000A60000G907D0130E40F0GC000D000E600C080BF6G04100720B0009000006040G63G07D40020A0005C0409287D0E00B13300654D0B002E0G0090560007384D0FC00B0FE10A90C5847000900002A3FG0088G0DC1240B0E0006FE3205900C1DA7B475613F0A080G20ED", + "4C1A0F090GBE3D570GD35000C08A000F0020CEBA041D8906E900000D05F0C00A0E897GF40200000024C009A800D50EF01BF0035097000602D0060B000E000894060430G0F0095A0080GE4A90DC00BF01000000E048A6237052010076B00004DC0100B00F5A0360E0700290DGE00000A500EF07612DGB40000D6B05030F741G00", + "G60B9F0DEC0A4508F10040200000DA004AC2580B0G000F900E0000C35F00020BE2B306A000975400000000004083EC2A1F003500006E000754002E00DA00B1366000009FBE02AD040DG06705C0000900380ED14A907G2BC0AB090200801D0050B5600C37F2001009D340B0G160E000020701E456A039F00000EAFD0000CB6305" ], "nightmare": [ - "001G40805320B0FDFB8DG1760AC9000EA460000000BD80G70E950F0210G76A4C4GB000A0003C96E090ECFD0B800542A3D3A0942E0G0BC71000008063000E0G0F004906EG00D3F80A5AC30B08G64FE0010DF032C1095AG0646000DA040CE835090F302G006081700B0070093FCE0250D0005408BC9D0G1F06G90607100B04A000", - "030E9A00C0020405G2058C17009F00A3070B2FED0500C000C8D00040EGA0B7004068D0700E05G000F90G0E0310B758007DE215FG93060B005A03C800G0240D000500A20E003D0006A69D540F00GE0038E00C0D300060900034216G085CF00E7D8050G02060EB30000070E00030D06F540G340756F80A200C200000D009500A00", - "0E00900030800F009A0070E601FB0G000F4D8B300006900E600304025000780B008CA0G0205400E0030408500000F0007DB210906C0050A3F05A0D000300G008002F407305BD010CD530F00E090C204G0G000C0A00E1D307AC1002D00073E0F0000100C0B70000GDC4D02000000G8E0F07A03F0B9D0E0C200B0ED00GC8206090", - "0G5EF00C070180B38041B06900D00G000007G100A6EC0D0FAD000042080G107656B20C01007F3E0A037DE00400G000084AE000GF02C5900B10GF05D63E8B002C0060027089F00CA02E0A080500000B90GF15D90AC4B208E7970BC4EG1A560F30B0DG0A83EF60C21000095607DC20B000F004000D0BA8000900064000G0105080", - "49800E00603C572A5000600C00700E0DC0D74A2150E0B068006E0000800D0G13020001D0050764CGB07C0G6028000309DF1903050000208BAG46800009B3E5D0ECF52080430B90A10402ED907C0106B0169GB0A40E087030780DC0030090G04E002409000B0500GC9D00F000G04EA000FB5137G8C06A09E40ECA1050908F0B06", - "50F0008GED020607030E04000B00DFG0D8203A070000000BC9GB600D040015806007F13A0C0B000E100CG7040E00500F800G090B050A6104A0E9000000107020E07510D32800C4B62010E04050000008G698A20C4FD3E7050003050810002DF0BGC00DAF39750061350D0610B00FG279070053G982E1B0C090A070C2DG600050", - "70960BD02000A0050E0B0002900000465024EF8910G6007C0C00A60400000900B900007050F0D4026F70C59GAD008030300CB02869705010GD00000E380C9A600200073AB500160F06B5D8C000370GA40710000006083CBDA3F8600B04C07E000000006D0019C0F3000020A187E30000C00A00F3406BG00100E0G000CF0D6290", - "9EC0A08F6000G07003006B00G94008005G0640000B8000AD8102G5EC30A700097C08E00640B30D0F00E0F1A30G7542C6065F08201EC0AG071004C000A6FD0E006090BF0GDC3E0152C0B072680004D00E080G300070006000003150C0280G790B04601EG5B0080F00G71E800B0300902400002C3AF0G60000000C94D051E20000", - "9GACD05002806000BD28009A50C0F7EG0E0108000A9000D500400000000D00981000832G00E000A0F080CD056G400329D000E0000013800B0300F1600002E50078006F0009300B0A42000008E05600F0050G004DF820CE1609001500A40B02072A00560000BE307006FB720103A0400001004ED000F0A600C4059A3B006001G0", - "C2070D000840G00095B60030F0G000010E0F6G2095DC000BG000800CB301F02E00401F028060B0002090370040C0D0000015CBE6A70D203GA00004900B03E0F006207AG03900008CE00400007D586200B97350006G0000A458GDF000C00407037109A600502F000D005200CFG68730000G0B2879DC0000606308D500000900E0", - "DF00C98030517BG00B8700000CF0020490014000BG000A80A3E000F080600900670000302BGF18D0G800A290000300500C0386G5000D27000210700D0006C00G000001C0E0B00G0500G200A0F50CE6170060F500D00G030B000EG0627130000004500G0F6782B1A31A3D980BGFE56070F070530040CB00E220B0604C03DA050F", - "G76CD0402FE5B80A0B500007A30C601E8403200E916B00G710E00F068GD00C20E07F12D040B630C5AD2067F43C5EG10B41B0E00CF07G06A20C30B00A1000FE7005009E0D60000B000ACE780BD2900456029B346070010ADG68D4F0A150G0200000070D30000095010F02A600C51D000000G08010000000FCC3A0400FB8097G6D", - "GCD4FE000700000A58A03000000F7400760104008CBD00F03F0967810000020D0000GF08BD12900EAD9F5370060020G00100D094E30GA80FE08G00B20FA7D00407009AE05B460G019048000F00CE000310068000FG70E092D5BE200718006F00800000F3040000E000GDC010350B4A00603C4BGA7008F9050B570200A9F1G0C0" + "0000AE000B0264080960000070C01A00B3080074E1A0050D10F0DG006895B0E70G0BC13D2F007605000086EB045AC2008C0300451009FD0E00E5F9A70C0301000B4920C600000G73F60000095738000AE8GD070002460C907030E41809GC0B6F0F709360400BE0D004B0700E0A00300G000G1C020360A00B310C0A0F9GED0006", + "0G0BC01D72000405000860320GD0BE7C0100G500AF00093D407D0EF830C01200F0070009GD010080004A80E0900F2D510200D0B0003ACG0F1C00020G8600009A001638705E0DG0B0083FE00C6B9G5A10A00G0B00F002360427B50DG6000C980E0A049F0E01G30500700E006A00004BC380920G00B0F0D1E6CD00730B040EA0G9", + "95AE0008GF7D000B80005200B4EC0FA323C00FD090600408B004GE1C30280D00600020CE008795F0020C0050003F00D0F0E70B0020153A400A9304076CDB80020008E104079A560G3B0FD0028146EC00A149000650BED83FE050B80F00000214C0F2000510A00G0E403A002G00000007G06089E07BF000C07E1B0003C0G20900", + "B1C047820003056900000903060B0E0C08030065D29C1GA79000FACD18G00043870CA5160E304FBGG20F74300A00C800A03BD0F864C090020001200B7G80A03000G000B05002000D09BD630A01E07080000050700CD6GA0B00E090D083B0005F0D4000A00000F0GE2F003D57G90EB000E0700000ADF139003GA68F9EC0205701", + "G0DA30004010085004080000305000E0002085E060A94BG00390704B8000A26DEB0900F003006A08A8F50B040090D301207DC090000850BE063C005D1GE09F720500B20AE14D398619034EC0062A000BD00EG300B07F010507A6D00F003CE00G7064000002B0000A0C1264AEF08G05070G00003750000C0F900F2G0C7ED08004", + "00700300A0DC000F960C85EF04G230A03A0109D200070000800GC00A0F530ED00036700190F00040008F040G75EA9D36000E0B39002D7C00270008F043C051BE00000000F000C0250D03027C0005B010C002010BE064807G14005080C2BG00E0F0C00000D00E6791794A0DB5001F00C0E2001F6750A000G36310EC940G78F05B", + "0F0B206D000085CA0170B00000280G00G80400EF05007000A60000G907D0130E40F0GC000D000E600C080BF6G04100720B0009000006040G63G07D40020A0005C0409287D0E00B13300654D0B002E0G0090560007384D0FC00B0FE10A90C5847000900002A3FG0088G0DC1240B0E0006FE3205900C1DA7B475613F0A080G20ED", + "4C1A0F090GBE3D570GD35000C08A000F0020CEBA041D8906E900000D05F0C00A0E897GF40200000024C009A800D50EF01BF0035097000602D0060B000E000894060430G0F0095A0080GE4A90DC00BF01000000E048A6237052010076B00004DC0100B00F5A0360E0700290DGE00000A500EF07612DGB40000D6B05030F741G00", + "G60B9F0DEC0A4508F10040200000DA004AC2580B0G000F900E0000C35F00020BE2B306A000975400000000004083EC2A1F003500006E000754002E00DA00B1366000009FBE02AD040DG06705C0000900380ED14A907G2BC0AB090200801D0050B5600C37F2001009D340B0G160E000020701E456A039F00000EAFD0000CB6305", + "GB824DAC100300500400B2938050000E0951G700A20D000C00300F150G90B0020019000070600F30CFA7004BD00000003605A02E0008007040G0F6075000010D25EB046030090A0F0CF6E5B0GD80042790007AG0E00F6D01D07A03800B2000E000040C70B80EG610012G0EF003AC790B000D0B3109G5A2C08A0C00062071DEF3", + "G003C00820015006F00C5D04G09010A712003A70050C8BD90A0021E000B003FG30000007A00F008E05B0F02090863G7098A0B05310D00020D60E80003400F15BBD0906A0583EG000AG30004000000508000098004F0D07B20F00D0G00A100C9300F0E000B24A00CD70500201DG00083FE08D40B0FC702AG1000A070F806304E0", + "00ABFE5006009081FD7496G3000805206C528710F000D4E3918EAC0200306F0700DGB3600501F00C09B00F0100G00700163000000B0AG050450700C00080B60080C0E500091F00A0G01AC0300E6B400FDF0502B0G3A0C0987069G00F004C1B05040100000000000D000071284G0E500907004B0502D08100BA000D9010567200", + "70200805ECG1490D840G0920070D00000F51E00DB00602370D00C7A00040B5GE03A00D680B2E105990DE503081AF00CBB0F000000470E8D0008500000D000G0AE26FD07C000090B0000403E6A009C7200B70800006F030E5G093A0127000008409G841C06E0A53F02EB7F053081G040000C0GB000F020000F0002AD000000B10", + "00900DEF6210005A60CAB9420D5700E0000E0007000C40080075AC000F9E60001E80FG60040A507090GB20000300140CF0070AC498B1G020C020010DF0E0806020AF0E96B70G30D0760410D50AF9E000E300C0FG000800000G1C7B3000D290800900E000G53B0007B0000351700400000030G708200DC940G000D0A0108F2B0E", + "0105D082GCA0E007000063CB70D04008DEGC7F0430B850090700000509000F0D007810F02E0AG0CB00CDA70008G01062F00600BECD50800090008G000400DE0000000801E000000006B10CGD90800A040CD00003A671B09000FE9060B0CD71530002E03F87940CG100E00A0800FC90D00093000G5A00F076000F4597D0000200", + "2000DG601B097000F7000AB100050090DCE84925G30A000005007CF86E0D0G30G000135D0008C94F000620809FG000500A8F04000000E1670009060701E302AG0053C74G80960DF0000A0109005008000G028500BC0E9070B89D00A60030540C000008G00010F60006BE00040980G00082F0A000740010D040010F7CE5023A00", + "89506B0000700E0F00E003G861CF5DB00C1B570E4820G36A06G00001005A09470035BE00000G700CE8670G35B0000094B2D00AF000E5600G0004000276A000E00GA000860E4000730B00A00F0037400603080D002060905E5D46E070F90B00080481009G0700CB35000904B0C000E001250071E00004A08D000D00000208F000", + "ED0CG300820B01AF0000000065040C3003G1F0400D90607E608BA7C0FE0G0200A040000508F7006B3B0600F20409GDC0C52FD07000GA000801E00CBG53000700D8043600000F9G01003750G000A2E00C09C20080006E3A00G060000A3100740000000D082G060097000E40309F81A0G0970G2060D000800446100000E700D003", + "000003G0F040E56010E60A0D7800F00400009F0C010E003A0DF07000C60A08120E0C00BG5A0160496000000000008A000934500062EB1000G21BA0E60F9D7000D0G000030B02A170B0A008250G06040D00C0D10704F50EG05800B00000D00000F00A1002D3604B07030700DB1EA00086EB00G57F09C003A081000E00B0270D0C", + "1G05B289ADE0000608070500000300A003E04D00620G0B8062C0G0A3589B400000000E0G00450000047000003A6C0G00AD8003CB000204000C037000DB89A5008769DGB0C3A1FE05D02GF007800000C100140030B90D7AG8003C80250070B09000G008000CB00F00719004E62500G8DA0F0800G0760000000000105000D8C069" ] } } diff --git a/fastlane/metadata/android/en-US/changelogs/53.txt b/fastlane/metadata/android/en-US/changelogs/53.txt new file mode 100644 index 0000000000000000000000000000000000000000..3612c28f4a233ad327f09f91303644f0f9a544cf --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/53.txt @@ -0,0 +1 @@ +Improve grids generator, update levels diff --git a/fastlane/metadata/android/fr-FR/changelogs/53.txt b/fastlane/metadata/android/fr-FR/changelogs/53.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce95c858999be38d64984471d6d286194b1dc3f1 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/53.txt @@ -0,0 +1 @@ +Amélioration du générateur de grilles, mise à jour des niveaux diff --git a/generator/batch.sh b/generator/batch.sh index ec543003557298cd0817cc8d9d454d060adaedb0..9e948a28006ec4673e52440913aad69b42d72981 100755 --- a/generator/batch.sh +++ b/generator/batch.sh @@ -1,23 +1,34 @@ #!/usr/bin/env bash +# This script should be run and redirected to a file like "batch.sh > grids.log" +# Then this grids file should be imported in "parse_grids.sh" to generate json + CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" ALLOWED_BLOCK_SIZE_VALUES="2x2 3x2 3x3 4x4" ALLOWED_DIFFICULTY_VALUES="easy medium hard nightmare" -GRIDS_COUNT=10 +GRIDS_COUNT=40 for BLOCK_SIZE in ${ALLOWED_BLOCK_SIZE_VALUES}; do + CELLS_COUNT=$(echo "(${BLOCK_SIZE})^2" | sed 's/x/\*/g' | bc) for DIFFICULTY in ${ALLOWED_DIFFICULTY_VALUES}; do echo "Block size: ${BLOCK_SIZE} / Difficulty: ${DIFFICULTY}" for i in $(seq ${GRIDS_COUNT}); do # Generate grid candidate GRID="$(python ${CURRENT_DIR}/generate.py ${BLOCK_SIZE} ${DIFFICULTY} | tail -n 1)" - # Ensure grid can be resolve without any assumption + + # Ensure grid can be resolved without any assumption CAN_BE_SOLVED="$(python ${CURRENT_DIR}/solve.py ${BLOCK_SIZE} ${GRID} | tail -n 1)" if [ "${CAN_BE_SOLVED}" == "Ok" ]; then - echo "${BLOCK_SIZE} ${DIFFICULTY} ${GRID}" + # Count "0" in grid, compute "emptiness" ratio + STRING="${GRID//[^0]}" + ZEROS_COUNT=$(echo "${#STRING}") + RATIO=$(echo "100*${ZEROS_COUNT}/${CELLS_COUNT}" | bc) + RATIO_STRING="$(printf "%02d" ${RATIO})" + + echo "${BLOCK_SIZE} (${RATIO_STRING}%) ${DIFFICULTY} ${GRID}" else - echo "FAILED: ${BLOCK_SIZE} ${DIFFICULTY} ${GRID}" + echo "FAILED / ${BLOCK_SIZE} ${DIFFICULTY}" fi done done diff --git a/generator/generate.py b/generator/generate.py index 73a66cba43721cd87b756ea90bcd127143b5af46..be9f1785520cc0df9b223151870a84f133fc22ee 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -2,20 +2,19 @@ # -*- coding: iso-8859-15 -*- import sys -import math from random import randint, shuffle if (len(sys.argv) != 3): - print('Usage: generate.py block-size difficulty') - print('block-size: [2x2|3x2|3x3|4x4]') - print('difficulty: [easy|medium|hard|nightmare]') - exit() + print('Usage: generate.py block-size difficulty') + print('block-size: [2x2|3x2|3x3|4x4]') + print('difficulty: [easy|medium|hard|nightmare]') + exit() blocksize, difficulty = sys.argv[1], sys.argv[2] -if not blocksize in ['2x2', '3x2', '3x3', '4x4']: - print('wrong size given') - exit() +if blocksize not in ['2x2', '3x2', '3x3', '4x4']: + print('wrong size given') + exit() splitted_blocksize = blocksize.split('x') size_horizontal = int(splitted_blocksize[0]) @@ -23,9 +22,9 @@ size_vertical = int(splitted_blocksize[1]) boardSize = size_horizontal * size_vertical -if not difficulty in ['easy', 'medium', 'hard', 'nightmare']: - print('wrong difficulty given') - exit() +if difficulty not in ['easy', 'medium', 'hard', 'nightmare']: + print('wrong difficulty given') + exit() debugFillGrid = False debugSolveGrid = False @@ -33,229 +32,277 @@ debugComputeGameGrid = True ############################################################################ -difficultyLevel = 1; +# +# Difficulty grid: +# (number of "force/retry" during grid generation) +# +# | Size | H+V | Easy | Medium | Hard | Nightmare | +# | :--: | --: | ---: | ---------- | ----------- | ----------- | +# | 2x2 | 4 | 1 | 12 - 4 = 8 | 15 - 4 = 11 | 18 - 4 = 14 | +# | 2x3 | 5 | 1 | 12 - 5 = 7 | 15 - 5 = 10 | 18 - 5 = 13 | +# | 3x2 | 5 | 1 | 12 - 5 = 7 | 15 - 5 = 10 | 18 - 5 = 13 | +# | 3x3 | 6 | 1 | 12 - 6 = 6 | 15 - 6 = 9 | 18 - 6 = 12 | +# | 4x4 | 8 | 1 | 12 - 8 = 4 | 15 - 8 = 7 | 18 - 8 = 10 | +# + +difficultyLevel = 1 if difficulty == 'easy': - difficultyLevel = 1 + difficultyLevel = 1 if difficulty == 'medium': - difficultyLevel = 11 - (size_horizontal + size_vertical) + difficultyLevel = 12 - (size_horizontal + size_vertical) if difficulty == 'hard': - difficultyLevel = 12 - (size_horizontal + size_vertical) + difficultyLevel = 15 - (size_horizontal + size_vertical) if difficulty == 'nightmare': - difficultyLevel = 13 - (size_horizontal + size_vertical) + difficultyLevel = 18 - (size_horizontal + size_vertical) -sys.stdout.write('Will generate grid: ['+str(size_horizontal)+'x'+str(size_vertical)+'], difficulty: '+difficulty+' (level '+str(difficultyLevel)+')\n') +sys.stdout.write('Will generate grid: [' + str(size_horizontal) + 'x' + str( + size_vertical) + '], difficulty: ' + difficulty + ' (level ' + str(difficultyLevel) + ')\n') stringValues = '0123456789ABCDEFG' + # draw grid (array style) def drawGrid(grid): - gridVerticalSize = len(grid) - gridHorizontalSize = len(grid[0]) - horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1 - - for row in range(gridHorizontalSize): - if ((row % size_vertical) == 0): - sys.stdout.write(('═' * horizontalLineLength) + '\n') - for col in range(gridVerticalSize): - if ((col % size_horizontal) == 0): - sys.stdout.write('║') - if grid[row][col] != 0: - sys.stdout.write(stringValues[grid[row][col]]) - else: - sys.stdout.write(' ') - sys.stdout.write('║\n') - sys.stdout.write(('═' * horizontalLineLength) + '\n') - sys.stdout.write('\n') + gridVerticalSize = len(grid) + gridHorizontalSize = len(grid[0]) + horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1 + + for row in range(gridHorizontalSize): + if ((row % size_vertical) == 0): + sys.stdout.write(('═' * horizontalLineLength) + '\n') + for col in range(gridVerticalSize): + if ((col % size_horizontal) == 0): + sys.stdout.write('║') + if grid[row][col] != 0: + sys.stdout.write(stringValues[grid[row][col]]) + else: + sys.stdout.write(' ') + sys.stdout.write('║\n') + sys.stdout.write(('═' * horizontalLineLength) + '\n') + sys.stdout.write('\n') + # draw grid (inline style) def drawGridInline(grid): - for row in range(len(grid)): - for col in range(len(grid[row])): - sys.stdout.write(stringValues[grid[row][col]]) - sys.stdout.write('\n') + for row in range(len(grid)): + for col in range(len(grid[row])): + sys.stdout.write(stringValues[grid[row][col]]) + sys.stdout.write('\n') + -#initialise empty grid +# initialise empty grid def generateEmptyGrid(boardSize): - emptyGrid = [] - for row in range(boardSize): - emptyGrid.append([]) - for col in range(boardSize): - emptyGrid[row].append(0) - return emptyGrid - -#A check if the grid is full + emptyGrid = [] + for row in range(boardSize): + emptyGrid.append([]) + for col in range(boardSize): + emptyGrid[row].append(0) + return emptyGrid + + +# A check if the grid is full def checkFullyCompletedGrid(grid): - for row in range(len(grid)): - for col in range(len(grid[row])): - if grid[row][col] == 0: - return False - return True + for row in range(len(grid)): + for col in range(len(grid[row])): + if grid[row][col] == 0: + return False + return True + # (deep) copy of grid def copyGrid(grid): - copiedGrid = [] - for row in range(len(grid)): - copiedGrid.append([]) - for col in range(len(grid[row])): - copiedGrid[row].append(grid[row][col]) - return copiedGrid - -#A backtracking/recursive function to check all possible combinations of numbers until a solution is found + copiedGrid = [] + for row in range(len(grid)): + copiedGrid.append([]) + for col in range(len(grid[row])): + copiedGrid[row].append(grid[row][col]) + return copiedGrid + + +# A backtracking/recursive function to check all +# possible combinations of numbers until a solution is found def solveGrid(grid, iterationSolveCount): - if debugSolveGrid: - sys.stdout.write('solveGrid / '+str(iterationSolveCount)+'\n') - gridSize = len(grid) - cellsCount = len(grid) * len(grid[0]) - numberList = [(value + 1) for value in range(gridSize)] - - global solutionsCount - - #Find next empty cell - for i in range(0, cellsCount): - row = i // gridSize - col = i % gridSize - if grid[row][col] == 0: - shuffle(numberList) - for value in numberList: - if debugSolveGrid: - sys.stdout.write('solveGrid: ['+str(row)+','+str(col)+'] try with value '+str(value)+'\n') - # Check that this value has not already be used on this row - if not(value in grid[row]): - # Check that this value has not already be used on this column - foundInColumn = False - for r in range(0, gridSize): - if (value == grid[r][col]): - foundInColumn = True - - if not foundInColumn: - # Get sub-square - blockColFrom = size_horizontal * int(col / size_horizontal) - blockRowFrom = size_vertical * int(row / size_vertical) - square = [grid[i][blockColFrom:blockColFrom + size_horizontal] for i in range(blockRowFrom, blockRowFrom + size_vertical)] - - # Check that this value has not already be used on this sub square - if not any(value in squareLine for squareLine in square): - grid[row][col] = value - if checkFullyCompletedGrid(grid): - if debugSolveGrid: - sys.stdout.write('solveGrid: grid complete, found solution\n') - iterationSolveCount += 1 - solutionsCount += 1 - break - else: + if debugSolveGrid: + sys.stdout.write('solveGrid / ' + str(iterationSolveCount) + '\n') + gridSize = len(grid) + cellsCount = len(grid) * len(grid[0]) + numberList = [(value + 1) for value in range(gridSize)] + + global solutionsCount + + # Find next empty cell + for i in range(0, cellsCount): + row = i // gridSize + col = i % gridSize + if grid[row][col] == 0: + shuffle(numberList) + for value in numberList: if debugSolveGrid: - sys.stdout.write('solveGrid: recursive call (solutionsCount='+str(solutionsCount)+', iterationSolveCount='+str(iterationSolveCount)+')\n') - if solveGrid(grid, iterationSolveCount + 1): - if debugSolveGrid: - sys.stdout.write('solveGrid: still searching for solution\n') - return True - break - grid[row][col] = 0 - -#A backtracking/recursive function to check all possible combinations of numbers until a solution is found + sys.stdout.write( + 'solveGrid: ' + + '[' + str(row) + ',' + str(col) + ']' + + ' try with value ' + str(value) + + '\n' + ) + # Check that this value has not already be used on this row + if not(value in grid[row]): + # Check that this value has not already be used on this column + foundInColumn = False + for r in range(0, gridSize): + if (value == grid[r][col]): + foundInColumn = True + + if not foundInColumn: + # Get sub-square + blockColFrom = size_horizontal * \ + int(col / size_horizontal) + blockRowFrom = size_vertical * int(row / size_vertical) + square = [grid[i][blockColFrom:blockColFrom + size_horizontal] + for i in range(blockRowFrom, blockRowFrom + size_vertical)] + + # Check that this value has not already be used on this sub square + if not any(value in squareLine for squareLine in square): + grid[row][col] = value + if checkFullyCompletedGrid(grid): + if debugSolveGrid: + sys.stdout.write( + 'solveGrid: grid complete, found solution\n') + iterationSolveCount += 1 + solutionsCount += 1 + break + else: + if debugSolveGrid: + sys.stdout.write('solveGrid: recursive call (solutionsCount=' + str( + solutionsCount) + ', iterationSolveCount=' + str(iterationSolveCount) + ')\n') + if solveGrid(grid, iterationSolveCount + 1): + if debugSolveGrid: + sys.stdout.write( + 'solveGrid: still searching for solution\n') + return True + break + grid[row][col] = 0 + + +# A backtracking/recursive function to check all possible combinations of numbers until a solution is found def fillGrid(grid, boardSize, iterationFillCount): - if debugFillGrid: - sys.stdout.write('fillGrid / '+str(iterationFillCount)+'\n') - drawGrid(grid) - - boardSize = len(grid) - cellsCount = len(grid) * len(grid[0]) - numberList = [(value + 1) for value in range(boardSize)] - - global solutionsCount - - # Find next empty cell - for i in range(0, cellsCount): - row = i // boardSize - col = i % boardSize - - # Ensure cell is not already set - if grid[row][col] == 0: - # Try to fill cell with random numbers, iteratively - shuffle(numberList) - for value in numberList: - if debugFillGrid: - sys.stdout.write('fillGrid: ['+str(row)+','+str(col)+'] -> try with value '+str(value)+'\n') - # Check that this value has not already be used on this row - if not(value in grid[row]): - # Check that this value has not already be used on this column - foundInColumn = False - for r in range(0, boardSize): - if (value == grid[r][col]): - foundInColumn = True - - if not foundInColumn: - # Get sub-square - blockColFrom = size_horizontal * int(col / size_horizontal) - blockRowFrom = size_vertical * int(row / size_vertical) - square = [grid[i][blockColFrom:blockColFrom + size_horizontal] for i in range(blockRowFrom, blockRowFrom + size_vertical)] - - # Check that this value has not already be used on this sub square - if not any(value in squareLine for squareLine in square): - if debugFillGrid: - sys.stdout.write('fillGrid: ['+str(row)+','+str(col)+'] <- '+str(value)+' / ok, no conflict\n') - grid[row][col] = value - if checkFullyCompletedGrid(grid): + if debugFillGrid: + sys.stdout.write('fillGrid / ' + str(iterationFillCount) + '\n') + drawGrid(grid) + + boardSize = len(grid) + cellsCount = len(grid) * len(grid[0]) + numberList = [(value + 1) for value in range(boardSize)] + + global solutionsCount + + # Find next empty cell + for i in range(0, cellsCount): + row = i // boardSize + col = i % boardSize + + # Ensure cell is not already set + if grid[row][col] == 0: + # Try to fill cell with random numbers, iteratively + shuffle(numberList) + for value in numberList: if debugFillGrid: - sys.stdout.write('fillGrid: found final solution\n') - return True - else: - if debugFillGrid: - sys.stdout.write('fillGrid: recursive call (iterationFillCount='+str(iterationFillCount)+')\n') - iterationFillCount += 1 - if fillGrid(grid, boardSize, iterationFillCount): - return True - break - if debugFillGrid: - sys.stdout.write('fillGrid: no solution found ['+str(row)+','+str(col)+'] <- 0\n') - grid[row][col] = 0 - -solutionsCount = 1 -def computeResolvableGrid(grid, maxAttemps): - global solutionsCount + sys.stdout.write( + 'fillGrid: [' + str(row) + ',' + str(col) + '] -> try with value ' + str(value) + '\n') + # Check that this value has not already be used on this row + if not(value in grid[row]): + # Check that this value has not already be used on this column + foundInColumn = False + for r in range(0, boardSize): + if (value == grid[r][col]): + foundInColumn = True + + if not foundInColumn: + # Get sub-square + blockColFrom = size_horizontal * \ + int(col / size_horizontal) + blockRowFrom = size_vertical * int(row / size_vertical) + square = [grid[i][blockColFrom:blockColFrom + size_horizontal] + for i in range(blockRowFrom, blockRowFrom + size_vertical)] + + # Check that this value has not already be used on this sub square + if not any(value in squareLine for squareLine in square): + if debugFillGrid: + sys.stdout.write( + 'fillGrid: [' + str(row) + ',' + str(col) + '] <- ' + str(value) + ' / ok, no conflict\n') + grid[row][col] = value + if checkFullyCompletedGrid(grid): + if debugFillGrid: + sys.stdout.write( + 'fillGrid: found final solution\n') + return True + else: + if debugFillGrid: + sys.stdout.write( + 'fillGrid: recursive call (iterationFillCount=' + str(iterationFillCount) + ')\n') + iterationFillCount += 1 + if fillGrid(grid, boardSize, iterationFillCount): + return True + break + if debugFillGrid: + sys.stdout.write( + 'fillGrid: no solution found [' + str(row) + ',' + str(col) + '] <- 0\n') + grid[row][col] = 0 - # A higher number of attemps will end up removing more numbers from the grid - # Potentially resulting in more difficiult grids to solve! - # Start Removing Numbers one by one - remainingAttemps = maxAttemps - while remainingAttemps > 0: - if debugComputeGameGrid: - sys.stdout.write('computeResolvableGrid / remainingAttemps: '+str(remainingAttemps)+'.\n') +solutionsCount = 1 - # Select a random cell that is not already empty - row = randint(0, boardSize - 1) - col = randint(0, boardSize - 1) - while grid[row][col] == 0: - row = randint(0, boardSize - 1) - col = randint(0, boardSize - 1) - # Remove value in this random cell - savedCellValue = grid[row][col] - grid[row][col] = 0 +def computeResolvableGrid(grid, maxAttemps): + global solutionsCount + + # A higher number of attemps will end up removing more numbers from the grid + # Potentially resulting in more difficiult grids to solve! + + # Start Removing Numbers one by one + remainingAttemps = maxAttemps + while remainingAttemps > 0: + if debugComputeGameGrid: + sys.stdout.write( + 'computeResolvableGrid / remainingAttemps: ' + str(remainingAttemps) + '.\n') + + # Select a random cell that is not already empty + row = randint(0, boardSize - 1) + col = randint(0, boardSize - 1) + while grid[row][col] == 0: + row = randint(0, boardSize - 1) + col = randint(0, boardSize - 1) + + # Remove value in this random cell + savedCellValue = grid[row][col] + grid[row][col] = 0 + + solutionsCount = 0 + if debugComputeGameGrid: + sys.stdout.write('computeResolvableGrid / Remove value in [' + str( + row) + ',' + str(col) + '] (was ' + str(savedCellValue) + ').\n') + drawGrid(grid) + sys.stdout.write( + 'computeResolvableGrid / Check grid unique solution...\n') + + solveGrid(copyGrid(grid), 0) + + # Non unique solution => restore this cell value + if solutionsCount != 1: + if debugComputeGameGrid: + sys.stdout.write( + 'computeResolvableGrid / Failed to solve grid (multiple solutions). Will try with clearing another cell.\n') + grid[row][col] = savedCellValue + remainingAttemps -= 1 + else: + if debugComputeGameGrid: + sys.stdout.write( + 'computeResolvableGrid / ok found unique solution.\n') - solutionsCount = 0 if debugComputeGameGrid: - sys.stdout.write('computeResolvableGrid / Remove value in ['+str(row)+','+str(col)+'] (was '+str(savedCellValue)+').\n') - drawGrid(grid) - sys.stdout.write('computeResolvableGrid / Check grid unique solution...\n') - - solveGrid(copyGrid(grid), 0) - - # Non unique solution => restore this cell value - if solutionsCount != 1: - if debugComputeGameGrid: - sys.stdout.write('computeResolvableGrid / Failed to solve grid (multiple solutions). Will try with clearing another cell.\n') - grid[row][col] = savedCellValue - remainingAttemps -= 1 - else: - if debugComputeGameGrid: - sys.stdout.write('computeResolvableGrid / ok found unique solution.\n') + sys.stdout.write('computeResolvableGrid / ok found solvable grid.\n') - if debugComputeGameGrid: - sys.stdout.write('computeResolvableGrid / ok found solvable grid.\n') +########################################################################### -######################### grid = generateEmptyGrid(boardSize) @@ -271,5 +318,13 @@ computeResolvableGrid(grid, difficultyLevel) sys.stdout.write('Generated grid:\n') drawGrid(grid) -sys.stdout.write('Inline grid ['+str(size_horizontal)+'x'+str(size_vertical)+'], difficulty: '+difficulty+' (level '+str(difficultyLevel)+'):\n') +sys.stdout.write( + 'Inline grid [' + str(size_horizontal) + 'x' + str(size_vertical) + ']' + + ', ' + + 'difficulty: ' + difficulty + + ' ' + + '(level ' + str(difficultyLevel) + ')' + + ':' + + '\n' +) drawGridInline(grid) diff --git a/generator/parse_grids.sh b/generator/parse_grids.sh new file mode 100755 index 0000000000000000000000000000000000000000..85fbe78321b4bd5ed97287ae513c68aaa2af07d1 --- /dev/null +++ b/generator/parse_grids.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +# Generated grids (source) +GRIDS_FILE="$1" + +# Game templates +GAME_TEMPLATES_FILE="${CURRENT_DIR}/../assets/files/templates.json" + +# Parameters +ALLOWED_BLOCK_SIZE_VALUES="2x2 3x2 3x3 4x4" +MAX_GRIDS_COUNT_PER_LEVEL=20 + +# Fetch grid from input file +VALID_GRIDS="$(cat "${GRIDS_FILE}" | grep -v "FAILED" | grep "0" | sort | sed 's/ /_/g')" + +OUTPUT="{\"templates\":{" +FIRST_BLOCK=1 + +function clean_grids() { + GRIDS_TO_CLEAN="$1" + echo "${GRIDS_TO_CLEAN}" | cut -d"_" -f4 | sed 's/^/"/' | sed 's/$/"/' | tr '\n' ',' | sed 's/,$//' +} + +for BLOCK_SIZE in ${ALLOWED_BLOCK_SIZE_VALUES}; do + GRIDS=$(echo "${VALID_GRIDS}" | grep "${BLOCK_SIZE}"); + GRIDS_COUNT=$(echo "${GRIDS}" | wc -l | awk '{print $1}') + echo "${BLOCK_SIZE}: found ${GRIDS_COUNT} grids" + + # example with 100 grids, 10 per level: + # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + # | <- 1/3 -> | <- 1/3 -> | <- 1/3 -> | + # | | | | + # 1111111111------------------2222222222-----------------------3333333333-------------------4444444444 + # | 10 | | 10 | | 10 | | 10 | + + # easy + GRIDS_LEVEL_1="$(echo "${GRIDS}" | head -n ${MAX_GRIDS_COUNT_PER_LEVEL})" + # medium + GRIDS_LEVEL_2="$(echo "${GRIDS}" | head -n $(echo "${GRIDS_COUNT}/3 - ${MAX_GRIDS_COUNT_PER_LEVEL}/2" | bc) | tail -n ${MAX_GRIDS_COUNT_PER_LEVEL})" + # hard + GRIDS_LEVEL_3="$(echo "${GRIDS}" | tail -n $(echo "${GRIDS_COUNT}/3 + ${MAX_GRIDS_COUNT_PER_LEVEL}/2" | bc) | head -n ${MAX_GRIDS_COUNT_PER_LEVEL})" + # nightmare + GRIDS_LEVEL_4="$(echo "${GRIDS}" | tail -n ${MAX_GRIDS_COUNT_PER_LEVEL})" + + if [ $FIRST_BLOCK -eq 1 ]; then + FIRST_BLOCK=0 + else + OUTPUT="${OUTPUT}," + fi + + OUTPUT="${OUTPUT} \"${BLOCK_SIZE}\": {" + + OUTPUT="${OUTPUT} \"easy\": [$(clean_grids "${GRIDS_LEVEL_1}")]," + OUTPUT="${OUTPUT} \"medium\": [$(clean_grids "${GRIDS_LEVEL_2}")]," + OUTPUT="${OUTPUT} \"hard\": [$(clean_grids "${GRIDS_LEVEL_3}")]," + OUTPUT="${OUTPUT} \"nightmare\": [$(clean_grids "${GRIDS_LEVEL_4}")]" + + OUTPUT="${OUTPUT} }" +done + +OUTPUT="${OUTPUT} }}" + +echo ${OUTPUT} | jq > "${GAME_TEMPLATES_FILE}" + +echo "Ok, done. Grids saved in ${GAME_TEMPLATES_FILE}" diff --git a/generator/solve.py b/generator/solve.py index 331ef6fb93f2361bede668bcfd2fc4cbba1b8c98..f575588fd2c63ca2eac2e7909ebb7881379240e1 100644 --- a/generator/solve.py +++ b/generator/solve.py @@ -12,18 +12,17 @@ # import sys -import math if (len(sys.argv) != 3): - print('Usage: solve.py block-size grid') - print('block-size: [2x2|3x2|3x3|4x4]') - exit() + print('Usage: solve.py block-size grid') + print('block-size: [2x2|3x2|3x3|4x4]') + exit() blocksize, gridTemplate = sys.argv[1], sys.argv[2] -if not blocksize in ['2x2', '3x2', '3x3', '4x4']: - print('wrong size given') - exit() +if blocksize not in ['2x2', '3x2', '3x3', '4x4']: + print('wrong size given') + exit() splitted_blocksize = blocksize.split('x') size_horizontal = int(splitted_blocksize[0]) @@ -32,196 +31,206 @@ size_vertical = int(splitted_blocksize[1]) boardSize = size_horizontal * size_vertical if (len(gridTemplate) != boardSize * boardSize): - print('wrong grid length (should be '+str(boardSize * boardSize)+')') - exit() + print('wrong grid length (should be ' + str(boardSize * boardSize) + ')') + exit() debugSolveGrid = False ############################################################################ -sys.stdout.write('Will solve grid: ['+str(size_horizontal)+'x'+str(size_vertical)+'] // '+gridTemplate+'\n') +sys.stdout.write('Will solve grid: [' + str(size_horizontal) + + 'x' + str(size_vertical) + '] // ' + gridTemplate + '\n') stringValues = '0123456789ABCDEFG' + # draw grid (array style) def drawGrid(grid): - gridVerticalSize = len(grid) - gridHorizontalSize = len(grid[0]) - horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1 - - for row in range(gridHorizontalSize): - if ((row % size_vertical) == 0): - sys.stdout.write(('═' * horizontalLineLength) + '\n') - for col in range(gridVerticalSize): - if ((col % size_horizontal) == 0): - sys.stdout.write('║') - if grid[row][col] != 0: - sys.stdout.write(stringValues[grid[row][col]]) - else: - sys.stdout.write(' ') - sys.stdout.write('║\n') - sys.stdout.write(('═' * horizontalLineLength) + '\n') - sys.stdout.write('\n') + gridVerticalSize = len(grid) + gridHorizontalSize = len(grid[0]) + horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1 + + for row in range(gridHorizontalSize): + if ((row % size_vertical) == 0): + sys.stdout.write(('═' * horizontalLineLength) + '\n') + for col in range(gridVerticalSize): + if ((col % size_horizontal) == 0): + sys.stdout.write('║') + if grid[row][col] != 0: + sys.stdout.write(stringValues[grid[row][col]]) + else: + sys.stdout.write(' ') + sys.stdout.write('║\n') + sys.stdout.write(('═' * horizontalLineLength) + '\n') + sys.stdout.write('\n') + # (deep) copy of grid def copyGrid(grid): - copiedGrid = [] - for row in range(len(grid)): - copiedGrid.append([]) - for col in range(len(grid[row])): - copiedGrid[row].append(grid[row][col]) - return copiedGrid + copiedGrid = [] + for row in range(len(grid)): + copiedGrid.append([]) + for col in range(len(grid[row])): + copiedGrid[row].append(grid[row][col]) + return copiedGrid + # Init grid from given template def initGrid(boardSize, gridTemplate): - grid = [] - index = 0 - for row in range(boardSize): - grid.append([]) - for col in range(boardSize): - grid[row].append(stringValues.index(gridTemplate[index])) - index += 1 - return grid + grid = [] + index = 0 + for row in range(boardSize): + grid.append([]) + for col in range(boardSize): + grid[row].append(stringValues.index(gridTemplate[index])) + index += 1 + return grid + # Check if grid is fully completed, without any empty cell def isFullyCompleted(grid): - for row in range(len(grid)): - for col in range(len(grid[row])): - if grid[row][col] == 0: - return False - return True + for row in range(len(grid)): + for col in range(len(grid[row])): + if grid[row][col] == 0: + return False + return True + # Check if a list contains duplicates (conflicts) def containsDuplicates(list): - tmp_set = set(list) - return (len(list) != len(tmp_set)) + tmp_set = set(list) + return (len(list) != len(tmp_set)) + # Check if given grid contains conflicts def hasConflict(grid, size_horizontal, size_vertical): - # Check horizontal conflicts - for row in range(len(grid)): - values = [] - for col in range(len(grid[row])): - value = grid[row][col] - if value != 0: - values.append(value) - if containsDuplicates(values): - # print('Horizontal conflict found') - return True - - # Check vertical conflicts - for col in range(len(grid[0])): - values = [] + # Check horizontal conflicts for row in range(len(grid)): - value = grid[row][col] - if value != 0: - values.append(value) - if containsDuplicates(values): - # print('Vertical conflict found') - return True - - # Check sub-blocks conflicts - for blockRow in range(size_horizontal): - for blockCol in range(size_vertical): - # Get sub-square - blockColFrom = size_horizontal * int(col / size_horizontal) - blockRowFrom = size_vertical * int(row / size_vertical) - values = [] - for rowInBlock in range(size_vertical): - for colInBlock in range(size_horizontal): - row = (blockRow * size_vertical) + rowInBlock; - col = (blockCol * size_horizontal) + colInBlock; - value = grid[row][col] - if value != 0: - values.append(value) - if containsDuplicates(values): - # print('Sub-block conflict found') - return True + values = [] + for col in range(len(grid[row])): + value = grid[row][col] + if value != 0: + values.append(value) + if containsDuplicates(values): + # print('Horizontal conflict found') + return True + + # Check vertical conflicts + for col in range(len(grid[0])): + values = [] + for row in range(len(grid)): + value = grid[row][col] + if value != 0: + values.append(value) + if containsDuplicates(values): + # print('Vertical conflict found') + return True + + # Check sub-blocks conflicts + for blockRow in range(size_horizontal): + for blockCol in range(size_vertical): + # Get sub-square + values = [] + for rowInBlock in range(size_vertical): + for colInBlock in range(size_horizontal): + row = (blockRow * size_vertical) + rowInBlock + col = (blockCol * size_horizontal) + colInBlock + value = grid[row][col] + if value != 0: + values.append(value) + if containsDuplicates(values): + # print('Sub-block conflict found') + return True + + return False - return False # Check if a value is allowed in a cell (without conflicting) def isValueAllowed(grid, size_horizontal, size_vertical, row, col, candidateValue): - testGrid = copyGrid(grid) - testGrid[row][col] = candidateValue - if not hasConflict(testGrid, size_horizontal, size_vertical): - return True - return False + testGrid = copyGrid(grid) + testGrid[row][col] = candidateValue + if not hasConflict(testGrid, size_horizontal, size_vertical): + return True + return False + # Get allowed values in a cell (witjout conflicting) def findAllowedValuesForCell(grid, size_horizontal, size_vertical, row, col): - allowedValues = [] - if not hasConflict(grid, size_horizontal, size_vertical): - for candidateValue in range(1, size_horizontal * size_vertical + 1): - if isValueAllowed(grid, size_horizontal, size_vertical, row, col, candidateValue): - allowedValues.append(candidateValue) - return allowedValues + allowedValues = [] + if not hasConflict(grid, size_horizontal, size_vertical): + for candidateValue in range(1, size_horizontal * size_vertical + 1): + if isValueAllowed(grid, size_horizontal, size_vertical, row, col, candidateValue): + allowedValues.append(candidateValue) + return allowedValues # Globally solve grid def solve(grid, size_horizontal, size_vertical): - iterations = 0 - maxIterations = 500 - boardSize = size_horizontal * size_vertical - - # Loop until grid is fully completed - while True: - iterations += 1 - if isFullyCompleted(grid) or (iterations > maxIterations): - break - - if debugSolveGrid: - print('===================================') - print('Iteration: '+str(iterations)) - - # Get first/next cell with only one allowed value - candidates = [] - if debugSolveGrid: - print('Searching for empty cells...') - for row in range(len(grid)): - for col in range(len(grid[row])): - if grid[row][col] == 0: - if debugSolveGrid: - print('Found empty cell ['+str(col)+','+str(row)+']') - candidates.append([row,col]) - - if len(candidates): - for candidate in candidates: - candidateRow = candidate[0] - candidateCol = candidate[1] - allowedValues = findAllowedValuesForCell(grid, size_horizontal, size_vertical, candidateRow, candidateCol) + iterations = 0 + maxIterations = 500 + + # Loop until grid is fully completed + while True: + iterations += 1 + if isFullyCompleted(grid) or (iterations > maxIterations): + break + if debugSolveGrid: - print('Allowed values for cell ['+str(candidateCol)+','+str(candidateRow)+']: '+str(allowedValues)) - if len(allowedValues) != 1: - if debugSolveGrid: - print(' Non unique allowed value for cell. Skip to next cell') - else: - value = allowedValues[0] - grid[candidateRow][candidateCol] = value - if debugSolveGrid: - print(' Found unique allowed value for cell ['+str(candidateCol)+','+str(candidateRow)+']: '+str(value)) - drawGrid(grid) + print('===================================') + print('Iteration: ' + str(iterations)) + + # Get first/next cell with only one allowed value + candidates = [] + if debugSolveGrid: + print('Searching for empty cells...') + for row in range(len(grid)): + for col in range(len(grid[row])): + if grid[row][col] == 0: + if debugSolveGrid: + print( + 'Found empty cell [' + str(col) + ',' + str(row) + ']') + candidates.append([row, col]) + + if len(candidates): + for candidate in candidates: + candidateRow = candidate[0] + candidateCol = candidate[1] + allowedValues = findAllowedValuesForCell( + grid, size_horizontal, size_vertical, candidateRow, candidateCol) + if debugSolveGrid: + print('Allowed values for cell [' + str(candidateCol) + ',' + str( + candidateRow) + ']: ' + str(allowedValues)) + if len(allowedValues) != 1: + if debugSolveGrid: + print(' Non unique allowed value for cell. Skip to next cell') + else: + value = allowedValues[0] + grid[candidateRow][candidateCol] = value + if debugSolveGrid: + print(' Found unique allowed value for cell [' + str( + candidateCol) + ',' + str(candidateRow) + ']: ' + str(value)) + drawGrid(grid) ######################### + sys.stdout.write('Building start grid:\n') grid = initGrid(boardSize, gridTemplate) drawGrid(grid) sys.stdout.write('Checking grid:\n') if hasConflict(grid, size_horizontal, size_vertical): - sys.stdout.write(' - oups, initial conflict found.\n') + sys.stdout.write(' - oups, initial conflict found.\n') else: - sys.stdout.write(' - ok, no initial conflict found.\n') - - sys.stdout.write('\n') - - sys.stdout.write('Solving grid...\n') - solve(grid, size_horizontal, size_vertical) - - if isFullyCompleted(grid): - sys.stdout.write('Ok, solved grid:\n') - drawGrid(grid) - sys.stdout.write('Ok\n') - else: - sys.stdout.write('Failed to solve grid\n') + sys.stdout.write(' - ok, no initial conflict found.\n') + sys.stdout.write('\n') + + sys.stdout.write('Solving grid...\n') + solve(grid, size_horizontal, size_vertical) + + if isFullyCompleted(grid): + sys.stdout.write('Ok, solved grid:\n') + drawGrid(grid) + sys.stdout.write('Ok\n') + else: + sys.stdout.write('Failed to solve grid\n')