The Finnternet Like the Internet but with more me

Drink the Elixir: Tic Tac Toe String Matching

One of the first projects I did in Elixir was a multiplayer Tic Tac Toe game using Phoenix. I stored the board as a string with nine characters (a character for each spot on the board).

Spot definitions:

  • ”-“ = Open spot
  • “x” = Taken by X
  • “o” = Taken by O

After every move, the board state would have to be checked to see the state of the game. I implemented this check using one of Elixir’s strengths, string matching. This is a good example of how to use string matching because it shows how to match specific parts in a string which to me wasn’t obvious at first.

The important things to know for string matching are:

_                           // ignore everything else
<<_ :: size(8*numOfChars)>> // ignore numOfChars characters (assuming UTF-8)
str1 <> str2                // append str2 to str1

Using these three techniques, we can pattern match against specific spots in the board to determine which of the following states the game is in.

Possible game states:

  • “win_x” -> X won the game
  • “win_o” -> O won the game
  • “draw” -> Nobody won & the board is full
  • “o” -> O’s turn
  • “x” -> X’s turn

The game state update case statement:

case board do
  # Horizontal
  "xxx" <> _ -> "win_x"
  "ooo" <> _ -> "win_o"
  <<_ :: size(24)>> <> "xxx" <> _ -> "win_x"
  <<_ :: size(24)>> <> "ooo" <> _ -> "win_o"
  <<_ :: size(48)>> <> "xxx" -> "win_x"
  <<_ :: size(48)>> <> "ooo" -> "win_o"
  # Vertical
  "x" <> <<_ :: size(16)>> <> "x" <> <<_ :: size(16)>> <> "x" <> _ -> "win_x"
  "o" <> <<_ :: size(16)>> <> "o" <> <<_ :: size(16)>> <> "o" <> _ -> "win_o"
  <<_ :: size(8)>> <> "x" <> <<_ :: size(16)>> <> "x" <> <<_ :: size(16)>> <> "x" <> _ -> "win_x"
  <<_ :: size(8)>> <> "o" <> <<_ :: size(16)>> <> "o" <> <<_ :: size(16)>> <> "o" <> _ -> "win_o"
  <<_ :: size(16)>> <> "x" <> <<_ :: size(16)>> <> "x" <> <<_ :: size(16)>> <> "x" -> "win_x"
  <<_ :: size(16)>> <> "o" <> <<_ :: size(16)>> <> "o" <> <<_ :: size(16)>> <> "o" -> "win_o"
  # Diagonal
  "x" <> <<_ :: size(24)>> <> "x" <> <<_ :: size(24)>> <> "x" -> "win_x"
  "o" <> <<_ :: size(24)>> <> "o" <> <<_ :: size(24)>> <> "o" -> "win_o"
  <<_ :: size(16)>> <> "x" <> <<_ :: size(8)>> <> "x" <> <<_ :: size(8)>> <> "x" <> _ -> "win_x"
  <<_ :: size(16)>> <> "o" <> <<_ :: size(8)>> <> "o" <> <<_ :: size(8)>> <> "o" <> _ -> "win_o"
  # No winning case, check if draw
  _ -> if String.contains?(board, "-") do
      if (game.state == "x"), do: "o", else: "x"
    else 
      "draw"
    end
end