| define(case_title, Internal player) | |||||
| --- | |||||
| title: case_title | |||||
| ... | |||||
| # case_title | |||||
| > Difficulty: case_difficulty/100 | |||||
| If `blackjack` is called with the `-i` option, it uses an _internal_ player to play against itself. By default it plays basic strategy. Run | |||||
| ```terminal | |||||
| include(run.sh)dnl | |||||
| ``` | |||||
| and you will get the following report with the results of playing one million hands with basic strategy. | |||||
| ```yaml | |||||
| include(report.yaml) | |||||
| ``` | |||||
| case_nav |
| --- | |||||
| title: Internal player | |||||
| ... | |||||
| # Internal player | |||||
| > Difficulty: 00/100 | |||||
| If `blackjack` is called with the `-i` option, it uses an _internal_ player to play against itself. By default it plays basic strategy. Run | |||||
| ```terminal | |||||
| blackjack -i | |||||
| ``` | |||||
| and you will get the following report with the results of playing one million hands with basic strategy. | |||||
| ```yaml | |||||
| --- | |||||
| rules: | |||||
| decks: 6 | |||||
| hands: 1e+06 | |||||
| hit_soft_17: 1 | |||||
| double_after_split: 1 | |||||
| blackjack_pays: 1.5 | |||||
| rng_seed: -421186149 | |||||
| number_of_burnt_cards: 0 | |||||
| no_negative_bankroll: 0 | |||||
| max_bet: 0 | |||||
| penetration: 0.75 | |||||
| penetration_sigma: 0.05 | |||||
| cpu: | |||||
| user: 1.88959 | |||||
| system: 0.32027 | |||||
| wall: 2.20904 | |||||
| second_per_hand: 2.2e-06 | |||||
| hands_per_second: 4.5e+05 | |||||
| player: | |||||
| wins: 0.445075 | |||||
| pushes: 0.087039 | |||||
| losses: 0.492821 | |||||
| dealer_blackjacks: 0.047582 | |||||
| player_blackjacks: 0.047375 | |||||
| dealer_busts: 0.23537 | |||||
| player_busts: 0.160637 | |||||
| doubled_hands: 0.107207 | |||||
| doubled_wins: 0.0613 | |||||
| insured_hands: 0 | |||||
| insured_wins: 0 | |||||
| number_of_hands: 1e+06 | |||||
| number_of_shuffles: 23215 | |||||
| total_money_waged: 1.23935e+06 | |||||
| worst_bankroll: -7212.5 | |||||
| final_bankroll: -7111 | |||||
| return: -0.007111 | |||||
| variance: 1.32892 | |||||
| deviation: 1.15279 | |||||
| error: 0.00115279 | |||||
| result: "(-0.7 ± 0.2) %" | |||||
| ... | |||||
| ``` | |||||
| ------- | |||||
| :::{.text-center} | |||||
| [Previous](../) | [Index](../) | [Next](../02-always-stand) | |||||
| ::: |
| blackjack -i |
| define(case_title, Always stand) | |||||
| --- | |||||
| title: case_title | |||||
| ... | |||||
| # case_title | |||||
| > Difficulty: case_difficulty/100 | |||||
| To play Blackjack as an “always-stander” run the following command: | |||||
| ```terminal | |||||
| include(run.sh)dnl | |||||
| ``` | |||||
| The UNIX command `yes stand` writes the string “stand” repeteadly to the standard output, which is piped to the executable `blackjack` (assumed to be installed system-wide). The arguments tell Libre Blackjack to play one hundred thousand hands (`-n1e5`) using a flat bet (`flat_bet`, it defaults to a unit bet in each hand) and without asking for insurance if the dealer shows an ace (`no_insurance`). As there is no `blackjack.conf` file, the rules are---as expected---the default ones (see the documentation for details). | |||||
| The `/dev/null` part is important, otherwise Libre Blackjack will think that there is a human at the other side of the table and will | |||||
| 1. run slower (it will add explicit time delays to mimic an actual human dealer), and | |||||
| 2. give all the details of the dealt hands in the terminal as ASCII (actually UTF-8) art | |||||
| This example is only one-way (i.e. the player ignores what the dealer says) so it is better to redirect the standard output to `/dev/null` to save execution time. The results are written as a [YAML](http://yaml.org/)-formatted data to `stderr` by default once the hands are over, so they will show up in the terminal nevertheless. This format is human-friendly (far more than JSON) so it can be easily parsed, but it also allows complex objects to be represented (arrays, lists, etc.). | |||||
| ```yaml | |||||
| include(report.yaml) | |||||
| ``` | |||||
| > **Exercise:** verify that the analytical probability of getting a natural playing with a single deck (for both the dealer and the player) is 32/663 = 0.04826546... | |||||
| case_nav |
| --- | |||||
| title: Always stand | |||||
| ... | |||||
| # Always stand | |||||
| > Difficulty: 02/100 | |||||
| To play Blackjack as an “always-stander” run the following command: | |||||
| ```terminal | |||||
| yes stand | blackjack -n1e5 --flat_bet --no_insurance > /dev/null | |||||
| ``` | |||||
| The UNIX command `yes stand` writes the string “stand” repeteadly to the standard output, which is piped to the executable `blackjack` (assumed to be installed system-wide). The arguments tell Libre Blackjack to play one hundred thousand hands (`-n1e5`) using a flat bet (`flat_bet`, it defaults to a unit bet in each hand) and without asking for insurance if the dealer shows an ace (`no_insurance`). As there is no `blackjack.conf` file, the rules are---as expected---the default ones (see the documentation for details). | |||||
| The `/dev/null` part is important, otherwise Libre Blackjack will think that there is a human at the other side of the table and will | |||||
| 1. run slower (it will add explicit time delays to mimic an actual human dealer), and | |||||
| 2. give all the details of the dealt hands in the terminal as ASCII (actually UTF-8) art | |||||
| This example is only one-way (i.e. the player ignores what the dealer says) so it is better to redirect the standard output to `/dev/null` to save execution time. The results are written as a [YAML](http://yaml.org/)-formatted data to `stderr` by default once the hands are over, so they will show up in the terminal nevertheless. This format is human-friendly (far more than JSON) so it can be easily parsed, but it also allows complex objects to be represented (arrays, lists, etc.). | |||||
| ```yaml | |||||
| --- | |||||
| rules: | |||||
| decks: 6 | |||||
| hands: 100000 | |||||
| hit_soft_17: 1 | |||||
| double_after_split: 1 | |||||
| blackjack_pays: 1.5 | |||||
| rng_seed: -2067081387 | |||||
| number_of_burnt_cards: 0 | |||||
| no_negative_bankroll: 0 | |||||
| max_bet: 0 | |||||
| penetration: 0.75 | |||||
| penetration_sigma: 0.05 | |||||
| cpu: | |||||
| user: 0.238384 | |||||
| system: 0.226465 | |||||
| wall: 0.462519 | |||||
| second_per_hand: 4.6e-06 | |||||
| hands_per_second: 2.2e+05 | |||||
| player: | |||||
| wins: 0.38547 | |||||
| pushes: 0.04744 | |||||
| losses: 0.56709 | |||||
| dealer_blackjacks: 0.04749 | |||||
| player_blackjacks: 0.04803 | |||||
| dealer_busts: 0.27168 | |||||
| player_busts: 0 | |||||
| doubled_hands: 0 | |||||
| doubled_wins: 0 | |||||
| insured_hands: 0 | |||||
| insured_wins: 0 | |||||
| number_of_hands: 100000 | |||||
| number_of_shuffles: 2070 | |||||
| total_money_waged: 100000 | |||||
| worst_bankroll: -15870.5 | |||||
| final_bankroll: -15868 | |||||
| return: -0.15868 | |||||
| variance: 0.984727 | |||||
| deviation: 0.992334 | |||||
| error: 0.00313804 | |||||
| result: "(-15.9 ± 0.6) %" | |||||
| ... | |||||
| ``` | |||||
| > **Exercise:** verify that the analytical probability of getting a natural playing with a single deck (for both the dealer and the player) is 32/663 = 0.04826546... | |||||
| ------- | |||||
| :::{.text-center} | |||||
| [Previous](../00-internal) | [Index](../) | [Next](../05-no-bust) | |||||
| ::: |
| yes stand | blackjack -n1e5 --flat_bet --no_insurance > /dev/null |
| fifo |
| define(case_title, No-bust strategy) | |||||
| --- | |||||
| title: case_title | |||||
| ... | |||||
| # case_title | |||||
| > Difficulty: case_difficulty/100 | |||||
| This directory shows how to play a “no-bust” strategy, i.e. not hitting any hand higher or equal to hard twelve with Libre Blackjack. The communication between the player and the back end is through standard input and output. The player reads from its standard input Libre Blackjack's commands and writes to its standard output the playing commands. In order to do this a FIFO (a.k.a. named pipe) is needed. So first, we create it (if it is not already created): | |||||
| ```terminal | |||||
| mkfifo fifo | |||||
| ``` | |||||
| Then we execute `blackjack`, piping its output to the player (say `no-bust.pl`) and reading the standard input from `fifo`, whilst at the same time we redirect the player's standard output to `fifo`: | |||||
| ```terminal | |||||
| include(run.sh)dnl | |||||
| ``` | |||||
| As this time the player is coded in an interpreted langauge, it is far smarter than the previous `yes`-based player. So the player can handle bets and insurances, and there is not need to pass the options `--flat_bet` nor `--no_insurance` (though they can be passed anyway). Let us take a look at the Perl implementation: | |||||
| ```perl | |||||
| include(no-bust.pl)dnl | |||||
| ``` | |||||
| The very same player may be implemented as a shell script: | |||||
| ```bash | |||||
| include(no-bust.sh)dnl | |||||
| ``` | |||||
| To check these two players give the same results, make them play against Libre Blackjack with the same seed (say one) and send the YAML report to two different files: | |||||
| ```terminal | |||||
| include(diff.sh) | |||||
| esyscmd(diff perl.yml shell.yml)dnl | |||||
| ``` | |||||
| As expected, the reports are the same. They just differ in the speed because the shell script is orders of magnitude slower than its Perl-based counterpart. | |||||
| > **Exercise:** modify the players so they always insure aces and see if it improves or degrades the result. | |||||
| case_nav |
| --- | |||||
| title: No-bust strategy | |||||
| ... | |||||
| # No-bust strategy | |||||
| > Difficulty: 05/100 | |||||
| This directory shows how to play a “no-bust” strategy, i.e. not hitting any hand higher or equal to hard twelve with Libre Blackjack. The communication between the player and the back end is through standard input and output. The player reads from its standard input Libre Blackjack's commands and writes to its standard output the playing commands. In order to do this a FIFO (a.k.a. named pipe) is needed. So first, we create it (if it is not already created): | |||||
| ```terminal | |||||
| mkfifo fifo | |||||
| ``` | |||||
| Then we execute `blackjack`, piping its output to the player (say `no-bust.pl`) and reading the standard input from `fifo`, whilst at the same time we redirect the player's standard output to `fifo`: | |||||
| ```terminal | |||||
| if test ! -e fifo; then | |||||
| mkfifo fifo | |||||
| fi | |||||
| blackjack -n1e5 < fifo | ./no-bust.pl > fifo | |||||
| ``` | |||||
| As this time the player is coded in an interpreted langauge, it is far smarter than the previous `yes`-based player. So the player can handle bets and insurances, and there is not need to pass the options `--flat_bet` nor `--no_insurance` (though they can be passed anyway). Let us take a look at the Perl implementation: | |||||
| ```perl | |||||
| #!/usr/bin/perl | |||||
| # this is needed to avoid deadlock with the fifo | |||||
| STDOUT->autoflush(1); | |||||
| while ($command ne "bye") { | |||||
| # do not play more than a number of commands | |||||
| # if the argument -n was not passed to blackjack | |||||
| if ($i++ == 123456789) { | |||||
| print "quit\n"; | |||||
| exit; | |||||
| } | |||||
| # read and process the commands | |||||
| chomp($command = <STDIN>); | |||||
| if ($command eq "bet?") { | |||||
| print "1\n"; | |||||
| } elsif ($command eq "insurance?") { | |||||
| print "no\n"; | |||||
| } elsif ($comm eq "play?") { | |||||
| print "count\n"; | |||||
| chomp($count = <STDIN>); # the count | |||||
| chomp($play = <STDIN>); # again the "play?" query | |||||
| if ($count < 12) { | |||||
| print "hit\n"; | |||||
| } else { | |||||
| print "stand\n"; | |||||
| } | |||||
| } | |||||
| } | |||||
| ``` | |||||
| The very same player may be implemented as a shell script: | |||||
| ```bash | |||||
| #!/bin/sh | |||||
| while read command | |||||
| do | |||||
| if test "${command}" = 'bye'; then | |||||
| exit | |||||
| elif test "${command}" = 'bet?'; then | |||||
| echo 1 | |||||
| elif test "${command}" = 'insurance?'; then | |||||
| echo "no" | |||||
| elif test "`echo ${command} | cut -c-5`" = 'play?'; then | |||||
| echo "count" | |||||
| read count | |||||
| read play # blackjack will ask again for 'play?' | |||||
| if test ${count} -lt 12; then | |||||
| echo "hit" | |||||
| else | |||||
| echo "stand" | |||||
| fi | |||||
| fi | |||||
| done | |||||
| ``` | |||||
| To check these two players give the same results, make them play against Libre Blackjack with the same seed (say one) and send the YAML report to two different files: | |||||
| ```terminal | |||||
| blackjack -n1e3 --rng_seed=1 --yaml_report=perl.yml \ | |||||
| < fifo | ./no-bust.pl > fifo | |||||
| blackjack -n1e3 --rng_seed=1 --yaml_report=shell.yml \ | |||||
| < fifo | ./no-bust.sh > fifo | |||||
| diff perl.yml shell.yml | |||||
| 15,19c15,19 | |||||
| < user: 0 | |||||
| < system: 0.022603 | |||||
| < wall: 0.034317 | |||||
| < second_per_hand: 3.4e-05 | |||||
| < hands_per_second: 2.9e+04 | |||||
| --- | |||||
| > user: 0.06838 | |||||
| > system: 0.13676 | |||||
| > wall: 11.1446 | |||||
| > second_per_hand: 1.1e-02 | |||||
| > hands_per_second: 9.0e+01 | |||||
| ``` | |||||
| As expected, the reports are the same. They just differ in the speed because the shell script is orders of magnitude slower than its Perl-based counterpart. | |||||
| > **Exercise:** modify the players so they always insure aces and see if it improves or degrades the result. | |||||
| ------- | |||||
| :::{.text-center} | |||||
| [Previous](../02-always-stand) | [Index](../) | [Next](../08-mimic-the-dealer) | |||||
| ::: |
| ../../blackjack -n100000 --rng_seed=1 --yaml_report=perl.yml < fifo | ./no-bust.pl > fifo | |||||
| ../../blackjack -n100000 --rng_seed=1 --yaml_report=shell.yml < fifo | ./no-bust.awk > fifo | |||||
| # diff perl.yml shell.yml |
| #!/usr/bin/gawk -f | |||||
| # mawk does not work, it hangs due to the one-sided FIFO | |||||
| { | |||||
| if (n++ > 1234567) { | |||||
| print "quit"; | |||||
| } | |||||
| } | |||||
| /bet\?/ { | |||||
| print "1" | |||||
| fflush() | |||||
| } | |||||
| /insurance\?/ { | |||||
| print "no" | |||||
| fflush() | |||||
| } | |||||
| /play\?/ { | |||||
| if ($2 < 12) { | |||||
| print "hit"; | |||||
| } else { | |||||
| print "stand"; | |||||
| } | |||||
| fflush() | |||||
| } | |||||
| /bye/ { | |||||
| exit; | |||||
| } |
| #!/usr/bin/perl | |||||
| # this is needed to avoid deadlock with the fifo | |||||
| STDOUT->autoflush(1); | |||||
| while ($command ne "bye") { | |||||
| # do not play more than a number of commands | |||||
| # if the argument -n was not passed to blackjack | |||||
| if ($i++ == 1234567) { | |||||
| print "quit\n"; | |||||
| } | |||||
| # read and process the commands | |||||
| chomp($command = <STDIN>); | |||||
| if ($command eq "bet?") { | |||||
| print "1\n"; | |||||
| } elsif ($command eq "insurance?") { | |||||
| print "no\n"; | |||||
| } elsif (substr($command, 0, 5) eq "play?") { | |||||
| @tokens = split(/ /, $command); | |||||
| if ($tokens[1] < 12) { | |||||
| print "hit\n"; | |||||
| } else { | |||||
| print "stand\n"; | |||||
| } | |||||
| } | |||||
| } |
| #!/bin/sh | |||||
| i=0 | |||||
| while read command | |||||
| do | |||||
| i=$((i+1)) | |||||
| if test ${i} -ge 12345; then | |||||
| echo "quit" | |||||
| elif test "${command}" = 'bye'; then | |||||
| exit | |||||
| elif test "${command}" = 'bet?'; then | |||||
| echo 1 | |||||
| elif test "${command}" = 'insurance?'; then | |||||
| echo "no" | |||||
| elif test "$(echo ${command} | cut -c-5)" = 'play?'; then | |||||
| count=$(echo ${command} | cut -f2 -d" ") | |||||
| if test ${count} -lt 12; then | |||||
| echo "hit" | |||||
| else | |||||
| echo "stand" | |||||
| fi | |||||
| fi | |||||
| done |
| if test ! -e fifo; then | |||||
| mkfifo fifo | |||||
| fi | |||||
| blackjack -n1e5 < fifo | ./no-bust.pl > fifo |
| d2p | |||||
| p2d |
| define(case_title, Mimic the dealer) | |||||
| --- | |||||
| title: case_title | |||||
| ... | |||||
| # case_title | |||||
| > Difficulty: case_difficulty/100 | |||||
| This example implements a “mimic-the-dealer strategy,” i.e. hits if the hand totals less than seventeen and stands on eighteen or more. The player stands on hard seventeen but hits on soft seventeen. | |||||
| This time, the configuration file `blackjack.conf` is used. If a file with this name exists in the directory where `blackjack` is executed, it is read and parsed. The options should be fairly self descriptive. See the [configuration file] section of the manual for a detailed explanation of the variables and values that can be entered. In particular, we ask to play one hundred thousand hands at a six-deck game where the dealer hits soft seventeens. If the random seed is set to a fixed value so each execution will lead to the very same sequence of cards. | |||||
| Now, there are two options that tell Libre Blackjack how the player is going to talk to the backend: `player2dealer` and `dealer2player`. The first one sets the communication mechanism from the player to the dealer (by default is `blackjack`’s standard input), and the second one sets the mechanism from the dealer to the player (by default `blackjack`’s standard output). In this case, the configuration file reads: | |||||
| ```ini | |||||
| include(blackjack.conf)dnl | |||||
| ``` | |||||
| This means that two FIFOs (a.k.a. named pipes) are to be used for communication, `player2dealer` from the player to the dealer and `dealer2player` for the dealer to the player. If these FIFOs do not exist, they are created by `blackjack` upon execution. | |||||
| The player this time is implemented as an awk script, whose input should be read from `dealer2player` and whose output should be written to `player2dealer`. To run the game, execute `blackjack` in one terminal making sure the current directory is where the `blackjack.conf` file exists. It should print a message telling that it is waiting for someone to be at the other side of the named pipes: | |||||
| ```terminal | |||||
| $ blackjack | |||||
| [...] | |||||
| waiting for dealer2player buffered fifo 'dealer2player'... | |||||
| ``` | |||||
| In another terminal run the player | |||||
| ```terminal | |||||
| $ ./mimic-the-dealer.awk < dealer2player > player2dealer | |||||
| ``` | |||||
| Both dealer and player may be run in the same terminal putting the first one on the background: | |||||
| ```terminal | |||||
| include(run.sh)dnl | |||||
| ``` | |||||
| To understand the decisions taken by the player, we have to remember that when Libre Blackjack receives the command `count` asking for the current player's count, it returns a positive number for hard hands and a negative number for soft hands. The instructions `fflush()` are needed in order to avoid deadlocks on the named pipes: | |||||
| ```awk | |||||
| include(mimic-the-dealer.awk)dnl | |||||
| ``` | |||||
| ```yaml | |||||
| include(report.yaml) | |||||
| ``` | |||||
| > **Exercise:** modify the player and the configuration file so both the dealer and the player may stand on soft seventeen. Analyze the four combinations (player h17 - dealer h17, player h17 - dealer s17, player s17 - dealer h17, player s17 - dealer s17) | |||||
| case_nav | |||||
| --- | |||||
| title: Mimic the dealer | |||||
| ... | |||||
| # Mimic the dealer | |||||
| > Difficulty: 08/100 | |||||
| This example implements a “mimic-the-dealer strategy,” i.e. hits if the hand totals less than seventeen and stands on eighteen or more. The player stands on hard seventeen but hits on soft seventeen. | |||||
| This time, the configuration file `blackjack.conf` is used. If a file with this name exists in the directory where `blackjack` is executed, it is read and parsed. The options should be fairly self descriptive. See the [configuration file] section of the manual for a detailed explanation of the variables and values that can be entered. In particular, we ask to play one hundred thousand hands at a six-deck game where the dealer hits soft seventeens. If the random seed is set to a fixed value so each execution will lead to the very same sequence of cards. | |||||
| Now, there are two options that tell Libre Blackjack how the player is going to talk to the backend: `player2dealer` and `dealer2player`. The first one sets the communication mechanism from the player to the dealer (by default is `blackjack`’s standard input), and the second one sets the mechanism from the dealer to the player (by default `blackjack`’s standard output). In this case, the configuration file reads: | |||||
| ```ini | |||||
| hands = 1e5 | |||||
| decks = 6 | |||||
| hit_soft_17 = 1 | |||||
| # uncomment to obtain the same cards each time | |||||
| # rng_seed = 1 | |||||
| player2dealer = fifo mimic_p2d | |||||
| dealer2player = fifo mimic_d2p | |||||
| buffered_fifo = 1 | |||||
| ``` | |||||
| This means that two FIFOs (a.k.a. named pipes) are to be used for communication, `player2dealer` from the player to the dealer and `dealer2player` for the dealer to the player. If these FIFOs do not exist, they are created by `blackjack` upon execution. | |||||
| The player this time is implemented as an awk script, whose input should be read from `dealer2player` and whose output should be written to `player2dealer`. To run the game, execute `blackjack` in one terminal making sure the current directory is where the `blackjack.conf` file exists. It should print a message telling that it is waiting for someone to be at the other side of the named pipes: | |||||
| ```terminal | |||||
| $ blackjack | |||||
| [...] | |||||
| waiting for dealer2player buffered fifo 'dealer2player'... | |||||
| ``` | |||||
| In another terminal run the player | |||||
| ```terminal | |||||
| $ ./mimic-the-dealer.awk < dealer2player > player2dealer | |||||
| ``` | |||||
| Both dealer and player may be run in the same terminal putting the first one on the background: | |||||
| ```terminal | |||||
| rm -f mimic_d2p mimic_p2d | |||||
| mkfifo mimic_d2p mimic_p2d | |||||
| blackjack & | |||||
| gawk -f mimic-the-dealer.awk < mimic_d2p > mimic_p2d | |||||
| ``` | |||||
| To understand the decisions taken by the player, we have to remember that when Libre Blackjack receives the command `count` asking for the current player's count, it returns a positive number for hard hands and a negative number for soft hands. The instructions `fflush()` are needed in order to avoid deadlocks on the named pipes: | |||||
| ```awk | |||||
| #!/usr/bin/gawk -f | |||||
| function abs(x){return ( x >= 0 ) ? x : -x } | |||||
| /bet\?/ { | |||||
| print "1"; | |||||
| fflush(); | |||||
| } | |||||
| /insurance\?/ { | |||||
| print "no"; | |||||
| fflush(); | |||||
| } | |||||
| /play\?/ { | |||||
| count = $2 | |||||
| # mimic the dealer: hit until 17 (hit soft 17) | |||||
| if (abs(count) < 17 || count == -17) { # soft hands are negative | |||||
| print "hit"; | |||||
| } else { | |||||
| print "stand"; | |||||
| } | |||||
| fflush(); | |||||
| } | |||||
| /bye/ { | |||||
| exit; | |||||
| } | |||||
| ``` | |||||
| ```yaml | |||||
| --- | |||||
| rules: | |||||
| decks: 6 | |||||
| hands: 100000 | |||||
| hit_soft_17: 1 | |||||
| double_after_split: 1 | |||||
| blackjack_pays: 1.5 | |||||
| rng_seed: -1448949563 | |||||
| number_of_burnt_cards: 0 | |||||
| no_negative_bankroll: 0 | |||||
| max_bet: 0 | |||||
| penetration: 0.75 | |||||
| penetration_sigma: 0.05 | |||||
| cpu: | |||||
| user: 0.631905 | |||||
| system: 1.19576 | |||||
| wall: 2.15273 | |||||
| second_per_hand: 2.2e-05 | |||||
| hands_per_second: 4.6e+04 | |||||
| player: | |||||
| wins: 0.41044 | |||||
| pushes: 0.09695 | |||||
| losses: 0.49261 | |||||
| dealer_blackjacks: 0.04658 | |||||
| player_blackjacks: 0.04663 | |||||
| dealer_busts: 0.18984 | |||||
| player_busts: 0.27268 | |||||
| doubled_hands: 0 | |||||
| doubled_wins: 0 | |||||
| insured_hands: 0 | |||||
| insured_wins: 0 | |||||
| number_of_hands: 100000 | |||||
| number_of_shuffles: 2326 | |||||
| total_money_waged: 100000 | |||||
| worst_bankroll: -5996.5 | |||||
| final_bankroll: -5994.5 | |||||
| return: -0.059945 | |||||
| variance: 0.955017 | |||||
| deviation: 0.97725 | |||||
| error: 0.00309034 | |||||
| result: "(-6.0 ± 0.6) %" | |||||
| ... | |||||
| ``` | |||||
| > **Exercise:** modify the player and the configuration file so both the dealer and the player may stand on soft seventeen. Analyze the four combinations (player h17 - dealer h17, player h17 - dealer s17, player s17 - dealer h17, player s17 - dealer s17) | |||||
| ------- | |||||
| :::{.text-center} | |||||
| [Previous](../05-no-bust) | [Index](../) | [Next](../20-basic-strategy) | |||||
| ::: | |||||
| h17 = true |
| #!/usr/bin/gawk -f | |||||
| function abs(x){return ( x >= 0 ) ? x : -x } | |||||
| /bet\?/ { | |||||
| print "1"; | |||||
| fflush(); | |||||
| } | |||||
| /insurance\?/ { | |||||
| print "no"; | |||||
| fflush(); | |||||
| } | |||||
| /play\?/ { | |||||
| # mimic the dealer: hit until 17 (hit soft 17) | |||||
| if (abs($2) < 17 || $2 == -17) { # soft hands are negative | |||||
| print "hit"; | |||||
| } else { | |||||
| print "stand"; | |||||
| } | |||||
| fflush(); | |||||
| } | |||||
| /bye/ { | |||||
| exit; | |||||
| } |
| rm -f d2p p2d | |||||
| mkfifo d2p p2d | |||||
| gawk -f mimic-the-dealer.awk < d2p > p2d & | |||||
| ../../blackjack -n100000 > d2p < p2d |
| --- | |||||
| title: Example players for LibreBlackjack | |||||
| ... | |||||
| # Example players for LibreBlackjack | |||||
| The subdirectory `players` contains some automatic players that play against LibreBlackjack. These players are coded in different languages and communicate with LibreBlackjack in a variety of ways in order to illustrate the design basis: | |||||
| * [`00-internal`](00-internal) uses the internal player that defaults to playing one million hands of basic strategy | |||||
| * [`02-always-stand`](02-always-stand), using the UNIX tool `yes` this player always says “stand” into the standard output (which is piped to libreblackjack’s standard input) no matter what the cards are | |||||
| * [`05-no-bust`](05-no-bust) is a PERL-based player does not bust (i.e. hits if the hard total is less than twelve) that receives tha cards through the standard input but draws or stands using a FIFO to talk back to the dealer | |||||
| * [`08-mimic-the-dealer`](08-mimic-the-dealer) does tha same the dealer do (hits soft seventeens). It is implemented in AWK using two FIFOs. | |||||
| * [`20-basic-strategy`](20-basic-strategy) derives the basic strategy from scratch in less than one minute. | |||||