Pull to refresh

Учим bash-скрипты, пишем Sokoban

Reading time 4 min
Views 138K
Мне кажется, что на свете еще есть люди, которые хорошо знают несколько языков программирования, но при этом не пишут скриптов для bash, потому что скриптовый язык bash выглядит для них слишком странным. Чтобы доказать, что bash — это несложно, я написала игру Сокобан (или Грузчик, кому как нравится), и хочу рассказать, как она работает.



Коротко о главном


Переменные в bash

В bash нет типов данных, а еще при присваивании нельзя ставить лишних пробелов:
y=11 #правильно
B="" #и это тоже правильно
x = 3 #а это - неправильно

Чтобы прочитать значение переменной, перед ее именем нужно ставить знак доллара. Не возбраняется (а иногда и необходимо) заключать имя переменной в фигурые скобки, а затем перед открывающей скобкой все же ставить знак доллара. Арифметические операции (целочисленные) выполнять можно так:
r=$(( $x + $y )) #и снова обратите внимание на скобки и пробелы
r=$(( ${x} + ${y} )) #можно делать и так

Массивы в bash бывают только одномерные, инициализировать их не нужно. Можно обращаться к данным по индексу, а так же задавать пачками:
map[3]=4
map[${r}]="_"
map=( 1 2 3 4 5 6 ) #обратите внимание на пробелы


Ввод и вывод

Для вывода используется команда echo. Она поддерживает escape-последовательности, и это очень удобно.
echo "Hello, world!" #привет, мир!
echo -en "\E[3;3f Hello, world!" #курсор будет установлен по координатам (3;3), а затем будет выведен текст
echo "${x}" #переменные выводят вот так
echo -en "\E[${x};${y}f Hello!" #кстати, их можно использовать в качестве параметров escape-последовательностей

Для ввода используется команда read.
read B #ожидать ввода, сохранить результат в переменную B
read -n 1 B #ожидать ввода с клавиатуры одного символа
read -t 1 -n 1 B #ожидать в течении секунды ввода символа с клавиатуры


Управляющие конструкции

Конструкция if-then-else:
if [[ "$B" = "Q" ]] #сравнение строк, обратите внимание на пробелы и скобки
then
#команды тут
fi #конец сравнения

if [[ "$B" -eq 3 ]] #сравнение чисел
then
#команды
fi #конец сравнения

Конструкции while и case:
while ( [ "$B" != "Q" ] ) do #в скобках - условие, обратите внимание на пробелы и скобки
#команды
done

case "$B" in #аналог команды switch
  "W"   )  команда1;; #обратите внимание на скобку и пробелы
  "S"   )  команда2;;
  [Q-Z] ) команда3;; #можно использовать маски, похожие на регэкспы
esac #конец case

Цикл for:
for (( value=1 ; value<LIMIT; value++ )) do #где LIMIT - переменная
#команды
done


Интерактивная компьютерная игра Sokoban


#!/bin/bash
#Компьютерная игра Sokoban
#Определим карту как массив
map=( W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W _ _ _ W W W W W W W W W W W W
W W W W W = _ _ W W W W W W W W W W W W
W W W W W _ _ = W W W W W W W W W W W W
W W W _ _ = _ = _ W W W W W W W W W W W
W W W _ W _ W W _ W W W W W W W W W W W
W _ _ _ W _ W W _ W W W W W W _ _ o o W
W _ = _ _ = _ _ _ _ _ _ _ _ _ _ _ o o W
W W W W W _ W W W _ W _ W W W _ _ o o W
W W W W W _ _ _ _ _ W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W
W W W W W W W W W W W W W W W W W W W W )

#И зададим начальные координаты грузчика
x=9
y=11
#За одно на всякий случай очистим переменную B, в которую будем сохранять ввод с клавиатуры
B=""

#установим счетчик цикла (а за одно размер игровой карты)
LIMIT=20

#Очистим экран
echo -en "\E[2J"

#И перейдем к главному циклу
while ( [ "$B" != "q" ] ) do

#Выведем карту на экран
for (( mx=1 ; mx<LIMIT; mx++ )) do
for (( my=1 ; my<LIMIT; my++ )) do
r=$(($mx*20+$my)) #у нас нет двумерных массивов, поэтому обойдемся одномерным
echo -en "\E[${mx};${my}f${map[${r}]}"
done
done

#За одно выведем всякую полезную информацию
echo -en "\E[22;2fWASD - move, Q - quit"
echo -en "\E[23;2fW - wall, X - hero, = and @ - chest, o - place for chest"
#И наконец - героя (чтобы курсор моргал в том месте, где он стоит)
echo -en "\E[${x};${y}fX\E[${x};${y}f" 

#Теперь очистим переменную для ввода с клавиатуры
B=""
#И прочитаем один символ
read -s -t 1 -n 1 B

#Сбросим переменные, в которые будем сохранять относительное перемещение грузчика
nx=0
ny=0

#Пришло время узнать, в какую сторону пользователь хочет переместить грузчика
case "$B" in
  [wW]   )  nx=$(( - 1));;
  [sS]   )  nx=$(( 1));;
  [aA]   )  ny=$(( - 1));;
  [dD]   )  ny=$(( 1));;
#На случай, если у кого-то нажат CAPS LOCK
  [qQ]   )  B="q";; 
esac

#Найдем координату клетки, на которую грузчик хочет перейти
r=$(( ($x + $nx) * $LIMIT + $y + $ny ))
#И сразу - следующую за ней
r2=$(( ($x + $nx + $nx) * $LIMIT + $y + $ny +$ny ))

#Если в этой клетке пусто, то
if [[ "${map[${r}]}" = "_" ]]
then
#Можно смело менять координаты
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi

#По местам для сундуков тоже можно ходить
if [[ "${map[${r}]}" = "o" ]] 
then
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi

#Ага, а что если ящик?
if [[ "${map[${r}]}" = "=" ]] 
then
#Если за ящиком пусто, то можно двигать
if [[ "${map[${r2}]}" = "_" ]] 
then
map[${r2}]="="
map[${r}]="_"
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi
#Если место для ящика свободно - тоже можно двигать
if [[ "${map[${r2}]}" = "o" ]] 
then
map[${r2}]="@"
map[${r}]="_"
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi
fi

#Столкнулись с ящиком, который стоит на месте
if [[ "${map[${r}]}" = "@" ]] 
then
#Если за ним пусто - значит, сдвинем ящик
if [[ "${map[${r2}]}" = "_" ]] 
then
map[${r2}]="="
map[${r}]="o"
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi
#Если за ним другое место - то тоже сдвинем
if [[ "${map[${r2}]}" = "o" ]] 
then
map[${r2}]="@"
map[${r}]="o"
x=$(( $x + $nx ))
y=$(( $y + $ny ))
fi
fi

#Возвращаемся к выводу на экран и опросу клавиатуры
done       
                    
#Пользователь нажал на Q - пора очистить экран от строительного мусора
echo -en "\E[2J"


Уфф! Вроде все. Конечно, рекомендую подробнее почитать про bash scripting.

С нетерпением жду появления 3D-движка или хотя бы аркадного платформера на bash. На JavaScript ведь можно...
Tags:
Hubs:
+158
Comments 67
Comments Comments 67

Articles