Como ver se há algum caractere correspondente em uma string?

2

Estou fazendo um gerador manual de cartas e quero que ele anote se uma mão específica é gerada, como uma trinca ou um full house. Eu estava tentando descobrir como fazer isso, e pensei em usar o grep nas strings, mas percebi que teria que fazer muitas linhas e seria excessivamente repetitivo. Existe um arquivo que contém a última string gerada chamada out.txt .

Esta é uma saída do script:

 io@conroe$ ./card 5
   ♦ 6    ♦ Q    ♠Q    ♥J    ♣3

Eu gostaria de algo assim:

 io@conroe$ ./card 5
   ♦ 6    ♦ Q    ♠Q    ♥J    ♣3
   PAIR - QUEENS

O código é simples para mim, mas ainda estou tentando entender como fazer essa parte. (Agora, o que seria muito legal é se eu pudesse encontrar um conjunto de pequenos cartões de pixels e usá-los no lugar do texto.) Código:

#!/bin/bash
cat /dev/null > out.txt 
z=$( < out.txt)
for (( y=1; y<=$1; y++ ))
  do    
      < /dev/urandom LC_CTYPE=C tr -dc HDCS | head -c 1 | while read -n 1 s  
do
        case $s in
    D)
        printf '  \e[0;31;47m ♦ '
        ;;
    H)
        printf '  \e[0;31;47m ♥'
        ;;
    S)
        printf '  \e[0;30;47m ♠'
        ;;
    C)
        printf '  \e[0;31;47m ♣'
        ;;
esac


done
    < /dev/urandom LC_CTYPE=C tr -dc "1""2""3""4""5""6""7""8""9""10""J""Q""K""A" | head -c 1 | while read -n 1 n
do  
        if [ $n = "0" ]
    then
        echo -n '10 '
        echo -n '10 ' >> out.txt

        else
        echo -n "$n "
        echo -n "$n " >> out.txt

        fi

done
printf '\e[0m'
  done
  printf "\n"

EDIT: By the way, as seqüências de caracteres em out.txt se parecem com isso: 6 Q Q J 3

    
por 智障的人 23.06.2015 / 23:40

4 respostas

4
printf '%s\n' {♠,♣,♢,♡}$'\t'{{2..10},J,K,Q,A} | shuf -n5 |
  gawk 'BEGIN{ split(",Twos,Threes,Fours,Fives,Sixes,Sevens,Eights,Nines,Tens",vt,","); vt["J"]="Jacks"; vt["Q"]="Queens"; vt["K"]="Kings"; vt["A"]="Aces"; } # values-text
        { c[$2]++; printf("%s %s", $1, $2(NR==5?"\n":"\t")) }
        END{ for(i in c){
                 if( c[i]==2 ){ print "PAIR:  " vt[i]; cp++ }  
                 if( c[i]==3 ){ print "THREE: " vt[i]; ct++ }
                 if( c[i]==4 ){ print "FOUR:  " vt[i] } }
             if( cp==2  ) { print "TWO PAIRS" }
             if( cp&&ct ) { print "FULL HOUSE" } }'

Exemplo de saída:

♡ Q    ♣ A    ♢ A    ♢ Q    ♡ 2 
PAIR:  Aces
PAIR:  Queens
TWO PAIRS

Aqui é a mesma coisa feita inteiramente por awk , exceto pelo método de propagar o rand () do awk, usando $RANDOM do bash passado para o awk através da opção -v . A saída é idêntica à acima.

gawk -v seed=$RANDOM '
  BEGIN{srand(seed) 
        split("♠♣♢♡",s,"")  # suit: 1-4 
        split("A,2,3,4,5,6,7,8,9,10,J,Q,K",v,",")  # values: 1-13
        split(",Twos,Threes,Fours,Fives,Sixes,Sevens,Eights,Nines,Tens",vt,","); vt["A"]="Aces"; vt["J"]="Jacks"; vt["Q"]="Queens"; vt["K"]="Kings"; # values-text
        for(es in s){ for(ev in v){ sv[i++]=s[es]" "v[ev] }}; # 0-51
        imax=4; for(i=0;i<=imax;i++){              # pick 5 cards at random from array 'v'
          rix=int(rand()*(52-i))+i                 # ranges from 0-51 to 4-51, as per 'i'
          tmp=sv[i]; sv[i]=sv[rix]; sv[rix]=tmp    # swap ramdom value to front of array, as per 'i' 
          split(sv[i],fv," "); c[fv[2]]++          # increment face-value counts  
          printf("%s", sv[i](i==imax?"\n":"\t"))   # print the full hand in incremets
        }
        for(i in c){
            if( c[i]==2 ){ print "PAIR:  " vt[i]; cp++ }  
            if( c[i]==3 ){ print "THREE: " vt[i]; ct++ }
            if( c[i]==4 ){ print "FOUR:  " vt[i] } }
        if( cp==2  ) { print "TWO PAIRS" }
        if( cp&&ct ) { print "FULL HOUSE" }}'
    
por 24.06.2015 / 03:34
2

Eu geraria uma lista de todas as combinações possíveis, depois embaralhei e peguei o número de cartões (isso evita lidar com o mesmo cartão várias vezes). Para contar os mesmos naipes e classificações, usaria matrizes associativas.

Isso deve começar:

#!/bin/bash

count=$1
((count>52)) && exit 1 # Not enough cards.

cards=() # Create an array.
for suit in $'\e[0;31;47m ♦' $'\e[0;31;47m ♥' $'\e[0;30;47m ♠' $'\e[0;30;47m ♣' ; do
    for rank in {2..10} J Q K A ; do
        cards+=("$suit $rank"$' \e[0m') # All possible combinations.
    done
done

hand=($(
        for ((i=0; i<${#cards[@]}; i++)) ; do
            echo $i
        done | shuf -n "$count" # Pick random cards.
        ))

# Associative arrays to count occurrences.
declare -A suits
declare -A ranks
for card_i in "${hand[@]}" ; do
    card="${cards[card_i]}"
    echo "$card"
    (( suits[${card:11:1}]++ ))  # Extract the suit
    (( ranks[${card:13:-5}]++ )) # and rank, add one to count.
done

for s in "${!suits[@]}" ; do
    echo "$s ${suits[$s]}"
done 

echo

for r in "${!ranks[@]}" ; do
    echo "$r ${ranks[$r]}"
done 
    
por 24.06.2015 / 00:20
2

Resposta atrasada aos comentários do OP. Eu usei shuf como o choroba, mas o meu é cartões de codificação como inteiros de 0 a 51. Alternativamente, você pode utilizar a variável $RANDOM para testes casuais.

Você pode obter isso por meio do link

#!/bin/bash

# This version conforms to Bash 3.2.53 on OS X.

stab=('\e[0;31;47m ♦' '\e[0;31;47m ♥' '\e[0;30;47m ♠' '\e[0;30;47m ♣')
ntab=(2 3 4 5 6 7 8 9 10 J Q K A)

match() {
        for i; do
                printf "  ${stab[$((i/13))]}${ntab[$((i%13))]}\e[0m"
        done
        printf "\n"

        suits=(0 0 0 0)
        nums=(0 0 0 0 0 0 0 0 0 0 0 0 0)
        same2=()
        same3=()
        same4=()
        flush=-1
        straight=-1
        conseq=0
        for i; do
                ((suits[i/13]++))
                ((nums[i%13]++))
        done
        for ((i=0; i<4; i++)); do
                ((suits[i]==5)) && flush=$i
        done
        for ((i=0; i<13; i++)); do
                case ${nums[$i]} in
                2) same2+=($i) ;;
                3) same3+=($i) ;;
                4) same4+=($i) ;;
                esac
                if ((i>0)); then
                        conseq=$((nums[i]>0?(nums[i-1]>0?conseq+1:1):0))
                else
                        conseq=$((nums[i]>0?1:0))
                fi
                ((conseq==5)) && straight=$i
        done
        if ((${#same4[*]}>0)); then
                echo "FOUR OF A KIND - ${ntab[${same4[0]}]}"
        elif ((${#same3[*]}>0)); then
                if ((${#same2}>0)); then
                        echo "FULL HOUSE - ${ntab[${same3[0]}]} ${ntab[${same2[0]}]}"
                else
                        echo "THREE OF A KIND - ${ntab[${same3[0]}]}"
                fi
        elif ((${#same2[*]}>1)); then
                echo "TWO PAIR - ${ntab[${same2[1]}]} ${ntab[${same2[0]}]}"
        elif ((${#same2[*]}>0)); then
                echo "ONE PAIR - ${ntab[${same2[0]}]}"
        elif ((straight>=0)); then
                if ((flush>=0)); then
                        if ((straight==12)); then
                                echo "ROYAL FLUSH"
                        else
                                echo "STRAIGHT FLUSH"
                        fi
                else
                        echo "STRAIGHT"
                fi
        elif ((flush>=0)); then
                echo "FLUSH"
        else
                echo "NO PAIR"
        fi
}

# Tests
match 14 45 0 11 49  # NO PAIR
match 51 13 39 9 50  # ONE PAIR
match 34 21 1 11 50  # TWO PAIR
match 8 3 21 22 34   # THREE OF A KIND
match 51 24 36 9 21  # STRAIGHT
match 1 3 5 7 9      # FLUSH
match 5 18 31 15 28  # FULL HOUSE
match 10 9 22 35 48  # FOUR OF A KIND
match 1 2 3 4 5      # STRAIGHT FLUSH
match 12 11 10 9 8   # ROYAL FLUSH

# Random draw
#match $(shuf -e -n 5 {0..51})

shuf=({0..51})
cards=()
for i in 0 1 2 3 4; do
        j=$((RANDOM%${#shuf[*]}))
        cards+=(${shuf[$j]})
        unset shuf[$j]
done
match "${cards[@]}"

Saída:

$ ./card.sh 
   ♥3   ♣8   ♦2   ♦K   ♣Q
NO PAIR
   ♣A   ♥2   ♣2   ♦J   ♣K
ONE PAIR - 2
   ♠10   ♥10   ♦3   ♦K   ♣K
TWO PAIR - K 10
   ♦10   ♦5   ♥10   ♥J   ♠10
THREE OF A KIND - 10
   ♣A   ♥K   ♠Q   ♦J   ♥10
STRAIGHT
   ♦3   ♦5   ♦7   ♦9   ♦J
FLUSH
   ♦7   ♥7   ♠7   ♥4   ♠4
FULL HOUSE - 7 4
   ♦Q   ♦J   ♥J   ♠J   ♣J
FOUR OF A KIND - J
   ♦3   ♦4   ♦5   ♦6   ♦7
STRAIGHT FLUSH
   ♦A   ♦K   ♦Q   ♦J   ♦10
ROYAL FLUSH
   ♠J   ♠4   ♥A   ♠6   ♣4
ONE PAIR - 4

Spottings de bug, sugestões são bem-vindas.

    
por 24.06.2015 / 02:21
2

Então você definitivamente está errado quando faz:

tr -dc ... </dev/urandom

Você não precisa -d elete nada. Se o seu objetivo é aleatoriedade - então você deve usar tudo o que conseguir.

Por exemplo:

tr '
deck()( HOME=/dev/null; ${deck:+"echo"}
        tr=$(printf '[%s*19]' 1 2 3 4 5 6 7 8 9 a b c d)
        tr '
time (deck|wc -l)
-7' "[J*9]$tr" | dd cbs=1 obs=2 conv=unblock | paste -d'W\nX\nY\nZ' - ~ - ~ - ~ - ~ | sed ' /^J/d;1!G;/^\(..\).*/d h;s/\n/&/51;tq' -e'd;:q' -eq ) <"${1:-/dev/urandom}"
-7' '[H*64][D*64][C*64][S*]' </dev/urandom |...

... que sempre retornaria um [HDCS] w / out excluindo qualquer entrada e retornaria em um espectro de bytes de entrada aleatórios.

Eu escrevi uma função que irá preencher um baralho embaralhado:

52
( deck | wc -l; ) \
    0.03s user 0.04s system 224% cpu 0.028 total

Fazendo ...

draw()  if      [ -n "${1##*[!0-9]*}" ] || return 2
        then    case    $((${#deck}>($1*3)))$deck in
                (?*[!0-9W-Za-d[:space:]]*)
                        return 2;;
                (0*)    deck=$deck$(deck)
                        draw "$1";;
                (1*)    eval "  hand='$(echo "$deck" |
                                sed "$1 N;s/\n/' deck='/")'"
        esac;   fi

... imprime ...

show()  case    $1      in
        (*[!0-9W-Za-d[:space:]]*|'')    return 2;;
        (*) (   eval "  $(printf "T='\t' E=3 nl='\n'")"
                str(){  m=$1 l=\$2 r=\$i$3 d=......
                        set 9 8 7 6 5 4 3 2 1
                        for i in d c b a "$@"
                        do      [ "$i" = d ] && M=ROYAL || M=STRAIGHT M=${M#"$m"}
                                eval printf "\"\ts/.*$l$d$r.*/"'$M${M:+ }$m:/;t$a.1\n"'
                                [ "$1" = 1 ] && unset a r l d i m M && break
                        shift;  done; }
                knd(){  c=$1    m="$1 OF A KIND" IFS=$nl
                        shift;  s="$*"; set -f .'\1'
                        until   [ "$#" -gt "$((c-1))" ]
                                do set "$@" "$@"
                        done;   shift "$(($#-(c-1)))"
                        printf  %b "\t/\([a-d0-9]\)$@/{\n"
                        for     s in $s; do eval 'printf "\t\t%s\n" "'"$s\""; done
                        printf  "\tt$a.1\n\t}\n"; unset l c a s IFS; }
                br(){   case    $a.$1   in
                        (.*|*[!0-9]*.)  return 2;;
                        (*.-t)  printf  "\n:$a.0\n%s\n" \
                                        "$a!b${n:-$((a+1)).0}";;
                        (*.-b)  printf ":$a.1\n";;esac;shift
                        for s do eval 'printf "\t%s\n" "'"$s\"";done
                        unset n IFS a s; }
                for k in k1.2,1.2 k1.1,1.1 uk1.1,1.1
                do      echo "$1" | sort -"$k"; echo
                done|   sed -ne:n -e'$!N;s/\n\(.\)//;tn
                                x;/./!g;x;$G;s/\n$//p'  |
                sed -ne"$(      a=1 br -t
                                a=1 str FLUSH   '\(.\)' '\'
                                a=1 br -b       's/.*\([W-Z]\).......\1.*/FLUSH:/' /:/h /^\[RS]/be n
                                a=2 br -t
                                a=2 knd 4       s/.\*/\$m:/
                                a=2 knd 3       s/// '/\(.\)[W-Z]/!s/.*[W-Z]/$m:/' s//FULLHOUSE:/          
                                a=2 knd 2       s///2 tP 's/.*/$m:/' :P 's/.*[W-Z]/2 PAIR:/'
                                a=2 br -b       /^\[F4]/h x //h //be x /:/h n
                                n=e a=3 br -t
                                a=3 str STRAIGHT .
                                a=3 br -b       /:/h x h)
                :e" -e'5!n;5!be' -e'y/123456789abcd/234567891JQKA/
                s/\(.\)\([W-Z]\)/ '"$E[0;3;47m  $E[m /g"'
                s/W\([^ ]* \)/1♦ /g;s/X\([^ ]* \)/1♥ /g
                s/Y\([^ ]* \)/0♠ /g;s/Z\([^ ]* \)/0♣ /g
                s/ 1/10/g;x;s/.*[^:]//;/.\{8\}/!s/$/'"$T/;G;s/\n/$T/
                s/\([^m]*m\)\{26\} /&\$nl\$nl$T$T/g;s/[[:space:]]*$//p"
)       esac

deck() usa como padrão os dados aleatórios do linux /dev/urandom PRNG, mas se for chamado com um argumento, interpretará isso como um nome de arquivo para uma fonte alternativa de entrada aleatória.

E todas as cartas devolvidas - (uma por linha) - são únicas. Não se incomoda tentar randomizar os trajes e atribuí-los na ordem round-robin. Ele não precisa se preocupar: a ordem dos valores das cartas já é aleatória, e as cartas terão que ser removidas para uniques em uma ordem aleatória de qualquer maneira, e então o resultado da poda operação é ternos aleatórios.

sed realmente lida com isso. O que sed faz é:

  • %código%
    • limpar todos os Jokers (valores de byte /^J/d )
  • %código%
    • Em todas as linhas, mas a primeira é 0-$(((256%13)-1)) uma cópia do 1!G old space anexado ao espaço padrão.
  • %código%
    • Se houver outro cartão em sua pilha atual que corresponda ao cartão que acabou de comprar, G elimina o espaço padrão e não salva nada ...
    • ... caso contrário, se a linha atual for única, copiará a pilha atual para h old space.
    • A primeira linha é sempre /^\(.*\)\n.*/d;h eld.
  • %código%
    • Se houver 51 d ewlines no espaço padrão nesse momento, h h uits entrada e imprime o deck para stdout ...
    • ... senão /\(.*\n\)\{51\}/q;d elimina o espaço padrão e não imprime nada.

Agora, se você quiser \n ...

1 2 3 4 5 6 7 8 9  a b c d
2 3 4 5 6 7 8 9 10 J Q K A

... que é uma função que preencheria automaticamente as variáveis atuais do shell sed e q conforme necessário. Ele puxa da parte superior de d o número de cartões solicitados em draw e coloca esses cartões em $hand . $deck é aparado a partir do topo de cada vez. Se $deck for chamado e $1 não for suficientemente grande o suficiente para preencher $hand conforme solicitado, então $deck será reabastecido com um novo% embaralhado draw() .

E por último:

W X Y Z
♦ ♥ ♠ ♣

Em todos os $deck e $hand , os cartões são armazenados em ordem de classificação como esta:

sort -k1.1,1.1

E os fatos também ...

ROYAL FLUSH
STRAIGHT FLUSH
4 OF A KIND
FULLHOUSE
FLUSH
STRAIGHT
3 OF A KIND
2 PAIR
2 OF A KIND

Isso facilita - cada cartão tem 2 bytes e eles sempre serão classificados de maneira apropriada. Os cartões em $deck e $deck são todos deck() ewline delimited - always. Então, em draw() , a parte mais difícil é feita simplesmente como ...

y/123456789abcd/234567891JQKA/

... onde ordenamos a mão no primeiro byte de cada linha. O resto é apenas comparar - que $hand faz ... um lote de. Ele lida com 2,3,4 de um tipo, fullhouse, 2 pares, royal straight, straight, royal flush e straight flush. Ele preferirá informar sobre eles na seguinte ordem, independentemente de quantos cards forem encontrados em seu primeiro argumento:

tr -dc ... </dev/urandom

Observe que a função $deck não está necessariamente vinculada a nenhum dos outros dois - ela pode ser chamada com um argumento gerado de qualquer maneira que coincida com o esquema de codificação mencionado acima e produzirá a saída desejada. Cada uma das funções pode se sustentar de maneira modular quando / se necessário.

Note também que não há limite de 5 cartas para qualquer uma das três funções - elas devem lidar com mãos de qualquer tamanho. E todos eles são projetados para trabalhar com \n persistente (com verificação de erros) - e, portanto, podem ser usados com algum nível de persistência.

É no final de show() que a codificação é decodificada . Todos os valores codificados são convertidos em uma única ação:

tr '
deck()( HOME=/dev/null; ${deck:+"echo"}
        tr=$(printf '[%s*19]' 1 2 3 4 5 6 7 8 9 a b c d)
        tr '
time (deck|wc -l)
-7' "[J*9]$tr" | dd cbs=1 obs=2 conv=unblock | paste -d'W\nX\nY\nZ' - ~ - ~ - ~ - ~ | sed ' /^J/d;1!G;/^\(..\).*/d h;s/\n/&/51;tq' -e'd;:q' -eq ) <"${1:-/dev/urandom}"
-7' '[H*64][D*64][C*64][S*]' </dev/urandom |...

Acontece imediatamente em uma única tradução de sed sem qualquer perigo de editar o valor por engano duas vezes.

Sua saída é semelhante a:

    
por 24.06.2015 / 17:52