From c3582b1f69b2e40fdbcfb79317ad3a67e7f9826e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Thu, 24 Jun 2021 11:45:39 +0200 Subject: [PATCH] Allow non square sub-blocks, add 3x2 game mode --- android/gradle.properties | 4 +- assets/files/templates.json | 38 ++++++++++ assets/icons/size_2.png | Bin 885 -> 0 bytes assets/icons/size_2x2.png | Bin 0 -> 5580 bytes assets/icons/size_3.png | Bin 1659 -> 0 bytes assets/icons/size_3x2.png | Bin 0 -> 7616 bytes assets/icons/size_3x3.png | Bin 0 -> 7436 bytes generator/batch.sh | 8 +- generator/generate.py | 76 +++++++++++-------- icons/build_game_icons.sh | 5 +- icons/size_2.svg | 2 - icons/size_2x2.svg | 2 + icons/size_3.svg | 2 - icons/size_3x2.svg | 2 + icons/size_3x3.svg | 2 + lib/entities/cell.dart | 13 ++-- lib/layout/board.dart | 6 +- lib/layout/game.dart | 5 +- lib/layout/parameters.dart | 6 +- lib/provider/data.dart | 16 ++-- lib/utils/board_utils.dart | 125 ++++++++++++++++++-------------- lib/utils/game_utils.dart | 3 +- lib/utils/random_pick_grid.dart | 10 +-- 23 files changed, 200 insertions(+), 125 deletions(-) delete mode 100644 assets/icons/size_2.png create mode 100644 assets/icons/size_2x2.png delete mode 100644 assets/icons/size_3.png create mode 100644 assets/icons/size_3x2.png create mode 100644 assets/icons/size_3x3.png delete mode 100644 icons/size_2.svg create mode 100644 icons/size_2x2.svg delete mode 100644 icons/size_3.svg create mode 100644 icons/size_3x2.svg create mode 100644 icons/size_3x3.svg diff --git a/android/gradle.properties b/android/gradle.properties index 81949df..957c40b 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.14 -app.versionCode=14 +app.versionName=0.0.15 +app.versionCode=15 diff --git a/assets/files/templates.json b/assets/files/templates.json index cd7ae73..4f5a298 100644 --- a/assets/files/templates.json +++ b/assets/files/templates.json @@ -38,6 +38,44 @@ "0410200010000200" ] }, + "3x2": { + "easy": [ + "000142214003050361006500503006000405", + "420000000000241030356410602040014603", + "540010030050120405003162360521210643", + "014300023160005423342516406230230005", + "200061004530000206006015045120302050", + "651400230600460201020004500103000006", + "010602206051160305523004640013001000", + "500004042060010032024650460320003046", + "060123102500340050605341006005450610", + "560034004002000041105263600400050306" + ], + "medium": [ + "005000600400300005200004020000010060", + "000060020051306040000005000000040020", + "023010100060500300030100000602005001", + "000306500002400000230000600030010640", + "000030006405030650002300000000160020", + "050100460000030020120603045000000305", + "063010410003000460645000030000100000", + "000000205004100000023010060001530040", + "620304000000500063400010003100040036", + "000120510600300000050301060010005000" + ], + "hard": [ + "015000000400200000000014000000156300", + "003160000050006004300015040200231000", + "060000002000000002000300004010100650", + "002501500400605200000060000000020600", + "030100002600000420021000053000000030", + "103204205100006005000003500000600002", + "000200060300500002012004000003040100", + "052400000001000050005006040032020000", + "200160060023000600000002050430004000", + "400000060000010400006002000205050046" + ] + }, "3x3": { "easy": [ "004210007090083010000500200007050831910600740400000000006100000801007450050300000", diff --git a/assets/icons/size_2.png b/assets/icons/size_2.png deleted file mode 100644 index ad699660fc4d991fb370035ed828b50d0722452c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 885 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE69Lx+145>_WOc@v$I14-?iy0VXB0!k&?2PAU z7#J8NOI#yLg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kz!zA_#EI9;>y6lkoEt6 z`v3pg|Nm$H|Cfb<7#J8B7#SIvm>8LvnOInuSXr6a*qGVbSvWaaxVTukxmkI5Sb2Hb z`1siP`Pl^o*aZdIg@ic8#JD6RxTU0c6%_as75S8u_*GN{H8cb@HHEabg>-a8jf})h zO~oxNB&@6?t*xc(?BqN=6nuQtVq(<e;?(2gHItLIQc|?i(zMglbuu$`va*b;t4(Ta z%^Dlbni|cTo2<IKto!?HCrxykKhJsL0@r0ry|--k*}lzh_pXrBr$W!3iM)0-_QC!5 zCy(QwK1q1?H0|q`jGsTU{{LIzR@=|Oz`$J+<QL4KsinPR=dRsfzkUDv<C8H11LGS{ z7srr_TW{~2^kZ_AaDBLlaiL(KgWADLCxP@i>8zqIzyHoZ=DTs_k~$}z8>#1F=P&tr zXwib%3qnjLa>>MI9Shca#m;yBdx%m&RpHa!ydJ9#d}IWLAp--$A(kI6+G_4AGc4b% zcpoe_hhdHR+vv+@yDsENU)SY)J1@`3>GGK#&I%E}{ndA4xX!L-%;0#tJa5_(;q5$N z9q0r*LwW7*b*05hCg<4}=xuqwYbA&8{v&ToLF#6xC2Y)!k2Bvnt5)^#uFp=<SAC;0 zvR(?zKhpfrQEl!KQIH}`Ji|sIe2f0_*{lmsN;g<8)a6lLsQIY|NnOKA#=NR;u`i!R zWX$V%eXaGIuX*aim#sh3r`IfHK~e`}KDf_d7X2;vMOtV^8LvZRhV-tJ9KP9i?7N?Q zvjVC4!&Pu`<9i?TcBkU(m~!uo_>y3OS$>WELLf;DJi{j8|DAmgjn~fIR?E(o5Sh_m zGFfz1*|giQK}rpH9lk3*`7Om*kO$&2FfcUOv;N52|8L2xLTQ2Ed6)AYjTTK2FQ4|{ axY8^3BF&n=rM@w|Af=wJelF{r5}E*z<T0@T diff --git a/assets/icons/size_2x2.png b/assets/icons/size_2x2.png new file mode 100644 index 0000000000000000000000000000000000000000..90a48baea5a702764515bfa638fcc2e154fbc0e1 GIT binary patch literal 5580 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 G&;$Vf;ps*I literal 0 HcmV?d00001 diff --git a/assets/icons/size_3.png b/assets/icons/size_3.png deleted file mode 100644 index 0d20bb1e00f264f617ca1154d3488e946d13f936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1659 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 diff --git a/assets/icons/size_3x2.png b/assets/icons/size_3x2.png new file mode 100644 index 0000000000000000000000000000000000000000..6c427f089bc80e4e0433d583289330e8f6b700b4 GIT binary patch literal 7616 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 KxvX<aXaWEYfzRgv literal 0 HcmV?d00001 diff --git a/assets/icons/size_3x3.png b/assets/icons/size_3x3.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4c28cb3295725e9a98d9d449ebaf7dbcbb60ae GIT binary patch literal 7436 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 literal 0 HcmV?d00001 diff --git a/generator/batch.sh b/generator/batch.sh index db34718..3a08b05 100755 --- a/generator/batch.sh +++ b/generator/batch.sh @@ -2,16 +2,16 @@ CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -ALLOWED_SIZE_VALUES="2 3" +ALLOWED_BLOCK_SIZE_VALUES="2x2 3x2 3x3" ALLOWED_DIFFICULTY_VALUES="easy medium hard" GRIDS_COUNT=10 -for SIZE in ${ALLOWED_SIZE_VALUES}; do - echo "Size: ${SIZE}x${SIZE}" +for BLOCK_SIZE in ${ALLOWED_BLOCK_SIZE_VALUES}; do + echo "Block size: ${BLOCK_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 + python ${CURRENT_DIR}/generate.py ${BLOCK_SIZE} ${DIFFICULTY} | tail -n 1 done done done diff --git a/generator/generate.py b/generator/generate.py index 6277232..4551ab8 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -1,20 +1,28 @@ +#!/usr/bin/python +# -*- coding: iso-8859-15 -*- + import sys +import math from random import randint, shuffle if (len(sys.argv) != 3): - print('Usage: generate.py size difficulty') - print('size: [2|3]') + print('Usage: generate.py block-size difficulty') + print('block-size: [2x2|3x2|3x3]') print('difficulty: [easy|medium|hard]') exit() -size, difficulty = sys.argv[1], sys.argv[2] - -size = int(size) +blocksize, difficulty = sys.argv[1], sys.argv[2] -if not size in [2, 3]: +if not blocksize in ['2x2', '3x2', '3x3']: print('wrong size given') exit() +splitted_blocksize = blocksize.split('x') +size_horizontal = int(splitted_blocksize[0]) +size_vertical = int(splitted_blocksize[1]) + +boardSize = size_horizontal * size_vertical + if not difficulty in ['easy', 'medium', 'hard']: print('wrong difficulty given') exit() @@ -30,13 +38,22 @@ if difficulty == 'hard': # draw grid (array style) def drawGrid(grid): - for row in range(len(grid)): - for col in range(len(grid[row])): + gridVerticalSize = len(grid) + gridHorizontalSize = len(grid[0]) + horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1 + + for row in range(gridHorizontalSize): + if ((row % size_vertical) == 0): + sys.stdout.write(('═' * horizontalLineLength) + '\n') + for col in range(gridVerticalSize): + if ((col % size_horizontal) == 0): + sys.stdout.write('║') if grid[row][col] != 0: sys.stdout.write(str(grid[row][col])) else: sys.stdout.write(' ') - sys.stdout.write('\n') + sys.stdout.write('║\n') + sys.stdout.write(('═' * horizontalLineLength) + '\n') sys.stdout.write('\n') # draw grid (inline style) @@ -47,12 +64,12 @@ def drawGridInline(grid): sys.stdout.write('\n') #initialise empty grid -def generateEmptyGrid(size): +def generateEmptyGrid(boardSize): emptyGrid = [] - for i in range(size * size): + for row in range(boardSize): emptyGrid.append([]) - for j in range(size * size): - emptyGrid[i].append(0) + for col in range(boardSize): + emptyGrid[row].append(0) return emptyGrid #A check if the grid is full @@ -97,9 +114,9 @@ def solveGrid(grid): 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)] + blockColFrom = size_horizontal * int(col / size_horizontal) + blockRowFrom = size_vertical * int(row / size_vertical) + square = [grid[i][blockColFrom:blockColFrom + size_horizontal] for i in range(blockRowFrom, blockRowFrom + size_vertical)] #Check that this value has not already be used on this sub square if not any(value in squareLine for squareLine in square): @@ -115,16 +132,16 @@ def solveGrid(grid): #A backtracking/recursive function to check all possible combinations of numbers until a solution is found def fillGrid(grid): - gridSize = len(grid) + boardSize = len(grid) cellsCount = len(grid) * len(grid[0]) - numberList = [(value + 1) for value in range(gridSize)] + numberList = [(value + 1) for value in range(boardSize)] global solutionsCount #Find next empty cell for i in range(0, cellsCount): - row = i // gridSize - col = i % gridSize + row = i // boardSize + col = i % boardSize if grid[row][col] == 0: shuffle(numberList) for value in numberList: @@ -132,15 +149,15 @@ def fillGrid(grid): 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): + for r in range(0, boardSize): 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)] + blockColFrom = size_horizontal * int(col / size_horizontal) + blockRowFrom = size_vertical * int(row / size_vertical) + square = [grid[i][blockColFrom:blockColFrom + size_horizontal] for i in range(blockRowFrom, blockRowFrom + size_vertical)] #Check that this value has not already be used on this sub square if not any(value in squareLine for squareLine in square): @@ -155,7 +172,6 @@ def fillGrid(grid): 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 @@ -165,11 +181,11 @@ def computeResolvableGrid(grid, wantedAttempts): attempts = wantedAttempts while attempts > 0: # Select a random cell that is not already empty - row = randint(0, gridSize - 1) - col = randint(0, gridSize - 1) + row = randint(0, boardSize - 1) + col = randint(0, boardSize - 1) while grid[row][col] == 0: - row = randint(0, gridSize - 1) - col = randint(0, gridSize - 1) + row = randint(0, boardSize - 1) + col = randint(0, boardSize - 1) # Remove value in this random cell savedCellValue = grid[row][col] @@ -183,7 +199,7 @@ def computeResolvableGrid(grid, wantedAttempts): grid[row][col] = savedCellValue attempts -= 1 -grid = generateEmptyGrid(size) +grid = generateEmptyGrid(boardSize) sys.stdout.write('Solved grid:\n') fillGrid(grid) diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh index 41dc2c4..819a80e 100755 --- a/icons/build_game_icons.sh +++ b/icons/build_game_icons.sh @@ -60,8 +60,9 @@ build_icon ${CURRENT_DIR}/button_start.svg ${BASE_DIR}/assets/icons/button_start build_icon ${CURRENT_DIR}/difficulty_easy.svg ${BASE_DIR}/assets/icons/difficulty_easy.png build_icon ${CURRENT_DIR}/difficulty_medium.svg ${BASE_DIR}/assets/icons/difficulty_medium.png build_icon ${CURRENT_DIR}/difficulty_hard.svg ${BASE_DIR}/assets/icons/difficulty_hard.png -build_icon ${CURRENT_DIR}/size_2.svg ${BASE_DIR}/assets/icons/size_2.png -build_icon ${CURRENT_DIR}/size_3.svg ${BASE_DIR}/assets/icons/size_3.png +build_icon ${CURRENT_DIR}/size_2x2.svg ${BASE_DIR}/assets/icons/size_2x2.png +build_icon ${CURRENT_DIR}/size_3x2.svg ${BASE_DIR}/assets/icons/size_3x2.png +build_icon ${CURRENT_DIR}/size_3x3.svg ${BASE_DIR}/assets/icons/size_3x3.png build_icon ${CURRENT_DIR}/skins/empty.svg ${BASE_DIR}/assets/skins/empty.png # Skins diff --git a/icons/size_2.svg b/icons/size_2.svg deleted file mode 100644 index a5a6172..0000000 --- a/icons/size_2.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="#6afffe"/><path d="m8.016 8.1944v83.701h83.701v-83.701zm60.683 60.683h-14.648v-14.648h14.648zm0 4.185v14.648h-14.648v-14.648zm4.185 0h14.648v14.648h-14.648zm0-4.185v-14.648h14.648v14.648zm0-23.018v-14.648h14.648v14.648zm-4.185 0h-14.648v-14.648h14.648zm-23.018 0h-14.648v-14.648h14.648zm0 8.3701v14.648h-14.648v-14.648zm0 18.833v14.648h-14.648v-14.648zm41.85-46.035h-14.648v-14.648h14.648zm-18.833-14.648v14.648h-14.648v-14.648zm-23.018 14.648h-14.648v-14.648h14.648zm-33.48-14.648h14.648v14.648h-14.648zm0 18.833h14.648v14.648h-14.648zm0 23.018h14.648v14.648h-14.648zm0 18.833h14.648v14.648h-14.648z" stroke-width="2.0925"/></svg> diff --git a/icons/size_2x2.svg b/icons/size_2x2.svg new file mode 100644 index 0000000..bb4ec58 --- /dev/null +++ b/icons/size_2x2.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="0" fill="none" stroke="#000"/><path d="m-0.16372-0.23334v100.4h100.4v-100.4zm72.788 72.788h-17.569v-17.569h17.569zm0 5.0198v17.569h-17.569v-17.569zm5.0198 0h17.569v17.569h-17.569zm0-5.0198v-17.569h17.569v17.569zm0-27.61v-17.569h17.569v17.569zm-5.0198 0h-17.569v-17.569h17.569zm-27.61 0h-17.569v-17.569h17.569zm0 10.04v17.569h-17.569v-17.569zm0 22.59v17.569h-17.569v-17.569zm50.198-55.217h-17.569v-17.569h17.569zm-22.59-17.569v17.569h-17.569v-17.569zm-27.61 17.569h-17.569v-17.569h17.569zm-40.158-17.569h17.569v17.569h-17.569zm0 22.59h17.569v17.569h-17.569zm0 27.61h17.569v17.569h-17.569zm0 22.59h17.569v17.569h-17.569z" stroke-width="2.5098"/><g transform="matrix(1.2347 0 0 1.2347 -11.926 -14.48)" fill="#00c700" stroke="#003e00" stroke-width="1.6198" aria-label="2x2"><path d="m35.254 91.326h-23.867q0.41016-3.5352 2.4805-6.6406 2.0898-3.125 7.8125-7.3633 3.4961-2.5977 4.4727-3.9453 0.97656-1.3477 0.97656-2.5586 0-1.3086-0.97656-2.2266-0.95703-0.9375-2.4219-0.9375-1.5234 0-2.5 0.95703-0.95703 0.95703-1.2891 3.3789l-7.9688-0.64453q0.46875-3.3594 1.7188-5.2344 1.25-1.8945 3.5156-2.8906 2.2852-1.0156 6.3086-1.0156 4.1992 0 6.5234 0.95703 2.3438 0.95703 3.6719 2.9492 1.3477 1.9727 1.3477 4.4336 0 2.6172-1.543 5-1.5234 2.3828-5.5664 5.2344-2.4023 1.6602-3.2227 2.3242-0.80078 0.66406-1.8945 1.7383h12.422z"/><path d="m37.617 70.584h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m88.613 91.326h-23.867q0.41016-3.5352 2.4805-6.6406 2.0898-3.125 7.8125-7.3633 3.4961-2.5977 4.4727-3.9453 0.97656-1.3477 0.97656-2.5586 0-1.3086-0.97656-2.2266-0.95703-0.9375-2.4219-0.9375-1.5234 0-2.5 0.95703-0.95703 0.95703-1.2891 3.3789l-7.9688-0.64453q0.46875-3.3594 1.7188-5.2344 1.25-1.8945 3.5156-2.8906 2.2852-1.0156 6.3086-1.0156 4.1992 0 6.5234 0.95703 2.3438 0.95703 3.6719 2.9492 1.3477 1.9727 1.3477 4.4336 0 2.6172-1.543 5-1.5234 2.3828-5.5664 5.2344-2.4023 1.6602-3.2227 2.3242-0.80078 0.66406-1.8945 1.7383h12.422z"/></g></svg> diff --git a/icons/size_3.svg b/icons/size_3.svg deleted file mode 100644 index 1b7e516..0000000 --- a/icons/size_3.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="#6afffe"/><path d="m65.824 4.0024h-62v90h90v-90zm9 79v-8h8v8zm8 2v7h-8v-7zm-38-2v-8h8v8zm8 2v7h-8v-7zm-38-2v-8h8v8zm8 2v7h-8v-7zm0-70v8h-8v-8zm-8-2v-7h8v7zm38 2v8h-8v-8zm-8-2v-7h8v7zm0 30v-7h8v7zm8 2v8h-8v-8zm-10-2h-7v-7h7zm0 2v8h-7v-8zm0 10v7h-7v-7zm2 0h8v7h-8zm10 0h7v7h-7zm0-2v-8h7v8zm0-10v-7h7v7zm0-11v-7h7v7zm-2 0h-8v-7h8zm-10 0h-7v-7h7zm-11 0h-7v-7h7zm0 4v7h-7v-7zm0 9v8h-7v-8zm0 10v7h-7v-7zm0 11v7h-7v-7zm4 0h7v7h-7zm9 0h8v7h-8zm10 0h7v7h-7zm11 0h7v7h-7zm0-4v-7h7v7zm0-9v-8h7v8zm0-10v-7h7v7zm0-11v-7h7v7zm0-9v-8h7v8zm-4 0h-7v-8h7zm-19 0h-7v-8h7zm-11 0h-7v-8h7zm-9 2v7h-8v-7zm0 11v7h-8v-7zm0 9v8h-8v-8zm0 10v7h-8v-7zm0 11v7h-8v-7zm2 9h7v8h-7zm11 0h7v8h-7zm19 0h7v8h-7zm11 0h7v8h-7zm9-2v-7h8v7zm0-11v-7h8v7zm0-9v-8h8v8zm0-10v-7h8v7zm0-11v-7h8v7zm0-9v-8h8v8zm0-10v-7h8v7zm-2-7v7h-7v-7zm-11 7h-7v-7h7zm-19-7v7h-7v-7zm-11 7h-7v-7h7zm-26-7h7v7h-7zm0 9h7v8h-7zm0 10h7v7h-7zm0 11h7v7h-7zm0 9h7v8h-7zm0 10h7v7h-7zm0 11h7v7h-7zm0 9h7v8h-7zm0 10h7v7h-7zm19 7v-7h7v7zm11-7h7v7h-7zm19 7v-7h7v7zm11-7h7v7h-7zm26 7h-7v-7h7zm0-9h-7v-8h7zm0-10h-7v-7h7zm0-11h-7v-7h7zm0-9h-7v-8h7zm0-10h-7v-7h7zm0-11h-7v-7h7zm0-9h-7v-8h7zm-7-10v-7h7v7z"/></svg> diff --git a/icons/size_3x2.svg b/icons/size_3x2.svg new file mode 100644 index 0000000..355de34 --- /dev/null +++ b/icons/size_3x2.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="0" fill="none" stroke="#000"/><path d="m0.14969-0.42875v100.8h99.863v-100.8zm69.716 82.666v-10.835h11.314v10.835zm11.314 4.0477v10.835h-11.314v-10.835zm-62.197-4.0477v-10.835h11.314v10.835zm11.314 4.0477v10.835h-11.314v-10.835zm39.569-72.627v-10.835h11.314v10.835zm11.314 4.0477v10.835h-11.314v-10.835zm-15.342-4.0477h-11.314v-10.835h11.314zm0 4.0477v10.835h-11.314v-10.835zm0 19.407v10.835h-11.314v-10.835zm4.0278 0h11.314v10.835h-11.314zm15.342 0h11.314v10.835h-11.314zm0-8.5725v-10.835h11.314v10.835zm0-14.882v-10.835h11.314v10.835zm-39.569-10.835v10.835h-11.314v-10.835zm0 14.882v10.835h-11.314v-10.835zm0 19.407v10.835h-11.314v-10.835zm0 14.882v10.835h-11.314v-10.835zm8.8853 0h11.314v10.835h-11.314zm15.342 0h11.314v10.835h-11.314zm15.343 0h11.314v10.835h-11.314zm-54.91-49.172v10.835h-11.314v-10.835zm0 14.882v10.835h-11.314v-10.835zm0 19.407v10.835h-11.314v-10.835zm0 14.882v10.835h-11.314v-10.835zm4.0278 19.407h11.314v10.835h-11.314zm20.2 0h11.314v10.835h-11.314zm30.684 0h11.314v10.835h-11.314zm-81.567-68.58h11.314v10.835h-11.314zm0 14.882h11.314v10.835h-11.314zm0 19.407h11.314v10.835h-11.314zm0 14.882h11.314v10.835h-11.314zm0 19.407h11.314v10.835h-11.314zm0 14.882h11.314v10.835h-11.314zm30.684 10.835v-10.835h11.314v10.835zm20.2-10.835h11.314v10.835h-11.314zm30.684 10.835v-10.835h11.314v10.835z" stroke-width="1.6844"/><g transform="matrix(1.2405 0 0 1.2405 -12.136 -19.028)" fill="#ff8d05" stroke="#4c2a00" stroke-width="1.6123" aria-label="3x2"><path d="m19.512 73.271-7.5195-1.3477q0.9375-3.5938 3.5938-5.5078 2.6758-1.9141 7.5586-1.9141 5.6055 0 8.1055 2.0898t2.5 5.2539q0 1.8555-1.0156 3.3594t-3.0664 2.6367q1.6602 0.41016 2.5391 0.95703 1.4258 0.87891 2.207 2.3242 0.80078 1.4258 0.80078 3.418 0 2.5-1.3086 4.8047-1.3086 2.2852-3.7695 3.5352-2.4609 1.2305-6.4648 1.2305-3.9062 0-6.1719-0.91797-2.2461-0.91797-3.7109-2.6758-1.4453-1.7773-2.2266-4.4531l7.9492-1.0547q0.46875 2.4023 1.4453 3.3398 0.99609 0.91797 2.5195 0.91797 1.6016 0 2.6562-1.1719 1.0742-1.1719 1.0742-3.125 0-1.9922-1.0352-3.0859-1.0156-1.0938-2.7734-1.0938-0.9375 0-2.5781 0.46875l0.41016-5.6836q0.66406 0.09766 1.0352 0.09766 1.5625 0 2.5977-0.99609 1.0547-0.99609 1.0547-2.3633 0-1.3086-0.78125-2.0898t-2.1484-0.78125q-1.4062 0-2.2852 0.85938-0.87891 0.83984-1.1914 2.9688z"/><path d="m37.441 72.88h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m88.438 93.622h-23.867q0.41016-3.5352 2.4805-6.6406 2.0898-3.125 7.8125-7.3633 3.4961-2.5977 4.4727-3.9453 0.97656-1.3477 0.97656-2.5586 0-1.3086-0.97656-2.2266-0.95703-0.9375-2.4219-0.9375-1.5234 0-2.5 0.95703-0.95703 0.95703-1.2891 3.3789l-7.9688-0.64453q0.46875-3.3594 1.7188-5.2344 1.25-1.8945 3.5156-2.8906 2.2852-1.0156 6.3086-1.0156 4.1992 0 6.5234 0.95703 2.3438 0.95703 3.6719 2.9492 1.3477 1.9727 1.3477 4.4336 0 2.6172-1.543 5-1.5234 2.3828-5.5664 5.2344-2.4023 1.6602-3.2227 2.3242-0.80078 0.66406-1.8945 1.7383h12.422z"/></g></svg> diff --git a/icons/size_3x3.svg b/icons/size_3x3.svg new file mode 100644 index 0000000..3037d35 --- /dev/null +++ b/icons/size_3x3.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="0" fill="none" stroke="#000"/><path d="m68.799-0.020495h-69.133v100.35h100.35v-100.35zm10.035 88.089v-8.9204h8.9204v8.9204zm8.9204 2.2301v7.8053h-8.9204v-7.8053zm-42.372-2.2301v-8.9204h8.9204v8.9204zm8.9204 2.2301v7.8053h-8.9204v-7.8053zm-42.372-2.2301v-8.9204h8.9204v8.9204zm8.9204 2.2301v7.8053h-8.9204v-7.8053zm0-78.053v8.9204h-8.9204v-8.9204zm-8.9204-2.2301v-7.8053h8.9204v7.8053zm42.372 2.2301v8.9204h-8.9204v-8.9204zm-8.9204-2.2301v-7.8053h8.9204v7.8053zm0 33.452v-7.8053h8.9204v7.8053zm8.9204 2.2301v8.9204h-8.9204v-8.9204zm-11.151-2.2301h-7.8053v-7.8053h7.8053zm0 2.2301v8.9204h-7.8053v-8.9204zm0 11.151v7.8053h-7.8053v-7.8053zm2.2301 0h8.9204v7.8053h-8.9204zm11.151 0h7.8053v7.8053h-7.8053zm0-2.2301v-8.9204h7.8053v8.9204zm0-11.151v-7.8053h7.8053v7.8053zm0-12.266v-7.8053h7.8053v7.8053zm-2.2301 0h-8.9204v-7.8053h8.9204zm-11.151 0h-7.8053v-7.8053h7.8053zm-12.266 0h-7.8053v-7.8053h7.8053zm0 4.4603v7.8053h-7.8053v-7.8053zm0 10.035v8.9204h-7.8053v-8.9204zm0 11.151v7.8053h-7.8053v-7.8053zm0 12.266v7.8053h-7.8053v-7.8053zm4.4603 0h7.8053v7.8053h-7.8053zm10.035 0h8.9204v7.8053h-8.9204zm11.151 0h7.8053v7.8053h-7.8053zm12.266 0h7.8053v7.8053h-7.8053zm0-4.4603v-7.8053h7.8053v7.8053zm0-10.035v-8.9204h7.8053v8.9204zm0-11.151v-7.8053h7.8053v7.8053zm0-12.266v-7.8053h7.8053v7.8053zm0-10.035v-8.9204h7.8053v8.9204zm-4.4603 0h-7.8053v-8.9204h7.8053zm-21.186 0h-7.8053v-8.9204h7.8053zm-12.266 0h-7.8053v-8.9204h7.8053zm-10.035 2.2301v7.8053h-8.9204v-7.8053zm0 12.266v7.8053h-8.9204v-7.8053zm0 10.035v8.9204h-8.9204v-8.9204zm0 11.151v7.8053h-8.9204v-7.8053zm0 12.266v7.8053h-8.9204v-7.8053zm2.2301 10.035h7.8053v8.9204h-7.8053zm12.266 0h7.8053v8.9204h-7.8053zm21.186 0h7.8053v8.9204h-7.8053zm12.266 0h7.8053v8.9204h-7.8053zm10.035-2.2301v-7.8053h8.9204v7.8053zm0-12.266v-7.8053h8.9204v7.8053zm0-10.035v-8.9204h8.9204v8.9204zm0-11.151v-7.8053h8.9204v7.8053zm0-12.266v-7.8053h8.9204v7.8053zm0-10.035v-8.9204h8.9204v8.9204zm0-11.151v-7.8053h8.9204v7.8053zm-2.2301-7.8053v7.8053h-7.8053v-7.8053zm-12.266 7.8053h-7.8053v-7.8053h7.8053zm-21.186-7.8053v7.8053h-7.8053v-7.8053zm-12.266 7.8053h-7.8053v-7.8053h7.8053zm-28.991-7.8053h7.8053v7.8053h-7.8053zm0 10.035h7.8053v8.9204h-7.8053zm0 11.151h7.8053v7.8053h-7.8053zm0 12.266h7.8053v7.8053h-7.8053zm0 10.035h7.8053v8.9204h-7.8053zm0 11.151h7.8053v7.8053h-7.8053zm0 12.266h7.8053v7.8053h-7.8053zm0 10.035h7.8053v8.9204h-7.8053zm0 11.151h7.8053v7.8053h-7.8053zm21.186 7.8053v-7.8053h7.8053v7.8053zm12.266-7.8053h7.8053v7.8053h-7.8053zm21.186 7.8053v-7.8053h7.8053v7.8053zm12.266-7.8053h7.8053v7.8053h-7.8053zm28.991 7.8053h-7.8053v-7.8053h7.8053zm0-10.035h-7.8053v-8.9204h7.8053zm0-11.151h-7.8053v-7.8053h7.8053zm0-12.266h-7.8053v-7.8053h7.8053zm0-10.035h-7.8053v-8.9204h7.8053zm0-11.151h-7.8053v-7.8053h7.8053zm0-12.266h-7.8053v-7.8053h7.8053zm0-10.035h-7.8053v-8.9204h7.8053zm-7.8053-11.151v-7.8053h7.8053v7.8053z" stroke-width="1.1151"/><g transform="matrix(1.2388 0 0 1.2388 -11.249 -16.591)" fill="#f00" stroke="#500000" stroke-width="1.6145" aria-label="3x3"><path d="m19.443 72.913-7.5195-1.3477q0.9375-3.5938 3.5938-5.5078 2.6758-1.9141 7.5586-1.9141 5.6055 0 8.1055 2.0898t2.5 5.2539q0 1.8555-1.0156 3.3594t-3.0664 2.6367q1.6602 0.41016 2.5391 0.95703 1.4258 0.87891 2.207 2.3242 0.80078 1.4258 0.80078 3.418 0 2.5-1.3086 4.8047-1.3086 2.2852-3.7695 3.5352-2.4609 1.2305-6.4648 1.2305-3.9062 0-6.1719-0.91797-2.2461-0.91797-3.7109-2.6758-1.4453-1.7773-2.2266-4.4531l7.9492-1.0547q0.46875 2.4023 1.4453 3.3398 0.99609 0.91797 2.5195 0.91797 1.6016 0 2.6562-1.1719 1.0742-1.1719 1.0742-3.125 0-1.9922-1.0352-3.0859-1.0156-1.0938-2.7734-1.0938-0.9375 0-2.5781 0.46875l0.41016-5.6836q0.66406 0.09766 1.0352 0.09766 1.5625 0 2.5977-0.99609 1.0547-0.99609 1.0547-2.3633 0-1.3086-0.78125-2.0898t-2.1484-0.78125q-1.4062 0-2.2852 0.85938-0.87891 0.83984-1.1914 2.9688z"/><path d="m37.373 72.522h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m72.803 72.913-7.5195-1.3477q0.9375-3.5938 3.5938-5.5078 2.6758-1.9141 7.5586-1.9141 5.6055 0 8.1055 2.0898t2.5 5.2539q0 1.8555-1.0156 3.3594t-3.0664 2.6367q1.6602 0.41016 2.5391 0.95703 1.4258 0.87891 2.207 2.3242 0.80078 1.4258 0.80078 3.418 0 2.5-1.3086 4.8047-1.3086 2.2852-3.7695 3.5352-2.4609 1.2305-6.4648 1.2305-3.9062 0-6.1719-0.91797-2.2461-0.91797-3.7109-2.6758-1.4453-1.7773-2.2266-4.4531l7.9492-1.0547q0.46875 2.4023 1.4453 3.3398 0.99609 0.91797 2.5195 0.91797 1.6016 0 2.6562-1.1719 1.0742-1.1719 1.0742-3.125 0-1.9922-1.0352-3.0859-1.0156-1.0938-2.7734-1.0938-0.9375 0-2.5781 0.46875l0.41016-5.6836q0.66406 0.09766 1.0352 0.09766 1.5625 0 2.5977-0.99609 1.0547-0.99609 1.0547-2.3633 0-1.3086-0.78125-2.0898t-2.1484-0.78125q-1.4062 0-2.2852 0.85938-0.87891 0.83984-1.1914 2.9688z"/></g></svg> diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart index e4d2cf5..4632c96 100644 --- a/lib/entities/cell.dart +++ b/lib/entities/cell.dart @@ -23,8 +23,6 @@ class Cell { imageAsset = 'assets/skins/' + myProvider.skin + '_' + this.value.toString() + '.png'; } - int size = myProvider.size; - return Container( decoration: BoxDecoration( color: this.getBackgroundColor(myProvider), @@ -101,7 +99,8 @@ class Cell { } static Border getCellBorders(Data myProvider, int row, int col) { - int size = myProvider.size; + int blockSizeHorizontal = myProvider.blockSizeHorizontal; + int blockSizeVertical = myProvider.blockSizeVertical; Border borders = Border.all( color: cellBorderSelectedColor, @@ -110,10 +109,10 @@ class Cell { if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) { borders = Border( - top: BorderSide(width: cellBorderWidth, color: ((row % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), - left: BorderSide(width: cellBorderWidth, color: ((col % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), - right: BorderSide(width: cellBorderWidth, color: (((col + 1) % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), - bottom: BorderSide(width: cellBorderWidth, color: (((row + 1) % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), + top: BorderSide(width: cellBorderWidth, color: ((row % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor), + left: BorderSide(width: cellBorderWidth, color: ((col % blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor), + right: BorderSide(width: cellBorderWidth, color: (((col + 1) % blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor), + bottom: BorderSide(width: cellBorderWidth, color: (((row + 1) % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor), ); } diff --git a/lib/layout/board.dart b/lib/layout/board.dart index cd4905e..fcc8806 100644 --- a/lib/layout/board.dart +++ b/lib/layout/board.dart @@ -28,15 +28,15 @@ class Board { } static Table buildGameTileset(Data myProvider) { - int size = myProvider.size; + int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; List cells = myProvider.cells; return Table( defaultColumnWidth: IntrinsicColumnWidth(), children: [ - for (var row = 0; row < pow(size, 2); row++) + for (var row = 0; row < boardSize; row++) TableRow(children: [ - for (var col = 0; col < pow(size, 2); col++) + for (var col = 0; col < boardSize; col++) Column(children: [ cells[row][col].widget( myProvider, diff --git a/lib/layout/game.dart b/lib/layout/game.dart index bda08eb..6b09b41 100644 --- a/lib/layout/game.dart +++ b/lib/layout/game.dart @@ -26,12 +26,11 @@ class Game { static Container buildSelectCellValueBar(Data myProvider) { List cells = myProvider.cells; - int size = myProvider.size; Color borderColor = Colors.blue; bool isCellSelected = (myProvider.currentCellCol != null && myProvider.currentCellRow != null); - int maxValue = pow(size, 2) + 1; + int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; return Container( margin: EdgeInsets.all(2), @@ -42,7 +41,7 @@ class Game { children: [ TableRow( children: [ - for (var value = 0; value < maxValue; value++) + for (var value = 0; value <= maxValue; value++) Column( children: [ Cell( diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart index b4bb302..9ac761c 100644 --- a/lib/layout/parameters.dart +++ b/lib/layout/parameters.dart @@ -70,11 +70,11 @@ class Parameters { } - static FlatButton _buildParameterButton(Data myProvider, String parameterCode, var parameterValue) { + static FlatButton _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) { String currentValue = myProvider.getParameterValue(parameterCode).toString(); - bool isActive = (parameterValue.toString() == currentValue); - String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue.toString() + '.png'; + bool isActive = (parameterValue == currentValue); + String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue + '.png'; return FlatButton( child: Container( diff --git a/lib/provider/data.dart b/lib/provider/data.dart index ea939bf..0225b29 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -4,7 +4,7 @@ class Data extends ChangeNotifier { // Configuration available values List _availableDifficultyLevels = ['easy', 'medium', 'hard']; - List _availableSizes = [2, 3]; + List _availableSizes = ['2x2', '3x2', '3x3']; List _availableSkins = ['default', 'food', 'nature']; List get availableDifficultyLevels => _availableDifficultyLevels; @@ -13,12 +13,14 @@ class Data extends ChangeNotifier { // Application default configuration String _level = 'medium'; - int _size = 3; + String _size = '3x3'; String _skin = 'default'; bool _showConflicts = false; // Game data bool _stateRunning = false; + int _blockSizeVertical = null; + int _blockSizeHorizontal = null; List _cells = []; int _currentCellCol = null; int _currentCellRow = null; @@ -29,9 +31,13 @@ class Data extends ChangeNotifier { notifyListeners(); } - int get size => _size; - set updateSize(int size) { + String get size => _size; + int get blockSizeVertical => _blockSizeVertical; + int get blockSizeHorizontal => _blockSizeHorizontal; + set updateSize(String size) { _size = size; + _blockSizeHorizontal = int.parse(_size.split('x')[0]); + _blockSizeVertical = int.parse(_size.split('x')[1]); notifyListeners(); } @@ -69,7 +75,7 @@ class Data extends ChangeNotifier { } } - setParameterValue(String parameterCode, var parameterValue) { + setParameterValue(String parameterCode, String parameterValue) { switch(parameterCode) { case 'difficulty': { updateLevel = parameterValue; } break; diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart index 539bf4d..2c93084 100644 --- a/lib/utils/board_utils.dart +++ b/lib/utils/board_utils.dart @@ -22,30 +22,33 @@ class BoardUtils { static Future<void> pickGrid(Data myProvider) async { - int size = myProvider.size; - String grid; RandomPickGrid randomPickGrid; randomPickGrid = RandomPickGrid(); - await randomPickGrid.init(myProvider.level, size); + await randomPickGrid.init(myProvider.level, myProvider.size); if (randomPickGrid.grid != null) { grid = randomPickGrid.grid; } - if (grid.length == pow(size, 4)) { - myProvider.updateCells = BoardUtils.createBoardFromTemplate(grid); + int blockSizeHorizontal = myProvider.blockSizeHorizontal; + int blockSizeVertical = myProvider.blockSizeVertical; + + if (grid.length == pow(blockSizeHorizontal * blockSizeVertical, 2)) { + print('Picked grid from template: ' + grid); + bool isSymetric = (blockSizeHorizontal == blockSizeVertical); + myProvider.updateCells = BoardUtils.createBoardFromTemplate(grid, isSymetric); } } - static List createEmptyBoard(int size) { + static List createEmptyBoard(int boardSize) { int index = 0; List cells = []; - for (var rowIndex = 0; rowIndex < pow(size, 2); rowIndex++) { + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { List row = []; - for (var colIndex = 0; colIndex < pow(size, 2); colIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { row.add(Cell(0, false)); } cells.add(row); @@ -55,44 +58,51 @@ class BoardUtils { } - static List createBoardFromTemplate(String grid) { + static List createBoardFromTemplate(String grid, bool isSymetric) { List cells = []; - int size = int.parse(pow(grid.length, 1/4).toStringAsFixed(0)); - int sideLength = pow(size, 2); + int boardSize = int.parse(pow(grid.length, 1/2).toStringAsFixed(0)); int index = 0; - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { List row = []; - for (var colIndex = 0; colIndex < sideLength; colIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { int value = int.parse(grid[index++]); row.add(Cell(value, (value != 0))); } cells.add(row); } - List<String> allowedFlip = ['', 'horizontal', 'vertical']; - List<String> allowedRotate = ['', 'left', 'right']; + List<String> allowedFlip = ['none', 'horizontal', 'vertical']; + List<String> allowedRotate = ['none', 'left', 'right']; + + // Forbid rotation if blocks are not symetric + if (!isSymetric) { + allowedRotate = ['none']; + } var rand = new Random(); String flip = allowedFlip[rand.nextInt(allowedFlip.length)]; String rotate = allowedRotate[rand.nextInt(allowedRotate.length)]; + print('flip board: ' + flip); + print('rotate board: ' + rotate); + switch(flip) { case 'horizontal': { - List transformedBoard = createEmptyBoard(size); - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { - for (var colIndex = 0; colIndex < sideLength; colIndex++) { - transformedBoard[rowIndex][colIndex].value = cells[sideLength - rowIndex - 1][colIndex].value; + List transformedBoard = createEmptyBoard(boardSize); + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + transformedBoard[rowIndex][colIndex].value = cells[boardSize - rowIndex - 1][colIndex].value; } } cells = transformedBoard; } break; case 'vertical': { - List transformedBoard = createEmptyBoard(size); - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { - for (var colIndex = 0; colIndex < sideLength; colIndex++) { - transformedBoard[rowIndex][colIndex].value = cells[rowIndex][sideLength - colIndex - 1].value; + List transformedBoard = createEmptyBoard(boardSize); + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + transformedBoard[rowIndex][colIndex].value = cells[rowIndex][boardSize - colIndex - 1].value; } } cells = transformedBoard; @@ -102,20 +112,20 @@ class BoardUtils { switch(rotate) { case 'left': { - List transformedBoard = createEmptyBoard(size); - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { - for (var colIndex = 0; colIndex < sideLength; colIndex++) { - transformedBoard[rowIndex][colIndex].value = cells[colIndex][sideLength - rowIndex - 1].value; + List transformedBoard = createEmptyBoard(boardSize); + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + transformedBoard[rowIndex][colIndex].value = cells[colIndex][boardSize - rowIndex - 1].value; } } cells = transformedBoard; } break; case 'right': { - List transformedBoard = createEmptyBoard(size); - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { - for (var colIndex = 0; colIndex < sideLength; colIndex++) { - transformedBoard[rowIndex][colIndex].value = cells[sideLength - colIndex - 1][rowIndex].value; + List transformedBoard = createEmptyBoard(boardSize); + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + transformedBoard[rowIndex][colIndex].value = cells[boardSize - colIndex - 1][rowIndex].value; } } cells = transformedBoard; @@ -124,8 +134,8 @@ class BoardUtils { } // Fix cells fixed states - for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) { - for (var colIndex = 0; colIndex < sideLength; colIndex++) { + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + for (var colIndex = 0; colIndex < boardSize; colIndex++) { cells[rowIndex][colIndex].isFixed = (cells[rowIndex][colIndex].value != 0) ? true : false; } } @@ -138,21 +148,24 @@ class BoardUtils { static bool checkBoardIsSolved(Data myProvider) { List cells = myProvider.cells; - int size = myProvider.size; - int sideLength = pow(size, 2); + + int blockSizeHorizontal = myProvider.blockSizeHorizontal; + int blockSizeVertical = myProvider.blockSizeVertical; + + int boardSize = blockSizeHorizontal * blockSizeVertical; bool isSolved = true; // reset conflict states - for (var row = 0; row < sideLength; row++) { - for (var col = 0; col < sideLength; col++) { + for (var row = 0; row < boardSize; row++) { + for (var col = 0; col < boardSize; col++) { cells[row][col].conflictsCount = 0; } } // check grid is fully completed - for (var row = 0; row < sideLength; row++) { - for (var col = 0; col < sideLength; col++) { + for (var row = 0; row < boardSize; row++) { + for (var col = 0; col < boardSize; col++) { if (cells[row][col].value == 0) { isSolved = false; } @@ -160,9 +173,9 @@ class BoardUtils { } // check lines does not contains a value twice - for (var row = 0; row < sideLength; row++) { + for (var row = 0; row < boardSize; row++) { List values = []; - for (var col = 0; col < sideLength; col++) { + for (var col = 0; col < boardSize; col++) { int value = cells[row][col].value; if (value != 0) { values.add(value); @@ -172,7 +185,7 @@ class BoardUtils { if (values.length != distinctValues.length) { print('line ' + row.toString() + ' contains duplicates'); // Add line to cells in conflict - for (var col = 0; col < sideLength; col++) { + for (var col = 0; col < boardSize; col++) { cells[row][col].conflictsCount++; } isSolved = false; @@ -180,9 +193,9 @@ class BoardUtils { } // check columns does not contains a value twice - for (var col = 0; col < sideLength; col++) { + for (var col = 0; col < boardSize; col++) { List values = []; - for (var row = 0; row < sideLength; row++) { + for (var row = 0; row < boardSize; row++) { int value = cells[row][col].value; if (value != 0) { values.add(value); @@ -192,7 +205,7 @@ class BoardUtils { if (values.length != distinctValues.length) { print('column ' + col.toString() + ' contains duplicates'); // Add column to cells in conflict - for (var row = 0; row < sideLength; row++) { + for (var row = 0; row < boardSize; row++) { cells[row][col].conflictsCount++; } isSolved = false; @@ -200,14 +213,16 @@ class BoardUtils { } // check blocks does not contains a value twice - for (var blockRow = 0; blockRow < size; blockRow++) { - for (var blockCol = 0; blockCol < size; blockCol++) { + int horizontalBlocksCount = blockSizeVertical; + int verticalBlocksCount = blockSizeHorizontal; + for (var blockRow = 0; blockRow < verticalBlocksCount; blockRow++) { + for (var blockCol = 0; blockCol < horizontalBlocksCount; blockCol++) { List values = []; - for (var rowInBlock = 0; rowInBlock < size; rowInBlock++) { - for (var colInBlock = 0; colInBlock < size; colInBlock++) { - int row = (blockRow * size) + rowInBlock; - int col = (blockCol * size) + colInBlock; + for (var rowInBlock = 0; rowInBlock < blockSizeVertical; rowInBlock++) { + for (var colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) { + int row = (blockRow * blockSizeVertical) + rowInBlock; + int col = (blockCol * blockSizeHorizontal) + colInBlock; int value = cells[row][col].value; if (value != 0) { values.add(value); @@ -219,10 +234,10 @@ class BoardUtils { if (values.length != distinctValues.length) { print('block [' + blockCol.toString() + ',' + blockRow.toString() + '] contains duplicates'); // Add blocks to cells in conflict - for (var rowInBlock = 0; rowInBlock < size; rowInBlock++) { - for (var colInBlock = 0; colInBlock < size; colInBlock++) { - int row = (blockRow * size) + rowInBlock; - int col = (blockCol * size) + colInBlock; + for (var rowInBlock = 0; rowInBlock < blockSizeVertical; rowInBlock++) { + for (var colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) { + int row = (blockRow * blockSizeVertical) + rowInBlock; + int col = (blockCol * blockSizeHorizontal) + colInBlock; cells[row][col].conflictsCount++; } } diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart index dd9d43a..4f47e67 100644 --- a/lib/utils/game_utils.dart +++ b/lib/utils/game_utils.dart @@ -8,8 +8,9 @@ class GameUtils { } static Future<void> startGame(Data myProvider) async { + print('Start new game: ' + myProvider.size); myProvider.updateStateRunning = true; - myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.size); + myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical); BoardUtils.pickGrid(myProvider); } } diff --git a/lib/utils/random_pick_grid.dart b/lib/utils/random_pick_grid.dart index 5348f27..58a0c45 100644 --- a/lib/utils/random_pick_grid.dart +++ b/lib/utils/random_pick_grid.dart @@ -6,27 +6,25 @@ class RandomPickGrid { String _grid; - init(String difficulty, int size) async { + init(String difficulty, String size) async { _grid = ''; await gridFromLocalFile(difficulty, size); } - Future<void> gridFromLocalFile(String difficulty, int size) async { - String sizeAsString = size.toString() + 'x' + size.toString(); - + Future<void> gridFromLocalFile(String difficulty, String size) async { // Get global grids list List grids; try { String jsonString = await rootBundle.loadString('assets/files/templates.json'); final jsonResponse = await json.decode(jsonString); - grids = jsonResponse['templates'][sizeAsString][difficulty]; + grids = jsonResponse['templates'][size][difficulty]; } catch (e) { print("$e"); } // Check we have enough grids if (grids.length < 1) { - print('Not enough grids [' + sizeAsString + ', ' + difficulty + '] in templates.'); + print('Not enough grids [' + size + ', ' + difficulty + '] in templates.'); } // Randomize grids list -- GitLab