Книга: Искусство программирования на языке сценариев командной оболочки

Пример A-11. "Игра "Жизнь""

Пример A-11. "Игра "Жизнь""

#!/bin/bash

# life.sh: Игра "Жизнь"

# ##################################################################### #

# Это Bash-версия известной игры Джона Конвея (John Conway) "Жизнь". #

# --------------------------------------------------------------------- #

# Прямоугольное игровое поле разбито на ячейки, в каждой ячейке может #

#+ располагаться живая особь. #

# Соответственно, ячейка с живой особью отмечается точкой, #

#+ не занятая ячейка -- остается пустой. #

# Изначально, ячейки заполняются из файла -- #

#+ это первое поколение, или "поколение 0" #

# Воспроизводство особей, в каждом последующем поколении, #

#+ определяется следующими правилами #

# 1) Каждая ячейка имеет "соседей" #

#+ слева, справа, сверху, снизу и 4 по диагоналям. #

# 123 #

# 4*5 #

# 678 #

# #

# 2) Если живая особь имеет 2 или 3 живых соседей, то она остается жить.#

# 3) Если пустая ячейка имеет 3 живых соседей -- #

#+ в ней "рождается" новая особь #

SURVIVE=2 #

BIRTH=3 #

# 4) В любом другом случае, живая особь "погибает" #

# ##################################################################### #

startfile=gen0 # Начальное поколение из файла по-умолчанию -- "gen0".

# если не задан другой файл, из командной строки.

#

if [ -n "$1" ] # Проверить аргумент командной строки -- файл с "поколениемn 0".

then

if [ -e "$1" ] # Проверка наличия файла.

then

startfile="$1"

fi

fi

ALIVE1=.

DEAD1=_

# Представление "живых" особей и пустых ячеек в файле с "поколением 0".

# Этот сценарий работает с игровым полем 10 x 10 grid (может быть увеличено,

#+ но большое игровое поле будет обрабатываться очень медленно).

ROWS=10

COLS=10

GENERATIONS=10 # Максимальное число поколений.

NONE_ALIVE=80 # Код завершения на случай,

#+ если не осталось ни одной "живой" особи.

TRUE=0

FALSE=1

ALIVE=0

DEAD=1

avar= # Текущее поколение.

generation=0 # Инициализация счетчика поколений.

# =================================================================

let "cells = $ROWS * $COLS"

# Количество ячеек на игровом поле.

declare -a initial # Массивы ячеек.

declare -a current

display ()

{

alive=0 # Количество "живых" особей.

# Изначально -- ноль.

declare -a arr

arr=( `echo "$1"` ) # Преобразовать аргумент в массив.

element_count=${#arr[*]}

local i

local rowcheck

for ((i=0; i<$element_count; i++))

do

# Символ перевода строки -- в конец каждой строки.

let "rowcheck = $i % ROWS"

if [ "$rowcheck" -eq 0 ]

then

echo # Перевод строки.

echo -n " " # Выравнивание.

fi

cell=${arr[i]}

if [ "$cell" = . ]

then

let "alive += 1"

fi

echo -n "$cell" | sed -e 's/_/ /g'

# Вывести массив, по пути заменяя символы подчеркивания на пробелы.

done

return

}

IsValid () # Проверка корректности координат ячейки.

{

if [ -z "$1" -o -z "$2" ] # Проверка наличия входных аргументов.

then

return $FALSE

fi

local row

local lower_limit=0 # Запрет на отрицательные координаты.

local upper_limit

local left

local right

let "upper_limit = $ROWS * $COLS - 1" # Номер последней ячейки на игровом поле.

if [ "$1" -lt "$lower_limit" -o "$1" -gt "$upper_limit" ]

then

return $FALSE # Выход за границы массива.

fi

row=$2

let "left = $row * $ROWS" # Левая граница.

let "right = $left + $COLS - 1" # Правая граница.

if [ "$1" -lt "$left" -o "$1" -gt "$right" ]

then

return $FALSE # Выхол за нижнюю строку.

fi

return $TRUE # Координаты корректны.

}

IsAlive () # Проверка наличия "живой" особи в ячейке.

# Принимает массив и номер ячейки в качестве входных аргументов.

{

GetCount "$1" $2 # Подсчитать кол-во "живых" соседей.

local nhbd=$?

if [ "$nhbd" -eq "$BIRTH" ] # "Живая".

then

return $ALIVE

fi

if [ "$3" = "." -a "$nhbd" -eq "$SURVIVE" ]

then # "Живая" если перед этим была "живая".

return $ALIVE

fi

return $DEAD # По-умолчанию.

}

GetCount () # Подсчет "живых" соседей.

# Необходимо 2 аргумента:

# $1) переменная-массив

# $2) cell номер ячейки

{

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" # Номера соседних ячеек.

let "center = $cell_number - 1"

let "bottom = $cell_number + $COLS - 1"

let "r = $cell_number / $ROWS"

for ((i=0; i<$ROW_NHBD; i++)) # Просмотр слева-направо.

do

let "t_top = $top + $i"

let "t_cen = $center + $i"

let "t_bot = $bottom + $i"

let "row = $r" # Пройти по соседям в средней строке.

IsValid $t_cen $row # Координаты корректны?

if [ $? -eq "$TRUE" ]

then

if [ ${array[$t_cen]} = "$ALIVE1" ] # "Живая"?

then # Да!

let "count += 1" # Нарастить счетчик.

fi

fi

let "row = $r - 1" # По верхней строке.

IsValid $t_top $row

if [ $? -eq "$TRUE" ]

then

if [ ${array[$t_top]} = "$ALIVE1" ]

then

let "count += 1"

fi

fi

let "row = $r + 1" # По нижней строке.

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" # Убедиться, что сама проверяемая ячейка

fi #+ не была подсчитана.

return $count

}

next_gen () # Обновить массив, в котором содержится информация о новом "поколении".

{

local array

local i=0

array=( `echo "$1"` ) # Преобразовать в массив.

while [ "$i" -lt "$cells" ]

do

IsAlive "$1" $i ${array[$i]} # "Живая"?

if [ $? -eq "$ALIVE" ]

then # Если "живая", то

array[$i]=. #+ записать точку.

else

array[$i]="_" # Иначе -- символ подчеркивания

fi #+ (который позднее заменится на пробел).

let "i += 1"

done

# let "generation += 1" # Увеличить счетчик поколений.

# Подготовка переменных, для передачи в функцию "display".

avar=`echo ${array[@]}` # Преобразовать массив в строку.

display "$avar" # Вывести его.

echo; echo

echo "Поколение $generation -- живых особей $alive"

if [ "$alive" -eq 0 ]

then

echo

echo "Преждеверменное завершение: не осталось ни одной живой особи!"

exit $NONE_ALIVE # Нет смысла продолжать

fi #+ если не осталось ни одной живой особи

}

# =========================================================

# main ()

# Загрузить начальное поколение из файла.

initial=( `cat "$startfile" | sed -e '/#/d' | tr -d 'n' |

sed -e 's/./. /g' -e 's/_/_ /g'` )

# Удалить строки, начинающиеся с символа '#' -- комментарии.

# Удалить строки перевода строки и вставить пробелы между элементами.

clear # Очистка экрана.

echo # Заголовок

echo "======================="

echo " $GENERATIONS поколений"

echo " в"

echo " игре " ЖИЗНЬ""

echo "======================="

# -------- Вывести первое поколение. --------

Gen0=`echo ${initial[@]}`

display "$Gen0" # Тлько вывод.

echo; echo

echo "Поколение $generation -- живых особей $alive"

# -------------------------------------------

let "generation += 1" # Нарастить счетчик поколений.

echo

# ------- Вывести второе поколение. -------

Cur=`echo ${initial[@]}`

next_gen "$Cur" # Обновить и вывести.

# ------------------------------------------

let "generation += 1" # Нарастить счетчик поколений.

# ------ Основной цикл игры ------

while [ "$generation" -le "$GENERATIONS" ]

do

Cur="$avar"

next_gen "$Cur"

let "generation += 1"

done

# ==============================================================

echo

exit 0

# --------------------------------------------------------------

# Этот сценарий имеет недоработку.

# Граничные ячейки сверху, снизу и сбоков остаются пустыми.

# Упражнение: Доработайте сценарий таким образом, чтобы ,

# + левая и правая стороны как бы "соприкасались",

# + так же и верхняя и нижняя стороны.

Оглавление книги


Генерация: 1.559. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз