Blackjack Race

By Degausser on Jan 17, 2017

Blackjack Race is a channel based multiplayer game, supporting up to your standard 7 players.

I saw a few versions of Blackjack on this site, but none of them were scripted well, whether it be the style of code, the game pace or the logic within the code to match the game. So here you go :)

Features include:

  • Simultaneous games within multiple channels at the same time
  • A "first to" style, meaning rather than betting, you're trying to accumulate a certain amount of coins to win
  • Derived from the real casino game, but adapted to suit IRC
  • A real shoe, with shuffling points when shoe density falls below a threshold
  • Accurate dealer simulation, standing with hard 17
  • Strategy play options: !hit !stand !double !split !surrender
  • Resplit as many times as you require! 10 seconds are added to the timer for each "layer" of hands all players go through
  • The Edge - An optional lower boundary which kicks players from the game when overstepped
  • AI opponents, with an intelligence range from 1 to 10
  • Automatic AI participation if only 1 player joins
  • Easily configure options and house rules within the script
  • No messy global variables
  • Possibility to save statistics via external files

Image

To start a game, type !blackjack
To join a game, type !join
To start a game with AI opponents ready to join, type !blackjack N, where N is a number from 1 to 7
To start a game with specific AI opponents, for example Jess and Kyle, type !blackjack Jess Kyle
To leave a game type !replace. This will replace you with an AI opponent
To view a list of AI opponents and their intelligence level, type !ai
To forcefully stop a game, type !blackjack stop OR !stop (Operators only)
To abort the games termination, type !abort
To view a list of ALL commands, type !commands

  • UPDATE 20/01/17 - Added AI, primarily so you can start a game by yourself
  • UPDATE 21/01/17 - Added !split and !surrender, for completions sake. Integration was a bit rough but i scraped by
  • UPDATE 22/01/17 - Fixed the randomization of cards in the shoe. Added !replace
  • UPDATE 24/01/17 - Fixed small conflicting error, added an easter egg
  • UPDATE 27/02/17 - Added house options, optional lower boundary and N card tricks, fixed dealer logic if all surrender

  • UPDATE 24/07/17
  • Added stats, which can be turned on and off. Includes top 5 lists and comparison
  • Top 5 lists can be displayed as: !top5 [-a/-u] [cpg / cph / gr / wr / h2h]
  • User stats can be found with: !stat [-a/-u] nickname
  • Comparisons can be done via: !compare [-a/-u] nickname1 [-a/-u] nickname2
  • !repair can be used by operators to fix the game if it won't start properly or becomes unresponsive
  • !commands is available to view all known commands with possible further detail

  • UPDATE 26/07/17 - Save total coins in external file, assist with external use. See the comments
  • UPDATE 24/09/17 - Added blackjackCoinListener alias to the bottom of the script, used for adding your own functionality each time coins are accumulated
;########################################################
;########################################################
; SETTINGS (for YOU.... yes YOU!!!)
;########################################################
;########################################################

;# THE DEALER HAS AN EDGE. If you win as much as you lose, all player balances will gradually decrease making the 'finish' impossible to reach.
;# to compensate, you can award points for draws, and reward more money for a win than you take away for a loss. The same applies for doubles.
;# for faster games: 200 40 20 30 0 -10 5 10 -3
;# more realistic game values: 150 40 20 30 -10 -25 0 0 -5

alias -l finish return 150
alias -l winDouble return 40
alias -l winSingle return 20
alias -l winBlackjack return 30
alias -l loseSingle return -10
alias -l loseDouble return -25
alias -l drawSingle return 0
alias -l drawDouble return 0
alias -l surrender return -5

;# theEdge - Set to 0 to disable. Kicks players from the game after falling below -N

alias -l theEdge return -100

;# Bonus payouts. N card tricks represent an auto-payout if a player's hand consists of N cards. set to 0 to disable, otherwise set to the amount which should be paid out.

alias -l var5CardTrickPayout return 5
alias -l var6CardTrickPayout return 10
alias -l var7CardTrickPayout return 15
alias -l var5Card21Payout return 20
alias -l var6Card21Payout return 40
alias -l var7Card21Payout return 100

;# House rules. 'decks' can be from 2-8. The other lines respresent rules which can be used in the game. To "activate" a rule, change the 0 to a 1.

alias -l decks return 2
alias -l dealerHitsOnSoft17 return 0
alias -l doubleWith3orMoreCards return 0
alias -l noResplitWithAces return 0
alias -l splitAcesOneCardOnly return 0

;# Colors. 3 are used, each represented below. Use 2 digits, from 00-15

alias -l color1 return 07
alias -l color2 return 03
alias -l color3 return 06

;# AI opponents. Just seperate the names by spaces, if you want to add more.
;# the intelligence levels correlate to the names, so Jess matches 6 and Kyle matches the last 1.

alias -l ai return Jess Trump DeathKnight AIMaster Jerry Lacey Jonorama Degausser NotTheSun Kyle
alias -l aiIntel return 6 9 8 10 3 5 4 7 2 1

;# 'statEnable' signifies whether the statistics will be recorded. 1 represents on, and 0 off
;# 'statDirectory' is your path to stats. there should be no slashes at the beginning or end of the path. use \ if you want to go deeper into folders
;# the other two aliases represent the minimum needed hands/games for a user to be displayed in the top5 lists

alias -l statEnable return 1
alias -l statDirectory return stats\blackjack
alias -l statMinHands return 10
alias -l statMinGames return 2

;########################################################
;########################################################
; TEXT EVENTS
;########################################################
;########################################################

on *:TEXT:*:#:{
  if ($hget($g($chan,1))) {
    if ($hget($g($chan,1), $nick)) && ($hget($g($chan,1), $nick) != check) && (!$isAI($chan, $nick)) {
      if ($timer($+(BlackJack,$chan,DrawTimer))) {
        if ($1 == !hit) || ($1 == !card) drawCardAction $chan $nick
        elseif ($1 == !double) drawCardAction $chan $nick -d
        elseif ($1 == !stand) standAction $chan $nick
        elseif ($1 == !split) splitAction $chan $nick
        elseif ($1 == !surrender) surrenderAction $chan $nick
      }
      if ($1 == !replace) || ($1 == !drop) replaceAction $chan $nick
    }
    elseif ($istok(!join.join,$1,46)) && ($timer($+(BlackJack,$chan,JoinTimer))) addPlayer $chan $nick
    if ($1-2 == !blackjack stop) || ($1 == !stop) triggerBlackjackStop $chan -m $nick
    elseif ($1 == !abort) && ($hget($s($chan), stop)) triggerAbort $1-
    elseif ($1 == !repair) && ($nick isop $chan) triggerRepair $chan
  }
  elseif ($1 == !blackjack) triggerBlackjack $1-
  if ($1 == !stat) triggerStats stat $1-
  elseif ($1 == !coins) triggerStats coins $1-
  elseif ($1 == !commands) triggerCommands $1-
  elseif ($1 == !top5) triggerTop5 $1-
  elseif ($1 == !compare) triggerCompare $1-
  elseif ($1 == !ai) .notice $nick List of computer opponents: $listAI
}

;########################################################
;########################################################
; LITTLE ALIASES
;########################################################
;########################################################

alias -l suits return ♣♠♥♦
alias -l cardify return $replace($1-,10,E,11,J,12,Q,13,K,1,A,E,10)
alias -l bracks return $+($chr(40),$1-,$chr(41))
alias -l cb1 return $+(,$color1,,$1-,)
alias -l cb2 return $+(,$color2,,$1-,)
alias -l cb3 return $+(,$color3,,$1-,)
alias -l c1 return $+(,$color1,$1-,,)
alias -l c2 return $+(,$color2,$1-,,)
alias -l c3 return $+(,$color3,$1-,,)
alias -l f { verif | return $+(",$mircdir,$statDirectory,\,$1,.ini,") }
alias -l l { verif | return $+(",$mircdir,$statDirectory,\,$1,.txt,") }
alias -l getStat return $iif($readini($1,$2,$3),$ifmatch,0)
alias -l fs filter -ffctue 1 32 $1- $1-
alias -l top5 return cph.wr.cpg.gr.h2h
alias -l g return $+(bj_game,_,$1,_,$2)
alias -l a return $+(bj_action,_,$1,_,$2)
alias -l d return $+(bj_deck,_,$1)
alias -l w return $+(bj_score,_,$1)
alias -l s return $+(bj_settings,_,$1)

