Card Counting
Mix.install([
{:kino, github: "livebook-dev/kino", override: true},
{:kino_lab, "~> 0.1.0-dev", github: "jonatanklosko/kino_lab"},
{:vega_lite, "~> 0.1.4"},
{:kino_vega_lite, "~> 0.1.1"},
{:benchee, "~> 0.1"},
{:ecto, "~> 3.7"},
{:math, "~> 0.7.0"},
{:faker, "~> 0.17.0"},
{:utils, path: "#{__DIR__}/../utils"},
{:tested_cell, git: "https://github.com/BrooklinJazz/tested_cell"}
])
Navigation
Setup
Ensure you type the ea
keyboard shortcut to evaluate all Elixir cells before starting. Alternatively you can evaluate the Elixir cells as you read.
Card Counting
Card counting is a strategy used in card games to determine if there are more high cards or low cards in the deck.
The most common strategy is the Hi-Lo strategy where we keep a running total based on the cards dealt. The higher the running total, the better the deck is for players.
Don’t worry if you aren’t familiar with cards. We’ll break down the goal without needing to know the rules of card games!
We’ll use integers to represent cards from 1
to 13
.
We either add 1, subtract 1, or do nothing based on the card’s value.
-
When the card is between
1
and6
we increment(+1)
the count. -
When the card is between
7
and9
we do nothing. -
When the card is between
10
and13
we decrenent(-1)
the count.
Card (4)
When the current card is 4
and the current count is 0
what is the next_count
?
Use arithmetic operators +
or -
with the count
variable to set the next_count
.
Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
card = 4
count = 0
next_count =
if card >= 1 && card <= 6 do
count + 1
end
assert next_count === 1
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
card = 4
count = 0
next_count =
if card >= 1 && card <= 6 do
count + 1
end
Card (7)
When the current card is 7
and the current count is 3
what is the next_count
?
Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
card = 7
count = 3
next_count = count
assert next_count == count
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
card = 7
count = 3
next_count = count
Card King (13)
When the current card is a King (13)
and the current count is 5
what is the next_count
?
Use arithmetic operators +
or -
with the count
variable to set the next_count
.
Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
card = 13
count = 5
next_count = count - 1
assert next_count === 4
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
card = 13
count = 5
next_count = count - 1
Random Card and Count (BONUS)
Now the card
is a random card between 2
and 14
, and the current count
is a random number between 1
and 10
.
-
Enum.random(2..14)
generates acard
between2
and14
. -
Enum.random(1..10)
generates acount
between2
and14
.
Based on the value of random_card
and count
, what is the next_count
?
Use arithmetic operators +
or -
with the count
variable to set the next_count
.
Replace nil
with your answer.
Hint There are multiple possible solutions. You might consider:
-
comparison operators
card <= 6 && count + 1 || card >= 10 && count - 1 || count
-
Use control flow such as
cond
orcase
.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
card = Enum.random(2..14)
count = Enum.random(1..10)
next_count =
cond do
card <= 6 ->
count + 1
card >= 10 ->
count - 1
true ->
count
end
cond do
card in 2..6 -> assert next_count === count + 1
card in 7..9 -> assert next_count === count
card in 10..14 -> assert next_count === count - 1
true -> raise "Something went wrong. Please reset the exercise."
end
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
card = Enum.random(2..14)
count = Enum.random(1..10)
next_count =
cond do
card <= 6 ->
count + 1
card >= 10 ->
count - 1
true ->
count
end
Commit Your Progress
Run the following in your command line from the project folder to track and save your progress in a Git commit.
$ git add .
$ git commit -m "finish card counting exercise"