저는 핸드 생성기를 만들고 있는데 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/urandom
PRNG에서 임의의 데이터를 추출하는 것이지만 인수를 사용하여 호출하면 이를 임의 입력의 대체 소스의 파일 이름으로 해석합니다.
모든 카드가 반환되었습니다.(한 줄에 하나씩)- 독특해요. 슈트를 무작위화하여 라운드 로빈 순서로 할당하려고 시도하지 않습니다. 하지만 사실은 그렇지 않아요걱정해야 해: 카드 값의 순서는 이미 무작위이고, 어쨌든 카드는 무작위 순서로 고유하게 가지치기 작업을 수행해야 하므로 가지치기 작업의 결과는 무작위입니다.
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
$deck
draw()
$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///
값을 두 번 잘못 편집할 위험이 없습니다.
출력은 다음과 같습니다.