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 afor..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
Bash "Declare -A" Does Not Work on MACos
Rename Part of File Name Based on Exact Match in Contents of Another File
How to Do 'Ret' Instruction from Code at _Start in MACos? Linux
How to Loop Through the Coming Frequency of the Keyword
Svn Checkout the Contents of a Folder, Not the Folder Itself
Recursively Find Files with a Specific Extension
Fuse Error: Transport Endpoint Is Not Connected
Finding Executable Files Using Ls and Grep
Given a Linux Username and a Password How to Test If It Is a Valid Account
How to Split Mailbox into Single File Per Message
Printing an Integer with X86 32-Bit Linux Sys_Write (Nasm)
Best Way to Set Environment Variables in Calling Shell
How to Wrap Lines Within Columns in Linux
Automatic Login on Angstrom Linux