RosettaCodeData/Task/Tic-tac-toe/Swift/tic-tac-toe.swift
2017-09-25 22:28:19 +02:00

206 lines
4.7 KiB
Swift

import Darwin
enum Token : CustomStringConvertible {
case cross, circle
func matches(tokens: [Token?]) -> Bool {
for token in tokens {
guard let t = token, t == self else {
return false
}
}
return true
}
func emptyCell(in tokens: [Token?]) -> Int? {
if tokens[0] == nil
&& tokens[1] == self
&& tokens[2] == self {
return 0
} else
if tokens[0] == self
&& tokens[1] == nil
&& tokens[2] == self {
return 1
} else
if tokens[0] == self
&& tokens[1] == self
&& tokens[2] == nil {
return 2
}
return nil
}
var description: String {
switch self {
case .cross: return "x"
case .circle: return "o"
}
}
}
struct Board {
var cells: [Token?] = [nil, nil, nil, nil, nil, nil, nil, nil, nil]
func cells(atCol col: Int) -> [Token?] {
return [cells[col], cells[col + 3], cells[col + 6]]
}
func cells(atRow row: Int) -> [Token?] {
return [cells[row * 3], cells[row * 3 + 1], cells[row * 3 + 2]]
}
func cellsTopLeft() -> [Token?] {
return [cells[0], cells[4], cells[8]]
}
func cellsBottomLeft() -> [Token?] {
return [cells[6], cells[4], cells[2]]
}
func winner() -> Token? {
let r0 = cells(atRow: 0)
let r1 = cells(atRow: 1)
let r2 = cells(atRow: 2)
let c0 = cells(atCol: 0)
let c1 = cells(atCol: 1)
let c2 = cells(atCol: 2)
let tl = cellsTopLeft()
let bl = cellsBottomLeft()
if Token.cross.matches(tokens: r0)
|| Token.cross.matches(tokens: r1)
|| Token.cross.matches(tokens: r2)
|| Token.cross.matches(tokens: c0)
|| Token.cross.matches(tokens: c1)
|| Token.cross.matches(tokens: c2)
|| Token.cross.matches(tokens: tl)
|| Token.cross.matches(tokens: bl) {
return .cross
} else
if Token.circle.matches(tokens: r0)
|| Token.circle.matches(tokens: r1)
|| Token.circle.matches(tokens: r2)
|| Token.circle.matches(tokens: c0)
|| Token.circle.matches(tokens: c1)
|| Token.circle.matches(tokens: c2)
|| Token.circle.matches(tokens: tl)
|| Token.circle.matches(tokens: bl) {
return .circle
}
return nil
}
func atCapacity() -> Bool {
return cells.filter { $0 == nil }.count == 0
}
mutating func play(token: Token, at location: Int) {
cells[location] = token
}
func findBestLocation(for player: Token) -> Int? {
let r0 = cells(atRow: 0)
let r1 = cells(atRow: 1)
let r2 = cells(atRow: 2)
let c0 = cells(atCol: 0)
let c1 = cells(atCol: 1)
let c2 = cells(atCol: 2)
let tl = cellsTopLeft()
let bl = cellsBottomLeft()
if let cell = player.emptyCell(in: r0) {
return cell
} else if let cell = player.emptyCell(in: r1) {
return cell + 3
} else if let cell = player.emptyCell(in: r2) {
return cell + 6
} else if let cell = player.emptyCell(in: c0) {
return cell * 3
} else if let cell = player.emptyCell(in: c1) {
return cell * 3 + 1
} else if let cell = player.emptyCell(in: c2) {
return cell * 3 + 2
} else if let cell = player.emptyCell(in: tl) {
return cell == 0 ? 0 : (cell == 1 ? 4 : 8)
} else if let cell = player.emptyCell(in: bl) {
return cell == 0 ? 6 : (cell == 1 ? 4 : 2)
}
return nil
}
func findMove() -> Int {
let empties = cells.enumerated().filter { $0.1 == nil }
let r = Int(arc4random()) % empties.count
return empties[r].0
}
}
extension Board : CustomStringConvertible {
var description: String {
var result = "\n---------------\n"
for (idx, cell) in cells.enumerated() {
if let cell = cell {
result += "| \(cell) |"
} else {
result += "| \(idx) |"
}
if (idx + 1) % 3 == 0 {
result += "\n---------------\n"
}
}
return result
}
}
while true {
var board = Board()
print("Who do you want to play as ('o' or 'x'): ", separator: "", terminator: "")
let answer = readLine()?.characters.first ?? "x"
var player: Token = answer == "x" ? .cross : .circle
var pc: Token = player == .cross ? .circle : .cross
print(board)
while true {
print("Choose cell to play on: ", separator: "", terminator: "")
var pos = Int(readLine() ?? "0") ?? 0
while !board.atCapacity() && board.cells[pos] != nil {
print("Invalid move. Choose cell to play on: ", separator: "", terminator: "")
pos = Int(readLine() ?? "0") ?? 0
}
if board.atCapacity() {
print("Draw")
break
}
board.play(token: player, at: pos)
print(board)
if let winner = board.winner() {
print("winner is \(winner)")
break
} else if board.atCapacity() {
print("Draw")
break
}
if let win = board.findBestLocation(for: pc) {
board.play(token: pc, at: win)
} else if let def = board.findBestLocation(for: player) {
board.play(token: pc, at: def)
} else {
board.play(token: pc, at: board.findMove())
}
print(board)
if let winner = board.winner() {
print("winner is \(winner)")
break
}
}
}