; Poker Night
; data model (lists/maps) + view{} table + age.* encrypted transport
set %chan #game
set %sb 5
set %bb 10
set %rankmap $map(A,14,K,13,Q,12,J,11,T,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2)
sidebar add poker "♠ Poker Night" poker_open
}
; join/open
alias poker {
age.
send %chan join $age.
me $nick 1000
toast Joined the table — waiting for players
}
on SIGNAL:age_msg {
; $from = sender fp ; $1 = move type ; $2- = payload
if ($1 ==
join) { poker_onjoin
}
elseif ($1 == start) { poker_onstart }
elseif ($1 == act) { poker_onact }
elseif ($1 == board) { poker_onboard }
elseif ($1 == show) { poker_onshow }
}
alias poker_onjoin {
; $2 = fp, $3 = name, $4 = stack
if ($has(%seat,$2) == false) {
set %seat $list() ; ensure containers exist (idempotent-ish)
}
; track roster as a map fp->name and an ordered list
setat %name $2 $3
setat %stack $2 $4
push %seats $2
poker_render
}
; start a hand
alias poker_deal {
set %handid $calc(%handid + 1)
set %board $list()
set %street preflop
set %pot 0
set %tocall %bb
; secret deck (dealer only). seed kept private; revealed at showdown to verify.
set %seed $age.rand(32)
poker_builddeck
; deal 2 hole cards per seat, sealed to each player's +AGE key
set %i 0
foreach %fp %seats {
set %c1 $get(%deck,$calc(%i * 2))
set %c2 $get(%deck,$calc(%i * 2 + 1))
age.seal %fp %c1 %c2
set %i $calc(%i + 1)
}
; community sits after all hole cards
set %base $calc($len(%seats) * 2)
; post blinds (seats 0,1) and open betting
poker_postblinds
age.send %chan start %handid
poker_render
}
alias poker_builddeck {
set %deck $list()
set %keyed $list()
foreach %r $list(2,3,4,5,6,7,8,9,T,J,Q,K,A) {
foreach %s $list(c,d,h,s) {
; keyed shuffle: sort cards by a PRF of (seed, card)
push %keyed $age.sha(%seed%r%s)~%r%s
}
}
set %keyed $sort(%keyed)
foreach %k %keyed {
set %card $get($split(%k,~),1)
push %deck %card
}
}
alias poker_postblinds {
set %sbfp $get(%seats,0)
set %bbfp $get(%seats,1)
setat %bet %sbfp %sb
setat %bet %bbfp %bb
setat %stack %sbfp $calc($get(%stack,%sbfp) - %sb)
setat %stack %bbfp $calc($get(%stack,%bbfp) - %bb)
set %pot $calc(%sb + %bb)
set %turn 2 ; UTG (simplified; heads-up handled by wrap)
if ($len(%seats) < 3) { set %turn 0 }
}
; our sealed hole cards arrive here
on SIGNAL:age_deal {
; $data = "<c1> <c2>"
poker_render
}
; betting
on SIGNAL:poker_fold { age.send %chan act fold }
on SIGNAL:poker_call { age.send %chan act call }
on SIGNAL:poker_raise { age.send %chan act raise %betamount }
on SIGNAL:poker_min { set %betamount %bb }
on SIGNAL:poker_q { set %betamount $calc(%pot / 4) }
on SIGNAL:poker_h { set %betamount $calc(%pot / 2) }
on SIGNAL:poker_t { set %betamount $calc(%pot * 3 / 4) }
on SIGNAL:poker_allin
{ set
%betamount $get(%stack,
$age.
me) }
alias poker_onact {
; $from acted: $2 = fold|check|call|raise ; $3 = amount
set %fp $from
if ($2 == fold) { setat %folded %fp 1 }
elseif ($2 == call) {
set %need $calc(%tocall - $get(%bet,%fp))
setat %bet %fp %tocall
setat %stack %fp $calc($get(%stack,%fp) - %need)
set %pot $calc(%pot + %need)
}
elseif ($2 == raise) {
set %need $calc($3 - $get(%bet,%fp))
setat %bet %fp $3
setat %stack %fp $calc($get(%stack,%fp) - %need)
set %pot $calc(%pot + %need)
set %tocall $3
}
poker_advance
poker_render
}
alias poker_advance {
; next live seat; if betting closed, deal next street (dealer authoritative)
set %turn $calc((%turn + 1) % $len(%seats))
; round closes when every live player has matched %tocall (simplified: no "has acted" bit)
set %open 0
foreach %fp %seats {
if ($get(%folded,%fp) != 1) { if ($get(%bet,%fp) != %tocall) { set %open 1 } }
}
if (%open == 0) { poker_nextstreet }
}
alias poker_nextstreet {
; dealer reveals the next board card(s) from its deck
if (%street == preflop) { set %street flop | poker_reveal 3 }
elseif (%street == flop) { set %street turn | poker_reveal 1 }
elseif (%street == turn) { set %street river | poker_reveal 1 }
elseif (%street == river) { poker_showdown }
set %tocall 0
}
alias poker_reveal {
; $1 = count. dealer pulls from %deck at %base and broadcasts.
set %n 0
while (%n < $1) {
set %card $get(%deck,%base)
push %board %card
age.send %chan board %card
set %base $calc(%base + 1)
set %n $calc(%n + 1)
}
}
on SIGNAL:poker_onboard { push %board $2 | poker_render }
; hand evaluation (returns a sortable score)
alias handscore {
set %ranks $list()
set %hist $map()
set %suits $map()
foreach %card $1- {
set %rv $get(%rankmap,$left(%card,1))
push %ranks %rv
setat %hist %rv $calc($get(%hist,%rv) + 1)
setat %suits $right(%card,1) $calc($get(%suits,$right(%card,1)) + 1)
}
set %ranks $sort(%ranks)
set %top 0
set %pairs 0
foreach %k $keys(%hist) {
set %c $get(%hist,%k)
if (%c > %top) { set %top %c }
if (%c == 2) { set %pairs $calc(%pairs + 1) }
}
foreach
%k $keys(%suits) { if ($get(%suits,
%k) > 4
) { set %
flush 1
} }
set %straight $hasstraight($join($sort($keys(%hist))))
set %cat 0
if (%top == 4) { set %cat 7 }
elseif (%top == 3) { if (%pairs > 0) { set %cat 6 } else { set %cat 3 } }
elseif (%
flush == 1
) { set
%cat 5
}
elseif (%straight == 1) { set %cat 4 }
elseif (%pairs > 1) { set %cat 2 }
elseif (%pairs == 1) { set %cat 1 }
return $calc(%cat * 100 + $get(%ranks,$calc($len(%ranks) - 1)))
}
; returns 1 if the sorted distinct rank-values ($1-) contain a 5-long run.
alias hasstraight {
set %prev 0
set %best 1
foreach %v $1- {
if (%prev > 0) {
if (%v ==
$calc(%prev + 1
)) { set %
run $calc(%
run + 1
) } else { set %
run 1
}
if (%
run >
%best) { set
%best %
run }
}
set %prev %v
}
if (%best > 4) { return 1 }
return 0
}
alias poker_showdown {
set %best 0
set %winner
foreach %fp %seats {
if ($get(%folded,%fp) != 1) {
; needs each player's revealed hole (collected via SIGNAL:poker_onshow); board is public
set %sc $handscore($get(%hole,%fp) $join(%board))
if (%sc > %best) { set %best %sc | set %winner %fp }
}
}
setat %stack %winner $calc($get(%stack,%winner) + %pot)
signal poker_win $get(%name,%winner) %pot
poker_render
}
on SIGNAL:poker_onshow { setat %hole $from $2- }
on SIGNAL:poker_win {
if ($1 ==
$nick) { echo %chan *** you won
%2 chips!
} else { echo %chan ***
$1 wins
%2 }
}
; the table view
alias poker_render {
set %turnfp $get(%seats,%turn)
set %dealerfp $get(%seats,0)
; precompute per-seat display into maps
foreach %fp %seats {
set %bc #3a3350
if (%fp == %turnfp) { set %bc #ffd166 }
setat %seatborder %fp %bc
set %dbg #00000000
set %dl
if (%fp == %dealerfp) { set %dl D | set %dbg #f0c020 }
setat %seatdealer %fp %dl
setat %seatdealerbg %fp %dbg
set %st
if ($get(%bet,%fp) > 0) { set %st BET $get(%bet,%fp) }
if ($get(%folded,%fp) == 1) { set %st FOLD }
setat %seatstatus %fp %st
}
set
%myc $split($get(%myhole,
me))
set %h1 $get(%myc,0)
set %h2 $get(%myc,1)
view {
surface bg #241b3a pad 8 {
column gap 12 pad 4 align center fill {
; seats around the oval — index 0 (us) anchored at the bottom
ring radius 150 {
foreach %fp %seats {
column align center gap 2 {
stack {
surface circle width 54 height 54 bg #6c5ce7 border $get(%seatborder,%fp) {
column align center
{ text
$left($get(%name,
%fp),1
) bold
color #ffffff textsize 20
}
}
surface circle width 20 height 20 bg $get(%seatdealerbg,%fp) align bottom {
text
$get(%seatdealer,
%fp) bold
color #1a1a1a textsize 11
}
}
text
$get(%name,
%fp) bold
color #ffffff textsize 11
text
$get(%stack,
%fp) color #c8c8d8 textsize 11
surface bg
#000000 pad 2
{ text
$get(%seatstatus,
%fp) bold
color #ffd166 textsize 10
}
}
}
}
; the felt centre: pot + community board
surface circle bg #2b2440 pad 14 {
column align center gap 4 {
text POT bold
color #b9a8e0 textsize 12
text
%pot bold
color #ffffff textsize 24
row gap 4 {
card $get(%board,0) width 38 | card $get(%board,1) width 38 | card $get(%board,2) width 38 | card $get(%board,3) width 38 | card $get(%board,4) width 38
}
}
}
; our hole cards
row gap 6 { card %h1 width 50 | card %h2 width 50 }
; actions
row gap 6 fill {
button "FOLD" poker_fold weight 1
button "CALL" poker_call weight 1
button "RAISE" poker_raise weight 1
}
row gap 4 fill {
button "MIN" poker_min weight 1
button "1/4" poker_q weight 1
button "1/2" poker_h weight 1
button "3/4" poker_t weight 1
button "ALL IN" poker_allin weight 1.2
}
}
}
}
}