Skip to content

Instantly share code, notes, and snippets.

@Bonney
Last active May 26, 2023 13:55
Show Gist options
  • Select an option

  • Save Bonney/824394d545a9d0d1ddeebfc73557ed4d to your computer and use it in GitHub Desktop.

Select an option

Save Bonney/824394d545a9d0d1ddeebfc73557ed4d to your computer and use it in GitHub Desktop.
Simple tic-tac-toe game in Swift.
import Foundation
enum Marker: String {
case x = "x"
case o = "o"
}
typealias Position = Int
enum ValidPositions {
static let all: [Position] = [
1, 2, 3,
4, 5, 6,
7, 8, 9
]
}
enum WinConditions {
static let all: [[Position]] = [
// Rows
[1, 2, 3], [4, 5, 6], [7, 8, 9],
// Columns
[1, 4, 7], [2, 5, 8], [3, 6, 9],
// Diagonals
[1, 5, 9], [3, 5, 7]
]
}
extension Array<Position> {
func containsWinCondition() -> Bool {
guard ValidPositions.all.contains(self) else {
return false
}
for condition in WinConditions.all {
if condition.allSatisfy(self.contains) {
return true
}
}
return false
}
}
struct Player {
let name: String
let marker: Marker
}
struct Square {
let position: Position
var player: Player?
var description: String {
if let player {
return "[\(player.marker.rawValue)]"
} else {
return "[\(String(position))]"
}
}
}
typealias Board = Array<Square>
extension Board {
static func new() -> Board {
ValidPositions.all.map {
Square(position: $0)
}
}
var description: String {
let row1 = self[0].description + self[1].description + self[2].description + "\n"
let row2 = self[3].description + self[4].description + self[5].description + "\n"
let row3 = self[6].description + self[7].description + self[8].description + "\n"
return row1 + row2 + row3 + "========="
}
}
enum Result {
case playing
case win(Player)
case tie
var description: String {
switch self {
case .playing:
return "Game is in progress."
case .win(let player):
return "The winner is " + player.name + "!"
case.tie:
return "It's a tie..."
}
}
}
class Game {
var board: Board = .new()
let player1: Player
let player2: Player
init(player1: Player, player2: Player) {
self.player1 = player1
self.player2 = player2
}
func freePositions() -> [Position] {
board
.filter { $0.player == nil }
.map { $0.position }
}
var result: Result {
// make arrays with the claimed spots for each player
// and check each for a win condition
let player1Positions = board.filter { $0.player?.marker == player1.marker }
let player2Positions = board.filter { $0.player?.marker == player2.marker }
let player1HasWin = player1Positions
.map { $0.position }
.containsWinCondition()
let player2HasWin = player2Positions
.map { $0.position }
.containsWinCondition()
if (player1Positions + player2Positions).count >= board.count {
return Result.tie
}
switch (player1HasWin, player2HasWin) {
case (true, true): return Result.tie
case (true, false): return Result.win(player1)
case (false, true): return Result.win(player2)
case (false, false): return Result.playing
}
}
var description: String {
board.description + "\n" + result.description + "\n========="
}
func mark(position: Position, for player: Player) {
print(player.name + "[\(player.marker.rawValue)] marks position \(String(position)).")
board[position - 1].player = player
}
}
let bob = Player(name: "Bob", marker: .x)
let alice = Player(name: "Joe", marker: .o)
let game = Game(player1: bob, player2: alice)
var turnPlayer: Bool = true
while case .playing = game.result {
print(game.description)
if let choice = game.freePositions().randomElement() {
game.mark(position: choice, for: turnPlayer ? bob : alice)
turnPlayer.toggle()
}
}
print(game.description)
print("GAME OVER")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment