Snake [ALFA] em Shell Script

Publicado por marcelo cavalca filho (última atualização em 30/09/2013)

[ Hits: 6.780 ]

Homepage: https://www.facebook.com/Colgatera

Download snake.bsh

Download 1380562200.snake.bsh (versão 2)




Fala pessoal do VOL!

Como treinamento de lógica avançada de programação estou tentando recriar jogos clássicos sem olhar nenhum código fonte (ou seja, fazer na raça mesmo :P).
Com isso decidi que meu primeiro jogo seria nada mais nada menos do que aquele que todos jogavam nos seus "Tijorolas": Snake!
Sim, aquela cobra simpática que vai aumentando conforme come a ração. :P

Estou há aproximadamente 3 meses mexendo no GNU/Linux e aprendendo Shell Script desde então. Nunca havia visto nenhum dos dois antes. :o

Enfim, segue abaixo o meu código, lembrando que ainda está na VERSÃO ALFA, portanto, existem mil e um bugs que ainda precisam ser encontrados e resolvidos.

Obs.: para movimentar-se utilize as teclas WASD (para quem não conhece: W - cima; A - esquerda; S - baixo; D - direita).
Espero que gostem e me deem um feedback, Cya. :D

  



Versões atualizadas deste script

Versão 2 - Enviado por marcelo cavalca filho em 30/09/2013

Changelog: (ATUALIZADO)

Essa é a melhor versão de Snake feita em Shell Script (ou não hehe), ela agora não possui erros e é bem mais enxuta que a anterior.
Me digam o que acharam e no que ainda posso melhorá-lo.
Meu próximo jogo a ser feito em shell será TETRIS (também a partir do zero :P)

Download 1380562200.snake.bsh


Esconder código-fonte

#!/bin/bash

#=====================================================================================#
#
#       Programa: snake.bsh
#
#       Autor:    Cavalca Filho, Marcelo (15/09/2013)
#
#       Objetivo: Jogo snake em shell :D
#
#=====================================================================================#
#
#       Versão:   1.0 - 19/09/2013
#         > Utiliza um vetor para exibir a cobra e reinicia o mapa inteiro a
#           cada movimento.
#
#         1.1 - 21/09/2013
#         > Ideia para criação de uma nova lógica pois a versão 1.0 trava mais
#           conforme a cobra vai aumentando.
#           Agora a cobra se move sozinha com o tempo, não necessitando o
#           "readkey" para cada próximo movimento.
#
#         1.2 - 22/09/2013
#         > Lógica melhorada para a posição da "nova comida" pois antes gerava
#           comida nas bordas ou dentro da cobra, bugando o jogo.
#
#         1.3 - 25/09/2013
#         > Versão alfa completa!
#           Agora o jogo não gera "lag" devido ao aumento da cobra pois com a
#           nova lógica a cobra não é uma matriz e sim o próprio terreno, por
#           assim dizer.
#           Pode parecer simples mas a lógica para a movimentação da cobra
#           conforme ela faz curvas é um pouco complexa. Leia o apêndice abaixo
#
#=====================================================================================#

#=============================== CURVAS: COMO LIDAR ==================================#
#
#       O grande problema no início foi que a cobra era uma matriz e o terreno
#   era outra. Isso gerava um pequeno "lag" conforme a cobra aumentava. Foi pensada
#   então em uma lógica que fizesse com que a cobra fosse parte do terreno.
#   O maior empecilho para isso foi quando ela se curvava: Para onde o "rabo" dela
#   vai quando ela vira? E se ela fizer mais de uma curva?
#   Então foi pensada na seguinte lógica: Existe uma matriz que guarda as curvas
#   feitas. Existe também um vetor que vai indicar qual o próximo movimento da
#   cabeça e qual o próximo movimento do rabo. Uma posição de CURVA armazena a
#   quantidade de curvas que ela ainda tem de realizar. Conforme ela vai fazendo
#   as curvas esse valor é decrementado e o vetor é renovado para que ele saiba
#   para onde ir na próxima curva.
#
#   ex.: a cobra vira:
#   baixo - direita - cima - direita - baixo
#    [4]  -   [1]   - [3]  -   [1]   -  [4]
#
#   CURVA[0,0]= 5 (total)
#   CURVA[0,1]= 4 (valor de "baixo")
#
#   O rabo encontrou a posição onde ele deve ir para baixo logo a matriz será
#   atualizada para:
#
#   CURVA[0,0]= 4
#   CURVA[0,1]= 1 (valor de "direita")
#
#   Portanto, na próxima curva que ela chegar ela saberá que tem de ir para a
#   direita, e depois "cima", e por aí vai :)
#
#
#   tl;dr: Tem uma matriz que guarda o movimento da próxima curva. Ao chegar lá
#          essa matriz é atualizada para os valores das outras curvas.
#
#=====================================================================================#

#================================= GERAR TERRENO =====================================#

clear
# Declaração das matrizes do campo
declare -A CAMPO
declare -A CURVA
# Aqui voce pode alterar a altura e a largura conforme desejar
# ___________
#|           |
   HEIGHT=15        # ALTURA
   WIDTH=30     # LARGURA
#|___________|

# Verdadeira geração do terreno. Ele irá colocar o campo na tela e inserir os caracteres
# para uma boa visualização do mesmo.
let m=${HEIGHT}+1
let n=${WIDTH}+1
let i=$n
while [ $i -ge 0 ]; do
    let j=$m
    while [ $j -ge 0 ]; do
    if [ $i -eq 0 ] || [ $j -eq 0 ] || [ $i -eq $n ] || [ $j -eq $m ]; then
        if [ $i -eq 0 ] && [ $j -eq 0 ]; then
        tput cup $j $i
        echo -ne "#"
        elif [ $i -eq $n ] && [ $j -eq 0 ]; then
        tput cup $j $i
        echo -ne "#"
        elif [ $i -eq $n ] && [ $j -eq $m ]; then
        tput cup $j $i
        echo -ne "#"
        elif [ $i -eq 0 ] && [ $j -eq $m ]; then
        tput cup $j $i
        echo -ne "#"
        elif [ $i -eq 0 ] || [ $i -eq $n ]; then
        tput cup $j $i
        echo -ne "‖"
        else
        tput cup $j $i
        echo -ne "="
        fi
        CAMPO[$i,$j]=1
#       echo -ne "█"
    else
        CAMPO[$i,$j]=0
    fi
    let j=$j-1
    done
    let i=$i-1
done

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


#=============================== INICIALIZAR VARIAVEIS ===============================#

DIFIC=0.05          # Dificuldade ou velocidade de atualização do movimento

# [!] De preferência não alterar as variáveis abaixo, a menos que saiba o que esteja
#     fazendo
# _____________________________________________________________________________________
#|                                                                                     |
    y[0]=1
    x[0]=2
    x[1]=1
    y[1]=1
    CURVA[0,0]=0
    m=$((RANDOM%${WIDTH}+1))    # Gerando...
    n=$((RANDOM%${HEIGHT}+1))   # ...a primeira...
    CAMPO[$m,$n]=2          # ...bolinha dentro da caixa.
    LASTMOV[0]=1            # A cobra andará para a direita primeiramente.
    LASTMOV[1]=1
    PONT=0
    INPUT="d"           # Inicialização da entrada.
    WHATDO="d"          # Qual movimento será o primeiro.
    REVDO="a"           # Reverso do primeiro movimento para evitar cobra louca.
    LOSTGAME=false          # Detecção de game perdido.
    tput civis
    tput cup $n $m
    echo -ne "*"
    tput cup ${y[0]} ${x[0]}
    echo -ne "ᕙ"
    tput cup ${y[1]} ${x[1]}
    echo -ne "∎"
    let n=$HEIGHT+4         # Exibição dos controles.
    tput cup $n 1

#|_______________________________________________________________________________________|


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

read -s -n 1 INPUT
while :; do
    let n=$HEIGHT+2
    tput cup $n 3
    stty echo
    echo "   X[0]=${x[0]}     Y[0]=${y[0]}      "
    echo "     PONTUACAO = $PONT     "
    stty -echo
    read -s -t ${DIFIC} -n 1 INPUT
    if [ $INPUT = "d" ] 2>&- || [ $INPUT = "a" ] 2>&- || [ $INPUT = "s" ] 2>&- || [ $INPUT = "w" ] 2>&- ; then
    if [ $INPUT != $WHATDO ] && [ $INPUT != $REVDO ]; then
        WHATDO=$INPUT
    fi
    fi

    if [ ${CAMPO[${x[1]},${y[1]}]} -eq 3 ]; then
    CAMPO[${x[1]},${y[1]}]=1
    LASTMOV[1]=${CURVA[0,1]}
    let CURVA[0,0]=${CURVA[0,0]}-1
    CURVA[1,0]=1
    while [ ${CURVA[1,0]} -le ${CURVA[0,0]} ]; do
        let CURVA[1,1]=${CURVA[1,0]}+1
        CURVA[0,${CURVA[1,0]}]=${CURVA[0,${CURVA[1,1]}]}
        CURVA[0,${CURVA[1,1]}]=0
        let CURVA[1,0]=${CURVA[1,0]}+1
    done
    fi

    if [ $WHATDO = "d" ]; then
        let m=${x[0]}+1
        let n=${y[0]}
        REVDO="a"
        if [ ${LASTMOV[0]} -ne 1 ]; then
        CAMPO[${x[0]},${y[0]}]=3
        let CURVA[0,0]=${CURVA[0,0]}+1
        CURVA[0,${CURVA[0,0]}]=1
        fi
        case ${CAMPO[$m,$n]} in
        0 ) CAMPO[${x[1]},${y[1]}]=0
            tput cup ${y[1]} ${x[1]}
            stty echo
            echo -ne " "
            case ${LASTMOV[1]} in
            1) let x[1]=${x[1]}+1;;
            2) let x[1]=${x[1]}-1;;
            3) let y[1]=${y[1]}-1;;
            4) let y[1]=${y[1]}+1;;
            esac
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            x[0]=$m
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕙ"
            stty -echo ;;
        1 ) LOSTGAME=true
            break ;;
        2 ) let PONT=$PONT+1
            stty echo
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            x[0]=$m
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕙ"
            NOYOUCANT=true
            while [ $NOYOUCANT = true ]; do
            m=$((RANDOM%${WIDTH}+1))
            n=$((RANDOM%${HEIGHT}+1))
            if [ ${CAMPO[$m,$n]} -eq 0 ]; then
                tput cup $n $m
                NOYOUCANT=false
            fi
            done
            CAMPO[$m,$n]=2
            echo -ne "*"
            stty -echo ;;
        3 ) LOSTGAME=true
            break ;;
        esac
        LASTMOV[0]=1
    fi

    if [ $WHATDO = "a" ]; then
        let m=${x[0]}-1
        let n=${y[0]}
        REVDO="d"
        if [ ${LASTMOV[0]} -ne 2 ]; then
        CAMPO[${x[0]},${y[0]}]=3
        let CURVA[0,0]=${CURVA[0,0]}+1
        CURVA[0,${CURVA[0,0]}]=2
        fi
        case ${CAMPO[$m,$n]} in
        0 ) CAMPO[${x[1]},${y[1]}]=0
            tput cup ${y[1]} ${x[1]}
            stty echo
            echo -ne " "
            case ${LASTMOV[1]} in
            1) let x[1]=${x[1]}+1;;
            2) let x[1]=${x[1]}-1;;
            3) let y[1]=${y[1]}-1;;
            4) let y[1]=${y[1]}+1;;
            esac
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            x[0]=$m
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕗ"
            stty -echo ;;
        1 ) LOSTGAME=true
            break ;;
        2 ) let PONT=$PONT+1
            stty echo
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            x[0]=$m
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕗ"
            NOYOUCANT=true
            while [ $NOYOUCANT = true ]; do
            m=$((RANDOM%${WIDTH}+1))
            n=$((RANDOM%${HEIGHT}+1))
            if [ ${CAMPO[$m,$n]} -eq 0 ]; then
                tput cup $n $m
                NOYOUCANT=false
            fi
            done
            CAMPO[$m,$n]=2
            echo -ne "*"
            stty -echo ;;
        esac
        LASTMOV[0]=2
    fi

    if [ $WHATDO = "w" ]; then
        let m=${x[0]}
        let n=${y[0]}-1
        REVDO="s"
        if [ ${LASTMOV[0]} -ne 3 ]; then
        CAMPO[${x[0]},${y[0]}]=3
        let CURVA[0,0]=${CURVA[0,0]}+1
        CURVA[0,${CURVA[0,0]}]=3
        fi
        case ${CAMPO[$m,$n]} in
        0 ) CAMPO[${x[1]},${y[1]}]=0
            tput cup ${y[1]} ${x[1]}
            stty echo
            echo -ne " "
            case ${LASTMOV[1]} in
            1) let x[1]=${x[1]}+1;;
            2) let x[1]=${x[1]}-1;;
            3) let y[1]=${y[1]}-1;;
            4) let y[1]=${y[1]}+1;;
            esac
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            y[0]=$n
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕓ"
            stty -echo ;;
        1 ) LOSTGAME=true
            break ;;
        2 ) let PONT=$PONT+1
            stty echo
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            y[0]=$n
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕓ"
            NOYOUCANT=true
            while [ $NOYOUCANT = true ]; do
            m=$((RANDOM%${WIDTH}+1))
            n=$((RANDOM%${HEIGHT}+1))
            if [ ${CAMPO[$m,$n]} -eq 0 ]; then
                tput cup $n $m
                NOYOUCANT=false
            fi
            done
            CAMPO[$m,$n]=2
            echo -ne "*"
            stty -echo ;;
        esac
        LASTMOV[0]=3
    fi

    if [ $WHATDO = "s" ]; then
        let m=${x[0]}
        let n=${y[0]}+1
        REVDO="w"
        if [ ${LASTMOV[0]} -ne 4 ]; then
        CAMPO[${x[0]},${y[0]}]=3
        let CURVA[0,0]=${CURVA[0,0]}+1
        CURVA[0,${CURVA[0,0]}]=4
        fi
        case ${CAMPO[$m,$n]} in
        0 ) CAMPO[${x[1]},${y[1]}]=0
            tput cup ${y[1]} ${x[1]}
            stty echo
            echo -ne " "
            case ${LASTMOV[1]} in
            1) let x[1]=${x[1]}+1;;
            2) let x[1]=${x[1]}-1;;
            3) let y[1]=${y[1]}-1;;
            4) let y[1]=${y[1]}+1;;
            esac
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            y[0]=$n
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕕ"
            stty -echo ;;
        1 ) LOSTGAME=true
            break ;;
        2 ) let PONT=$PONT+1
            stty echo
            tput cup ${y[0]} ${x[0]}
            echo -ne "∎"
            y[0]=$n
            CAMPO[${x[0]},${y[0]}]=1
            tput cup ${y[0]} ${x[0]}
            echo -ne "ᕕ"
            NOYOUCANT=true
            while [ $NOYOUCANT = true ]; do
            m=$((RANDOM%${WIDTH}+1))
            n=$((RANDOM%${HEIGHT}+1))
            if [ ${CAMPO[$m,$n]} -eq 0 ]; then
                tput cup $n $m
                NOYOUCANT=false
            fi
            done
            CAMPO[$m,$n]=2
            echo -ne "*"
            stty -echo ;;
        esac
        LASTMOV[0]=4
    fi

    if [ $INPUT = "q" ] 2>/dev/null; then
        clear
        tput cnorm
        exit 0
    fi
