2015-03-02 3 views
2

Описание проблемы:
Я хочу перебирать массив (сплющенный 2D -> 1D массив прямо сейчас) и продолжать проверять его ближайших соседей. Из этого я хочу определить, мертвы ли они/живы («X» или «.») И изменить свое состояние в соответствии с моими правилами (упрощенным конвейером).Проверка соседних элементов в плоском массиве

Моя сетка выглядит следующим образом .: например

............... 
...........X... 
.X.......X...X. 
............... 
....X.......... 
....X.......... 
............... 
.....XX........ 
..........X.... 
............... 
Cells alive: 9 

Но у меня есть этот массив сплющенный в 1D массив для перебора над ним. Таким образом, в основном это превращается в нечто вроде этого: ....X...X....X. и т.д. После записи его на бумаге, я думаю, есть несколько случаев, чтобы проверить в этой «сетки»:

  • TopLeft элемент (я = 0) - первый элемент , 3 соседей/случаи, чтобы проверить
  • TopRight элемент (I = NCOLUMNS - 1), как указано выше
  • BottomLeft элементов (I = NCOLUMNS * Nrows - NCOLUMNS), как указано выше
  • BottomRight элементов (I = NCOLUMNS * Nrows - 1) - последний элемент, как указано выше
  • Элементы «границы» (5 nei ghbours каждый без угловых элементов)
  • Средних элементов с 8 соседями

Но это, кажется, совершенно глупо, чтобы проверить его с некоторыми, если это и заявлением случае. Если бы я мог использовать реальные 2D-массивы, я бы мог просто создать массив смещений (-1, 1), (0, 1) ... и так далее. Но я не могу придумать, как обращаться с этим кодом. Я буду очень рад за любые советы/примеры и так далее.

Мой код до сих пор:

cellsAlive=0 

#STDIN variables 
geneFile=$1 
nRows=$2 
nColumns=$3 
let "cells = $nRows * $nColumns" 
declare -i tick_rate # instead maybe use watch or sleep 

readarray -t geneArr < $geneFile # -t removes a trailing newline from each line read. 
elementsCounts=${#geneArr[@]} 

echo -e "--- Coordinates ---" 
for ((i = 0; i < $elementsCounts; i++)); do 
    echo "${geneArr[$i]}" #| cut -d' ' -f2 $geneFile | head -2 
done 

echo -e "\n--- Grid ---" 
#file must end with a newline 
[[ $geneFile && -f $geneFile && -r $geneFile ]] || { printf >&2 'arg must be readable file.\n'; exit; } 

array=() 
for ((i=0; i<nRows*nColumns; ++i)); do 
    array+=('.') 
done 
printf "\n" 

while read -r x y; do 
    [[ $x && $y ]] || continue 
    [[ $x = +([[:digit:]]) && $y = +([[:digit:]]) ]] || continue 
    ((x=10#$x,y=10#$y)) #10 digit base 
    ((x<nRows && y<nColumns)) || continue 
    array[x+y*nRows]='X' 
    if [[ ${array[x+y*nRows]} == 'X' ]]; then 
     let "cellsAlive += 1" 
    fi 
done < "$geneFile" 

# print to stdout and to file 
for((i=0;i<nColumns;++i)); do 
    printf '%s' "${array[@]:i*nRows:nRows}" $'\n' 
done | tee currentState 

arrayCopy=("${array[@]}") 
printf "Cells alive: %d" $cellsAlive ; printf "\n" 

# printf "\n" 

for ((i = 0; i < ${#arrayCopy[@]}; i++)); do 
    #neighboursCount=0 
    case $i in 

     "0") if [[ ${arrayCopy[$(($i - 1))]} == 'X' ]] || [[ ${arrayCopy[$(($i + $nColumns))]} == 'X' ]] || [[ ${arrayCopy[$(($i + $nColumns + 1))]} == 'X' ]] ; then #TopLeft 
      echo "That is just ridiculous way to check it..." 
     fi ;; 
     "$(($nColumns - 1))") printf "${arrayCopy[$i]}" ;; #TopRight 
     "$(($nColumns*$nRows-1))") printf "${arrayCopy[$i]}" ;; #BottomRight 
     "$(($nColumns*$nRows-$nColumns))") printf "${arrayCopy[$i]}" ;; #BottomLeft 
     *) ;; #Middle elements with 8 neighbours 
    esac 
done 
printf "\n" 

Заранее спасибо за помощь.

Пример geneFile.txt (добавить пустым, как в конце):

1 2 
4 5 
6 7 
13 2 
5 7 
4 4 
9 2 
11 1 
10 8 
+0

звучит для меня как вы хотите реализовать как версию для игры life :) –

+0

