#!/bin/bash
# Author: Jarret W. Buse
# Date Created: 04/29/2023
# Date Modified: 04/29/2023
# Description: Yahtzee game in BASH



function screensize {
    col=`tput cols`
    row=`tput lines`
    if [[ col -lt "100" ]]
    then
        col=100
    fi
    if [[ row -lt "25" ]]
    then
        row=25
    fi
    printf '\e[8;'${row}';'${col}'t'
}

function initialize {
    count=1
    for word in {A..M}
    do
        card[$count]=""
        card[$count]=$word
        ((count+=1))
    done
    die[1]="[1]"
    die[2]="[2]"
    die[3]="[3]"
    die[4]="[4]"
    die[5]="[5]"
    die[6]="[6]"
    instr="ABCDEFGHIJKLM12345XR"
}

function showcard {
    place1=1
    place2=7
    tput clear
    echo -e "Ones \t ${card[$place1]} \t\t 3 of a kind \t\t ${card[$place2]}"
    ((place1+=1))
    ((place2+=1))
    echo -e "Twos \t ${card[$place1]} \t\t 4 of a kind \t\t ${card[$place2]}"
    ((place1+=1))
    ((place2+=1))
    echo -e "Threes \t ${card[$place1]} \t\t Full House \t\t ${card[$place2]}"
    ((place1+=1))
    ((place2+=1))
    echo -e "Fours \t ${card[$place1]} \t\t Small Straight \t ${card[$place2]}"
    ((place1+=1))
    ((place2+=1))
    echo -e "Fives \t ${card[$place1]} \t\t Large Straight  \t ${card[$place2]}"
    ((place1+=1))
    ((place2+=1))
    echo -e "Sixes \t ${card[$place1]} \t\t Yahtzee \t\t ${card[$place2]}"
    ((place2+=1))
    echo -e "\t\t\t Chance \t\t ${card[$place2]}"
    echo -e "Sub-Total: \t $total "
    echo -e "Bonus: \t\t $bonus "
    echo -e "Lower Total: \t $ltotal "  
    echo " "
    echo " "
    echo -e "Game Total: \t $gtotal" 
}

function showdice {

    tput cup 0 70
    echo "Roll    Keep  "
    for count in {1..5}
    do
        tput cup $count 70
        if [[ ${reroll[$count]} -eq "1" ]]
        then
            echo "  ${die[${roll[$count]}]}   $count       "
        else
            echo "      $count   ${die[${roll[$count]}]}"
        fi
    done
}

function rolldice {
    for count in {1..5}
    do
        if [[ ${reroll[$count]} -eq "1" ]]
        then
            roll[$count]=$(( ( SRANDOM %6 ) + 1 ))
        fi
    done
}

function turninit {
# initialize for a new turn of three rolls
    for count in {1..5}
    do
        reroll[${count}]=1
    done
    breakout=0
}

function showrollcount {
    tput cup 14 0
    echo "Roll: $rollcount"
}

function makeinstr {
# Regenerate list of available slots to choose from on the scorecard
    instr1="ABCDEFGHIJKLM"
    instr=""
    for count in {1..13}
    do
        if [[ $instr1 == *"${card[$count]}"* ]]
        then
            instr="${instr}${card[$count]}"
        fi
    done
    instr="${instr}123456XR"
}

function makelastinstr {
# Regenerate list of available slots to choose from on the scorecard
    instr1="ABCDEFGHIJKLM"
    instr=""
    for count in {1..13}
    do
        if [[ $instr1 == *"${card[$count]}"* ]]
        then
            instr="${instr}${card[$count]}"
        fi
    done
    instr="${instr}X"
}

scorecount() {
# score tally for left side of scorecard
    tally=0
    check=$1
    rollnow=1
    for count in {1..5}
    do
        if [[ ${roll[$count]} -eq $check ]]
        then
            tally=$((tally+check))
        fi
    done
    echo $tally
}

function sortdice {
    for ((i = 1; i<6; i++))
    do
        for((j = 1; j<6-i; j++))
            do
                    if [[ ${roll[j]} -gt ${roll[$((j+1))]} ]]
                then
                        # swap
                        temp=${roll[j]}
                        roll[$j]=${roll[$((j+1))]}
                        roll[$((j+1))]=$temp
                    fi
            done
    done
}

function getdielist {
    list=""
    for count in {1..5}
    do
        list="${list}${roll[$count]}"
    done
}

function removedupes {
    list1=""
    comp=" "
    for ((i=0; i<${#list}; ++i)) ; do
        char1="${list:i:1}"
        if [[ $char1 -ne $comp ]]
        then
            list1="${list1}${char1}"
            comp=$char1
        fi
    done
    list=$list1
}

function scoreright {
    tally=0
    sortdice
    case $inp in
        G) # 3 of a kind
            if ([[ ${roll[1]} -eq ${roll[2]} ]] && [[ ${roll[2]} -eq ${roll[3]} ]]) || ([[ ${roll[2]} -eq ${roll[3]} ]] && [[ ${roll[3]} -eq ${roll[4]} ]]) || ([[ ${roll[3]} -eq ${roll[4]} ]] && [[ ${roll[4]} -eq ${roll[5]} ]])
            then
                ((tally=${roll[1]}+${roll[2]}+${roll[3]}+${roll[4]}+${roll[5]}))
            fi
            card[7]=${tally};;
        H) # 4 of a kind
            if ([[ ${roll[1]} -eq ${roll[2]} ]] && [[ ${roll[2]} -eq ${roll[3]} ]] && [[ ${roll[3]} -eq ${roll[4]} ]]) || ([[ ${roll[2]} -eq ${roll[3]} ]] && [[ ${roll[3]} -eq ${roll[4]} ]] && [[ ${roll[4]} -eq ${roll[5]} ]])
            then
                ((tally=${roll[1]}+${roll[2]}+${roll[3]}+${roll[4]}+${roll[5]}))
            fi
            card[8]=${tally};;
        I) # full house
            if ([[ ${roll[1]} -eq ${roll[2]} ]] && [[ ${roll[2]} -eq ${roll[3]} ]] && [[ ${roll[4]} -eq ${roll[5]} ]]) || ([[ ${roll[1]} -eq ${roll[2]} ]] && [[ ${roll[3]} -eq ${roll[4]} ]] && [[ ${roll[4]} -eq ${roll[5]} ]])
            then
                tally=25
            fi
            card[9]=${tally};;
        J) # small straight
            getdielist
            removedupes
            if [[ $list == *"1234"* ]] || [[ $list == *"2345"* ]] || [[ $list == *"3456"* ]]
            then
                tally=30
            fi
            card[10]=${tally};;
        K) # large straight
            getdielist
            if [[ $list == *"12345"* ]] || [[ $list == *"23456"* ]]
            then
                tally=40
            fi
            card[11]=${tally};;
        L) # yahtzee
            if [[ ${roll[1]} -eq ${roll[2]} ]] && [[ ${roll[2]} -eq ${roll[3]} ]] && [[ ${roll[3]} -eq ${roll[4]} ]] && [[ ${roll[4]} -eq ${roll[5]} ]]
            then
                tally=50
            fi
            card[12]=${tally};;
        M) # chance
            ((tally=${roll[1]}+${roll[2]}+${roll[3]}+${roll[4]}+${roll[5]}))
            card[13]=${tally};;
    esac
    showcard
}

function scoreleft {
# user selected A-F to make a choice on the left side of the scorecard
    case $inp in
        A)  # ones
            card[1]=$( scorecount "1" );;
        B)  # twos
            card[2]=$( scorecount "2" );;
        C)  # threes
            card[3]=$( scorecount "3" );;
        D)  # fours
            card[4]=$( scorecount "4" );;
        E)  # fives
            card[5]=$( scorecount "5" );;
        F)  # sixes
            card[6]=$( scorecount "6" );;
    esac
    makeinstr
    showcard
}

