func (b *Board) inBounds(pt *point.Point) bool {
	var x, y int = pt.X(), pt.Y()
	return x < len(b.board[0]) && y < len(b.board) &&
		x >= 0 && y >= 0
}
inBounds returns true if x and y are in bounds
on the board, false otherwise.
package board
import (
	"container/list"
	"fmt"
	"strings"
	"github.com/otrego/clamshell/core/color"
	"github.com/otrego/clamshell/core/move"
	"github.com/otrego/clamshell/core/point"
)
type Board struct {
	board [][]color.Color
	ko    *point.Point
}
func New(size int) *Board {
	board := Board{
		make([][]color.Color, size),
		nil,
	}
	for i := 0; i < size; i++ {
		board.board[i] = make([]color.Color, size)
	}
	return &board
}
func (b *Board) PlaceStone(m *move.Move) (move.List, error) {
	if !b.inBounds(m.Point()) {
		return nil, fmt.Errorf("move %v out of bounds for %dx%d board",
			m.Point(), len(b.board[0]), len(b.board))
	}
	if b.colorAt(m.Point()) != color.Empty {
		return nil, fmt.Errorf("move %v already occupied", m.Point())
	}
	b.setColor(m)
	capturedStones := b.findCapturedGroups(m)
	if len(capturedStones) == 0 && len(b.capturedStones(m.Point())) != 0 {
		b.setColor(move.New(color.Empty, m.Point()))
		return nil, fmt.Errorf("move %v is suicidal", m.Point())
	}
	if len(capturedStones) == 1 {
		if b.ko != nil && *(b.ko) == *(m.Point()) {
			b.setColor(move.New(color.Empty, m.Point()))
			return nil, fmt.Errorf("%v is an illegal ko move", m.Point())
		}
		b.ko = capturedStones[0]
	} else {
		b.ko = nil
	}
	var captured move.List
	opp := m.Color().Opposite()
	for _, pt := range capturedStones {
		captured = append(captured, move.New(opp, pt))
	}
	captured.Sort()
	b.removeCapturedStones(capturedStones)
	return captured, nil
}
func (b *Board) findCapturedGroups(m *move.Move) []*point.Point {
	pt := m.Point()
	points := b.getNeighbors(pt)
	capturedStones := make([]*point.Point, 0)
	for _, point := range points {
		if b.inBounds(point) {
			capturedStones = append(capturedStones, b.capturedStones(point)...)
		}
	}
	return capturedStones
}
func (b *Board) removeCapturedStones(capturedStones []*point.Point) {
	for _, point := range capturedStones {
		b.setColor(move.New(color.Empty, point))
	}
}
func (b *Board) capturedStones(pt *point.Point) []*point.Point {
	stoneGroup, captured := b.getStoneGroup(pt)
	if captured {
		return stoneGroup
	}
	return nil
}
func (b *Board) getStoneGroup(pt *point.Point) ([]*point.Point, bool) {
	expanded := make(map[point.Point]bool)
	captured := true
	c := b.colorAt(pt)
	queue := list.New()
	queue.PushBack(pt)
	for queue.Len() > 0 {
		e := queue.Front()
		queue.Remove(e)
		pt1, ok := e.Value.(*point.Point)
		if !ok {
			panic("e.Value was not of type point.Point")
		}
		if !b.inBounds(pt1) {
			continue
		} else if b.colorAt(pt1) == color.Empty {
			captured = false
		} else if b.colorAt(pt1) == c && !expanded[*pt1] {
			expanded[*pt1] = true
			points := b.getNeighbors(pt1)
			for _, point := range points {
				queue.PushBack(point)
			}
		}
	}
	stoneGroup := make([]*point.Point, len(expanded))
	i := 0
	for key := range expanded {
		stoneGroup[i] = point.New(key.X(), key.Y())
		i++
	}
	return stoneGroup, captured
}