#!tclxtcp -f

signal error int

set numclients 0
set cards      {c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 \
		d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 \
		h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13 \
		s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13}
		
set rankNames {{1 Ace} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} \
		{9 9} {10 10} {11 Jack} {12 Queen} {13 King} {14 Ace}}

if {![info exists tablenames]} {
	set tablenames {1 2 3 4 5 6 7 8 9 10}
	set clients {}
	set tables {}
	set players {}
	set maxPlayers 8
	set playersNeeded 2
	set anteAmount 5
	set betLimit 50
	set initialCash 10000
	set hands {}
}

proc makeEmptyTable {} {
	global tablenames tables
	set tab [lvarpop tablenames]
	if {[llength $tab]} {
	  keylset tables $tab.players {}
	  keylset tables $tab.waiters {}
	  keylset tables $tab.watchers {}
	  keylset tables $tab.leavers {}
	  keylset tables $tab.active 0
	  keylset tables $tab.round 0
	  keylset tables $tab.pot 0
	  keylset tables $tab.np {}
	} else {
	  return [lindex $tables [random [llength $tables]]]}
}

proc killEmptyTable {t} {
	global tablenames tables
	keyldel tables $t
	lvarpush tablenames $t
}

proc checkEmptyTables {} {
	global tables
	set empty {}
	foreach t [keylkeys tables] {
	  if {[empty $t]} then {
	    lvarpush empty $t
	  }}
	if {[llength $empty]==0} then {
	  makeEmptyTable
	} else {
	if {[llength $empty]>1} then {
	  foreach t [rest [keylkeys tables]] {
	    killEmptyTable $t
	  }
	}}
}

proc name {p} { global players; keylget players $p.name }
proc cash {p} {	global players; keylget players $p.cash }
proc call {p} {	global players; keylget players $p.call }
proc setCall {p a} { global players; keylset players $p.call $a }
proc tableof {p} { global players; keylget players $p.table }
proc bc {p} { global players; keylget players $p.bc }
proc upcards {p} { global players; keylget players $p.uc }
proc downcards {p} { global players; keylget players $p.dc }
proc cards {p} { concat [upcards $p] [downcards $p] }
proc pin {p} { global players; keylget players $p.in }
proc setIn {p} { global players; keylset players $p.in 1}
proc pout {p} { expr "![pin $p]" }
proc setOut {p} { global players; keylset players $p.in 0}
proc hadaturn {p} { global players; keylget players $p.hat }
proc setHadTurn {p} { global players; keylset players $p.hat 1 }
proc setNotHadTurn {p} { global players; keylset players $p.hat 0 }

proc numplayers {t} { llength [players $t] }
proc players {t} { global tables; keylget tables $t.players }
proc numwaiters {t} { llength [waiters $t] }
proc waiters {t} { global tables; keylget tables $t.waiters }
proc numwatchers {t} { llength [watchers $t] }
proc watchers {t} { global tables; keylget tables $t.watchers }
proc active {t} {global tables; keylget tables $t.active}
proc round {t} {global tables; keylget tables $t.round}
proc inactive {t} { expr "![active $t]" }
proc inplayers {t} {
	set result {}
	foreach p [players $t] {
	  if [pin $p] then { lappend result $p }}
	return $result
}
proc recipients {t} {concat [players $t] [waiters $t] [watchers $t]}
proc deck {t} { global tables; keylget tables $t.deck }
proc pot {t} { global tables; keylget tables $t.pot }
proc empty {t} { expr "[numplayers $t]+[numwaiters $t]+[numwatchers $t]==0" }
# next player
proc np {t} { global tables; keylget tables $t.np }
proc room {t} {
 global tables maxPlayers
 expr "$maxPlayers - [numplayers $t]"
}

checkEmptyTables
proc rest {list} {set l $list; lvarpop l; return $l}

proc mySend {p message} {
	whenidle doSend $p $message
}

proc doSend {p message} {
	[bc $p] send $message
}

#	if {[catch {[bc $p] send $message} result]} {
	  # player p has died, so we need to gracefully remove him
	  # from the game.  Not implemented yet.