Спасибо за исправление сетки. Да, я просто буду рад за какую-то подсказку прямо сейчас, я собираюсь в кругах и думаю об изменении того, как я подошел к проблеме, - но, возможно, это не обязательно, и кто-то даст хороший совет ^^. Если вопрос слишком общий, я могу попытаться сузить его до определенной проблемы (хотя мне действительно нужно больше общего совета). – learningHappily

ответ

2

ок. Вот так. Поскольку я нашел этот вопрос интересным для реализации в bash, я просто написал реализацию игры жизни конвея.

Основная часть ответа на ваш вопрос, вероятно: Как получить доступ к соседям для позиции в матрице, если она линеаризована?.

Таким образом, вы можете получить доступ к элементу в пониженной матрице

(row*fieldwidth)+columnoffset. 

Каждого сосед, то можно получить доступ с помощью регулировки row и columnoffset по +/-1 начиная с строкой и columnoffset в 0.

Посмотрите на функцию getnextstate, чтобы просмотреть специальные предметы.

Итак, вот реализация. Вы можете указать файл, содержащий только CELLALIVEMARKER, CELLDEADMARKER и пробелы.Если длина для плоской матрицы не соответствует параметру width/height для FIELD, она просто подставляет случайные значения.

#!/bin/bash 

# system values 
BASENAME="/usr/bin/basename" 
ECHO="/bin/echo" 
SLEEP="/bin/sleep" 
TPUT="/usr/bin/tput" 
GREP="/bin/grep" 
WC="/usr/bin/wc" 
CAT="/bin/cat" 

if [ "${#}" != "4" -a "${#}" != "5" ]; then 
    ${ECHO} "USAGE: ./$(${BASENAME} ${0}) FIELDWIDTH FIELDHEIGHT RULESALIVE RULESDEAD [LINSTARTMATRIX]" 
    ${ECHO} "EXAMPLES: ./$(${BASENAME} ${0}) 50   50   \"2 3\" \"3\"" 
    ${ECHO} "   ./$(${BASENAME} ${0}) 50   50   \"2 3\" \"3\"" init.mtx 
    exit 
fi 

# field values 
FWIDTH=${1} 
FHEIGHT=${2} 
# number of living neighbours for a living cell to stay alive in the next generation 
RULESALIVE=($(${ECHO} ${3})) 
# number of living neighbours for a dead cell to become alive in the next generation 
RULESDEAD=($(${ECHO} ${4})) 
CELLALIVEMARKER="o" 
CELLDEADMARKER="." 
FIELD=() # flatted matrix representation 
# if there are just marker values or spaces in the file it is a valid one 
${CAT} ${5} | ${GREP} -oq '[^\'${CELLALIVEMARKER}'\'${CELLDEADMARKER}'\ ]' 
isvalid="${?}" 
if [ "${5}" != "" ] && [ "${isvalid}" == "1" ]; then 
    FIELD=($(${CAT} ${5})) 
    # fill up with randoms if the length won't fit the dimension parameters 
    if [ "${#FIELD[@]}" != "$((${FWIDTH}*${FHEIGHT}))" ]; then 
    ${ECHO} "I: Padding matrix with random values." 
    # fill up field with randoms if its too short 
    for((i=${#FIELD[@]}; i<${FWIDTH}*${FHEIGHT}; i=$((${i}+1)))); do 
     cell="${CELLALIVEMARKER}" 
     alive=$((${RANDOM}%2)) 
     if [ "x${alive}" == "x1" ]; then 
     cell="${CELLDEADMARKER}" 
     fi 
     FIELD[${#FIELD[@]}]="${cell}" 
    done 
    fi 
else 
    # fill random field 
    for((i=0; i<${FWIDTH}*${FHEIGHT}; i=$((${i}+1)))); do 
    cell="${CELLALIVEMARKER}" 
    alive=$((${RANDOM}%2)) 
    if [ "x${alive}" == "x1" ]; then 
     cell="${CELLDEADMARKER}" 
    fi 
    FIELD[${#FIELD[@]}]="${cell}" 
    done 
fi 

# evaluate rules and get the next state for the cell 
getnextstate() { 
    local i="${1}" # row 
    local j="${2}" # col 
    local neighbours="" 

    # left upper 
    if [ "${i}" -eq "0" -a "${j}" -eq "0" ]; then 
    neighbours="${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}+1)))]}" 
    # right upper 
    elif [ "${i}" -eq "0" -a "${j}" -eq "$((${FWIDTH}-1))" ]; then 
    neighbours="${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]}" 
    # left bottom 
    elif [ "${i}" -eq "$((${FHEIGHT}-1))" -a "${j}" -eq "0" ]; then 
    neighbours="~${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}-1)*${FWIDTH})+(${j}+1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]}" 
    # right bottom 
    elif [ "${i}" -eq "$((${FHEIGHT}-1))" -a "${j}" -eq "$((${FWIDTH}-1))" ]; then 
    neighbours="?${FIELD[$((((${i}-1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]}" 
    # upper 
    elif [ "${i}" -eq "0" -a "${j}" -gt "0" ]; then 
    neighbours="-${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}+1)))]}" 
    # bottom 
    elif [ "${i}" -eq "$((${FHEIGHT}-1))" -a "${j}" -gt "0" ]; then 
    neighbours="=${FIELD[$((((${i}-1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}-1)*${FWIDTH})+(${j}+1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]}" 
    # right 
    elif [ "${i}" -gt "0" -a "${j}" -eq "0" ]; then 
    neighbours="#${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}-1)*${FWIDTH})+(${j}+1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}+1)))]}" 
    # left 
    elif [ "${i}" -gt "0" -a "${j}" -eq "$((${FWIDTH}-1))" ]; then 
    neighbours="_${FIELD[$((((${i}-1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]}" 
    # center 
    else 
    neighbours="@${FIELD[$((((${i}-1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}-1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}-1)*${FWIDTH})+(${j}+1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}-1)))]} ${FIELD[$(((${i}*${FWIDTH})+(${j}+1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}-1)))]} ${FIELD[$((((${i}+1)*${FWIDTH})+${j}))]} ${FIELD[$((((${i}+1)*${FWIDTH})+(${j}+1)))]}" 
    fi 

    # count neighbours alive 
    ncnt=$(${ECHO} ${neighbours} | ${GREP} -o ${CELLALIVEMARKER} | ${WC} -l) 
    # evaluate rules 
    local next="" 
    if [ "${FIELD[$(((${i}*${FWIDTH})+${j}))]}" == "${CELLALIVEMARKER}" ] && [[ "$(${ECHO} ${RULESALIVE[@]})" =~ ${ncnt} ]]; then 
    next="${CELLALIVEMARKER}" 
    elif [ "${FIELD[$(((${i}*${FWIDTH})+${j}))]}" == "${CELLDEADMARKER}" ] && [[ "$(${ECHO} ${RULESDEAD[@]})" =~ ${ncnt} ]]; then 
    next="${CELLALIVEMARKER}" 
    else 
    next="${CELLDEADMARKER}" 
    fi 
    ${ECHO} ${next} 
} 

firstrun=1 
while [ true ]; do 
    # print lines 
    FIELD_UPDATE=() 

    for((i=0; i<${FHEIGHT}; i=$((${i}+1)))); do 
    line="" 
    # calculate lines 
    for((j=0; j<${FWIDTH}; j=$((${j}+1)))); do 
     if [ "${firstrun}" == "1" ]; then 
     line="${line}${FIELD[$(((${i}*${FWIDTH})+${j}))]} " 
     # start calculation just after the first print 
     elif [ "${firstrun}" == "0" ]; then 
     line="${line}$(getnextstate ${i} ${j}) " 
     fi 
    done 
    FIELD_UPDATE=($(${ECHO} ${FIELD_UPDATE[@]}) $(${ECHO} ${line})) 
    ${ECHO} ${line} 
    done 
    FIELD=(${FIELD_UPDATE[@]}) 
    ${SLEEP} 2 
    # refresh lines in the field 
    for((i=0; i<${FHEIGHT}; i=$((${i}+1)))); do 
    # refresh lines 
    ${TPUT} cuu1 
    ${TPUT} el 
    done 
    firstrun=0 
done 

Так считывании файла init.mtx, содержащий следующую матрицу

. o . . . . . . . . 
. . o . . . . . . . 
o o o . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 

вы можете создать простой планер (от верхнего левого угла в нижний правый)

. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . . . 
. . . . . . . . o o 
. . . . . . . . o o 

с использованием Конвея по умолчанию, выполнив этот сценарий следующим образом:

./gameoflife 10 10 "2 3" "3" init.mtx 

Надеюсь, это поможет. И btw было забавно реализовать это в bash :)

+0

в настоящее время это просто реализация forwrd. поэтому, если кто-то знает, как указать некоторые значения производительности, пожалуйста, дайте мне знать :) –

+0

Вау, я не исключал всеобъемлющего ответа duch! Большое спасибо, рад Это было весело для вас :-). – learningHappily

+1

Не обижайтесь, но ваш стиль очень плох. Пожалуйста, изучите современные техники Bash - вы знаете, так что мы не спам в Интернете с помощью антикварных и сломанных методов оболочки для будущих поколений. Могу ли я указать вам на [BashFAQ] (http://mywiki.wooledge.org/BashFAQ) и [Bash Guide] (http://mywiki.wooledge.org/BashGuide)? –

Смежные вопросы