rendered paste body#
# Marky's Uno v0.96
# Copyright (C) 2004 Mark A. Day (techwhiz@earthlink.net)
#
# Uno(tm) is Copyright (C) 2001 Mattel, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Default Settings
set UnoChan "#gamez"
set UnoRobot "n00bass"
set UnoPointsName "Points"
set UnoStopAfter 1
set UnoBonus 1000
set UnoWildDrawTwos 1
set UnoCFGFile "scripts/uno/uno.cfg"
set UnoScoreFile "scripts/uno/UnoScores"
set UnoMaxNickLen 9
set UnoMaxPlayers 10
set UnoOpFlags "o|o"
set UnoNTC "NOTICE"
# Command Binds
bind pub - !unocmds UnoCmds
bind pub - !remove UnoRemove
bind pub - !pause UnoPause
bind pub - !unowon UnoWon
bind pub - !unotop10 UnoTopTen
bind pub - !unotop10won UnoTopTenWon
bind pub - !unotop3last UnoTopThreeLast
bind pub - !unofast UnoTopFast
bind pub - !unohigh UnoHighScore
bind pub - !unoplayed UnoPlayed
bind pub - !unorecords UnoRecords
bind pub - !unoversion UnoVersion
bind pub - !uno UnoInit
bind pub o|o !stop UnoStop
# DCC Binds
bind dcc - unohands dccUnoHands
bind dcc - unorehash dcc_unorehash
# Cron Bind For Score Reset
bind time - "00 00 01 * *" UnoNewMonth
# Global Variables
set UnoOn 0
set UnoMode 0
set UnoPaused 0
set UnoPlayers 0
set MasterDeck ""
set UnoDeck ""
set DiscardPile ""
set PlayCard ""
set RoundRobin ""
set ThisPlayer ""
set ThisPlayerIDX 0
set UnoStartTime [unixtime]
set IsColorChange 0
set ColorPicker ""
set IsDraw 0
set UnoIDX ""
set UnPlayedRounds 0
# Scores Records And Ads
set UnoLastMonthCards(0) "Nobody 0"
set UnoLastMonthCards(1) "Nobody 0"
set UnoLastMonthCards(2) "Nobody 0"
set UnoLastMonthGames(0) "Nobody 0"
set UnoLastMonthGames(1) "Nobody 0"
set UnoLastMonthGames(2) "Nobody 0"
set UnoFast "Nobody 600"
set UnoHigh "Nobody 0"
set UnoPlayed "Nobody 0"
set UnoRecordHigh "Nobody 0"
set UnoRecordFast "Nobody 600"
set UnoRecordCard "Nobody 0"
set UnoRecordWins "Nobody 0"
set UnoRecordPlayed "Nobody 0"
set UnoAdNumber 0
# Card Stats
set CardStats(played) 0
set CardStats(passed) 0
set CardStats(drawn) 0
set CardStats(wilds) 0
set CardStats(draws) 0
set CardStats(skips) 0
set CardStats(revs) 0
# Timers
set UnoStartTimer ""
set UnoSkipTimer ""
set UnoCycleTimer ""
set UnoBotTimer ""
# Grace periods and timeouts
# AutoSkip period can be raised but not lower than 2
set AutoSkipPeriod 2
set StartGracePeriod 30
set RobotRestartPeriod 1
set UnoCycleTime 30
# Nick colours
set UnoNickColour "06 13 03 07 12 10 04 11 09 08"
# Debugging info
set Debug 0
set UnoVersion "0.96.74.3"
#
# Bind Channel Commands
#
proc UnoBindCmds {} {
bind pub - join JoinUno
bind pub - jo JoinUno
bind pub - order UnoOrder
bind pub - od UnoOrder
bind pub - time UnoTime
bind pub - ti UnoTime
bind pub - cards UnoShowCards
bind pub - ca UnoShowCards
bind pub - play UnoPlayCard
bind pub - pl UnoPlayCard
bind pub - card UnoTopCard
bind pub - cd UnoTopCard
bind pub - turn UnoTurn
bind pub - tu UnoTurn
bind pub - draw UnoDraw
bind pub - dr UnoDraw
bind pub - color UnoColorChange
bind pub - co UnoColorChange
bind pub - pass UnoPass
bind pub - pa UnoPass
bind pub - count UnoCount
bind pub - ct UnoCount
bind pub - stats UnoCardStats
bind pub - st UnoCardStats
}
#
# Unbind Channel Commands
#
proc UnoUnbindCmds {} {
catch {unbind pub - join JoinUno}
catch {unbind pub - jo JoinUno}
catch {unbind pub - order UnoOrder}
catch {unbind pub - od UnoOrder}
catch {unbind pub - time UnoTime}
catch {unbind pub - ti UnoTime}
catch {unbind pub - cards UnoShowCards}
catch {unbind pub - ca UnoShowCards}
catch {unbind pub - play UnoPlayCard}
catch {unbind pub - pl UnoPlayCard}
catch {unbind pub - card UnoTopCard}
catch {unbind pub - cd UnoTopCard}
catch {unbind pub - turn UnoTurn}
catch {unbind pub - tu UnoTurn}
catch {unbind pub - draw UnoDraw}
catch {unbind pub - dr UnoDraw}
catch {unbind pub - color UnoColorChange}
catch {unbind pub - co UnoColorChange}
catch {unbind pub - pass UnoPass}
catch {unbind pub - pa UnoPass}
catch {unbind pub - count UnoCount}
catch {unbind pub - ct UnoCount}
catch {unbind pub - stats UnoCardStats}
catch {unbind pub - st UnoCardStats}
}
#
# Reset Game Variables
#
proc UnoReset {} {
global UnoOn UnoMode UnoPaused UnoPlayers RoundRobin UnoDeck ThisPlayer ThisPlayerIDX PlayCard
global DiscardPile IsColorChange ColorPicker IsDraw UnoIDX MasterDeck CardStats
global UnoStartTimer UnoSkipTimer UnoCycleTimer
set UnoMode 0
set UnoPaused 0
set UnoPlayers 0
set MasterDeck ""
set UnoDeck ""
set DiscardPile ""
set RoundRobin ""
set ThisPlayer ""
set ThisPlayerIDX 0
set PlayCard ""
set IsColorChange 0
set ColorPicker ""
set IsDraw 0
set UnoIDX ""
set UnoAdNumber 0
set CardStats(played) 0
set CardStats(passed) 0
set CardStats(drawn) 0
set CardStats(wilds) 0
set CardStats(draws) 0
set CardStats(skips) 0
set CardStats(revs) 0
set UnoStartTimer ""
set UnoSkipTimer ""
set UnoCycleTimer ""
return
}
#
# Stop a game
#
proc UnoStop {nick uhost hand chan arg} {
global Debug UnoChan UnoOn UnoPaused UnPlayedRounds UnoStartTimer UnoSkipTimer UnoCycleTimer
if {$chan != $UnoChan} {return}
catch {killutimer $UnoStartTimer}
catch {killtimer $UnoSkipTimer}
catch {killutimer $UnoCycleTimer}
unomsg "[unoad]\00306 stopped by \00304\[\00312$nick!$uhost\00304\]\003"
set UnoOn 0
set UnoPaused 0
set UnPlayedRounds 0
UnoUnbindCmds
UnoReset
return
}
#
# First Entry
#
proc UnoInit {nick uhost hand chan arg} {
global UnoChan UnoOn
if {($chan != $UnoChan)||($UnoOn > 0)} {return}
unomsg "[unoad] \00304\[\00310$nick\!$uhost\00304\]\003"
set UnoOn 1
UnoBindCmds
UnoNext
return
}
#
# Initialize a new game
#
proc UnoNext {} {
global UnoOn MasterDeck UnoDeck UnoMode StartGracePeriod UnoHand NickColor UnoVersion UnoStartTimer UnoSkipTimer
if {$UnoOn == 0} {return}
UnoReset
set UnoMode 1
set MasterDeck [list B0 B1 B1 B2 B2 B3 B3 B4 B4 B5 B5 B6 B6 B7 B7 B8 B8 B9 B9 BR BR BS BS BDT BDT R0 R1 R1 R2 R2 R3 R3 R4 R4 R5 R5 R6 R6 R7 R7 R8 R8 R9 R9 RR RR RS RS RDT RDT Y0 Y1 Y1 Y2 Y2 Y3 Y3 Y4 Y4 Y5 Y5 Y6 Y6 Y7 Y7 Y8 Y8 Y9 Y9 YR YR YS YS YDT YDT G0 G1 G1 G2 G2 G3 G3 G4 G4 G5 G5 G6 G6 G7 G7 G8 G8 G9 G9 GR GR GS GS GDT GDT W W W W WDF WDF WDF WDF]
set UnoDeck ""
binary scan [unixtime] S1 rseed
set newrand [expr srand($rseed)]
while {[llength $UnoDeck] != 108} {
set pcardnum [rand [llength $MasterDeck]]
set pcard [lindex $MasterDeck $pcardnum]
lappend UnoDeck $pcard
set MasterDeck [lreplace $MasterDeck $pcardnum $pcardnum]
}
if [info exist UnoHand] {unset UnoHand}
if [info exist NickColor] {unset NickColor}
unomsg " "
unomsg "[unoad]\003 \0030,6 v$UnoVersion by Marky \003"
unomsg "[unoad]\003 \0030,12 Join Now - You Have $StartGracePeriod Seconds \003"
set UnoStartTimer [utimer $StartGracePeriod UnoStart]
return
}
#
# Start a new game
#
proc UnoStart {} {
global UnoChan UnoOn UnoCycleTime UnoRobot Debug UnoIDX UnoStartTime UnoPlayers RoundRobin ThisPlayer ThisPlayerIDX UnoDeck DiscardPile UnoMode UnoHand PlayCard AutoSkipPeriod
global UnoSkipTimer UnPlayedRounds UnoStopAfter NickColor
if {$UnoOn == 0} {return}
if {[llength $RoundRobin] == 0} {
unomsg "[unoad] \0030,3 No players - Next game in $UnoCycleTime seconds \003"
incr UnPlayedRounds
if {($UnoStopAfter > 0)&&($UnPlayedRounds >= $UnoStopAfter)} {
unomsg "[unoad] \0030,6 Stopped after $UnoStopAfter unplayed rounds - Type !uno to restart \003"
utimer 1 "UnoStop $UnoRobot $UnoRobot none $UnoChan none"
return
}
UnoCycle
return
}
# Bot Joins If One Player
if {[llength $RoundRobin] == 1} {
incr UnoPlayers
lappend RoundRobin "$UnoRobot"
lappend UnoIDX "$UnoRobot"
if [info exist UnoHand($UnoRobot)] {unset UnoHand($UnoRobot)}
if [info exist NickColor($UnoRobot)] {unset NickColor($UnoRobot)}
set UnoHand($UnoRobot) ""
set NickColor($UnoRobot) [colornick $UnoPlayers]
unomsg "[nikclr $UnoRobot]\003 joins [unoad]\003"
UnoShuffle 7
while {[llength $UnoHand($UnoRobot)] != 7} {
set pcardnum [rand [llength $UnoDeck]]
set pcard [lindex $UnoDeck $pcardnum]
set UnoDeck [lreplace ${UnoDeck} $pcardnum $pcardnum]
lappend UnoHand($UnoRobot) "$pcard"
}
if {$Debug > 1} { unolog $UnoRobot $UnoHand($UnoRobot) }
}
unomsg "\0030,6 Welcome to \003 [unoad]\003"
unomsg "\0030,10 \002$UnoPlayers\002 players this round \0030,6 $RoundRobin \003"
set UnoMode 2
set ThisPlayer [lindex $RoundRobin 0]
# Draw Card From Deck - First Top Card
set DiscardPile ""
set pcardnum [rand [llength $UnoDeck]]
set pcard [lindex $UnoDeck $pcardnum]
# Play Doesnt Start With A Wild Card
while {[string range $pcard 0 0] == "W"} {
set pcardnum [rand [llength $UnoDeck]]
set pcard [lindex $UnoDeck $pcardnum]
}
set PlayCard $pcard
set UnoDeck [lreplace ${UnoDeck} $pcardnum $pcardnum]
set Card [CardColor $pcard]
unomsg "[nikclr $ThisPlayer]\003 plays first - the top card is $Card \003"
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set UnoStartTime [unixtime]
# Start Auto-Skip Timer
set UnoSkipTimer [timer $AutoSkipPeriod UnoAutoSkip]
set UnPlayedRounds 0
return
}
#
# Cycle a new game
#
proc UnoCycle {} {
global UnoOn UnoMode UnoCycleTime UnoCycleTimer UnoSkipTimer
if {$UnoOn == 0} {return}
set UnoMode 4
catch {killtimer $UnoSkipTimer}
set AdTime [expr $UnoCycleTime /2]
set UnoAdTimer [utimer $AdTime UnoScoreAdvertise]
set UnoCycleTimer [utimer $UnoCycleTime UnoNext]
return
}
#
# Add a player
#
proc JoinUno {nick uhost hand chan arg} {
global Debug UnoIDX UnoMode UnoPlayers RoundRobin UnoDeck UnoHand UnoChan NickColor UnoMaxPlayers
if {($chan != $UnoChan)||($UnoMode < 1)||($UnoMode > 2)} {return}
if {[llength $RoundRobin] == $UnoMaxPlayers} {
unontc $nick "Sorry $nick - Uno already has $UnoMaxPlayers players, try next game"
return
}
set pcount 0
while {[lindex $RoundRobin $pcount] != ""} {
if {[lindex $RoundRobin $pcount] == $nick} {
return
}
incr pcount
}
incr UnoPlayers
lappend RoundRobin $nick
lappend UnoIDX $nick
if [info exist UnoHand($nick)] {unset UnoHand($nick)}
if [info exist NickColor($nick)] {unset NickColor($nick)}
set UnoHand($nick) ""
set NickColor($nick) [colornick $UnoPlayers]
# Re-Shuffle Deck
UnoShuffle 7
# Deal Cards To Player
set Card ""
while {[llength $UnoHand($nick)] != 7} {
set pcardnum [rand [llength $UnoDeck]]
set pcard [lindex $UnoDeck $pcardnum]
set UnoDeck [lreplace ${UnoDeck} $pcardnum $pcardnum]
lappend UnoHand($nick) $pcard
append Card [CardColor $pcard]
}
if {$Debug > 1} { unolog $nick $UnoHand($nick) }
unomsg "[nikclr $nick]\003 joins [unoad]\003"
unontc $nick "Hand: $Card"
return
}
#
# Add card(s) to players hand
#
proc UnoAddDrawToHand {cplayer idx num} {
global UnoHand UnoDeck RoundRobin CardStats
# Check if deck needs reshuffling
UnoShuffle $num
set Card ""
set newhand [expr [llength $UnoHand($cplayer)] + $num]
while {[llength $UnoHand($cplayer)] != $newhand} {
set pcardnum [rand [llength $UnoDeck]]
set pcard [lindex $UnoDeck $pcardnum]
set UnoDeck [lreplace ${UnoDeck} $pcardnum $pcardnum]
lappend UnoHand($cplayer) $pcard
append Card [CardColor $pcard]
}
showdraw $idx $Card
incr CardStats(drawn) $num
}
#
# Remove played card from player's hand
#
proc UnoRemoveCardFromHand {cplayer pcard} {
global UnoHand
set UnoHand($cplayer) [lreplace $UnoHand($cplayer) $pcard $pcard]
}
#
# Add card to discard pile
#
proc AddToDiscardPile {playcard} {
global DiscardPile
if {[string range $playcard 1 1] != ""} {
lappend DiscardPile $playcard
}
}
#
# Draw a card
#
proc UnoDraw {nick uhost hand chan arg} {
global UnoChan UnoMode UnoDeck ThisPlayer ThisPlayerIDX UnoHand RoundRobin IsDraw CardStats
if {($chan != $UnoChan)||($UnoMode != 2)||($nick != $ThisPlayer)} {return}
if {$IsDraw == 0} {
set IsDraw 1
UnoShuffle 1
set dcardnum [rand [llength $UnoDeck]]
set dcard [lindex $UnoDeck $dcardnum]
lappend UnoHand($nick) $dcard
set UnoDeck [lreplace ${UnoDeck} $dcardnum $dcardnum]
append Card [CardColor $dcard]
showdraw $ThisPlayerIDX $Card
showwhodrew $nick
incr CardStats(drawn)
UnoAutoSkipReset
return
}
unontc $nick "You've already drawn a card $nick, play a card or pass"
UnoAutoSkipReset
return
}
#
# Pass a turn
#
proc UnoPass {nick uhost hand chan arg} {
global UnoChan UnoMode ThisPlayer IsDraw ThisPlayerIDX RoundRobin IsColorChange CardStats
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
if {($nick != $ThisPlayer)||($IsColorChange == 1)} {return}
UnoAutoSkipReset
if {$IsDraw == 1} {
incr CardStats(passed)
set IsDraw 0
UnoNextPlayer
playpass $nick $ThisPlayer
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
UnoRobotRestart
} {
unontc $nick "Sorry $nick - You cant pass without drawing a card"
}
return
}
#
# Color change
#
proc UnoColorChange {nick uhost hand chan arg} {
global UnoChan UnoMode IsDraw PlayCard ColorPicker IsColorChange ThisPlayer ThisPlayerIDX RoundRobin
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
if {($nick != $ColorPicker)||($IsColorChange == 0)} {return}
UnoAutoSkipReset
regsub -all \[`.,!{}] $arg "" arg
set NewColor [string toupper [string range $arg 0 0]]
switch $NewColor {
"B" { set PlayCard "B"; set Card " \0030,12 Blue \003 "}
"G" { set PlayCard "G"; set Card " \0030,3 Green \003 "}
"Y" { set PlayCard "Y"; set Card " \0031,8 Yellow \003 "}
"R" { set PlayCard "R"; set Card " \0030,4 Red \003 "}
default { unontc $nick "Choose A Valid Color - Red, Green, Blue, or Yellow"; return }
}
UnoNextPlayer
unomsg "[nikclr $ColorPicker]\003 chose $Card and play continues with [nikclr $ThisPlayer]\003"
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set ColorPicker ""
set IsColorChange 0
set IsDraw 0
UnoRobotRestart
return
}
#
# Skip card
#
proc PlayUnoSkipCard {nick pickednum crd} {
global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats
set c0 [string range $crd 0 0]
set c1 [string range $crd 1 1]
set cip0 [string range $PlayCard 0 0]
set cip1 [string range $PlayCard 1 1]
if {$c1 != "S"} {return 0}
if {($c0 != $cip0)&&($c1 != $cip1)} {return 0}
incr CardStats(played)
incr CardStats(skips)
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
set SkipPlayer $ThisPlayer
UnoNextPlayer
set SkippedPlayer [lindex $RoundRobin $ThisPlayerIDX]
UnoNextPlayer
# No Cards Left = Winner
if {[check_unowin $SkipPlayer $Card] > 0} {
showwin $SkipPlayer $Card
UnoWin $SkipPlayer
UnoCycle
return 1
}
playskip $nick $Card $SkippedPlayer $ThisPlayer
check_hasuno $SkipPlayer
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set IsDraw 0
return 1
}
#
# Reverse card
#
proc PlayUnoReverseCard {nick pickednum crd} {
global IsDraw UnoIDX ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats
set c0 [string range $crd 0 0]
set c1 [string range $crd 1 1]
set cip0 [string range $PlayCard 0 0]
set cip1 [string range $PlayCard 1 1]
if {$c1 != "R"} {return 0}
if {($c0 != $cip0)&&($c1 != $cip1)} {return 0}
incr CardStats(played)
incr CardStats(revs)
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
# Reverse RoundRobin and Move To Next Player
set NewRoundRobin ""
set OrigOrderLength [llength $RoundRobin]
set IDX $OrigOrderLength
while {$OrigOrderLength != [llength $NewRoundRobin]} {
set IDX [expr ($IDX - 1)]
lappend NewRoundRobin [lindex $RoundRobin $IDX]
}
set Newindexorder ""
set OrigindexLength [llength $UnoIDX]
set IDX $OrigindexLength
while {$OrigindexLength != [llength $Newindexorder]} {
set IDX [expr ($IDX - 1)]
lappend Newindexorder [lindex $UnoIDX $IDX]
}
set UnoIDX $Newindexorder
set RoundRobin $NewRoundRobin
set ReversePlayer $ThisPlayer
# Next Player After Reversing RoundRobin
set pcount 0
while {$pcount != [llength $RoundRobin]} {
if {[lindex $RoundRobin $pcount] == $ThisPlayer} {
set ThisPlayerIDX $pcount
break
}
incr pcount
}
# <3 Players Act Like A Skip Card
if {[llength $RoundRobin] > 2} {
incr ThisPlayerIDX
if {$ThisPlayerIDX >= [llength $RoundRobin]} {set ThisPlayerIDX 0}
}
set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
# No Cards Left = Winner
if {[check_unowin $ReversePlayer $Card] > 0} {
showwin $ReversePlayer $Card
UnoWin $ReversePlayer
UnoCycle
return 1
}
playcard $nick $Card $ThisPlayer
check_hasuno $ReversePlayer
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set IsDraw 0
return 1
}
#
# Draw Two card
#
proc PlayUnoDrawTwoCard {nick pickednum crd} {
global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats UnoWildDrawTwos
set CardOk 0
set c0 [string range $crd 0 0]
set c2 [string range $crd 2 2]
set cip0 [string range $PlayCard 0 0]
set cip1 [string range $PlayCard 1 1]
set cip2 [string range $PlayCard 2 2]
if {$c2 != "T"} {return 0}
if {$c0 == $cip0} {set CardOk 1}
if {$cip2 == "T"} {set CardOk 1}
if {$UnoWildDrawTwos != 0} {
if {($cip1 != "")&&($cip2 != "F")} {set CardOk 1}
}
if {$CardOk == 1} {
incr CardStats(draws)
incr CardStats(played)
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
set DrawPlayer $ThisPlayer
set DrawPlayerIDX $ThisPlayerIDX
# Move to the player that draws
UnoNextPlayer
set PlayerThatDrew $ThisPlayer
set PlayerThatDrewIDX $ThisPlayerIDX
# Move To The Next Player
UnoNextPlayer
if {[check_unowin $nick $Card] > 0} {
UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 2
showwin $nick $Card
UnoWin $nick
UnoCycle
return 1
}
playdraw $nick $Card $PlayerThatDrew $ThisPlayer
UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 2
check_hasuno $nick
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set IsDraw 0
return 1
}
return 0
}
#
# Wild Draw Four card
#
proc PlayUnoWildDrawFourCard {nick pickednum crd isrobot} {
global ThisPlayer ThisPlayerIDX PlayCard RoundRobin IsColorChange ColorPicker CardStats
if {[string range $crd 2 2] != "F"} {return 0}
incr CardStats(wilds)
incr CardStats(played)
set ColorPicker $ThisPlayer
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
# move to the player that draws
UnoNextPlayer
set PlayerThatDrew $ThisPlayer
set PlayerThatDrewIDX $ThisPlayerIDX
if {$isrobot > 0} {
# choose color and move to next player
set cip [UnoBotPickAColor]
UnoNextPlayer
}
if {[check_unowin $nick $Card] > 0} {
UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 4
showwin $nick $Card
UnoWin $nick
UnoCycle
return 1
}
if {$isrobot > 0} {
botplaywildfour $ColorPicker $PlayerThatDrew $ColorPicker $cip $ThisPlayer
set ColorPicker ""
set IsColorChange 0
} {
playwildfour $nick $PlayerThatDrew $ColorPicker
set IsColorChange 1
}
UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 4
check_hasuno $nick
if {$isrobot > 0} {
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
}
set IsDraw 0
return 1
}
#
# Wild card
#
proc PlayUnoWildCard {nick pickednum crd isrobot} {
global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin IsColorChange ColorPicker CardStats
if {[string range $crd 0 0] != "W"} {return 0}
incr CardStats(wilds)
incr CardStats(played)
set ColorPicker $ThisPlayer
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
# Ok to remove this?
#set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
#set DrawnPlayer $ThisPlayer
if {$isrobot > 0} {
# Make A Color Choice
set cip [UnoBotPickAColor]
UnoNextPlayer
}
# No Cards Left = Winner
if {[check_unowin $nick $Card] > 0} {
showwin $nick $Card
UnoWin $nick
UnoCycle
return 1
}
if {$isrobot > 0} {
botplaywild $nick $ColorPicker $cip $ThisPlayer
set ColorPicker ""
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set IsColorChange 0
} {
playwild $nick $ColorPicker
set IsColorChange 1
}
check_hasuno $nick
set IsDraw 0
return 1
}
#
# Number card
#
proc PlayUnoNumberCard {nick pickednum crd} {
global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats
set CardOk 0
set c1 [string range $crd 0 0]
set c2 [string range $crd 1 1]
set cip1 [string range $PlayCard 0 0]
set cip2 [string range $PlayCard 1 1]
if {$c2 == -1} {return 0}
if {$c1 == $cip1} {set CardOk 1}
if {($cip2 != "")} {
if {$c2 == $cip2} {set CardOk 1}
}
if {$CardOk == 1} {
incr CardStats(played)
AddToDiscardPile $PlayCard
UnoRemoveCardFromHand $nick $pickednum
set PlayCard $crd
set Card [CardColor $crd]
set NumberCardPlayer $ThisPlayer
UnoNextPlayer
if {[check_unowin $NumberCardPlayer $Card] > 0} {
showwin $NumberCardPlayer $Card
UnoWin $NumberCardPlayer
UnoCycle
return 1
}
playcard $nick $Card $ThisPlayer
check_hasuno $NumberCardPlayer
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
set IsDraw 0
return 1
}
unontc $nick "Oops! Not a valid card - draw or play another"
return 0
}
#
# Attempt to find card in hand
#
proc UnoFindCard {nick pickednum crd IsRobot} {
global UnoRobot ThisPlayer ThisPlayerIDX
#if {$Debug > 1} {unolog $UnoRobot "UnoFindCard: [lindex $UnoHand($ThisPlayer) $pickednum"}
# Wild Draw Four
set FoundCard [PlayUnoWildDrawFourCard $nick $pickednum $crd $IsRobot]
if {$FoundCard == 1} {return 4}
# Wild
set FoundCard [PlayUnoWildCard $nick $pickednum $crd $IsRobot]
if {$FoundCard == 1} {return 5}
# Draw Two
set FoundCard [PlayUnoDrawTwoCard $nick $pickednum $crd]
if {$FoundCard == 1} {return 3}
# Skip
set FoundCard [PlayUnoSkipCard $nick $pickednum $crd]
if {$FoundCard == 1} {return 1}
# Reverse
set FoundCard [PlayUnoReverseCard $nick $pickednum $crd]
if {$FoundCard == 1} {return 2}
# Number card
set FoundCard [PlayUnoNumberCard $nick $pickednum $crd]
if {$FoundCard == 1} {return 6}
return 0
}
#
# Play a card
#
proc UnoPlayCard {nick uhost hand chan arg} {
global UnoChan UnoMode IsDraw IsColorChange ColorPicker UnoPlayers RoundRobin UnoHand ThisPlayer
if {($chan != $UnoChan)||($UnoMode != 2)||($nick != $ThisPlayer)} {return}
UnoAutoSkipReset
if {$IsColorChange == 1} {return}
regsub -all \[`,.!{}\ ] $arg "" arg
if {$arg == ""} {return}
set pcard [string toupper [string range $arg 0 3]]
set CardInPlayerHand 0
set pcount 0
while {[lindex $UnoHand($nick) $pcount] != ""} {
if {$pcard == [lindex $UnoHand($nick) $pcount]} {
set pcardnum $pcount
set CardInPlayerHand 1
break
}
incr pcount
}
if {$CardInPlayerHand == 0} {
unontc $nick "Sorry $nick - You don't have that card - draw or play another"
return
}
set CardFound [UnoFindCard $nick $pcardnum $pcard 0]
switch $CardFound {
0 {return}
4 {return}
5 {return}
default {UnoRobotRestart; return}
}
}
#
# Robot Player
#
proc UnoRobotPlayer {} {
global Debug UnoIDX IsDraw IsColorChange ColorPicker UnoMode UnoPlayers RoundRobin UnoDeck UnoHand ThisPlayer ThisPlayerIDX PlayCard CardStats UnoRobot
# Check for a valid card in hand
set CardOk 0
set IsDraw 0
set CardCount 0
set cip1 [string range $PlayCard 0 0]
set cip2 [string range $PlayCard 1 1]
while {$CardCount < [llength $UnoHand($ThisPlayer)]} {
set playcard [lindex $UnoHand($ThisPlayer) $CardCount]
set c1 [string range $playcard 0 0]
set c2 [string range $playcard 1 1]
#if {$Debug > 1} {unolog $UnoRobot "Trying: $playcard"}
if {($c1 == $cip1)||($c2 == $cip2)||($c1 == "W")} {
set CardOk 1
set pcard $playcard
set pcardnum $CardCount
break
}
incr CardCount
}
# Play the card if found
if {$CardOk == 1} {
set CardFound [UnoFindCard $UnoRobot $pcardnum $pcard 1]
switch $CardFound {
0 {}
5 {return}
6 {return}
default {UnoRobotRestart; return}
}
}
# Bot draws a card
UnoShuffle 1
set dcardnum [rand [llength $UnoDeck]]
set dcard [lindex $UnoDeck $dcardnum]
lappend UnoHand($UnoRobot) "$dcard"
set UnoDeck [lreplace ${UnoDeck} $dcardnum $dcardnum]
showwhodrew $UnoRobot
set CardOk 0
set CardCount 0
incr CardStats(drawn)
while {$CardCount < [llength $UnoHand($ThisPlayer)]} {
set playcard [lindex $UnoHand($ThisPlayer) $CardCount]
set c1 [string range $playcard 0 0]
set c2 [string range $playcard 1 1]
# if {$Debug > 1} {unolog $UnoRobot "DrawTry: $playcard"}
if {($c1 == $cip1)||($c2 == $cip2)||($c1 == "W")} {
set CardOk 1
set pcard $playcard
set pcardnum $CardCount
break
}
incr CardCount
}
# Bot plays drawn card or passes turn
if {$CardOk == 1} {
set CardFound [UnoFindCard $UnoRobot $pcardnum $pcard 1]
if {$CardFound == 1} {UnoRobotRestart; return}
switch $CardFound {
0 {}
5 {return}
6 {return}
default {UnoRobotRestart; return}
}
} {
incr CardStats(passed)
set IsDraw 0
UnoNextPlayer
playpass $UnoRobot $ThisPlayer
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
}
return
}
#
# Autoskip inactive players
#
proc UnoAutoSkip {} {
global UnoMode ThisPlayer ThisPlayerIDX RoundRobin AutoSkipPeriod IsColorChange ColorPicker
global UnoIDX UnoPlayers UnoDeck UnoHand UnoChan UnoSkipTimer Debug NickColor UnoPaused
if {$UnoMode != 2} {return}
if {$UnoPaused != 0} {return}
if {[uno_isrobot $ThisPlayerIDX]} {return}
set Idler $ThisPlayer
set IdlerIDX $ThisPlayerIDX
if {[unotimerexists UnoAutoSkip] != ""} {
unolog "uno" "AutoSkip Timer Already Exists"
return
}
set InChannel 0
set uclist [chanlist $UnoChan]
set pcount 0
while {[lindex $uclist $pcount] != ""} {
if {[lindex $uclist $pcount] == $Idler} {
set InChannel 1
break
}
incr pcount
}
if {$InChannel == 0} {
unomsg "[nikclr $Idler]\003 left the channel and is removed from Uno\003"
if {$IsColorChange == 1} {
if {$Idler == $ColorPicker} {
# Make A Color Choice
set cip [UnoPickAColor]
unomsg "\0030,13 $Idler \003was picking a color : randomly selecting $cip \003"
set IsColorChange 0
} {
unolog "uno" "UnoAutoRemove: IsColorChange set but $Idler not ColorPicker"
}
}
UnoNextPlayer
unomsg "[nikclr $Idler]\003 was the current player, continuing with [nikclr $ThisPlayer]\003"
if {![uno_isrobot $ThisPlayerIDX]} {
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
}
set UnoPlayers [expr ($UnoPlayers -1)]
# Remove Player From Game And Put Cards Back In Deck
if {$UnoPlayers > 1} {
set RoundRobin [lreplace ${RoundRobin} $IdlerIDX $IdlerIDX]
set UnoIDX [lreplace ${UnoIDX} $IdlerIDX $IdlerIDX]
lappend UnoDeck "$UnoHand($Idler)"
unset UnoHand($Idler)
unset NickColor($Idler)
}
switch $UnoPlayers {
1 {
showwindefault $ThisPlayer
UnoWin $ThisPlayer
UnoCycle
}
0 {
unomsg "[unoad] \0030,10 No players - No winner - Recycling \003"
UnoCycle
}
default {
if {![uno_isrobot $ThisPlayerIDX]} {
UnoAutoSkipReset
UnoRobotRestart
}
}
}
return
}
if {$Debug > 0} {unolog "uno" "AutoSkip Player: $Idler"}
unomsg "[nikclr $Idler]\003 idle for \00313$AutoSkipPeriod \003minutes and is skipped"
# Player Was ColorPicker
if {$IsColorChange == 1} {
if {$Idler == $ColorPicker} {
# Make A Color Choice
set cip [UnoPickAColor]
unomsg "[nikclr $Idler]\003 was picking a color : randomly selecting $cip"
set IsColorChange 0
} {
unolog "uno" "UnoRemove: IsColorChange set but $Idler not ColorPicker"
}
}
UnoNextPlayer
unomsg "[nikclr $Idler]\003 was the current player. Continuing with [nikclr $ThisPlayer]\003"
if {![uno_isrobot $ThisPlayerIDX]} {
set Card [CardColorAll $ThisPlayer]
showcards $ThisPlayerIDX $Card
} {
UnoRobotRestart
}
UnoAutoSkipReset
return
}
#
# Pause play
#
proc UnoPause {nick uhost hand chan arg} {
global UnoChan UnoOn UnoMode UnoOpFlags UnoPaused
if {$chan != $UnoChan} {return}
if {$UnoOn != 1} {return}
if {$UnoMode != 2} {return}
if {([validuser $nick])&&([matchattr $nick $UnoOpFlags $UnoChan])} {
if {$UnoPaused == 0} {
set UnoPaused 1
UnoUnbindCmds
unomsg "[unoad] \0030,4 Paused by $nick \003"
} {
set UnoPaused 0
UnoBindCmds
UnoAutoSkipReset
unomsg "[unoad] \0030,4 Play resumed by $nick \003"
}
}
}
#
# Remove user from play
#
proc UnoRemove {nick uhost hand chan arg} {
global UnoChan UnoOn UnoCycleTime UnoIDX UnoPlayers ThisPlayer ThisPlayerIDX RoundRobin UnoDeck DiscardPile UnoHand IsColorChange ColorPicker NickColor UnoOpFlags
if {$chan != $UnoChan} {return}
if {$UnoOn == 0} {return}
regsub -all \[`,.!{}] $arg "" arg
# Allow Ops To Remove Another Player
set UnoOpRemove 0
if {[string length $arg] > 0} {
if {([validuser $nick])&&([matchattr $nick $UnoOpFlags $UnoChan])} {
set UnoOpRemove 1
set UnoOpNick $nick
set nick $arg
} {
return
}
}
set PlayerFound 0
# Remove Player If Found - Put Cards Back To Bottom Of Deck
set pcount 0
while {[lindex $RoundRobin $pcount] != ""} {
if {[string tolower [lindex $RoundRobin $pcount]] == [string tolower $nick]} {
set PlayerFound 1
set FoundIDX $pcount
set nick [lindex $RoundRobin $pcount]
break
}
incr pcount
}
if {$PlayerFound == 1} {
if {$UnoOpRemove > 0} {
unomsg "[nikclr $nick]\003 was removed from Uno by $UnoOpNick"
} {
unontc $nick "You are now removed from the current Uno game."
unomsg "[nikclr $nick]\003 left Uno"
}
# Player Was ColorPicker
if {$IsColorChange == 1} {
if {$nick == $ColorPicker} {
# Make A Color Choice
set cip [UnoPickAColor]
unomsg "[nikclr $nick]\003 was choosing a color... I randomly select $cip"
set IsColorChange 0
} {
unolog "uno" "UnoRemove: IsColorChange Set but $nick not ColorPicker"
}
}
if {$nick == $ThisPlayer} {
UnoNextPlayer
if {$UnoPlayers > 2} {
unomsg "[nikclr $nick]\003 was the current player, continuing with [nikclr $ThisPlayer]\003"
}
UnoAutoSkipReset
}
set UnoPlayers [expr ($UnoPlayers -1)]
# Remove Player From Game And Put Cards Back In Deck
if {$UnoPlayers > 1} {
set RoundRobin [lreplace ${RoundRobin} $FoundIDX $FoundIDX]
set UnoIDX [lreplace ${UnoIDX} $FoundIDX $FoundIDX]
lappend DiscardPile "$UnoHand($nick)"
unset UnoHand($nick)
unset NickColor($nick)
}
set pcount 0
while {[lindex $RoundRobin $pcount] != ""} {
if {[lindex $RoundRobin $pcount] == $ThisPlayer} {
set ThisPlayerIDX $pcount
break
}
incr pcount
}
if {$UnoPlayers == 1} {
showwindefault $ThisPlayer
UnoWin $ThisPlayer
UnoCycle
return
}
UnoRobotRestart
} {
# Player not in current game
return
}
if {$UnoPlayers == 0} {
unomsg "[unoad] \0030,10 No players - No winner - Recycling \003"
UnoCycle
}
return
}
#
# Move to next player
#
proc UnoNextPlayer {} {
global ThisPlayer ThisPlayerIDX RoundRobin
incr ThisPlayerIDX
if {$ThisPlayerIDX >= [llength $RoundRobin]} {set ThisPlayerIDX 0}
set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
}
#
# Pick a random color for skipped/removed players
#
proc UnoPickAColor {} {
global PlayCard
set ucolors "r g b y"
set pcol [string tolower [lindex $ucolors [rand [llength $ucolors]]]]
switch $pcol {
"r" {set PlayCard "R"; return "\0030,4 Red \003"}
"g" {set PlayCard "G"; return "\0030,3 Green \003"}
"b" {set PlayCard "B"; return "\0030,12 Blue \003"}
"y" {set PlayCard "Y"; return "\0031,8 Yellow \003"}
}
}
#
# Robot picks a color by checking hand for 1st color card
# found with matching color, else picks color at random
#
proc UnoBotPickAColor {} {
global PlayCard UnoHand ThisPlayer
set CardCount 0
while {$CardCount < [llength $UnoHand($ThisPlayer)]} {
set thiscolor [string range [lindex $UnoHand($ThisPlayer) $CardCount] 0 0]
switch $thiscolor {
"R" {set PlayCard "R"; return "\0030,4 Red \003"}
"G" {set PlayCard "G"; return "\0030,3 Green \003"}
"B" {set PlayCard "B"; return "\0030,12 Blue \003"}
"Y" {set PlayCard "Y"; return "\0031,8 Yellow \003"}
}
incr CardCount
}
set ucolors "r g b y"
set pcol [string tolower [lindex $ucolors [rand [llength $ucolors]]]]
switch $pcol {
"r" {set PlayCard "R"; return "\0030,4 Red \003"}
"g" {set PlayCard "G"; return "\0030,3 Green \003"}
"b" {set PlayCard "B"; return "\0030,12 Blue \003"}
"y" {set PlayCard "Y"; return "\0031,8 Yellow \003"}
}
}
#
# Set robot for next turn
#
proc UnoRobotRestart {} {
global UnoMode ThisPlayerIDX RobotRestartPeriod UnoBotTimer
if {$UnoMode != 2} {return}
if {![uno_isrobot $ThisPlayerIDX]} {return}
set UnoBotTimer [utimer $RobotRestartPeriod UnoRobotPlayer]
}
#
# Reset autoskip timer
#
proc UnoAutoSkipReset {} {
global AutoSkipPeriod UnoMode UnoSkipTimer
catch {killtimer $UnoSkipTimer}
if {$UnoMode == 2} {
set UnoSkipTimer [timer $AutoSkipPeriod UnoAutoSkip]
}
}
#
# Channel triggers
#
#
# Show current player order
#
proc UnoOrder {nick uhost hand chan arg} {
global UnoChan UnoMode UnoPlayers RoundRobin
if {($chan != $UnoChan)||($UnoMode < 2)} {return}
unomsg "\0030,10 Player Order \[$UnoPlayers\]: \0030,6 $RoundRobin "
return
}
#
# Show game running time
#
proc UnoTime {nick uhost hand chan arg} {
global UnoChan UnoMode
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
set unotime "\0030,10 Game Time: \0030,6 [duration [game_time]] \003"
unomsg "$unotime"
return
}
#
# Show player what cards they hold
#
proc UnoShowCards {nick uhost hand chan arg} {
global UnoChan UnoMode UnoHand ThisPlayerIDX
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
if [info exist UnoHand($nick)] {
set Card ""
set ccnt 0
while {[llength $UnoHand($nick)] != $ccnt} {
set pcard [lindex $UnoHand($nick) $ccnt]
append Card [CardColor $pcard]
incr ccnt
}
if {![uno_isrobot $ThisPlayerIDX]} {
unontc $nick "Hand: $Card\003"
}
}
return
}
#
# Show current player
#
proc UnoTurn {nick uhost hand chan arg} {
global UnoChan UnoMode ThisPlayer RoundRobin UnoMode
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
if {[llength $RoundRobin] < 1 } {return}
set info "\0030,10 Current Player: \0030,6 $ThisPlayer \003"
unomsg "$info"
return
}
#
# Show current top card
#
proc UnoTopCard {nick uhost hand chan arg} {
global PlayCard UnoChan UnoMode
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
set pcard $PlayCard
set Card [CardColor $pcard]
unomsg "\0030,10 Card In Play: \003 $Card \003"
return
}
#
# Show card stats
#
proc UnoCardStats {nick uhost hand chan arg} {
global UnoChan UnoMode CardStats
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
set passdraw [format "%3.1f" [get_ratio $CardStats(passed) $CardStats(drawn)]]
set skiprev [expr $CardStats(skips) +$CardStats(revs)]
unomsg "\0030,10 Card Stats: \0030,6 Played:$CardStats(played) Pass\/Draw Ratio:$passdraw\% Skip\/Rev:$skiprev DrawCards:$CardStats(draws) WildCards:$CardStats(wilds) \003"
return
}
#
# Card count
#
proc UnoCount {nick uhost hand chan arg} {
global RoundRobin UnoHand UnoMode UnoChan
if {($chan != $UnoChan)||($UnoMode != 2)} {return}
set ordcnt 0
set crdcnt ""
while {[lindex $RoundRobin $ordcnt] != ""} {
append crdcnt "\0030,10 [lindex $RoundRobin $ordcnt] \0030,6 [llength $UnoHand([lindex $RoundRobin $ordcnt])] Cards "
incr ordcnt
}
unomsg "$crdcnt\003"
return
}
#
# Show player's score
#
proc UnoWon {nick uhost hand chan arg} {
global UnoScoreFile UnoPointsName
regsub -all \[`,.!] $arg "" arg
if {[string length $arg] == 0} {set arg $nick}
set scorer [string tolower $arg]
set pflag 0
set f [open $UnoScoreFile r]
while {[gets $f sc] != -1} {
set cnick [string tolower [lindex [split $sc] 0]]
if {$cnick == $scorer} {
set pmsg "\0030,10 [lindex [split $sc] 0] \0030,6 [lindex $sc 2] $UnoPointsName in [lindex $sc 1] Games \003"
set pflag 1
}
}
close $f
if {$pflag == 0} {
set pmsg "\0030,10 $arg \0030,6 No Score \003"
}
unomsg "$pmsg"
return
}
#
# Display current top10
#
proc UnoTopTen {nick uhost hand chan arg} {
global UnoChan
if {$chan != $UnoChan} {return}
UnoTop10 1
return
}
proc UnoTopTenWon {nick uhost hand chan arg} {
global UnoChan
if {$chan != $UnoChan} {return}
UnoTop10 0
return
}
#
# Display last month's top3
#
proc UnoTopThreeLast {nick uhost hand chan arg} {
global UnoChan
if {$chan != $UnoChan} {return}
UnoLastMonthTop3 $nick $uhost $hand $chan 0
unomsg " "
UnoLastMonthTop3 $nick $uhost $hand $chan 1
return
}
#
# Display month fastest game
#
proc UnoTopFast {nick uhost hand chan arg} {
global UnoChan UnoFast
if {$chan != $UnoChan} {return}
unomsg "\0030,6 This Month's Fastest Game \0030,10 [lindex [split $UnoFast] 0] [duration [lindex $UnoFast 1]] \003"
return
}
#
# Display month high score
#
proc UnoHighScore {nick uhost hand chan arg} {
global UnoChan UnoHigh UnoPointsName
if {$chan != $UnoChan} {return}
unomsg "\0030,6 This Month's High Score \0030,10 [lindex [split $UnoHigh] 0] [lindex $UnoHigh 1] $UnoPointsName \003"
return
}
#
# Display month most cards played
#
proc UnoPlayed {nick uhost hand chan arg} {
global UnoChan UnoPlayed
if {$chan != $UnoChan} {return}
unomsg "\0030,6 This Month's Most Cards Played \0030,10 [lindex [split $UnoPlayed] 0] [lindex $UnoPlayed 1] Cards \003"
return
}
#
# Show all-time records
#
proc UnoRecords {nick uhost hand chan arg} {
global UnoChan UnoRecordFast UnoRecordHigh UnoRecordCard UnoRecordWins UnoRecordPlayed
if {$chan != $UnoChan} {return}
unomsg "\0030,6 All-Time Records: Points \0030,10 $UnoRecordCard \0030,6 Games \0030,10 $UnoRecordWins \0030,6 Fast \0030,10 [lindex $UnoRecordFast 0] [duration [lindex $UnoRecordFast 1]] \0030,6 High Score \0030,10 $UnoRecordHigh \0030,6 Cards Played \0030,10 $UnoRecordPlayed \003"
return
}
#
# Display month top10
#
proc UnoTop10 {mode} {
global UnoScoreFile unsortedscores UnoPointsName UnoRobot
if {($mode < 0)||($mode > 1)} {set mode 0}
switch $mode {
0 {set winners "\0030,6 Top10 Game Wins "}
1 {set winners "\0030,6 Top10 $UnoPointsName "}
}
if ![file exists $UnoScoreFile] {
set f [open $UnoScoreFile w]
puts $f "$UnoRobot 0 0"
unomsg "\0030,10 The Score File Is Empty - Created A New One\003"
close $f
return
}
if [info exists unsortedscores] {unset unsortedscores}
if [info exists top10] {unset top10}
set f [open $UnoScoreFile r]
while {[gets $f s] != -1} {
switch $mode {
0 {set unsortedscores([lindex [split $s] 0]) [lindex $s 1]}
1 {set unsortedscores([lindex [split $s] 0]) [lindex $s 2]}
}
}
close $f
for {set s 0} {$s < 10} {incr s} {
set top10($s) "Nobody 0"
}
set s 0
foreach n [lsort -decreasing -command UnoSortScores [array names unsortedscores]] {
set top10($s) "$n $unsortedscores($n)"
incr s
}
for {set s 0} {$s < 10} {incr s} {
if {[lindex $top10($s) 1] > 0} {
append winners "\0030,6 #[expr $s +1] \0030,10 [lindex [split $top10($s)] 0] [lindex $top10($s) 1] "
} {
append winners "\0030,6 #[expr $s +1] \0030,10 Nobody 0 "
}
}
unomsg "$winners\003"
return
}
#
# Last month's top3
#
proc UnoLastMonthTop3 {nick uhost hand chan arg} {
global UnoChan UnoLastMonthCards UnoLastMonthGames UnoPointsName
if {$chan != $UnoChan} {return}
if {$arg == 0} {
if [info exists UnoLastMonthCards] {
set UnoTop3 "\0030,6 Last Month's Top 3 $UnoPointsName Won "
for { set s 0} { $s < 3 } { incr s} {
append UnoTop3 "\0030,6 #[expr $s +1] \0030,10 $UnoLastMonthCards($s) "
}
unomsg "$UnoTop3"
}
} {
if [info exists UnoLastMonthGames] {
set UnoTop3 "\0030,6 Last Month's Top 3 Games Won "
for { set s 0} { $s < 3 } { incr s} {
append UnoTop3 "\0030,6 #[expr $s +1] \0030,10 $UnoLastMonthGames($s) "
}
unomsg "$UnoTop3"
}
}
}
#
# Show game help
#
proc UnoCmds {nick uhost hand chan arg} {
global UnoChan
if {$chan != $UnoChan} {return}
unontc $nick "Uno Commands: !uno !stop !remove \[nick\] !unowon \[nick\] !unocmds"
unontc $nick "Uno Stats: !unotop10 !unotop3last !unofast !unohigh !unorecords"
unontc $nick "Uno Card Commands: jo=join pl=play dr=draw pa=pass co=color"
unontc $nick "Uno Chan Commands: ca=cards cd=card tu=turn od=order ct=count st=stats ti=time"
return
}
#
# Uno version
#
proc UnoVersion {nick uhost hand chan arg} {
global UnoVersion
unomsg "[unoad]\003 \0030,6 v$UnoVersion by Marky \003"
return
}
#
# Read score file
#
proc UnoReadScores {} {
global unogameswon unoptswon UnoScoreFile UnoRobot
if [info exists unogameswon] { unset unogameswon }
if [info exists unoptswon] { unset unoptswon }
if ![file exists $UnoScoreFile] {
set f [open $UnoScoreFile w]
puts $f "$UnoRobot 0 0"
close $f
}
set f [open $UnoScoreFile r]
while {[gets $f s] != -1} {
set unogameswon([lindex [split $s] 0]) [lindex $s 1]
set unoptswon([lindex [split $s] 0]) [lindex $s 2]
}
close $f
return
}
#
# Clear top10 and write monthly scores
#
proc UnoNewMonth {min hour day month year} {
global unsortedscores unogameswon unoptswon UnoLastMonthCards UnoLastMonthGames UnoScoreFile UnoRobot
global UnoFast UnoHigh UnoPlayed UnoRecordFast UnoRecordHigh UnoRecordPlayed UnoRecordCard UnoRecordWins
set lmonth [UnoLastMonthName $month]
unomsg "[unoad] \0030,4 Clearing monthly scores \003"
set UnoMonthFileName "$UnoScoreFile.$lmonth"
# Read Current Scores
UnoReadScores
# Write To Old Month File
if ![file exists $UnoMonthFileName] {
set f [open $UnoMonthFileName w]
foreach n [array names unogameswon] {
puts $f "$n $unogameswon($n) $unoptswon($n)"
}
close $f
}
# Find Top 3 Card Holders and Game Winners
set mode 0
while {$mode < 2} {
if [info exists unsortedscores] {unset unsortedscores}
if [info exists top10] {unset top10}
set f [open $UnoScoreFile r]
while {[gets $f s] != -1} {
switch $mode {
0 {set unsortedscores([lindex [split $s] 0]) [lindex $s 1]}
1 {set unsortedscores([lindex [split $s] 0]) [lindex $s 2]}
}
}
close $f
set s 0
foreach n [lsort -decreasing -command UnoSortScores [array names unsortedscores]] {
set top10($s) "$n $unsortedscores($n)"
incr s
}
for {set s 0} {$s < 3} {incr s} {
if {[lindex $top10($s) 1] > 0} {
switch $mode {
0 {set UnoLastMonthGames($s) "[lindex [split $top10($s)] 0] [lindex $top10($s) 1]"}
1 {set UnoLastMonthCards($s) "[lindex [split $top10($s)] 0] [lindex $top10($s) 1]"}
}
} {
switch $mode {
0 {set UnoLastMonthGames($s) "Nobody 0"}
1 {set UnoLastMonthCards($s) "Nobody 0"}
}
}
}
incr mode
}
# Update records
if {[lindex $UnoFast 1] < [lindex $UnoRecordFast 1]} {set UnoRecordFast $UnoFast}
if {[lindex $UnoHigh 1] > [lindex $UnoRecordHigh 1]} {set UnoRecordHigh $UnoHigh}
if {[lindex $UnoPlayed 1] > [lindex $UnoRecordPlayed 1]} {set UnoRecordPlayed $UnoPlayed}
if {[lindex $UnoLastMonthCards(0) 1] > [lindex $UnoRecordCard 1]} {set UnoRecordCard $UnoLastMonthCards(0)}
if {[lindex $UnoLastMonthGames(0) 1] > [lindex $UnoRecordWins 1]} {set UnoRecordWins $UnoLastMonthGames(0)}
# Wipe last months records
set UnoFast "$UnoRobot 60"
set UnoHigh "$UnoRobot 100"
set UnoPlayed "$UnoRobot 100"
# Save Top3 And Records To Config File
Uno_WriteCFG
# Wipe This Months Score File
set f [open $UnoScoreFile w]
puts $f "$UnoRobot 0 0"
close $f
unolog "uno" "Cleared monthly scores"
return
}
#
# Update score of winning player
#
proc UnoUpdateScore {winner cardtotals} {
global unogameswon unoptswon UnoScoreFile
UnoReadScores
if {[info exists unogameswon($winner)]} {
incr unogameswon($winner)
} {
set unogameswon($winner) 1
}
if {[info exists unoptswon($winner)]} {
incr unoptswon($winner) $cardtotals
} {
set unoptswon($winner) $cardtotals
}
set f [open $UnoScoreFile w]
foreach n [array names unogameswon] {
puts $f "$n $unogameswon($n) $unoptswon($n)"
}
close $f
return
}
#
# Display winner and game statistics
#
proc UnoWin {winner} {
global UnoHand ThisPlayer RoundRobin UnoPointsName CardStats UnoMode UnoCycleTime UnoFast UnoHigh UnoPlayed UnoBonus
set cardtotals 0
set UnoMode 3
set ThisPlayerIDX 0
set needCFGWrite 0
set UnoTime [game_time]
unomsg "\0030,6 Card Totals \003"
# Total up all player's cards
while {$ThisPlayerIDX != [llength $RoundRobin]} {
set Card ""
set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
if {$ThisPlayer != $winner} {
set ccount 0
while {[lindex $UnoHand($ThisPlayer) $ccount] != ""} {
set cardtotal [lindex $UnoHand($ThisPlayer) $ccount]
set c1 [string range $cardtotal 0 0]
set c2 [string range $cardtotal 1 1]
set cardtotal 0
if {$c1 == "W"} {
set cardtotal 50
} {
switch $c2 {
"S" {set cardtotal 20}
"R" {set cardtotal 20}
"D" {set cardtotal 20}
default {set cardtotal $c2}
}
}
set cardtotals [expr $cardtotals + $cardtotal]
incr ccount
}
set Card [CardColorAll $ThisPlayer]
unomsg "[strpad [nikclr $ThisPlayer] 12] $Card"
}
incr ThisPlayerIDX
}
# Check high score record
set HighScore [lindex $UnoHigh 1]
if {$cardtotals > $HighScore} {
unomsg "\0030,4 $winner Broke The High Score Record And Gets $UnoBonus Bonus $UnoPointsName \003"
set UnoHigh "$winner $cardtotals"
incr cardtotals $UnoBonus
set needCFGWrite 1
}
# Check played cards record
set HighPlayed [lindex $UnoPlayed 1]
if {$CardStats(played) > $HighPlayed} {
unomsg "\0030,4 $winner Broke The Most Cards Played Record And Gets $UnoBonus Bonus $UnoPointsName \003"
set UnoPlayed "$winner $CardStats(played)"
incr cardtotals $UnoBonus
set needCFGWrite 1
}
# Check fast game record
set FastRecord [lindex $UnoFast 1]
if {$UnoTime < $FastRecord} {
unomsg "\0030,4 $winner Broke The Fast Record And Gets $UnoBonus Bonus $UnoPointsName \003"
incr cardtotals $UnoBonus
set UnoFast "$winner $UnoTime"
set needCFGWrite 1
}
# Winner
unomsg "\0030,10 $winner \0030,6 $cardtotals $UnoPointsName In [duration $UnoTime] \003"
# Card stats
set passdraw [format "%3.1f" [get_ratio $CardStats(passed) $CardStats(drawn)]]
set skiprev [expr $CardStats(skips) +$CardStats(revs)]
unomsg "\0030,10 Cardstats \0030,6 Played:$CardStats(played) Pass\/Draw Ratio:$passdraw\% Skip\/Rev:$skiprev DrawCards:$CardStats(draws) WildCards:$CardStats(wilds) \003"
unomsg "[unoad] \0030,3 Next Game In $UnoCycleTime Seconds \003"
# Write scores
UnoUpdateScore $winner $cardtotals
# Write records
if {$needCFGWrite > 0} {Uno_WriteCFG}
return
}
#
# Re-Shuffle deck
#
proc UnoShuffle {len} {
global UnoDeck DiscardPile
if {[llength $UnoDeck] >= $len} { return }
unomsg "[unoad] \0030,4 Re-Shuffling Uno Deck \003"
lappend DiscardPile "$UnoDeck"
set UnoDeck ""
set NewDeckSize [llength $DiscardPile]
while {[llength $UnoDeck] != $NewDeckSize} {
set pcardnum [rand [llength $DiscardPile]]
set pcard [lindex $DiscardPile $pcardnum]
lappend UnoDeck "$pcard"
set DiscardPile [lreplace ${DiscardPile} $pcardnum $pcardnum]
}
return
}
#
# Read config file
#
proc Uno_ReadCFG {} {
global UnoCFGFile UnoLastMonthCards UnoLastMonthGames UnoPointsName UnoScoreFile UnoRobot UnoChan UnoFast UnoHigh UnoPlayed UnoStopAfter UnoBonus
global UnoRecordHigh UnoRecordFast UnoRecordCard UnoRecordWins UnoRecordPlayed UnoWildDrawTwos
if {[file exist $UnoCFGFile]} {
set f [open $UnoCFGFile r]
while {[gets $f s] != -1} {
set kkey [string tolower [lindex [split $s "="] 0]]
set kval [lindex [split $s "="] 1]
switch $kkey {
botname {set UnoRobot $kval}
channel {set UnoChan $kval}
points {set UnoPointsName $kval}
scorefile {set UnoScoreFile $kval}
stopafter {set UnoStopAfter $kval}
wilddrawtwos {set UnoWildDrawTwos $kval}
lastmonthcard1 {set UnoLastMonthCards(0) $kval}
lastmonthcard2 {set UnoLastMonthCards(1) $kval}
lastmonthcard3 {set UnoLastMonthCards(2) $kval}
lastmonthwins1 {set UnoLastMonthGames(0) $kval}
lastmonthwins2 {set UnoLastMonthGames(1) $kval}
lastmonthwins3 {set UnoLastMonthGames(2) $kval}
fast {set UnoFast $kval}
high {set UnoHigh $kval}
played {set UnoPlayed $kval}
bonus {set UnoBonus $kval}
recordhigh {set UnoRecordHigh $kval}
recordfast {set UnoRecordFast $kval}
recordcard {set UnoRecordCard $kval}
recordwins {set UnoRecordWins $kval}
recordplayed {set UnoRecordPlayed $kval}
}
}
close $f
if {$UnoStopAfter < 0} {set UnoStopAfter 0}
if {$UnoBonus < 0} {set UnoBonus 1000}
if {($UnoWildDrawTwos < 0)||($UnoWildDrawTwos > 1)} {set UnoWildDrawTwos 0}
return
}
putcmdlog "\[Uno\] Config file $UnoCFGFile not found... saving defaults"
Uno_WriteCFG
return
}
#
# Write config file
#
proc Uno_WriteCFG {} {
global UnoCFGFile UnoLastMonthCards UnoLastMonthGames UnoPointsName UnoScoreFile UnoRobot UnoChan UnoFast UnoHigh UnoPlayed UnoStopAfter UnoBonus
global UnoRecordHigh UnoRecordFast UnoRecordCard UnoRecordWins UnoRecordPlayed UnoWildDrawTwos
set f [open $UnoCFGFile w]
puts $f "# This file is automatically overwritten"
puts $f "BotName=$UnoRobot"
puts $f "Channel=$UnoChan"
puts $f "Points=$UnoPointsName"
puts $f "ScoreFile=$UnoScoreFile"
puts $f "StopAfter=$UnoStopAfter"
puts $f "WildDrawTwos=$UnoWildDrawTwos"
puts $f "LastMonthCard1=$UnoLastMonthCards(0)"
puts $f "LastMonthCard2=$UnoLastMonthCards(1)"
puts $f "LastMonthCard3=$UnoLastMonthCards(2)"
puts $f "LastMonthWins1=$UnoLastMonthGames(0)"
puts $f "LastMonthWins2=$UnoLastMonthGames(1)"
puts $f "LastMonthWins3=$UnoLastMonthGames(2)"
puts $f "Fast=$UnoFast"
puts $f "High=$UnoHigh"
puts $f "Played=$UnoPlayed"
puts $f "Bonus=$UnoBonus"
puts $f "RecordHigh=$UnoRecordHigh"
puts $f "RecordFast=$UnoRecordFast"
puts $f "RecordCard=$UnoRecordCard"
puts $f "RecordWins=$UnoRecordWins"
puts $f "RecordPlayed=$UnoRecordPlayed"
close $f
return
}
#
# Score advertiser
#
proc UnoScoreAdvertise {} {
global UnoChan UnoAdNumber UnoRobot
unomsg " "
switch $UnoAdNumber {
0 {UnoTop10 0}
1 {UnoLastMonthTop3 $UnoRobot none none $UnoChan 0}
2 {UnoTop10 1}
3 {UnoRecords $UnoRobot none none $UnoChan ""}
4 {UnoPlayed $UnoRobot none none $UnoChan ""}
5 {UnoHighScore $UnoRobot none none $UnoChan ""}
6 {UnoTopFast $UnoRobot none none $UnoChan ""}
}
incr UnoAdNumber
if {$UnoAdNumber > 6} {set UnoAdNumber 0}
return
}
#
# Color all cards in hand
#
proc CardColorAll {cplayer} {
global UnoHand
set pCard ""
set ccount 0
while {[llength $UnoHand($cplayer)] != $ccount} {
append pCard [CardColor [lindex $UnoHand($cplayer) $ccount]]
incr ccount
}
return $pCard
}
#
# Color a single card
#
proc CardColor {pcard} {
set cCard ""
set c2 [string range $pcard 1 1]
switch [string range $pcard 0 0] {
"W" {
if {$c2 == "D"} {
append cCard "[wildf]"
} {
append cCard "[wild]"
}
return $cCard
}
"Y" {append cCard " \0031,8 Yellow "}
"R" {append cCard " \0030,4 Red "}
"G" {append cCard " \0030,3 Green "}
"B" {append cCard " \0030,12 Blue "}
}
switch $c2 {
"S" {append cCard "\002Skip\002 \003 "}
"R" {append cCard "\002Reverse\002 \003 "}
"D" {append cCard "\002Draw Two\002 \003 "}
default {append cCard "$c2 \003 "}
}
return $cCard
}
#
# Check if player has Uno
#
proc check_hasuno {cplayer} {
global UnoHand
if {[llength $UnoHand($cplayer)] > 1} {return}
hasuno $cplayer
return
}
#
# Check for winner
#
proc check_unowin {cplayer ccard} {
global UnoHand
if {[llength $UnoHand($cplayer)] > 0} {return 0}
return 1
}
#
# Show player what cards they have
#
proc showcards {idx pcards} {
global UnoIDX
if {[uno_isrobot $idx]} {return}
unontc [lindex $UnoIDX $idx] "Hand: $pcards"
}
#
# Check if this is the robot player
#
proc uno_isrobot {cplayerIDX} {
global RoundRobin UnoRobot UnoMaxNickLen
if {[string range [lindex $RoundRobin $cplayerIDX] 0 $UnoMaxNickLen] != $UnoRobot} {return 0}
return 1
}
# Show played card
proc playcard {who crd nplayer} {
unomsg "[nikclr $who]\003 plays $crd \003to [nikclr $nplayer]\003"
}
# Show played draw card
proc playdraw {who crd dplayer nplayer} {
unomsg "[nikclr $who]\003 plays $crd [nikclr $dplayer]\003 draws \002two cards\002 and is skipped - Current Player: [nikclr $nplayer]\003"
}
# Show played wildcard
proc playwild {who chooser} {
unomsg "[nikclr $who]\003 plays [wild] Choose a color [nikclr $chooser]\003"
}
# Show played wild draw four
proc playwildfour {who skipper chooser} {
unomsg "[nikclr $who]\003 plays [wildf] [nikclr $skipper]\003 draws \002four cards\002 and is skipped - Choose a color [nikclr $chooser]\003"
}
# Show played skip card
proc playskip {who crd skipper nplayer} {
unomsg "[nikclr $who]\003 plays $crd\003 and skips [nikclr $skipper]\003 to [nikclr $nplayer]\003"
}
proc showwhodrew {who} {
unomsg "[nikclr $who]\003 \002drew\002 a card"
}
proc playpass {who nplayer} {
unomsg "[nikclr $who]\003 \002passes\002 to [nikclr $nplayer]\003"
}
proc botplaywild {who chooser ncolr nplayer} {
unomsg "[nikclr $who]\003 plays [wild] and chooses $ncolr \003 Current Player: [nikclr $nplayer]\003"
}
# Show played wild draw four
proc botplaywildfour {who skipper chooser choice nplayer} {
unomsg "[nikclr $who]\003 plays [wildf] [nikclr $skipper]\003 draws \002four cards\002 and is skipped. [nikclr $chooser]\003 chooses $choice\003 Current Player: [nikclr $nplayer]\003"
}
# Show a player what they drew
proc showdraw {idx crd} {
global UnoIDX
if {[uno_isrobot $idx]} {return}
unontc [lindex $UnoIDX $idx] "Draw: $crd"
}
# Show Win
proc showwin {who crd} {
unomsg "[nikclr $who]\003 plays $crd \003and \002\00309W\00312i\00313n\00308s\002 [unoad]\003"
}
# Show Win by default
proc showwindefault {who} {
unomsg "[nikclr $who] \002\00309W\00312i\00313n\00308s [unoad]\002\003 By Default \003"
}
# Player Has Uno
proc hasuno {who} {
global UnoChan
putquick "PRIVMSG $UnoChan :\001ACTION says [nikclr $who] \002\00309H\00312a\00313s \00309U\00312n\00313o\00308! \002\003\001"
}
#
# Utility Functions
#
# Check if a timer exists
proc unotimerexists {cmd} {
foreach i [timers] {
if {![string compare $cmd [lindex $i 1]]} then {
return [lindex $i 2]
}
}
return
}
# Sort Scores
proc UnoSortScores {s1 s2} {
global unsortedscores
if {$unsortedscores($s1) > $unsortedscores($s2)} {return 1}
if {$unsortedscores($s1) < $unsortedscores($s2)} {return -1}
if {$unsortedscores($s1) == $unsortedscores($s2)} {return 0}
}
# Calculate Game Running Time
proc game_time {} {
global UnoStartTime
set UnoCurrentTime [unixtime]
set gt [expr ($UnoCurrentTime - $UnoStartTime)]
return $gt
}
# Colorize Nickname
proc nikclr {nick} {
global NickColor
return "\003$NickColor($nick)$nick"
}
proc colornick {pnum} {
global UnoNickColour
set c [lindex $UnoNickColour [expr $pnum-1]]
set nik [format "%02d" $c]
return $nik
}
# Ratio Of Two Numbers
proc get_ratio {num den} {
set n 0.0
set d 0.0
set n [expr $n +$num]
set d [expr $d +$den]
if {$d == 0} {return 0}
set ratio [expr (($n /$d) *100.0)]
return $ratio
}
# Name Of Last Month
proc UnoLastMonthName {month} {
switch $month {
00 {return "Dec"}
01 {return "Jan"}
02 {return "Feb"}
03 {return "Mar"}
04 {return "Apr"}
05 {return "May"}
06 {return "Jun"}
07 {return "Jul"}
08 {return "Aug"}
09 {return "Sep"}
10 {return "Oct"}
11 {return "Nov"}
default {return "???"}
}
}
# String Pad
proc strpad {str len} {
set slen [string length $str]
if {$slen > $len} {return $str}
while {$slen < $len} {
append str " "
incr slen
}
return $str
}
# Uno!
proc unoad {} {
return "\002\0033U\00312N\00313O\00308!\002"
}
# Wild Card
proc wild {} {
return " \0031,8 \002W\0030,3I \0030,4L\0030,12D\002 \003 "
}
# Wild Draw Four Card
proc wildf {} {
return " \0031,8 \002W\0030,3I \0030,4L\0030,12D \0031,8D\0030,3r\0030,4a\0030,12w \0031,8F\0030,3o\0030,4u\0030,12r\002 \003 "
}
#
# Channel And DCC Messages
#
proc unomsg {what} {
global UnoChan
putquick "PRIVMSG $UnoChan :$what"
}
proc unontc {who what} {
global UnoNTC
putquick "$UnoNTC $who :$what"
}
proc unolog {who what} {
putcmdlog "\[$who\] $what"
}
#
# DCC Routines
#
# Show All Players Cards
proc dccUnoHands {hand idx arg} {
global UnoHand RoundRobin
set n 0
while {$n != [llength $RoundRobin]} {
set un [lindex $RoundRobin $n]
unolog $un $UnoHand($un)
incr n
}
}
# Rehash Configuration
proc dcc_unorehash {hand idx arg} {
unolog "$hand" "Rehashing UNO Configuration"
Uno_ReadCFG
return
}
Uno_ReadCFG
UnoReadScores
putlog "Loaded Color Uno $UnoVersion (C) 2004 by Marky"