Current section
Files
Jump to
Current section
Files
lib/igo/board.ex
alias Igo.Stone, as: Stone
alias Igo.Printer, as: Printer
defmodule Igo.Board do
def new(size) do
build_board(size * size, [])
end
def place_stone(board, color, coord) do
index = board_index(board, coord)
board = List.replace_at(board, index, color)
capture_stones(board, Stone.opposite_color(color), coord)
end
def capture_stones(board, capture_color, coord) do
coords = coords_around(board, coord)
captures =
Enum.reduce(coords, [], fn next_coord, group ->
coord_group = stone_group_without_liberties(board, next_coord, capture_color, group)
if coord_group == :liberty do
group
else
coord_group
end
end)
board = remove_stones(board, captures)
{board, length(captures)}
end
def star?(size, {y, x}) do
middle = (size - 1) / 2
cond do
size < 9 && size >= 3 ->
y == middle && x == middle
size < 13 ->
((y == 2 || y == 6) && (x == 2 || x == 6)) || (y == 4 && x == 4)
size >= 13 ->
bottom = size - 4
(y == 3 || y == middle || y == bottom) && (x == 3 || x == middle || x == bottom)
end
end
def at_coord(board, coord) do
index = board_index(board, coord)
Enum.at(board, index)
end
def print(board) do
Printer.print_rows(Enum.chunk_every(board, size(board)))
end
def territory_group_with_dead_stones(board, coord, color, {territory, dead_stones}) do
next_color = at_coord(board, coord)
if next_color == color do
{territory, dead_stones}
else
dead_stones =
if next_color != :liberty && !Enum.member?(dead_stones, coord) do
dead_stones ++ [coord]
else
dead_stones
end
if Enum.member?(territory, coord) do
{territory, dead_stones}
else
coords = coords_around(board, coord)
Enum.reduce(coords, {territory ++ [coord], dead_stones}, fn next_coord, state ->
territory_group_with_dead_stones(board, next_coord, color, state)
end)
end
end
end
def stone_group_without_liberties(board, coord) do
group = [coord]
color = at_coord(board, coord)
if color == :liberty do
:liberty
else
coords = coords_around(board, coord)
Enum.reduce(coords, group, fn next_coord, new_group ->
stone_group_without_liberties(board, next_coord, color, new_group)
end)
end
end
defp stone_group_without_liberties(board, coord, color, group) do
if group == :liberty do
:liberty
else
next_color = at_coord(board, coord)
cond do
next_color == :liberty ->
:liberty
next_color == color ->
if Enum.member?(group, coord) do
group
else
coords = coords_around(board, coord)
Enum.reduce(coords, group ++ [coord], fn next_coord, new_group ->
stone_group_without_liberties(board, next_coord, color, new_group)
end)
end
true ->
group
end
end
end
defp coords_around(board, {y, x}) do
coords = [
{y - 1, x},
{y, x + 1},
{y + 1, x},
{y, x - 1}
]
Enum.filter(coords, fn {y, x} ->
y >= 0 && y < size(board) && x >= 0 && x < size(board)
end)
end
defp remove_stones(board, coords) do
Enum.reduce(coords, board, fn coord, board ->
index = board_index(board, coord)
List.replace_at(board, index, :liberty)
end)
end
defp size(board) do
round(:math.sqrt(length(board)))
end
defp board_index(board, {y, x}) do
size(board) * y + x
end
defp build_board(size, board) when size <= 0 do
board
end
defp build_board(size, board) do
build_board(size - 1, board ++ [:liberty])
end
end