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')