Small, beginner project idea that might give a little Elixir outreach, too

I was browsing the orange site yesterday and, as happens from time to time, the Jeff Atwood GitHub repo for “BASIC Computer Games” came up as a topic:

“Basic Computer Games (GitHub)”

In this repo, they take the classic 1973 book “101 BASIC Computer Games”, or “BASIC Computer Games” as it was later called, and rewrite those games in different computer languages. Given the relative simplicity of these games, the accessibility of BASIC as a language to define the requirements, and the challenge offered by Jeff Atwood in this repo… it struck me as a fairly interesting and unique way for someone starting with Elixir to get a problem to solve with just a touch more meat than a “hello world” or an overly prescriptive, hand holding tutorial. Also I noticed that nobody had yet contributed any Elixir, Erlang, or Other BEAM language (OK, I didn’t search exhaustively, but I didn’t see any where I looked) versions to the repo; seemed like a low-effort way to get a little more Elixir exposure in the broader world.

Finally, a note about the book, “BASIC Computer Games (Book)”. As I mentioned, it originally published in 1973 and it’s just a collection of 101 games written in the BASIC computer language as collected by David H. Ahl, specifically the flavor of BASIC that shipped with the old DEC minicomputers… think PDP-11. Later editions targeted microcomputer BASICs. The second version of the book was the first computer book to sell over one million copies. For me personally, the book was a substantial part of my introduction to programming… entering the games into my VIC-20 & C-64 and having to convert from the BASIC of the book to the BASIC of my computers… without the internet for help… built in me an understanding of the basics of computing, troubleshooting, and using documentation which I use to this day. I will often continue to use programs from this book as early learning projects when starting a new computer language.

Anyway… just a thought… adopt or ignore at your leisure!


Thanks for sharing – took a quick crack at porting the first game Acey Ducey.

Acey Ducey

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; you may want to alter this value if you want to start with more or less than $100. The game keeps going on until you lose all your money or interrupt the program.

The original program author was Bill Palmby of Prairie View, Illinois.

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})

  # 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)

  # 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 # equivalent to{funds: 100, deck: 100})

For transparency and posterity’s sake, there’s a bug in win?/3. (╯°□°)╯︵ ┻━┻

The version above depends on the unfortunate and incorrect assumption that the first card’s value is less than that of the second card. The version below uses Enum.sort/1 to not rely on that assumption being true.

  # for a stricter win condition (non-inclusive)
  defp win?({_, first}, {_, second}, {_, third}) do
    [floor, ceiling] = Enum.sort([first, second])
    (floor < third) && (third < ceiling)
  # for a looser win condition (inclusive)
  #defp win?({_, first}, {_, second}, {_, third}) do
    #[_, middle, _] = Enum.sort([first, second, third])
    #middle == third