Skip to content

Commit

Permalink
Merge pull request #880 from codeanpeace/main
Browse files Browse the repository at this point in the history
Add an Elixir port of Acey Ducey
  • Loading branch information
coding-horror authored Aug 7, 2023
2 parents d611eca + 233592b commit 11c6776
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions 01_Acey_Ducey/elixir/acey_ducey.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
########################################################
#
# Acey Ducey
#
# From: BASIC Computer Games (1978)
# Edited by David Ahl
#
# "This is a simulation of the Acey Ducey card game.
# In the game, the dealer (the computer) deals two
# cards face up. You have an option to bet or not to
# bet depending on whether or not you feel the next
# card dealt will have a value between the first two.
#
# "Your initial money is set to $100. The game keeps
# going on until you lose all your money or interrupt
# the program.
#
# "The original BASIC program author was Bill Palmby
# of Prairie View, Illinois."
#
# To run this file:
# > mix run acey_ducey.exs --no-mix-exs
#
# This uses the following techniques:
#
# - The `Game` module uses a recursive `play/1` function.
# - The `Game` module stores the game state in a `%Game{}` struct.
# - The classic 52 playing card deck is set as a module attribute generated via a comprehension.
# - The deck is automatically shuffled when there are less than 3 cards remaining in the deck.
# - The initial deck defaults to an empty list which triggers a shuffle when the game begins.
# - The initial funds defaults to 100 but it can be explicitly set in the `%Game{}` struct.
# - The prompt to place a bet will automatically re-prompt when given an invalid input.
# - The bets are assumed to be the whole integers for simplicity.
#
########################################################

defmodule Game do
@deck for suit <- [:spades, :hearts, :clubs, :diamonds], value <- 1..13, do: {suit, value}

defstruct funds: 100, deck: []

def play(), do: play(%__MODULE__{}) # for convenience

def play(%__MODULE__{funds: funds}) when funds <= 0, do: IO.puts("~~~ game over ~~~")
def play(%__MODULE__{deck: deck} = game) when length(deck) < 3, do: play(%{game | deck: Enum.shuffle(@deck)})
def play(%__MODULE__{deck: deck, funds: funds} = game) do
IO.gets("<hit enter>\n")

[first_card, second_card, third_card | remaining_deck] = deck

IO.puts("~~~ new round ~~~")
IO.puts("first card: #{format(first_card)}")
IO.puts("second card: #{format(second_card)}\n")
IO.puts("funds: $#{funds}")

bet = prompt_to_place_bet(funds)
new_funds = if win?(first_card, second_card, third_card), do: funds + bet, else: funds - bet

IO.puts("\nthird card: #{format(third_card)}")
IO.puts("funds: $#{funds} => $#{new_funds}")
IO.puts("~~~ end round ~~~\n")

play(%{game | deck: remaining_deck, funds: new_funds})
end

# re-prompt if invalid integer and/or out of bounds
defp prompt_to_place_bet(funds) do
input = IO.gets("place your bet: $")
case Integer.parse(input) do
{bet, _} when bet in 0..funds -> bet
_ -> prompt_to_place_bet(funds)
end
end

# for a stricter win condition (non-inclusive)
defp win?({_, first}, {_, second}, {_, third}) when third > first and third < second, do: true
# for a looser win condition (inclusive)
#defp win?({_, first}, {_, second}, {_, third}) when third in first..second, do: true
defp win?(_, _, _), do: false

defp format({suit, value}) do
case value do
1 -> "ace of #{suit}"
11 -> "prince of #{suit}"
12 -> "queen of #{suit}"
13 -> "king of #{suit}"
value -> "#{value} of #{suit}"
end
end
end

Game.play() # equivalent to Game.play(%Game{funds: 100, deck: 100})

0 comments on commit 11c6776

Please sign in to comment.