Skip to content

Instantly share code, notes, and snippets.

@marciok
Last active March 23, 2016 15:59
Show Gist options
  • Save marciok/34450d720f18f8c4bff1 to your computer and use it in GitHub Desktop.
Save marciok/34450d720f18f8c4bff1 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe game written in Swift using a functional programming approach.
// Tic-Tac-Toe game written in Swift using a functional programming approach.
import UIKit
struct Board {
let field: [[String]]
let currentState: State
init(field: [[String]], currentState: State) {
self.currentState = currentState
self.field = field
}
init() {
self.currentState = .Xplay
let line = [" ", " ", " "]
self.field = [line, line, line]
}
enum State {
case Xplay
case Oplay
case Xwon
case Owon
case InvalidMove
}
enum LinePosition: Int {
case Top
case Mid
case Bottom
}
enum ColumnPosition: Int {
case Left
case Mid
case Right
}
}
struct Game {
private static let winnerSequenceSize = 3
private static func changeState(board: Board, newState: Board.State) -> Board {
return Board(field: board.field, currentState: newState)
}
private static func changeField(board: Board, newField: [[String]]) -> Board {
return Board(field: newField, currentState: board.currentState)
}
private static func addMove(board: Board, line: Board.LinePosition, column: Board.ColumnPosition) -> Board {
var field = board.field
let x = line.rawValue
let y = column.rawValue
if field[x][y] != " " {
print("Invalid move !!!")
return changeState(board, newState: .InvalidMove)
}
if board.currentState == .Xplay {
field[x][y] = "𝗫"
} else if board.currentState == .Oplay {
field[x][y] = "⚫️"
}
return changeField(board, newField: field)
}
private static func togglePlayer(board: Board) -> Board {
if board.currentState == .Oplay {
return changeState(board, newState: .Xplay)
} else if board.currentState == .Xplay {
return changeState(board, newState: .Oplay)
}
return board
}
private static func flatLine(field: [[String]]) -> [String] {
return field.filter { return hasSameElements($0) }.flatMap { $0 }
}
private static func isWinningSequence(sequence: [String]) -> Bool {
return sequence.count == winnerSequenceSize
}
private static func winner(board: Board, sequence: [String]) -> Board {
guard let firstElement = sequence.first else {
return board
}
if firstElement == "𝗫" {
return changeState(board, newState: .Xwon)
} else if firstElement == "⚫️" {
return changeState(board, newState: .Owon)
}
return board
}
private static func winnerTuples(filed: [[String]]) -> [(Int, Int)] {
var diagonalTuples: [(Int, Int)] = Array()
var verticalTuples: [(Int, Int)] = Array()
for i in 0..<board.field.count {
verticalTuples += [(i, i)]
for j in 0..<board.field.first!.count {
diagonalTuples += [(j, i)]
}
}
return diagonalTuples + verticalTuples + verticalTuples.reverse()
}
private static func checkForWinner(board: Board) -> Board {
let sequencesInLine = flatLine(board.field)
if isWinningSequence(sequencesInLine) {
return winner(board, sequence: sequencesInLine)
}
var winnerSequenceBuffer: [String] = []
let winnerSequence = winnerTuples(board.field).flatMap { (x,y) -> [String] in
winnerSequenceBuffer += [board.field[x][y]]
if winnerSequenceBuffer.count == winnerSequenceSize {
if hasSameElements(winnerSequenceBuffer) {
return winnerSequenceBuffer
} else {
winnerSequenceBuffer = []
}
}
return []
}
return winner(board, sequence: winnerSequence)
}
private static func hasSameElements(line: [String]) -> Bool {
let uniqueElements = Set(line)
return uniqueElements.count == 1 && uniqueElements.first != " "
}
internal static func play(board: Board, line: Board.LinePosition, column: Board.ColumnPosition) -> Board {
guard board.currentState == .Xplay ||
board.currentState == .Oplay else {
print("Game Over")
return board
}
return checkForWinner(togglePlayer(addMove(board, line: line, column: column)))
}
}
let board = Board()
let board1 = Game.play(board, line: .Bottom, column: .Left)
print(board1)
let board2 = Game.play(board1, line: .Top, column: .Mid)
print(board2)
let board3 = Game.play(board2, line: .Mid, column: .Mid)
print(board3)
let board4 = Game.play(board3, line: .Top, column: .Right)
print(board4)
let board5 = Game.play(board4, line: .Mid, column: .Left)
print(board5)
let board6 = Game.play(board5, line: .Top, column: .Left)
print(board6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment