Bash Simplified Sudoku

Bash simplified sudoku


$ awk '
function check(num) {

return num != 45 ? 1 : 0;
}
{
row = 0;
for (i = 1; i <= 9; i++) {
row += $i;
col[i] += $i;
}
if (check(row) > 0) {
errors[++error_len] = sprintf("error in line %s: %s", FNR, $0);
}
}
END {
for (i = 1; i <= 9; i++) {
if (check(row) > 0) {
errors[++error_len] = sprintf("error in column %s: %s", i, col[i]);
}
}
if (error_len) {
for (i = 0; i <= error_len; i++) {
print(errors[i]);
}
}
else {
print("all good");
}
}
' sudoku
error in line 4: 6 7 1 4 8 2 6 9 3

$ cat sudoku
8 1 2 9 7 4 3 6 5
9 3 4 6 5 1 7 8 2
7 6 5 8 2 3 9 4 1
6 7 1 4 8 2 6 9 3 <-- see the 6 here thats an error:
2 8 9 3 6 5 4 1 7
6 4 3 7 1 9 2 5 8
1 9 6 5 3 7 8 2 4
3 2 8 1 4 6 5 7 9
4 5 7 2 9 8 1 3 6

Bash shell script checker

A solution without awk, combining different other tools.

EDIT: This solution works for an inputfile called input and spaces between the digits. See comments about changing this behaviour.

echo "Checking 9 lines"
if [ $(wc -l <input ) -ne 9 ]; then
echo "Wrong number of lines"
exit 1
fi
echo "Check for correct layout"
if [ $(grep -cv '^[1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9]$' input ) -ne 0 ]; then
echo "Not all lines are correct, maybe spaces at the end of a line?"
grep -v '^[1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9] [1-9]$' input
exit 1
fi
i=0
while read -r line; do
((i++))
if [ $(echo "${line}" | tr " " "\n" | sort -u | wc -l ) -ne 9 ]; then
echo "Wrong nr of unique numbers in row $i"
fi
done <input
for j in {1..9}; do
if [ $(cut -d" " -f${j} input | sort -u | wc -l ) -ne 9 ]; then
echo "Wrong nr of unique numbers in column $j"
fi
done

Sudoku puzzle test


but script does not replace all the values and I don't understand why.

That happens because your code looks for the column number as a value in the current row, but that value may have already been changed to 0, and so there is no match, and the current value will not be replaced by 0.

In your example this happens in the first row, when the inner loop is in its 3rd iteration. Then newnum is equal to 2, at which moment the current sudoku row copiedSudoku[num] looks like this:

[0, 0, 4, 6, 7, 8, 9, 1, 2]

As you see, the previous two iterations of the inner loop placed a zero, but now testNums[newnum] is 3, and that number was already erased, so the test method call will return false, and so that 4 is not cleared.

Issues

  • The main comment on this algorithm is that it should be unnecessary to modify anything in the given Sudoku. The algorithm should just look and calculate.

  • It is overkill to create a regular expression just to find one digit. For that you can use the include array method.

  • The .test RegExp method expects a string as argument, but you pass it an array. This means the array is converted to a comma-separated string, and so it still kinda works, but it certainly is not advised.

  • It is not good practice to use a for..in loop over an array. If you are interested in the values in the array, use a for..of loop.

  • As your function's name suggests a boolean return value, it should not return the Sudoku, but indicate success with true or false.

Read-only solution

As said, there should be no need to place zeroes. It doesn't help to reach your goal.





const hasAllDigits = arr =>
arr.reduce((acc, digit) => acc | (1 << digit), 0) == 0x3FE;

const doneOrNot = (sudoku) => {
for (let i = 0; i < 9; i+=3) {
for (let j = 0; j < 3; j++) {
let k = i + j;
// Check k-th row
if (!hasAllDigits(sudoku[k])) return false;
// Check k-th column
if (!hasAllDigits(sudoku.map(row => row[k]))) return false;
// Check k-th 3x3 box
if (!hasAllDigits(sudoku.slice(j*3, j*3+3)
.flatMap(row => row.slice(i, i + 3)))) return false;
}
}
return true;
};

console.log(doneOrNot(
[[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 5, 3, 4, 8],
[1, 9, 8, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 4, 5, 2, 8, 6, 1, 7, 9]],
));

Bash Shell functions

You have defined the function correctly but a statement inside the function is causing the error.

You cant do:

result=expr $var * 100

You need to get the result of expr using subshell execution($() recommended) or using backticks; its better to use bash's $(()) instead of calling an external process(expr)

$ function sum { var=$1; result=$(($var * 100)); echo $result; }
$ export -f sum
$ sum 10
1000

Special symbols as patterns in bash case statement

your $calc is the raw string of the input

#!/bin/bash

read -p "Give expression example: 42+6 " calc
read NUM1 NUM2 <<<${calc//[^0-9]/ }
read oper <<<${calc//[0-9]/}

case $oper in
\+)
echo "$NUM1 plus $NUM2 equals $NUM1+$NUM2"
;;
\-)
echo "$NUM1 minus $NUM2 equals $NUM1-$NUM2"
;;
\*)
echo "$NUM1 multiplied by $NUM2 equals $NUM1*$NUM2"
;;
\/)
echo "$NUM1 divided by $NUM2 equals $NUM1/$NUM2"
;;
esac


Related Topics



Leave a reply



Submit