#	error "Player returned an error"
#	} else {
#	if {[info exists result]} {return $result} else {
#	puts stdout "Result does not exist."}
#	}

proc comtables {p} {
	global tables
	set a1 TABLES
	foreach table [keylkeys tables] {
	  set t1 {}
	  keylset t1 pot [pot $table]
	  foreach pl [players $table] {
	    keylset t1 players.[name $pl] [cash $pl]
	  }
	  foreach wa [waiters $table] {
	    keylset t1 waiters.[name $wa] [cash $wa]
	  }
	  lappend a1 $t1
	}
	return $a1
}

proc comusers {p} {
	global players
	set a1 "USERS [llength $players] "
	foreach p [keylkeys players] {
	  set a1 [concat $a1 [list [concat [name $p] [playerStatus $p]]]]
	}
	return $a1
}

proc member {item list} {
	expr "[lsearch $list $item] != -1"
}

proc playerStatus {p} {
	global tables players
	if {![keylget players $p.table]} then {
		return "NotAtAnyTable 0 [cash $p]"
	} else {
	set t [tableof $p]
	if {[member $p [players $t]]} then {
	   return "playing $t [cash $p]"
	} else {
	if {[member $p [watchers $t]]} then {
	   return "watching $t [cash $p]"
	} else {return "waiting $t [cash $p]"
	}}}
}

proc comtable {p} {
	global tables
	set table [tableof $p]
	set a1 "  Table $table has [numplayers $table] players.  "]
	return $a1
}

proc comwatchTable {p table} {
	global players tables
	if {[pin $p]} then {
	  error "ErrAtTable You are currently playing at table [tableof $p]"
	} else {
	if {![keylget tables $table {}]} then {
		error "ErrNoTable That table does not exist"
	} else {
	set t [tableof $p]
	if {$t} then {
	  stopWatching $p
	  stopWaiting $p
	}
	keylset tables $table.watchers [concat [watchers $table] $p]
	keylset players $p.table $table
	return "WATCH You are watching table $table"}}
}

proc comsignon {p name password} {
	global players initialCash
	keylset players $p.name $name $p.cash $initialCash $p.call 0
	return "[keylget players $p.cash] chips are now yours"
}

proc comjoinTable {p table} {
	global players tables
	if {[expr "[cash $p] <= 0"]} then {
		error "ErrNoCash You don't have enough cash"
	} else {
	if {[pin $p]} then {
		error "ErrAtTable You need to disconnect from your current table first"
	} else {
	if {![keylget tables $table {}]} then {
		error "ErrNoTable That table does not exist"
	} else {
	if {$table==[tableof $p]} then {
		watcherToWaiter $p
	} else {	
	if {[tableof $p]>0} then {
		comexitTable $p
	}

	startWaiting $p $table
	checkEmptyTables
	return "JOIN You are waiting at table $table"
	}}}}
}

proc remove {item list} {
	third [intersect3 $item $list]
}

proc comexitTable {p} {
	global players tables
	if {![keylget players $p {}]} then {
		error "ErrSignon You need to signon first"
	} else {
	if {![keylget players $p.table]} then {
		return "NotAtAnyTable You were not at any table"
	} else {
	if [pin $p] then {
		error "ErrPlaying You are in the middle of a hand"
	} else {
	set table [tableof $p]
	stopWatching $p
	stopWaiting $p

	foreach r [recipients $table] {
	  if {$r!=$p} {doSend $r "Leave [name $p] has left table $table"}
	}

	checkEmptyTables

	# this next line is no good; we need to become a "leaver"
	keylset tables $table.players [remove $p [players $table]]

	keylset players $p.table 0

	return "Leave You have left table $table"
	}}}
}

proc comdeal {p} {
	set t [tableof $p]
	if [inactive $t] {
	  dealTable $t
	  return "Deal Dealt the first two cards"
	} else {error "ErrGameProgress A game is in progress"}
}

proc comfold {p} {
	takeTurn $p fold {}
}

