Skip to content
Snippets Groups Projects
Commit 213728cf authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Merge branch...

Merge branch '44-ensure-all-templates-can-be-resolved-add-solver-script-fix-templates' into 'master'

Resolve "Ensure all templates can be resolved, add solver script, fix templates"

Closes #44

See merge request !37
parents 737c651a 26f63c02
No related branches found
No related tags found
1 merge request!37Resolve "Ensure all templates can be resolved, add solver script, fix templates"
Pipeline #1740 passed
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
app.versionName=0.0.33
app.versionCode=33
app.versionName=0.0.34
app.versionCode=34
This diff is collapsed.
Ensure each grid template can be resolved, update templates, add solve script
Vérification que chaque modèle peut être résolu, mise à jour des modèles, ajout d'un script (naïf) de résolution
......@@ -7,11 +7,18 @@ ALLOWED_DIFFICULTY_VALUES="easy medium hard nightmare"
GRIDS_COUNT=10
for BLOCK_SIZE in ${ALLOWED_BLOCK_SIZE_VALUES}; do
echo "Block size: ${BLOCK_SIZE}"
for DIFFICULTY in ${ALLOWED_DIFFICULTY_VALUES}; do
echo "Difficulty: ${DIFFICULTY}"
echo "Block size: ${BLOCK_SIZE} / Difficulty: ${DIFFICULTY}"
for i in $(seq ${GRIDS_COUNT}); do
python ${CURRENT_DIR}/generate.py ${BLOCK_SIZE} ${DIFFICULTY} | tail -n 1
# Generate grid candidate
GRID="$(python ${CURRENT_DIR}/generate.py ${BLOCK_SIZE} ${DIFFICULTY} | tail -n 1)"
# Ensure grid can be resolve without any assumption
CAN_BE_SOLVED="$(python ${CURRENT_DIR}/solve.py ${BLOCK_SIZE} ${GRID} | tail -n 1)"
if [ "${CAN_BE_SOLVED}" == "Ok" ]; then
echo "${BLOCK_SIZE} ${DIFFICULTY} ${GRID}"
else
echo "FAILED: ${BLOCK_SIZE} ${DIFFICULTY} ${GRID}"
fi
done
done
done
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
#
# Naive sudoku grid solver
#
# Will try to solve sudoku grid without making any assumption
# -> pick and fill first cell where only one value is allowed
# and loop until grid is filled
#
# Will fail if solution needs any assumption on a cell value
#
import sys
import math
if (len(sys.argv) != 3):
print('Usage: solve.py block-size grid')
print('block-size: [2x2|3x2|3x3|4x4]')
exit()
blocksize, gridTemplate = sys.argv[1], sys.argv[2]
if not blocksize in ['2x2', '3x2', '3x3', '4x4']:
print('wrong size given')
exit()
splitted_blocksize = blocksize.split('x')
size_horizontal = int(splitted_blocksize[0])
size_vertical = int(splitted_blocksize[1])
boardSize = size_horizontal * size_vertical
if (len(gridTemplate) != boardSize * boardSize):
print('wrong grid length (should be '+str(boardSize * boardSize)+')')
exit()
debugSolveGrid = False
############################################################################
sys.stdout.write('Will solve grid: ['+str(size_horizontal)+'x'+str(size_vertical)+'] // '+gridTemplate+'\n')
stringValues = '0123456789ABCDEFG'
# draw grid (array style)
def drawGrid(grid):
gridVerticalSize = len(grid)
gridHorizontalSize = len(grid[0])
horizontalLineLength = ((size_horizontal + 1) * size_vertical) + 1
for row in range(gridHorizontalSize):
if ((row % size_vertical) == 0):
sys.stdout.write(('' * horizontalLineLength) + '\n')
for col in range(gridVerticalSize):
if ((col % size_horizontal) == 0):
sys.stdout.write('')
if grid[row][col] != 0:
sys.stdout.write(stringValues[grid[row][col]])
else:
sys.stdout.write(' ')
sys.stdout.write('\n')
sys.stdout.write(('' * horizontalLineLength) + '\n')
sys.stdout.write('\n')
# (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
# Init grid from given template
def initGrid(boardSize, gridTemplate):
grid = []
index = 0
for row in range(boardSize):
grid.append([])
for col in range(boardSize):
grid[row].append(stringValues.index(gridTemplate[index]))
index += 1
return grid
# Check if grid is fully completed, without any empty cell
def isFullyCompleted(grid):
for row in range(len(grid)):
for col in range(len(grid[row])):
if grid[row][col] == 0:
return False
return True
# Check if a list contains duplicates (conflicts)
def containsDuplicates(list):
tmp_set = set(list)
return (len(list) != len(tmp_set))
# Check if given grid contains conflicts
def hasConflict(grid, size_horizontal, size_vertical):
# Check horizontal conflicts
for row in range(len(grid)):
values = []
for col in range(len(grid[row])):
value = grid[row][col]
if value != 0:
values.append(value)
if containsDuplicates(values):
# print('Horizontal conflict found')
return True
# Check vertical conflicts
for col in range(len(grid[0])):
values = []
for row in range(len(grid)):
value = grid[row][col]
if value != 0:
values.append(value)
if containsDuplicates(values):
# print('Vertical conflict found')
return True
# Check sub-blocks conflicts
for blockRow in range(size_horizontal):
for blockCol in range(size_vertical):
# Get sub-square
blockColFrom = size_horizontal * int(col / size_horizontal)
blockRowFrom = size_vertical * int(row / size_vertical)
values = []
for rowInBlock in range(size_vertical):
for colInBlock in range(size_horizontal):
row = (blockRow * size_vertical) + rowInBlock;
col = (blockCol * size_horizontal) + colInBlock;
value = grid[row][col]
if value != 0:
values.append(value)
if containsDuplicates(values):
# print('Sub-block conflict found')
return True
return False
# Check if a value is allowed in a cell (without conflicting)
def isValueAllowed(grid, size_horizontal, size_vertical, row, col, candidateValue):
testGrid = copyGrid(grid)
testGrid[row][col] = candidateValue
if not hasConflict(testGrid, size_horizontal, size_vertical):
return True
return False
# Get allowed values in a cell (witjout conflicting)
def findAllowedValuesForCell(grid, size_horizontal, size_vertical, row, col):
allowedValues = []
if not hasConflict(grid, size_horizontal, size_vertical):
for candidateValue in range(1, size_horizontal * size_vertical + 1):
if isValueAllowed(grid, size_horizontal, size_vertical, row, col, candidateValue):
allowedValues.append(candidateValue)
return allowedValues
# Globally solve grid
def solve(grid, size_horizontal, size_vertical):
iterations = 0
maxIterations = 500
boardSize = size_horizontal * size_vertical
# Loop until grid is fully completed
while True:
iterations += 1
if isFullyCompleted(grid) or (iterations > maxIterations):
break
if debugSolveGrid:
print('===================================')
print('Iteration: '+str(iterations))
# Get first/next cell with only one allowed value
candidates = []
if debugSolveGrid:
print('Searching for empty cells...')
for row in range(len(grid)):
for col in range(len(grid[row])):
if grid[row][col] == 0:
if debugSolveGrid:
print('Found empty cell ['+str(col)+','+str(row)+']')
candidates.append([row,col])
if len(candidates):
for candidate in candidates:
candidateRow = candidate[0]
candidateCol = candidate[1]
allowedValues = findAllowedValuesForCell(grid, size_horizontal, size_vertical, candidateRow, candidateCol)
if debugSolveGrid:
print('Allowed values for cell ['+str(candidateCol)+','+str(candidateRow)+']: '+str(allowedValues))
if len(allowedValues) != 1:
if debugSolveGrid:
print(' Non unique allowed value for cell. Skip to next cell')
else:
value = allowedValues[0]
grid[candidateRow][candidateCol] = value
if debugSolveGrid:
print(' Found unique allowed value for cell ['+str(candidateCol)+','+str(candidateRow)+']: '+str(value))
drawGrid(grid)
#########################
sys.stdout.write('Building start grid:\n')
grid = initGrid(boardSize, gridTemplate)
drawGrid(grid)
sys.stdout.write('Checking grid:\n')
if hasConflict(grid, size_horizontal, size_vertical):
sys.stdout.write(' - oups, initial conflict found.\n')
else:
sys.stdout.write(' - ok, no initial conflict found.\n')
sys.stdout.write('\n')
sys.stdout.write('Solving grid...\n')
solve(grid, size_horizontal, size_vertical)
if isFullyCompleted(grid):
sys.stdout.write('Ok, solved grid:\n')
drawGrid(grid)
sys.stdout.write('Ok\n')
else:
sys.stdout.write('Failed to solve grid\n')
#!/usr/bin/env bash
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
# Fetch grid from game templates
GRIDS="$(cat "${CURRENT_DIR}/../assets/files/templates.json" | grep "0" | sed 's/"//g' | sed 's/,//g')"
function get_block_size() {
BLOCK_SIZE=""
GRID_LENGTH="$(echo -n "${GRID}" | wc -c)"
if [ "${GRID_LENGTH}" == "16" ]; then
BLOCK_SIZE="2x2"
fi
if [ "${GRID_LENGTH}" == "36" ]; then
BLOCK_SIZE="3x2"
fi
if [ "${GRID_LENGTH}" == "81" ]; then
BLOCK_SIZE="3x3"
fi
if [ "${GRID_LENGTH}" == "256" ]; then
BLOCK_SIZE="4x4"
fi
echo "${BLOCK_SIZE}"
}
for GRID in ${GRIDS}; do
BLOCK_SIZE="$(get_block_size "${GRID}")"
echo "GRID: ${BLOCK_SIZE} / ${GRID}"
python ${CURRENT_DIR}/solve.py ${BLOCK_SIZE} ${GRID} | tail -n 1
done
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment