LDP/LDP/guide/docbook/abs-guide/msquare.sh

113 lines
3.2 KiB
Bash

#!/bin/bash
# msquare.sh
# Magic Square generator (odd-order squares only!)
# Author: mendel cooper
# reldate: 19 Jan. 2009
# License: Public Domain
# A C-program by the very talented Kwon Young Shin inspired this script.
# http://user.chollian.net/~brainstm/MagicSquare.htm
# Definition: A "magic square" is a two-dimensional array
# of integers in which all the rows, columns,
# and *long* diagonals add up to the same number.
# Being "square," the array has the same number
# of rows and columns. That number is the "order."
# An example of a magic square of order 3 is:
# 8 1 6
# 3 5 7
# 4 9 2
# All the rows, columns, and the two long diagonals add up to 15.
# Globals
EVEN=2
MAXSIZE=31 # 31 rows x 31 cols.
E_usage=90 # Invocation error.
dimension=
declare -i square
usage_message ()
{
echo "Usage: $0 order"
echo " ... where \"order\" (square size) is an ODD integer"
echo " in the range 3 - 31."
# Actually works for squares up to order 159,
#+ but large squares will not display pretty-printed in a term window.
# Try increasing MAXSIZE, above.
exit $E_usage
}
calculate () # Here's where the actual work gets done.
{
local row col index dimadj j k cell_val=1
dimension=$1
let "dimadj = $dimension * 3"; let "dimadj /= 2" # x 1.5, then truncate.
for ((j=0; j < dimension; j++))
do
for ((k=0; k < dimension; k++))
do # Calculate indices, then convert to 1-dim. array index.
# Bash doesn't support multidimensional arrays. Pity.
let "col = $k - $j + $dimadj"; let "col %= $dimension"
let "row = $j * 2 - $k + $dimension"; let "row %= $dimension"
let "index = $row*($dimension) + $col"
square[$index]=cell_val; ((cell_val++))
done
done
} # Plain math, visualization not required.
print_square () # Output square, one row at a time.
{
local row col idx d1
let "d1 = $dimension - 1" # Adjust for zero-indexed array.
for row in $(seq 0 $d1)
do
for col in $(seq 0 $d1)
do
let "idx = $row * $dimension + $col"
printf "%3d " "${square[idx]}"; echo -n " "
done # Displays up to 13th order neatly in 80-column term window.
echo # Newline after each row.
done
}
#################################################
if [[ -z "$1" ]] || [[ "$1" -gt $MAXSIZE ]]
then
usage_message
fi
let "test_even = $1 % $EVEN"
if [ $test_even -eq 0 ]
then # Can't handle even-order squares.
usage_message
fi
calculate $1
print_square # echo "${square[@]}" # DEBUG
exit $?
#################################################
# Exercises:
# ---------
# 1) Add a function to calculate the sum of each row, column,
# and *long* diagonal. The sums must match.
# This is the "magic constant" of that particular order square.
# 2) Have the print_square function auto-calculate how much space
# to allot between square elements for optimized display.
# This might require parameterizing the "printf" line.
# 3) Add appropriate functions for generating magic squares
# with an *even* number of rows/columns.
# This is non-trivial(!).
# See the URL for Kwon Young Shin, above, for help.