proc comcall {p} {
	takeTurn $p call {}
}

proc comraise {p amount} {
	takeTurn $p raise $amount
}

proc comendgame {p} {
	global tables
	set t [tableof $p]
	if [inactive $t] {
	  return "ErrInactive Your table is already inactive"
	} else {
	keylset tables $t.active 0
	foreach pl [players $t] {
	  setOut $pl
	  setNotHadTurn $pl
	}
	foreach r [recipients $t] {
	  if {$r!=$p} {doSend $r "ENDGAME [name $p] has ended the game"}
	}}
}

proc comlogout {p} {
	global players
	comexitTable $p
	[bc $p] close
	$p close
	keyldel players $p
}

proc comhelpme {p} {
	return "HELP Implemented commands: tables, table, signon <name>
<password>, joinTable <tableName>, exitTable <tableName>,  users,
 helpme."
}

proc userProcess {p c} {
	global clients players
	if {[keylget players $p.name {}]} {
	  puts stdout "[name $p] sends [first $c]"
	} else {
	  puts stdout "Unregistered $p sends [first $c]"}
	set com [first $c]	
	if {[lsearch {tables watchTable signon joinTable exitTable
			helpme leaveSoon table fold call raise logout
			deal users endgame
			} $com]!=-1} {
		return [eval [join "com $com" {}] $p [rest $c]]
		} else {error "ErrUnknown Command $com not known"}
}

proc doBackConnect {} {
	global players newPlayer newPort
	set p $newPlayer
	keylset players $p.bc \
	  [tcp connect [$p hostname] $newPort]
	puts stdout "Back-connection was successful"
	keylset players $p.table 0 $p.cash -1
	keylset players $p.in 0 $p.uc {} $p.dc {} $p.hat 0
	keylset players $p.name "<Unregistered from [$p hostname]>"
}

proc userPrompt {p portnum} {
	global numclients clients newPlayer newPort
	puts stdout "Client sent [first $portnum] as his portnum"
	if {[scan $portnum "%d" port]} {
		puts stdout "Client's hostname is [$p hostname]"
		keylset clients $p $port
		$p command userProcess
		set newPlayer $p
		set newPort $port
		whenidle doBackConnect
		return "LOGIN About to log you in on port $port.  $numclients client(s) currently connected."
	} else {
	  error "ErrNotAPort That was not a port number.  Please resend."
	}
}

proc mylogin {p} {
	global numclients
	incr numclients
	puts stdout \
	 "Connection request from [$p hostname]; $numclients connected"
	$p command userPrompt
	return "Online Casino.  What is your port number:"
}

random seed

if {![catch {set s}]} {puts stdout "Poker server still listening at port \
[$s configure -port]"} else {
	set s [tcp server -port [random 10000] -command mylogin]
	$s start
	puts stdout "Poker server listening at port [$s configure -port]"
}

proc shutdown {} {
	foreach svr [tcp servers] { $svr stop }
	destroy .
}

proc x {list} {return [lindex $list 0]}
proc y {list} {return [lindex $list 1]}

proc dealCard {c turn hand} {
	global hands

	set h [lindex $hands $hand]
	lvarpush h $c
	set hands [lreplace $hands $hand $hand $h]
}

# doStartGame will start the game going if it has ended, and there are
# enough waiters+players at the table

proc doStartGame {t} {
	global playersNeeded

	if {![active $t] && [numwaiters $t]+[numplayers $t]>=$playersNeeded} {
	  doNewDeal $t
	  puts stdout "Starting a new game on table $t"
	}
}

proc stopWatching {p} {
	global tables players
	set t [tableof $p]
	if $t&&[member $p [watchers $t]] then {
		keylset tables $t.watchers [remove $p [watchers $t]]
		mySend $p "STOPWATCH You have stopped watching table $t"
		keylset players $p.table 0
	}
}

proc stopWaiting {p} {
	global tables players
	set t [tableof $p]
	if $t&&[member $p [waiters $t]] then {
		keylset tables $t.waiters [remove $p [waiters $t]]
		keylset players $p.table 0
	}
}

proc startWaiting {p t} {
	global tables players
	foreach r [recipients $t] {
	  doSend $r "Join [name $p] is waiting at table $t"
	}
	checkEmptyTables
	keylset tables $t.waiters [concat [waiters $t] $p]
	keylset players $p.table $t
	
	#  Need to start a game if there are enough people at the
	#  table and no game is in progress

	whenidle doStartGame $t
}

proc watcherToWaiter {p} {
	stopWatching $p
	startWaiting $p
}

proc waiterToPlayer {p} {
	global tables
	set t [tableof $p]
	keylset tables $t.players [concat $p [players $t]]
	foreach r [recipients $t] {
	  mySend $r "Play [name $r] is now a player"}
	}
}

proc playerToWatcher {p} {
	global tables
	set t [tableof $p]
	keylset tables $t.players [remove $p [players $t]]
	keylset tables $t.watchers [concat $p [watchers $t]]
	mySend $p "WATCH You are now a watcher"
	setOut $p
	setNotHadTurn $p
	foreach r [recipients $t] {
	  if {$r!=$p} then {
	  mySend $r "Watch [name $r] is now a watcher"}
	}
}

#  allhadaturn returns 1 (true) if every inplayer at the table t has
#  had a turn and also every player's call amount is 0.  hadaturn
#  indicates that they've been offered a chance to bet or fold.

proc allhadaturn {t} {
	set plist [inplayers $t]
	set result 1
	foreach p $plist {
	  if {![hadaturn $p]||[call $p]>0} then {set result 0}
	}
	return $result
}


#  nextRound
#
#  If this is the last round, need to count up and see who's won, send
#  everyone a message to that effect, and give them their winnings
#
#  Otherwise, need to setNotHadTurn for all inplayers, deal cards, set
#  np of the table to be the first inplayer, and call nextTurn.

proc nextRound {t} {
	global players tables

	if {[round $t]==4||[llength [inplayers $t]]==1} then {
	  #  count up and see who has won
	  set p [winner $t]
	  #  send everyone a message about the result
	  foreach r [recipients $t] {
	    mySend $r "Winner [name [first $p]] wins [pot $t] with [second $p]"
	  }
	  #  give winnings to winner
	  keylset players [first $p].cash [expr "[cash [first $p]] + [pot $t]"]
	  keylset tables $t.pot 0
	  keylset tables $t.active 0
	  doStartGame $t

	} else {

	if {[lempty [inplayers $t]]} then {
	  foreach r [recipients $t] {
	    mySend $r "PassedOut Everyone dropped.  Pot stays, we ante again"
	  }
	  doNextDeal $t

	} else {

	  foreach p [inplayers $t] {setNotHadTurn $p}
	  keylset tables $t.np [first [inplayers $t]]
	  dealCards $t
	}}
}


#  What can be true when you call nextTurn?
#  we can give each player a flag, hadaturn, which is true once they
#  have been given an opportunity to play once.  Then the terminating
#  condition is that every player that is in has hadaturn.

proc nextTurn {t} {
	global tables betLimit

	set p [np $t]
	# p is guaranteed to be IN at this point, or empty list

	if {[allhadaturn $t] || $p == "noplayer"} then {
	  puts stdout "Moving on to next round of dealing.  Round = [round $t]"
	  nextRound $t
	} else {

	if [cash $p]<[call $p] then {
	  mySend $p "LowCash Not enough cash to call."

	  # go to the next player
	  setNextPlayer $p $t
	  setHadTurn $p
							# no current player
	  if [np $t]==$p then {keylset tables $t.np noplayer}
	  playerToWatcher $p
	}

	# tell the player it is his turn to wager, and how much he can wager

	set maxbet [min [cash $p] $betLimit]
	setHadTurn $p
	mySend $p "YourTurn [call $p] $maxbet [cash $p] You can bet [call $p] to call, $maxbet max bet, you have [cash $p]"

	# start a timer waiting for his response

	}
}

#  signal trap sigalrm alarmCommand