function checkinput {
#inp is a valid choice - maybe??
        rollnow=0
        if [[ "${inp}" == "X" ]]
        then
            tput cup 17 0
            echo "You Quit.  Thanks for playing!"
            exit 0
        elif [[ "${inp}" -ge "1" ]] && [[ "${inp}" -le "5" ]]
        then
            # choosing to move a die
            if [[ ${reroll[$inp]} -eq "0" ]]
            then
                reroll[$inp]="1"
            else
                reroll[$inp]="0"
            fi
            showdice
        elif [[ "${inp}" == "R" ]]
        then
            # Roll dice
            rollnow=1
        elif [[ "${inp}" == [ABCDEF] ]]
        then
            # choosing to place a value on score card - left side
            scoreleft
            rollnow=1
            breakoutnow=1
        elif [[ "${inp}" == [GHIJKLM] ]]
        then
            # choosing to place a value on right score card
            scoreright
            rollnow=1
            breakoutnow=1
        fi
        showdice
}

function getuserinput {
    valid=0
    while [[ valid -eq "0" ]]
    do
        tput cup 15 0
        read -n1 -p "What do you want to do (R to roll, X to exit game): " inp
        inp=${inp^^}
        # check user's input for validity
        if [[ $instr == *"$inp"* ]]
        then
            # input is valid
            valid=1
        else
            # input is not valid
            valid=0
            tput cup 16 0
            echo "**** Invalid choice! *******"
        fi
    done
}

function subtotal {
    total=0
    instr1="ABCDEF"
    for count in {1..6}
    do
        if [[ $instr1 != *"${card[$count]}"* ]]
        then
            ((total=$total+${card[$count]}))
        fi
    done
    if [[ $total -ge "63" ]]
    then
        bonus=35
    fi
    ((ltotal=$total+$bonus))
}

function grandtotal {
    gtotal=$ltotal
    instr1="GHIJKLM"
    for count in {7..13}
    do
        if [[ $instr1 != *"${card[$count]}"* ]]
        then
            ((gtotal=$gtotal+${card[$count]}))
        fi
    done
    showcard
}

declare -a card
declare -a die
declare -a roll
declare -a reroll
declare -i total=0
declare -i bonus=0
declare -i ltotal=0
declare -i gtotal=0
declare -i rollcount=0
declare -i breakout

screensize
initialize  # initialize the card values and dice
showcard    # show the card on the creen
for game in {1..13}
do
  turninit  # initialize a new turn
  rollnow=0
  rollcount=1
  breakoutnow=0
  while [[ $rollcount -le "3" ]] && [[ $breakoutnow -eq "0" ]]
  do
    # roll all 5 dice
    rollnow=0
    rolldice
    showdice
    showrollcount
    makeinstr
    if [[ rollcount -eq "3" ]]
    then
        for count in {1..5}
        do
        # rolls are done, so all are moved to keep
            reroll[$count]=0
        done
        showdice
        makelastinstr
    fi
    while [[ $rollnow -eq "0" ]]
    do
        valid=0
        getuserinput
        # check input and perform command
        checkinput
        if [[ $breakoutnow -eq "1" ]]
        then
            rollnow=1
            break
        fi
    done
    ((rollcount+=1))
    if [[ $rollcount -eq "4" ]]
    then
        breakoutnow=1
    fi
    rollnow=0
    if [[ $breakoutnow -eq "1" ]]
    then
        rollcount=4
        break
    fi
  done
  rollcount=0
  subtotal
  grandtotal
done
echo "You Finished Your Game! Your Final Score is $gtotal."
echo "$(date +%F)  $gtotal" >>~/etc/iyaht.scores
echo -e "\n -[ Top Ten ]-"
head -n 5 ~/etc/iyaht.scores | sort -k 2nr