Bash-ты ойын жазу арқылы үйренеміз. 2048 ойыны



Bash скрипттары Linux жүйесінде көптеген жұмыстарды жеңілдетуі мүмкін. Бірақ bash программалық тіл болмағандықтан (жай командалық қабат) оның көптеген өзіне тән ерекшеліктері бар. Сондықтан, оны қолданудан алдын жақсылап үйренуге тура келеді. habrahabr сайтында bash-ты үйрену үшін өте көптеген ойындар жазылған (Шахмат, Quadronix, Xonix, Теңіз соғысы, Sokoban, керек десеңіз 3D шутер). Бірақ, менің байқағаным, ол жазылғандарды оқып шығу бір бөлек, ал өзіңе жазу бір бөлек. Өйткені шын жұмыста керек кезде бәрібір, жаңылысып отырасың. Сондықтан мен де үйрену үшін бір ойын жасауды шештім. Ең бастысы ол жай көшірме немесе аударма емес (қарап қоймау үшін :-)), жаңа ойын болуы керек. Сөйтіп, өзіме жақында пайда болған 2048 ойынын таңдап алдым. Өйткені, логикасы оңай және аз уақыттың ішінде жасауға болады.


Бұл қалай сонда деп отқандарға кішігірім демонстрация ;).


Алдымен bash-тың ерекшеліктерін қарастырып кетейік.

Айнымалылар
Bash-скрипттарында айнымалылар типтері деген түсінік жоқ. Айнымалыларды жариялау және мән беру келесі түрде анықталады:
user@localhost$ my_var # айнымалыны жариялау
user@localhost$ my_var=5 # айнымалыға мән беру

теңдік символының екі жағында бос орын қалдыруға болмайды, яғни айнымалыға былай мәр беру «my_var = 5» дұрыс емес. Айнымалыны оқу үшін, оның алдына "$" символы қойылады:

user@localhost$ echo $my_var # немесе ${my_var}
5
user@localhost$ echo "My variable value $my_var" # екі тырнақшаның ішінде пайдалануға болады
My variable 5

Айнымалыларды «declare» bash конструкциясы (builtin) арқылы типтеуге болады, бұл жақсы практикалардың бірі болып саналады:

user@localhost$ declare -r MY_CONST=3 # айнымалыны тек оқуға жариялау
user@localhost$ MY_CONST=4
-bash: MY_CONST: readonly variable

Осы секілді, бұл конструкцияның келесі кілттері бар:
-i — сандық тип
-a — массив
-l — айнамалы мәні тек іші регистрде болады
-u — айнамалы мәні тек үлкен регистрде болады

Функциялар
Функция келесі түрде анықталады:

function my_func() {
	:
}

Бірақ, аргументтерді жақша ішінде беруге болмайды, аргументтер командаға аргумен берген секілді берілед және функция ішінде $1, $2… айнымалыларында қол жетімді болады. Мысалы

function my_func() {
	echo "$1, $2!"
}

my_func "hello" "world"

нәтижесінде консолда «Hello, world!» мәтіні шығарылады. Функция ішінде declare-нің толық аналогы local пайдаланылады. Келесі ерекшелік өте маңызды:

i=3

function f() {
    echo $i
}

function g() {
    local i=5
    f
}

function h() {
    local i=10
    f
}

g
h

Нәтижесінде консольда 5 және 10 шығарылады.

Массивтер

my_arr=( 1 2 3 4 ) # массивті анықтау
${my_arr[0]} # массив элементін оқу
my_arr[2]=10 # массив элементінңің мәнін өзгерту
${#my_arr[*]} # массив элементтерінің саны

Bash-та екі өлшемді массив жоқ. Бірақ бір өлшемді массивті екі өлшемді ретінде пайдалануға болады. Мысалы:

my_arr=( 
	1 2 3
	4 5 6
	7 8 9
)

for (( i = 0; i < SIZE; i++ )); do
    for (( j = 0; j < SIZE; j++ )); do
        echo "${my_arr[i * 3 + j]}" # 3 баған саны
    done
done


Бұл қысқаша шолу болатын, егер қызығушылық болса әр тақырыптың өзі үлкен мақала болады. 2048 ойынының ережесін википедиядан алдым. Алдымен ойынға керек функцияларды анықтаймыз. Ойынның циклі келесі қадамдардан тұрады:
1. Пайдаланушы басқан бағытқа қарай плиткаларды ығыстыру
2. Плитка мәні 2048-ге жеткенін тексеру
3. Экранға плиткалардың жаңа мәндерін шығару
4. 1-қадамға қайта оралу

Бұл циклді орындайтын басты функцияны анықтаймыз:

function main() {
    stty -echo # echo өшіру үшін

    newGame # жаңа ойын бастаймыз

    while [[ true ]]; do
        action # плиткаларды ығыстыру
        if [[ $shifted = "true" ]]; then
            placeRandomTile # егер плиткалар ығысқан болса, кездейсоқ плитка қосу
        fi
        display # экранға шығарамыз

        if isYouWin; then # пайдаланушы жеңген жеңбегендігін тексереміз
            askWantContinue
            display
        fi
    done
}


Плитка мәндерін tiles массивінде сақтаймыз. newGame функциясы оның мәндерін тазалап отырады:

function newGame() {
    score=0
    shifted=false
    tiles=(
        0 0 0 0
        0 0 0 0
        0 0 0 0
        0 0 0 0
    )

    placeRandomTile; placeRandomTile; # ойын басында екі кездейсоқ плитка қосылады
    display
}


«action» функциясы пайдаланушының батырма басуын күтіп, плиткаларды керекті жақтарға ығыстырады:

function action() {
    shifted=false
    local -l key

    read -n 1 key
    case $key in
        $KEY_UP ) shiftToTop ;;
        $KEY_DOWN ) shiftToBottom ;;
        $KEY_LEFT ) shiftToLeft ;;
        $KEY_RIGHT ) shiftToRight ;;
    esac
}


shiftToTop, shiftToBottom, shiftToLeft, shiftToRight функциялары, сәйкесінше плиткаларды жоғарыға, төменге, солға, оңға ығыстырады. Ал ең үздік нәтижені файлда сақтаймыз. Ол нәтижені жазатын және оқитын функциялар:

function writeBestScore() {
    if [[ -w $BEST_SCORE_FILE ]]; then
        if (( score > bestScore )); then
            echo "$score" > $BEST_SCORE_FILE
        fi
    fi
}

function readBestScore() {
    if [[ -r $BEST_SCORE_FILE ]]; then
        bestScore=$(cat $BEST_SCORE_FILE)
    else
        bestScore=0
    fi
}


Қорытынды
Жазу барысында өте көп шалындым деуге болады, әдемі debugger болмағандықтан, қателіктер не үшін болып жатқанын түсіну қиын. Әсіресе массивтерде шатысып кетесің, толық соңына дейін жақсылап үйрену үшін тағы бір қиындау ойын жазу керек сияқты. Кодым мықты, таза деп мақтана алмайм, бірақ ойынды жазу кішкентай болсын тәжірибе берді.

Ойынды тағы толықтыруға болады, жасалмаған функционалдар:
1. Ойынның соңғы қалпын файлда сақтау
2. Соңғы жүрісті артқа қайтару
3. Пайдаланушының жеңілгендігін тексеру және оған хабарлау
4. Баған бойынша сандарды түзулеу

1 және 2-ші функционалды файлға жазу арқылы істеуге болады. Бірақ екі күн скриптті қарай беріп жалығып кеттім. Кодты келесі ресурстардан қарауға болады: pastebin және github. href=«asciinema.org/a/9614»>asciinema.org сайтындағы жазба.

Ойынды қосу үшін, келесі командаларды орындаңыз:

chmod +x game2048.bash
./game2048.bash

Ойыннан шығу үшін Ctrl+C батырмасын басыңыз.

P.S. Ойында бір үлкен қателік бар, көреміз кімнің бірінші байқайтынын :-)

P.P.S. Сайтта мақала жарияланбағанына көп болыпты, белсенді болайық, қазақша ресурстарды көтерейік достар )) kamyrov бауырымыз айтқандай "Қазақ айтишниктері АЛҒА!)"

UPDATE. Ең бастысын ұмытып кетіппін. Ойынның басқарылуы. Ойында плиткаларды ығыстыру үшін «w», «s», «a», «d» батырмаларын пайдаланыңыз. Олар, сәйкесінше «Жоғары», «Төмен», «Солға» және «Оңға» ығыстыру.

  • +2
1 пікір
kamyrov
Жарайсын Раимбек!) Cүйікті ойынымды командалық жолда ойнайтын болдық енді)
Тек тіркелген қолданушылар ғана пікір қалдыра алады.