done
if [ $LOSTGAME ]; then
    clear
    echo "Voce perdeu! Conseguiu um total de $PONT pontos!"
    echo
fi
tput cnorm
exit

Scripts recomendados

Instalador de novas versões do Java no Debian

Jogo de labirinto feito em shell script

Script para listar tentativas de acessos

Instalar WebCam LG LIC-110 e derivadas

Listas em shell


  

Comentários
[1] Comentário enviado por lcavalheiro em 26/09/2013 - 14:34h

Do caralho, meus parabéns!

[2] Comentário enviado por colgatera em 26/09/2013 - 14:44h

Obrigado @Icavalheiro :D espero que tenha mesmo gostado :)

O grande bug dele são essas letras que aparecem no meio do mapa devido ao shell estar sempre lendo o teclado(creio eu), ainda não encontrei um jeito de dar um 'silence' no teclado pra nao aparecer nada do que o usuario digita :/

[3] Comentário enviado por lcavalheiro em 26/09/2013 - 14:52h

Neste trecho
if [ $INPUT = "d" ] 2>&- || [ $INPUT = "a" ] 2>&- || [ $INPUT = "s" ] 2>&- || [ $INPUT = "w" ] 2>&- ; then
if [ $INPUT != $WHATDO ] && [ $INPUT != $REVDO ]; then
WHATDO=$INPUT
fi
fi

Inclua uma condição para jogar para /dev/null qualquer coisa digitada que não seja os comandos de movimento.

[4] Comentário enviado por colgatera em 30/09/2013 - 10:26h


[3] Comentário enviado por lcavalheiro em 26/09/2013 - 14:52h:

Neste trecho
if [ $INPUT = "d" ] 2>&- || [ $INPUT = "a" ] 2>&- || [ $INPUT = "s" ] 2>&- || [ $INPUT = "w" ] 2>&- ; then
if [ $INPUT != $WHATDO ] && [ $INPUT != $REVDO ]; then
WHATDO=$INPUT
fi
fi

Inclua uma condição para jogar para /dev/null qualquer coisa digitada que não seja os comandos de movimento.


Obrigado pela dica! Agora consegui anular as entradas do teclado hehe
Vou tentar postar a versão nova agora, que aliás está bem menor com relação a esta :D

[5] Comentário enviado por lcavalheiro em 30/09/2013 - 10:57h


[4] Comentário enviado por colgatera em 30/09/2013 - 10:26h:


[3] Comentário enviado por lcavalheiro em 26/09/2013 - 14:52h:

Neste trecho
if [ $INPUT = "d" ] 2>&- || [ $INPUT = "a" ] 2>&- || [ $INPUT = "s" ] 2>&- || [ $INPUT = "w" ] 2>&- ; then
if [ $INPUT != $WHATDO ] && [ $INPUT != $REVDO ]; then
WHATDO=$INPUT
fi
fi

Inclua uma condição para jogar para /dev/null qualquer coisa digitada que não seja os comandos de movimento.

Obrigado pela dica! Agora consegui anular as entradas do teclado hehe
Vou tentar postar a versão nova agora, que aliás está bem menor com relação a esta :D


Show de bola!


Contribuir com comentário