XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X . X X X . . X X X . . X X X . . X X o . . X X . XXXXXXX . . X X X . X . . X X . X . X . X X X . X . . . X X . X . . . . X X X . X X . . . . X X . . . X . . . X X X . . . . . . X X XXXXXX . . . . . . X X XXXXXX . . . . . . X X XXXXXX . . . . . X X X . . . . . . X X . . . . . . X X . . . . . . X X . . . . . .X X X . . X . . . . X X . . . X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Понятно, что в зависимости от конфигурации препятствия шарик будет отскакивать в разных направлениях, а писать сложные проверки не хотелось:
X X X _ 1 _ _ _ 2 _ X _ _ 1 X 2 _ _
(На рисунках выше шарик до удара двигался в направлении вверх и вправо.)
Несложно понять, что для определения следующего положения шарика необходимо знать кусочек поля размером 3x3, и что возможно всего 28=256 вариантов препятствий вокруг шарика.
Но кодировать все 256 вариантов тоже не хочется, поэтому вспоминаем про сопоставление с шаблоном -- идею, о которой узнал, разбираясь с функциональным программированием. Заметим, что в некоторых случаях направление дальнейшего движения зависит только от ключевых кусков стены и не зависит от того, что находится в остальных местах, например:
_ X _ X X X X X _ _ X X _ 1 _ _ 1 _ _ 1 _ _ 1 _ _ _ 2 _ _ 2 _ _ 2 _ _ 2
Введём обозначения: X -- стена, _ -- пустая клетка, ? -- всё что угодно.
Тогда приведённые выше конфигурации и некоторые другие задаются следующим шаблоном:
? X ? ? o _ ? _ _
И всё, осталось только написать функцию сопоставления с шаблоном и придумать набор шаблонов, полностью покрывающих все возможные варианты стен. Всё это можно найти в скрипте ниже (Почему Bash? Так надо.)
#!/bin/bash -i
declare -a map
mapWidth=$COLUMNS
mapHeight=$LINES
((ballX = mapWidth / 2))
((ballY = mapHeight / 2))
ballDX=1
ballDY=1
function compare() {
local -a pattern=( $1 )
local -a x=( $2 )
local i
for ((i = 0; i < 8; ++i)); do
if [[ "${pattern[$i]}" != "?" && "${pattern[$i]}" != "${x[$i]}" ]]; then
return 1
fi
done
return 0
}
rules=(
# pattern dy dx
"? _ _ ? _ ? ? ?" 1 1
"? X ? ? _ ? _ _" -1 1
"_ _ ? _ X ? ? ?" 1 -1
"? X ? ? ? ? X ?" 0 0
"? ? ? X X ? ? ?" 0 0
"X ? X ? ? X ? X" 0 0
"? X ? ? ? X ? X" 0 0
"X ? ? ? X X ? ?" 0 0
"? ? X X ? ? ? X" 0 0
"X ? X ? ? ? X ?" 0 0
"_ _ X _ _ _ _ _" -1 -1
"? X ? _ X _ _ ?" -1 -1
"X ? X _ ? _ _ X" -1 -1
"X ? X ? _ ? _ _" -1 1
"_ _ X _ ? ? ? X" 1 -1
"X ? ? ? X ? X ?" 0 0
"? X ? X ? ? ? X" 0 0
"? X ? ? X X ? ?" 0 0
"? ? X X ? ? X ?" 0 0
"X ? ? _ X _ _ ?" -1 -1
"? X ? ? _ X _ _" -1 1
"_ _ X _ ? ? X ?" 1 -1
"_ _ ? _ X X ? ?" 1 -1
"? X ? _ ? _ _ X" -1 -1
"? ? X X _ ? _ _" -1 1
"_ _ X _ _ X _ _" 0 0
)
function findRule() {
local x=$1
local countRules=$((${#rules[*]} / 3))
for ((i = 0; i < countRules; ++i)); do
if compare "${rules[$((i * 3))]}" "$x"; then
echo "${rules[$((i * 3 + 1))]} ${rules[$((i * 3 + 2))]}"
break
fi
done
}
function moveBall() {
local -a x
x[0]=${map[$(((ballY + ballDY) * mapWidth + ballX - ballDX))]}
x[1]=${map[$(((ballY + ballDY) * mapWidth + ballX ))]}
x[2]=${map[$(((ballY + ballDY) * mapWidth + ballX + ballDX))]}
x[3]=${map[$(((ballY ) * mapWidth + ballX - ballDX))]}
x[4]=${map[$(((ballY ) * mapWidth + ballX + ballDX))]}
x[5]=${map[$(((ballY - ballDY) * mapWidth + ballX - ballDX))]}
x[6]=${map[$(((ballY - ballDY) * mapWidth + ballX ))]}
x[7]=${map[$(((ballY - ballDY) * mapWidth + ballX + ballDX))]}
local -a rule=( `findRule "${x[*]}"` )
((ballDY *= rule[0]))
((ballDX *= rule[1]))
((ballX += ballDX))
((ballY += ballDY))
}
countPoints=20
function fillMap() {
local i
local j
for ((i = 1; i < mapHeight - 1; ++i)); do
for ((j = 1; j < mapWidth - 1; ++j)); do
map[$((i * mapWidth + j))]="_"
done
done
for ((i = 0; i < mapWidth; ++i)); do
map[$i]="X"
map[$(((mapHeight - 1) * mapWidth + $i))]="X"
done
for ((i = 0; i < mapHeight; ++i)); do
map[$((i * mapWidth))]="X"
map[$((i * mapWidth + mapWidth - 1))]="X"
done
for ((i = 0; i < countPoints; ++i)); do
map[$((RANDOM % (mapWidth * mapHeight)))]="X"
done
map[$((ballY * mapWidth + ballX))]="_"
}
function drawMap() {
local i
local j
for ((i = 0; i < mapHeight; ++i)); do
for ((j = 0; j < mapWidth; ++j)); do
if [[ ${map[$((i * mapWidth + j))]} == "X" ]]; then
echo -ne "\e[$((i + 1));$((j + 1))fX"
fi
done
done
}
function clearBall() {
echo -ne "\e[$((ballY + 1));$((ballX + 1))f."
}
function drawBall() {
echo -ne "\e[$((ballY + 1));$((ballX + 1))fo"
}
function init() {
clear
fillMap
drawMap
}
function run() {
local -l key=""
while : ; do
read -n 1 -t 0.1 key
if [[ "$key" == "q" ]]; then
break;
fi
clearBall
moveBall
drawBall
done
}
function finish() {
clear
}
init
run
finish
Более подробно про сопоставление с шаблоном можно почитать, например, в статье Евгения Кирпичёва Элементы функционального программирования и ещё где-нибудь, а про программирование на Bash в Advanced Bash-Scripting Guide.




