Git Product home page Git Product logo

tictactoe's Introduction

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


 o │ x │ x
───┼───┼───
 x │ x │ o
───┼───┼───
 o │ o │ x

And in some rare cases where your partner had one to many 🍺 you might see this:

playerX, which square:
9

🎉  playerX won!


 x │ o │ o
───┼───┼───
 4 │ x │ 6
───┼───┼───
 7 │ 8 │ x

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 {
        Self.rows
            .map(ascii(row:))
            .joined(separator: "\n───┼───┼───\n")
    }
}

private extension TicTacToe.Board {
    
    func ascii(row: Row) -> String {
        " " + row.map(ascii(square:)).joined(separator: "")
    }

    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]
}

tictactoe's People

Contributors

msandiford avatar sajjon avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.