Repository: TheAlgorithms/Swift Branch: master Commit: 2c9eb8b85dee Files: 57 Total size: 101.8 KB Directory structure: gitextract_yelu_m4v/ ├── .github/ │ └── workflows/ │ ├── .stale.yml │ └── directory_workflow.yml ├── .gitignore ├── DIRECTORY.md ├── README.md ├── Search/ │ ├── BinarySearch.swift │ └── LinearSearch.swift ├── algorithms/ │ ├── AI/ │ │ └── minimax/ │ │ ├── README.md │ │ └── Sources/ │ │ ├── Minimax.playground/ │ │ │ ├── Contents.swift │ │ │ ├── Sources/ │ │ │ │ ├── Model/ │ │ │ │ │ ├── Board/ │ │ │ │ │ │ ├── Board.swift │ │ │ │ │ │ ├── BoardPosition.swift │ │ │ │ │ │ └── BoardStatus.swift │ │ │ │ │ ├── GameModel/ │ │ │ │ │ │ ├── DifficultLevel.swift │ │ │ │ │ │ └── GameModel.swift │ │ │ │ │ ├── Minimax/ │ │ │ │ │ │ ├── GameStateValue.swift │ │ │ │ │ │ └── Minimax.swift │ │ │ │ │ └── Player/ │ │ │ │ │ ├── Player.swift │ │ │ │ │ ├── PlayerSymbol.swift │ │ │ │ │ └── PlayerType.swift │ │ │ │ └── View/ │ │ │ │ └── BoardView.swift │ │ │ ├── contents.xcplayground │ │ │ └── playground.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── Tests/ │ │ ├── Tests/ │ │ │ ├── BoardTests.swift │ │ │ ├── Info.plist │ │ │ ├── MinimaxTests.swift │ │ │ └── PlayerTests.swift │ │ └── Tests.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ ├── conversion/ │ │ ├── binary-to-decimal.swift │ │ └── decimal-to-binary.swift │ ├── palindrome/ │ │ ├── palindrome_indices.swift │ │ ├── palindrome_recursion.swift │ │ └── palindrome_reversed.swift │ └── parsing/ │ └── shunting_yard/ │ └── shunting_yard.swift ├── data_structures/ │ ├── Linked List/ │ │ └── LinkedList.swift │ ├── Stack/ │ │ └── stack.swift │ ├── doubly_linked_list/ │ │ └── DoublyLinkedList.swift │ ├── heap/ │ │ └── heap.swift │ ├── queue/ │ │ └── queue.swift │ └── union_find/ │ └── union_find.swift ├── graph/ │ ├── BFS/ │ │ └── BFS.swift │ ├── DFS/ │ │ └── DFS.swift │ ├── Graph.swift │ └── spanning_tree/ │ ├── dijkstra.swift │ └── kruskal.swift ├── recursion/ │ └── fibonacci.swift ├── sorts/ │ ├── BubbleSort.swift │ ├── CocktailSort.swift │ ├── InsertionSort.swift │ ├── MergeSort.swift │ ├── PancakeSort.swift │ ├── QuickSort.swift │ └── SelectionSort.swift └── trees/ └── tree.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/.stale.yml ================================================ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale daysUntilStale: 30 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. daysUntilClose: 7 # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) onlyLabels: [] # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable exemptLabels: - "Status: on hold" # Set to true to ignore issues in a project (defaults to false) exemptProjects: false # Set to true to ignore issues in a milestone (defaults to false) exemptMilestones: false # Set to true to ignore issues with an assignee (defaults to false) exemptAssignees: false # Label to use when marking as stale staleLabel: stale # Comment to post when removing the stale label. # unmarkComment: > # Your comment here. # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': pulls: # Comment to post when marking as stale. Set to `false` to disable markComment: > This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale Pull Request. closeComment: > Please reopen this pull request once you commit the changes requested or make improvements on the code. If this is not the case and you need some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the reviewers. Thank you for your contributions! issues: # Comment to post when marking as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale Issue. closeComment: > Please reopen this issue once you add more information and updates here. If this is not the case and you need some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the reviewers. Thank you for your contributions! ================================================ FILE: .github/workflows/directory_workflow.yml ================================================ name: directory_md on: [push, pull_request] jobs: MainSequence: name: DIRECTORY.md runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 # v2 is broken for git diff - uses: actions/setup-python@v2 - name: Setup Git Specs run: | git config --global user.name github-actions git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY - name: Update DIRECTORY.md shell: python run: | import os from typing import Iterator URL_BASE = "https://github.com/TheAlgorithms/Swift/blob/master" g_output = [] def good_filepaths(top_dir: str = ".") -> Iterator[str]: fs_exts = tuple(".swift".split()) for dirpath, dirnames, filenames in os.walk(top_dir): dirnames[:] = [d for d in dirnames if d[0] not in "._"] for filename in filenames: if os.path.splitext(filename)[1].lower() in fs_exts: yield os.path.join(dirpath, filename).lstrip("./") def md_prefix(i): return f"{i * ' '}*" if i else "\n##" def print_path(old_path: str, new_path: str) -> str: global g_output old_parts = old_path.split(os.sep) for i, new_part in enumerate(new_path.split(os.sep)): if i + 1 > len(old_parts) or old_parts[i] != new_part: if new_part: g_output.append(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") return new_path def build_directory_md(top_dir: str = ".") -> str: global g_output old_path = "" for filepath in sorted(good_filepaths(), key=str.lower): filepath, filename = os.path.split(filepath) if filepath != old_path: old_path = print_path(old_path, filepath) indent = (filepath.count(os.sep) + 1) if filepath else 0 url = "/".join((URL_BASE, filepath, filename)).replace(" ", "%20") filename = os.path.splitext(filename.replace("_", " ").title())[0] g_output.append(f"{md_prefix(indent)} [{filename}]({url})") return "# List of all files\n" + "\n".join(g_output) with open("DIRECTORY.md", "w") as out_file: out_file.write(build_directory_md(".") + "\n") - name: Commit DIRECTORY.md run: | git commit -m "updating DIRECTORY.md" DIRECTORY.md || true git diff DIRECTORY.md git push --force origin HEAD:$GITHUB_REF || true ================================================ FILE: .gitignore ================================================ .DS_Store ================================================ FILE: DIRECTORY.md ================================================ # List of all files ## Algorithms * Ai * Minimax * Sources * Minimax.Playground * [Contents](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Contents.swift) * Sources * Model * Board * [Board](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/Board.swift) * [Boardposition](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/BoardPosition.swift) * [Boardstatus](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/BoardStatus.swift) * Gamemodel * [Difficultlevel](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/GameModel/DifficultLevel.swift) * [Gamemodel](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/GameModel/GameModel.swift) * Minimax * [Gamestatevalue](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Minimax/GameStateValue.swift) * [Minimax](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Minimax/Minimax.swift) * Player * [Player](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/Player.swift) * [Playersymbol](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/PlayerSymbol.swift) * [Playertype](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/PlayerType.swift) * View * [Boardview](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Minimax.playground/Sources/View/BoardView.swift) * Tests * Tests * [Boardtests](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Tests/Tests/BoardTests.swift) * [Minimaxtests](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Tests/Tests/MinimaxTests.swift) * [Playertests](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/AI/minimax/Sources/Tests/Tests/PlayerTests.swift) * Conversion * [Binary-To-Decimal](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/conversion/binary-to-decimal.swift) * [Decimal-To-Binary](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/conversion/decimal-to-binary.swift) * Palindrome * [Palindrome Indices](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/palindrome/palindrome_indices.swift) * [Palindrome Recursion](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/palindrome/palindrome_recursion.swift) * [Palindrome Reversed](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/palindrome/palindrome_reversed.swift) * Parsing * Shunting Yard * [Shunting Yard](https://github.com/TheAlgorithms/Swift/blob/master/algorithms/parsing/shunting_yard/shunting_yard.swift) ## Data Structures * Doubly Linked List * [Doublylinkedlist](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/doubly_linked_list/DoublyLinkedList.swift) * Heap * [Heap](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/heap/heap.swift) * Linked List * [Linkedlist](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/Linked%20List/LinkedList.swift) * Queue * [Queue](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/queue/queue.swift) * Stack * [Stack](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/Stack/stack.swift) * Union Find * [Union Find](https://github.com/TheAlgorithms/Swift/blob/master/data_structures/union_find/union_find.swift) ## Graph * Bfs * [Bfs](https://github.com/TheAlgorithms/Swift/blob/master/graph/BFS/BFS.swift) * Dfs * [Dfs](https://github.com/TheAlgorithms/Swift/blob/master/graph/DFS/DFS.swift) * [Graph](https://github.com/TheAlgorithms/Swift/blob/master/graph/Graph.swift) * Spanning Tree * [Dijkstra](https://github.com/TheAlgorithms/Swift/blob/master/graph/spanning_tree/dijkstra.swift) * [Kruskal](https://github.com/TheAlgorithms/Swift/blob/master/graph/spanning_tree/kruskal.swift) ## Recursion * [Fibonacci](https://github.com/TheAlgorithms/Swift/blob/master/recursion/fibonacci.swift) ## Search * [Binarysearch](https://github.com/TheAlgorithms/Swift/blob/master/Search/BinarySearch.swift) * [Linearsearch](https://github.com/TheAlgorithms/Swift/blob/master/Search/LinearSearch.swift) ## Sorts * [Bubblesort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/BubbleSort.swift) * [Cocktailsort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/CocktailSort.swift) * [Insertionsort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/InsertionSort.swift) * [Mergesort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/MergeSort.swift) * [Pancakesort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/PancakeSort.swift) * [Quicksort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/QuickSort.swift) * [Selectionsort](https://github.com/TheAlgorithms/Swift/blob/master/sorts/SelectionSort.swift) ## Trees * [Tree](https://github.com/TheAlgorithms/Swift/blob/master/trees/tree.swift) ================================================ FILE: README.md ================================================ # The Algorithms - Swift ### All algorithms implemented in Swift (for education) These implementations are for learning purposes. They may be less efficient than the implementations in the Swift standard library. ## Community Channel We're on [Gitter](https://gitter.im/TheAlgorithms)! Please join us. ## List of Algorithms See our [directory](DIRECTORY.md). ================================================ FILE: Search/BinarySearch.swift ================================================ import Foundation public func binarySearch(_ a: [T], key: T) -> Int? { var lowerBound = 0 var upperBound = a.count while lowerBound < upperBound { let midIndex = lowerBound + (upperBound - lowerBound) / 2 if a[midIndex] == key { return midIndex } else if a[midIndex] < key { lowerBound = midIndex + 1 } else { upperBound = midIndex } } return nil } // The code below can be used for testing // var numbers = [7, 10, 13, 17, 19, 24, 29, 31, 32, 37, 41, 43, 47, 51, 53, 119, 163, 611, 627] // if let searchIndex = binarySearch(numbers, key: 10) { // print("Element found on index: \(searchIndex)") // } // else { // print("Element not found") // } ================================================ FILE: Search/LinearSearch.swift ================================================ import Foundation func linearSearch(_ array: [T], _ object: T) -> Int? { for (index, obj) in array.enumerated() where obj == object { return index } return nil } // The code below can be used for testing // var numbers = [10, 119, 13, 24, 53, 17, 31, 7, 19, 627, 47, 163, 37, 611, 29, 43, 51, 41, 32] // if let searchIndex = linearSearch(numbers,31) { // print("Element found on index: \(searchIndex)") // } // else { // print("Element not found") // } ================================================ FILE: algorithms/AI/minimax/README.md ================================================ # Minimax algorithm

## Runtime environment ## Table of contents * [General info](#general-info) * [Functionality](#functionality) * [Pseudocode](#pseudocode) * [Demo](#demo) * [Sources](#sources) ## General info It is an example of implementation and use ``minimax algorithm`` in ``Tic Tac Toe`` game. Minimax is an algorithm that searches deeply into all possible states in the game. There are two types of players in the algorithm. One that wants to maximize the state of the game and one that wants to minimaze the state of the game. There are three states in the tic-tac-toe game: - `` -1 `` - if the minimizing player wins - `` 0 `` - in case of a tie - `` 1 `` - if the maximizing player wins ``Alpha-beta prunning`` this is a method that interrupts the search of those branches that do not lead to win. Alpha for maximizing player and beta for minimizing player. Alpha-beta prunnings reduce the time complexity of the algorithm. Parameters: - ``searching depth`` - how many moves in depth is to be calculated by the algorithm Input: - ``actual board state`` - in the form of an array for players symbols (cross/circle) - ``two player symbols`` - cross / circle Output: - ``the best move for autonomus(AI) player`` - Position(row: Int, column: Int) ## Functionality - example of use in Swift Playground with interactive UIView - unit tests in XCode ## Pseudocode ``` function alphabeta(node, depth, α, β, maximizingPlayer) is if depth = 0 or node is a terminal node then return the heuristic value of node if maximizingPlayer then value := −∞ for each child of node do value := max(value, alphabeta(child, depth − 1, α, β, FALSE)) if value ≥ β then break (* β cutoff *) α := max(α, value) return value else value := +∞ for each child of node do value := min(value, alphabeta(child, depth − 1, α, β, TRUE)) if value ≤ α then break (* α cutoff *) β := min(β, value) return value ``` ## Demo

## Sources * Minimax algorithm: https://en.wikipedia.org/wiki/Minimax * Alpha-beta prunning: https://en.wikipedia.org/wiki/Alpha–beta_pruning ## Author Written by Michał Nowak(mnowak061) ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Contents.swift ================================================ import UIKit import PlaygroundSupport let boardSize = CGSize(width: 500, height: 550) let boardView = BoardView(frame: CGRect(origin: .zero, size: boardSize)) PlaygroundPage.current.needsIndefiniteExecution = true PlaygroundPage.current.liveView = boardView ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/Board.swift ================================================ public struct Board { // MARK: -- Public variable's public var size: Int // MARK: -- Private variable's private var table: [ [PlayerSymbol?] ] // MARK: -- Public function's public init(size: Int) { self.size = size self.table = [] self.clear() } public mutating func clear() { self.table = Array(repeating: Array(repeating: PlayerSymbol.empty, count: size), count: size) } public func hasEmptyField() -> Bool { for i in 0 ..< self.size { for j in 0 ..< self.size { if self.table[i][j] == PlayerSymbol.empty { return true } } } return false } public func symbol(forPosition position: Position) -> PlayerSymbol? { guard position.row < self.size, position.column < size else { return nil } return self.table[position.row][position.column] } public mutating func makeMove(player: Player, position: Position) { guard self.symbol(forPosition: position) == PlayerSymbol.empty else { return } guard self.symbol(forPosition: position) != player.symbol else { return } self.table[position.row][position.column] = player.symbol } public func check(player: Player) -> BoardStatus { let playerSymbol: PlayerSymbol = player.symbol if self.foundWinInRows(playerSymbol) { return BoardStatus.win } if self.foundWinInColumns(playerSymbol) { return BoardStatus.win } if self.foundWinInSlants(playerSymbol) { return BoardStatus.win } if self.hasEmptyField() { return BoardStatus.continues } else { return BoardStatus.draw } } // MARK: -- Private function's private func foundWinInRows(_ playerSymbol: PlayerSymbol) -> Bool { for i in 0 ..< self.size { var theSameSymbolsInRowCount = 0 for j in 0 ..< self.size - 1 { if self.table[i][j] == self.table[i][j+1] && (self.table[i][j] == playerSymbol) { theSameSymbolsInRowCount += 1 } else { theSameSymbolsInRowCount = 0 } } if theSameSymbolsInRowCount == self.size - 1 { return true } } return false } private func foundWinInColumns(_ playerSymbol: PlayerSymbol) -> Bool { for j in 0 ..< self.size { var theSameSymbolsInColumnCount = 0 for i in 0 ..< self.size - 1 { if self.table[i][j] == self.table[i+1][j] && (self.table[i][j] == playerSymbol) { theSameSymbolsInColumnCount += 1 } else { theSameSymbolsInColumnCount = 0 } } if theSameSymbolsInColumnCount == self.size - 1 { return true } } return false } private func foundWinInSlants(_ playerSymbol: PlayerSymbol) -> Bool { var theSameSymbolsInSlantCount = 0 for i in 0 ..< self.size { for j in -(self.size - 1) ... 0 { if(self.table[-j][i] == playerSymbol) { var k: Int = -j var l: Int = i theSameSymbolsInSlantCount = 0 while l < self.size && k >= 0 { if self.table[k][l] == playerSymbol { theSameSymbolsInSlantCount += 1 } else { theSameSymbolsInSlantCount = 0 } k -= 1 l += 1 if theSameSymbolsInSlantCount == self.size { return true } } theSameSymbolsInSlantCount = 0 } theSameSymbolsInSlantCount = 0 } theSameSymbolsInSlantCount = 0 } theSameSymbolsInSlantCount = 0 for i in 0 ..< self.size { for j in 0 ..< self.size { if(self.table[j][i] == playerSymbol) { var k: Int = j var l: Int = i theSameSymbolsInSlantCount = 0 while l < self.size && k < self.size { if self.table[k][l] == playerSymbol { theSameSymbolsInSlantCount += 1 } else { theSameSymbolsInSlantCount = 0 } k += 1 l += 1 if theSameSymbolsInSlantCount == self.size { return true } } theSameSymbolsInSlantCount = 0 } theSameSymbolsInSlantCount = 0 } theSameSymbolsInSlantCount = 0 } return false } } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/BoardPosition.swift ================================================ public typealias Position = (row: Int, column: Int) ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Board/BoardStatus.swift ================================================ public enum BoardStatus { case continues case win case draw } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/GameModel/DifficultLevel.swift ================================================ public enum DifficultLevel: Int { case easy = 2 case medium = 3 case hard = 5 } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/GameModel/GameModel.swift ================================================ import Foundation public class GameModel { // MARK: -- Public variable's public var board: Board! public var gameStatus: BoardStatus // MARK: -- Private variable's private var playersList: [Player]! private var movementsSequence: [Int]! private var actualPlayerIndex: Int! private var actualPlayer: Player { get { return playersList[actualPlayerIndex] } } private var difficultLevel: DifficultLevel = DifficultLevel.hard // MARK: -- Public function's public init(boardSize: Int, playersList: [Player], difficultLevel: DifficultLevel) { self.board = Board.init(size: boardSize) self.playersList = playersList self.difficultLevel = difficultLevel self.gameStatus = BoardStatus.continues self.generateMovementsSequence() self.changeActualPlayer() } public func update() { self.gameStatus = board.check(player: actualPlayer) switch self.gameStatus { case BoardStatus.continues: changeActualPlayer() case BoardStatus.draw: changeActualPlayer() default: break } } public func playerMakeMove(selectedPosition: (row: Int, column: Int)) { guard board.symbol(forPosition: selectedPosition) == PlayerSymbol.empty else { return } guard board.hasEmptyField() == true else { return } board.makeMove(player: actualPlayer, position: selectedPosition) update() } public func makeMinimaxMove() { guard actualPlayer.type == PlayerType.computer else { return } guard board.hasEmptyField() == true else { return } sleep(1) let selectedPosition: Position = minimaxMove(board: board, player: playersList[0], opponent: playersList[1], depth: self.difficultLevel.rawValue) board.makeMove(player: actualPlayer, position: selectedPosition) update() } public func newRound() { board.clear() gameStatus = BoardStatus.continues generateMovementsSequence() changeActualPlayer() } // MARK: -- Private function's private func generateMovementsSequence() { self.movementsSequence = [] let playersCount = playersList.count let movesCount = (board.size * board.size) var move = Int.random(in: 0 ..< playersCount) movementsSequence.append(move) for _ in 0 ..< movesCount - 1 { move += 1 movementsSequence.append(move % playersCount) } } private func changeActualPlayer() { if !movementsSequence.isEmpty { actualPlayerIndex = movementsSequence.first! movementsSequence.removeFirst() } } } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Minimax/GameStateValue.swift ================================================ public enum GameStateValue: Int { case min = -1 case null = 0 case max = 1 } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Minimax/Minimax.swift ================================================ public func minimaxMove(board: Board, player: Player, opponent: Player, depth: Int) -> Position { var bestVal = GameStateValue.min var bestMoves: [Position] = [] for i in 0 ..< board.size { for j in 0 ..< board.size { if board.symbol(forPosition: Position(i, j)) == PlayerSymbol.empty { var tempBoard = board let move = Position(i, j) tempBoard.makeMove(player: opponent, position: (i, j)) let moveVal = minMax(board: tempBoard, player: opponent, opponent: player, depth: depth, alpha: GameStateValue.min.rawValue, beta: GameStateValue.max.rawValue, maximizingPlayer: false) if moveVal.rawValue > bestVal.rawValue { bestVal = moveVal bestMoves = [] bestMoves.append(move) } else if moveVal == bestVal { bestMoves.append(move) } } } } return bestMoves[Int.random(in: 0 ..< bestMoves.count)] } public func minMax(board: Board, player: Player, opponent: Player, depth: Int, alpha: Int, beta: Int, maximizingPlayer: Bool) -> GameStateValue { var alpha = alpha var beta = beta if let gameResult = evaluateGameState(board: board, player: player, opponent: opponent) { guard depth != 0 && gameResult != GameStateValue.min && gameResult != GameStateValue.max && gameResult != GameStateValue.null else { return gameResult } } if maximizingPlayer { var maxEval = GameStateValue.min for i in 0 ..< board.size { for j in 0 ..< board.size { if board.symbol(forPosition: Position(i, j)) == PlayerSymbol.empty { var tempBoard = board tempBoard.makeMove(player: player, position: Position(i, j)) let eval = minMax(board: tempBoard, player: player, opponent: opponent, depth: depth - 1, alpha: alpha, beta: beta, maximizingPlayer: !maximizingPlayer) maxEval = GameStateValue(rawValue: max(maxEval.rawValue, eval.rawValue))! alpha = max(alpha, eval.rawValue) if beta <= alpha { break } } } } return maxEval } else { var minEval = GameStateValue.max for i in 0 ..< board.size { for j in 0 ..< board.size { if board.symbol(forPosition: Position(i, j)) == PlayerSymbol.empty { var tempBoard = board tempBoard.makeMove(player: opponent, position: (i, j)) let eval = minMax(board: tempBoard, player: player, opponent: opponent, depth: depth - 1, alpha: alpha, beta: beta, maximizingPlayer: !maximizingPlayer) minEval = GameStateValue(rawValue: min(minEval.rawValue, eval.rawValue))! beta = min(beta, eval.rawValue) if beta <= alpha { break } } } } return minEval } } public func evaluateGameState(board: Board, player: Player, opponent: Player) -> GameStateValue? { if board.check(player: player) == BoardStatus.win { return GameStateValue.max } else if board.check(player: opponent) == BoardStatus.win { return GameStateValue.min } else if board.check(player: player) == BoardStatus.draw || board.check(player: opponent) == BoardStatus.draw { return GameStateValue.null } return nil } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/Player.swift ================================================ public struct Player { // MARK: -- Public variable's public var type: PlayerType public var symbol: PlayerSymbol // MARK: -- Public function's public init(type: PlayerType, symbol: PlayerSymbol) { self.type = type self.symbol = symbol } } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/PlayerSymbol.swift ================================================ public enum PlayerSymbol: String { case empty = "" case circle = "⭕️" case cross = "❌" } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/Model/Player/PlayerType.swift ================================================ public enum PlayerType { case computer case human } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/Sources/View/BoardView.swift ================================================ import UIKit public class BoardView: UIView { // MARK: -- Public public var gameModel: GameModel! public var players = [Player(type: .human, symbol: .circle), Player(type: .computer, symbol: .cross)] // MARK: -- Override's public override init(frame: CGRect) { super.init(frame: frame) self.setupBoard() self.setupResetButton() self.setupIndicator() self.startGame() } required init?(coder: NSCoder) { super.init(coder: coder) } // MARK: -- Private private var buttons: [UIButton] = [] private var stackView: UIStackView! private var resetButton: UIButton! private var indicator: UIActivityIndicatorView! private func startGame() { self.gameModel = GameModel.init(boardSize: 3, playersList: self.players, difficultLevel: .hard) DispatchQueue.global(qos: .userInteractive).async { self.blockViewForUser() self.gameModel.makeMinimaxMove() self.unblockViewForUser() } } private func updateUI() { if gameModel.gameStatus != BoardStatus.continues { self.resetButton.setTitle("New game", for: .normal) blockButtons() } else { self.resetButton.setTitle("Reset", for: .normal) } boardToButtons() } private func boardToButtons() { var buttonIndex = 0 for row in 0 ..< 3 { for column in 0 ..< 3 { let symbol = gameModel.board.symbol(forPosition: Position(row, column)) if symbol != PlayerSymbol.empty { self.buttons[buttonIndex].setTitle(symbol?.rawValue, for: .normal) self.buttons[buttonIndex].isUserInteractionEnabled = false } buttonIndex += 1 } } } private func setupBoard() { self.stackView = UIStackView() self.stackView.translatesAutoresizingMaskIntoConstraints = false self.stackView.axis = .vertical self.stackView.alignment = .fill self.stackView.distribution = .fillEqually self.stackView.spacing = 10 self.addSubview(self.stackView) for index in 1 ... 3 { let boardRow = self.createBoardRow(rowNumber: index) self.stackView.addArrangedSubview(boardRow) } // constraints let constraints = [ self.stackView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10), self.stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), self.stackView.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -20), self.stackView.heightAnchor.constraint(equalTo: self.stackView.widthAnchor) ] NSLayoutConstraint.activate(constraints) } private func createBoardRow(rowNumber: Int) -> UIStackView { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .horizontal stackView.alignment = .fill stackView.distribution = .fillEqually stackView.spacing = 10 for index in 1 ... 3 { let button = UIButton() let id = String(index + ( (rowNumber - 1) * 3 ) ) button.restorationIdentifier = id button.backgroundColor = .lightGray button.titleLabel?.font = UIFont(name: "Helvetica", size: 50) button.addTarget(self, action: #selector(buttonPressed(_:)), for: .touchUpInside) self.buttons.append(button) stackView.addArrangedSubview(button) } return stackView } private func blockViewForUser() { DispatchQueue.main.async { self.resetButton.isHidden = true self.indicator.isHidden = false self.indicator.startAnimating() self.blockButtons() self.updateUI() } } private func unblockViewForUser() { DispatchQueue.main.async { self.unblockButtons() self.updateUI() self.resetButton.isHidden = false self.indicator.isHidden = true self.indicator.stopAnimating() } } @objc private func buttonPressed(_ sender: UIButton) { let position = buttonIDtoPosition(id: sender.restorationIdentifier!) DispatchQueue.global(qos: .userInteractive).async { self.gameModel.playerMakeMove(selectedPosition: position) self.blockViewForUser() self.gameModel.makeMinimaxMove() self.unblockViewForUser() } } private func setupResetButton() { self.resetButton = UIButton(type: .system) self.resetButton.translatesAutoresizingMaskIntoConstraints = false self.resetButton.setTitle("Reset", for: .normal) self.resetButton.backgroundColor = .lightGray self.resetButton.addTarget(self, action: #selector(resetButtonPressed(_:)), for: .touchUpInside) self.addSubview(self.resetButton) // constraints let constraints = [ self.resetButton.topAnchor.constraint(equalTo: self.stackView.bottomAnchor, constant: 10), self.resetButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), self.resetButton.widthAnchor.constraint(equalTo: self.widthAnchor) ] NSLayoutConstraint.activate(constraints) } @objc private func resetButtonPressed(_ sender: UIButton) { self.gameModel.newRound() self.clearButtons() self.startGame() } private func setupIndicator() { self.indicator = UIActivityIndicatorView() self.indicator.translatesAutoresizingMaskIntoConstraints = false self.indicator.backgroundColor = .lightGray self.addSubview(self.indicator) // constraints let constraints = [ self.indicator.topAnchor.constraint(equalTo: self.stackView.bottomAnchor, constant: 10), self.indicator.bottomAnchor.constraint(equalTo: self.bottomAnchor), self.indicator.widthAnchor.constraint(equalTo: self.widthAnchor) ] NSLayoutConstraint.activate(constraints) } private func buttonIDtoPosition(id: String) -> Position { switch id { case "1": return Position(0, 0) case "2": return Position(0, 1) case "3": return Position(0, 2) case "4": return Position(1, 0) case "5": return Position(1, 1) case "6": return Position(1, 2) case "7": return Position(2, 0) case "8": return Position(2, 1) case "9": return Position(2, 2) default: return Position(0, 0) } } private func clearButtons() { for button in self.buttons { button.setTitle("", for: .normal) button.isUserInteractionEnabled = true } } private func unblockButtons() { for button in self.buttons { button.isUserInteractionEnabled = true } } private func blockButtons() { for button in self.buttons { button.isUserInteractionEnabled = false } } } ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/contents.xcplayground ================================================ ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/playground.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: algorithms/AI/minimax/Sources/Minimax.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests/BoardTests.swift ================================================ import XCTest class BoardTests: XCTestCase { private var sut: Board! private var boardSize = 3 override func setUp() { super.setUp() sut = Board(size: boardSize) } override func tearDown() { sut = nil super.tearDown() } func testInit() { XCTAssertEqual(sut.size, boardSize) XCTAssertEqual(allFieldsAreEmpty(), true) } func testSymbolForPosition() { let player = Player(type: .human, symbol: .circle) let position = Position(0, 0) sut.clear() XCTAssertEqual(sut.symbol(forPosition: position), PlayerSymbol.empty) sut.makeMove(player: player, position: position) XCTAssertEqual(sut.symbol(forPosition: position), player.symbol) } func testClear() { let player = Player(type: .computer, symbol: .circle) let position = Position(0, 0) sut.makeMove(player: player, position: position) XCTAssertEqual(allFieldsAreEmpty(), false) sut.clear() XCTAssertEqual(allFieldsAreEmpty(), true) } func testHasEmptyField() { let player = Player(type: .computer, symbol: .circle) sut.clear() XCTAssertEqual(sut.hasEmptyField(), true) sut.makeMove(player: player, position: Position(0, 0)) sut.makeMove(player: player, position: Position(0, 1)) sut.makeMove(player: player, position: Position(0, 2)) sut.makeMove(player: player, position: Position(1, 0)) sut.makeMove(player: player, position: Position(1, 1)) sut.makeMove(player: player, position: Position(1, 2)) sut.makeMove(player: player, position: Position(2, 0)) sut.makeMove(player: player, position: Position(2, 1)) sut.makeMove(player: player, position: Position(2, 2)) XCTAssertEqual(sut.hasEmptyField(), false) } func testMakeMove() { let firstPlayer = Player(type: .human, symbol: .circle) let secondPlayer = Player(type: .human, symbol: .cross) let position = Position(0, 0) sut.clear() sut.makeMove(player: firstPlayer, position: position) sut.makeMove(player: secondPlayer, position: position) XCTAssertEqual(sut.symbol(forPosition: position), firstPlayer.symbol) } func testCheck() { let firstPlayer = Player(type: .computer, symbol: .circle) let secondPlayer = Player(type: .computer, symbol: .cross) sut.clear() XCTAssertEqual(sut.check(player: firstPlayer), BoardStatus.continues) XCTAssertEqual(sut.check(player: secondPlayer), BoardStatus.continues) sut.clear() sut.makeMove(player: firstPlayer, position: Position(0, 0)) sut.makeMove(player: firstPlayer, position: Position(0, 1)) sut.makeMove(player: firstPlayer, position: Position(0, 2)) XCTAssertEqual(sut.check(player: firstPlayer), BoardStatus.win) XCTAssertEqual(sut.check(player: secondPlayer), BoardStatus.continues) sut.clear() sut.makeMove(player: firstPlayer, position: Position(0, 0)) sut.makeMove(player: firstPlayer, position: Position(1, 0)) sut.makeMove(player: firstPlayer, position: Position(2, 0)) XCTAssertEqual(sut.check(player: firstPlayer), BoardStatus.win) XCTAssertEqual(sut.check(player: secondPlayer), BoardStatus.continues) sut.clear() sut.makeMove(player: firstPlayer, position: Position(0, 0)) sut.makeMove(player: firstPlayer, position: Position(1, 1)) sut.makeMove(player: firstPlayer, position: Position(2, 2)) XCTAssertEqual(sut.check(player: firstPlayer), BoardStatus.win) XCTAssertEqual(sut.check(player: secondPlayer), BoardStatus.continues) sut.clear() sut.makeMove(player: firstPlayer, position: Position(0, 0)) sut.makeMove(player: secondPlayer, position: Position(0, 1)) sut.makeMove(player: secondPlayer, position: Position(0, 2)) sut.makeMove(player: secondPlayer, position: Position(1, 0)) sut.makeMove(player: firstPlayer, position: Position(1, 1)) sut.makeMove(player: firstPlayer, position: Position(1, 2)) sut.makeMove(player: secondPlayer, position: Position(2, 0)) sut.makeMove(player: firstPlayer, position: Position(2, 1)) sut.makeMove(player: secondPlayer, position: Position(2, 2)) XCTAssertEqual(sut.check(player: firstPlayer), BoardStatus.draw) XCTAssertEqual(sut.check(player: secondPlayer), BoardStatus.draw) } private func allFieldsAreEmpty() -> Bool { var allFieldAreEmpty = true for row in 0 ..< sut.size { for column in 0 ..< sut.size { if sut.symbol(forPosition: Position(row, column)) != PlayerSymbol.empty { allFieldAreEmpty = false } } } return allFieldAreEmpty } } ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests/MinimaxTests.swift ================================================ import XCTest class MinimaxTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testEvaluateGameState() { var board = Board(size: 3) let firstPlayer = Player(type: .human, symbol: .cross) let secondPlayer = Player(type: .human, symbol: .circle) board.clear() XCTAssertEqual(evaluateGameState(board: board, player: firstPlayer, opponent: secondPlayer), nil) board.makeMove(player: firstPlayer, position: Position(0, 0)) XCTAssertEqual(evaluateGameState(board: board, player: firstPlayer, opponent: secondPlayer), nil) board.makeMove(player: firstPlayer, position: Position(0, 1)) board.makeMove(player: firstPlayer, position: Position(0, 2)) XCTAssertEqual(evaluateGameState(board: board, player: firstPlayer, opponent: secondPlayer), .max) XCTAssertEqual(evaluateGameState(board: board, player: secondPlayer, opponent: firstPlayer), .min) board.clear() board.makeMove(player: secondPlayer, position: Position(0, 0)) board.makeMove(player: secondPlayer, position: Position(0, 1)) board.makeMove(player: secondPlayer, position: Position(0, 2)) board.makeMove(player: firstPlayer, position: Position(1, 0)) XCTAssertEqual(evaluateGameState(board: board, player: firstPlayer, opponent: secondPlayer), .min) XCTAssertEqual(evaluateGameState(board: board, player: secondPlayer, opponent: firstPlayer), .max) board.clear() board.makeMove(player: firstPlayer, position: Position(0, 0)) board.makeMove(player: secondPlayer, position: Position(0, 1)) board.makeMove(player: secondPlayer, position: Position(0, 2)) board.makeMove(player: secondPlayer, position: Position(1, 0)) board.makeMove(player: firstPlayer, position: Position(1, 1)) board.makeMove(player: firstPlayer, position: Position(1, 2)) board.makeMove(player: secondPlayer, position: Position(2, 0)) board.makeMove(player: firstPlayer, position: Position(2, 1)) board.makeMove(player: secondPlayer, position: Position(2, 2)) XCTAssertEqual(evaluateGameState(board: board, player: firstPlayer, opponent: secondPlayer), .null) XCTAssertEqual(evaluateGameState(board: board, player: secondPlayer, opponent: firstPlayer), .null) } } ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests/PlayerTests.swift ================================================ import XCTest class PlayerTests: XCTestCase { private var sut: Player! private var playerType: PlayerType = .human private var playerSymbol: PlayerSymbol = .circle override func setUp() { super.setUp() sut = Player(type: playerType, symbol: playerSymbol) } override func tearDown() { sut = nil super.tearDown() } func testInit() { XCTAssertEqual(sut.type, playerType) XCTAssertEqual(sut.symbol, playerSymbol) } } ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 9D029435268265690015843C /* BoardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D029434268265690015843C /* BoardTests.swift */; }; 9D02946D268285E20015843C /* PlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02946C268285E20015843C /* PlayerTests.swift */; }; 9D0294852682B1850015843C /* Minimax.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02947A2682B1840015843C /* Minimax.swift */; }; 9D0294862682B1850015843C /* PlayerSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02947B2682B1840015843C /* PlayerSymbol.swift */; }; 9D0294872682B1850015843C /* Board.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02947D2682B1840015843C /* Board.swift */; }; 9D0294882682B1850015843C /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02947E2682B1840015843C /* Player.swift */; }; 9D0294892682B1850015843C /* BoardPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D02947F2682B1840015843C /* BoardPosition.swift */; }; 9D02948A2682B1850015843C /* PlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D0294802682B1840015843C /* PlayerType.swift */; }; 9D02948B2682B1850015843C /* GameModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D0294812682B1840015843C /* GameModel.swift */; }; 9D02948C2682B1850015843C /* DifficultLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D0294822682B1840015843C /* DifficultLevel.swift */; }; 9D02948D2682B1850015843C /* BoardStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D0294832682B1840015843C /* BoardStatus.swift */; }; 9D02948E2682B1850015843C /* GameStateValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D0294842682B1850015843C /* GameStateValue.swift */; }; 9DB8564E268129FE0046878A /* MinimaxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DB8564D268129FE0046878A /* MinimaxTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 9D029434268265690015843C /* BoardTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardTests.swift; sourceTree = ""; }; 9D02946C268285E20015843C /* PlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTests.swift; sourceTree = ""; }; 9D02947A2682B1840015843C /* Minimax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Minimax.swift; path = ../Minimax.playground/Sources/Model/Minimax/Minimax.swift; sourceTree = ""; }; 9D02947B2682B1840015843C /* PlayerSymbol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlayerSymbol.swift; path = ../Minimax.playground/Sources/Model/Player/PlayerSymbol.swift; sourceTree = ""; }; 9D02947D2682B1840015843C /* Board.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Board.swift; path = ../Minimax.playground/Sources/Model/Board/Board.swift; sourceTree = ""; }; 9D02947E2682B1840015843C /* Player.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Player.swift; path = ../Minimax.playground/Sources/Model/Player/Player.swift; sourceTree = ""; }; 9D02947F2682B1840015843C /* BoardPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BoardPosition.swift; path = ../Minimax.playground/Sources/Model/Board/BoardPosition.swift; sourceTree = ""; }; 9D0294802682B1840015843C /* PlayerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlayerType.swift; path = ../Minimax.playground/Sources/Model/Player/PlayerType.swift; sourceTree = ""; }; 9D0294812682B1840015843C /* GameModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameModel.swift; path = ../Minimax.playground/Sources/Model/GameModel/GameModel.swift; sourceTree = ""; }; 9D0294822682B1840015843C /* DifficultLevel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DifficultLevel.swift; path = ../Minimax.playground/Sources/Model/GameModel/DifficultLevel.swift; sourceTree = ""; }; 9D0294832682B1840015843C /* BoardStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BoardStatus.swift; path = ../Minimax.playground/Sources/Model/Board/BoardStatus.swift; sourceTree = ""; }; 9D0294842682B1850015843C /* GameStateValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameStateValue.swift; path = ../Minimax.playground/Sources/Model/Minimax/GameStateValue.swift; sourceTree = ""; }; 9DB8564A268129FE0046878A /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9DB8564D268129FE0046878A /* MinimaxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinimaxTests.swift; sourceTree = ""; }; 9DB8564F268129FE0046878A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 9DB85647268129FE0046878A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 9DB8563F268129EF0046878A = { isa = PBXGroup; children = ( 9D02947D2682B1840015843C /* Board.swift */, 9D02947F2682B1840015843C /* BoardPosition.swift */, 9D0294832682B1840015843C /* BoardStatus.swift */, 9D0294822682B1840015843C /* DifficultLevel.swift */, 9D0294812682B1840015843C /* GameModel.swift */, 9D0294842682B1850015843C /* GameStateValue.swift */, 9D02947A2682B1840015843C /* Minimax.swift */, 9D02947E2682B1840015843C /* Player.swift */, 9D02947B2682B1840015843C /* PlayerSymbol.swift */, 9D0294802682B1840015843C /* PlayerType.swift */, 9DB8564C268129FE0046878A /* Tests */, 9DB8564B268129FE0046878A /* Products */, ); sourceTree = ""; }; 9DB8564B268129FE0046878A /* Products */ = { isa = PBXGroup; children = ( 9DB8564A268129FE0046878A /* Tests.xctest */, ); name = Products; sourceTree = ""; }; 9DB8564C268129FE0046878A /* Tests */ = { isa = PBXGroup; children = ( 9DB8564F268129FE0046878A /* Info.plist */, 9D02946C268285E20015843C /* PlayerTests.swift */, 9D029434268265690015843C /* BoardTests.swift */, 9DB8564D268129FE0046878A /* MinimaxTests.swift */, ); path = Tests; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 9DB85649268129FE0046878A /* Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 9DB85650268129FE0046878A /* Build configuration list for PBXNativeTarget "Tests" */; buildPhases = ( 9DB85646268129FE0046878A /* Sources */, 9DB85647268129FE0046878A /* Frameworks */, 9DB85648268129FE0046878A /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Tests; productName = Tests; productReference = 9DB8564A268129FE0046878A /* Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 9DB85640268129EF0046878A /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; TargetAttributes = { 9DB85649268129FE0046878A = { CreatedOnToolsVersion = 12.5; }; }; }; buildConfigurationList = 9DB85643268129EF0046878A /* Build configuration list for PBXProject "Tests" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 9DB8563F268129EF0046878A; productRefGroup = 9DB8564B268129FE0046878A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 9DB85649268129FE0046878A /* Tests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 9DB85648268129FE0046878A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 9DB85646268129FE0046878A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 9D029435268265690015843C /* BoardTests.swift in Sources */, 9D02948E2682B1850015843C /* GameStateValue.swift in Sources */, 9DB8564E268129FE0046878A /* MinimaxTests.swift in Sources */, 9D0294872682B1850015843C /* Board.swift in Sources */, 9D0294892682B1850015843C /* BoardPosition.swift in Sources */, 9D02948A2682B1850015843C /* PlayerType.swift in Sources */, 9D02948B2682B1850015843C /* GameModel.swift in Sources */, 9D02948C2682B1850015843C /* DifficultLevel.swift in Sources */, 9D0294862682B1850015843C /* PlayerSymbol.swift in Sources */, 9D02948D2682B1850015843C /* BoardStatus.swift in Sources */, 9D0294882682B1850015843C /* Player.swift in Sources */, 9D02946D268285E20015843C /* PlayerTests.swift in Sources */, 9D0294852682B1850015843C /* Minimax.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 9DB85644268129EF0046878A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Debug; }; 9DB85645268129EF0046878A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; 9DB85651268129FE0046878A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.example.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; }; 9DB85652268129FE0046878A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.example.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 9DB85643268129EF0046878A /* Build configuration list for PBXProject "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 9DB85644268129EF0046878A /* Debug */, 9DB85645268129EF0046878A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 9DB85650268129FE0046878A /* Build configuration list for PBXNativeTarget "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 9DB85651268129FE0046878A /* Debug */, 9DB85652268129FE0046878A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 9DB85640268129EF0046878A /* Project object */; } ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: algorithms/AI/minimax/Sources/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded ================================================ FILE: algorithms/conversion/binary-to-decimal.swift ================================================ import Foundation /// This function accepts a binary number as String and converts it to decimal as Int. /// If it's not valid binary number (a.k.a not made only from digits), it returns nil. public func convertBinaryToDecimal(binary: String) -> Int? { if let _ = Int(binary) { var decimal = 0 let digits = binary.map { Int(String($0))! }.reversed() print(digits) var power = 1 for digit in digits { decimal += digit * power power *= 2 } return decimal } return nil } ================================================ FILE: algorithms/conversion/decimal-to-binary.swift ================================================ /// This function accepts a non-negative number and returns its binary form as String. public func convertDecimalToBinary(decimal: Int) -> String { var binary = "" var decimal = decimal while decimal != 0 { binary.insert(decimal % 2 == 0 ? "0" : "1", at: binary.startIndex) decimal /= 2 } return binary } ================================================ FILE: algorithms/palindrome/palindrome_indices.swift ================================================ // A palindrome is a string that reads the same forwards and backwards. // // Examples: "level", "radar", "madam", "A man, a plan, a canal: Panama". extension String { /// Iteratively comparing characters from the beginning and end of the string. Only include letters and numbers. /// - Complexity: O(n), without allocating new space. func isPalindrome() -> Bool { var leftIndex = startIndex var rightIndex = index(before: endIndex) while leftIndex < rightIndex { guard self[leftIndex].isLetter || self[leftIndex].isNumber else { leftIndex = index(after: leftIndex) continue } guard self[rightIndex].isLetter || self[rightIndex].isNumber else { rightIndex = index(before: rightIndex) continue } guard self[leftIndex].lowercased() == self[rightIndex].lowercased() else { return false } leftIndex = index(after: leftIndex) rightIndex = index(before: rightIndex) } return true } } ================================================ FILE: algorithms/palindrome/palindrome_recursion.swift ================================================ // A palindrome is a string that reads the same forwards and backwards. // // Examples: "level", "radar", "madam", "A man, a plan, a canal: Panama". extension String { /// Recursively comparing characters from the beginning and end of the string. Only include letters and numbers. /// - Complexity: O(n), without allocating new space. func isPalindrome() -> Bool { isPalindromeRecursion( leftIndex: startIndex, rightIndex: index(before: endIndex) ) } private func isPalindromeRecursion( leftIndex: String.Index, rightIndex: String.Index ) -> Bool { guard leftIndex < rightIndex else { return true } guard self[leftIndex].isLetter || self[leftIndex].isNumber else { return isPalindromeRecursion( leftIndex: index(after: leftIndex), rightIndex: rightIndex ) } guard self[rightIndex].isLetter || self[rightIndex].isNumber else { return isPalindromeRecursion( leftIndex: leftIndex, rightIndex: index(before: rightIndex) ) } guard self[leftIndex].lowercased() == self[rightIndex].lowercased() else { return false } return isPalindromeRecursion( leftIndex: index(after: leftIndex), rightIndex: index(before: rightIndex) ) } } ================================================ FILE: algorithms/palindrome/palindrome_reversed.swift ================================================ // A palindrome is a string that reads the same forwards and backwards. // // Examples: "level", "radar", "madam", "A man, a plan, a canal: Panama". extension String { /// Using the `reverse()` method to reverse the string and comparing it with the original. Only include letters and numbers. /// - Complexity: O(n), with allocating O(n) space. func isPalindrome() -> Bool { let input = lowercased().filter { $0.isLetter || $0.isNumber } return input == String(input.reversed()) } } ================================================ FILE: algorithms/parsing/shunting_yard/shunting_yard.swift ================================================ import Foundation enum ShuntingYard { enum Operator: String, CaseIterable { case power = "^" case plus = "+" case minus = "-" case times = "*" case divide = "/" } static func evaluate(_ string: String) -> Double { let scanner = Scanner(string: string) var numberStack: [Double] = [] var operatorStack: [Operator] = [] func applyOperator(_ op: Operator) { guard let a = numberStack.popLast(), let b = numberStack.popLast() else { return } numberStack.append(op.apply(a, b)) } while !scanner.isAtEnd { if let op = scanner.scanOperator() { while let last = operatorStack.last, last.precedence > op.precedence || (op.leftAssociative && last.precedence == op.precedence) { applyOperator(last) operatorStack.removeLast() } operatorStack.append(op) } else if let number = scanner.scanDouble() { numberStack.append(number) } else { break } } while let op = operatorStack.popLast() { applyOperator(op) } return numberStack.first ?? 0 } } extension ShuntingYard.Operator { var precedence: Int { switch self { case .power: return 3 case .divide, .times: return 2 case .plus, .minus: return 1 } } var leftAssociative: Bool { switch self { case .power: return false case .plus, .minus, .times, .divide: return true } } func apply(_ a: Double, _ b: Double) -> Double { switch self { case .power: return pow(b, a) case .divide: return b / a case .times: return a * b case .plus: return a + b case .minus: return b - a } } } private extension Scanner { func scanOperator() -> ShuntingYard.Operator? { for op in ShuntingYard.Operator.allCases { if scanString(op.rawValue) != nil { return op } } return nil } } func testShuntingYard() { func test(_ x: String) { print(x,"=", ShuntingYard.evaluate(x)) } test("3 + 4 * 5") test("4 * 5 + 3") test("2 ^ 3 ^ 4") test("10.5 - 4 * 5") test("2 + 3 ^ 4") test("2 * 3 ^ 4") test("3 ^ 4") } ================================================ FILE: data_structures/Linked List/LinkedList.swift ================================================ import Foundation public class Node { public var value: Value? public var next: Node? public init(value: Value? = nil, next: Node? = nil) { self.value = value self.next = next } } extension Node: CustomStringConvertible { public var description: String { guard let next = next else { return "\(String(describing: value))" } return "\(String(describing: value)) -> " + String(describing: next) + " " } } public struct LinkedList { public var head: Node? public var tail: Node? public init() {} public var isEmpty: Bool { return head == nil } public mutating func push(_ value: Value) { head = Node(value: value, next: head) if tail == nil { tail = head } } public mutating func append(_ value: Value) { guard !isEmpty else { push(value) return } tail!.next = Node(value: value) tail = tail!.next } public func node(at index: Int) -> Node? { var currentNode = head var currentIndex = 0 while currentNode != nil && currentIndex < index { currentNode = currentNode!.next currentIndex += 1 } return currentNode } @discardableResult public mutating func insert(_ value: Value, after node: Node) -> Node { guard tail !== node else { append(value) return tail! } node.next = Node(value: value, next: node.next) return node.next! } @discardableResult public mutating func pop() -> Value? { defer { head = head?.next if isEmpty { tail = nil } } return head?.value } @discardableResult public mutating func removeLast() -> Value? { guard let head = head else { return nil } guard head.next != nil else { return pop() } var prev = head var current = head while let next = current.next { prev = current current = next } prev.next = nil tail = prev return current.value } @discardableResult public mutating func remove(after node: Node) -> Value? { defer { if node.next === tail { tail = node } node.next = node.next?.next } return node.next?.value } } extension LinkedList: CustomStringConvertible { public var description: String { guard let head = head else { return "Empty list" } return String(describing: head) } } // Below you can find a testing Scenario for Playground. /* import UIKit // Test Linked List let node1 = Node(value: 1) let node2 = Node(value: 2) let node3 = Node(value: 3) node1.next = node2 node2.next = node3 print(node1) var list = LinkedList() list.push(3) list.push(2) list.push(1) print(list) var listAppend = LinkedList() listAppend.append(1) listAppend.append(2) listAppend.append(3) print(listAppend) */ ================================================ FILE: data_structures/Stack/stack.swift ================================================ import Foundation public struct Stack { private var elements = [T]() public mutating func push(_ element: T) { elements.append(element) } public mutating func pop() -> T? { return elements.popLast() } public var isEmpty: Bool { return elements.isEmpty } public var count: Int { return elements.count } public var peek: T? { return elements.last } } // The code below can be used for testing var stack = Stack() stack.push(10) stack.push(20) stack.push(30) print(stack.count) print(stack.peek) print(stack.isEmpty) print(stack.pop()) print(stack.pop()) print(stack.pop()) print(stack.isEmpty) print(stack.count) ================================================ FILE: data_structures/doubly_linked_list/DoublyLinkedList.swift ================================================ import Foundation public class Node { public var value: Value? public var next: Node? public var prev: Node? public init(value: Value? = nil, next: Node? = nil, prev: Node? = nil) { self.value = value self.next = next self.prev = prev } } extension Node: CustomStringConvertible { public var description: String { guard let next: Node = self.next else { return "\(String(describing: value))" } return "\(String(describing: value)) <-> \(String(describing: next)) " } } public struct DoublyLinkedList { public var head: Node? public var tail: Node? public var count: Int = 0 public var isEmpty: Bool { return head == nil } public mutating func push(_ value: Value) { let new: Node = Node(value: value, next: head) if head != nil { head!.prev = new } head = new if tail == nil { tail = head } count += 1 } public mutating func append(_ value: Value) { guard !isEmpty else { push(value) return } tail!.next = Node(value: value, prev: tail) tail = tail!.next count += 1 } @discardableResult public mutating func insert(_ value: Value, after node: Node) -> Node { guard tail !== node else { append(value) return tail! } var new: Node = Node(value: value, next: node.next, prev: node) node.next?.prev = new node.next = new count += 1 return node.next! } @discardableResult public mutating func insert(_ value: Value, before node: Node) -> Node { guard head !== node else { push(value) return head! } var new: Node = Node(value: value, next: node, prev: node.prev) node.prev?.next = new node.prev = new count += 1 return node.prev! } public func node(at index: Int) -> Node? { guard index > -1 || index < count else { return nil } let startFromTail: Bool = index > count / 2 var currentNode: Node? = startFromTail ? tail : head var currentIndex: Int = startFromTail ? count - 1 : 0 var change: Int = startFromTail ? -1 : 1 while currentNode != nil { if currentIndex == index { break } currentNode = startFromTail ? currentNode!.prev : currentNode!.next currentIndex += change } return currentNode } @discardableResult public mutating func pop() -> Value? { defer { head = head?.next count -= 1 if isEmpty { tail = nil } else { head!.prev = nil } } return head?.value } @discardableResult public mutating func removeLast() -> Value? { defer { tail = tail?.prev count -= 1 if isEmpty { head = nil } else { tail!.next = nil } } return tail?.value } @discardableResult public mutating func remove(after node: Node) -> Value? { defer { if node.next != nil { count -= 1 } if node.next === tail { tail = node } if let next2node: Node = node.next?.next { next2node.prev = node } node.next = node.next?.next } return node.next?.value } @discardableResult public mutating func remove(before node: Node) -> Value? { defer { if node.prev != nil { count -= 1 } if node.prev === head { head = node } if let prev2node: Node = node.prev?.prev { prev2node.next = node } node.prev = node.prev?.prev } return node.prev?.value } } extension DoublyLinkedList: CustomStringConvertible { public var description: String { guard let head: Node = self.head else { return "Empty list" } return String(describing: head) } } // Here are testing scenarios to run in a Swift playground /* var list = DoublyLinkedList() list.push(4) list.push(2) list.push(1) list.append(6) var n = list.node(at: 2) list.insert(5, after: n!) list.insert(3, before: n!) print(list) print(list.pop()!) print(list.removeLast()!) print(list.remove(after: n!)!) print(list.remove(before: n!)!) print(list.count) */ ================================================ FILE: data_structures/heap/heap.swift ================================================ struct Heap { let compare: (Element, Element) -> Bool private var items : [Element] init(_ items : [Element], compare: @escaping (Element, Element) -> Bool) { self.compare = compare self.items = items for index in (0 ..< count / 2).reversed() { heapify(index) } } /// The minimum item on this heap or nil if the heap is empty var min: Element? { return items.first } /// The number of items on this heap var count: Int { return items.count } /// true if this heap is empty var isEmpty: Bool { return items.isEmpty } /// Removes and returns the minimum item from the heap. /// - returns: The minimum item from the heap or nil if the heap is empty. mutating func extractMin() -> Element? { guard let result = items.first else { return nil } items.removeFirst() heapify(0) return result } /// Inserts a new item into this heap /// - parameter item: The new item to insert mutating func insert(item : Element) { items.append(item) var i = items.count - 1 while i > 0 && compare(items[i], items[parent(i)]) { items.swapAt(i, parent(i)) i = parent(i) } } /// Restores the heap property starting at a given index /// - parameter index: The index to start at private mutating func heapify(_ index : Int) { var minimumIndex = index if left(index) < count && compare(items[left(index)], items[minimumIndex]) { minimumIndex = left(index) } if right(index) < count && compare(items[right(index)], items[minimumIndex]) { minimumIndex = right(index) } if minimumIndex != index { items.swapAt(minimumIndex, index) heapify(minimumIndex) } } /// Returns the index of the left child of an item private func left(_ index : Int) -> Int { return 2 * index + 1 } /// Returns the index of the right child of an item private func right(_ index: Int) -> Int { return 2 * index + 2 } /// Returns the index of the parent of an item private func parent(_ index: Int) -> Int { return (index - 1) / 2 } } extension Heap: ExpressibleByArrayLiteral where Element: Comparable { init(arrayLiteral elements: Element...) { self.init(elements, compare: <) } init(_ elements: [Element]) { self.init(elements, compare: <) } } ================================================ FILE: data_structures/queue/queue.swift ================================================ // Create simple queue // Tejas Nanaware struct Queue { private var elements: [T] = [] mutating func push(_ value: T) { elements.append(value) } mutating func pop() -> T? { guard !elements.isEmpty else { return nil } return elements.removeFirst() } } var queue = Queue() queue.push("One") queue.push("Two") queue.push("Three") print(queue.pop()) print(queue) print(queue.pop()) print(queue) print(queue.pop()) print(queue) ================================================ FILE: data_structures/union_find/union_find.swift ================================================ class UnionFindNode { var rank = 0 private var parent: UnionFindNode? = nil func findRoot() -> UnionFindNode { var x = self while let parent = x.parent { x.parent = parent.parent ?? parent x = parent } return x } @discardableResult static func union(_ x: UnionFindNode, _ y: UnionFindNode) -> UnionFindNode { var x = x.findRoot() var y = y.findRoot() guard x !== y else { return x } if x.rank < y.rank { swap(&x, &y) } y.parent = x if x.rank == y.rank { x.rank = y.rank + 1 } return x } static func inSameSet(_ x: UnionFindNode, _ y: UnionFindNode) -> Bool { return x.findRoot() === y.findRoot() } } func testUnionFind() { let a = UnionFindNode() let b = UnionFindNode() let c = UnionFindNode() print("a, b", UnionFindNode.inSameSet(a, b)) print("b, c", UnionFindNode.inSameSet(b, c)) print("a, c", UnionFindNode.inSameSet(a, c)) print("Joining a, b") UnionFindNode.union(a, b) print("a, b", UnionFindNode.inSameSet(a, b)) print("b, c", UnionFindNode.inSameSet(b, c)) print("a, c", UnionFindNode.inSameSet(a, c)) print("Joining b, c") UnionFindNode.union(b, c) print("a, b", UnionFindNode.inSameSet(a, b)) print("b, c", UnionFindNode.inSameSet(b, c)) print("a, c", UnionFindNode.inSameSet(a, c)) print("New node d") let d = UnionFindNode() print("a, d", UnionFindNode.inSameSet(a, d)) print("Joining d, c") UnionFindNode.union(d, c) print("a, d", UnionFindNode.inSameSet(a, d)) } ================================================ FILE: graph/BFS/BFS.swift ================================================ // MARK: - Basic requirement struct Edge { let from: Int let to: Int } public class Node { var val: Int var neighbors: [Int] public init(_ val: Int) { self.val = val self.neighbors = [] } } // MARK: - BFS implementation func testBFS(edges: [Edge]) { var graph = [Int: Node]() for edge in edges { graph[edge.from] = Node(edge.from) graph[edge.to] = Node(edge.to) } for edge in edges { graph[edge.from]?.neighbors.append(edge.to) graph[edge.to]?.neighbors.append(edge.from) } var visited: [Bool] = Array(repeating: false, count: graph.count + 1) var nodesOfCurrentLevel: [Int] = [] for node in 1...graph.count { if visited[node] == false { nodesOfCurrentLevel.append(node) while(nodesOfCurrentLevel.isEmpty == false) { var nodesOfNextLevel: [Int] = [] let sizeOfQueue = nodesOfCurrentLevel.count for index in 0.. { var from: G.Node var to: G.Node var value: G.EdgeValue } protocol Graph { typealias Edge = GraphEdge associatedtype Node: Equatable associatedtype EdgeValue func edges(from: Node) -> [Edge] } struct AdjacencyList: Graph { typealias EdgeValue = EdgeValue typealias Node = Node var graph: [Node: [Edge]] = [:] func edges(from node: Node) -> [Edge] { graph[node, default: []] } mutating func insert(from: Node, to: Node, value: EdgeValue) { graph[from, default: []].append(Edge(from: from, to: to, value: value)) } var allEdges: [Edge] { graph.values.flatMap { $0 } } } extension AdjacencyList where EdgeValue == () { mutating func insert(from: Node, to: Node) { insert(from: from, to: to, value: ()) } } extension Graph { func depthFirstSearch(start: Node, destination: Node) -> [Edge]? { if start == destination { return [] } for edge in edges(from: start) { if let path = depthFirstSearch(start: edge.to, destination: destination) { return [edge] + path } } return nil } } extension Graph where Node: Hashable { func breadthFirstSearch(start: Node, destination: Node) -> [Edge]? { var queue: [(Node, [Edge])] = [(start, [])] var visited: Set = [start] while !queue.isEmpty { let (current, path) = queue.removeFirst() if current == destination { return path } for edge in edges(from: current) where visited.insert(edge.to).inserted { queue.append((edge.to, path + [edge])) } } return nil } } extension GraphEdge: CustomDebugStringConvertible { var debugDescription: String { if type(of: value) == Void.self { return "\(from) -- \(to)" } else { return "\(from) -\(value)- \(to)" } } } var graph = AdjacencyList() graph.insert(from: "a", to: "b") graph.insert(from: "a", to: "d") graph.insert(from: "b", to: "c") graph.insert(from: "c", to: "d") func test(_ message: String, _ list: [GraphEdge]?) { print(message) if let list = list { for edge in list { print(edge) } } else { print("Not found") } print("") } test("Depth-first a -> d", graph.depthFirstSearch(start: "a", destination: "d")) test("Depth-first a -> e", graph.depthFirstSearch(start: "a", destination: "e")) test("Breadth-first a -> d", graph.breadthFirstSearch(start: "a", destination: "d")) test("Breadth-first a -> e", graph.breadthFirstSearch(start: "a", destination: "e")) ================================================ FILE: graph/spanning_tree/dijkstra.swift ================================================ class Node : CustomStringConvertible { // unique identifier required for each node var identifier : Int var distance : Int = Int.max var edges = [Edge]() var visited = false var description: String { var edgesString = String() edges.forEach{ edgesString.append($0.description)} return "{ Node, identifier: \(identifier.description) + Edges: \(edgesString) + }" } init(visited: Bool, identifier: Int, edges: [Edge]) { self.visited = visited self.identifier = identifier self.edges = edges } static func == (lhs: Node, rhs: Node) -> Bool { return lhs.identifier == rhs.identifier } } class Edge { var from: Node // does not actually need to be stored! var to: Node var weight: Int var description : String { return "{ Edge, from: \(from.identifier), to: \(to.identifier), weight: \(weight) }" } init(to: Node, from: Node, weight: Int) { self.to = to self.weight = weight self.from = from } } class Graph { var nodes: [Node] = [] } // Complete the quickestWayUp function below. func setupGraphwith(edges: [[Int]]) -> Graph { let graph = Graph() // create all the nodes // The first and last node need to be included, so need nodes from "to" and "from" let nodeNames = Set ( edges.map{ $0[0] } + edges.map{ $0[1]} ) for node in nodeNames { let newNode = Node(visited: false, identifier: node, edges: []) graph.nodes.append(newNode) } // create all the edges to link the nodes for edge in edges { if let fromNode = graph.nodes.first(where: { $0.identifier == edge[0] }) { if let toNode = graph.nodes.first(where: { $0.identifier == edge[1] }) { let forwardEdge = Edge(to: toNode, from: fromNode, weight: edge[2]) fromNode.edges.append(forwardEdge) } } } return graph } func shortestPath (source: Int, destination: Int, graph: Graph) -> Int { var currentNode = graph.nodes.first{ $0.identifier == source }! currentNode.visited = true currentNode.distance = 0 var toVisit = [Node]() toVisit.append(currentNode) while ( !toVisit.isEmpty) { toVisit = toVisit.filter{ $0.identifier != currentNode.identifier } currentNode.visited = true // Go to each adjacent vertex and update the path length for connectedEdge in currentNode.edges { let dist = currentNode.distance + connectedEdge.weight if (dist < connectedEdge.to.distance) { connectedEdge.to.distance = dist toVisit.append(connectedEdge.to) if (connectedEdge.to.visited == true) { connectedEdge.to.visited = false } } } currentNode.visited = true //set current node to the smallest vertex if !toVisit.isEmpty { currentNode = toVisit.min(by: { (a, b) -> Bool in return a.distance < b.distance })! } if (currentNode.identifier == destination) { return currentNode.distance } } return -1 } ================================================ FILE: graph/spanning_tree/kruskal.swift ================================================ enum Kruskal { struct Vertex { let name: String let node = UnionFindNode() init(_ name: String) { self.name = name } } struct Edge { let from: Vertex let to: Vertex let weight: Int } typealias Graph = [Edge] static func kruskal(_ graph: Graph) -> Graph { var edges = Heap(graph) { $0.weight < $1.weight } var result: Graph = [] result.reserveCapacity(edges.count) while let edge = edges.extractMin() { guard !UnionFindNode.inSameSet(edge.from.node, edge.to.node) else { continue } UnionFindNode.union(edge.from.node, edge.to.node) result.append(edge) } return result } } extension Kruskal.Vertex: CustomStringConvertible { var description: String { name } } extension Kruskal.Edge: CustomStringConvertible { var description: String { "\(from) --(\(weight))-- \(to)" } } func testKruskal() { typealias Vertex = Kruskal.Vertex typealias Edge = Kruskal.Edge let A = Vertex("A") let B = Vertex("B") let C = Vertex("C") let D = Vertex("D") let E = Vertex("E") let F = Vertex("F") let G = Vertex("G") let graph = [ Edge(from: A, to: B, weight: 7), Edge(from: A, to: D, weight: 5), Edge(from: B, to: C, weight: 8), Edge(from: B, to: D, weight: 9), Edge(from: B, to: E, weight: 7), Edge(from: C, to: E, weight: 5), Edge(from: D, to: E, weight: 15), Edge(from: D, to: F, weight: 6), Edge(from: E, to: F, weight: 8), Edge(from: E, to: G, weight: 9), Edge(from: F, to: G, weight: 11), ] print(Kruskal.kruskal(graph).map { String(describing: $0) }.joined(separator: "\n") ) } ================================================ FILE: recursion/fibonacci.swift ================================================ // The Fibonacci numbers, commonly denoted F(n) form a sequence, // called the Fibonacci sequence, such that # each number is the sum // of the two preceding ones, starting from 0 and 1. That is, // // F(0) = 0, F(1) = 1 // F(n) = F(n - 1) + F(n - 2), for n > 1 // // Given n, calculate F(n). // // @leticiafaleia func fibonacci(_ number: Int) -> Int { guard number > 1 else { return number } return fibonacci(number - 1) + fibonacci(number - 2) } fibonacci(5) ================================================ FILE: sorts/BubbleSort.swift ================================================ import Foundation extension Array where Element: Comparable { func bubbleSort(by areInIncreasingOrder: ((Element, Element) -> Bool) = (<)) -> [Element] { var data = self for i in 0..<(data.count-1) { for j in 0..<(data.count-i-1) where areInIncreasingOrder(data[j+1], data[j]) { data.swapAt(j, j + 1) } } return data } } func swap(left: inout T, right: inout T) { print("Swapping \(left) and \(right)") let temp = right right = left left = temp } // The code below can be used for testing // let numberList : Array = [8, 2, 10, 9, 7, 5] // let results: Array = numberList.bubbleSort() // print(results) ================================================ FILE: sorts/CocktailSort.swift ================================================ /* Cocktail Sort (or Cocktail shaker sort) is a variation of Bubble sort. The Bubble sort algorithm always traverses elements from left and moves the largest element to its correct position in first iteration and second largest in second iteration and so on. Cocktail Sort traverses through a given array in both directions alternatively. */ import Foundation func cocktailSort(_ a: [T]) -> [T] { var list = a var swapped = true var start = 0 var end = list.count - 1 while (swapped) { swapped = false for i in start.. list[i + 1]) { list.swapAt(i, i+1) swapped = true } } if (!swapped) { break } swapped = false end -= 1 for index in stride(from: end-1, through: start, by: -1) { if (list[index] > list[index + 1]) { list.swapAt(index, index+1) swapped = true } } start += 1 } return list } // The code below can be used for testing //var numbers = [2, -4, 4, 6, 1, 12, 9, 0] //numbers = cocktailSort(numbers) //print(numbers) ================================================ FILE: sorts/InsertionSort.swift ================================================ import Foundation func insertionSort(_ array: [T], by comparison: (T, T) -> Bool) -> [T] { guard array.count > 1 else { return array } var sortedArray = array for index in 1.. 0, comparison(temp, sortedArray[currentIndex - 1]) { sortedArray[currentIndex] = sortedArray[currentIndex - 1] currentIndex -= 1 } sortedArray[currentIndex] = temp } return sortedArray } // The code below can be used for testing /* let numbers = [10, 1, 3, 8, 4, 2] print(insertionSort(numbers, by: >)) print(insertionSort(numbers, by: <)) let names = ["Jack", "Paul", "Olivia", "Emma", "Michael"] print(insertionSort(names, by: >)) print(insertionSort(names, by: <)) */ ================================================ FILE: sorts/MergeSort.swift ================================================ import Foundation extension Array where Element: Comparable { mutating func mergeSort(by comparison: (Element, Element) -> Bool) { guard self.count > 1 else { return } _mergeSort(from: 0, to: count - 1, by: comparison) } mutating private func _mergeSort( from left: Int, to right: Int, by comparison: (Element, Element) -> Bool ) { if left < right { let mid = left + (right - left) / 2 _mergeSort(from: 0, to: mid, by: comparison) _mergeSort(from: mid + 1, to: right, by: comparison) _merge(from: left, mid: mid, to: right, by: comparison) } } mutating private func _merge( from left: Int, mid: Int, to right: Int, by comparison: (Element, Element) -> Bool ) { var copy = [Element](repeating: self[left], count: right - left + 1) var (leftStartIndex, rightStartIndex, currentIndex) = (left, mid + 1, 0) for _ in left ... right { if leftStartIndex > mid { copy[currentIndex] = self[rightStartIndex] rightStartIndex += 1 } else if rightStartIndex > right { copy[currentIndex] = self[leftStartIndex] leftStartIndex += 1 } else if comparison(self[leftStartIndex], self[rightStartIndex]) { copy[currentIndex] = self[leftStartIndex] leftStartIndex += 1 } else { copy[currentIndex] = self[rightStartIndex] rightStartIndex += 1 } currentIndex += 1 } leftStartIndex = left for i in copy.indices { self[leftStartIndex] = copy[i] leftStartIndex += 1 } } func mergeSorted(by comparison: (Element, Element) -> Bool) -> Array { var copy = self copy.mergeSort(by: comparison) return copy } } // The code below can be used for testing // var numberList = [15, 2, 23, 11, 3, 9] // debugPrint(numberList.mergeSorted(by: >)) // numberList.mergeSort(by: <) // debugPrint(numberList) ================================================ FILE: sorts/PancakeSort.swift ================================================ /* Pancake sorting is the mathematical problem of sorting a disordered stack of pancakes in order of size when a spatula can be inserted at any point in the stack and used to flip all pancakes above it. */ import Foundation func flip(array: [Int], key: Int) -> [Int] { var flippedArray = array var pos = key var start = 0 var aux = 0 while (start < pos) { aux = flippedArray[start] flippedArray[start] = flippedArray[pos] flippedArray[pos] = aux start += 1 pos -= 1 } return flippedArray } func pancakeSort(_ array: [Int]) -> [Int] { var list = array var currentSize = list.count for _ in (1 ..< currentSize).reversed() { let listToSearch = list[0...currentSize-1] let max = listToSearch.max() ?? 0 let indexOfMax = listToSearch.firstIndex(of: max) ?? 0 if indexOfMax != currentSize - 1 { list = flip(array: list, key: indexOfMax) list = flip(array: list, key: currentSize - 1) } currentSize -= 1 } return list } // The code below can be used for testing //var numbers = [2, 4, 6, 12, 3, -2, 9, 14, 22, 0, 18] //numbers = pancakeSort(numbers) //print(numbers) ================================================ FILE: sorts/QuickSort.swift ================================================ import Foundation extension Array where Element: Comparable { /// Sorts the array using the QuickSort algorithm in place. /// /// The QuickSort algorithm sorts the array by first choosing a pivot. This pivot is used to rearrange /// all elements, moving the smaller ones to the left of it. This operation is then recursevely applied /// to the subarrays formed around the pivot. mutating func quickSort() { guard count > 1 else { return } _quickSort(from: 0, to: count - 1) } mutating private func _quickSort(from left: Int, to right: Int) { guard left < right, right - left > 0 else { return } let pivotIndex = partition(from: left, to: right) _quickSort(from: left, to: pivotIndex - 1) _quickSort(from: pivotIndex + 1, to: right) } /// This method is where the pivot is chosen, so the smaller elements get moved to the left, /// and the bigger ones to the right. mutating private func partition(from left: Int, to right: Int) -> Int { /// Chooses the pivot, which in this case is always the first element, which is not very efficient. let pivotIndex = left swapAt(pivotIndex, right) let pivot = self[right] var i = left for j in i ..< right { // If the element is smaller than the pivot, move it to the left. if self[j] <= pivot { swapAt(i, j) i += 1 } } // Move the pivot to its right sorted position. swapAt(i, right) return i } /// Returns a sorted version of this array using the QuickSort algorithm. func quickSorted() -> Array { var copy = self copy.quickSort() return copy } } // Use the following code to test it: // var numbers = [1002, 42, 55, 124, 205] // debugPrint(numbers.quickSorted()) // // numbers.quickSort() // debugPrint(numbers) // // The console should print: // [42, 55, 124, 205, 1002] // [42, 55, 124, 205, 1002] ================================================ FILE: sorts/SelectionSort.swift ================================================ import Foundation extension Array where Element: Comparable { func selectionSort() -> Array { guard self.count > 1 else { return self } var output: Array = self for primaryindex in 0.. output[secondaryindex] { minimum = secondaryindex } secondaryindex += 1 } if primaryindex != minimum { output.swapAt(primaryindex, minimum) } } return output } } // The code below can be used for testing // let numberList : Array = [15, 2, 23, 11, 3, 9] // let results: Array = numberList.selectionSort() // print(results) ================================================ FILE: trees/tree.swift ================================================ import Foundation public class TreeNode { public var value: T public weak var parent: TreeNode? public var children = [TreeNode]() public init(value: T) { self.value = value } public func addChild(_ node: TreeNode) { children.append(node) node.parent = self } } /* Checks the node's value property, if there is no match, check the child nodes. Repeat the same process recursively */ extension TreeNode where T: Equatable { func search(_ value: T) -> TreeNode? { if value == self.value { return self } for child in children { if let found = child.search(value) { return found } } return nil } } // The code below can be used for testing let tree = TreeNode(value: "animals") let reptilesNode = TreeNode(value: "reptiles") let mammalsNode = TreeNode(value: "mammals") let lizardsNode = TreeNode(value: "lizards") let snakesNode = TreeNode(value: "snakes") let dogsNode = TreeNode(value: "dogs") let humansNode = TreeNode(value: "humans") tree.addChild(reptilesNode) tree.addChild(mammalsNode) reptilesNode.addChild(lizardsNode) reptilesNode.addChild(snakesNode) mammalsNode.addChild(dogsNode) mammalsNode.addChild(humansNode) print(tree.search("humans")?.value) print(tree.search("lizards")?.value) print(tree.search("dragons")?.value)