TicTacToe
Play
swift run TicTacToe
Which starts the game, presenting this beautiful ascii graphics:
-------------
| 1 | 2 | 3 |
-------------
| 4 | 5 | 6 |
-------------
| 7 | 8 | 9 |
-------------
playerX, which square:
And since this is the most pointless game in the history of games, get used to seeing the draw result.
⚖️ Game ended with a draw
-------------
| ✖ | ◯ | ✖ |
-------------
| ◯ | ✖ | ◯ |
-------------
| ✖ | ✖ | ◯ |
-------------
And in some rare cases where your partner had one to many
playerX, which square:
7
🎉 playerX won!
-------------
| ✖ | ◯ | ✖ |
-------------
| ◯ | ✖ | ◯ |
-------------
| ✖ | 8 | 9 |
-------------
Implementation
main.swift
func run() throws {
var game = TicTacToe(matchUp: .humanVersusHuman)
var result: TicTacToe.Result!
repeat {
result = try game.play()
} while result == nil
print("\n\(result!)")
game.printBoard()
}
TicTacToe.swift
mutating func play() throws -> Result? {
defer { activePlayer.toggle() }
let index = repeatedReadSquare(
prompt: "\(activePlayer), which square:",
ifBadNumber: "☣️ Bad input",
ifSquareTaken: "⚠️ Square not free",
tipOnHowToExitProgram: "💡 You can quit this program by pressing: `CTRL + c`"
) { board.is(square: $0, .free) }
try board.play(index: index, by: activePlayer)
if board.isFull() {
return .draw
} else {
return board.winner().map { .win(by: $0) }
}
}
Board
public extension TicTacToe {
struct Board: Equatable, CustomStringConvertible {
internal var fillAtSquare: [Square: Fill] = [:]
}
}
extension TicTacToe.Board {
/// Squares 0 through 8
enum Square: UInt8, ExpressibleByIntegerLiteral, CaseIterable, Hashable {
case zero, one, two, three, four, five, six, seven, eight
}
}
Got smarter ASCII print? PR!
public extension TicTacToe.Board {
func ascii() -> String {
let rowSeparator: String = .init(repeating: "-", count: 13) + "\n"
var output = rowSeparator
for row in Self.rows {
defer { output += "\n" + rowSeparator }
output += row.map({ "| \(ascii(square: $0)) "}).joined() + "|"
}
return output
}
}
private extension TicTacToe.Board {
func ascii(square: Square) -> String {
self[square].map({ $0.ascii }) ?? square.ascii
}
}
private extension TicTacToe.Board.Fill {
var ascii: String { rawValue }
}
private extension TicTacToe.Board.Square {
var ascii: String { "\(rawValue + 1)" }
}
Got smarter win condition check? PR!
public extension TicTacToe.Board {
func winner() -> Player? {
func hasPlayerWon(_ player: Player) -> Bool {
func check(_ squareMatrix: [[Square]]) -> Bool {
squareMatrix.reduce(false, { hasWon, squareList in
hasWon || squareList.allSatisfy({ hasPlayer(player, filledSquare: $0) })
})
}
return Self.winConditions.reduce(false, { hasWon, squareMatrix in
hasWon || check(squareMatrix)
})
}
return Player.allCases.first(where: { hasPlayerWon($0) })
}
}
extension TicTacToe.Board {
typealias Row = [Square]
typealias Column = [Square]
typealias Diagonal = [Square]
// MARK: Rows
static let firstRow: Row = [0, 1, 2]
static let secondRow: Row = [3, 4, 5]
static let thirdRow: Row = [6, 7, 8]
static let rows: [Row] = [firstRow, secondRow, thirdRow]
// MARK: Columns
static let firstColumn: Column = [0, 3, 6]
static let secondColumn: Column = [1, 4, 7]
static let thirdColumn: Column = [2, 5, 8]
static let columns: [Column] = [firstColumn, secondColumn, thirdColumn]
// MARK: Diagonals
/// Main, Major, Principal, Primary; diagonal: ╲
/// from top left, to bottom right
static let mainDiagonal: Diagonal = [2, 4, 6]
/// Anti-, Minor, Counter, Secondary; diagonal: ╱
/// from bottom left, to top right
static let antiDiagonal: Diagonal = [0, 4, 8]
static let diagonals: [Diagonal] = [mainDiagonal, antiDiagonal]
static let winConditions: [[[Square]]] = [rows, columns, diagonals]
}