diff --git a/android/gradle.properties b/android/gradle.properties index 85b94f88ee157e1d1b3cec184c8948902443d36f..65eed6426393974efb5a056ec44936d42b5ef2a1 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.7 -app.versionCode=7 +app.versionName=0.0.8 +app.versionCode=8 diff --git a/assets/files/templates.json b/assets/files/templates.json index d0312cf72a6cf310d52a43b2c10a4958036123c8..cd7ae73e952b2c251ac4e5addee7204f2c0ac351 100644 --- a/assets/files/templates.json +++ b/assets/files/templates.json @@ -2,43 +2,78 @@ "templates": { "2x2": { "easy": [ - "0000320400210100", - "0003034000040000", - "0402000000004020", - "0002001443002000", - "4312020000000020" + "0120040000010300", + "4000002030000401", + "4000030000010023", + "0100000410022300", + "0040000101343012", + "0000020334020000", + "0003130200200134", + "2000431000230000", + "0103304000000300", + "0043000000144030" ], "medium": [ - "3012200003000000", - "0003104030002100", - "0410100400000030", - "0100000030120000" + "4000010000010034", + "3002040100031300", + "4000000302000010", + "0100300000020043", + "0000004040013000", + "1003000420000000", + "1000000302300002", + "2001010400430000", + "0401200300004000", + "3004042000002000" ], "hard": [ - "0001000003000034", - "1000032000040002", - "0420000000100040", - "0300010010030240" + "0001000004200200", + "4030100000040000", + "0130021000002000", + "2000130000010020", + "0103300000010400", + "0000042000001004", + "0300000301044000", + "0001010002000003", + "0020020300010300", + "0410200010000200" ] }, "3x3": { "easy": [ - "402370008801000000009086400108020000200060009000090207006450100000000603700032905", - "000100035060203000020059041002008304000000000509600700450320010000401070780005000", - "000130000070960050596000001000700105380000079704009000800000243040083010000091000", - "000300000001006030384200100700083056600000003830610002008005649060400200000002000" + "004210007090083010000500200007050831910600740400000000006100000801007450050300000", + "740150820562400070000000006308002000106040932090700605901830067680001000437690018", + "798413000340002809251800047003060092070000008519728030127650980900281670860907501", + "061030480300100000408060000009401008832070904100093056000009002903748001704052830", + "807625419000038602056090837729050060508061973103800500070902041081047296092316780", + "046000510815400039709060800074000208082541060053000104060014025091002300007093000", + "305000000009020600742301085918043500030107020407080019803710096674009132200406700", + "090002015165097040078450693802914000000005309000003081014529876580706132006130054", + "608010307070650800409008000000083600965074003300000072104500730806307200030020064", + "046050900000600700825010430002470085150809640000060000900238000038000120200190860" ], "medium": [ - "800050003004902700000340600305100800200030001001008409006093000003807200100060004", - "020000370500036029000200605004701006000000000600904100802009000950340007043000050", - "005060300091000005206400078000957010000000000080143000510004709900000620002030500", - "040006090080000320090570040005307900800090004009804200050089030073000010010700060" + "200960400006070900700000200000002040064090800002080307090000030038000502407100098", + "900500103010000005200001470825390604740000308000005702000219000060854020090063000", + "847300950900040003100008640004030001309804005000070406401785009000020004500000200", + "032070100048100090105300002001000050003000000200817400000783004000901000507426830", + "028000196506400700900816205069042000000078002702000000000591008100000000695004000", + "300000005006800100200005090002500000000107420718000003007008054460000200830640001", + "460100507915007000000000030000709002040000103000200050007024600000000780100675309", + "010039020000600041200000003360107002020003160000265370040076005100904700090010000", + "840063502100090003030702100204087300000420007080000009000076930050314200026000000", + "010805306004010800000000400057400008021073040040200710600000130000380004470026080" ], "hard": [ - "010900700050000060006207900600700080320804096080006007005602300060000010008003040", - "000009850900600402007050001000000093861000724730000000100020600406003009092800000", - "064900100000020006107006090000050364030000010576040000090500201700090000005007680", - "008007010512000370470051000000705900000010000004306000000560098045000763030900100" + "300089050520600007679150300000390804090068000700010005000071030000000000000935701", + "016007000000040105000000020023900046840000900607300010002501000008000600100639000", + "001800200089006050000053000600004700090070000007200809002700604000002100304000020", + "005000001000002598021000000009006003010000045000018700030049806407000000090760010", + "060000000058093000934080700002650007800900020400010008009307106000000080040009075", + "092500730030000900004930100008320506607050020300000000000005810500070009000208050", + "400063001008507000000900070300000910890070000106005004001600402000058036500400000", + "000007800004003000000000092000000184007009030810300059009034010400600020756190000", + "000003040009020078060700000952604000040210000000000900300500780090001060006002010", + "040070620300800904062000700407080000000615040056040000784060009600100070900000000" ] } } diff --git a/generator/batch.sh b/generator/batch.sh new file mode 100755 index 0000000000000000000000000000000000000000..db347183e0300a15b2ef6d13a6e74bc31ad2a93e --- /dev/null +++ b/generator/batch.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +ALLOWED_SIZE_VALUES="2 3" +ALLOWED_DIFFICULTY_VALUES="easy medium hard" +GRIDS_COUNT=10 + +for SIZE in ${ALLOWED_SIZE_VALUES}; do + echo "Size: ${SIZE}x${SIZE}" + for DIFFICULTY in ${ALLOWED_DIFFICULTY_VALUES}; do + echo "Difficulty: ${DIFFICULTY}" + for i in $(seq ${GRIDS_COUNT}); do + python ${CURRENT_DIR}/generate.py ${SIZE} ${DIFFICULTY} | tail -n 1 + done + done +done diff --git a/generator/generate.py b/generator/generate.py new file mode 100644 index 0000000000000000000000000000000000000000..62772326bce89f6af22d2c815882a14b0cb9daff --- /dev/null +++ b/generator/generate.py @@ -0,0 +1,198 @@ +import sys +from random import randint, shuffle + +if (len(sys.argv) != 3): + print('Usage: generate.py size difficulty') + print('size: [2|3]') + print('difficulty: [easy|medium|hard]') + exit() + +size, difficulty = sys.argv[1], sys.argv[2] + +size = int(size) + +if not size in [2, 3]: + print('wrong size given') + exit() + +if not difficulty in ['easy', 'medium', 'hard']: + print('wrong difficulty given') + exit() + +############################################################################ + +# medium -> 5 (default) ; easy -> 1 ; hard -> 5 +difficultyLevel = 5; +if difficulty == 'easy': + difficultyLevel = 1 +if difficulty == 'hard': + difficultyLevel = 10 + +# draw grid (array style) +def drawGrid(grid): + for row in range(len(grid)): + for col in range(len(grid[row])): + if grid[row][col] != 0: + sys.stdout.write(str(grid[row][col])) + else: + sys.stdout.write(' ') + sys.stdout.write('\n') + sys.stdout.write('\n') + +# draw grid (inline style) +def drawGridInline(grid): + for row in range(len(grid)): + for col in range(len(grid[row])): + sys.stdout.write(str(grid[row][col])) + sys.stdout.write('\n') + +#initialise empty grid +def generateEmptyGrid(size): + emptyGrid = [] + for i in range(size * size): + emptyGrid.append([]) + for j in range(size * size): + emptyGrid[i].append(0) + return emptyGrid + +#A check if the grid is full +def checkFullyCompletedGrid(grid): + for row in range(len(grid)): + for col in range(len(grid[row])): + if grid[row][col] == 0: + return False + return True + +# (deep) copy of grid +def copyGrid(grid): + copiedGrid = [] + for row in range(len(grid)): + copiedGrid.append([]) + for col in range(len(grid[row])): + copiedGrid[row].append(grid[row][col]) + return copiedGrid + +#A backtracking/recursive function to check all possible combinations of numbers until a solution is found +def solveGrid(grid): + gridSize = len(grid) + cellsCount = len(grid) * len(grid[0]) + numberList = [(value + 1) for value in range(gridSize)] + + global solutionsCount + + #Find next empty cell + for i in range(0, cellsCount): + row = i // gridSize + col = i % gridSize + if grid[row][col] == 0: + shuffle(numberList) + for value in numberList: + #Check that this value has not already be used on this row + if not(value in grid[row]): + #Check that this value has not already be used on this column + foundInColumn = False + for r in range(0, gridSize): + if (value == grid[r][col]): + foundInColumn = True + + if not foundInColumn: + # Get sub-square + blockColFrom = size * int(col / size) + blockRowFrom = size * int(row / size) + square = [grid[i][blockColFrom:blockColFrom + size] for i in range(blockRowFrom, blockRowFrom + size)] + + #Check that this value has not already be used on this sub square + if not any(value in squareLine for squareLine in square): + grid[row][col] = value + if checkFullyCompletedGrid(grid): + solutionsCount += 1 + break + else: + if solveGrid(grid): + return True + break + grid[row][col] = 0 + +#A backtracking/recursive function to check all possible combinations of numbers until a solution is found +def fillGrid(grid): + gridSize = len(grid) + cellsCount = len(grid) * len(grid[0]) + numberList = [(value + 1) for value in range(gridSize)] + + global solutionsCount + + #Find next empty cell + for i in range(0, cellsCount): + row = i // gridSize + col = i % gridSize + if grid[row][col] == 0: + shuffle(numberList) + for value in numberList: + #Check that this value has not already be used on this row + if not(value in grid[row]): + #Check that this value has not already be used on this column + foundInColumn = False + for r in range(0, gridSize): + if (value == grid[r][col]): + foundInColumn = True + + if not foundInColumn: + # Get sub-square + blockColFrom = size * int(col / size) + blockRowFrom = size * int(row / size) + square = [grid[i][blockColFrom:blockColFrom + size] for i in range(blockRowFrom, blockRowFrom + size)] + + #Check that this value has not already be used on this sub square + if not any(value in squareLine for squareLine in square): + grid[row][col] = value + if checkFullyCompletedGrid(grid): + return True + else: + if fillGrid(grid): + return True + break + grid[row][col] = 0 + +solutionsCount = 1 +def computeResolvableGrid(grid, wantedAttempts): + gridSize = size * size + global solutionsCount + + # A higher number of attempts will end up removing more numbers from the grid + # Potentially resulting in more difficiult grids to solve! + + # Start Removing Numbers one by one + attempts = wantedAttempts + while attempts > 0: + # Select a random cell that is not already empty + row = randint(0, gridSize - 1) + col = randint(0, gridSize - 1) + while grid[row][col] == 0: + row = randint(0, gridSize - 1) + col = randint(0, gridSize - 1) + + # Remove value in this random cell + savedCellValue = grid[row][col] + grid[row][col] = 0 + + solutionsCount = 0 + solveGrid(copyGrid(grid)) + + # Non unique solution => restore this cell value + if solutionsCount != 1: + grid[row][col] = savedCellValue + attempts -= 1 + +grid = generateEmptyGrid(size) + +sys.stdout.write('Solved grid:\n') +fillGrid(grid) +drawGrid(grid) + +computeResolvableGrid(grid, difficultyLevel) + +sys.stdout.write('Generated grid:\n') +drawGrid(grid) + +sys.stdout.write('Inline grid:\n') +drawGridInline(grid)