/* This code includes region comments to collapse code sections in VS Code via the "#region folding for VS Code" extension by maptz */ //#region Imports import { FreeCost } from "./api/Costs"; import { Localization } from "./api/Localization"; import { theory } from "./api/Theory"; import { TextAlignment } from "./api/ui/properties/TextAlignment"; import { Thickness } from "./api/ui/properties/Thickness"; import { Color } from "./api/ui/properties/Color"; import { Theme } from ".api/Settings"; import { LayoutOptions } from "./api/ui/properties/LayoutOptions"; //#endregion //#region Globals var id = "sudoku_theory_alpha"; var name = "Sudoku Alpha"; var description = "A minigame theory which allows you to play different difficulties of sudoku (alpha version, still missing a lot of the ultimately planned features)"; var authors = "AfuroZamurai"; var version = 1.7; var currency; var easy, medium, hard, expert, omega, devilish; var difficulty; /** * A board is containing 81 cells, with each cell being the following datastructure (TODO: change to a faster representation) * * cell = { number: number, isGiven: number > 0, cornerMarks: [], centerMarks: [] } */ var easyBoard, mediumBoard, hardBoard, expertBoard, omegaBoard, devilishBoard; var easyUndoStack, mediumUndoStack, hardUndoStack, expertUndoStack, omegaUndoStack, devilishUndoStack; var easyRedoStack, mediumRedoStack, hardRedoStack, expertRedoStack, omegaRedoStack, devilishRedoStack; var easyTimer, mediumTimer, hardTimer, expertTimer, omegaTimer, devilishTimer; var easyBestTimer, mediumBestTimer, hardBestTimer, expertBestTimer, omegaBestTimer, devilishBestTimer; var currentStartTime; // intentionally almost completed board for testing purposes var test; var testBoard; var testUndoStack; var testRedoStack; var testTimer; var testBestTimer; const TEST = "Test"; const EASY = "Easy"; const MEDIUM = "Medium"; const HARD = "Hard"; const EXPERT = "Expert"; const OMEGA = "Omega"; const DEVILISH = "Devilish"; const REWARD_TEST = 1; const REWARD_EASY = 30; const REWARD_MEDIUM = 150; const REWARD_HARD = 750; const REWARD_EXPERT = 2500; const REWARD_OMEGA = 5000; const REWARD_DEVILISH = 10000; const ROWS = 9; const COLS = 9; const BOARD_SIZE = ROWS * COLS; const BOX_SIZE = 3; const NORMAL_MODE = 0; const CORNER_MODE = 1; const CENTER_MODE = 2; const UNDO_MODE = 0; const REDO_MODE = 1; var isPopupOpen = false; var gameGrid = null; var hintLabel; var undoButton; var redoButton; var timerLabel; var bestTimeLabel; /* Puzzle difficulty for now is defined by the number of givens. Easy = 47 Medium = 42 Hard = 37 Expert = around 32 (30-33) Omega = difficult puzzles provided by the user Omega from Discord Devilish = custom searched puzzles with high challenge */ let easyPuzzles = ['094260150700013694681090703109074580503680940020001367945700206830009470000500000', '528304176371060800000807025103200704089401200040953010230605080004709502065008430', '603275409049086275200109630050000007700012306816093040930604820020800064408000753', '900106000000859421251347080045910308030004600790630005823461750000705263507000014', '008490512009006000750213089500100200821370496040062103096001840400058301185034900', '507006001010502467480317502690003208803004015102060700060400970758630004934021050', '050896300104302500863501009280704901010269487900080605530600700021007800070425103', '007040600516702040300960000030200984009310275704059136401038592208197003060005807', '070300809300021075108975340512096008830400097040130206450280063020047001789600020', '100200005098370004002518960041002500706001300280600410315760208604180793879403050', '600501000827940105130008900412600873000810452000724000070459200260087590954102380', '005708062003495017800020059540179080030050201608234090154300000089540076720981004', '030691420806473001004080607908000063240300570367015000509046300403250896080130705', '208530961070918300039024700380200004001400080402891637023009406800000510714056809', '048037026207601049301049700013400072600700854400928031700806500009102068006350097', '000978000064521700897460500600107928051006037078340060480030091100892670020014803', '207540001503900247980021030002006054746890120351070960400050300100204076025017480', '001004705007890040024056380050083164413070029680410003205937618098002407006100050', '062850907105260030489031060093480250214905300050023000840092170500647820000508600', '010406070573281049960530020048760350026305198050000460007853216031600080005002700', '009078103341695287700132450807904000410023900026007008000200095090081630600059812', '865431700190060300407050008500210976710680030326579840904800603080107029000300480', '873009060926007004045682093010300527708210030600975801084720600007400350301856000', '403680015002593470756401900609010020104700859075040003921806007500174390000250001', '800029530002645700904783060020400906706902413500310872690500027030890040070201309', '291700805856923400004508260067150948015689700000300056579001600003067002002800301', '608001907053078041027930060406809570810756093005042180300200700079003620081467000', '700000836002100954605038270540019608020840593908065147453600080200783000871020000', '430670508950481200008000469109507304240000007560034091000825700604003080875146032', '703492605805601002406300090008156730001049006000230410080924503062510074500068920', '630714502050638970008005006746100830093070045810049200500903000027081050361527400', '752130604008026031100784952503001268017200400280403000900800005374605009005097140', '906530071080069005005810096678294503503706249490000000320000650769320180000071930', '576800149403700050208400360000040708080256431002370690309087204001930576760010080', '702800653060003084008700201005480309304150800280076015903600500800294037140530926', '873945601051003489009086730406010390307290856900600004100802003702050008060370140', '531279064002541003070300052100000438097018026450600900640805017210004389709120000', '430059812006017030189200705070342591013508020200970003840020309000005148001480250', '106785209400000301289130706768092003051006908900308070817900064004050132020640007', '009080400314952786060703095480200031692104078700560000906820014803400900007305062', '200700609800130007065002400502090306079001500480320001603278190027513804158640730', '050837060610590827070060354030900000724308619006410783000105070087003295240780100', '864325010037890020520610483001470238000500094943002060015006009470031600206750040', '701020900420600803389500620000731200008050400150842796804060179010007308600183542', '000800703070026040280307060304079080007210650521684097439762008600000409710095236', '007960143200031580004078206851200009600090014903000700700024968428650071160783020', '250700000809403120407219560970105000008670350043980706600321004714096200092500680', '014008700958020000276010540720083450065900823809200607082090304100030260643750081', '500090001604003295709050436071002600902108753035600024000310560047800902056907348', '983204105652030784001080329300000600067000513090320007516093208034760951700100400', '960000507705194000084705000300827091400350268020040003012508974547610080803472600', '913780560605902104020000930060491753007056009009300810091203687308060240046570000', '020003000300140506041090870408630090902854107753920608684700350007380062000069781', '500060014090800026600010980029436075004152809103790042078340200016900053030621790', '048006910700000020010247065859162003071059602400873000002704109500601038084395076', '038070095509000010270098463600154308005063140003987520057419600820036000906020704', '803097054017003009025601008000469800700130605096705423089310572200008306304506001', '453060792009003406761094300000720108000349007302158000500000640200615809698437250', '009080070800391054403270190006134509041700200385629010098500041067042035004000926', '003219640010473029400050710940687032082031900070904806060095081098000305504160200', '600571940508040731001308620900000350057230186012860400035024060009700010874056290', '402968000001325400936700582070003026394000718600190040069010073840600250507802604', '102940086684005070750006314010070030000053401003189705948231607005008002201564003', '200890076010070390060004518035682000702049635649300001500003700300460250498725160', '008040016590137042000580739819700650632004000704960283105390007040005098083002105', '930716080165004307040030026006408701000000865873601209600020053397805000251900478', '845316090193000006260059308408030760009047820301268450704601930000584000012000084', '302901806061520070400806051603000400708000029940680317134205008290308564500040130', '013450926600002008050916000560204803200195047479800500087309400900580201305640780', '080640732720050149930071805002193580318005200600800307007006028293400050005912400', '040600901718005263056320740890002470070409125004006809060893014485160090039200000', '984007205206090010053008007401879506020310700005206381047060152500080904009452608', '740309600800402350309060107002038500014900006638104702207580460450790081100243070', '800260715700080403325070860032519607070300090598027301907401006050000104604752008', '605040012028067500400592876030001090796008251250900304069300008300705169080629400', '005348020003200618070096000401027935096500102007031486020013067700069250608750304', '523000041861003795400061008080007350054012067609050412005296100938100520010800074', '690723801850401300007586020065219080083000492000008165040060530030970018928105007', '136794800580230100970500064051000780800150249009002016690018472403027058000900031', '059070200804130900160802003415086390080019067970240850008007000632950780790408630', '040029736006740205020003900653297810071060059800305027300472008480050003765130090', '401607802003008506065392047007009014084231050900000603256083070300124060109506308', '050004680000982041184605007807300154306108702001420806018000200470296300039870405', '560200049071400200004009768027604030300507920459308000010032006603701582742805301', '002108306093050400000740019316400975004630820050907634030004162560309048427000093', '507400306900210400431600082280150034640008571000060298870590100104032860006041029', '007046030903217084040530270030009412100700050089050367002804095008375100076190843', '062970510400300208395100040020000354950041720007500981034215860080403100506789030', '600200009907084200520697031400106900000470516196508400701942350050003104348750002', '968243051040600903520107460004080179007024386036001000602030800410865037085700090', '824057139500089074001420080407000610300971002200840090059204860082305741103000920', '734086029920004836056009170107840090083907005549362708305020060600570400000001250', '460895073097031008583004000342070965006059314015306820020487001008010640100002009', '000019050409056281201800306002490803093002614148070905980004002027065108600708530', '028496700000005986000710045819002403740063510000901872650127090087300024290600150', '010004602604020830008003495820000507300617009060082004159030748432900106780451903', '007403080200605374048072060000369805900004010405800903876900052102048036530026198', '596032007804000631300000920403026819009010750160807200940703560052461098008059004', '820046370600215409004800206740008005050637100106594002098700513501309620362000090', '097405062300987500500602093002060901954070038680009275879000326405700100013006057'] let mediumPuzzles = ['180520360200600489079480210000700900500001620060008053718962000040070806056034070', '093006075052040810780203904000030406246807000509024100315000090020071008007002341', '087000950940070631506900020008201003030050042201034700020009400803467205704510008', '008020016010005009290036587030850901007490820000001030820049673649000100573012000', '920057600068400700500903001092108503403020079150009000817005306600070200230684010', '007063900400592736600000452042309000100254800980070000350701004060900170870645300', '703094200100630900040008760650200807008015049079000001004380070017450096236001405', '940003170005614000063207408800175620021309007076008501008000000430506002619040080', '370920000000730692502410380900867005030000800108200069010004056063100400085672903', '740130050261040809030082000100405086003000975600803100820716000076008041905020068', '950032008004000090001040367413256809608100050070004030040067980507009023109020045', '201070946094600007006400528718030090900184030420900050000018273080020060532006100', '790054300203067540004081290870000123100038050306020079450002000008009012002870460', '100847050500920486000063200400000060678005300312780500709250108040109020051030704', '068430097050209480300010260020708506105002730097100840006024000574086900030000054', '006503402040160097051029008005002073410900060027651800060084701000200306830700950', '042095810005000004836047209010080025429050630080064001000809063001076980000002147', '000908537000003261302060400700805000591076042086190000207504090940007325130080004', '203605007178240000059870142002007400000108075000060389060520900930004500024096018', '408053769050009000690000235009001584002074310104068000006300470973402600200080190', '307400208082936740000027095058600430400070120106003000765004900910062080020005073', '102900000009042600007186902000001038516030700283507010701050004035419207098020060', '260071408000500901009040063430659002000480010875003040086214007007068090314000086', '491300020070001905030869701250008103068100090000020006305700800109486300007903214', '030806050000400006008052490280040000047093080503081200410968307020170940079304061', '091800032500001064763050090630200405008509013000376280020190006340705001900000847', '182006903903028046400100070310080500004000397006075080509607810000802700038409025', '840007069030904502502680700060071843704800601180000050075020400428300106906008000', '030680721020000080085071900579106030008950102000807006302560000001398064046002059', '540076310823000000107920500000614930006200740910350800700190060030068250090402108', '400301509060090030930050147109065200006832904020149070600000020085670301010008450', '090003852231900460408720000049005100802130090000009786070080631103604000006070948', '070042050320906018009078600701500804085204300040817090508003409007691200600400100', '100368020000007801700050390261709450308400109045006000013072004420681705000900602', '060510802200003100013609005021706093598100076007054000000270008159400027700300614', '297130040008004009010090700025840160600950034380600025046000251800701000139420070', '704860000290040500106035004040600710670921308302000650930780260508000490000502801', '090005000060004378801627005042108000370452106508090034000070043903001800784503010', '080629105300080200600700409007205090023807014061030800400102000075090601190546023', '000208963309760280286004500007500000198007040030809672060305009072400001040182050', '000005902320071540001248630070000420860120090500364701710053069030090000040002853', '036090251918060407072301690301070506049000003000803040694007020080650100007982000', '805603204060080070300194500093500000178430600502001830930002746004010052050007980', '604000153103604780070000260496052000037091600802407035005210400000086071380009500', '080430195004960200000050060410892750075010380806503401007000008300005942920106003', '030472580801000002000100690015029070089030106307008024742006800050291043190807000', '002340816800205047069078250901480705005030108300090004108007462070600000506000301', '007005900160784032300096400200850007015062084800401250970000020500137006081029003', '140580963690070208803160400302900701008001300904037520000090000209600805570008140', '026083570785009203030027040100390068040706001209010000070900104010268090300401650', '059812600020003900108069504075094030001280000940056107704000002003008069206070341', '000502089000074203502639471156783004030095000809240000008000506400061038907300002', '192070054500040008006300070654910237008403060023567801001009020409100005070680900', '320780960609005108841200750400839001000050039000170205530920800710463002006000040', '900070100071500402320061078006003040000206931500104000060807019180349706003605204', '034906008079800516000720900056092704120504680407308000008050327010000065360007400', '380709006040003180620005403005020009100030740864900325950010804000057032070800561', '504703800083100602670080300130072004009306050752048160098020016000605400005030709', '020487030000600400040010809104306098297008040008049001075061302609800500810530906', '852070410070006290006245000609001802005009001040837609004700003320514007107060084', '008300700643000018917040520031409002400705380006020090069014870020607040070908036', '009601030200000050460050708002435980700816240000092516103520000970084000508309170', '618209405049306701030450000075040019200800304094105870006700182007508090080060000', '590003006001200705037100089045602010800047500623008900052900007004021653380054090', '528040010047000003090600807762010090000000004830057062009461508001572036006389470', '059068130300010897001490200090004070417605300608009005030840501000906008706251900', '302510084051008609004970000070360541245000307103005290800030056500204973009000010', '870951206106070800053040090030020009015806007029005468000360004940017603000009152', '123070009769420080040090000005300720900804031400050068350007196890130472007006003', '074300612520180000913200400000073008000691534059002007100726809208400306067000050', '106000472800000905045300860658037090003146580400890300020609700781503600500080010', '652000981800002046041896005700000160000705839000341020029070413073524000006003700', '800701426007500009000024058534870260601200070090010083425683190978002000310000000', '097530240010000009043607508309008600680050371072400900060240890908016050000780102', '800000009000718000531062007709050628603480500008600904010806490400001703976204085', '050640170000530096968201003093724015670000902200100830584000060000005001136907500', '640180390900654708081900064000200401000003007258740900812070005430020070090006283', '080650104615030070340807009530020900008100725206900400027040081000590007104208306', '003659040700180030026340008900830020207090005804205601015008472000520069090700803', '580410092240807001003905000600702003038009257720384010810070649000600020906000708', '710920500006100809459803020540700096083409002000500081160008930095001270072300004', '500962103008307260300500009061890057005210800400703010070620030003005908902108740', '000100346200709015035480029304060082608502003790018004927051000001090657060800000', '175008204090105073384702090500200009003070450400000716001390608046001900038050027', '502061309379000046610300005800054700006102594090706801040205030100079052008000907', '092047016506208007000090530207006000000300784450170003609803000120760300083910465', '820601000517000600034820090005706180142980076086130450000570010901000830450300200', '300206789050009064602470010006010423130000006074083091420000050519740008708095000', '487903001200007008060021050730506410102708900900002005504019286603205007000600509', '153792060090053102047800050705200049029000800014968520070000306001309200002004015', '400108070305002001019406238203800600078009102601527003054203800102600004007004005', '084965010361720900502000006000000037037416092026530481009184005000270000040053700', '570000008030015672028407039060040800004709065901603024702154000000900250810036007', '009004506068251930537960000600410050084502619020800700051003200076000394002080070', '586700400000052070092003105019304600004510297070289010050608043460930820300007000', '740610005500790403096350200008070604375080009964023000082530007450000010107049080', '001090748706400920400820065520004316008030070040069800930572001160000200070310094', '010206700679081400085009006000030002020147358708900600400013807093002510057804060', '200087600070000290609210705056008001390641800800530400000076389908054072031090004', '500910003090370621012008095001504200486720000075093108003200860740006310000000954'] let hardPuzzles = ['060005790758100000903600205124903080306050100000270004082019350500400800000030040', '000000100020600097046890250200000781050003046609087300103940002000710500005206408', '063054000001000480500710039002500007006023010800906340000048190038005276100060004', '006950087007208040043010500081006200200340100060000975050000803432100006000790052', '070086509604000030020430001405720006092000000703108024010500000509071082047003060', '000090837003085900000420106095260000078031000100008400061057240204000308739000600', '208753610040000038006100209000498070600000495000005302800310540104007000073506000', '310290006506001300009600814070800200205300680008004075197030000000080007060400529', '060320000008900563000004289080000050704210000013507600835140070007090120002065030', '000000001029570060174609580005800920306420108040000076030908205450006030001300000', '390018000806007400014000820605031907000006080100204056260009000007650009539000074', '009000468000340170070805029095026030010403600620078900060000213001700000830050004', '002098100610004070059031000105060390020000501040900862090387200060100003700050480', '086702390502000041004001007810065000030008526000490000740300900900507800258010060', '930050061147300900068040200050200000814000300000814090706009005401003080300072640', '005008009410005730200017560108020095540906320003000000090004000070509642300160080', '010005203009020080002400700008902307090703058300086004000074060100060805560010439', '703085090040003170100207003006008304057326810020090050600700280005004006891000000', '005010060701800230040930508003040790100520086068000000200354870004079020930000100', '900004230206915004081070000094037020010206045560090380100008000000000702007649500', '410007502890060004003805600200701406070090810000053000040610730158000209300009040', '600492700019000250000050360502678040003200070801005002340001020007004508008703010', '400500070000980010560002084380250600000037920002041003234090105901006007006300002', '003007250100093070060500094000008467370040800009026010050809002280301040901060005', '000028400083000026750000318205706890040800762000910004070065080100430007096100000', '010593000005160792008000500806054100007020045200009870400005031681002000003070604', '736520080000076002190040500000710020040080000208005604020908040409000836385001070', '080690075920000040400105803300007501010400037506308000090000302650009704204070060', '180406702090070140002013000840009030017050690300800020000087200960002305004005019', '867050023409000600002830007001004002095070040700681000570000216200360900000107085', '050003080801002730007050400100400000063715240075020096008100967040089012309000000', '100009006736020040004073080302064010010705020000800905200001068085340200900080703', '500783602231900000070000400018530000005012098900006003100040060002090874007058301', '009002010400801009570000830008029700050004306600300000102405670730010904905700280', '030402000010000048850067003060004309002608017740300025580000962079000500001503800', '900705120300020080007189403009503010026940000803070600540000708702000069008350000', '040270008507006010100090306009015640060007085800040290050060700201430009430050020', '000040065031870040007259038010002683490000500203061400800704016000930070000000902', '050000001907000402003064570810030720600085030009027005700000089000013607204098150', '010789300020006180490003070001370200800005430000028095065047000009600000307501602', '000000090209560800341970200003710500068090370010000640000024006050007108820150430', '903005071001000008060203059090067800072104030105000760506030200800001040040059083', '001000280385010004200000009000065700104379800650480002760150000900004063503800120', '014900200760300005000001080023050091000409030075610802051037420200000008098540006', '040090830138000670700600104050470086063205400970000010600801023005730000082050000', '405006301609000470370200050000920700008451600900078005290060013003094200500100008', '705002060004071508900034070009045020506080013800960000000008651007000239602010400', '860005004000380100000602093309000001040200060106470028430028009000910530007560402', '048000005200300190701205800020081040500036710079004068000053206406010903000007050', '000094100630050074015200600000940807070100903908320000086732050200080001094500300', '300109000007002190000508042260000703870056000410800200042091500008400670601083900', '080701002006500000095863000020430067017002940360000508800046001730050400001009830', '028060009901700205070005000094806001000320648000500030030050182005403700789010400', '000006100301800095907032040700900600020057008089640720100403070005009060406210080', '900030520000870009064095000306710800200080375090500006810000402072409030005100960', '750400000200000064006310907938000005600009120042060080000024508000795306001038072', '070000003034891026802005100000064070090080465016200030021040300780500002000030918', '400103070907048310012500080003006509620804700071000000065097840000065100000300250', '470006350002940000081000009000580094043070000010690720500307012030250870067008900', '320000450005000601061200009070904160506021000903805000800079200030046908007080034', '924008006000042503037100004040080300056970100782050040008003065010090402000605070', '306080000400309520070004081000960054002710938501000002060803010283000097000247000', '960000080040085300507049620701000003830026407004800012090002000300500006216008095', '200705106700908300043010000007103260900500800431200090080060720005009080124050009', '070006410620090370080000069000310090960280005047560020005420100830001040400970003', '100800526006095070040072019401050003009000800360400107074908200603004900008001060', '405093001270000060613008005800200490047019602000075300026007100030560009008040070', '008012070060000089100607203091030506520800704640000300900301002075460001004050900', '080000001501400670720309040095026010204008590600005708900200050160070003307000460', '402030000006400079150689302000903006904007513010040080020075000380200001000160205', '062073000080402601503000090000500716305069802400210000800000000020805173009641500', '760000810089000004241805073018209060007080400003460980000700100070920300950600020', '506403900020000080107890500805610030070004210240900000004068103000700860900301420', '071000040000142000063090502300207000027401690056080700048319070610000005009005480', '204080605305604098000020130903700040001098007600002900000000050000237081047516300', '006000412100500060480076530300402807020350046009001000250900004060010370807040020', '102048609090203070080006004306007802400000000058102000009850740701069005060004903', '700235100000070802000400030000060240005710080904080065600050008027043019093801470', '000100040804000630360580201500042019720009008008070000000760082600905370970020106', '063004190900810004000007560320100000400503901605002480700605800284309005000000072', '060000094000013000025709008089006423473005000000800050900052180008460730106907002', '900305172001048600006000300800900027023650000140007503007039058008001046400800900', '702630090540020007060007104090005000376400800000003249050208003921000005008019670', '100900020600740051090052300000070680230860009900520070000000095752603800840210700', '000302108451900000000750069869400020500007001002000340000130700340069805918004002', '007005100809036000000100486080540009200067840100902305040700002708013000653090700', '001260003800517000269000000400000700700094120016800950025001480040920060903605070', '003080007400200951500014086050869200002053008000070190090000003016027040304008610', '040100207009070005300208040051340080760900000003017052807500690200803001600094300', '025800063703004059900100700302040000067082005050709030008006091000203640014050020', '002070390050600708490305006004810200030409800600020010708061950320040600010507000', '000008970007500130030001004409700061001630820060000045905800416180057200200090080', '091072805470090000200304070103020000789051602000009004600080050027160080005007069', '060000800008900134000720095800000453030069000701040080052490010307002500409310067', '009762500000008320014500007600250700008936001053001900385400000007080290060010045', '030001507408007109070026008160273000050000600000800240003650894200049306900010050', '000230600005010000904050170400680030050927010061005928380000040207160050009400802', '109073560740050300006890000003915470000000058070402906608000703020601040035000090', '006301009057090230403020060060700190300005080514600370080450007000900020000072648', '803900600506080071070501000908340200600052930100007040000030002000409853020600794'] let expertPuzzles = ['009026000040108209000000378030200405501600000870003906200004000608050001050070080', '000000927040300010609000300020860704780020005500009001002043060000970800305100002', '020000405008001039069705020010086070700250004900000600000000310603900200000408506', '000050079060080003507460000004073060059200100083000020006700001200904000018025400', '000090080300008200085207106006000700007000591001030004020005600408109005700064010', '208060003000000560500300401090008070700409000006500010300250600470030008010800209', '000034700200000500530002086000960400070008003805000912040006000900800205602310007', '002000060390060005000004803000703004608000007054000021010040052079001000000805906', '600009704027010000800300050040960100050103009008007200900702003000006040360590800', '908000006510902700040508000302000000000007089007654100009100007000030004420080560', '720960000006030500500004020407090083000100060390087004600050300012000900050401008', '009034060002050000500010802030790000020401700801000040004508031300060005600000280', '013800405400005069008790000736400008002000500800009013009000020050013000000672000', '201070406850400000006000539002003007070010800509004001000108305460000090000650270', '037420900400070105080600000800190006040003700100060203000207010005040009060800302', '030802400715000009200900060000290300400075000080600051091040800026000170000300605', '300000719059004000020100600600020040001080007080007390004503860030690005007002001', '050749002002003001000060700000018000431500069678000000200030400006201500500400037', '300100040060009710580046000000070500620300000007602180100008490040200800900400026', '000108030302000009076000485010004560008207000945010000000035000700090002003460810', '090005013260800070000300900905006840006701300021500009000020400017040030400007090', '070000500060001000800050940409100300000030012200000807500627009021340050300800700', '002000048750000020000130950005207600000010003107004002010500009008063470906420000', '509004208027090600806005003010200870900601005003000040000000931600050000084007002', '107080004400300500080027090050100300860032000200004010000795001300810207005000006', '080400001200063000570100038704002010002050907030090600107006005008040092050000300', '010670000000090204023500800080710402700904031005006000000809007506020009004000305', '050000400270005060300140009800200040000060007000490583096020001001050300402807000', '300160409027000580004070000050230006060040130709600005100908000090003720800050000', '002300904030080670500007002085001700009030000600004598400070000000010050890200306', '709500003063900014005062000008000902010009450000008000800104730400073209020000006', '062001005000704010800000006980005307000000000050842060405020073010006809070430100', '009002810150030072040608300900080160500200000000006009307001200006700050000409003', '501700000009085010004960080360000400000002050070439060210003007800010390000840600', '008100000450072300930006008000000025060903014070620800840090002002400000603050100', '005040270080912030400000090000500803006874000190036000209005000030180007000007540', '000016070500007100009050306000002709401000500020095068706040020240800000008003601', '016002090408900007520040010000064000805000000060701089300006000004520700007008265', '008304920000070804360900070000043000500001090020000560040760008701008002030210040', '820456900003800400010007602400630527000004000090010003209060050005003008000005710', '003906002625100000000070008000060284200004010001007305197050040040730900800000060', '000790240800006003706500000900850010014007060300100420000043079293000500001000000', '300000000100690427090001080005320000080700090412050700000070608704800031060400005', '030409027800000000100020506005090604400057091000600700094008300002160000700034800', '306104950000300007004029008421600700500710690900003000000006100010080035752000000', '260008040009200081070094030001006700007080090800025100400000300903000074002001650', '007060000580004073930015408000036000412000007000200059095408201000001000073000080', '017000000630200900000610402001000009300005000009420507020087004050300091700054068', '097000080106050004030068002721004003000000410600003205508300007000170000004902600', '070302040010050002090006300600815007002009004381700000060070100000508000835400006', '041002700090600030506090100008001000020870009000305260905003040100057082400060007', '200106409000037001000590080086004702029600010407000030100063000004200006708900500', '019004586360007004000090000280309000000050620097020010000500003041870950800000140', '000002107080507094306000000120003780000400000005006010090700003010008250048209600', '900000800070120050806000371000690000600000023000007104054830006092005700000012095', '204356800000000060507208003800120000006004370700000009020901000000680090010035406', '005090070390020000100706008030860009400007160020300040950000001004980700600040025', '400008039000000700063029050704083100500090060039001420810052600000400900002000070', '006000070030024001200300050800007030109402600007000104700100096084006300005980000', '300000740008040092100907806050300289060270000000090004003020000004506001025008300', '008000000105600700700390056400002008017005002006700035204080003000000069300014200', '036000000400206081920073005000057000008000940090010603300520809005000000170004026', '092700000001000374000068009283600000000042031006900500607005000300090045400010820', '006000308000900025475008010138760000004030002090180700850001090600200030007000604', '012004006809700050006310870400006000000507980003040205300000019600280500004003000', '000062390901700050026000004400038075000095406210000000300050000058040203070800010', '038520400002006870000007000064090002509340000000005100080003061010070090300800750', '000000008070064050231800000000907001063100509004005703400002360007000010850700200', '400500000360702109001003007972008061000900000050006203640000058020080000000497300', '607004030050102800000700900091020006070090040000803170005000082309046700016500000', '700951000008004160000000040002080000130005490000093006506038010970000203400200075', '607000100800230009090000708000005802020710065460009000009300040050904070701600003', '500309000082050070060000204700000016903600000400100089000840000031062500007003698', '002600150061080024095070800080100907740003500200000083000040360000002000104309000', '506000000090000804740106030600000000304002091009070250000081905070304002200600013', '007043006000000000000200481080090607036000009072460018060080920403107000005600000', '603000021070204309800005000090040105000598040002300060060019800305800407007000200', '009700610000020403500604080803100042000050367200090008060371000040006500010000090', '003810600045900201002006970020030004000107020700040068850720100000500090016008000', '570000000380600051091030407200045700000001906000800020068200003050470600409010000', '400600980380700000200030017004090302075080409000500000010046000039010200000200005', '000107020018300000405006090920000051000750060064008300009503600006900480800000007', '090000000587390400003060082016405700000903540400000010800000695700020000034001008', '030170209600084007104500800007400100090003054060090020705908060200001000040000380', '034000060000008000001796000300000750006302018405009230809240300000000020570103900', '607014300003980100008605920000742090700000008920000500160003052005809006040000000', '060408000120030040008715060000601090040000520906000007005290014700003008003500009', '013040025008002070060507000006005400300000790090820010904070300000906000070310008', '100500030005064800820100090204030050000007004680009100010000207090005083067240000', '090350000206098100051006007000630000403000200607009001040000706000210950170500003', '010750000600000070800009205120070350080002040300096801030400089054103000700000100', '030000050001506800000039276807000000000600329300201400085900102200480000090700060', '008190600004200080060000307080002009000500031037019000903000054010040000502087906', '001095040070003600950206007100820054003060700000040209500000018049000000607008003', '009503470062000003000041000080700250006004809031009000048067100150000060000000702', '000000000049836700700000406103409050002170003098000060000005830061200090580001002', '700010400020900800148003050512000900006070543000800010890300100600200070004050600', '590000600060020179000300000203057900000106008410000007800205000007093410005004082', '002003806041056000000000190000340700006010509178000020900000000780460003603009052', '201006400900503700300010005009000003000870600410000920000201000060400258700038094'] let omegaPuzzles = ['003005060020040000100000500000024600008000200001890000007000009000060080040500700', '806040200000000000907050301000000002408209603600000000102030405000000000009080706', '500200000010050000000009074700000005030000100005007020000020860004006200009800000', '001750000009000000000046800130020007200000003400070051005390000000000300000085100', '000000004007000300010507060002040900000905000006070400050208030003000700900000000', '600200900090010050002003008000004009080000070300800000800600300070040010004002005', '009010006000023000820000450300000060000801000040000007071000095000670000200050600', '000203090607000000000009070701000008000000000400000507020900000000000604030104000', '300010008090000020005000700000207000800040003000803000008000200050000080100060004', '000000000030502060008090200060000030004000900050000070009020800070609050000000000', '020700004500000100008001006100050900090000030006090008200400800005000002700009060', '000000001000007209004102800308400000100000006000009407003701500406300000800000000', '001002005002080400570000090000708006010000030400903000090000057006050100300800200', '005300000030008400090060020008020070000501000060070100040090030003800050000002600', '000000000070030920019025630004000210000000000057090460095140370000000000042367590', '000500800000002010300060004002009000800600005070000300003000002010800700500004090'] let devilishPuzzles = ['000000001000002030004050600000001072060080500700000000040090000096400800307008000', '980700600500040000003009020700080000040500000006001002009000301000003260000000079', '980760000750000000004500870500900460040030000000005002400006008090800540000000001', '020400080000009000000070001006800400300000007045000020000003000004600050900010008', '980360500500700900006009080600030000090200070025006300200600700001000004000000050', '020000080400009020709000004000500800300010050090004007070002008000300600001060000', '490100000200060400007004000600900200020050080003000007010200600000010050000008003', '100050009056009000079200000001003800007060001000000040090070006000800020000004300', '000400009000080100700002060010000008060090400500003070600020000030005000075600020', '120000700007000036600070000000008050040900008001020300000500000002060100900004000', '003006000406080100080100000030001400000900000900070050040008600002000004000500072', '100000009400000200080003050000708060000012400000000000060800070030605000900020000', '987600500400800690030000002700040009000700050000069700500400900008000040000001000', '000020300004300005030006010070080900800400002001000050900200500006003000020090007', '080200600300004000500090000009000070010700006000040210070820060003050008000000100', '000050009007100200000003040010600007006000020870000600030090000700004050008200100', '005079000670500900800000000400000030008900500000020001006700800000030020000001004', '900760500700050690000008007600409035000030060003006400300040900020000000007001000', '600700800708960500050008000500007400030400000000090020400070600000806001000000050', '020050000400009030080200600060000008300040070000000001500037000007005090000100000', '980000000007900600000050000700400900006030020000005001070500800004010050000002003', '005009000670500000800070500400003020030000001006900800007800600000010004000002000', '900800700080060050000000004700009000050030008002100000060000030001200900000040005', '000450009400000230600002000070590000300008000090700001000000860040010007000000000', '987650400300400600000008000600900300020000010000070006500040003000500064000009500', '050000004003900070100000600008370000000095000000200090600000500040000001007020080', '023000009400000100009020040200090001000005800005600000000800070060007000900040003', '820760500700080600005000000900007300004830000000090020300900700000076001000000050', '003400009000080200060007010006001050010000900570000000600005090000090004000200800', '987000000600900500050080000400300950060092070000000002100063005040100000006009100', '004000001070003080200000500000009000090300060000706002001000008080600070500080400', '023050700056700003700000000000004890500020007008000000300060002000901400010000000', '100400080050009002009000000000600803600000001000041060072000500000074000800300010', '100020300002300001040005060000080900500400003006000020900700500007008000010030007', '980600500600000080004000000790300050003006900000002703370800005001007000020060070', '087600503600000090040500700070400800000020001000006070030200080004300057000004300', '001000004070002050600000800000007090000210007050309000008000600030900020400000001', '003050000400100200090007000200000408070000050000600100600800004000030090030004800', '100200003040050020002000600500003070060020800000900000005040200080700060900000001', '103006000406080100080000000010003600005000090000200007030001800001070002900500000', '005090000670800000800000600500003020040000001007900500008700900000040030000010002', '900700600700500400061020007800600500030000090000009000600007004008000700000800065', '080760500704009030000000000400000200208001040000200008307910000040007300009030070', '980710500700400800006005000500900400030074000000050020100040089007000100000100004', '960780500700005000005060000408007900006009800000040030020000050007006209000200001', '400000008050020040006004900070302000000050700000001030009000400020030050800000006', '300000002080070010006900000050704000000000008000510070009000300010040080200000006', '000010007020400080300000500090806000000049060002000000007000100040900020005000003', '003450000000080060080007005200800100300001900010070004000000020040700001900010600', '009000003050002060400000700000508020000190000010006000300000900020800050007000004', '980740500500009070007500000600030000008605700005408300020000003009004800000000010', '900780500700060900005009000500007800004830000000000020300900700000076019000000050', '903700500500400600040050080700004006039000400000900070300600700002000000070095001', '980670000750090600006000000400900050008450700000003004027000030009830500000000001', '608700900700960500050008000500007400030400000000090020400070800000806001000000050', '023406000000080000090300400000005008000010070006200900700000050062000300040000001', '980650400400800600000000070800300960020000030300010005600000000094000300038400006', '103050000007100006080007100040900060008004300700020000000000092800001400000500010', '907600500640501700503020000800900400000008050000000008700065094000400070000000600', '980060500700005080005000004300200000006009400000070023010000040009007800000810095', '800000004000090030001400700060305000002000000000069050007000100020030090400000008', '020450009006009000000200400000100000008000013090005200300000078060004500000070000', '070000000300020050004100600005040020030600400000008009001030040050400700000009008', '980500600750060090006009000400000005008750060005900080030000200007800030000030001', '950860700007000060000005009408006900030020040009008600100000090005007100000100006', '080060500704030600062000070400500900090003085008090006040050800000009000800000001', '100450700007009030600700000000007003040010900500600000010000098000000002002008300', '900000002080007040006000100000058070000010000050703000200000600040500030001000009', '402000001070009020008000600000705090000002000050930000100000800020500030006000004', '007000004020003010600000800000205000050030090000109000008000007010300020400000601', '500060700010304000080070000030100000005080900000002040809000007000000020600000509', '600000008030007040009000100000302070000014000020750000800000009040200050001000600', '005089000600700000070000900500030002400000010007600500006500800000020003000001040', '890760500600080900005009000500007800004830000000000020300900600000076001000000050', '006002000300050700040900000700000108000000040000080037100030005009400000020006000', '380700600005064000000300000830000700090000030002000001060900800000020050000001004', '012000000300040000500100030001600050060070008000009600000006900003400020000080007', '500000009020100070008000300040207000000080000000605010300000800060004020009000005', '000500008700068000000210000002000040090000300600005001100070006040000020003000900', '000700900000000030009030806003050060700400000010002000005080090020000000400001005', '087650400630040700002000060070305080008704500000080007050030800000001009000000000', '890500600700080050005004000300002140006400090000010000020090005007800060000000003', '120050000050000006006003000001060008080900070000008400000700800000004090002030005', '900740500507060900003000070600090420000800000000002001200000059050020046006000200', '083760500704009080060080700305806200020005000006020000030050800000000010000000007', '080700600700050020004000007000302000010090000300005090900030010070000008036000400', '090700600540080070007004000300020500910000000005400080009800060000003002400090100', '120406000006080002080000000000900050000003400600070001007005090800000300060010007', '010000002300000040005006700000087900000904000008560000009800500020000001400000030', '700000009030400060005000200010806000000070000000031080200000700040008030009000005', '002001000000030040300600700007009005080000020100400600006007003050080000700100900', '008001000300070900050200000600000409000000050000090730400030007001500000020008000', '980560700750400900003009000800006000075800090040050800500600400000020008000000010', '907650400400800500006094070800000000094000600065900080600500800030020000000000001', '940003600005060040000008500000300020010000075002070400100009200006020004000800000', '005069000670800000800000600040000030007500900000020001008700500000010020000003004', '000090000560700000708000500400009030007800900000020001006900700000010040000003002', '980730500700400900000008030600000000050047600040600009400500200002004005000020001', '103400000006080003080000040010600008307000000000007200500000090000092500030100004', '920760500700405900005020000470500600003000080000000000100040050090100046000007100'] let testPuzzles = ['127539468568214793439678251016953824354182976892467315643725189281396547975841632', '384275619175946823296831574748329165961587032532164798457692381819753246623418957', '412697835675318429839425167728153694146279583953046712391784256267531948584962371', '413598672268471395795263418142385769956712843387049521639827154571934286824156937', '932756148458321976176849235715984623324675819869213054697532481581467392243198567', '910643258832159467546728913374916825289435176651287394768392541193564782425871639', '142687539573129864698453721261735948984261357057948612736812495429576183815394276', '973182654456379218128564379814693725790421836362857491531246987689735142247918563', '348576192962318745517492386876251934439867251125934867754189623683725410291643578', '386917524524683971971524368418362795659741830732859416163495287895276143247138659', '182374596560912748974586321357461982846297135291853674619728453738645219425139867', '983076254257948631641325789764851923832697415195234867378412596529763148416589372', '417356298253891764986247351734962185109578432825413976692135847371684529548729613', '374186259815294736692357814987435621251968473436721598128543067769812345543679182', '576138249142697385893542716358271964264389157719456032425913678681725493937864521', '368579124541268793729413856196735402257846931483921675972654318635187249814392567', '149872356356914287728563914213496578685137429497258631962381745874625193531749062', '854621937327498651691537248273159864148263795965874123786342519539706482412985376', '531276849764918325928435761279341658485697132613852974307184296196523487842769513', '403785162875621493162943857514892376236457918987136245648379521321564789759218634', '831945726692738451074612983129863574753294168468571392917486235385129647246357819', '821350769643927815759861432295618374376549128184273596438796251912435687567182943', '519382647634795281287146953390451876768239514451678392826914735943567128175823469', '628457931491623785357918624513742869764891253982365417146589372279136548835274106', '937045128182937546564128973471562389693871254825394761318756492259483617746219835', '863972154142856793795413268386241975254697831970385642437129586518764329629538417', '395864712621739548478152396984327651756941023213586479542693187837215964169478235', '549738261201954378378162954726591483184326597953487612692873145815249736437615829', '948726351351948672762015948827463519416592783593871426135684297274139865689257134', '532184679691273845478659213243917586156328794987465132365801427829746351714532968', '874251936653798214192346758915423687287165493436879125021534869368917542549682371', '873195462149286735625374189254069317387512694916743528531428976762951843498637251', '145986237379214586268573914534897621791640853826135749452369178613758492987421365', '593461278486273519712598630159327486634185927278649351327914865845736192961852743', '786251943439687512521943867692438751147596238853712694975324186360179425214865379', '468523179592417368137968542715234986843679215906185734271896453384752691659341827', '762591834583246791149073652437928165916357428258164973374685219821739546695412387', '492317568586924713317658294728193456645872931130465872261749385874531629953286147', '372459681689271543145683729268397154794516832513824967926148375057932416431765298', '156324789794851632832679415579482361463517298281936547318245976645798123907163854', '136589742428371695579246813654813279287954361903627584361795428895462137742138956', '459781632368524197217639845692145783871963524503872916984356271125497368736218459', '543928176718653429962147583135786294496532718827491365289374651351069847674815932', '497261853813579624625348179304856912582913467961427385738192546156734298249685731', '963527418247168593815934672321876945479352861586409327754293186638741259192685734', '846291357371485269952763814785324196613978542029516783198637425234859671567142938', '712943568956728134843561972395470681428316795671859243534297816269184357187635429', '739214685516873904248659317673592841921748536854361279185426793397185462462937158', '596483127321795864487126935813942576659317480742658319264839751135274698978561243', '862397145453621790971458326328964517145783962697215483589172634736849251214536879', '159873246276154398384296715765348921892617504413529867527431689948762153631985472', '351692784879345261642781359984263175726519843135874926467130592518926437293457618', '592786341613542978748193625481239756965874132327651894856327419139465087274918563', '453698721289317456167452893876243905321975684945861372692584137738126549514739268', '759638241138942675264751839941587326382164597576293418493875162817426950625319784', '136758924854239176972641035329867451567314298418925367243176589795482613681593742', '875623194931584762264179583358946271129357648746218359617492835593861427482735016', '548796132372518694169234875256971348487623519903845726821457963795362481634189257', '952368741834719562761542398375481609246975813189236475623894157517623984498157236', '178562943693174852542938671257689134364751298819423567986247315421395786735806429', '327649815541827936968513247472961058185374629639285174794138562253796481816452793', '184327596963485712725961380698534127471892635532716849349678251256143978817259463', '953628174871493625624571389495786231012359468368214597289165743147832956536947812', '231864597598137462746295813385921674967458031412376985673549128124683759859712346', '961253784753849261428671359284765193135492670679318425596124837342587916817936542', '906437251214865937537129684453216798762948315891573426675391842328754169149682573', '728304159594718632613925874437286915186597423259431768972643581845172396361859247', '397824516285613947416597832651742389749381605823956471168475293932168754574239168', '358792461741306295269541783593417826872659134614238579136824957427965318985173642', '265893741974512368138476592893164275657230814412785936521347689389621457746958123', '821476395763259184549813276935162748287945631416380529192534867378691452654728913', '637529481891473265245168397476982153152346879389715642518637924964251738723094516', '947853126816927435325416879504178692189362547672549381491735268258694713763281954', '546823971792641853381579426125387649473196285968452307859764132237918564614235798', '871362594569174328342859716954631287107485639683927451738216945415793862296548173', '158726934724593168963841725896172543412635897537984216675019482341258679289467351', '174239658956478231308165497431597862265843179789612345513786924647921583892354716', '576481329493527681128963547712304865835716492649852173957638214284175936361249758', '047182356528693147163574892296851734351467289784239561419325678875916423632748915', '652137984718954632349862517895426173164783259273591468487619025531248796926375841', '463958127971243856258617493719586342342791685685324971824130769197462538536879214', '726983415914765238538142697642597183891234576357806924173659842485321769269478351', '195623487384597261762418395679284153248135976513769842420956738936872514857341629', '892457163316829547574613928238791456149365782657248319921576804485932671763184295', '356942187471863529289571346628359714197284635543617892965438271732190468814726953', '439278156156934872827156934295683417641527398783419265962741583518362749374895601', '493215768687943215521678349768594123314826957902731684136489572245367891879152436', '864932751517486239923571486395014627742693518186725943431267895278359164659148372', '317269450485713269926458317591836724874921635632547981148392576753684192269175843', '642589317175326498893741526964208731357914682218673954786195243531462879429837165', '734825961195476832286913574479658213321749685568231749652184397917362408843597126', '375642981861739254249158673584396712617284395923571468798413526456027139132965847', '594137286782496351136582974845679123329814567617253498273961845468325719951740632', '694857123751932486283164579169085734527643918348791265816479352475326891932518647', '421356798795824316836917245269435871157698423380271659573162984942583167618749532', '419582763583746912267913845394675128871234596652198374038421657746859231125367489', '632897415485316729719425836148659372263741958957283641371962584826534107594178263', '325748196178296435649513278264871359583629741017354682756482913431965827892137564', '546921380197384526238576941713468259859213674624795138982637415461852793375149862', '195746832623859174748132956251368749967425381384971265876214593519083427432597618'] //#endregion var init = () => { /////////////////// // Setup currency = theory.createCurrency("⋆", "\star"); theory.secondaryEquationHeight = 100; /////////////////// // Sudoku variables // easy { let getDesc = () => "Easy difficulty (gain " + REWARD_EASY + " ⋆)"; let getInfo = () => "Open a sudoku with an easy difficulty"; easy = theory.createUpgrade(0, currency, new FreeCost()); easy.getDescription = (_) =>getDesc(); easy.getInfo = (amount) => getInfo(); easy.bought = (amount) => { easy.level -= amount; if (!isPopupOpen) { difficulty = EASY; showSudokuPopup(EASY); } }; } // medium { let getDesc = () => "Medium difficulty (gain " + REWARD_MEDIUM + " ⋆)"; let getInfo = () => "Open a sudoku with a medium difficulty"; medium = theory.createUpgrade(1, currency, new FreeCost()); medium.getDescription = (_) =>getDesc(); medium.getInfo = (amount) => getInfo(); medium.bought = (amount) => { medium.level -= amount; if (!isPopupOpen) { difficulty = MEDIUM; showSudokuPopup(MEDIUM); } }; } // hard { let getDesc = () => "Hard difficulty (gain " + REWARD_HARD + " ⋆)"; let getInfo = () => "Open a sudoku with a hard difficulty"; hard = theory.createUpgrade(2, currency, new FreeCost()); hard.getDescription = (_) =>getDesc(); hard.getInfo = (amount) => getInfo(); hard.bought = (amount) => { hard.level -= amount; if (!isPopupOpen) { difficulty = HARD; showSudokuPopup(HARD); } }; } // expert { let getDesc = () => "Expert difficulty (gain " + REWARD_EXPERT + " ⋆)"; let getInfo = () => "Open a sudoku with an expert difficulty"; expert = theory.createUpgrade(3, currency, new FreeCost()); expert.getDescription = (_) =>getDesc(); expert.getInfo = (amount) => getInfo(); expert.bought = (amount) => { expert.level -= amount; if (!isPopupOpen) { difficulty = EXPERT; showSudokuPopup(EXPERT); } }; } // omega { let getDesc = () => "Omega difficulty (gain " + REWARD_OMEGA + " ⋆)"; let getInfo = () => "Open a sudoku with an omega difficulty"; omega = theory.createUpgrade(4, currency, new FreeCost()); omega.getDescription = (_) =>getDesc(); omega.getInfo = (amount) => getInfo(); omega.bought = (amount) => { omega.level -= amount; if (!isPopupOpen) { difficulty = OMEGA; showSudokuPopup(OMEGA); } }; } // devilish { let getDesc = () => "Devilish difficulty (gain " + REWARD_DEVILISH + " ⋆)"; let getInfo = () => "Open a sudoku with a devilish difficulty"; devilish = theory.createUpgrade(5, currency, new FreeCost()); devilish.getDescription = (_) =>getDesc(); devilish.getInfo = (amount) => getInfo(); devilish.bought = (amount) => { devilish.level -= amount; if (!isPopupOpen) { difficulty = DEVILISH; showSudokuPopup(DEVILISH); } }; } // test { let getDesc = () => "Test difficulty (gain " + REWARD_TEST + " ⋆)"; let getInfo = () => "Almost solved Sudokus for testing purposes"; test = theory.createUpgrade(6, currency, new FreeCost()); test.getDescription = (_) =>getDesc(); test.getInfo = (amount) => getInfo(); test.bought = (amount) => { test.level -= amount; if (!isPopupOpen) { difficulty = TEST; showSudokuPopup(TEST); } }; } /////////////// // Achievements // Planned for the full release (or late beta) } //#region Theory Code var tick = (elapsedTime, multiplier) => { if (gameGrid && gameGrid.width > 0 && gameGrid.heightRequest < 0) gameGrid.heightRequest = Math.max(gameGrid.width, 0); //log("elapsedTime: " + elapsedTime + ", multiplier: " + multiplier + " , elapsedTime * multiplier = " + elapsedTime * multiplier); if(currentStartTime != null) { updateTimerLabel(); } } var getInternalState = () => { // save all difficulty board states and undo/redo stacks log("Saving..."); // Save currently running timer in case of shutdown and reset current start time updateTimerInSave(); var saveArray = [easyBoard, mediumBoard, hardBoard, expertBoard, omegaBoard, devilishBoard, testBoard, easyUndoStack, mediumUndoStack, hardUndoStack, expertUndoStack, omegaUndoStack, devilishUndoStack, testUndoStack, easyRedoStack, mediumRedoStack, hardRedoStack, expertRedoStack, omegaRedoStack, devilishRedoStack, testRedoStack, easyTimer, mediumTimer, hardTimer, expertTimer, omegaTimer, devilishTimer, testTimer, easyBestTimer, mediumBestTimer, hardBestTimer, expertBestTimer, omegaBestTimer, devilishBestTimer, testBestTimer]; var saveState = JSON.stringify(saveArray); //log(saveState); return saveState; } var setInternalState = (state) => { // restore all difficulty board states and undo/redo stacks log("Restoring..."); var saveState = JSON.parse(state); //log(saveState); easyBoard = saveState[0]; mediumBoard = saveState[1]; hardBoard = saveState[2]; expertBoard = saveState[3]; omegaBoard = saveState[4]; devilishBoard = saveState[5]; testBoard = saveState[6]; easyUndoStack = saveState[7]; mediumUndoStack = saveState[8]; hardUndoStack = saveState[9]; expertUndoStack = saveState[10]; omegaUndoStack = saveState[11]; devilishUndoStack = saveState[12]; testUndoStack = saveState[13]; easyRedoStack = saveState[14]; mediumRedoStack = saveState[15]; hardRedoStack = saveState[16]; expertRedoStack = saveState[17]; omegaRedoStack = saveState[18]; devilishRedoStack = saveState[19]; testRedoStack = saveState[20]; easyTimer = saveState[21]; mediumTimer = saveState[22]; hardTimer = saveState[23]; expertTimer = saveState[24]; omegaTimer = saveState[25]; devilishTimer = saveState[26]; testTimer = saveState[27]; easyBestTimer = saveState[28]; mediumBestTimer = saveState[29]; hardBestTimer = saveState[30]; expertBestTimer = saveState[31]; omegaBestTimer = saveState[32]; devilishBestTimer = saveState[33]; testBestTimer = saveState[34]; } var getPrimaryEquation = () => { return "\\text{Select\\ a\\ difficulty\\ to\\ play\\ a\\ random\\ Sudoku}"; } var getSecondaryEquation = () => { return "\\text{The goal of a Sudoku is to place numbers such that \\\\ each number between 1 and 9 appears exactly once \\\\ in each row, column or 3x3 box.}"; } var getTau = () => currency.value; var rewardForDifficulty = (difficulty) => { var reward = 0; switch(difficulty) { case EASY: reward = REWARD_EASY; break; case MEDIUM: reward = REWARD_MEDIUM; break; case HARD: reward = REWARD_HARD; break; case EXPERT: reward = REWARD_EXPERT; break; case OMEGA: reward = REWARD_OMEGA; break; case DEVILISH: reward = REWARD_DEVILISH; break; case TEST: reward = REWARD_TEST; break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } return reward; } var increaseCurrency = (reward) => { currency.value += reward; } var resetDifficulty = (difficulty) => { resetUndoRedoStacks(difficulty); resetTimer(difficulty); switch(difficulty) { case EASY: easyBoard = null; break; case MEDIUM: mediumBoard = null; break; case HARD: hardBoard = null; break; case EXPERT: expertBoard = null; break; case OMEGA: omegaBoard = null; break; case DEVILISH: devilishBoard = null; break; case TEST: testBoard = null; break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var isFinished = (difficulty) => { switch(difficulty) { case EASY: return easyBoard == null; case MEDIUM: return mediumBoard == null; case HARD: return hardBoard == null; case EXPERT: return expertBoard == null; case OMEGA: return omegaBoard == null; case DEVILISH: return devilishBoard == null; case TEST: return testBoard == null; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } init(); //#endregion //#region UI Utility var getBackgroundColor = (preset) => { switch(game.settings.theme) { case Theme.STANDARD: if(preset) return Color.fromRgb(0.2, 0.2, 0.2); return Color.fromRgb(0.4, 0.4, 0.4); case Theme.DARK: if(preset) return Color.fromRgb(0.3, 0.3, 0.3); return Color.fromRgb(0, 0, 0); case Theme.LIGHT: if(preset) return Color.fromRgb(0.7, 0.7, 0.7); return Color.fromRgb(1, 1, 1); } } /** * Selects a red background color for cells containing invalid numbers. * The color is adjusted according to theme and will be different for given and open cells. * @param {*} preset If the given square is representing a given number * @returns A red color, differing according to the theme and if it is a given or open cell */ var getInvalidBackgroundColor = (preset) => { switch(game.settings.theme) { case Theme.STANDARD: if(preset) return Color.fromRgb(0.5, 0.2, 0.2); return Color.fromRgb(0.7, 0.4, 0.4); case Theme.DARK: if(preset) return Color.fromRgb(0.9, 0.3, 0.3); return Color.fromRgb(0.5, 0, 0); case Theme.LIGHT: if(preset) return Color.fromRgb(0.9, 0.3, 0.3); return Color.fromRgb(1, 0.7, 0.7); } } var resetBackgroundColors = () => { for (var square of gameGrid.children) { var cell = board[getBoardNum(square.row, square.column)]; square.backgroundColor = getBackgroundColor(cell.isGiven) } } var getBorderColor = () => { switch(game.settings.theme) { case Theme.STANDARD: return Color.fromRgb(0.7, 0.7, 0.7); case Theme.DARK: return Color.fromRgb(0.8, 0.8, 0.8); case Theme.LIGHT: return Color.fromRgb(0.3, 0.3, 0.3); } } var getSelectedBorderColor = () => { switch(game.settings.theme) { case Theme.STANDARD: return Color.fromRgb(0, 0, 1); case Theme.DARK: return Color.fromRgb(1, 0, 0); case Theme.LIGHT: return Color.fromRgb(0, 0, 1); } } var markInvalidCells = (wrongCells) => { for (var wrongCell of wrongCells) { var row = wrongCell[0]; var column = wrongCell[1]; var wrongSquare = gameGrid.children[row * 9 + column]; //log("(" + row + "," + column + "): " + wrongSquare.content.children[0].text); var isGiven = board[row * 9 + column].isGiven; wrongSquare.backgroundColor = getInvalidBackgroundColor(isGiven); } } /** * Gets the currently used time for the open puzzle (if existing) and updates the timer. * It will also reset the current start time to now. * @returns */ var updateTimerInSave = () => { // No puzzle open or not started if(difficulty == undefined || difficulty == null || currentStartTime == null) return; var timer = getTimerForDifficulty(difficulty); log(timer.time); var elapsedTime = getElapsedTime(timer); timer.time = elapsedTime; log(timer.time); // TODO: save hints when implemented currentStartTime = Date.now(); } var UpdateBoardAndSquare = (cell, cellIndex) => { board[cellIndex] = cell; var square = GetSquare(cellIndex); updateSquareFromCell(square, cell); } var GetSquare = (index) => { return gameGrid.children[index]; } let markFontSize = 12; var createSquare = (i, j, cell) => { let number = ui.createLabel({ fontSize: 30, horizontalOptions: LayoutOptions.CENTER, verticalOptions: LayoutOptions.CENTER, text: cell.number < 1 ? "" : `${cell.number}`, }); let pencilMarks = []; let layoutOptions = [LayoutOptions.START, LayoutOptions.CENTER, LayoutOptions.END] for (let v = 0; v < layoutOptions.length; v++) { for (let h = 0; h < layoutOptions.length; h++) { if(h == 1 && v != 1 || h != 1 && v == 1) continue; let markLabel = ui.createLabel({ fontSize: markFontSize, horizontalOptions: layoutOptions[h], verticalOptions: layoutOptions[v] }); pencilMarks.push(markLabel); } } let markGrid = ui.createGrid({ children: pencilMarks }); var square = ui.createFrame({ row: i, column: j, margin: new Thickness(1, 1, j % 3 == 2 ? 4 : 1, i % 3 == 2 ? 4 : 1), backgroundColor: getBackgroundColor(cell.isGiven), borderColor: getBorderColor(), content: ui.createGrid({ children: [number, markGrid] }), }); if(isEmpty(cell)) { if(cell.cornerMarks.length > 0) setCornerNumbers(cell, square.content); if(cell.centerMarks.length > 0) setCenterNumbers(cell, square.content); } square.onTouched = (e) => { if (e.type == TouchType.PRESSED) { if(isFinished(difficulty)) return; if(selectedGivenSquare != null) { selectedGivenSquare.borderColor = getBorderColor(); } if(selectedSquare != null) { selectedSquare.borderColor = getBorderColor(); } if(cell.isGiven) { selectedGivenSquare = square; selectedSquare = null; } else{ selectedSquare = square; selectedGivenSquare = null; } square.borderColor = getSelectedBorderColor(); } } return square; } var createGameGrid = (board) => { gameGrid = ui.createGrid({ columnSpacing: 1, rowSpacing: 1, }); let c = []; for (let x = 0; x < 9; x++) { for (let y = 0; y < 9; y++) { var cell = board[getBoardNum(x, y)]; var square = createSquare(x, y, cell); c.push(square); } } gameGrid.children = c; return gameGrid; } var createNumberButtonsGrid = (difficulty, board, stateLabel) => { let numberButtons = []; for(let i = 1; i <= 9; i++) { var numberButton = ui.createButton({ row: 0, column: i - 1, text: `${i}`, onClicked: () => { if(selectedSquare === null || isFinished(difficulty)) return; log("clicked " + i + " for mode " + textForMode(mode) + " and square (" + selectedSquare.row + "," + selectedSquare.column + ")"); if(currentStartTime == null) { currentStartTime = Date.now(); } var cellIndex = getBoardNum(selectedSquare.row, selectedSquare.column); var cell = board[cellIndex]; addToUndoStack(difficulty, cell, cellIndex); resetRedoStack(difficulty); setNumber(i, board, selectedSquare.row, selectedSquare.column); if(mode != NORMAL_MODE) return; resetBackgroundColors(); var checkResult = checkBoard(board); var fullBoard = hasAllNumbers(board); if(checkResult[0] && fullBoard) { var timer = getTimerForDifficulty(difficulty); var elapsedTime = getElapsedTime(timer); timer.time = elapsedTime; var timerString = TimerAsString(timer); UpdateTimerIfBetter(difficulty); log("Won " + difficulty + " in " + timerString); var stars = rewardForDifficulty(difficulty); increaseCurrency(stars); stateLabel.text = "Congratulations! You won " + stars + " stars as a reward."; resetDifficulty(difficulty); undoButton.isEnabled = false; gameGrid.FadeTo(0.5, 10); } else { if(fullBoard) stateLabel.text = "Solution is incorrect!"; markInvalidCells(checkResult[1]); } } }); numberButtons.push(numberButton); } let numberButtonGrid = ui.createGrid({ children: numberButtons }); return numberButtonGrid; } var createButtonsGrid = (difficulty) => { let buttons = [null, null, null, null, null, null]; buttons[0] = ui.createButton({ row: 0, column: 0, text: "Clear", onClicked: () => { if(selectedSquare === null || isFinished(difficulty)) return; var cellIndex = getBoardNum(selectedSquare.row, selectedSquare.column); var cell = board[cellIndex]; addToUndoStack(difficulty, cell, cellIndex); resetRedoStack(difficulty); clearCell(board, selectedSquare.row, selectedSquare.column, selectedSquare.content); resetBackgroundColors(); var checkResult = checkBoard(board); if(checkResult[1].size > 0) markInvalidCells(checkResult[1]); } }); buttons[1] = ui.createButton({ row: 0, column: 1, text: textForMode(mode), onClicked: () => { log("Pressed Mode"); mode = (mode + 1) % 3; buttons[1].text = textForMode(mode); } }); buttons[2] = ui.createButton({ row: 0, column: 2, text: "Replay", onClicked: () => { //reset timer etc. log("Pressed Replay"); clearAndSetBoard(difficulty); } }); undoButton = ui.createButton({ row: 1, column: 0, text: "Undo", onClicked: () => { log("Pressed Undo"); undo(difficulty); } }); undoButton.isEnabled = getStackForModeAndDifficulty(difficulty, UNDO_MODE).length > 0; buttons[3] = undoButton; redoButton = ui.createButton({ row: 1, column: 1, text: "Redo", onClicked: () => { log("Pressed Redo"); redo(difficulty); } }); redoButton.isEnabled = getStackForModeAndDifficulty(difficulty, REDO_MODE).length > 0; buttons[4] = redoButton; buttons[5] = ui.createButton({ row: 1, column: 2, text: "Hint (+10s)", onClicked: () => { log("Pressed Hint"); hintLabel.text = "Hint: not implemented"; } }); buttons[5].isEnabled = false; let buttonGrid = ui.createGrid({ columnDefinitions: ["1*", "1*", "1*"], rowDefinitions: ["1*", "1*"], children: buttons }); return buttonGrid; } var createPopupUI = (difficulty, board) => { var gameGrid = createGameGrid(board); var currentTimer = getTimerForDifficulty(difficulty); var currentTimerString = TimerAsString(currentTimer); var timerString = "Time: " + currentTimerString; var bestTimer = getBestTimerForDifficulty(difficulty); var bestTimerReadable = TimerAsString(bestTimer); var bestTimerString = "Best Time: " + bestTimerReadable; timerLabel = ui.createLatexLabel({text: timerString, horizontalTextAlignment: TextAlignment.CENTER, margin: new Thickness(0, 5, 0, 5)}); bestTimeLabel = ui.createLatexLabel({text: bestTimerString, horizontalTextAlignment: TextAlignment.CENTER, margin: new Thickness(0, 3, 0, 5)}); hintLabel = ui.createLatexLabel({text: "Hint: not included in Alpha", horizontalTextAlignment: TextAlignment.CENTER, margin: new Thickness(0, 3, 0, 5)}); var stateLabel = ui.createLatexLabel({text: "Solve for " + rewardForDifficulty(difficulty) + " stars", horizontalTextAlignment: TextAlignment.CENTER, margin: new Thickness(0, 3, 0, 5)}); var numberButtonGrid = createNumberButtonsGrid(difficulty, board, stateLabel); var buttonGrid = createButtonsGrid(difficulty); popup = ui.createPopup({ title: `Sudoku - ${difficulty}`, closeOnBackgroundClicked: false, content: ui.createStackLayout({ children: [ timerLabel, bestTimeLabel, hintLabel, stateLabel, gameGrid, numberButtonGrid, buttonGrid ] }), onDisappearing: () => { log("Popup closed"); // When closed by us after pressing "Replay" timer is already taken care of if(currentStartTime != null){ var timer = getTimerForDifficulty(difficulty); var elapsedTime = getElapsedTime(timer); timer.time = elapsedTime; currentStartTime = null; } else { difficulty == undefined; } } }); var checkResult = checkBoard(board); if(checkResult[1].size > 0) markInvalidCells(checkResult[1]); return popup; } var popup = null; var selectedGivenSquare = null; var selectedSquare = null; var mode = NORMAL_MODE; var textForMode = (mode) => { switch(mode) { case NORMAL_MODE: return "Normal"; case CORNER_MODE: return "Corners"; case CENTER_MODE: return "Center"; default: throw new Error("Unknown mode! Cannot continue execution."); } } //#endregion //#region UI Sudoku Popup var showSudokuPopup = (difficulty) => { selectedSquare = null; mode = NORMAL_MODE; board = getBoard(difficulty) var popup = createPopupUI(difficulty, board); popup.show(); } //#endregion //#region Sudoku Minigame Utility var getBoard = (difficulty) => { var randomBoard = null; switch(difficulty) { case EASY: if(easyBoard == null || easyBoard.length == 0) { randomBoard = getRandomBoard(difficulty); easyBoard = generate(randomBoard); } return easyBoard; case MEDIUM: if(mediumBoard == null || mediumBoard.length == 0) { randomBoard = getRandomBoard(difficulty); mediumBoard = generate(randomBoard); } return mediumBoard; case HARD: if(hardBoard == null || hardBoard.length == 0) { randomBoard = getRandomBoard(difficulty); hardBoard = generate(randomBoard); } return hardBoard; case EXPERT: if(expertBoard == null || expertBoard.length == 0) { randomBoard = getRandomBoard(difficulty); expertBoard = generate(randomBoard); } return expertBoard; case OMEGA: if(omegaBoard == null || omegaBoard.length == 0) { randomBoard = getRandomBoard(difficulty); omegaBoard = generate(randomBoard); } return omegaBoard; case DEVILISH: if(devilishBoard == null || devilishBoard.length == 0) { randomBoard = getRandomBoard(difficulty); devilishBoard = generate(randomBoard); } return devilishBoard; case TEST: if(testBoard == null || testBoard.length == 0) { randomBoard = getRandomBoard(difficulty); testBoard = generate(randomBoard); } return testBoard; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var clearAndSetBoard = (difficulty) => { board = null; selectedSquare = null; mode = NORMAL_MODE; resetDifficulty(difficulty); board = getBoard(difficulty); // TODO: maybe invest the effort to use the existing popup popup.hide(); popup = createPopupUI(difficulty, board); popup.show(); } var checkBoard = (board) => { return checkForDuplicates(board, hasAllNumbers(board)); } var setNormalNumber = (number, cell, gridCellContent) => { if(cell.number == number) { cell.number = 0; cell.cornerMarks = [] cell.centerMarks = [] gridCellContent.children[0].text = ""; return; } var marksGrid = gridCellContent.children[1]; for(var j = 0; j < marksGrid.children.length; j++) { marksGrid.children[j].text = "" } cell.number = number; gridCellContent.children[0].text = `${number}` } var setNormalNumberFromCell = (cell, gridCellContent) => { if(cell.number < 1) { gridCellContent.children[0].text = ""; return; } var marksGrid = gridCellContent.children[1]; for(var j = 0; j < marksGrid.children.length; j++) { marksGrid.children[j].text = "" } gridCellContent.children[0].text = `${cell.number}` } var clearCornerNumbers = (gridCellContent) => { for (let i of [0, 1, 3, 4]) { gridCellContent.children[1].children[i].text = ""; } } var setCornerNumbers = (cell, gridCellContent) => { let markIndices = [0, 1, 3, 4, 0, 1, 3, 4, 4] for(let i = 0; i < cell.cornerMarks.length; i++) { var markIndex = markIndices[i]; if(i <= 3) { gridCellContent.children[1].children[markIndex].text = [0, 3].includes(markIndex) ? ` ${cell.cornerMarks[i]}` : `${cell.cornerMarks[i]} `; } else { gridCellContent.children[1].children[markIndex].text += [0, 3].includes(markIndex) ? ` ${cell.cornerMarks[i]}` : `${cell.cornerMarks[i]} `; } } } var setCornerNumber = (number, cell, gridCellContent) => { if(!isEmpty(cell)) return; if(cell.cornerMarks.length == 0) { cell.cornerMarks.push(number); gridCellContent.children[1].children[0].text = ` ${number}`; return; } if(cell.cornerMarks.includes(number)) { log(cell.cornerMarks.join(', ')); cell.cornerMarks.splice(cell.cornerMarks.indexOf(number), 1); log(cell.cornerMarks.join(', ')); } else { cell.cornerMarks.push(number); cell.cornerMarks.sort(); } clearCornerNumbers(selectedSquare.content); setCornerNumbers(cell, selectedSquare.content); } var setCornerNumbersFromCell = (cell, square, clearBeforeSet) => { if(!isEmpty(cell)) return; if(cell.cornerMarks.length == 0) return; if(clearBeforeSet) clearCornerNumbers(square.content); setCornerNumbers(cell, square.content); } var clearCenterNumbers = (gridCellContent) => { gridCellContent.children[1].children[2].text = ""; } var setCenterNumbers = (cell, gridCellContent) => { gridCellContent.children[1].children[2].text = cell.centerMarks.join(''); } var setCenterNumber = (number, cell, gridCellContent) => { if(!isEmpty(cell)) return; if(cell.centerMarks.length == 0) { cell.centerMarks.push(number); gridCellContent.children[1].children[2].text = `${number}`; return; } if(cell.centerMarks.includes(number)) { cell.centerMarks.splice(cell.centerMarks.indexOf(number), 1); } else { cell.centerMarks.push(number); cell.centerMarks.sort(); } setCenterNumbers(cell, gridCellContent); } var setCenterNumbersFromCell = (cell, square) => { if(!isEmpty(cell)) return; if(cell.centerMarks.length == 0) return; setCenterNumbers(cell, square.content); } var setNumber = (number, board, r, c) => { if(selectedSquare != null) { var cell = board[getBoardNum(r, c)]; switch (mode) { case NORMAL_MODE: setNormalNumber(number, cell, selectedSquare.content); break; case CORNER_MODE: setCornerNumber(number, cell, selectedSquare.content); break; case CENTER_MODE: setCenterNumber(number, cell, selectedSquare.content); break; default: break; } } } /** * Updates the UI of a square according to the values of a given cell * @param {*} square * @param {*} cell */ var updateSquareFromCell = (square, cell) => { clearSquare(square.content); setNormalNumberFromCell(cell, square.content); if(cell.number >= 1) return; setCornerNumbersFromCell(cell, square, false); setCenterNumbersFromCell(cell, square); } /** * Resets the UI for a square (removing any number, corner- and center marks) * @param {*} gridCellContent */ var clearSquare = (gridCellContent) => { gridCellContent.children[0].text = ""; clearCornerNumbers(gridCellContent); clearCenterNumbers(gridCellContent); } /** * Resets a cell and updates the UI accordingly * @param {*} board * @param {*} r * @param {*} c * @param {*} gridCellContent */ var clearCell = (board, r, c, gridCellContent) => { var cell = board[getBoardNum(r, c)]; cell.number = 0; cell.cornerMarks = []; cell.centerMarks = []; clearSquare(gridCellContent); } var increaseCurrency = (reward) => { currency.value += reward; } var getStackForModeAndDifficulty = (difficulty, operationMode) => { var difficultyStack; switch(difficulty) { case EASY: if(operationMode == UNDO_MODE){ if(easyUndoStack == null) easyUndoStack = []; difficultyStack = easyUndoStack; } else if (operationMode == REDO_MODE) { if(easyRedoStack == null) easyRedoStack = []; difficultyStack = easyRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case MEDIUM: if(operationMode == UNDO_MODE){ if(mediumUndoStack == null) mediumUndoStack = []; difficultyStack = mediumUndoStack; } else if (operationMode == REDO_MODE) { if(mediumRedoStack == null) mediumRedoStack = []; difficultyStack = mediumRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case HARD: if(operationMode == UNDO_MODE){ if(hardUndoStack == null) hardUndoStack = []; difficultyStack = hardUndoStack; } else if (operationMode == REDO_MODE) { if(hardRedoStack == null) hardRedoStack = []; difficultyStack = hardRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case EXPERT: if(operationMode == UNDO_MODE){ if(expertUndoStack == null) expertUndoStack = []; difficultyStack = expertUndoStack; } else if (operationMode == REDO_MODE) { if(expertRedoStack == null) expertRedoStack = []; difficultyStack = expertRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case OMEGA: if(operationMode == UNDO_MODE){ if(omegaUndoStack == null) omegaUndoStack = []; difficultyStack = omegaUndoStack; } else if (operationMode == REDO_MODE) { if(omegaRedoStack == null) omegaRedoStack = []; difficultyStack = omegaRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case DEVILISH: if(operationMode == UNDO_MODE){ if(devilishUndoStack == null) devilishUndoStack = []; difficultyStack = devilishUndoStack; } else if (operationMode == REDO_MODE) { if(devilishRedoStack == null) devilishRedoStack = []; difficultyStack = devilishRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; case TEST: if(operationMode == UNDO_MODE){ if(testUndoStack == null) testUndoStack = []; difficultyStack = testUndoStack; } else if (operationMode == REDO_MODE) { if(testRedoStack == null) testRedoStack = []; difficultyStack = testRedoStack; } else{ throw new Error("Unrecognized operationMode " + operationMode + "; cannot proceed"); } break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } return difficultyStack; } var resetUndoRedoStacks = (difficulty) => { switch(difficulty) { case EASY: easyUndoStack = []; easyRedoStack = []; break; case MEDIUM: mediumUndoStack = []; mediumRedoStack = []; break; case HARD: hardUndoStack = []; hardRedoStack = []; break; case EXPERT: expertUndoStack = []; expertRedoStack = []; break; case OMEGA: omegaUndoStack = []; omegaRedoStack = []; break; case DEVILISH: devilishUndoStack = []; devilishRedoStack = []; break; case TEST: testUndoStack = []; testRedoStack = []; break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var resetRedoStack = (difficulty) => { var difficultyRedoStack = getStackForModeAndDifficulty(difficulty, REDO_MODE); difficultyRedoStack = []; redoButton.isEnabled = false; } /** * For a given difficulty adds a cell to the stack containing all the previous states. * @param {*} difficulty The difficulty of the puzzle to select the correct stack * @param {*} cell The cell before an action took place, to save it for future restoration * @param {number} cellIndex The index of the saved cell in the board */ var addToUndoStack = (difficulty, cell, cellIndex) => { var undoState = { cellIndex: cellIndex, cell: deepcopyCell(cell) }; var difficultyUndoStack = getStackForModeAndDifficulty(difficulty, UNDO_MODE); difficultyUndoStack.push(undoState); undoButton.isEnabled = true; } /** * For a given difficulty adds a cell to the stack containing all the states reverted by undo operations. * @param {*} difficulty The difficulty of the puzzle to select the correct stack * @param {*} cell The cell before an action took place, to save it for future restoration * @param {number} cellIndex The index of the saved cell in the board */ var addToRedoStack = (difficulty, cell, cellIndex) => { var redoState = { cellIndex: cellIndex, cell: deepcopyCell(cell) }; var difficultyRedoStack = getStackForModeAndDifficulty(difficulty, REDO_MODE); difficultyRedoStack.push(redoState); redoButton.isEnabled = true; } /** * Undo the last taken action. This will pop the latest entry from the undo stack, update the board, update the UI and add the cell to the redo stack. * @param {*} difficulty The difficulty stack to get the previous state from */ var undo = (difficulty) => { restoreState(difficulty, UNDO_MODE); } /** * Redo the last undone action. This will pop the latest entry from the redo stack, update the board, update the UI and add the cell to the undo stack. * @param {*} difficulty The difficulty stack to get the previous state from */ var redo = (difficulty) => { restoreState(difficulty, REDO_MODE); } var restoreState = (difficulty, operationMode) => { if(operationMode != UNDO_MODE && operationMode != REDO_MODE) throw new Error("Unrecognized operation mode " + operationMode + "; cannot proceed"); var difficultyStack = getStackForModeAndDifficulty(difficulty, operationMode); var previousState = difficultyStack.pop(); var currentState = board[previousState.cellIndex]; var operation = operationMode == UNDO_MODE ? "Undo" : "Redo"; logObject(previousState, operation + " to following cell state: "); logObject(previousState.cell, "Cell: "); UpdateBoardAndSquare(previousState.cell, previousState.cellIndex); if(operationMode == UNDO_MODE) { addToRedoStack(difficulty, currentState, previousState.cellIndex); } else { addToUndoStack(difficulty, currentState, previousState.cellIndex); } log(difficultyStack.length); if(difficultyStack.length == 0) { var operationButton = operationMode == UNDO_MODE ? undoButton : redoButton; operationButton.isEnabled = false; } // Update invalid markers resetBackgroundColors(); var checkResult = checkBoard(board); if(checkResult[1].size > 0) markInvalidCells(checkResult[1]); } var getBestTimerForDifficulty = (difficulty) => { switch(difficulty) { case EASY: if(easyBestTimer == null){ easyBestTimer = { time: -1, hints: -1 }; } return easyBestTimer; case MEDIUM: if(mediumBestTimer == null){ mediumBestTimer = { time: -1, hints: -1 }; } return mediumBestTimer; case HARD: if(hardBestTimer == null){ hardBestTimer = { time: -1, hints: -1 }; } return hardBestTimer; case EXPERT: if(expertBestTimer == null){ expertBestTimer = { time: -1, hints: -1 }; } return expertBestTimer; case OMEGA: if(omegaBestTimer == null){ omegaBestTimer = { time: -1, hints: -1 }; } return omegaBestTimer; case DEVILISH: if(devilishBestTimer == null){ devilishBestTimer = { time: -1, hints: -1 }; } return devilishBestTimer; case TEST: if(testBestTimer == null){ testBestTimer = { time: -1, hints: -1 }; } return testBestTimer; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var getTimerForDifficulty = (difficulty) => { //log("Getting timer for difficulty " + difficulty); switch(difficulty) { case EASY: if(easyTimer == null){ easyTimer = { time: 0, hints: 0 }; } return easyTimer; case MEDIUM: if(mediumTimer == null){ mediumTimer = { time: 0, hints: 0 }; } return mediumTimer; case HARD: if(hardTimer == null){ hardTimer = { time: 0, hints: 0 }; } return hardTimer; case EXPERT: if(expertTimer == null){ expertTimer = { time: 0, hints: 0 }; } return expertTimer; case OMEGA: if(omegaTimer == null){ omegaTimer = { time: 0, hints: 0 }; } return omegaTimer; case DEVILISH: if(devilishTimer == null){ devilishTimer = { time: 0, hints: 0 }; } return devilishTimer; case TEST: if(testTimer == null){ testTimer = { time: 0, hints: 0 }; } return testTimer; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var resetTimer = (difficulty) => { currentStartTime = null; log("Resetting timer for difficulty " + difficulty); switch(difficulty) { case EASY: easyTimer.time = 0; easyTimer.hints = 0; break; case MEDIUM: mediumTimer.time = 0; mediumTimer.hints = 0; break; case HARD: hardTimer.time = 0; hardTimer.hints = 0; break; case EXPERT: expertTimer.time = 0; expertTimer.hints = 0; break; case OMEGA: omegaTimer.time = 0; omegaTimer.hints = 0; break; case DEVILISH: devilishTimer.time = 0; devilishTimer.hints = 0; break; case TEST: testTimer.time = 0; testTimer.hints = 0; break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } } var UpdateTimerIfBetter = (difficulty) => { var timer = getTimerForDifficulty(difficulty); var bestTimer = getBestTimerForDifficulty(difficulty); if(bestTimer.time < 0 || timer.time < bestTimer.time || timer.time == bestTimer.time && timer.hints < bestTimer.hints) { bestTimer.time = timer.time; bestTimer.hints = timer.hints; } } var getElapsedTime = (timer) => { var currentTime = Date.now(); var difference = currentTime - currentStartTime + timer.time; return difference; } var updateTimerLabel = () => { var timer = getTimerForDifficulty(difficulty); var elapsedTime = getElapsedTime(timer); var tempTimer = { time: elapsedTime, hints: timer.hints }; //logObject(tempTimer); var timerString = TimerAsString(tempTimer); timerLabel.text = "Time: " + timerString; } /** * Converts the number of milliseconds saved in a timer into a human readable format. * The format is analogous to the one used for minigames, except including hours (maybe useful for really difficult puzzles): * hh:mm:ss:fff * @param {*} timer The timer to use * @param {bool} includeHints If true, the string will include the amount of hints used for this timer in brackets * @returns The time converted to a string formatted hh:mm:ss:fff or - when no time was set so far; optionally includes (X hints) */ var TimerAsString = (timer, includeHints) => { if(timer.time < 0) return "-"; //log("Creating human readable form of " + timer.time); //Get hours from milliseconds var hours = timer.time / (1000 * 60 * 60); var absoluteHours = Math.floor(hours); var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours; //Get remainder from hours and convert to minutes var minutes = (hours - absoluteHours) * 60; var absoluteMinutes = Math.floor(minutes); var m = absoluteMinutes > 9 ? absoluteMinutes : '0' + absoluteMinutes; //Get remainder from minutes and convert to seconds var seconds = (minutes - absoluteMinutes) * 60; var absoluteSeconds = Math.floor(seconds); var s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds; //Get remainder from seconds and convert to milliseconds var milliseconds = (seconds - absoluteSeconds) * 1000; var absoluteMilliSeconds = Math.floor(milliseconds); var ms = absoluteMilliSeconds > 99 ? absoluteMilliSeconds : '0' + absoluteMilliSeconds > 9 ? absoluteMilliSeconds : '0' + absoluteMilliSeconds; var humanReadableString = h + ':' + m + ':' + s + ':' + ms; if(includeHints){ humanReadableString = humanReadableString + " (" + timer.hints + " hints)"; } return humanReadableString; } //#endregion //////////////////////////////////////////////////////// // // SUDOKU Code // //#region Sudoku Game Utility var getBoardNum = (row, col) => row * ROWS + col; /** * Checks if the cell has no valid Sudoku number (pencilmarks do not count!) * @param {*} cell * @returns True if no number is in the cell */ var isEmpty = (cell) => { return cell.number < 1; } var hasAllNumbers = (board) => { for(let i = 0; i < BOARD_SIZE; i++) { if(board[i].number < 1) { return false; } } return true; } /** * Checks a given board for numbers violating the Sudoku rules, so appearing more than once in a row, column or box. * Can be used to check if a board is solved, if all cells have a number. * It will check for every rule violation and not abort early. * @param {*} board The board to be checked * @param {*} fullBoard If the board has numbers in every cell * @returns An array where the first entry indicates if the board is in a correctly solved state (impossible if fullBoard is false) * and the second entry is an array holding [row, column] pairs of all numbers violating the rules (empty if solved) */ var checkForDuplicates = (board, fullBoard) => { var correct = fullBoard; var wrongCells = new Set(); // Check rows for(let r = 0; r < ROWS; r++) { for (let rc = 0; rc < COLS; rc++) { var currentNumber = board[getBoardNum(r, rc)].number; if(currentNumber < 1) continue; for(let rcc = rc + 1; rcc < COLS; rcc++) { var rowCompareNumber = board[getBoardNum(r, rcc)].number; if(currentNumber == rowCompareNumber) { correct = false; wrongCells.add([r, rc]); wrongCells.add([r, rcc]); } } } } // Check columns for(let c = 0; c < COLS; c++) { for (let cr = 0; cr < ROWS; cr++) { var currentNumber = board[getBoardNum(cr, c)].number; if(currentNumber < 1) continue; for(let crc = cr + 1; crc < ROWS; crc++) { var columnCompareNumber = board[getBoardNum(crc, c)].number; if(currentNumber == columnCompareNumber) { correct = false; wrongCells.add([cr, c]); wrongCells.add([crc, c]); } } } } // Check boxes for(let bri = 0; bri < ROWS; bri += 3) { for(let bci = 0; bci < COLS; bci += 3) { for(let br = 0; br < BOX_SIZE; br++) { for(let bc = 0; bc < BOX_SIZE; bc++) { var currentNumber = board[getBoardNum(bri + br, bci + bc)].number; if(currentNumber < 1) continue; for(let brc = 0; brc < BOX_SIZE; brc++) { for(let bcc = 0; bcc < BOX_SIZE; bcc++) { if(brc == br && bcc == bc) continue; var boxCompareNumber = board[getBoardNum(bri + brc, bci + bcc)].number; if(currentNumber == boxCompareNumber) { correct = false; wrongCells.add([bri + br, bci + bc]); wrongCells.add([bri + brc, bci + bcc]); } } } } } } } return [correct, wrongCells]; } var boardFromString = (boardString) => { var board = [] for(let i = 0; i < boardString.length; i++) { var curChar = boardString.charAt(i); var number = parseInt(curChar, 10); board[i] = { number: number, isGiven: number > 0, cornerMarks: [], centerMarks: [] } } return board } var getRandomBoard = (difficulty) => { var boardString = '' switch(difficulty) { case EASY: var index = Math.min(easyPuzzles.length -1 , Math.round(Math.random() * easyPuzzles.length)); boardString = easyPuzzles[index]; break; case MEDIUM: var index = Math.min(mediumPuzzles.length -1 , Math.round(Math.random() * mediumPuzzles.length)); boardString = mediumPuzzles[index]; break; case HARD: var index = Math.min(hardPuzzles.length -1 , Math.round(Math.random() * hardPuzzles.length)); boardString = hardPuzzles[index]; break; case EXPERT: var index = Math.min(expertPuzzles.length -1 , Math.round(Math.random() * expertPuzzles.length)); boardString = expertPuzzles[index]; break; case OMEGA: var index = Math.min(omegaPuzzles.length -1 , Math.round(Math.random() * omegaPuzzles.length)); boardString = omegaPuzzles[index]; break; case DEVILISH: var index = Math.min(devilishPuzzles.length -1 , Math.round(Math.random() * devilishPuzzles.length)); boardString = devilishPuzzles[index]; break; case TEST: var index = Math.min(testPuzzles.length -1 , Math.round(Math.random() * testPuzzles.length)); boardString = testPuzzles[index]; break; default: throw new Error("Unrecognized difficulty " + difficulty + "; cannot proceed"); } return boardFromString(boardString); } var deepcopyCell = (cell) => { var cellcopy = JSON.parse(JSON.stringify(cell)); return cellcopy; } var deepcopy = (board) => { var copy = [] for(let i = 0; i < board.length; i++) { // Apparently that is the way to go? var cellcopy = JSON.parse(JSON.stringify(board[i])); copy[i] = cellcopy; } return copy; } var rotateBoard = (board, numRotations) => { for(let i = 0; i < numRotations; i++) { var copy = deepcopy(board); for(let r = 0; r < ROWS; r++) { for(let c = 0; c < COLS; c++) { // index magic was adapted from here: https://math.stackexchange.com/a/1676457/528931 (and here https://github.com/MitchelPaulin/sudoku-rs/blob/main/src/puzzle_transformer.rs) board[c * 9 + 9 - r - 1] = copy[getBoardNum(r, c)] } } } } var mirrorBoardHorizontally = (board) => { var copy = deepcopy(board); for(let r = 0; r < ROWS; r++) { for(let c = 0; c < COLS; c++) { board[getBoardNum(r, c)] = copy[getBoardNum(r, COLS - 1 - c)]; } } } var mirrorBoardVertically = (board) => { var copy = deepcopy(board); for(let r = 0; r < ROWS; r++) { for(let c = 0; c < COLS; c++) { board[getBoardNum(r, c)] = copy[getBoardNum(ROWS -1 - r, c)]; } } } var permutateRowsGlobally = (board) => { var permutation = getPermutationMapping(3, false); var copy = deepcopy(board); for(let rb = 0; rb < 3; rb++) { var correspondentRowBlock = permutation[rb]; for(let r = 0; r < 3; r++) { for(let c = 0; c < COLS; c++) { board[getBoardNum(rb * 3 + r, c)] = copy[getBoardNum(correspondentRowBlock * 3 + r, c)]; } } } } var permutateRowsBlockwise = (board) => { var permutationBlock1 = getPermutationMapping(3, false); var permutationBlock2 = getPermutationMapping(3, false); var permutationBlock3 = getPermutationMapping(3, false); var copy = deepcopy(board); for(let r = 0; r < 3; r++) { for(let c = 0; c < COLS; c++) { board[getBoardNum(permutationBlock1[r], c)] = copy[getBoardNum(r, c)]; board[getBoardNum(permutationBlock2[r] + 3, c)] = copy[getBoardNum(r + 3, c)]; board[getBoardNum(permutationBlock3[r] + 6, c)] = copy[getBoardNum(r + 6, c)]; } } } var permutateColumnsGlobally = (board) => { var permutation = getPermutationMapping(3, false); var copy = deepcopy(board); for(let cb = 0; cb < 3; cb++) { var correspondentColumnBlock = permutation[cb]; for(let r = 0; r < ROWS; r++) { for(let c = 0; c < 3; c++) { board[getBoardNum(r, cb * 3 + c)] = copy[getBoardNum(r, correspondentColumnBlock * 3 + c)]; } } } } var permutateColumnsBlockwise = (board) => { var permutationBlock1 = getPermutationMapping(3, false); var permutationBlock2 = getPermutationMapping(3, false); var permutationBlock3 = getPermutationMapping(3, false); var copy = deepcopy(board); for(let c = 0; c < 3; c++) { for(let r = 0; r < ROWS; r++) { board[getBoardNum(r, permutationBlock1[c])] = copy[getBoardNum(r, c)]; board[getBoardNum(r, permutationBlock2[c] + 3)] = copy[getBoardNum(r, c + 3)]; board[getBoardNum(r, permutationBlock3[c] + 6)] = copy[getBoardNum(r, c + 6)]; } } } var permutateNumbers = (board) => { var permutation = getPermutationMapping(ROWS + 1, true); var copy = deepcopy(board); for(let r = 0; r < ROWS; r++) { for(let c = 0; c < COLS; c++) { board[getBoardNum(r, c)].number = permutation[copy[getBoardNum(r, c)].number]; } } } var getPermutationMapping = (size, includeFixedZero) => { var mapping = []; var index = includeFixedZero ? 1 : 0; var numbers = [] for(let n = index; n < size; n++) { numbers[n - index] = n; } for(let i = 0; i < size; i++) { var mappedNumber = Math.round((Math.random() * (numbers.length - 1))); var chosen = numbers.splice(mappedNumber, 1)[0]; mapping[i] = chosen; } if(includeFixedZero) { mapping.unshift(0); } return mapping; } /** * This is currently just doing some symmetry rotation and number permutation. * In the future it should actually generate a puzzle whose difficulty is dependent on the strategies needed to solve it. * If that can be done in a reasonable enough time... * @param {*} board * @returns */ var generate = (board) => { // This has the potential for a lot (> 1e10 if somebody else's math is correct) of combinations, so you shouldn't really see any repeat or anything looking that familiar :) var percentage = 0.5; //logBoard(board); var rotate = Math.random() > percentage; if(rotate) { var numRotations = Math.round( Math.random() * 3); if(numRotations > 0) { rotateBoard(board, numRotations); //log("rotated board " + numRotations + " times"); //logBoard(board); } } var mirrorHorizontally = Math.random() > percentage; if(mirrorHorizontally) { mirrorBoardHorizontally(board); //log("mirrored horizontally"); //logBoard(board); } var mirrorVertically = Math.random() > percentage; if(mirrorVertically) { mirrorBoardVertically(board); //log("mirrored vertically"); //logBoard(board); } var permutateRowsGlobal = Math.random() > percentage; if(permutateRowsGlobal) { permutateRowsGlobally(board); //log("permutated rows globally"); //logBoard(board); } var permutateRowsBlock = Math.random() > percentage; if(permutateRowsBlock) { permutateRowsBlockwise(board); //log("permutated rows blockwise"); //logBoard(board); } var permutateColumnsGlobal = Math.random() > percentage; if(permutateColumnsGlobal) { permutateColumnsGlobally(board); //log("permutated columns globally"); //logBoard(board); } var permutateColumnsBlock = Math.random() > percentage; if(permutateColumnsBlock) { permutateColumnsBlockwise(board); //log("permutated columns blockwise"); //logBoard(board); } var permutateNums = Math.random() > percentage; if(permutateNums) { permutateNumbers(board); //log("permutated numbers"); //logBoard(board); } return board; } //#endregion //#region DEBUG var logObject = (object, prefix = "") => { var output = ''; for (var property in object) { output += property + ': ' + object[property]+'; '; } log(prefix + "object: " + output); } var logBoard = (board) => { for(var r = 0; r < ROWS; r++){ log("-------------------------------------"); currentRowCells = [] for(var c = 0; c < COLS; c++){ var cell = board[r * 9 + c]; currentRowCells.push(cell.number < 1 ? ' ' : cell.number); currentRowCells.push('|'); } log('| ' + currentRowCells.join(' ')); } log("-------------------------------------"); } //#endregion