proc takeTurn {p command args} {

	set table [tableof $p]

	# check if it is this player's turn now
	if {[np $table]==$p} then {
		# if so, check if it's in time

		# TIME IS NOT BEING CHECKED NOW
		
		# if so, execute the command (fold, call, or raise)
		# else player implicitly folded

		if {$command=="fold"} then { foldPlayer $p } else {
		if {$command=="call"} then { callPlayer $p } else {
		if {$command=="raise"} then { raisePlayer $p $args }}}

		nextTurn $table

	} else {
		error "ErrNotYourTurn It is not your turn"
	}
}


# setNextPlayer assumes that the provided player p is playing at the
# table, and that he/she is still in the hand

proc setNextPlayer {p table} {
	global tables
	
	set ip [inplayers $table]
	if [llength $ip]==1 then {
		keylset tables $table.np "noplayer"
	} else {
	if [set i [lsearch [inplayers $table] $p]]==-1 then {
		error "ErrNotIn Player wasn't in!  Weirdness"
	} else {
	  if {[llength $ip]== $i+1} then {
		keylset tables $table.np [first $ip]
	  } else {
		keylset tables $table.np [lindex $ip [expr $i+1]]
	  }
	}}
}

proc foldPlayer {p} {
	global tables

	setNextPlayer $p [tableof $p]
	setOut $p
	setNotHadTurn $p
	setCall $p 0
	foreach r [recipients [tableof $p]] {
	  mySend $r "Folds [name $p] has folded"
	}
}

proc callPlayer {p} {
	global players tables

	keylset players $p.cash [expr "[cash $p] - [call $p]"]
	keylset players $p.call 0
	set t [tableof $p]
	set pot [pot $t]
	keylset tables $t.pot [expr "$pot + [call $p]"]
	foreach r [recipients $t] {
	  mySend $r "Calls [name $p] [call $p]"
	}
	setNextPlayer $p [tableof $p]
}

# if raise, add the raiseamount to the call amounts of each
# player that is still in at that table

proc raisePlayer {p amount} {
	global players tables

	keylset players $p.cash [expr "[cash $p]-[call $p]- $amount"]
	set t [tableof $p]
	keylset tables $t.pot [expr "[pot $t] + [call $p] + $amount"]
	keylset players $p.call 0
	foreach r [recipients $t] {
	  mySend $r "Raises [name $p] [call $p] $amount"
	}
	foreach r [inplayers $t] {
	  if {$r != $p} then {
	    keylset players $r.call [expr "[call $r] + $amount"]
	  }
	}
	setNextPlayer $p [tableof $p]
}

#  doNewDeal will start a new game

proc doNewDeal {t} {
	global tables

	keylset tables $t.active 1
	keylset tables $t.round 1
	shuffle $t
	doNextDeal $t
}

#  doNextDeal will deal the first round of cards, one face down and
#  one face up to each player
#  doNextDeal should determine whether enough people are at the table
#  to justify starting to deal, and only deal if there are

proc doNextDeal {t} {
	global tables
	dealFirstRound $t
	keylset tables $t.np [first [inplayers $t]]
	nextTurn $t
}

#  dealCards will deal subsequent rounds of cards.  It increments the
#  round number

proc dealCards {t} {
	global tables
	keylset tables $t.round [expr "1+[round $t]"]
	dealMiddleRound $t
	nextTurn $t
}

proc shuffle {table} {
	global tables players cards
	set w [waiters $table]
	while {![lempty $w] && [room $table]} {
		waiterToPlayer [lvarpop w]
	}
	keylset tables $table.waiters $w
	foreach p [players $table] {
		keylset players $p.uc {}
		keylset players $p.dc {}
		setIn $p
	}
	random seed
	set deck2 $cards
	set deck {}
	loop n 0 52 {
	 lvarpush deck [lvarpop deck2 [random [expr 52-$n]]]
	}
	keylset tables $table.deck $deck
}

proc dealFaceUpCard {p} {
	global tables players
	set table [tableof $p]
	keylset players $p.uc [concat [first [deck $table]] [upcards $p]]
	keylset tables $table.deck [rest [deck $table]]
}