alias -l hfreedom {
  if ($hget($g($1,1))) hfree $g($1,1)
  if ($hget($a($1,1))) hfree $a($1,1)
  if ($hget($d($1))) hfree $d($1)
  if ($hget($w($1))) hfree $w($1)
  if ($hget($s($1))) hfree $s($1)
  hfreedomMirror $1
}

alias -l hfreedomMirror {
  var %x = 2
  while ($hget($g($1,%x))) {
    hfree $hget($g($1,%x))
    hfree $hget($a($1,%x))
    inc %x
  }
}

alias -l verif {
  if (!isdir($+(",$mircdir,$statDirectory,\,"))) {
    var %x = 1
    while (%x <= $numtok($statDirectory,92)) {
      if (!isdir($+(",$mircdir,$gettok($statDirectory,$+(1-,%x),92),\,"))) .mkdir $+(",$mircdir,$gettok($statDirectory,$+(1-,%x),92),\,")
      inc %x
    }
  }
}

;########################################################
;########################################################
; GAME TRIGGERS
;########################################################
;########################################################

alias triggerBlackjack {
  if (!$2) blackjackStart $chan $nick
  elseif ($istok(1.2.3.4.5.6.7,$2,46)) blackjackStart $chan $nick $2
  elseif ($filterAI($2-)) blackjackStart $chan $nick $ifmatch
  else {
    .notice $nick Invalid parameter. To start a game with AI opponents ready to join, type !blackjack N. N can be a number from 1-7
    .notice $nick You can also specify certain AI opponents you want in the game, for example: !blackjack AIMaster Degausser Trump
  }
}

alias triggerBlackjackStop {
  if ($3 !isop $1) .notice $3 You must be an operator to stop a game!
  else {
    if ($cardsRemaining($1)) {
      if (!$hget($s($1), stop)) stopAction $1 $3
      else .notice $3 The game will end after the current round!
    }
    elseif ($timer($+(BlackJack,$1,JoinTimer))) {
      $+(.timerBlackJack,$chan,*) off
      if ($2 == -m) msg $chan $cb3(Blackjack Race) has been stopped by $c3($3)
      hfreedom $1
    }
    else .notice $3 You can't perform this action now!
  }
}

alias -l triggerAbort {
  if ($timer($+(BlackJack,$chan,DrawTimer))) && ($nick isop $chan) abortAction $chan $nick
  elseif ($nick !isop $chan) .notice $nick You must be an operator to abort the games termination!
  else .notice $nick You can't perform that action now
}

alias -l triggerStats {
  if (!$3) .notice $nick Invalid parameters. Specify a name. The -a flag can be used to specify an AI character like: $+(!,$1) -a Degausser
  elseif (((!$4) && (!$userStats($3))) || (($4) && (!$userStats($4)))) || (($1 == coins) && ((!$userStats($4).user) && (!$userStats($3).user))) .notice $nick Sorry, I haven't heard that name before
  else {
    var %s
    if (!$4) %s = $chan -u $3
    elseif ($3 == -a) && ($userStats($4).ai) && ($1 != coins) %s = $chan -a $4
    else %s = $chan -u $4
    if ($1 == stat) displayStats %s
    elseif ($1 == coins) displayCoins %s
  }
}

alias -l triggerCommands {
  if ($istok(!ai.ai,$2,46)) .notice $nick This will send you a notice with a list of AI opponents you're able to play against
  elseif ($istok(!stat.stat,$2,46)) .notice $nick These will send a message with user statistics for the specified name. You can specify the -a flag to define AI opponents, such as: !stat -a Degausser
  elseif ($istok(!coins.coins,$2,46)) .notice $nick These will send a message with total user coins. Define a user (not AI) by simply specifying a username.
  elseif ($istok(!commands.commands,$2,46)) .notice $nick This will notice you a list of commands.... silly!
  elseif ($istok(!compare.compare,$2,46)) {
    .notice $nick Compare two players based on their stats in comparison to all other players. You can specify two names, and optionally a -a or -u before the names to specify AI or user
    .notice $nick For example, !compare Anne -a Degausser. Example 2: !compare -a Kyle -u Sarah
  }
  elseif ($istok(!blackjack.blackjack,$2,46)) {
    .notice $nick This command will start a game. You can specify the number of AI opponents ready to join by typing !blackjack N, where N is a number from 1-7
    .notice $nick You can also specify specific players by using their names (type !ai for a list) like so: !blackjack Degausser NotTheSun
    .notice $nick If a game is active, channel operators can end the game by typing: !blackjack stop
  }
  elseif ($istok(!replace.replace,$2,46)) .notice $nick Specifically an in-game command to be used if you don't want to play anymore. The game will be counted as a loss
  elseif ($istok(!top5.top5,$2,46)) {
    .notice $nick Displays lists of top 5 players. You can specify a -a or -u flag to view distinct AI or user lists. You can also specify the type of list from: $replace($top5,$chr(46),$chr(32))
    .notice $nick Example 1: !top5 -u wr :: Example 2: !top5 cph
  }
  elseif ($istok(!abort.abort,$2,46)) {
    .notice $nick Allows you to abort the !blackjack stop operation, before a hand finishes
  }
  elseif ($istok(!stop.stop,$2,46)) {
    .notice $nick Stops the entire round. Does not apply game based statistics
  }
  elseif ($istok(!repair.repair,$2,46)) {
    .notice $nick Repairs the game if you're having trouble starting a new game
  }
  else {
    .notice $nick Helpful commands - IN GAME: !hit !stand !double !split !surrender !replace !stop !abort !repair - OUTSIDE: !blackjack !commands !ai !stat !coins !top5 !compare
    .notice $nick Type !commands command for more information on some commands, for example: !commands ai
  }
}

alias -l triggerTop5 {
  var %t = $gettok($top5,1,46)
  if (!$3) {
    if ($istok(-a.-u,$2,46)) displayTop5 $chan $2 %t
    else displayTop5 $chan -n $iif($istok($top5,$2,46),$2,%t)
  }
  elseif ($left($2,1) == $chr(45) && $len($2) == 2) displayTop5 $chan $iif($istok(-a.-u,$2,46),$2,-n) $iif($istok($top5,$3,46),$3,%t)
  elseif ($2) displayTop5 $chan -n $iif($istok($top5,$2,46),$2,%t)
  else displayTop5 $chan -n %t
}

