Загрузка данных


#!/usr/bin/env bash

# Создать и перемешать колоду.
new_deck() {
    deck=()

    for suit in "черви" "бубны" "трефы" "пики"; do
        for rank in {1..13}; do
            deck+=("$rank:$suit")
        done
    done

    # Перемешивание Фишера — Йетса.
    for ((i = ${#deck[@]} - 1; i > 0; i--)); do
        j=$((RANDOM % (i + 1)))
        temp=${deck[i]}
        deck[i]=${deck[j]}
        deck[j]=$temp
    done

    deck_position=0
}

# Взять верхнюю карту колоды.
draw_card() {
    local card=${deck[deck_position]}
    ((deck_position++))

    local rank=${card%%:*}
    local suit=${card#*:}

    case "$rank" in
        1)
            CARD="Туз $suit"
            CARD_VALUE=11
            ;;
        11)
            CARD="Валет $suit"
            CARD_VALUE=10
            ;;
        12)
            CARD="Дама $suit"
            CARD_VALUE=10
            ;;
        13)
            CARD="Король $suit"
            CARD_VALUE=10
            ;;
        *)
            CARD="$rank $suit"
            CARD_VALUE=$rank
            ;;
    esac
}

# Посчитать очки руки. Туз при необходимости превращается из 11 в 1.
total() {
    local sum=0
    local aces=0
    local value

    for value in "$@"; do
        sum=$((sum + value))
        if (( value == 11 )); then
            aces=$((aces + 1))
        fi
    done

    while (( sum > 21 && aces > 0 )); do
        sum=$((sum - 10))
        aces=$((aces - 1))
    done

    printf '%s' "$sum"
}

# Показать карты руки.
show_hand() {
    local title=$1
    shift

    printf '%s: ' "$title"
    printf '%s; ' "$@"
    printf '\n'
}

# Проверить, является ли рука блэкджеком: ровно две карты и 21 очко.
is_blackjack() {
    local card_count=$1
    shift

    (( card_count == 2 )) && (( $(total "$@") == 21 ))
}

while true; do
    new_deck

    player_cards=()
    player_values=()
    dealer_cards=()
    dealer_values=()

    echo
    echo "=== Игра «21 очко» ==="

    # Сначала две карты получает игрок.
    for _ in 1 2; do
        draw_card
        player_cards+=("$CARD")
        player_values+=("$CARD_VALUE")
    done

    # Затем две карты получает дилер.
    for _ in 1 2; do
        draw_card
        dealer_cards+=("$CARD")
        dealer_values+=("$CARD_VALUE")
    done

    show_hand "Ваши карты" "${player_cards[@]}"
    printf 'Ваши очки: %s\n' "$(total "${player_values[@]}")"
    printf 'Открытая карта дилера: %s\n' "${dealer_cards[0]}"
    echo "Вторая карта дилера скрыта."

    player_total=$(total "${player_values[@]}")
    player_blackjack=0
    dealer_blackjack=0

    if is_blackjack "${#player_cards[@]}" "${player_values[@]}"; then
        player_blackjack=1
        echo "У вас блэкджек!"
    fi

    if is_blackjack "${#dealer_cards[@]}" "${dealer_values[@]}"; then
        dealer_blackjack=1
    fi

    # Если у кого-то блэкджек, сразу сравниваем результаты.
    if (( player_blackjack || dealer_blackjack )); then
        echo
        show_hand "Карты дилера" "${dealer_cards[@]}"
        printf 'Очки дилера: %s\n' "$(total "${dealer_values[@]}")"

        if (( player_blackjack && dealer_blackjack )); then
            echo "У обоих блэкджек — ничья."
        elif (( player_blackjack )); then
            echo "Вы выиграли: у вас блэкджек!"
        else
            echo "Дилер выиграл: у него блэкджек."
        fi
    else
        # Сначала играет только игрок.
        while (( player_total < 21 )); do
            read -r -p "Взять карту или остановиться? (д/н): " answer

            case "$answer" in
                д|Д|да|Да|ДА|y|Y)
                    draw_card
                    player_cards+=("$CARD")
                    player_values+=("$CARD_VALUE")
                    player_total=$(total "${player_values[@]}")

                    show_hand "Ваши карты" "${player_cards[@]}"
                    printf 'Ваши очки: %s\n' "$player_total"
                    ;;
                н|Н|нет|Нет|НЕТ|n|N)
                    break
                    ;;
                *)
                    echo "Введите «д» (добрать) или «н» (остановиться)."
                    ;;
            esac
        done

        echo

        # Если игрок не перебрал, после его хода играет дилер.
        if (( player_total <= 21 )); then
            echo "Теперь ход дилера."
            show_hand "Карты дилера" "${dealer_cards[@]}"
            printf 'Очки дилера: %s\n' "$(total "${dealer_values[@]}")"

            dealer_total=$(total "${dealer_values[@]}")

            # Дилер добирает, пока у него меньше 17 очков.
            while (( dealer_total < 17 )); do
                draw_card
                dealer_cards+=("$CARD")
                dealer_values+=("$CARD_VALUE")
                dealer_total=$(total "${dealer_values[@]}")

                echo "Дилер берёт карту: $CARD"
                printf 'Очки дилера: %s\n' "$dealer_total"
            done
        else
            dealer_total=$(total "${dealer_values[@]}")
            echo "Перебор! Дилер не добирает карты."
        fi

        echo
        show_hand "Ваши карты" "${player_cards[@]}"
        printf 'Ваши очки: %s\n' "$player_total"
        show_hand "Карты дилера" "${dealer_cards[@]}"
        printf 'Очки дилера: %s\n' "$dealer_total"

        # Определить победителя.
        if (( player_total > 21 )); then
            echo "Вы перебрали — дилер выиграл."
        elif (( dealer_total > 21 )); then
            echo "Дилер перебрал — вы выиграли!"
        elif (( player_total > dealer_total )); then
            echo "Вы выиграли!"
        elif (( player_total < dealer_total )); then
            echo "Дилер выиграл."
        else
            echo "Ничья."
        fi
    fi

    read -r -p "Сыграть ещё раз? (д/н): " again
    case "$again" in
        д|Д|да|Да|ДА|y|Y)
            ;;
        *)
            echo "Спасибо за игру!"
            break
            ;;
    esac
done