proc dealFaceDownCard {p} {
	global tables players
	set table [tableof $p]
	keylset players $p.dc [concat [first [deck $table]] [downcards $p]]
	keylset tables $table.deck [rest [deck $table]]
}

proc dealMiddleRound {table} {
	foreach p [inplayers $table] {dealFaceUpCard $p}
	foreach r [recipients $table] {tellCards $r}
}	

proc dealFirstRound {table} {
	global anteAmount

	foreach p [inplayers $table] {
	  ante $p
	}
	foreach r [recipients $table] {
	  mySend $r "Ante $anteAmount Every player antes $anteAmount"
	}
	foreach p [inplayers $table] {
		  dealFaceUpCard $p
	}
	foreach p [inplayers $table] {
	  dealFaceDownCard $p
	}
	foreach r [recipients $table] {
	  tellCards $r
	}
}


proc tellCards {r} {
	global players tables
	set t [tableof $r]
	foreach p [inplayers $t] {
	  set n [name $p]
	  if {$p==$r} then {
		keylset m $n [list up [upcards $p] down [downcards $p]]
	  } else {
		keylset m $n [list up [upcards $p]]
	  }
	}
	lvarpush m Cards
	mySend $r $m
}

proc ante {p} {
	global players tables anteAmount
	set table [tableof $p]
	if {[expr "[cash $p] < $anteAmount"]} then {
	  mySend $p "LowCash Not enough cash for ante."
	  playerToWatcher $p
	} else {
	  keylset players $p.cash [expr "[cash $p] - $anteAmount"]
	  keylset tables $table.pot [expr "[pot $table] + $anteAmount"]
	}
}

proc first {l} {return [lindex $l 0]}
proc second {l} {return [lindex $l 1]}
proc third {l} {return [lindex $l 2]}
proc fourth {l} {return [lindex $l 3]}
proc fifth {l} {return [lindex $l 4]}


#  Winner will determine the winner from among the inplayers at table
#  t.  It presently assumes there will be no draws.  If there are
#  fewer than five cards in some player's hand, there had better be
#  only one player still in the game.

proc winner {t} {
	set besthand {"Junk" {0 0}}
	set bestplayer {}
	if {[llength [inplayers $t]]==1} then {
		return [list [first [inplayers $t]] "Others Dropping Out"]
	}
	if {[llength [concat [upcards [first [inplayers $t]]] \
			     [downcards [first [inplayers $t]]]]]<5} {
		error "Called winner too early: multiple players, incomplete hands"
	}
	foreach p [inplayers $t] {
	  set n [name $p]
	  keylset m $n [list up [upcards $p] down [downcards $p]]
	  set hand [evalHand [concat [upcards $p] [downcards $p]]]
	  if {[betterHand [second $hand] [second $besthand]]} then {
	    set besthand $hand
	    set bestplayer $p
	  }
	}
	lvarpush m FinalCards
	foreach r [recipients $t] {
	  mySend $r $m
	}
	return [list $bestplayer [first $besthand]]
}

proc betterHand {h1 h2} {
	set m [min [llength $h1] [llength $h2]]
	for {set i 0} {$i<$m} {incr i} {
	  if {[lindex $h1 $i] > [lindex $h2 $i]} then {
	    return 1
	  } else {
	  if {[lindex $h1 $i] < [lindex $h2 $i]} then {
	    return 0
	  }
	}}
}

proc evalHand {h} {
	set r [zeroranks $h]
	set rold [ranks $h]
	set sort [reverse [lsort $r]]
	set s [suits $h]
	set c [countCards $r]
	set m [listmax $rold]
	set v [royalFlush $r $s $m]
	if [sz [first $v]] {return [list "Royal Flush" [concat 9 $v]]}
	set v [straightFlush $r $s $m]
	if [sz [first $v]] {return [list "Straight Flush" [concat 8 $v]]}
	set v [fourKind $c]
	if [sz [first $v]] {return [list "Four of a Kind" [concat 7 $v]]}
	set v [fullHouse $c]
	if [sz [first $v]] {return [list "Full House" [concat 6 $v]]}
	set v [flush $s]
	if [sz [first $v]] {return [list "Flush" [concat 5 $sort]]}
	set v [straight $r $m]
	if [sz [first $v]] {return [list "Straight" [concat 4 $v]]}
	set v [threeKind $c]
	if [sz [first $v]] {return [list "Three of a Kind" [concat 3 $v]]}
	set v [twoPair $c]
	if [sz [first $v]] {return [list "Two Pair" [concat 2 $v]]}
	set v [onePair $c]
	if [sz [first $v]] {return [list "A Pair" [concat 1 $v]]}
	return [list "High Card" [concat 0 $sort]]
}

proc royalFlush {r s m} {
	if {[straight $r $m]==14&&[flush $s]} {return 14} {return 0}
}

proc straightFlush {r s m} {
	if {[set st [straight $r $m]]&&[flush $s]} {return $st} {return 0}
}

proc fourKind {c} {
	if [llength $c]==2\
	  {if [y [first $c]]==4||[y [second $c]]==4\
		{return [sortc $c]}\
		{return 0}}\
	  {return 0}
}

proc fullHouse {c} {
	#Assumes we've already failed the fourKind test.
	if [llength $c]==2 {return [sortc $c]} {return 0}
}

proc sortc {c} {
	set r {}
	set res {}
	foreach p $c {
	 set l {}
	 keylget res [y $p] l
	 keylset res [y $p] [lsort [concat [x $p] $l]]
	}
	loop n 1 6 {
	  if [keylget res $n {}] {lvarpush r [reverse [keylget res $n]]}
	}
	return [join $r]
}

proc reverse {l} {
	set res {}
	foreach e $l {lvarpush res $e}
	return $res
}

proc flush {s} {
	set result 1
	set s1 [lvarpop s]
	foreach c $s {if {$c != $s1} {set result 0}}
	return $result
}

proc straight {r m} {
	set result $m
	loop i [expr "$m - 4"] $m {
	 if [lsearch $r [zeronum $i]]==-1 {set result 0}
	}
	if $result==0&&$m==14 {return [straight [concat 01 $r] 5]}\
	 {return [zeronum $result]}
}

proc threeKind {c} {
	if [llength $c]==3 {
	  if [y [first $c]]==3||[y [second $c]]==3||[y [third $c]]==3\
		 {return [sortc $c]} {return 0}} {return 0}
}

proc twoPair {c} {
	#Assumes we've already failed the threeKind test.
	if [llength $c]==3 {return [sortc $c]} {return 0}
}

proc onePair {c} {
	if [llength $c]==4 {return [sortc $c]} {return 0}
}

proc countCards {r} {
	set res {}
	foreach c $r {
	 if [keylget res $c {}] \
	 {keylset res $c [expr "[keylget res $c] + 1"]} \
	 {keylset res $c 1}
	}
	return $res
}

proc listmax {l} {
	set m 0
	foreach c $l {set m [max $m $c]}
	return $m
}

proc zeronum {n} {
	set b [split $n {}]
	if [llength $b]==1 {set b [concat 0 $b]}
	set b [join $b {}]
	return $b
}

#Strip one leading zero from a number between 1 and 9.
proc sz {n} {
	set b [split $n {}]
	if [llength $b]==2&&[first $b]==0 {return [second $b]}
	return $n
}

proc zeroranks {h} {
	set r [ranks $h]
	foreach c $r {
	lvarpush res [zeronum $c]
	}
	return $res
}

proc ranks {h} {
	foreach c $h {
	 set b [split $c {}]
	 lvarpop b
	 set b [join $b {}]
	 if $b==1 {set b 14}
	 lvarpush r $b
	}
	return $r
}

proc suits {h} {
	foreach c $h {
	lvarpush r [lindex [split $c {}] 0]
	}
	return $r
}

proc card {p whichcard} {
	global hands
	return [lindex [lindex $hands $p] $whichcard]
}
