Skip to content

Instantly share code, notes, and snippets.

@carlosipe
Created September 14, 2016 22:59
Show Gist options
  • Select an option

  • Save carlosipe/5c5d334fce9e59c770aca370d27ab36e to your computer and use it in GitHub Desktop.

Select an option

Save carlosipe/5c5d334fce9e59c770aca370d27ab36e to your computer and use it in GitHub Desktop.
defmodule ConnectFour.Board do
defstruct [
free: MapSet.new([]),
player1: MapSet.new([]),
player2: MapSet.new([]),
dimensions: {0,0}
]
def new({x,y} = dimensions) do
%__MODULE__{
dimensions: dimensions,
free: MapSet.new(for row <- 1..x, column <- 1..y, do: {row, column} )
}
end
def move({_board, player, field} = movement) do
movement
|> check_valid_movement
|> apply_movement
|> check_winning_play(player, field)
# possible output
# {:error, reason}
# {:ok, board}
# {:winner, player}
end
def check_valid_movement({board, _player, {x,y} = field} = movement) do
cond do
!MapSet.member?(board.free, field) -> {:error, "Not a free position"}
MapSet.member?(board.free, {x, y-1}) -> {:error, "There's free space below"}
true -> {:ok, movement}
end
end
def apply_movement({:ok, {board, player, field}}) do
new_board = %{board |
:free => MapSet.delete(board.free, field),
player => player_pieces(board, player) |> MapSet.put(field)
}
{:ok, new_board}
end
def apply_movement{:error, reason} do
{:error, reason}
end
def player_pieces(board, player) do
Map.get(board, player)
end
def check_winning_play({:ok, board}, player, field) do
cond do
winning_move?(player_pieces(board, player), field) ->
{:winner, player}
true -> {:ok, board}
end
end
def check_winning_play({:error, reason}, _player, _field) do
{:error, reason}
end
def winning_move?(player_pieces, {_x,_y} = field) do
[
vertical_contiguous(field),
horizontal_contiguous(field),
first_diagonal_contiguous(field),
second_diagonal_contiguous(field)
]
|> Enum.reduce(false,
fn(line,win) ->
win || check_in_line(line, MapSet.put(player_pieces, field))
end
)
end
def vertical_contiguous({x,y}) do
-4..4 |> Enum.map(fn(i) -> {x,y+i} end)
end
def horizontal_contiguous({x,y}) do
-4..4 |> Enum.map(fn(i) -> {x+i,y} end)
end
def first_diagonal_contiguous({x,y}) do
-4..4 |> Enum.map(fn(i) -> {x+i,y+i} end)
end
def second_diagonal_contiguous({x,y}) do
-4..4 |> Enum.map(fn(i) -> {x+i,y-i} end)
end
def check_in_line(line, player_pieces) do
line
|> Enum.reduce(0,
fn(f, count)->
count =
cond do
count == 4 -> 4
MapSet.member?(player_pieces, f) -> count + 1
true -> 0
end
count
end
)
|> case do
4 -> true
_ -> false
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment