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

## Changes
# Tue Jan 27 2026 Added some color
# Tue Jan 20 2026 Added 'resize' to reset the terminal size
# Tue Jan 20 2026 Add red highlight to top score in list
# Fri Jan 09 2026 Fixed top ten list I added

#colors
BY=$(tput setaf 3; tput bold)
BC=$(tput setaf 6; tput bold)
BG=$(tput setaf 2; tput bold)
BR=$(tput setaf 1; tput bold)
N=$(tput sgr0)

# beginning size
export icol=`tput cols`
export irow=`tput lines`

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

function resize {
	if [[ col -ne "$icol" ]]
	then
		col=$icol
	fi
	if [[ row -lt "irow" ]]
	then
		row=$irow
	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 "${BG}Sub-Total:${BC} \t ${BY}$total ${BC}"
	echo -e "${BG}Bonus:${BC}\t\t ${BY}$bonus ${BC}"
	echo -e "${BG}Lower Total:${BC}\t ${BY}$ltotal ${BC}"	
	echo " "
	echo " "
	echo -e "${BG}Game Total:${BC}\t ${BR}$gtotal${BC}" 
}

function showdice {

	tput cup 0 70
	echo "${BG}Roll    Keep${BC}  "
	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 "${BG}Roll: ${BY}$rollcount${BC}"
}

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!"
            resize
			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 (${BG}R${BC} to roll, ${BR}X${BC} 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 "$(tput rev)**** Invalid choice! ****${N}${BC}"
		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
tput setaf 6; tput bold
showcard	# show the card on the screen
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 ${BR}$gtotal${BC}."
echo "$(date +%F)  $gtotal" >>~/etc/iyaht.scores
echo -e "\n -[ Top Ten ]-"
sort -k 2nr ~/etc/iyaht.scores | head -n 10 |cat -n | sed s/${gtotal}$/$(tput setaf 1; tput bold)${gtotal}$(tput setaf 6)/1

resize
