Last active
May 26, 2023 13:55
-
-
Save Bonney/824394d545a9d0d1ddeebfc73557ed4d to your computer and use it in GitHub Desktop.
Simple tic-tac-toe game in Swift.
This file contains hidden or 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
| 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