alias -l triggerCompare {
  if ($0 == 3) {
    if ($userStats($2)) && ($userStats($3)) displayCompare -u $2 -u $3 $chan
    else .notice $nick One of more of the specified users could not be found
  }
  elseif ($0 == 5) {
    if ($userStats($3)) && ($userStats($5)) displayCompare $iif($2 == -a,-a,-u) $3 $iif($4 == -a,-a,-u) $5 $chan
    else .notice $nick One of more of the specified users could not be found
  }
  elseif ($0 < 3) .notice $nick You have to specify two users to compare them. Type !compare player1 player2
  elseif ($0 > 5) .notice $nick You've specified to many parameters! To specify users or AI, type: !compare -u user -a AIname
  else {
    if ($userStats($3)) {
      if ($userStats($4)) displayCompare $iif($2 == -a,-a,-u) $3 -u $4 $chan
      elseif ($userStats($2)) displayCompare -u $2 $iif($4` == -a,-a,-u) $3 $chan
    }
    elseif ($userStats($2)) && ($userStats($4)) displayCompare -u $2 $iif($3 == -a,-a,-u) $4 $chan
    else .notice $nick One or more of the specified users are not recognized
  }
}

alias -l triggerRepair {
  if (!$timerCheck($1)) {
    msg $1 $cb3(Blackjack Race) was found in a $c2(broken state) $+ , and has been $c3(repaired)
    hfreedom $1
  }
}

alias -l timerCheck {
  if ($timer($+(blackjack,$1,DrawTimer)).secs isnum) return $true
  if ($timer($+(blackjack,$1,JoinTimer)).secs isnum) return $true
  if ($timer($+(blackjack,$1,AutoTimer)).secs isnum) return $true
  if ($timer($+(blackjack,$1,RepeatGame)).secs isnum) return $true
  if ($timer($+(blackjack,$1,NextRound)).secs isnum) return $true
  if ($timer($+(blackjack,$1,AnnounceWinner)).secs isnum) return $true
  if ($timer($+(blackjack,$1,EndGame)).secs isnum) return $true
  return $false
}

;########################################################
;########################################################
; ACTIONS
;########################################################
;########################################################

alias -l drawCardAction {
  if ($numerifyTotal($cardTotal($1,-u,$2).hard) < 21) && (!$istok(double.stand, $hget($a($1,1), $2), 46)) {
    var %d = $iif(d isin $3,$true,$false)
    if ((%d) && ($numtok($hget($g($1,1), $2), 32) > 2)) && (!$doubleWith3orMoreCards) .notice $nick You can only double with the first two cards!
    else {
      var %card = $drawCard($1), %z
      hadd $g($1,1) $2 $hget($g($1,1), $2) %card
      %z = $numerifyTotal($cardTotal($1,-u,$2).hard)
      if (%z > 20) && (!%d) hadd $a($1,1) $2 stand
      else hadd $a($1,1) $2 $iif(%d,double,hit)
      msg $1 $c2($2) $iif(%d,doubled,hit) and drew: $cardflair(%card) :: Current hand: $cardflair($hget($g($1,1), $2)) $bracks($cardTotal($1,-u,$2))
      if (%z == 21) && ($($+($chr(36),var,$numtok($hget($g($1,1),$2),32),Card21Payout),2)) {
        hinc $w($1) $2 $ifmatch
        msg $1 $c1($2) has hit a $cb3($numtok($hget($g($1,1),$2),32) Card 21) and is rewarded $c2($ifmatch coins)
      }
      elseif (%z < 21) && ($($+($chr(36),var,$numtok($hget($g($1,1),$2),32),CardTrickPayout),2)) {
        hinc $w($1) $2 $ifmatch
        msg $1 $c1($2) has hit a $cb3($numtok($hget($g($1,1),$2),32) Card Trick) and is rewarded $c2($ifmatch coins)
      }
      if ((%d) || (%z > 20)) {
        hinc $s($1) completed 1
        if ($splitHand($1,$2)) createSplits $1 $2 $ifmatch
        elseif ($actionsComplete($1)) checkWinner $1 -f
      }
    }
  }
  else .notice $2 You can't draw anymore cards!
}

alias -l standAction {
  if ($numerifyTotal($cardTotal($1,-u,$2).hard) < 21) && (!$istok(double.stand, $hget($a($1,1), $2), 46)) {
    hadd $a($1,1) $2 stand
    hinc $s($1) completed 1
    msg $1 $c2($2) stands with the hand: $cardflair($hget($g($1,1), $2)) $bracks($cardTotal($1,-u,$2))
    if ($splitHand($1,$2)) createSplits $1 $2 $ifmatch
    elseif ($actionsComplete($1)) checkWinner $1 -f
  }
  else .notice $2 You can't perform this action!
}

alias -l splitAction {
  var %hand = $hget($g($1,1),$2)
  if ($cardPair(%hand)) && (!$istok(double.stand,$hget($a($1,1),$2),46)) {
    if ($noResplitWithAces) && ($gettok($gettok(%hand,1,32),2,95) == A) && ($hget($g($1,2),$2)) .notice $2 You can't split Aces more than once!
    else {
      msg $1 $c2($2) splits the hand: $cardflair($hget($g($1,1),$2)) $bracks($cardTotal($1,-u,$2))
      hadd $newSplitTable($1,$2) $2 $gettok($hget($g($1,1),$2),2,32)
      hadd $g($1,1) $2 $gettok($hget($g($1,1), $2),1,32)
      hinc $s($1) actions 1
      createSplits $1 $2
    }
  }
  else .notice $2 You can't perform this action!
}

alias -l surrenderAction {
  if ($numtok($hget($g($1,1), $2), 32) == 2) && ($numerifyTotal($cardTotal($1,-u,$2).hard) < 21) && (!$hget($m($1,1),$2)) && (!$istok(double.stand, $hget($a($1,1), $2), 46)) {
    msg $1 $c2($2) surrenders their hand: $cardflair($hget($g($1,1), $2)) $bracks($cardTotal($1,-u,$2)) and $c1($iif($surrender > 0,wins,loses) $abs($surrender) $iif($abs($surrender) == 1,coin,coins))
    hdel $a($1,1) $2
    hadd $g($1,1) $2 check
    hinc $w($1) $2 $surrender
    hinc $s($1) completed 1
    processHandStats $1 $2 total 1
    processHandStats $1 $2 lose 1
    processHandStats $1 $2 coins $surrender
    if ($actionsComplete($1)) checkWinner $1 -f
  }
  else .notice $2 You can't perform this action!
}

alias -l replaceAction {
  if (!$istok($hget($s($1),replace),$nick,32)) {
    hadd $s($1) replace $hget($s($1), replace) $2
    msg $1 $cb3($2) $c3(will be replaced after the next round!)
  }
}

alias -l stopAction {
  hadd $s($1) stop $2
  msg $1 $cb3(Blackjack Race) $c3(will be stopped after the next round!)
}

alias -l abortAction {
  if ($hget($s($1),stop)) hdel $s($1) stop
  msg $1 $cb3(Blackjack Race) $c3(will no longer be stopped after this round!)
}

alias -l actionsComplete {
  if ($hget($s($1), completed) == $hget($s($1), actions)) return $true
  return $false
}

;########################################################
;########################################################
; SPLIT TABLES
;########################################################
;########################################################

alias -l newSplitTable {
  var %x = 2
  while (%x) {
    if (!$hget($g($1, %x))) {
      hmake $g($1, %x) 1
      hmake $a($1, %x) 1
      $+(.timerBlackJack,$1,DrawTimer) -o 1 $calc($timer($+(BlackJack,$1,DrawTimer)).secs + 10) checkWinner $1
    }
    if (!$hget($g($1, %x), $2)) return $g($1,%x)
    inc %x
  }
}

alias -l splitHand {
  var %x = 2
  while ($hget($g($1,%x),$2)) {
    if ($numtok($ifmatch,32) == 1) return %x
    inc %x
  }
  return $false
}

;########################################################
;########################################################
; THE EDGE
;########################################################
;########################################################

alias -l offTheEdge {
  if ($theEdge < 0) {
    var %x = 1, %n = $hget($w($1),0).item, %p, %s, %e = $v1, %count = 1, %countvar, %minimum, %a
    while (%x <= %n) {
      %s = $hget($w($1),%x).data
      if (!$len(%countvar)) %countvar = %s
      elseif (%countvar == %s) inc %count
      if (%s > %minimum) || (!$len(%minimum)) %minimum = %s
      inc %x
    }
    if (%count != %n) {
      if (%minimum > %e) %minimum = %e
      %x = 1
      while ($len($hget($w($1),%x).item)) {
        %p = $hget($w($1),%x).item
        %s = $hget($w($1),%p)
        if (%s < %minimum) {
          %a = $iif($isAI($1,%p),-a,-u)
          msg $1 $cb2(%p) has tipped over $c3(The Edge) and fallen into obscurity! $cb3(Goodbye %p $+ !)
          preProcessHandStats $1 %p %a lose
          if (%a == -u) coinSave $1 %p
          hdel $g($1,1) %p
          hdel $w($1) %p
          top5PreWrite $1 %p %a
        }
        else {
          if (%s == %minimum) msg $1 $c2(%p) is on $cb3(The Edge) with $+(04,%s,) coins!
          inc %x
        }
      }
    }
  }
}

;########################################################
;########################################################
; ARTIFICIAL INTELLIGENCE
;########################################################
;########################################################

alias -l getIntel return $iif($findtok($ai,$1,1,32) <= $numtok($aiIntel,32),$iif($gettok($aiIntel,$findtok($ai,$1,1,32),32) isnum 1-10, $v1, 5),5)
alias -l isAI return $istok($hget($s($1), ai),$2,32)

alias -l listAI {
  var %x = 1, %y = $numtok($ai,32), %r, %u
  while (%x <= %y) {
    %u = $gettok($ai,%x,32)
    %r = %r $c2(%u) $c1($bracks($getIntel(%u))) /
    inc %x
  }
  return $left(%r,-1)
}

alias -l filterAI {
  var %x = 1, %y = $numtok($1-,32), %return, %a
  while (%x <= %y) {
    %a = $gettok($1-,%x,32)
    if ($istok($ai,%a,32)) && (!$istok(%return,%a,32)) %return = $addtok(%return,%a,32)
    inc %x
  }
  return %return
}

alias -l generateAI {
  var %x = 1, %y = $numtok($ai,32), %s, %u, %n = $iif($2- isnum 1-7,$iif($2- <= $numtok($ai,32),$2-,$numtok($ai,32)),$numtok($2-,32))
  while (%x <= %n) {
    if ($2- isnum 1-7) {
      while ($istok(%s,%u,32) || !%u) {
        %u = $gettok($ai,$rand(1,$numtok($ai,32)),32)
      }
      %s = %s %u
    }
    elseif ($istok($ai,$gettok($2-,%x,32),32)) %u = $gettok($2-,%x,32)
    else %u = $null
    if (%u) $+(.timerBlackJack,$1,JoinTimer,%u) -o 1 $rand(2,28) addPlayer $1 %u -a
    inc %x
  }
}

alias -l rollAI {
  var %x = 1, %ai = $hget($s($1), ai), %y = $numtok(%ai, 32), %player
  while (%x <= %y) {
    %player = $gettok(%ai,%x,32)
    if (!$istok(double.stand, $hget($a($1,1), %player), 46)) timeAI $1 %player
    inc %x
  }
}

alias -l performAIAction {
  if ($timer($+(BlackJack,$1,DrawTimer))) {
    var %action = stand, %intel = $getIntel($2), %dealer = $cardTotal($1, -d).hard, %hard = $cardTotal($1,-u,$2).hard, %soft = $cardTotal($1,-u,$2).soft, %d = $iif($numtok($hget($g($1,1), $2), 32) == 2 || $doubleWith3orMoreCards,1,0), %s = $iif($cardPair($hget($g($1,1),$2)),$ifmatch,0)
    if (%s == A) && ($hget($g($1,2),$2)) && ($noResplitWithAces) %s = 0
    if (%hard < 12) || (%soft < 8) %action = hit
    if (%intel > 1) && (%hard < 17) %action = hit
    if (%intel > 1) && (%s) && (%soft == 2) %action = split
    if (%intel > 2) && (%dealer < 11) && (%hard isnum 10-11) %action = double
    if (%intel > 2) && (%dealer > 6) && ((%soft > 7) && (%hard > 13)) %action = stand
    if (%intel > 3) && (%dealer isnum 2-6) && (%hard > 11) && (%soft > 7) %action = stand
    if (%intel > 3) && (%s) && (%hard == 16) && (%dealer < 11) %action = split
    if (%intel > 4) && (%dealer isnum 2-3) && (%hard < 14) && (!$istok(double.split,%action,46)) %action = hit
    if (%intel > 4) && (%dealer > 6) && ((%hard < 15) || (%soft < 8) && (!$istok(double.split,%action,46))) %action = hit
    if (%intel > 5) && (%dealer == 9) && (%soft < 9) && (%action != split) %action = hit
    if (%intel > 5) && (%dealer isnum 10-11) && (%soft == 8) %action = hit
    if (%intel > 5) && (%s) && (%dealer isnum 3-6) && ($istok(4.6.12.14.18,%hard,46)) %action = split
    if (%intel > 6) && (%dealer == 10) && (%hard isnum 10-11) %action = hit
    if (%intel > 6) && (%dealer == 9) && (%hard isnum 10-11) %action = double
    if (%intel > 6) && (%dealer == 11) && (%hard < 17) && (%soft > 2) %action = hit
    if (%intel > 7) && (%dealer == 3) && (%hard == 13) && (%soft == 13) %action = stand
    if (%intel > 7) && (%dealer > 7) && (%hard isnum 12-16) && (!$istok(double.split,%action,46)) %action = hit
    if (%intel > 7) && (%s) && (%dealer == 2) && (%hard isnum 4-6) %action = split
    if (%intel > 7) && (%s) && (%dealer == 7) && ($istok(4.6.14.16,%hard,46)) %action = split
    if (%intel > 8) && (%dealer isnum 3-6) && (%hard == 9) %action = double
    if (%intel > 8) && (%dealer isnum 5-6) && (%soft isnum 2-8) && (%hard > 11) %action = $iif(!%d && %soft == 8,stand,double)
    if (%intel > 8) && (%dealer == 7) && (%hard isnum 15-16) && (%action != split) %action = hit
    if (%intel > 8) && (%s) && (%hard == 18) && ($istok(4.5.6.8.9,%dealer,46)) %action = split
    if (%intel > 9) && (%dealer == 4) && (%soft isnum 5-8) && (%hard > 11) && (%action != split) %action = $iif(!%d && %soft == 8,stand,double)
    if (%intel > 9) && (%dealer == 3) && (%soft isnum 7-8) && (%hard > 11) && (%action != split) %action = $iif(!%d && %soft == 8,stand,double)
    if (%action == hit) || ((%action == double) && (!%d)) drawCardAction $1 $2
    elseif (%action == double) drawCardAction $1 $2 -d
    elseif (%action == split) splitAction $1 $2
    else standAction $1 $2
    if (!$istok(double.stand, $hget($a($1,1), $2), 46)) timeAI $1 $2
  }
}

alias -l timeAI {
  $+(.timerBlackJack,$1,DrawTimer,$2) -o 1 $rand(2,7) performAIAction $1 $2
}

;########################################################
;########################################################
; SHOE MANAGEMENT
;########################################################
;########################################################

alias -l createDeck {
  var %i = 1, %d = $calc($hget($s($1), decks) * 52)
  while (%i <= %d) {
    hadd $d($1) %i 1
    inc %i
  }
}

alias -l cardsRemaining return $hget($d($1), 0).item

alias -l drawCard {
  if (!$cardsRemaining($1)) checkShuffle $1
  var %r = $rand(1,$cardsremaining($1)), %t = $hget($d($1), %r).item
  hdel $d($1) %t
  return $+($mid($suits,$calc((%t % 4) + 1),1),_,$cardify($calc((%t % 13) + 1)))
}

;########################################################
;########################################################
; GAME FLOW
;########################################################
;########################################################

alias -l blackjackStart {
  hfreedom $1
  hmake $g($1,1) 1
  hmake $a($1,1) 1
  hmake $d($1) $floor($calc(($iif($decks isnum 1-8,$v1,2) * 52) / 10))
  hmake $w($1) 1
  hmake $s($1) 1
  hadd $s($1) decks $iif($decks isnum 1-8,$v1,2)
  hadd $s($1) stats $iif($statEnable,1,0)
  msg $1 $c2($2) has started a game of $cb3(Blackjack Race!) Type $c2(!join) to join the game, you have $c1(30 seconds!)
  $+(.timerBlackJack,$1,JoinTimer) -o 1 30 startGame $1
  if ($3) generateAI $1 $3-
}

alias -l addPlayer {
  if (!$hget($g($1,1), $2)) {
    hadd $g($1,1) $2 check
    hadd $w($1) $2 0
    if ($3) && (a isin $3) hadd $s($1) ai $iif($hget($s($1), ai),$ifmatch $2,$2)
    msg $1 $c2($2) $+ $iif(($3) && (a isin $3),$+($chr(32),$bracks(AI level $getIntel($2)))) has joined the game! $c1($players($1)) players joined! $iif($hget($g($1,1), 0).item < 7 && $timer($+(BlackJack,$1,JoinTimer)).secs > 1,You've $c1($timer($+(BlackJack,$1,JoinTimer)).secs) seconds left to join!)
    if ($hget($g($1,1), 0).item > 6) startGame $1 -f
  }
}

alias -l startGame {
  $+(.timerBlackJack,$1,JoinTimer*) off
  if (!$players($1)) {
    msg $1 Nobody has joined the game. $c2(1 player) is required! Turning off.
    blackjackStop $1 -f
  }
  else {
    if ($players($1) == 1) {
      while ($players($1) < 2) {
        if (($isAI($1, $hget($g($1,1), 1).item)) && ($numtok($ai,32) < 2)) || (!$ai) addPlayer $1 $+(Agent,$rand(11111,99999)) -a
        else addPlayer $1 $gettok($ai,$rand(1,$numtok($ai,32)),32) -a
      }
    }
    hadd $s($1) original_players $players($1)
    createDeck $1
    var %text = The aim is to win $cb1($finish coins) from the $c2(Dealer) before everyone else. Bet amounts are in units of $winSingle $+ , $cb3($iif($hget($s($1), decks) > 1,$v1 decks,1 deck)) in use. Get ready...
    if ($2) && (f isin $2) msg $1 There are a maximum of $c1($players($1) players!) %text
    else msg $1 $c1(Time is up!) There are $c1($players($1) players!) %text
    $+(.timerBlackJack,$1,RepeatGame) -o 1 10 startRound $1
  }
}

alias -l startRound {
  createDefaults $1
  if ($actionsComplete($1)) $+(.timerBlackJack,$1,AutoTimer) -o 1 2 checkWinner $1 -f
  else {
    $+(.timerBlackJack,$1,DrawTimer) -o 1 30 checkWinner $1
    if ($hget($s($1), ai)) rollAI $1
  }
}

alias -l createDefaults {
  var %x = 1, %p = $players($1), %player, %actions
  hadd $s($1) dealer $drawCard($1)
  hadd $s($1) actions $hget($g($1,1),0).item
  hadd $s($1) completed 0
  while (%x <= %p) {
    %player = $hget($g($1,1), %x).item
    hadd $g($1,1) %player $drawCard($1) $drawCard($1)
    %actions = $iif($numerifyTotal($cardTotal($1,-u,%player).hard) < 21,hit,stand)
    hadd $a($1,1) %player %actions
    if (%actions == stand) hinc $s($1) completed 1
    msg $1 $c2($+(%player,:)) Your cards are: $cardflair($hget($g($1,1), %player)) :: Total: $bracks($cardTotal($1,-u,%player)) $iif(%actions == hit,:: You can: !hit !stand !double $+ $iif($cardPair($hget($g($1,1), %player)),$+($chr(32),!split))) $+ $iif(%actions == hit,$+($chr(32),!surrender)))
    inc %x
  }
  msg $1 The $cb2(Dealer) is showing: $cardflair($hget($s($1), dealer))
}

alias -l createSplits {
  if ($3) {
    var %temp = $hget($g($1,1),$2)
    hadd $g($1,1) $2 $hget($g($1,$3),$2)
    hadd $g($1,$3) $2 %temp
    hadd $a($1,$3) $2 $hget($a($1,1),$2)
  }
  hadd $g($1,1) $2 $hget($g($1,1),$2) $drawCard($1)
  var %one = $iif(($splitAcesOneCardOnly) && ($gettok($gettok($hget($g($1,1),$2),1,32),2,95) == A),1,0), %actions = $iif($numerifyTotal($cardTotal($1,-u,$2).hard) < 21 && !%one,hit,stand)
  hadd $a($1,1) $2 %actions
  msg $1 $c2($+($2,:)) Your cards are: $cardflair($hget($g($1,1), $2)) :: Total: $bracks($cardTotal($1,-u,$2)) $iif(%actions == hit && !%one,:: You can: !hit !stand !double $+ $iif($cardPair($hget($g($1,1), $2)) && (($cardPair($hget($g($1,1), $2)) != A) || (!$noResplitWithAces)),$+($chr(32),!split)))
  if (%actions == stand) {
    hinc $s($1) completed 1
    if ($splitHand($1,$2)) forwardSplitter $1 $2 $splitHand($1,$2)
    elseif ($actionsComplete($1)) checkWinner $1 -f
  }
}

alias -l forwardSplitter {
  createSplits $1-
}

alias -l blackjackStop {
  if ($hget($s($1),stats)) {
    var %x = 1, %f, %u, %p = $players($1), %w, %winner = $3
    while ($hget($g($1,1), %x).item) {
      %u = $ifmatch
      %f = $iif($isAI($1,%u),-a,-u)
      %w = $iif(%winner && ($2 != -f),$iif(%winner == %u,win,lose),draw)
      preProcessHandStats $1 %u %f %w
      top5PreWrite $1 %u %f
      if (%f == -u) coinSave $1 %u
      inc %x
    }
    top5Sort
  }
  hfreedom $1
}

;########################################################
;########################################################
; POST HAND
;########################################################
;########################################################

alias -l checkWinner {
  $+(.timerBlackJack,$1,DrawTimer,*) off
  var %dealer = $dealerOption($1)
  dealerDraw $1 %dealer
  roundDisplay $1 %dealer $iif($2,$2)
}

alias -l roundDisplay {
  var %t = $hget($s($1), dealer), %win = $getWinner($1).win, %draw = $getWinner($1).draw, %lose = $getWinner($1).lose, %finished = $cb1($iif(f isin $3,All hands are finished!,Time is up!))
  var %displaywin = $formatPlayers($1, %win).win, %displaylose = $formatPlayers($1, %lose).lose, %displaydraw = $formatPlayers($1, %draw).draw
  if ($istok(1.2,$2,46)) msg $1 %finished The $cb2(Dealer) shows $cardflair($gettok(%t,1,32)) and $iif($2 = 2,draws the following cards:,drew the card:) $cardflair($gettok(%t,2-,32)) :: Total: $cb2($cardTotal($1, -d).hard)
  elseif ($2 == 0) msg $1 %finished Everyone went $c1(bust) so the $cb2(Dealer) wins
  elseif ($2 == 4) msg $1 %finished Everyone $c1(surrendered) so the $cb2(Dealer) doesn't draw cards
  else msg $1 %finished The $cb2(Dealer) can't make $c1(Blackjack) so doesn't draw cards
  if (%win) msg $1 $c1($iif($numtok(%win, 32) != 1,$v1 players,%win)) beat the dealer $+ $iif($numtok(%win, 32) != 1,: %displaywin,: $gettok(%displaywin,2-,32))
  if (%draw) msg $1 $c1($iif($numtok(%draw, 32) != 1,$v1 players,%draw)) managed to draw $+ $iif($numtok(%draw, 32) != 1,: %displaydraw,: $gettok(%displaydraw,2-,32))
  if (!%win) && (!%draw) && (%lose) && ($istok(1.2,$2,46)) msg $1 The $cb2(Dealer) beat everyone with: $cardflair(%t) :: Total: $cb2($cardTotal($1,-d).hard)
  $+(.timerBlackJack,$1,nextRound) -o 1 2 nextRound $1
}

alias -l checkShuffle {
  var %r = $cardsRemaining($1)
  if (%r < $calc(($players($1) + 1) * 7)) {
    msg $1 $cb3(The cards are getting shuffled and reloaded) $bracks($+($round($calc((%r / ($hget($s($1), decks) * 52)) * 100),2),$chr(37)) density)
    createDeck $1
  }
  else msg $1 $cb3(%r cards) remain in the shoe $bracks($+($round($calc((%r / ($hget($s($1), decks) * 52)) * 100),2),$chr(37)) density)
}

alias -l nextRound {
  msg $1 Current scores: $allScores($1)
  offTheEdge $1
  if ($overallWinner($1)) {
    var %winner = $ifmatch, %coins = $hget($w($1), %winner)
    $+(.timerBlackJack,$1,announceWinner) -o 1 2 msg $1 Okay we have an $cb3(overall winner.) $cb2(%winner) wins the game, finishing with $c1(%coins coins!)
    $+(.timerBlackJack,$1,endGame) -o 1 4 blackjackStop $1 -m %winner
  }
  elseif ($hget($s($1), stop)) {
    $+(.timerBlackJack,$1,announceStop) -o 1 2 msg $1 $cb3(Blackjack Race) was set to be stopped by $c2($hget($s($1), stop)) and has been terminated 
    $+(.timerBlackJack,$1,endGame) -o 1 4 blackjackStop $1 -f
  }
  else {
    hdel -w $a($1,1) *
    hfreedomMirror $1
    $+(.timerBlackJack,$1,ShuffleTimer) -o 1 5 checkShuffle $1
    if ($hget($s($1),replace)) $+(.timerBlackJack,$1,ReplaceUser) -o 1 10 replaceUser $1
    $+(.timerBlackJack,$1,RepeatGame) -o 1 12 startRound $1
  }
}

alias -l replaceUser {
  var %x = 1, %u = $hget($s($1),replace), %y = $numtok(%u,32), %user, %a
  hdel $s($1) replace
  while (%x <= %y) {
    %user = $gettok(%u,%x,32)
    if ($hget($g($1,1), %user)) {
      while (!%a) || ($hget($g($1,1), %a)) {
        %a = $iif($rand(1,20) == 1,$+($chr(74),$chr(109),m,$chr(105),$chr($+(10,1))),$+(Agent,$rand(10000,99999)))
      }
      preProcessHandStats $1 %user -u lose
      top5PreWrite $1 %user -u
      coinSave $1 %user
      hadd $g($1,1) %a $hget($g($1,1),%user)
      hadd $w($1) %a $hget($w($1),%user)
      hdel $g($1,1) %user
      hdel $w($1) %user
      hadd $s($1) ai $iif($hget($s($1), ai),$ifmatch %a,%a)
      msg $1 $cb3(%a) replaces $c2(%user) and joins the game!
    }
    inc %x
  }
}

;########################################################
;########################################################
; CARD FUNCTIONS
;########################################################
;########################################################

alias -l numerifyTotal {
  if ($1- == TOO MANY) return 30
  if ($1- == BLACKJACK) return 22
  return $1-
}

alias -l cardflair {
  tokenize 32 $1-
  var %x = 1 ,%s ,%d
  while ($eval($+($,%x),2)) {
    %d = $remove($ifmatch,_)
    %s = %s $iif($left(%d,1) isin $left($suits,2),%d,$+(04,%d,))
    inc %x
  }
  return %s
}

alias -l cardPair {
  if ($gettok($gettok($1-,1,32),2,95) == $gettok($gettok($1-,2-,32),2-,95)) return $v1
  return $false
}

alias -l cardTotal {
  var %s = $iif($2 == -u,$hget($g($1,1), $3),$iif($2 == -d,$hget($s($1), dealer),$2-)), %x = 1, %c = $numtok(%s, 32), %return = 0, %n, %soft = 0
  while (%x <= %c) {
    %n = $replace($gettok($gettok(%s,%x,32),2,95),A,1,J,10,Q,10,K,10)
    inc %return %n
    if (%n == 1) && (!%soft) && (%return < 12) inc %soft
    elseif (%soft) && (%return > 11) dec %soft
    if (%return > 21) return TOO MANY
    inc %x
  }
  if (%soft) {
    if (%return == 11) return $iif(%x == 3,BLACKJACK,21)
    if (!$prop) return $+(%return,/,$calc(%return + 10))
    if ($prop == hard) return $calc(%return + 10)
  }
  return %return
}

alias -l formatPlayers {
  var %x = 1, %y = $numtok($2-,32), %u, %return, %split = 1, %hand, %i, %c, %d
  while (%x <= %y) {
    %u = $gettok($2-,%x,32)
    %hand = $hget($g($1,%split), %u)
    if ($dealerVS($1, %hand) == $prop) {
      if (!%i) { %return = %return %u $+ : $bracks($cardflair(%hand)) / | %i = 1 }
      else %return = $left(%return,-1) $bracks($cardflair(%hand)) /
      processHandStats $1 %u total 1 $iif($isAI($1, %u),-a)
      processHandStats $1 %u $prop 1 $iif($isAI($1, %u),-a)
      if ($prop == win) && ($numerifyTotal($cardTotal($1, %hand).hard) == 22) {
        hinc $w($1) %u $winBlackjack
        processHandStats $1 %u coins $winBlackjack $iif($isAI($1, %u),-a)
      }
      else {
        %d = $iif($hget($a($1,%split),%u) == Double,$v2,Single)
        %c = $eval($+($,$prop,%d),2)
        hinc $w($1) %u %c
        if (%d == Double) processHandStats $1 %u double 1 $iif($isAI($1, %u),-a)
        processHandStats $1 %u coins %c $iif($isAI($1, %u),-a)
      }
    }
    inc %split
    if ($hget($g($1,%split), %u)) continue
    %split = 1
    %i = 0
    inc %x
  }
  return $left(%return,-1)
}

;########################################################
;########################################################
; DEALER FUNCTIONS
;########################################################
;########################################################

alias -l dealerOption {
  var %x = 1, %y = $hget($g($1,1),0).item, %return = 0, %t, %m = 1, %p, %q = 1
  while (%x <= %y) {
    %p = $hget($g($1,1),%x).item
    if ($hget($g($1,%m),%p) == check) inc %q
    else {
      %t = $numerifyTotal($cardTotal($1, $hget($g($1,%m), %p)).hard)
      if (%t < 22) return 2
      elseif (%t == 22) %return = 1
      inc %m
      if ($hget($g($1,%m),%p)) continue
    }
    %m = 1
    inc %x
  }
  if (%return == 1) && (!$istok(10.11,$cardTotal($1,-d).hard,46)) return 3
  if (%q < %x) return %return
  return 4
}

alias -l dealerDraw {
  while ($cardTotal($1,-d).hard < 17) || (($dealerHitsOnSoft17) && ($cardTotal($1,-d).soft < 8)) {
    if ($istok(0.3.4,$2,46)) break
    hadd $s($1) dealer $hget($s($1), dealer) $drawCard($1)
    if ($2 == 1) break
  }
}

alias -l dealerVS {
  var %cards = $numerifyTotal($cardTotal($1,$2-).hard), %dealer = $numerifyTotal($cardTotal($1,-d).hard)
  if ((%cards < %dealer) && (%dealer < 30)) || (%cards == 30) return lose
  if (%cards == %dealer) return draw
  return win
}

;########################################################
;########################################################
; OTHER FUNCTIONS
;########################################################
;########################################################

alias -l players return $iif($hget($g($1,1), 0).item, $ifmatch, 0)

alias -l getWinner {
  var %return , %x = 1, %p = $players($1), %player, %split = 1, %h
  while (%x <= %p) {
    %player = $hget($g($1,1), %x).item
    if ($hget($g($1,1),%player) != check) {
      %h = $hget($g($1, %split), %player)
      inc %split
      if ($prop == $dealerVS($1,%h)) %return = $addtok(%return,%player,32)
      elseif ($hget($g($1,%split), %player)) continue
      %split = 1
    }
    inc %x
  }
  return %return
}

alias -l allScores {
  var %return, %x = 1, %y = $hget($w($1), 0).item, %s
  while (%x <= %y) {
    %s = $hget($w($1), %x).data
    %return = %return $hget($w($1), %x).item $+ : $iif(%s == 0,0,$+(,$iif(%s < 0,04,$iif(%s >= 100,06,03)),%s,)) ::
    inc %x
  }
  return $left(%return, -2)
}

alias -l overallWinner {
  var %return, %x = 1, %y = $hget($w($1), 0).item, %score = $finish - 1, %data
  while (%x <= %y) {
    %data = $hget($w($1), %x).data
    if (%data >= %score) {
      if (%data > %score) %return = $hget($w($1), %x).item
      else %return = %return $hget($w($1), %x).item
      %score = %data
    }
    inc %x
  }
  if (%x == 2) return $hget($w($1), 1).item
  if ($numtok(%return, 32) == 1) return %return
  return $false
}

;########################################################
;########################################################
; STATISTICS WRITE
;########################################################
;########################################################

alias -l preProcessHandStats {
  if ($hget($s($1),stats)) {
    var %p = $hget($s($1),original_players)
    processHandStats $1 $2 total 1 $3 games
    processHandStats $1 $2 players %p $3 games
    processHandStats $1 $2 $4 1 $3 games
    processHandStats $1 $2 $+($4,_,single) $iif($4 == win,$calc(%p - 1),1) $3 games
    processHandStats $1 $2 coins $iif($hget($w($1),$2),$ifmatch,0) $3 games
  }
}

alias -l processHandStats {
  if ($hget($s($1),stats)) {
    var %a = $iif($5 && a isin $5,$true), %z = $iif($6,$6,hands), %f = $iif(%a,$+(%z,_ai),%z)
    if (!%a) || ($istok($ai,$2,32)) writeini -n $f(%f) $2 $3 $calc($getStat($f(%f),$2,$3) + $4)
  }
}

alias -l userStats {
  if (!$prop) {
    if ($getStat($f($iif($2,$2,hands)),$1,total)) return $true
    if ($getStat($f($iif($2,$2,hands_ai)),$1,total)) return $true
    return $false
  }
  if ($prop == ai) return $iif($getStat($f($+($iif($2,$2,hands),_ai)),$1,total),$true,$false)
  if ($prop == user) return $iif($getStat($f($+($iif($2,$2,hands))),$1,total),$true,$false)
}

alias -l coinSave {
  if ($hget($s($1),stats)) {
    var %new = $iif($hget($w($1),$2),$ifmatch,0)
    writeini -n $f(coins) $2 coins $calc($getStat($f(coins),$2,coins) + %new)
    blackjackCoinListener $2 %new $getStat($f(coins),$2,coins)
  }
}

alias -l top5Write {
  var %a = $iif(a isin $2,$true), %f = $iif($istok(cph.wr,$3,46),$f($iif(%a,hands_ai,hands)),$f($iif(%a,games_ai,games))), %data = $top5Data($1,%f,$3), %l
  if ($len(%data)) {
    %l = $read($l($3),w,$+(*,$chr(32),$1,$iif(%a,$+($chr(32),-a),$null)))
    %l = $iif($readn,$ifmatch,0)
    write $iif(%l,$+(-l,%l)) $l($3) %data $1 $iif(%a,-a,$null)
    writeini -n %f $1 $3 %data
  }
}

alias -l top5Data {
  if ($istok(cpg.cph,$3,46)) return $round($calc($getStat($2,$1,coins) / $getStat($2,$1,total)),2)
  if ($3 == h2h) return $+($round($calc(($getStat($2,$1,win_single) / ($getStat($2,$1,win_single) + $getStat($2,$1,lose_single))) * 100),2),$chr(37))
  if ($3 == wr) return $round($calc(($getStat($2,$1,win) / ($getStat($2,$1,win) + $getStat($2,$1,lose) + $getStat($2,$1,draw))) * 100),2)
  var %t = $getStat($2,$1,total)
  return $round($calc(((($getStat($2,$1,win) * ((%t * ($getStat($2,$1,players) / %t)) / %t)) / %t) / ($getStat($2,$1,players) / %t)) * 100),2)
}

alias -l top5PreWrite {
  var %x = 1
  if ($hget($s($1),stats)) {
    if ($istok($ai,$2,32) || (a !isin $3)) {
      while ($gettok($top5,%x,46)) {
        top5Write $2 $3 $ifmatch
        inc %x
      }
    }
  }
}

alias -l top5Sort {
  var %x = 1, %l
  while ($gettok($top5,%x,46)) {
    %l = $l($ifmatch)
    if ($exists(%l)) fs %l
    inc %x
  }
}

;########################################################
;########################################################
; STATISTICS DISPLAY
;########################################################
;########################################################

alias -l displayStats {
  var %a = $iif((($2 == -a) && $getStat($f(hands_ai),$3,total)) || !$getStat($f(hands),$3,total),$true), %f = $f($+(hands,$iif(%a,_ai))), %f2 = $f($+(games,$iif(%a,_ai))), %x = 1, %c, %g, %h
  var %t = $getStat(%f,$3,total), %w = $getStat(%f,$3,win), %d = $getStat(%f,$3,draw), %o = $round($calc(($getStat(%f,$3,double) / (%w + $getStat(%f,$3,lose) + %d)) * 100),2) $+ $chr(37)
  while ($gettok($top5,%x,46)) {
    %g = $ifmatch
    %h = $read($l(%g),w,$+(*,$chr(32),$3,$iif(%a,$+($chr(32),-a),$null)))
    %c = %c $+($upper(%g),:) $c3($ord($iif($readn,$ifmatch,$lines($l(%g))))) $c2(/)
    inc %x
  }
  msg $1 Play stats for $cb1($3 $+ :) Hands: $c3(%t) $c2(/) Wins: $c3(%w) $c2(/) Draws: $c3(%d) $c2(/) CPH: $c3($top5Data($3,%f,cph)) $c2(/) Win Rate: $c3($top5Data($3,%f,wr)) $c2(/) Double Rate: $c3(%o)
  msg $1 Game stats for $cb1($3 $+ :) Games: $c3($getStat(%f2,$3,total)) $c2(/) Wins: $c3($getStat(%f2,$3,win)) $c2(/) CPG: $c3($top5Data($3,%f2,cpg)) $c2(/) Game Rating: $c3($top5Data($3,%f2,gr)) $c2(/) H2H Tier: $c3($top5Data($3,%f2,h2h))
  msg $1 List stats for $cb1($3 $+ :) $gettok(%c,$+(1-,$calc($numtok(%c,32) - 1)),32)
}

alias -l displayCoins {
  %f = $f(coins)
  if ($ini(%f,$3)) msg $1 Total coins for $cb1($3 $+ :) $c3($getStat(%f,$3,coins))
}

alias -l displayTop5 {
  var %x = 1, %f = $l($3), %l = $lines(%f), %c = 0, %r, %f2, %a, %game = $iif($istok(cpg.gr.h2h,$3,46),games,hands), %y = $iif(%game == games,$statMinGames,$statMinHands), %p
  msg $1 $cb3(Top 5) $c3($iif(a isin $2,robots,$iif(u isin $2,users,players)) for:) $c2($iif($3 == cph,Coins Per Hand,$iif($3 == wr,Win Rate,$iif($3 == cpg,Coins Per Game,$iif($3 == gr,Game Rating,Head to Head Tier))))) $bracks(min %y %game)
  while (%x <= %l) && (%c < 5) {
    %r = $read(%f,%x)
    %p = $gettok(%r,2,32)
    %a = $iif($gettok(%r,3,32) == -a,$true,$false)
    %f2 = $f($iif(%a,$+(%game,_,ai),%game))
    if ($getStat(%f2,%p,total) >= %y) && ((($2 == -a) && (%a)) || (($2 == -u) && (!%a)) || ($2 == -n)) {
      inc %c
      msg $1 $cb1($+(%c,$chr(46))) $c2(%p) $iif(%a,$+($chr(32),$bracks(AI level $getIntel(%p)))) $cb3($bracks($gettok(%r,1,32)))
    }
    inc %x
  }
}

alias -l displayCompare {
  var %x = 1, %a1 = $iif((($1 == -a) && ($getStat($f(hands_ai),$2,total))) || (!$getStat($f(hands),$2,total)),$true,$false), %a2 = $iif((($3 == -a) && ($getStat($f(hands_ai),$4,total))) || (!$getStat($f(hands),$4,total)),$true,$false)
  var %f, %r1, %r2, %t1, %t2
  while (%x <= $numtok($top5,46)) {
    %f = $l($gettok($top5,%x,46))
    %r1 = $read(%f,w,$+(*,$chr(32),$2,$iif(%a1,$+($chr(32),-a),$null)))
    inc %t1 $iif($readn,$ifmatch,$lines(%f))
    %r2 = $read(%f,w,$+(*,$chr(32),$4,$iif(%a2,$+($chr(32),-a),$null)))
    inc %t2 $iif($readn,$ifmatch,$lines(%f))    
    inc %x
  }
  %r2 = $round($calc((%t1 / (%t1 + %t2)) * 100),2)
  %r1 = $calc(100 - %r2)
  if (%r1 > %r2) msg $5 $cb1(>>>) $cb2($2) $cb1(<<<) $cb3($+(%r1,$chr(37))) $c1(vs) $+(%r2,$chr(37)) $c2($4)
  elseif (%r2 > %r1) msg $5 $c2($2) $+(%r1,$chr(37)) $c1(vs) $cb3($+(%r2,$chr(37))) $cb1(>>>) $cb2($4) $cb1(<<<)
  else  msg $5 $cb2($2) $cb3($+(%r1,$chr(37))) $c1(vs) $cb3($+(%r2,$chr(37))) $cb2($4)
}

;########################################################
;########################################################
; HELPER FUNCTIONS (use outside script)
;########################################################
;########################################################

; return coins in coins.ini
alias blackjackGetCoins {
  return $getStat($f(coins),$1,coins)
}

; remove coins from coins.ini
alias blackjackDelCoins {
  var %u = $1, %f = $f(coins)
  writeini -n %f %u coins $calc($getStat(%f,%u,coins) - $iif($2 isnum,$ifmatch,0))
  if ($isid) return $getStat(%f,$1,coins)
}

;########################################################
;########################################################
; ACCUMULATED COINS LISTENER
;########################################################
;########################################################

; listener, each time the user accumulates more coins (runs once per user per game played)
; you can 'transport' the coins accumulated to a separate script/file, and/or delete them
; these coins are displayed by the !coins command, removing them will be reflected.
; you can copy new points ($2) to a global/master file which works with your other scripts, if you want
; take note that $2 and $3 can both be negatives, make proper checks to keep things in accordance with the rest of your script

alias -l blackjackCoinListener {
  ; $1 = the username
  ; $2 = coins added for user after current game
  ; $3 = new total coins for user
  ; your commands here:

  ; example echo after each time coins are added (uncomment to try)
  ; echo -a $1 finished the game, $iif($2 >= 0,winning,losing) $2 coins, and now has $3 total coins!

  ; example removing coins just earnt:
  ; blackjackDelCoins $1 $2

  ; example write to a separate file which holds stars collected by users. Each star is worth 20 coins
  ; also remove N stars worth of coins from the coin bank.
  ; if ($3 > 0) {
  ;   var %file = stars.ini, %coinsPerStar = 20, %stars = $floor($calc($3 / %coinsPerStar))
  ;   writeini %file $1 stars $calc($iif($readini(%file,$1,stars),$v1,0) + %stars)
  ;   blackjackDelCoins $1 $calc($3 - ($3 % %coinsPerStar))
  ; }

  ; hint: removing the if statement surrounding the previous 3 commands will hold it's current functionality,
  ; but will also remove stars from the stars file if the user falls to -20 or less
}

MediaDriven never4get

We have a few active mIRC scripters! Find our small community at irc.lunarirc.net

Comments

Sign in to comment.
VectorOne   -  Jan 17, 2021

Greetings, I came across this script about a year ago and put in into my mirc. It works well, and I thank Degausser for writing it!!! However, I had to add an AWOL componant when players forgot they were playing or left the channel. I also colorized the suits and card values. If you would like to play my version, it can be found in abjects.net in #café (yes, with accent egu; alt-130 in windows) Have fun!

PS. For those who see squigglies in stead of the suits, you likely need at least mirc v7.6 available at www.mirc.com

 Respond  
Illusion   -  Apr 25, 2018

Why am I seeing these symbols instead of hearts, clubs, spades and diamonds in your print screen? Is there a way to fix this so i can view the nice symbols?

Example: cards: ♦2 ♥2 ♥K :: T

Degausser  -  Apr 25, 2018

Something to do with the character encoding. you may need to upgrade mIRC, not entirely sure. The symbols are just for aesthetics though. This reference may help in getting proper character display: https://wiki.anidb.info/w/How_to_get_UTF-8_to_work_with_mIRC

If you still can't get it working, you can make a little edit within the script and step around it:

Line 102: alias -l suits return ♣♠♥♦
Replace it with: alias -l suits return cshd

This will let you use a letter in reference to suit, rather than a card symbol

Illusion  -  Apr 25, 2018

Thanks I will give the update a try. BTW....the game is awesome. You did a great job. I was able to see it thru a liteirc platform and thru mibbit and it looked great. Kudos to you!

Sign in to comment

cptpan   -  Jul 22, 2017

THIS IS SO GOOD. Finally a blackjack script that works and looks good. Thanks so much for this!

How can you check how many coins you have outside of a game?

Degausser  -  Jul 24, 2017

I've updated the script, giving you a few settings and commands to show some statistics. The command list was also getting bulky, so i also added a !commands trigger to view the list of commands. Enjoy Captain xD

cptpan  -  Jul 25, 2017

I kept editing this post so I sorted it into sections.

  • SUGGESTIONS

Does it keep a tally of how many total coins (games x 150 coins) that you've won?

You should also give 2nd and 3rd place coins too!

And maybe coins can be used to by items for the game or just to show off what you've bought? (Not sure how coins are actually used or work in the game, maybe now it just works off the stats and coins are just used to win a round).

I thought you should also have a !pause command (but I think the update means it waits for everyone's turn now anyway)

  • ERRORS

It says: /mkdir: unable to create 'my folder\stats\blackjack' (line 112, script18.mrc)

(I manually made the \stats folder and it seemed to then make the files inside it in a stats\blackjack\ folder on its own)

PROCESSCOMMAND.STATS Unknown command

Not sure what this one is about

  • THANKS

This script is so awesome. Thanks for making it and thanks for updating it so quick!

We've wanted a decent Blackjack script for so long. This is epic.

Degausser  -  Jul 25, 2017

Edited about 3 hours after original post. The source has been edited. I've done most things for you.

Okie day. I suspect the directory isn't being constructed properly because you're using an older version of windows which does not let you create a folder named: stats\blackjack, if a folder named stats\ doesn't already exist. I now create the directories piece by piece, so it should be working. I'm also using mIRC version 7.46 if it helps.

PROCESSCOMMAND.STATS Unknown command <-- I have no idea how you get this. Maybe it was related to the malfunction within the directory structure. If you still get random errors like this, let me know. It's very hard to diagnose without a line number though.

About !pause. Each round, everyone has 30 seconds to complete their actions. Rounds conclude after all players have made their actions, or time is up. If Player A splits his hand, then the most hands per person is Player A with 2 hands. This adds 10 seconds to the clock, so now all players have 40 seconds. If Player B makes 3 splits in the same round, he now has the most hands with 4, so 30 seconds are added to the clock instead of 10. Each additional 'layer' of splits any of the active players reach, 10 seconds are added, but if ALL players reach the next layer, it's still just 10 seconds which are added.

It's a little hard to explain, but basically time remaining = 30 seconds + ((numberOfHandsFromPlayerWithMostSplits - 1) * 10)

The statistics - maybe i wasn't so clear in my explanations here. Firstly, there are flags. -u indicates a user, -a indiciates AI. If you specify -u, and no user under the name is found, it will try to resolve an AI opponent under this name before indicating no users are found. Here are some things you can do with user stats / top 5 lists / comparisons.

!stat -a Degausser - searches for AI opponent named Degausser and displays stats. Fallback is a user
!stat -u Degausser - searches for user named Degausser and displays stats. Fallback is an AI opponent
!stat Degausser - searches for user named Degausser and displays stats. Fallback is an AI opponent

!top5 - displays a list of top 5 players by Coins Per Hand
!top5 -a - displays a list of top 5 AI opponents by Coins Per Hand
!top5 -u - displays a list of top5 Users by Coins Per Hand
!top5 wr - displays a list of players by Win Rate
!top5 -a cpg - displays a list of top5 AI opponents by Coins Per Game
!top5 -u gr - displays a list of top5 Users by Game Rank

CPH = coins per hand
CPG = coins per game
WR = win rate
GR = game rank
H2H = head to head

!compare -a Degausser -u Bob - Compares AI opponent Degausser to user Bob
!compare Degausser -a Bob - Searches for user Degausser and AI opponent Bob, before falling back on AI Opponent Degausser and user Bob

I've also added a !coins command, so that you can see exactly how many coins you have. It works the same way as !stat, but just displays total coins. This is exclusive to users, not AI. All coins accumulated during games will be saved into a file named coins.ini

Finally, the use of coins. This game was made with the purpose of being a fairly robust solution to blackjack on IRC, but only as a base. The script uses the information saved in external files to calculate statistics, so "using coins to buy items" would effectively damage the statistics. I did try and make the script as clear as possible, divided into sections, so that you can navigate your way around and either add or extend functionality. However, I added a couple of global functions at the bottom of the script to help you along.

$blackjackGetCoins(user)
returns the number of coins this user has in his "coin bank". 0 is returned if no coins are found, whether the user exists or not.

/blackjackDelCoins user N
deletes N coins from nickname specified, even if nickname doesn't yet exist.

$blackjackDelCoins(user, N)
works as above, but returns the new number of coins AFTER taking away N coins.

So i guess like this, it can help tie together external scripts which make use of the saved coins.

cptpan  -  Jul 26, 2017

I'm using windows 8.1 but a really old version of mIRC for the bot, but everything is working fine now.

Thanks for being so diligent on updates, this is probably one of the best games on this entire site, and I've been here for YEARS.

Awesome stuff!

Sign in to comment

dma   -  Feb 13, 2017

very good script it's the bomb

 Respond  
Karlien   -  Feb 08, 2017

Works great! Nice game play and options. We're having lots of fun playing this.
Just trying to mess with the colors a tad is a bit tricky, but really they are fine as is :)
~Thanks for your time on this one~

Degausser  -  Feb 13, 2017

No problem :)

dma  -  Feb 18, 2017

Best game here.

Degausser  -  Feb 18, 2017

hehe thanks <3

Quite proud of this one myself, some of the functions really pack a lot of power. Had a lot of fun writing this :)

Illusion  -  Apr 25, 2018

Trying to give this a try now, but this may seem like a dumb question......Why am I seeing a bunch of symbols for the card pics for diamond, club, spade and heart? Is there a way to fix this? Example: cards: ♦2 ♥2 ♥K :: T

newklear  -  Mar 20, 2020

Wow, 3 years ago already! Great script Degausser, love the logic used in the functions, especially the AI :)

Sign in to comment

Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.