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

389 lines
10 KiB
Bash
Raw Normal View History

2002-04-01 16:05:47 +00:00
#!/bin/bash
# life.sh: "Life in the Slow Lane"
2011-08-29 23:59:19 +00:00
# Author: Mendel Cooper
# License: GPL3
2008-11-23 22:43:47 +00:00
# Version 0.2: Patched by Daniel Albers
#+ to allow non-square grids as input.
# Version 0.2.1: Added 2-second delay between generations.
2002-04-01 16:05:47 +00:00
# ##################################################################### #
# This is the Bash script version of John Conway's "Game of Life". #
# "Life" is a simple implementation of cellular automata. #
# --------------------------------------------------------------------- #
2008-07-20 23:16:47 +00:00
# On a rectangular grid, let each "cell" be either "living" or "dead." #
2002-04-01 16:05:47 +00:00
# Designate a living cell with a dot, and a dead one with a blank space.#
2011-08-29 23:59:19 +00:00
# Begin with an arbitrarily drawn dot-and-blank grid, #
#+ and let this be the starting generation: generation 0. #
2002-04-01 16:05:47 +00:00
# Determine each successive generation by the following rules: #
2008-07-20 23:16:47 +00:00
# 1) Each cell has 8 neighbors, the adjoining cells #
#+ left, right, top, bottom, and the 4 diagonals. #
2008-05-09 18:58:34 +00:00
# #
2002-04-01 16:05:47 +00:00
# 123 #
2008-07-20 23:16:47 +00:00
# 4*5 The * is the cell under consideration. #
2002-04-01 16:05:47 +00:00
# 678 #
# #
# 2) A living cell with either 2 or 3 living neighbors remains alive. #
SURVIVE=2 #
2011-08-29 23:59:19 +00:00
# 3) A dead cell with 3 living neighbors comes alive, a "birth." #
2002-04-01 16:05:47 +00:00
BIRTH=3 #
2004-11-15 13:35:56 +00:00
# 4) All other cases result in a dead cell for the next generation. #
2002-04-01 16:05:47 +00:00
# ##################################################################### #
2008-05-09 18:58:34 +00:00
startfile=gen0 # Read the starting generation from the file "gen0" ...
2002-06-03 14:35:48 +00:00
# Default, if no other file specified when invoking script.
#
if [ -n "$1" ] # Specify another "generation 0" file.
then
startfile="$1"
fi
2006-05-19 16:11:49 +00:00
############################################
# Abort script if "startfile" not specified
2008-05-09 18:58:34 +00:00
#+ and
2008-07-20 23:16:47 +00:00
#+ default file "gen0" not present.
2006-05-19 16:11:49 +00:00
2009-01-22 14:43:09 +00:00
E_NOSTARTFILE=86
2006-05-19 16:11:49 +00:00
if [ ! -e "$startfile" ]
then
echo "Startfile \""$startfile"\" missing!"
exit $E_NOSTARTFILE
fi
############################################
2002-06-03 14:35:48 +00:00
2002-04-01 16:05:47 +00:00
ALIVE1=.
DEAD1=_
2008-07-20 23:16:47 +00:00
# Represent living and dead cells in the start-up file.
2002-04-01 16:05:47 +00:00
2011-08-29 23:59:19 +00:00
# -----------------------------------------------------#
2002-04-01 16:05:47 +00:00
# This script uses a 10 x 10 grid (may be increased,
2011-08-29 23:59:19 +00:00
#+ but a large grid will slow down execution).
2002-04-01 16:05:47 +00:00
ROWS=10
COLS=10
2011-08-29 23:59:19 +00:00
# Change above two variables to match desired grid size.
# -----------------------------------------------------#
2002-04-01 16:05:47 +00:00
GENERATIONS=10 # How many generations to cycle through.
2009-01-22 14:43:09 +00:00
# Adjust this upwards
2002-04-01 16:05:47 +00:00
#+ if you have time on your hands.
2008-07-20 23:16:47 +00:00
NONE_ALIVE=85 # Exit status on premature bailout,
2002-04-01 16:05:47 +00:00
#+ if no cells left alive.
2011-08-29 23:59:19 +00:00
DELAY=2 # Pause between generations.
2002-04-01 16:05:47 +00:00
TRUE=0
FALSE=1
ALIVE=0
DEAD=1
2005-03-21 13:51:11 +00:00
avar= # Global; holds current generation.
2002-04-01 16:05:47 +00:00
generation=0 # Initialize generation count.
# =================================================================
2008-07-20 23:16:47 +00:00
let "cells = $ROWS * $COLS" # How many cells.
2002-04-01 16:05:47 +00:00
2008-07-20 23:16:47 +00:00
# Arrays containing "cells."
declare -a initial
2002-04-01 16:05:47 +00:00
declare -a current
display ()
{
2008-07-20 23:16:47 +00:00
alive=0 # How many cells alive at any given time.
2002-04-01 16:05:47 +00:00
# Initially zero.
declare -a arr
arr=( `echo "$1"` ) # Convert passed arg to array.
element_count=${#arr[*]}
local i
local rowcheck
for ((i=0; i<$element_count; i++))
2002-04-01 16:05:47 +00:00
do
# Insert newline at end of each row.
2004-11-15 13:35:56 +00:00
let "rowcheck = $i % COLS"
2002-04-01 16:05:47 +00:00
if [ "$rowcheck" -eq 0 ]
then
echo # Newline.
echo -n " " # Indent.
fi
cell=${arr[i]}
if [ "$cell" = . ]
then
let "alive += 1"
fi
echo -n "$cell" | sed -e 's/_/ /g'
2009-01-22 14:43:09 +00:00
# Print out array, changing underscores to spaces.
2002-04-01 16:05:47 +00:00
done
return
}
2011-08-29 23:59:19 +00:00
IsValid () # Test if cell coordinate valid.
2002-04-01 16:05:47 +00:00
{
if [ -z "$1" -o -z "$2" ] # Mandatory arguments missing?
then
return $FALSE
fi
local row
local lower_limit=0 # Disallow negative coordinate.
local upper_limit
local left
local right
let "upper_limit = $ROWS * $COLS - 1" # Total number of cells.
if [ "$1" -lt "$lower_limit" -o "$1" -gt "$upper_limit" ]
then
return $FALSE # Out of array bounds.
fi
row=$2
2004-11-15 13:35:56 +00:00
let "left = $row * $COLS" # Left limit.
2002-04-01 16:05:47 +00:00
let "right = $left + $COLS - 1" # Right limit.
if [ "$1" -lt "$left" -o "$1" -gt "$right" ]
then
return $FALSE # Beyond row boundary.
fi
return $TRUE # Valid coordinate.
}
2008-07-20 23:16:47 +00:00
IsAlive () # Test whether cell is alive.
2009-01-22 14:43:09 +00:00
# Takes array, cell number, and
2008-07-20 23:16:47 +00:00
{ #+ state of cell as arguments.
GetCount "$1" $2 # Get alive cell count in neighborhood.
2002-04-01 16:05:47 +00:00
local nhbd=$?
if [ "$nhbd" -eq "$BIRTH" ] # Alive in any case.
then
return $ALIVE
fi
if [ "$3" = "." -a "$nhbd" -eq "$SURVIVE" ]
then # Alive only if previously alive.
return $ALIVE
fi
2011-08-29 23:59:19 +00:00
return $DEAD # Defaults to dead.
2002-04-01 16:05:47 +00:00
}
GetCount () # Count live cells in passed cell's neighborhood.
# Two arguments needed:
# $1) variable holding array
# $2) cell number
{
local cell_number=$2
local array
local top
local center
local bottom
local r
local row
local i
local t_top
local t_cen
local t_bot
local count=0
local ROW_NHBD=3
array=( `echo "$1"` )
let "top = $cell_number - $COLS - 1" # Set up cell neighborhood.
let "center = $cell_number - 1"
let "bottom = $cell_number + $COLS - 1"
2004-11-15 13:35:56 +00:00
let "r = $cell_number / $COLS"
2002-04-01 16:05:47 +00:00
for ((i=0; i<$ROW_NHBD; i++)) # Traverse from left to right.
2002-04-01 16:05:47 +00:00
do
let "t_top = $top + $i"
let "t_cen = $center + $i"
let "t_bot = $bottom + $i"
2008-07-20 23:16:47 +00:00
let "row = $r" # Count center row.
2002-04-01 16:05:47 +00:00
IsValid $t_cen $row # Valid cell position?
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_cen]} = "$ALIVE1" ] # Is it alive?
2009-01-22 14:43:09 +00:00
then # If yes, then ...
2002-04-01 16:05:47 +00:00
let "count += 1" # Increment count.
fi
fi
let "row = $r - 1" # Count top row.
IsValid $t_top $row
if [ $? -eq "$TRUE" ]
then
2008-05-09 18:58:34 +00:00
if [ ${array[$t_top]} = "$ALIVE1" ] # Redundancy here.
2011-08-29 23:59:19 +00:00
then # Can it be optimized?
2002-04-01 16:05:47 +00:00
let "count += 1"
fi
fi
let "row = $r + 1" # Count bottom row.
IsValid $t_bot $row
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_bot]} = "$ALIVE1" ]
then
let "count += 1"
fi
fi
done
if [ ${array[$cell_number]} = "$ALIVE1" ]
then
let "count -= 1" # Make sure value of tested cell itself
fi #+ is not counted.
return $count
}
next_gen () # Update generation array.
{
local array
local i=0
array=( `echo "$1"` ) # Convert passed arg to array.
while [ "$i" -lt "$cells" ]
do
2011-08-29 23:59:19 +00:00
IsAlive "$1" $i ${array[$i]} # Is the cell alive?
2002-04-01 16:05:47 +00:00
if [ $? -eq "$ALIVE" ]
then # If alive, then
array[$i]=. #+ represent the cell as a period.
else
array[$i]="_" # Otherwise underscore
2008-05-09 18:58:34 +00:00
fi #+ (will later be converted to space).
2002-04-01 16:05:47 +00:00
let "i += 1"
done
2011-08-29 23:59:19 +00:00
# let "generation += 1" # Increment generation count.
### Why was the above line commented out?
2004-11-15 13:35:56 +00:00
2002-04-01 16:05:47 +00:00
# Set variable to pass as parameter to "display" function.
avar=`echo ${array[@]}` # Convert array back to string variable.
display "$avar" # Display it.
echo; echo
2005-03-21 13:51:11 +00:00
echo "Generation $generation - $alive alive"
2002-04-01 16:05:47 +00:00
if [ "$alive" -eq 0 ]
then
echo
2009-01-22 14:43:09 +00:00
echo "Premature exit: no more cells alive!"
2002-04-01 16:05:47 +00:00
exit $NONE_ALIVE # No point in continuing
fi #+ if no live cells.
}
# =========================================================
# main ()
2011-08-29 23:59:19 +00:00
# {
2002-04-01 16:05:47 +00:00
# Load initial array with contents of startup file.
initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\
# Delete lines containing '#' comment character.
2009-01-22 14:43:09 +00:00
sed -e 's/\./\. /g' -e 's/_/_ /g'` )
2002-04-01 16:05:47 +00:00
# Remove linefeeds and insert space between elements.
clear # Clear screen.
echo # Title
2008-11-23 22:43:47 +00:00
setterm -reverse on
2002-04-01 16:05:47 +00:00
echo "======================="
2008-11-23 22:43:47 +00:00
setterm -reverse off
2002-04-01 16:05:47 +00:00
echo " $GENERATIONS generations"
echo " of"
echo "\"Life in the Slow Lane\""
2008-11-23 22:43:47 +00:00
setterm -reverse on
2002-04-01 16:05:47 +00:00
echo "======================="
2008-11-23 22:43:47 +00:00
setterm -reverse off
sleep $DELAY # Display "splash screen" for 2 seconds.
2002-04-01 16:05:47 +00:00
# -------- Display first generation. --------
Gen0=`echo ${initial[@]}`
display "$Gen0" # Display only.
echo; echo
2005-03-21 13:51:11 +00:00
echo "Generation $generation - $alive alive"
2008-11-23 22:43:47 +00:00
sleep $DELAY
2002-04-01 16:05:47 +00:00
# -------------------------------------------
2008-07-20 23:16:47 +00:00
let "generation += 1" # Bump generation count.
2002-04-01 16:05:47 +00:00
echo
# ------- Display second generation. -------
Cur=`echo ${initial[@]}`
next_gen "$Cur" # Update & display.
2008-11-23 22:43:47 +00:00
sleep $DELAY
2002-04-01 16:05:47 +00:00
# ------------------------------------------
let "generation += 1" # Increment generation count.
2002-06-03 14:35:48 +00:00
# ------ Main loop for displaying subsequent generations ------
2002-04-01 16:05:47 +00:00
while [ "$generation" -le "$GENERATIONS" ]
do
Cur="$avar"
next_gen "$Cur"
let "generation += 1"
2008-11-23 22:43:47 +00:00
sleep $DELAY
2002-04-01 16:05:47 +00:00
done
2002-06-03 14:35:48 +00:00
# ==============================================================
2002-04-01 16:05:47 +00:00
echo
2011-08-29 23:59:19 +00:00
# }
2002-04-01 16:05:47 +00:00
2008-07-20 23:16:47 +00:00
exit 0 # CEOF:EOF
2006-05-19 16:11:49 +00:00
2002-04-01 16:05:47 +00:00
2004-11-15 13:35:56 +00:00
# The grid in this script has a "boundary problem."
2016-10-24 12:36:06 +00:00
# The top, bottom, and sides border on a void of dead cells.
2002-04-01 16:05:47 +00:00
# Exercise: Change the script to have the grid wrap around,
2004-11-15 13:35:56 +00:00
# + so that the left and right sides will "touch,"
2002-04-01 16:05:47 +00:00
# + as will the top and bottom.
2004-11-15 13:35:56 +00:00
#
# Exercise: Create a new "gen0" file to seed this script.
# Use a 12 x 16 grid, instead of the original 10 x 10 one.
# Make the necessary changes to the script,
#+ so it will run with the altered file.
#
# Exercise: Modify this script so that it can determine the grid size
#+ from the "gen0" file, and set any variables necessary
#+ for the script to run.
# This would make unnecessary any changes to variables
#+ in the script for an altered grid size.
2008-05-09 18:58:34 +00:00
#
# Exercise: Optimize this script.
2011-08-29 23:59:19 +00:00
# It has redundant code.