Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Card Counting

card_counting.livemd

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

Return Home Report An Issue

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 and 6 we increment (+1) the count.
  • When the card is between 7 and 9 we do nothing.
  • When the card is between 10 and 13 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 &amp;&amp; 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 &amp;&amp; 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 a card between 2 and 14.
  • Enum.random(1..10) generates a count between 2 and 14.

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:

  1. comparison operators card <= 6 && count + 1 || card >= 10 && count - 1 || count
  2. Use control flow such as cond or case.
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"