import * as _ from "lodash" ; import { GetDEBUG } from "../app" ; import { ICavalcadePlayer , ICavalcadeResult } from "./cavalcade-player" ; import { GAME , SetCavalcade } from "./GAME" ; import { GameState , SetGameState } from "./gamestate" ; import { Gui } from "./gui" ; import { Input } from "./input" ; import { CardItem , CardItems , CardSuit } from "./items/cards" ; import { IChoice } from "./link" ; import { Party } from "./party" ; import { IParse , Text } from "./text" ; import { Rand } from "./utility" ; enum CScore { Cavalcade = 0 , Fourkind = 1 , Flush = 2 , FullHouse = 3 , Mixed = 4 , Threekind = 5 , TwoPair = 6 , Pair = 7 } export interface ICavalcadeOpts { stag ? : CardItem ; NextRound ? : ( ) => void ; onPost ? : ( ) => void ; token ? : string ; bet ? : number ; } interface ICavalcadeScore { num : number ; val ? : number ; suit ? : CardSuit ; } export class Cavalcade { static get Score ( ) { return CScore ; } public static CardCountSorter ( a , b ) { if ( a . num === b . num ) { return ( a . val < b . val ) ? - 1 : 1 ; } else { return ( a . num > b . num ) ? - 1 : 1 ; } } public static EvaluateWinnerSorter ( playerA , playerB ) { if ( ! playerA . res && ! playerB . res ) { return 0 ; } else if ( ! playerA . res ) { return 1 ; } else if ( ! playerB . res ) { return - 1 ; } if ( playerA . res . score !== playerB . res . score ) { return playerA . res . score < playerB . res . score ? - 1 : 1 ; } if ( playerA . res . stag !== playerB . res . stag ) { return playerA . res . stag ? 1 : - 1 ; } if ( playerA . res . score === Cavalcade . Score . Mixed ) { return 0 ; } if ( playerA . res . high !== playerB . res . high ) { return playerA . res . high < playerB . res . high ? - 1 : 1 ; } if ( playerA . res . low !== playerB . res . low ) { return playerA . res . low < playerB . res . low ? - 1 : 1 ; } return 0 ; } public Deck : CardItem [ ] ; public players : ICavalcadePlayer [ ] ; public stag : CardItem ; public NextRound : ( ) => void ; public onPost : ( ) => void ; public token : string ; public round : number ; public bet : number ; public pot : number ; public house : CardItem [ ] ; public winner : ICavalcadePlayer ; public winners : ICavalcadePlayer [ ] ; constructor ( players , opts ? ) { opts = opts || { } ; this . Deck = [ ] ; for ( let i = 0 ; i < 5 ; i ++ ) { this . Deck . push ( CardItems . Light [ i ] ) ; } for ( let i = 0 ; i < 5 ; i ++ ) { this . Deck . push ( CardItems . Darkness [ i ] ) ; } for ( let i = 0 ; i < 5 ; i ++ ) { this . Deck . push ( CardItems . Shadow [ i ] ) ; } this . players = players ; this . stag = opts . stag || CardItems . Shadow [ 3 ] ; this . NextRound = opts . NextRound || this . CoinGameRound ; this . onPost = opts . onPost || Gui . NextPrompt ; this . token = opts . token || "coin" ; this . round = 0 ; this . bet = opts . bet || 0 ; this . pot = 0 ; } public PullCard ( ) { const cIdx = Rand ( this . Deck . length ) ; const card = this . Deck [ cIdx ] ; this . Deck . splice ( cIdx , 1 ) ; return card ; } public PrepGame ( keepOut ? ) { SetGameState ( GameState . Cavalcade , Gui ) ; const that = this ; _ . each ( this . players , ( p ) => { if ( ! keepOut ) { p . out = false ; } p . hand = [ ] ; if ( p . out ) { p . folded = true ; } else { p . hand . push ( that . PullCard ( ) ) ; p . hand . push ( that . PullCard ( ) ) ; p . folded = false ; } p . res = undefined ; } ) ; this . house = [ ] ; this . house . push ( this . PullCard ( ) ) ; this . house . push ( this . PullCard ( ) ) ; this . house . push ( this . PullCard ( ) ) ; SetCavalcade ( this ) ; } public Finish ( ) { const cav = this ; _ . each ( cav . players , ( p ) => { if ( p . folded ) { return ; } _ . each ( cav . house , ( h ) => { p . hand . push ( h ) ; } ) ; Text . Add ( "[Poss] hand:<br>" , { Poss : p . Possessive ( ) } ) ; _ . each ( p . hand , ( h , key ) => { if ( key > 0 ) { Text . Add ( ", " ) ; } if ( h === cav . stag ) { Text . Add ( h . name + " (*)" , undefined , "bold" ) ; } else { Text . Add ( h . name ) ; } } ) ; Text . Add ( "<br>" ) ; } ) ; Text . Flush ( ) ; } public EvaluateHand ( hand ) { if ( ! hand || hand . length !== 5 ) { return ; } let hasStag = false ; for ( let i = 0 ; i < 5 ; i ++ ) { if ( hand [ i ] === this . stag ) { hasStag = true ; break ; } } const counts = [ ] ; for ( let i = 0 ; i < 5 ; i ++ ) { if ( hand [ i ] === this . stag ) { continue ; } const cardVal = hand [ i ] . val ; let foundVal = false ; for ( const count of counts ) { if ( cardVal === count . val ) { count . num ++ ; foundVal = true ; break ; } } if ( ! foundVal ) { counts . push ( { num : 1 , val : cardVal } ) ; } } counts . sort ( Cavalcade . CardCountSorter ) ; const suits = [ ] ; for ( let i = 0 ; i < 5 ; i ++ ) { if ( hand [ i ] === this . stag ) { continue ; } const cardSuit = hand [ i ] . suit ; let foundSuit = false ; for ( const suit of suits ) { if ( cardSuit === suit . suit ) { suit . num ++ ; foundSuit = true ; break ; } } if ( ! foundSuit ) { suits . push ( { num : 1 , suit : cardSuit } ) ; } } suits . sort ( Cavalcade . CardCountSorter ) ; if ( hasStag ) { counts [ 0 ] . num ++ ; suits [ 0 ] . num ++ ; } if ( suits [ 0 ] . num === 5 ) { return { score : Cavalcade . Score . Cavalcade , suit : suits [ 0 ] . suit , stag : ( suits [ 0 ] . suit !== this . stag . suit ) ? hasStag : false } ; } if ( counts [ 0 ] . num === 4 ) { return { score : Cavalcade . Score . Fourkind , high : counts [ 0 ] . val , stag : hasStag } ; } if ( suits [ 0 ] . num === 4 ) { return { score : Cavalcade . Score . Flush , suit : suits [ 0 ] . suit , stag : ( suits [ 0 ] . suit !== this . stag . suit ) ? hasStag : false } ; } if ( counts [ 0 ] . num === 3 && counts [ 1 ] . num === 2 ) { return { score : Cavalcade . Score . FullHouse , high : counts [ 0 ] . val , low : counts [ 1 ] . val , stag : hasStag } ; } if ( counts . length === 5 || ( counts . length === 4 && hasStag ) ) { return { score : Cavalcade . Score . Mixed , stag : hasStag } ; } if ( counts [ 0 ] . num === 3 ) { return { score : Cavalcade . Score . Threekind , stag : hasStag , high : counts [ 0 ] . val } ; } if ( counts [ 0 ] . num === 2 && counts [ 1 ] . num === 2 ) { return { score : Cavalcade . Score . TwoPair , high : counts [ 0 ] . val , low : counts [ 1 ] . val , stag : hasStag } ; } if ( counts [ 0 ] . num === 2 ) { return { score : Cavalcade . Score . Pair , high : counts [ 0 ] . val , stag : hasStag } ; } } public SuitStr ( suit ) { switch ( suit ) { case CardSuit . Light : return "Light" ; case CardSuit . Darkness : return "Darkness" ; case CardSuit . Shadow : return "Shadow" ; } } public ResultStr ( res ) { let str ; switch ( res . score ) { case Cavalcade . Score . Cavalcade : str = "Full Cavalcade of " + this . SuitStr ( res . suit ) ; break ; case Cavalcade . Score . Fourkind : str = "Fourkind of " + ( res . high + 1 ) ; break ; case Cavalcade . Score . Flush : str = "Partial flush of " + this . SuitStr ( res . suit ) ; break ; case Cavalcade . Score . FullHouse : str = "Full house of " + ( res . high + 1 ) + " and " + ( res . low + 1 ) ; break ; case Cavalcade . Score . Mixed : str = "Mixed Cavalcade" ; break ; case Cavalcade . Score . Threekind : str = "Threekind of " + ( res . high + 1 ) ; break ; case Cavalcade . Score . TwoPair : str = "Two pairs of " + ( res . high + 1 ) + " and " + ( res . low + 1 ) ; break ; case Cavalcade . Score . Pair : str = "Pair of " + ( res . high + 1 ) ; break ; default : str = "silch" ; } if ( res . stag ) { str += " (Stag)" ; } return str ; } public CoinGameRound ( ) { const party = GAME ( ) . party ; const that = this ; const parse = { token : that . token , bet : ( ) => that . bet . toString ( ) , pot : ( ) => that . pot . toString ( ) , } ; Gui . ClearButtons ( ) ; const options = [ ] ; switch ( that . round ) { case 0 : _ . each ( that . players , ( p ) => { if ( p . folded ) { return ; } p . purse . coin -= that . bet ; that . pot += that . bet ; } ) ; if ( ! GAME ( ) . player . out ) { Text . Add ( "You put [bet] [token]s in the pot. The dealer gives you two cards." , parse ) ; Text . NL ( ) ; } Text . Add ( "There are [pot] [token]s in the pot." , parse , "bold" ) ; Text . NL ( ) ; case 1 : case 2 : case 3 : if ( GAME ( ) . player . out ) { Text . Add ( "You are no longer in the game." ) ; } else if ( GAME ( ) . player . folded ) { Text . Add ( "You have folded." ) ; } else { Text . Add ( "Your cards are " ) ; let card = GAME ( ) . player . hand [ 0 ] ; if ( card === that . stag ) { Text . Add ( card . name + " (*)" , undefined , "bold" ) ; } else { Text . Add ( card . name ) ; } Text . Add ( ", " ) ; card = GAME ( ) . player . hand [ 1 ] ; if ( card === that . stag ) { Text . Add ( card . name + " (*)" , undefined , "bold" ) ; } else { Text . Add ( card . name ) ; } Text . Add ( "." ) ; } Text . NL ( ) ; if ( that . round > 0 ) { Text . Add ( "The dealer reveals a house card." ) ; Text . NL ( ) ; } if ( that . house [ that . round - 1 ] === that . stag ) { Text . Add ( "The revealed card is [stag]!" , { stag : that . stag . name } ) ; Text . NL ( ) ; Text . Add ( "The card is discarded, and the dealer pulls another one from the deck." ) ; that . house [ that . round - 1 ] = that . PullCard ( ) ; Text . Flush ( ) ; Gui . NextPrompt ( ( ) => { Text . Clear ( ) ; that . NextRound ( ) ; } ) ; return ; } Text . Add ( "The house hand is: " ) ; let i ; for ( i = 0 ; i < that . round ; i ++ ) { const card = that . house [ i ] ; Text . Add ( card . name ) ; if ( i < 2 ) { Text . Add ( ", " ) ; } } for ( ; i < 3 ; i ++ ) { const card = that . house [ i ] ; if ( GetDEBUG ( ) ) { Text . Add ( card . name ) ; } Text . Add ( "(hidden)" ) ; if ( i < 2 ) { Text . Add ( ", " ) ; } } Text . Add ( "." ) ; Text . NL ( ) ; Text . Add ( "There are [pot] [token]s in the pot." , parse , "bold" ) ; if ( GAME ( ) . player . folded ) { Input . buttons [ 0 ] . Setup ( "Next" , ( ) => { Text . Clear ( ) ; that . NextRound ( ) ; } , true ) ; } else { Input . buttons [ 0 ] . Setup ( "Call" , ( ) => { Text . Clear ( ) ; Text . Add ( "You call." ) ; Text . NL ( ) ; that . NextRound ( ) ; } , true ) ; Input . buttons [ 4 ] . Setup ( "Raise" , ( ) => { Text . Clear ( ) ; Text . Add ( "You raise the bet." ) ; Text . NL ( ) ; _ . each ( that . players , ( p ) => { if ( p . folded ) { return ; } p . purse . coin -= Math . min ( that . bet , p . purse . coin ) ; that . pot += that . bet ; } ) ; that . NextRound ( ) ; } , that . players [ 0 ] . purse . coin >= that . bet ) ; Input . buttons [ 8 ] . Setup ( "Fold" , ( ) => { Text . Clear ( ) ; Text . Add ( "You fold." ) ; Text . NL ( ) ; GAME ( ) . player . folded = true ; that . NextRound ( ) ; } , true ) ; } break ; default : that . Finish ( ) ; Text . NL ( ) ; that . winners = [ ] ; _ . each ( that . players , ( p ) => { if ( p . folded ) { return ; } that . winners . push ( p ) ; } ) ; _ . each ( that . winners , ( w ) => { Text . Add ( `HAND EVALUATION (${ w . name }):<br>` ) ; w . res = that . EvaluateHand ( w . hand ) ; const result = that . ResultStr ( w . res ) ; Text . Add ( `Hand evaluated as: ${ result }` ) ; Text . NL ( ) ; } ) ; that . winners . sort ( Cavalcade . EvaluateWinnerSorter ) ; const winner = that . winners [ 0 ] ; let idx = 1 ; for ( ; idx < that . winners . length ; idx ++ ) { if ( Cavalcade . EvaluateWinnerSorter ( winner , that . winners [ idx ] ) !== 0 ) { break ; } } that . winners = that . winners . slice ( 0 , idx ) ; if ( that . winners . length > 1 ) { Text . Add ( "There was a draw! The pot is split between the winners." ) ; that . pot = Math . floor ( that . pot / that . winners . length ) ; _ . each ( that . winners , ( w ) => { w . purse . coin += that . pot ; if ( party . InParty ( w as any ) ) { Text . NL ( ) ; parse . pot = that . pot . toString ( ) ; Text . Add ( "The party gains [pot] [token]s!" , parse ) ; } } ) ; } else { that . winner = that . winners [ 0 ] ; Text . Add ( "[name] won the round!" , { name : that . winner . NameDesc ( ) } ) ; that . winner . purse . coin += that . pot ; if ( party . InParty ( that . winner as any ) ) { Text . NL ( ) ; parse . pot = that . pot . toString ( ) ; Text . Add ( "The party gains [pot] [token]s!" , parse ) ; } } that . onPost ( ) ; break ; } Text . Flush ( ) ; that . round ++ ; } }