Last active
March 23, 2016 15:59
-
-
Save marciok/34450d720f18f8c4bff1 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe game written in Swift using a functional programming approach.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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