문자열에 일치하는 문자가 있는지 확인하는 방법은 무엇입니까?

문자열에 일치하는 문자가 있는지 확인하는 방법은 무엇입니까?

저는 핸드 생성기를 만들고 있는데 3카드 핸드나 풀 하우스와 같은 특정 핸드가 생성되었는지 기록할 수 있기를 원합니다. 이 작업을 수행하는 방법을 알아내려고 노력 중이고 문자열에 grep을 사용하는 방법도 생각했지만 많은 줄을 만들어야 하고 지나치게 반복적이라는 것을 깨달았습니다. 이라는 마지막 생성된 문자열을 보유하는 파일이 있습니다 out.txt.

다음은 스크립트의 출력입니다.

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

나는 다음과 같은 것을 원합니다 :

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

코드가 단순해 보이지만 여전히 이 부분을 완료하는 방법을 찾으려고 노력 중입니다. (이제 작은 픽셀 카드 그래픽 세트를 찾아 텍스트 대신 사용할 수 있다면 정말 좋을 것 같습니다.) 코드:

#!/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"

편집: 그런데 문자열은 out.txt다음과 같습니다. 6 Q Q J 3

답변1

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" } }'

출력 예:

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

bash가 options 를 통해 awk에 전달된 awk의 rand() 시드 메서드를 사용한다는 awk점을 제외하면 여기서는 정확히 동일한 작업이 수행됩니다. 출력은 위와 동일합니다. $RANDOM-v

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" }}'

답변2

가능한 모든 조합의 목록을 생성한 다음 이를 섞고 주어진 수의 카드를 선택합니다(이렇게 하면 동일한 카드를 여러 번 처리하는 것을 방지할 수 있습니다). 동일한 슈트와 순위를 계산하기 위해 연관 배열을 사용하겠습니다.

이렇게 하면 시작됩니다.

#!/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 

답변3

OP의 의견에 대한 답변이 지연되었습니다. 나는 shuf초로바를 사용하고 있지만 내 카드는 0에서 51 사이의 정수로 인코딩됩니다. 또는 $RANDOM임시 테스트에 변수를 활용할 수도 있습니다 .

이 정보는 다음을 통해 얻을 수 있습니다.https://gist.github.com/yaegashi/57065723166e3a72b79e

#!/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[@]}"

산출:

$ ./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

버그가 발견되었습니다. 제안을 환영합니다.

답변4

따라서 이 작업을 수행하면 확실히 오류가 발생합니다.

tr -dc ... </dev/urandom

-d아무것도 삭제할 필요가 없습니다 . 당신의 목표가 무작위성이라면, 당신은 얻은 모든 것을 사용해야 합니다.

예를 들어:

tr '\0-\377' '[H*64][D*64][C*64][S*]' </dev/urandom |...

[HDCS] ...항상 입력을 제거하지 않고 하나를 반환하고 이를 임의 입력 바이트의 확산 스펙트럼으로 반환합니다.

섞인 덱을 채우는 함수를 작성했습니다.

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

행위...

time (deck|wc -l)

...인쇄...

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

deck()기본값은 Linux /dev/urandomPRNG에서 임의의 데이터를 추출하는 것이지만 인수를 사용하여 호출하면 이를 임의 입력의 대체 소스의 파일 이름으로 해석합니다.

모든 카드가 반환되었습니다.(한 줄에 하나씩)- 독특해요. 슈트를 무작위화하여 라운드 로빈 순서로 할당하려고 시도하지 않습니다. 하지만 사실은 그렇지 않아요걱정해야 해: 카드 값의 순서는 이미 무작위이고, 어쨌든 카드는 무작위 순서로 고유하게 가지치기 작업을 수행해야 하므로 가지치기 작업의 결과는 무작위입니다.

sed이 문제는 실제로 처리될 수 있습니다. 이것이 하는 일은 sed:

  • /^J/d
    • 모든 광대 지우기(바이트 값 0-$(((256%13)-1)))
  • 1!G
    • 첫 번째 줄을 제외한 모든 줄에서 G이전 공간의 복사본을 h패턴 공간에 추가합니다.
  • /^\(.*\)\n.*\1/d;h
    • 현재 스택에 방금 가져온 카드와 일치하는 다른 카드가 있으면 d패턴 공간이 삭제되고 아무것도 저장되지 않습니다.
    • ...그렇지 않으면 현재 행이 지금까지 고유한 경우 현재 스택을 h이전 공간에 복사합니다.
    • 첫 번째 줄은 항상 h필드입니다.
  • /\(.*\n\)\{51\}/q;d
    • \n당시 패턴 공간에 51개의 유라인이 있으면 sed q입력을 가져와 데크를 표준 ​​출력으로 인쇄합니다...
    • ...그렇지 않으면 d패턴 공간이 제거되고 아무것도 인쇄되지 않습니다.

이제 당신이 원한다면 draw...

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

...이 함수는 현재 쉘 변수 $hand와 . 요청한 카드 수 중 위에서부터 카드를 뽑아서 $deck넣습니다. 매번 위에서부터 다듬습니다. 호출했는데 요청한 대로 채울 만큼 충분하지 않은 경우 먼저 섞인 새 카드로 보충됩니다.$deck$1$hand$deckdraw()$deck$hand$deck$deck

마침내:

show()  case    $1      in
        (*[!0-9W-Za-d[:space:]]*|'')    return 2;;
        (*) (   eval "  $(printf "T='\t' E=\033 nl='\n'")"
                str(){  m=$1 l=\$1$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\(.\)/\1/;tn
                                x;/./!g;x;$G;s/\n$//p'  |
                sed -ne"$(      a=1 br -t
                                a=1 str FLUSH   '\(.\)' '\\\1'
                                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]\1/!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\2;47m \1 $E[m /g"'
                s/W\([^ ]* \)/1\1♦ /g;s/X\([^ ]* \)/1\1♥ /g
                s/Y\([^ ]* \)/0\1♠ /g;s/Z\([^ ]* \)/0\1♣ /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()카드 draw()는 다음과 같은 정렬 순서로 저장됩니다.

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

그리고 정장...

W X Y Z
♦ ♥ ♠ ♣

이렇게 하면 작업이 단순해집니다. 각 카드는 2바이트이고 항상 적절하게 정렬됩니다. $hand카드는 항상 줄로 구분 $deck되어 있습니다 . \n따라서 show()가장 어려운 부분은 간단하게 수행할 수 있습니다.

sort -k1.1,1.1

...각 행의 첫 번째 바이트에서 손을 정렬합니다. 나머지는 단지 비교일 뿐입니다. 이것은 sed...많은의. 2, 3, 4 종류, 풀 하우스, 2 페어, 로얄 스트레이트, 스트레이트, 로얄 플러쉬, 스트레이트 플러쉬를 처리할 수 있습니다. 첫 번째 인수에서 발견된 카드 수에 관계없이 다음 순서로 보고하는 것이 좋습니다.

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

show()함수는 반드시 다른 두 개 중 하나에 연결될 필요는 없습니다. 위의 인코딩 체계와 일치하는 방식으로 생성된 인수를 사용하여 호출할 수 있으며 원하는 출력을 생성합니다. 각 기능은 필요한 경우 모듈 방식으로 독립적으로 존재할 수 있습니다.

또한 세 가지 기능에는 카드 5장 제한이 없습니다. 모든 크기의 손을 다룰 수 있어야 합니다. 모두 오래 지속되도록 설계되었습니다.$deck (오류 검사 포함)- 그래서 어느 정도 활용이 가능합니다.

show()인코딩의 끝은디코딩됨. 인코딩된 모든 값은 단일 작업으로 디코딩 및 렌더링됩니다.

y/123456789abcd/234567891JQKA/

한 번의 번역으로 즉시 발생하므로 y///값을 두 번 잘못 편집할 위험이 없습니다.

출력은 다음과 같습니다.

여기에 이미지 설명을 입력하세요.

관련 정보