Skip to content

Instantly share code, notes, and snippets.

@mknippen
Created March 21, 2018 05:54
Show Gist options
  • Save mknippen/0e55b1be6810a1b7c49b9cd1febc0aef to your computer and use it in GitHub Desktop.
Save mknippen/0e55b1be6810a1b7c49b9cd1febc0aef to your computer and use it in GitHub Desktop.
Go Flood Fill Playground
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
enum Stone {
case Black, White, None
func opposite() -> Stone {
switch self {
case .Black:
return .White
case .White:
return .Black
default:
return .Black
}
}
}
let gridSize = 9
var board: [[Stone]] = []
let row: [Stone] = Array(repeating: .None, count: gridSize)
board = Array(repeating: row, count: gridSize)
var views: [[UIView]] = []
let boardColor = UIColor(red: 0.75, green: 0.375, blue: 0.2, alpha: 1.0)
func drawGrid(view: UIView) {
let widthOfCell = round(view.frame.width / CGFloat(gridSize))
let heightOfCell = round(view.frame.height / CGFloat(gridSize))
for (i, row) in board.enumerated() {
var viewRow: [UIView] = []
for (j, _) in row.enumerated() {
let frame = CGRect(x: CGFloat(i)*widthOfCell, y: CGFloat(j)*heightOfCell, width: widthOfCell, height: heightOfCell)
let cell = UIView(frame: frame)
cell.backgroundColor = boardColor
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 2
view.addSubview(cell)
viewRow.append(cell)
}
views.append(viewRow)
}
}
func createBoard() -> UIView {
let frame = CGRect(x: 0, y: 0, width: 500, height: 500)
let view = UIView(frame: frame)
drawGrid(view: view)
return view
}
var checked: [[Bool]] = []
var spots: [(Int,Int)] = []
//true = surrounded
func floodFill(x: Int, y: Int, stone: Stone) -> Bool {
//stone is the color that is placed at the spot (x,y)
//are we still in the grid?
if x >= gridSize || x < 0 || y >= gridSize || y < 0 {
return true
}
//have we already checked this spot
if checked[x][y] {
return true
}
checked[x][y] = true
let spot = board[x][y]
let target = stone.opposite()
//is the spot that we're looking at the same color as what we're looking for? If so, we need to look in each direction
if spot == stone {
spots.append((x,y))
//TODO: This could probably look a bit cleaner
//south
if floodFill(x: x, y: y+1, stone: stone) {
//north
if floodFill(x: x, y: y-1, stone: stone) {
//west
if floodFill(x: x-1, y: y, stone: stone) {
if floodFill(x: x+1, y: y, stone: stone) {
return true
}
}
}
}
return false
} else if spot == target {
return true
} else {
//empty spot
return false
}
}
//clear all of our checks. Use this before calling floodFill
func clearChecked() {
let row = Array(repeating: false, count: gridSize)
checked = Array(repeating: row, count: gridSize)
spots = []
}
//adds a move to the board. It can also be used to remove a stone from the board
func addMove(x: Int, y: Int, stone: Stone) {
let cell = views[x][y]
board[x][y] = stone
switch stone {
case .None:
cell.backgroundColor = boardColor
case .White:
cell.backgroundColor = UIColor.white
case .Black:
cell.backgroundColor = UIColor.black
}
if stone == .None {
return
}
//Do we need to remove any stones?
//TODO: neaten this up, so there's not so much copy paste
//check group to the north
if y > 0 && board[x][y-1] == stone.opposite() {
clearChecked()
// print("\(x), \(y)")
if floodFill(x: x, y: y-1, stone: stone.opposite()) {
print("Need to remove stone(s) to the north: \(spots)")
for s in spots {
addMove(x: s.0, y: s.1, stone: .None)
}
}
}
//south
if y < gridSize-1 && board[x][y+1] == stone.opposite() {
clearChecked()
if floodFill(x: x, y: y+1, stone: stone.opposite()) {
print("Need to remove stone(s) to the south: \(spots)")
for s in spots {
addMove(x: s.0, y: s.1, stone: .None)
}
}
}
//east
if x < gridSize-1 && board[x+1][y] == stone.opposite() {
clearChecked()
if floodFill(x: x+1, y: y, stone: stone.opposite()) {
print("Need to remove stone(s) to the east: \(spots)")
for s in spots {
addMove(x: s.0, y: s.1, stone: .None)
}
}
}
//west
if x > 0 && board[x-1][y] == stone.opposite() {
clearChecked()
if floodFill(x: x-1, y: y, stone: stone.opposite()) {
print("Need to remove stone(s) to the west: \(spots)")
for s in spots {
addMove(x: s.0, y: s.1, stone: .None)
}
}
}
//is this move legal?
clearChecked()
let illegal = floodFill(x: x, y: y, stone: stone)
if illegal {
print("Illegal Move: \(spots)")
board[x][y] = .None
cell.backgroundColor = boardColor
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = createBoard()
//standard ponnuki
func testPonuki() {
addMove(x: 2, y: 3, stone: .Black)
addMove(x: 3, y: 4, stone: .Black)
addMove(x: 1, y: 4, stone: .Black)
addMove(x: 2, y: 4, stone: .White)
addMove(x: 2, y: 5, stone: .Black)
}
func testCorner() {
addMove(x: 1, y: 0, stone: .Black)
addMove(x: 0, y: 1, stone: .Black)
addMove(x: 0, y: 0, stone: .White)
}
func testSide() {
addMove(x: 3, y: 0, stone: .Black)
addMove(x: 5, y: 0, stone: .Black)
addMove(x: 4, y: 1, stone: .Black)
addMove(x: 4, y: 0, stone: .White)
}
func testSurroundingTwo() {
addMove(x: 2, y: 3, stone: .Black)
addMove(x: 2, y: 6, stone: .Black)
addMove(x: 1, y: 4, stone: .Black)
addMove(x: 1, y: 5, stone: .Black)
addMove(x: 3, y: 4, stone: .Black)
addMove(x: 2, y: 4, stone: .White)
addMove(x: 2, y: 5, stone: .White)
addMove(x: 3, y: 5, stone: .Black)
}
func testSingleEye() {
addMove(x: 2, y: 3, stone: .Black)
addMove(x: 3, y: 3, stone: .Black)
addMove(x: 4, y: 3, stone: .Black)
addMove(x: 1, y: 4, stone: .Black)
addMove(x: 1, y: 5, stone: .Black)
addMove(x: 1, y: 6, stone: .Black)
addMove(x: 5, y: 4, stone: .Black)
addMove(x: 5, y: 5, stone: .Black)
addMove(x: 5, y: 6, stone: .Black)
addMove(x: 2, y: 7, stone: .Black)
addMove(x: 3, y: 7, stone: .Black)
addMove(x: 4, y: 7, stone: .Black)
addMove(x: 3, y: 4, stone: .White)
addMove(x: 4, y: 4, stone: .White)
addMove(x: 4, y: 5, stone: .White)
addMove(x: 2, y: 6, stone: .White)
addMove(x: 3, y: 6, stone: .White)
addMove(x: 4, y: 6, stone: .White)
addMove(x: 2, y: 4, stone: .White)
addMove(x: 2, y: 5, stone: .White)
//removes last liberty, illegal move, should be true
addMove(x: 3, y: 5, stone: .White)
//removes white group, should be false
addMove(x: 3, y: 5, stone: .Black)
}
func testDoubleEye() {
addMove(x: 0, y: 7, stone: .White)
addMove(x: 1, y: 7, stone: .White)
addMove(x: 2, y: 7, stone: .White)
addMove(x: 3, y: 7, stone: .White)
addMove(x: 1, y: 8, stone: .White)
addMove(x: 3, y: 8, stone: .White)
addMove(x: 0, y: 6, stone: .Black)
addMove(x: 1, y: 6, stone: .Black)
addMove(x: 2, y: 6, stone: .Black)
addMove(x: 3, y: 6, stone: .Black)
addMove(x: 4, y: 7, stone: .Black)
addMove(x: 4, y: 8, stone: .Black)
//illegal move, double eye
addMove(x: 0, y: 8, stone: .Black)
//white takes the same spot, allowed
addMove(x: 0, y: 8, stone: .White)
//black now kills
addMove(x: 2, y: 8, stone: .Black)
}
testDoubleEye()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment