aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/pai_gow_rules.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/pai_gow_rules.go')
-rw-r--r--internal/casino/pai_gow_rules.go79
1 files changed, 76 insertions, 3 deletions
diff --git a/internal/casino/pai_gow_rules.go b/internal/casino/pai_gow_rules.go
index ced7c47..3a1f4b5 100644
--- a/internal/casino/pai_gow_rules.go
+++ b/internal/casino/pai_gow_rules.go
@@ -21,7 +21,11 @@ type PaiGowConfig struct {
Variant string `yaml:"variant,omitempty"`
Envy bool `yaml:"envy,omitempty"`
PushAceHigh string `yaml:"push_ace_high,omitempty"` // Face Up only: 5_15_40 or 5_12_40
- ActionTicks int `yaml:"action_ticks,omitempty"`
+ // Autoset makes a legal arrangement lock immediately instead of waiting
+ // for the player's "set" confirmation. Players may toggle it per seat with
+ // "autoset on|off"; this field only seeds the default at join.
+ Autoset bool `yaml:"autoset,omitempty"`
+ ActionTicks int `yaml:"action_ticks,omitempty"`
// A2345SecondHighest makes the wheel the second-highest straight, below
// Broadway and above a six-high straight. It defaults to true; only an
// explicit false demotes A2345 to the lowest straight.
@@ -95,7 +99,7 @@ func paiGowRules(c TableConfig) string {
if !r.A2345High() {
wheel = "A2345 is the lowest straight."
}
- s := r.Label() + "\nSeven cards from a fresh 53-card deck. Joker is an ace unless completing a straight, flush or straight flush. " + wheel + "\nSet two cards low and five high: 24 chooses low cards 2/4; 13456 chooses the high hand. High must strictly outrank low. Copies go to the dealer.\nBoth hands win: win; both lose: loss; one wins: push."
+ s := r.Label() + "\nSeven cards from a fresh 53-card deck. Joker is an ace unless completing a straight, flush or straight flush. " + wheel + "\nArrange two cards low and five high: 24 chooses low cards 2/4; 13456 chooses the high hand. Your five-card hand must strictly outrank your two-card hand. The board then shows the five high cards and the two low cards, and set locks the hand and reveals the dealer; copies go to the dealer.\nBoth hands win: win; both lose: loss; one wins: push."
switch r.Variant {
case PaiGowClassic:
s += " Wins pay 0.95:1 (profit rounded down, minimum one credit)."
@@ -106,7 +110,7 @@ func paiGowRules(c TableConfig) string {
case PaiGowNoPush:
s += " No commission; split results use two extra cards: player must beat dealer, ties lose; Joker is ace."
}
- s += "\nHouse way: preserve the best high-hand category, maximize low within it; split two pair, full houses and trips-pairs when this improves low to a pair. Never foul. Use house or set <digits>; set locks your hand. Timeout/disconnect uses house way.\nBet main first, optionally bet <amount> fortune before dealing. Fortune uses all seven cards regardless of split; highest listed prize only, profit odds plus stake returned."
+ s += "\nHouse way: preserve the best high-hand category, maximize low within it; split two pair, full houses and trips-pairs when this improves low to a pair. Never foul. house arranges the house way and still needs set; autoset on locks any legal arrangement immediately (autoset off restores the confirmation). Timeout/disconnect uses house way.\nBet main first, optionally bet <amount> fortune before dealing. Fortune uses all seven cards regardless of split; highest listed prize only, profit odds plus stake returned."
if r.Envy {
s += "\nEnvy: Fortune stake of at least 5 qualifies for fixed bonuses on OTHER active players' hands, never your own or dealer; multiple bonuses add."
} else {
@@ -148,6 +152,7 @@ func pgCompare(a, b pgRank) int {
}
return 0
}
+
// pgNatural keeps the default-rules entry point used by tests and callers that
// do not thread a table configuration. Production evaluation goes through
// pgNaturalRules.
@@ -258,6 +263,73 @@ func pgEvaluateRules(cards []card, a2345High bool) pgRank {
return best
}
+// pgRankWords names card ranks for the hand-strength labels shown at the
+// showdown. Index 14 is ACE so a pair of aces reads "PAIR OF ACES".
+var pgRankWords = [...]string{"", "", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING", "ACE"}
+
+func pgRankWord(v int) string {
+ if v >= 2 && v <= 14 {
+ return pgRankWords[v]
+ }
+ return "?"
+}
+
+func pgRankPlural(v int) string {
+ word := pgRankWord(v)
+ switch word {
+ case "SIX":
+ return "SIXES"
+ case "ACE":
+ return "ACES"
+ default:
+ return word + "S"
+ }
+}
+
+func pgStraightName(v int) string {
+ switch v {
+ case 16:
+ return "ACE-HIGH"
+ case 15:
+ return "FIVE-HIGH"
+ default:
+ return pgRankWord(v) + "-HIGH"
+ }
+}
+
+// pgRankName renders a hand strength for the showdown, for example
+// "PAIR OF ACES", "TWO PAIR ACES AND KINGS" or "STRAIGHT ACE-HIGH".
+func pgRankName(r pgRank) string {
+ switch r[0] {
+ case 9:
+ return "FIVE OF A KIND " + pgRankPlural(r[1])
+ case 8:
+ if r[1] == 16 {
+ return "ROYAL FLUSH"
+ }
+ return "STRAIGHT FLUSH " + pgStraightName(r[1])
+ case 7:
+ return "FOUR OF A KIND " + pgRankPlural(r[1])
+ case 6:
+ return "FULL HOUSE " + pgRankPlural(r[1]) + " FULL OF " + pgRankPlural(r[2])
+ case 5:
+ return "FLUSH " + pgRankWord(r[1]) + "-HIGH"
+ case 4:
+ return "STRAIGHT " + pgStraightName(r[1])
+ case 3:
+ return "THREE OF A KIND " + pgRankPlural(r[1])
+ case 2:
+ return "TWO PAIR " + pgRankPlural(r[1]) + " AND " + pgRankPlural(r[2])
+ case 1:
+ return "PAIR OF " + pgRankPlural(r[1])
+ default:
+ if r[1] > 0 {
+ return "HIGH CARD " + pgRankWord(r[1])
+ }
+ return "HIGH CARD"
+ }
+}
+
type pgSplit struct{ high, low []card }
func pgSet(cards []card, digits []int) pgSplit {
@@ -437,6 +509,7 @@ func pgFortuneRules(cards []card, envy, a2345High bool) (FortuneRow, bool) {
}
return rows[bestIndex], true
}
+
// pgBest keeps the default-rules entry point used by tests and callers that do
// not thread a table configuration. Production evaluation goes through
// pgBestRules.