Repository: joalbright/Gameboard Branch: master Commit: 0c14a0a928f8 Files: 99 Total size: 478.1 KB Directory structure: gitextract_tij_wt8g/ ├── .gitignore ├── Gameboards.playground/ │ ├── Pages/ │ │ ├── Checkers.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Chess.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Go.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Mancala.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Minesweeper.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Sudoku.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ └── TicTacToe.xcplaygroundpage/ │ │ └── Contents.swift │ ├── Sources/ │ │ ├── Boards/ │ │ │ ├── Alquerque.swift │ │ │ ├── Backgammon.swift │ │ │ ├── Bombsweeper.swift │ │ │ ├── Checkers.swift │ │ │ ├── Chess.swift │ │ │ ├── Dots.swift │ │ │ ├── Doubles.swift │ │ │ ├── Four.swift │ │ │ ├── Go.swift │ │ │ ├── InProgress/ │ │ │ │ ├── Battleship.swift │ │ │ │ ├── Dominos.swift │ │ │ │ ├── Majong.swift │ │ │ │ ├── Pilare.swift │ │ │ │ ├── Tetris.swift │ │ │ │ └── Trouble.swift │ │ │ ├── Ludo.swift │ │ │ ├── Mancala.swift │ │ │ ├── Memory.swift │ │ │ ├── Pegs.swift │ │ │ ├── Sudoku.swift │ │ │ ├── TicTacToe.swift │ │ │ └── Words.swift │ │ ├── Core/ │ │ │ ├── Extensions.swift │ │ │ ├── Grid.swift │ │ │ ├── Operators.swift │ │ │ └── Views/ │ │ │ ├── BackgammonView.swift │ │ │ ├── DotsView.swift │ │ │ ├── FourView.swift │ │ │ ├── GoView.swift │ │ │ ├── MancalaView.swift │ │ │ ├── MatrixView.swift │ │ │ ├── PegsView.swift │ │ │ ├── SudokuView.swift │ │ │ ├── TicTacToeView.swift │ │ │ └── WordsView.swift │ │ ├── Gameboard.swift │ │ └── Validation.swift │ └── contents.xcplayground ├── Games/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Colors/ │ │ │ ├── Accent.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── Background.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Offest.colorset/ │ │ │ │ └── Contents.json │ │ │ └── Text.colorset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── BoardController/ │ │ ├── BoardViewController.swift │ │ ├── BoardViews/ │ │ │ ├── BackgammonBoardView.swift │ │ │ ├── BombsweeperBoardView.swift │ │ │ ├── CheckersBoardView.swift │ │ │ ├── ChessBoardView.swift │ │ │ ├── DotsBoardView.swift │ │ │ ├── DoublesBoardView.swift │ │ │ ├── FourBoardView.swift │ │ │ ├── GoBoardView.swift │ │ │ ├── MancalaBoardView.swift │ │ │ ├── MemoryBoardView.swift │ │ │ ├── PegsBoardView.swift │ │ │ ├── SudokuBoardView.swift │ │ │ ├── TicTacToeBoardView.swift │ │ │ └── WordsBoardView.swift │ │ └── Boards/ │ │ ├── Backgammon.storyboard │ │ ├── Bombsweeper.storyboard │ │ ├── Checkers.storyboard │ │ ├── Chess.storyboard │ │ ├── Dots.storyboard │ │ ├── Doubles.storyboard │ │ ├── Four.storyboard │ │ ├── Go.storyboard │ │ ├── Mancala.storyboard │ │ ├── Memory.storyboard │ │ ├── Pegs.storyboard │ │ ├── Sudoku.storyboard │ │ ├── TicTacToe.storyboard │ │ └── Words.storyboard │ ├── Info.plist │ └── MainViewController.swift ├── Games.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── README.md ├── RM_Checkers.md ├── RM_Chess.md ├── RM_Go.md ├── RM_Minesweeper.md ├── RM_Sudoku.md ├── RM_TicTacToe.md └── icon.ai ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## Build generated build/ DerivedData/ ## Various settings *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata/ ## Other *.moved-aside *.xccheckout *.xcscmblueprint ## Obj-C/Swift specific *.hmap *.ipa ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ .build/ .DS_Store ================================================ FILE: Gameboards.playground/Pages/Checkers.xcplaygroundpage/Contents.swift ================================================ import UIKit var checkers = Gameboard(.Checkers) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.66, green:0.62, blue:0.48, alpha:1) colors.foreground = UIColor(red:0.62, green:0.58, blue:0.44, alpha:1) colors.player1 = UIColor(red:0.8, green:0.13, blue:0, alpha:1) colors.player2 = UIColor(red:0.13, green:0.13, blue:0.13, alpha:1) colors.selected = UIColor.whiteColor() colors.highlight = UIColor.whiteColor() checkers.boardColors = colors // collection of moves let moves: [(Square,Square)] = [ ((2,1),(3,2)), // move ((5,2),(4,3)), // move ((2,3),(3,4)), // move ((4,3),(2,1)), // jump ((2,5),(4,3)), // cannot jump yourself ((2,5),(3,4)), // cannot land on your own piece ((1,0),(2,1)), // cannot land on another piece ] // loop moves for move in moves { do { try checkers.move(pieceAt: move.0, toSquare: move.1) } catch { print(error) } } checkers.showAvailable((1,2)) checkers.visualize() ================================================ FILE: Gameboards.playground/Pages/Chess.xcplaygroundpage/Contents.swift ================================================ import UIKit var chess = Gameboard(.Chess) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.52, green:0.68, blue:0.43, alpha:1) colors.foreground = UIColor(red:0.48, green:0.64, blue:0.39, alpha:1) colors.player1 = UIColor.whiteColor() colors.player2 = UIColor.blackColor() colors.selected = UIColor(red:0.06, green:0.46, blue:0.71, alpha:1) colors.highlight = UIColor(red:0.06, green:0.46, blue:0.71, alpha:1) chess.boardColors = colors // collection of moves let moves: [(ChessSquare,ChessSquare)] = [ (B7,B5), // pawn leaps (C2,C4), // pawn leaps (B5,C4), // pawn takes pawn (B1,C3), // knight charges (C8,A6), // bishop advances (E2,E4), // pawn leaps (G7,G6), // pawn creeps (F1,C4), // bishop take pawn (A6,D3), // blocked move throws error ] // loop moves for move in moves { do { try chess.move(pieceAt: move.0, toSquare: move.1) } catch { print(error) } } chess.showAvailable(A6) chess.visualize() ================================================ FILE: Gameboards.playground/Pages/Go.xcplaygroundpage/Contents.swift ================================================ import UIKit var go = Gameboard(.Go) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.36, green:0.29, blue:0.16, alpha:1) colors.foreground = UIColor(red:0.11, green:0.08, blue:0.03, alpha:1) colors.player1 = UIColor.whiteColor() colors.player2 = UIColor.blackColor() go.boardColors = colors // collection of guesses let moves: [Square] = [ // moves (1,1), (6,7), (1,7), (6,0), (4,4), (4,5), (1,2), ] // loop moves for move in moves { do { try go.move(toSquare: move) } catch { print(error) } } go.visualize() ================================================ FILE: Gameboards.playground/Pages/Mancala.xcplaygroundpage/Contents.swift ================================================ import UIKit ================================================ FILE: Gameboards.playground/Pages/Minesweeper.xcplaygroundpage/Contents.swift ================================================ import UIKit enum MoveType { case Guess, Mark } var minesweeper = Gameboard(.Minesweeper, testing: true) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.5, green:0.5, blue:0.5, alpha:1) colors.foreground = UIColor(red:0.6, green:0.6, blue:0.6, alpha:1) colors.player1 = UIColor.yellowColor() colors.player2 = UIColor.blackColor() colors.highlight = UIColor.blueColor() colors.selected = UIColor.redColor() minesweeper.boardColors = colors // collection of guesses let guesses: [(Square,MoveType)] = [ ((4,3),.Guess), // guess ((9,0),.Mark), // mark ((7,4),.Mark), // mark ((4,1),.Mark), // mark ((4,0),.Guess), // guess ((0,9),.Guess), // guess ((2,7),.Mark), // guess ((6,9),.Guess), // guess ((1,0),.Guess), // game over ] // loop guesses for guess in guesses { do { switch guess.1 { case .Guess: try minesweeper.guess(toSquare: guess.0) case .Mark: try minesweeper.mark(toSquare: guess.0) } } catch { print(error) } } minesweeper.visualize(CGRect(x: 0, y: 0, width: 199, height: 199)) ================================================ FILE: Gameboards.playground/Pages/Sudoku.xcplaygroundpage/Contents.swift ================================================ import UIKit var sudoku = Gameboard(.Sudoku, testing: true) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.19, green:0.78, blue:0.71, alpha:1) colors.foreground = UIColor(red:0.09, green:0.4, blue:0.44, alpha:1) sudoku.boardColors = colors // collection of guesses let guesses: [(Square,Guess)] = [ // guesses ((0,2),"5"), ((8,8),"4"), ((6,5),"4"), ((2,7),"4"), ((3,0),"4"), ((5,3),"1"), ((6,2),"1"), ((7,8),"1"), ((2,5),"2"), ] // loop guesses for guess in guesses { do { try sudoku.guess(toSquare: guess.0, withGuess: guess.1) } catch { print(error) } } sudoku.visualize(CGRect(x: 0, y: 0, width: 198, height: 198)) ================================================ FILE: Gameboards.playground/Pages/TicTacToe.xcplaygroundpage/Contents.swift ================================================ import UIKit var tictactoe = Gameboard(.TicTacToe) // setup colors var colors = BoardColors() colors.player1 = UIColor(red:0.43, green:0.98, blue:0.7, alpha:1) colors.player2 = UIColor(red:1, green:0.15, blue:0.18, alpha:1) tictactoe.boardColors = colors // collection of moves let moves: [Square] = [ (1,1), (0,0), (0,2), (2,0), (1,0), (1,2), (0,1), (1,1), // cant play filled spot (2,1), (2,2), // stalemate ] // loop moves for move in moves { do { try tictactoe.move(toSquare: move) } catch { print(error) } } tictactoe.visualize() ================================================ FILE: Gameboards.playground/Sources/Boards/Alquerque.swift ================================================ import UIKit public struct Alquerque { } ================================================ FILE: Gameboards.playground/Sources/Boards/Backgammon.swift ================================================ import UIKit public struct Backgammon { public static var board: Grid { return Grid([ "● ○ ○ ●".array(), "● ○ ○ ●".array(), "● ○ ○ ".array(), "● ○ ".array(), "● ○ ".array(), "○ ● ".array(), "○ ● ".array(), "○ ● ● ".array(), "○ ● ● ○".array(), "○ ● ● ○".array() ]) } public static let playerPieces = ["●","○"] } extension Grid { public func backgammon(_ rect: CGRect) -> UIView { let view = BackgammonView(frame: rect) let w = (rect.width - 50.0) / 12 let h = (rect.height - 110.0) / 10 view.backgroundColor = colors.background view.backgroundColor2 = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let y = r > 4 ? 95 : 15 let x = c > 5 ? 35 : 15 var piece = "\(item)" let label = UILabel(frame: CGRect(x: c * w + x, y: r * h + y, width: w, height: h)) label.textColor = player(piece) == 0 ? colors.player1 : colors.player2 if player(piece) == 1 { if let index = playerPieces[1].array().index(of: piece) { piece = playerPieces[0].array()[index] } } label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2, weight: .regular) view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Bombsweeper.swift ================================================ import UIKit extension Difficulty { var bombFlags: Int { switch self { case .easy: return 20 case .medium: return 20 case .hard: return 20 } } var bombs: Int { switch self { case .easy: return 10 case .medium: return 15 case .hard: return 20 } } var bombsize: Int { switch self { case .easy: return 10 case .medium: return 15 case .hard: return 20 } } } public struct Bombsweeper { public static var board: Grid { // randomize play area let grid = Grid(10 ✕ (10 ✕ EmptyPiece)) for (r,_) in grid.content.enumerated() { grid[r,4] = "•" } for (r,row) in grid.content.enumerated() { grid[r] = row.randomize().randomize().randomize() } return addBombCount(grid) } public static var staticboard: Grid { let grid = Grid([ " • ".array(), "• • ".array(), " • • • ".array(), " •".array(), " • ".array(), " • ".array(), " ".array(), " • ".array(), " • ".array(), "• •• ".array() ]) return addBombCount(grid) } public static var field: Grid { return Grid(10 ✕ (10 ✕ "•")) } public static let playerPieces = ["⚑","✘","⚐"] public static func validateGuess(_ s1: Square, _ grid: Grid, _ solution: Grid) throws { let a1 = solution[s1.0,s1.1] guard a1 != "⚑" else { throw MoveError.invalidmove } grid[s1.0,s1.1] = a1 guard a1 != "•" else { grid[s1.0,s1.1] = "✘"; throw GameStatus.gameover } guard a1 == EmptyPiece else { return } try checkAdjacent(s1, grid, solution) } public static func validateMark(_ s1: Square, _ grid: Grid, _ solution: Grid) throws { let g1 = grid[s1.0,s1.1] guard ["⚑","•"].contains(g1) else { throw MoveError.invalidmove } grid[s1.0,s1.1] = g1 == "•" ? "⚑" : "•" } public static func checkAdjacent(_ s1: Square, _ grid: Grid, _ solution: Grid) throws { let adjacent = [ (-1,-1),(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1) ] for a in adjacent { let s = (s1.0 + a.0, s1.1 + a.1) guard grid.onBoard(s) else { continue } let a1 = solution[s.0,s.1] guard a1 != grid[s.0,s.1] else { continue } grid[s.0,s.1] = a1 guard a1 == EmptyPiece else { continue } try checkAdjacent(s, grid, solution) } } public static func addBombCount(_ grid: Grid) -> Grid { for r in grid.rowRange { for c in grid.colRange { guard grid[r,c] != "•" else { continue } let bombs = bombCount((r,c), grid) grid[r,c] = bombs == 0 ? EmptyPiece : "\(bombs)" } } return grid } public static func bombCount(_ s1: Square, _ grid: Grid) -> Int { var count = 0 let adjacent = [ (-1,-1),(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1) ] for a in adjacent { let s = (s1.0 + a.0, s1.1 + a.1) guard grid.onBoard(s) else { continue } if grid[s.0,s.1] == "•" { count += 1 } } return count } } extension Grid { public func bomb(_ rect: CGRect) -> UIView { let view = UIView(frame: rect) view.backgroundColor = colors.background view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - content.count + 1) / content.count let h = (rect.height - content.count + 1) / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let piece = "\(item)" let holder = UIView(frame: CGRect(x: c * w + c, y: r * h + r, width: w, height: h)) holder.backgroundColor = player(piece) == 1 ? colors.selected : colors.background let label = UILabel(frame: CGRect(x: 0, y: 0, width: w, height: h)) label.text = player(piece) == 2 ? playerPieces[0] : piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 10, weight: .regular) label.textColor = [0,2].contains(player(piece)) ? colors.player1 : colors.player2 label.backgroundColor = .clear if piece == "•" { label.textColor = colors.foreground label.backgroundColor = colors.foreground } if let num = Int("\(item)"), num > 0 { label.textColor = colors.highlight } holder.addSubview(label) view.addSubview(holder) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Checkers.swift ================================================ import UIKit public struct Checkers { public enum PieceType: String { case none = " " case checker1 = "●" case checker2 = "○" case king1 = "◉" case king2 = "◎" } public static var board: Grid { return Grid([ 8 ✕ (EmptyPiece %% "●"), 8 ✕ ("●" %% EmptyPiece), 8 ✕ (EmptyPiece %% "●"), 8 ✕ EmptyPiece, 8 ✕ EmptyPiece, 8 ✕ ("○" %% EmptyPiece), 8 ✕ (EmptyPiece %% "○"), 8 ✕ ("○" %% EmptyPiece) ]) } public static let playerPieces = ["●◉","○◎"] public static func validateJump(_ s1: Square, _ s2: Square, _ p1: Piece, _ p2: Piece, _ grid: Grid, _ hint: Bool = false) -> Bool { let m1 = s2.0 - s1.0 let m2 = s2.1 - s1.1 let e1 = s1.0 + m1 / 2 let e2 = s1.1 + m2 / 2 guard let jumpedPieceType: PieceType = PieceType(rawValue: grid[e1,e2]), jumpedPieceType != .none else { return false } switch PieceType(rawValue: p1) ?? .none { case .checker1: guard m1 == 2 && abs(m2) == 2 else { return false } guard jumpedPieceType != .checker1, jumpedPieceType != .king1 else { return false } case .checker2: guard m1 == -2 && abs(m2) == 2 else { return false } guard jumpedPieceType != .checker2, jumpedPieceType != .king2 else { return false } case .king1: guard abs(m1) == 2 && abs(m2) == 2 else { return false } guard jumpedPieceType != .checker1, jumpedPieceType != .king1 else { return false } case .king2: guard abs(m1) == 2 && abs(m2) == 2 else { return false } guard jumpedPieceType != .checker2, jumpedPieceType != .king2 else { return false } case .none: return false } guard !hint else { return true } grid[e1,e2] = EmptyPiece // remove other player piece return true } public static func validateMove(_ s1: Square, _ s2: Square, _ p1: Piece, _ p2: Piece, _ grid: Grid, _ hint: Bool = false) throws -> Piece? { let m1 = s2.0 - s1.0 let m2 = s2.1 - s1.1 var kingPiece: PieceType? guard p2 == EmptyPiece else { throw MoveError.invalidmove } switch PieceType(rawValue: p1) ?? .none { case .checker1: guard (m1 == 1 && abs(m2) == 1) || validateJump(s1, s2, p1, p2, grid, hint) else { throw MoveError.invalidmove } if s2.c == grid.content.count - 1 { kingPiece = .king1 } case .checker2: guard (m1 == -1 && abs(m2) == 1) || validateJump(s1, s2, p1, p2, grid, hint) else { throw MoveError.invalidmove } if s2.c == 0 { kingPiece = .king2 } case .king1, .king2: guard (abs(m1) == 1 && abs(m2) == 1) || validateJump(s1, s2, p1, p2, grid, hint) else { throw MoveError.invalidmove } case .none: throw MoveError.incorrectpiece } guard !hint else { return nil } let piece = grid[s2.0,s2.1] grid[s2.0,s2.1] = kingPiece?.rawValue ?? p1 // place my piece in target square grid[s1.0,s1.1] = EmptyPiece // remove my piece from original square return piece } } extension Grid { public func checker(_ rect: CGRect, highlights: [Square], selected: Square?) -> UIView { let view = UIView(frame: rect) let w = rect.width / content.count let h = rect.height / content.count view.backgroundColor = colors.background view.layer.cornerRadius = 10 view.layer.masksToBounds = true for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { var piece = "\(item)" let holder = UIView(frame: CGRect(x: c * w, y: r * h, width: w, height: h)) holder.backgroundColor = (c + r) % 2 == 0 ? colors.background : colors.foreground let label = HintLabel(frame: CGRect(x: 0, y: 0, width: w, height: h)) label.backgroundColor = .clear label.textColor = player(piece) == 0 ? colors.player1 : colors.player2 label.highlightColor = colors.highlight if player(piece) == 1 { if let index = playerPieces[1].array().index(of: piece) { piece = playerPieces[0].array()[index] } } if let selected = selected, selected.0 == r && selected.1 == c { label.textColor = colors.selected } for highlight in highlights { label.highlight = label.highlight ? true : highlight.0 == r && highlight.1 == c } label.text = piece label.textAlignment = .center label.font = UIFont(name: "Apple Symbols", size: (w + h) / 2 - 10) holder.addSubview(label) view.addSubview(holder) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Chess.swift ================================================ import UIKit public struct Chess { public enum PieceType: String { case none = " " case rook1 = "♜" case knight1 = "♞" case bishop1 = "♝" case queen1 = "♛" case king1 = "♚" case pawn1 = "♟" case rook2 = "♖" case knight2 = "♘" case bishop2 = "♗" case queen2 = "♕" case king2 = "♔" case pawn2 = "♙" } public static var board: Grid { return Grid([ "♜♞♝♛♚♝♞♜".array(), 8 ✕ "♟", 8 ✕ EmptyPiece, 8 ✕ EmptyPiece, 8 ✕ EmptyPiece, 8 ✕ EmptyPiece, 8 ✕ "♙", "♖♘♗♕♔♗♘♖".array() ]) } public static let playerPieces = ["♜♞♝♛♚♝♞♜♟","♖♘♗♕♔♗♘♖♙"] public static func validateEmptyPath(_ s1: Square, _ s2: Square, _ grid: Grid) throws { let mRow = s2.0 - s1.0 let mCol = s2.1 - s1.1 let d1 = mRow == 0 ? 0 : mRow > 0 ? 1 : -1 let d2 = mCol == 0 ? 0 : mCol > 0 ? 1 : -1 var p1 = s1.0 + d1, p2 = s1.1 + d2 while p1 != s2.0 || p2 != s2.1 { guard grid[p1,p2] == EmptyPiece else { throw MoveError.blockedmove } p1 += d1 p2 += d2 } } public static func validateMove(_ s1: Square, _ s2: Square, _ p1: Piece, _ p2: Piece, _ grid: Grid, _ hint: Bool = false) throws -> Piece? { let mRow = s2.0 - s1.0 let mCol = s2.1 - s1.1 // let dR = mRow == 0 ? 0 : mRow > 0 ? 1 : -1 // let dC = mCol == 0 ? 0 : mCol > 0 ? 1 : -1 switch PieceType(rawValue: p1) ?? .none { case .bishop1, .bishop2: guard abs(mRow) == abs(mCol) else { throw MoveError.invalidmove } try validateEmptyPath(s1, s2, grid) case .king1, .king2: guard abs(mRow) < 2 && abs(mCol) < 2 else { throw MoveError.invalidmove } case .knight1, .knight2: guard (abs(mRow) == 2 && abs(mCol) == 1) || (abs(mRow) == 1 && abs(mCol) == 2) else { throw MoveError.invalidmove } case .pawn1: guard (abs(mCol) == 0 && p2 == EmptyPiece) || (abs(mCol) == 1 && mRow == 1 && p2 != EmptyPiece) else { throw MoveError.invalidmove } guard (mRow < 2 && mRow > 0) || (s1.0 == 1 && mRow == 2) else { throw MoveError.invalidmove } try validateEmptyPath(s1, s2, grid) case .pawn2: guard (abs(mCol) == 0 && p2 == EmptyPiece) || (abs(mCol) == 1 && mRow == -1 && p2 != EmptyPiece) else { throw MoveError.invalidmove } guard (mRow > -2 && mRow < 0) || (s1.0 == 6 && mRow == -2) else { throw MoveError.invalidmove } try validateEmptyPath(s1, s2, grid) case .queen1, .queen2: guard mRow == 0 || mCol == 0 || abs(mRow) == abs(mCol) else { throw MoveError.invalidmove } try validateEmptyPath(s1, s2, grid) case .rook1, .rook2: guard mRow == 0 || mCol == 0 else { throw MoveError.invalidmove } try validateEmptyPath(s1, s2, grid) case .none: throw MoveError.incorrectpiece } guard !hint else { return nil } let piece = grid[s2.0,s2.1] grid[s2.0,s2.1] = p1 // place my piece in target square grid[s1.0,s1.1] = EmptyPiece // remove my piece from original square return piece } } // Coordinates public let A8 = ("a",8), A7 = ("a",7), A6 = ("a",6), A5 = ("a",5), A4 = ("a",4), A3 = ("a",3), A2 = ("a",2), A1 = ("a",1) public let B8 = ("b",8), B7 = ("b",7), B6 = ("b",6), B5 = ("b",5), B4 = ("b",4), B3 = ("b",3), B2 = ("b",2), B1 = ("b",1) public let C8 = ("c",8), C7 = ("c",7), C6 = ("c",6), C5 = ("c",5), C4 = ("c",4), C3 = ("c",3), C2 = ("c",2), C1 = ("c",1) public let D8 = ("d",8), D7 = ("d",7), D6 = ("d",6), D5 = ("d",5), D4 = ("d",4), D3 = ("d",3), D2 = ("d",2), D1 = ("d",1) public let E8 = ("e",8), E7 = ("e",7), E6 = ("e",6), E5 = ("e",5), E4 = ("e",4), E3 = ("e",3), E2 = ("e",2), E1 = ("e",1) public let F8 = ("f",8), F7 = ("f",7), F6 = ("f",6), F5 = ("f",5), F4 = ("f",4), F3 = ("f",3), F2 = ("f",2), F1 = ("f",1) public let G8 = ("g",8), G7 = ("g",7), G6 = ("g",6), G5 = ("g",5), G4 = ("g",4), G3 = ("g",3), G2 = ("g",2), G1 = ("g",1) public let H8 = ("h",8), H7 = ("h",7), H6 = ("h",6), H5 = ("h",5), H4 = ("h",4), H3 = ("h",3), H2 = ("h",2), H1 = ("h",1) ================================================ FILE: Gameboards.playground/Sources/Boards/Dots.swift ================================================ import UIKit public struct Dots { public static var board: Grid { return Grid(17 ✕ (17 ✕ ("●" %% "0") %% 17 ✕ ("0" %% EmptyPiece))) } // public static var board: Grid { return Grid(8 ✕ (8 ✕ "00000")) } public static let playerPieces = ["1","2"] public static func validateMove(_ s1: Square, _ p1: Piece, _ grid: Grid, _ player: Int) throws { guard p1 == "0" else { throw MoveError.invalidmove } grid[s1.0,s1.1] = playerPieces[player] checkSpaces(s1, grid, player: player) } public static func checkSpaces(_ s1: Square, _ grid: Grid, player: Int) { let adjacent2 = [ (-1,0),(0,1),(1,0),(0,-1) ] for a in adjacent2 { let s = (s1.0 + a.0, s1.1 + a.1) guard grid.onBoard(s) else { continue } guard grid[s.0,s.1] == EmptyPiece, checkClosed(s, grid) else { continue } grid[s.0,s.1] = playerPieces[player] } } public static func checkClosed(_ s1: Square, _ grid: Grid) -> Bool { var count = 0 let adjacent2 = [ (-1,0),(0,1),(1,0),(0,-1) ] for a in adjacent2 { let s = (s1.0 + a.0, s1.1 + a.1) guard grid.onBoard(s) else { continue } if grid[s.0,s.1] != "0" { count += 1 } } return count == 4 } } extension Grid { public func dots(_ rect: CGRect) -> UIView { let view = DotsView(frame: rect) view.p = padding view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = rect.width / 17 let h = rect.height / 17 for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let player = "\(item)" guard ["1","2"].contains(player) else { continue } if (r + c) % 2 == 0 { /// Space let dotView = DotsSquareView(frame: CGRect(x: c * w - padding / 2, y: r * h - padding / 2, width: w, height: h).insetBy(dx: -5, dy: -5)) dotView.backgroundColor = .clear dotView.playerColor = player == "1" ? colors.player1 : colors.player2 view.addSubview(dotView) } else { /// Line // let (dx,dy) = sp[s] // // let width = dx == 0 ? w + 14 : 14 // let height = dy == 0 ? h + 14 : 14 // let x = c * w + padding + w / 2 + (dx * w / 2) // let y = r * h + padding + w / 2 + (dy * h / 2) // // let lineView = DotsLineView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // // lineView.backgroundColor = .clear // lineView.center = CGPoint(x: x, y: y) // lineView.playerColor = side == "1" ? colors.player1 : colors.player2 // lineView.lineColor = colors.foreground // // view.addSubview(lineView) } } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Doubles.swift ================================================ import UIKit public struct Doubles { public static var board: Grid { return Grid(4 ✕ (4 ✕ EmptyPiece)) } public static let playerPieces: [String] = [] public static var staticboard: Grid { return Grid([ " 2 ".array(), " ".array(), " 8".array(), [EmptyPiece,EmptyPiece,"16","128"], ]) } public static func validateMove(_ s1: Square, _ s2: Square, _ p1: Piece, _ p2: Piece, _ grid: Grid) throws -> Piece? { guard p1 != EmptyPiece && (p1 == p2 || p2 == EmptyPiece) else { throw MoveError.invalidmove } let double = "\((Int(p1) ?? 0) + (Int(p2) ?? 0))" grid[s2.0][s2.1] = p2 == EmptyPiece ? p1 : double grid[s1.0][s1.1] = EmptyPiece // remove initial piece return p1 == p2 && p1 != EmptyPiece ? double : nil } public static func random(_ grid: Grid) -> Bool { let c = Int(arc4random_uniform(4)) let r = Int(arc4random_uniform(4)) guard EmptyPiece == grid[c][r] else { return random(grid) } grid[c][r] = "2" return true } } extension String { var doublesColor: UIColor { switch self { case "2": return #colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1) case "4": return #colorLiteral(red: 0.5843137503, green: 0.8235294223, blue: 0.4196078479, alpha: 1) case "8": return #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1) case "16": return #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1) case "32": return #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1) case "64": return #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1) case "128": return #colorLiteral(red: 0.9568627477, green: 0.6588235497, blue: 0.5450980663, alpha: 1) case "256": return #colorLiteral(red: 0.9411764741, green: 0.4980392158, blue: 0.3529411852, alpha: 1) case "512": return #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1) case "1024": return #colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1) case "2048": return #colorLiteral(red: 0.8549019694, green: 0.250980407, blue: 0.4784313738, alpha: 1) case "4096": return #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1) default: return .clear } } } extension Grid { public func doubles(_ rect: CGRect) -> UIView { let view = UIView(frame: rect) view.backgroundColor = colors.background view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - padding * 2) / 4 let h = (rect.height - padding * 2) / 4 for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let piece = "\(item)" let holder = UIView(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h).insetBy(dx: 2, dy: 2)) holder.backgroundColor = piece == EmptyPiece ? colors.foreground : piece.doublesColor holder.layer.cornerRadius = 10 holder.layer.masksToBounds = true let label = UILabel(frame: CGRect(x: 0, y: 0, width: w - 4, height: h - 4)) label.backgroundColor = .clear label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 4 - 5, weight: .heavy) label.textColor = colors.player1 label.adjustsFontSizeToFitWidth = true holder.addSubview(label) view.addSubview(holder) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Four.swift ================================================ import UIKit public struct Four { public static var board: Grid { return Grid(6 ✕ (7 ✕ EmptyPiece)) } public static let playerPieces = ["◉","◎"] public static var staticboard: Grid { return Grid([ 7 ✕ EmptyPiece, 7 ✕ EmptyPiece, " ◎ ".array(), " ◉ ".array(), " ◎◉ ".array(), " ◎◉◉ ".array() ]) } public static func validateDrop(_ s1: Square, _ p1: Piece, _ grid: Grid) throws { guard grid[s1.0 + 1][s1.1] == EmptyPiece else { throw MoveError.invalidmove } grid[s1.0 + 1][s1.1] = p1 guard grid.onBoard(s1) else { return } grid[s1.0][s1.1] = EmptyPiece } } extension Grid { public func four(_ rect: CGRect) -> UIView { let view = FourView(frame: rect) let w = (rect.width - padding * 2) / 7 let h = (rect.height - padding * 2) / 7 view.backgroundColor = colors.foreground view.holeColor = colors.background view.spotColor = colors.selected view.p = padding view.layer.cornerRadius = 10 view.layer.masksToBounds = true for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { var piece = "\(item)" let label = UILabel(frame: CGRect(x: c * w + padding, y: r * h + padding + h, width: w, height: h)) label.textColor = player(piece) == 0 ? colors.player1 : colors.player2 if player(piece) == 1 { if let index = playerPieces[1].array().index(of: piece) { piece = playerPieces[0].array()[index] } } label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 10, weight: .regular) view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Go.swift ================================================ import UIKit enum GoError: Error { case openchain } public struct Go { public static var board: Grid { return Grid(9 ✕ (9 ✕ EmptyPiece)) } public static let playerPieces = ["●","○"] public static func checkCapture(_ s1: Square, _ p1: Piece, _ grid: Grid) { let points = [ (-1,0),(0,1),(1,0),(0,-1) ] func checkChain(_ s1: Square, _ chain: [Square]) throws -> [Square] { var chain = chain var squares = [s1] for p in points { let s = (s1.0 + p.0, s1.1 + p.1) guard !(chain.contains { $0.0 == s.0 && $0.1 == s.1 }) else { continue } guard grid.onBoard(s) else { continue } let a1 = grid[s.0,s.1] guard a1 != p1 else { continue } guard a1 != EmptyPiece else { throw GoError.openchain } chain.append(s) do { squares += try checkChain(s, chain) } catch { throw error } } return squares } for p in points { let s = (s1.0 + p.0, s1.1 + p.1) guard grid.onBoard(s) else { continue } let a1 = grid[s.0,s.1] guard a1 != EmptyPiece && a1 != p1 else { continue } if let squares = try? checkChain(s, [s]) { for s in squares { grid[s.0,s.1] = EmptyPiece } } } } public static func validateMove(_ s1: Square, _ p1: Piece, _ grid: Grid, _ player: Int) throws { guard p1 == EmptyPiece else { throw MoveError.invalidmove } grid[s1.0,s1.1] = playerPieces[player] checkCapture(s1, playerPieces[player], grid) } } extension Grid { public func go(_ rect: CGRect) -> UIView { let view = GoView(frame: rect) view.p = padding view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - padding * 2) / 8 let h = (rect.height - padding * 2) / 8 for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { var piece = "\(item)" let label = UILabel(frame: CGRect(x: c * w + padding - w / 2, y: r * h + padding - h / 2, width: w, height: h)) label.textColor = player(piece) == 0 ? colors.player1 : colors.player2 if player(piece) == 1 { if let index = playerPieces[1].array().index(of: piece) { piece = playerPieces[0].array()[index] } } label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2, weight: .thin) view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Battleship.swift ================================================ import UIKit public struct Battleship { public enum PieceType: String { case carrier case battleship case cruiser case destoyer var horizontal: String { switch self { case .carrier: return "◀︎◼︎◼︎◼︎▶︎" case .battleship: return "◀︎◼︎◼︎▶︎" case .cruiser: return "◀︎◼︎▶︎" case .destoyer: return "◀︎▶︎" } } var vertical: String { switch self { case .carrier: return "▲◼︎◼︎◼︎▼" case .battleship: return "▲◼︎◼︎▼" case .cruiser: return "▲◼︎▼" case .destoyer: return "▲▼" } } } public static var board: Grid { return Grid(6 ✕ (7 ✕ EmptyPiece)) } public static let playerPieces = ["",""] } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Dominos.swift ================================================ import UIKit public struct Dominos { public static var board: Grid { return Grid(6 ✕ (7 ✕ EmptyPiece)) } public static let playerPieces = ["🀰🀱🀲🀳🀴🀵🀶🀷🀸🀹🀺🀻🀼🀽🀾🀿🁀🁁🁂🁃🁄🁅🁆🁇🁈🁉🁊🁋🁌🁍🁎🁏🁐🁑🁒🁓🁔🁕🁖🁗🁘🁙🁚🁛🁜🁝🁞🁟🁠🁡🁢🁣🁤🁥🁦🁧🁨🁩🁪🁫🁬🁭🁮🁯🁰🁱🁲🁳🁴🁵🁶🁷🁸🁹🁺🁻🁼🁽🁾🁿🂀🂁🂂🂃🂄🂅🂆🂇🂈🂉🂊🂋🂌🂍🂎🂏🂐🂑🂒🂓"] } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Majong.swift ================================================ import UIKit public struct Majong { public static var board: Grid { return Grid(6 ✕ (7 ✕ EmptyPiece)) } public static let playerPieces = ["🀄︎🀀🀁🀂🀃🀅🀆🀇🀈🀉🀊🀋🀌🀍🀎🀏🀐🀑🀒🀓🀔🀕🀖🀗🀘🀙🀚🀛🀜🀝🀞🀟🀠🀡🀢🀣🀤🀥🀦🀧🀨🀩🀪🀫"] } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Pilare.swift ================================================ import UIKit public struct Pilare { public static var board: Grid { return Grid([ 6 ✕ "∙●", ["∙●","∙","∙","∙","∙","∙●"], ["∙●","∙","∙","∙","∙","∙●"], ["∙○","∙","∙","∙","∙","∙○"], ["∙○","∙","∙","∙","∙","∙○"], 6 ✕ "∙○" ]) } public static let playerPieces = ["●","○"] } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Tetris.swift ================================================ // // Tetris.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class Tetris: UIView { /* // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code } */ } ================================================ FILE: Gameboards.playground/Sources/Boards/InProgress/Trouble.swift ================================================ // // Trouble.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class Trouble: UIView { /* // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code } */ } ================================================ FILE: Gameboards.playground/Sources/Boards/Ludo.swift ================================================ import UIKit public struct Ludo { public static var board: Grid { return Grid([ 5 ✕ "●", 5 ✕ "●", "●● ○○".array(), 5 ✕ "○", 5 ✕ "○", ]) } public static let playerPieces = ["●","○"] } ================================================ FILE: Gameboards.playground/Sources/Boards/Mancala.swift ================================================ import UIKit public struct Mancala { public static var board: Grid { return Grid([ "333333".array(), "333333".array(), "000000".array(), "000000".array(), "333333".array(), "333333".array() ]) } public static let playerPieces = ["123456789","123456789"] public static var staticboard: Grid { return Grid([ "341433".array(), "345033".array(), "000000".array(), "000001".array(), "333444".array(), "330404".array() ]) } } extension Grid { public func mancala(_ rect: CGRect) -> UIView { let view = MancalaView(frame: rect) let w = (rect.width - padding * 2) / 6 let h = (rect.height - padding * 2) / 6 view.holeColor = colors.foreground view.backgroundColor = colors.background view.p = padding view.layer.cornerRadius = 20 view.layer.masksToBounds = true for (r,row) in content.enumerated() { guard r > 3 || r < 2 else { continue } for (c,item) in row.enumerated() { let label = MancalaSpotView(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h)) label.stones = Int("\(item)") ?? 0 label.textColor = colors.foreground label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 4 - 10, weight: .heavy) view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Memory.swift ================================================ import UIKit enum MemoryError: Error { case badmatch case nocard case samecard } extension Difficulty { var memoryDeck: String { switch self { case .easy: return "🂡🂭🂮🂱🂽🂾🃁🃍🃎🃑🃝🃞🃟" case .medium: return "🂡🂫🂬🂭🂮🂱🂻🂼🂽🂾🃁🃋🃌🃍🃎🃑🃛🃜🃝🃞🃟" case .hard: return "🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂬🂭🂮🂱🂲🂳🂴🂵🂶🂷🂸🂹🂺🂻🂼🂽🂾🃁🃂🃃🃄🃅🃆🃇🃈🃉🃊🃋🃌🃍🃎🃑🃒🃓🃔🃕🃖🃗🃘🃙🃚🃛🃜🃝🃞🃟" } } var memoryDeckPairs: [String] { return Array(memoryDeck.randomize().prefix(memoryCount.unique)) } var memoryDeckRandomized: [String] { return (memoryDeckPairs * memoryCount.pairs).randomize().randomize() } var memoryCount: (unique: Int, pairs: Int) { switch self { case .easy: return (4,4) case .medium: return (18,2) case .hard: return (32,2) } } var memoryBoard: Grid { switch self { case .easy: return Grid(4 ✕ (4 ✕ "🂠")) case .medium: return Grid(6 ✕ (6 ✕ "🂠")) case .hard: return Grid(8 ✕ (8 ✕ "🂠")) } } } extension String { var memoryColor: UIColor { switch self { case "🂡","🂢","🂣","🂤","🂥","🂦","🂧","🂨","🂩","🂪","🂫","🂬","🂭","🂮","🃑","🃒","🃓","🃔","🃕","🃖","🃗","🃘","🃙","🃚","🃛","🃜","🃝","🃞": return .darkText case "🂱","🂲","🂳","🂴","🂵","🂶","🂷","🂸","🂹","🂺","🂻","🂼","🂽","🂾","🃁","🃂","🃃","🃄","🃅","🃆","🃇","🃈","🃉","🃊","🃋","🃌","🃍","🃎": return .systemRed case "🃟": return .orange default: return .clear } } } public struct Memory { public static let playerPieces = ["🂠"] static func solution(_ difficulty: Difficulty) -> Grid { let grid = difficulty.memoryBoard let deck = difficulty.memoryDeckRandomized for r in grid.rowRange { for c in grid.colRange { grid[c,r] = deck[c+r*4] } } return grid } static func puzzle(_ difficulty: Difficulty) -> Grid { return difficulty.memoryBoard } public static func validateSelection(_ s1: Square, _ c1: Card, _ grid: Grid) throws { guard grid[s1.0,s1.1] != EmptyPiece else { throw MoveError.invalidmove } grid[s1.0,s1.1] = c1 } public static func validateMatch(_ s1: Square, _ s2: Square, _ c1: Card, _ c2: Card, _ grid: Grid, _ reset: Bool = false) throws -> Card? { if reset { let card = c1 == c2 ? EmptyPiece : "🂠" grid[s1.0][s1.1] = card grid[s2.0][s2.1] = card } else { guard grid[s1.0,s1.1] != EmptyPiece else { throw MemoryError.nocard } grid[s1.0,s1.1] = c1 } guard s1 != s2 else { throw MemoryError.samecard } guard c1 == c2 else { throw MemoryError.badmatch } return c1 } } extension Grid { public func memory(_ rect: CGRect) -> UIView { let view = UIView(frame: rect) view.backgroundColor = colors.background view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - content.count + 1) / content.count let h = (rect.height - content.count + 1) / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let piece = "\(item)" let label = UILabel(frame: CGRect(x: c * w + c, y: r * h + r, width: w, height: h)) label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 10, weight: .regular) label.textColor = player(piece) == 0 ? colors.player1 : piece.memoryColor view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Pegs.swift ================================================ // // Pegs.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class Pegs: NSObject { public static var board: Grid { return Grid([ "!!●●●!!".array(), "!!●●●!!".array(), "●●●●●●●".array(), "●●● ●●●".array(), "●●●●●●●".array(), "!!●●●!!".array(), "!!●●●!!".array() ]) } public static let playerPieces = ["●"] public static func validateMove(_ s1: Square, _ s2: Square, _ p1: Piece, _ p2: Piece, _ grid: Grid, _ hint: Bool = false) throws -> Piece? { let m1 = s2.0 - s1.0 let m2 = s2.1 - s1.1 guard p1 == "●" && p2 == EmptyPiece else { throw MoveError.invalidmove } guard (abs(m1) == 2 && abs(m2) == 0) || (abs(m1) == 0 && abs(m2) == 2) else { throw MoveError.invalidmove } let e1 = s1.0 + m1 / 2 let e2 = s1.1 + m2 / 2 let piece = grid[e1,e2] guard piece == "●" else { throw MoveError.invalidmove } guard !hint else { return nil } grid[s2.0,s2.1] = p1 grid[s1.0,s1.1] = EmptyPiece // remove initial piece grid[e1,e2] = EmptyPiece // remove jumped piece return piece } } extension Grid { public func pegs(_ rect: CGRect, highlights: [Square], selected: Square?) -> UIView { let view = PegsView(frame: rect) view.backgroundColor = colors.background view.p = padding view.color = colors.foreground view.lineColor = colors.player2 let w = (rect.width - padding * 2) / content.count let h = (rect.height - padding * 2) / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { guard "\(item)" != "!" else { continue } let label = HintLabel(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h)) label.text = "\(item)" label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 15, weight: .regular) label.textColor = colors.player1 label.highlightColor = colors.highlight if let selected = selected, selected.0 == r && selected.1 == c { label.textColor = colors.selected } for highlight in highlights { label.highlight = label.highlight ? true : highlight.0 == r && highlight.1 == c } view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Sudoku.swift ================================================ import UIKit extension Difficulty { var sudokuRange: CountableClosedRange { switch self { case .easy: return 2...6 case .medium: return 3...5 case .hard: return 4...4 } } } public struct Sudoku { public static var board: Grid { let grid = Grid([ "123456789".array(), "456789123".array(), "789123456".array(), "234567891".array(), "567891234".array(), "891234567".array(), "345678912".array(), "678912345".array(), "912345678".array() ]) return randomize(grid) } public static var staticboard: Grid { let grid = Grid([ "123456789".array(), "456789123".array(), "789123456".array(), "234567891".array(), "567891234".array(), "891234567".array(), "345678912".array(), "678912345".array(), "912345678".array() ]) return Grid([ grid[6], grid[8], grid[7], grid[1], grid[0], grid[2], grid[5], grid[3], grid[4] ]) } public static let playerPieces = ["123456789"] public static func validateGuess(_ s1: Square, _ g1: Guess, _ grid: Grid, _ solution: Grid) throws { guard g1 != EmptyPiece else { throw MoveError.invalidmove } guard g1 == solution[s1.0,s1.1] else { throw MoveError.incorrectguess } grid[s1.0,s1.1] = g1 } static func randomize(_ grid: Grid) -> Grid { var grid = grid for _ in 0...2 { let g1: [[String]] = [grid[0],grid[1],grid[2]].randomize() let g2: [[String]] = [grid[3],grid[4],grid[5]].randomize() let g3: [[String]] = [grid[6],grid[7],grid[8]].randomize() grid = Grid([ g1[0], g1[1], g1[2], g2[0], g2[1], g2[2], g3[0], g3[1], g3[2] ]) grid = flipGrid(grid) } return grid } static func flipGrid(_ grid: Grid) -> Grid { let newGrid = Grid(9 ✕ (9 ✕ EmptyPiece)) for r in newGrid.rowRange { for c in newGrid.colRange { newGrid[c,r] = grid[r,c] } } return newGrid } static func puzzle(_ grid: Grid, difficulty: Difficulty) -> Grid { let grid = Grid(grid.content) for r in 0...8 { var cols = [0,1,2,3,4,5,6,7,8].randomize() cols.removeSubrange(difficulty.sudokuRange) for c in cols { grid[r,c] = EmptyPiece } } return grid } static func staticpuzzle(_ grid: Grid) -> Grid { let grid = Grid(grid.content) let hide = [ [0,2,3,4,6,7], [0,1,3,6,7,8], [1,2,3,5,6,7], [0,1,2,4,5,8], [0,1,4,6,7,8], [2,3,4,5,7,8], [0,1,2,5,6,7], [0,3,4,5,6,8], [1,3,4,6,7,8] ] for (r,row) in hide.enumerated() { for c in row { grid[r,c] = EmptyPiece } } return grid } } extension Grid { public func sudoku(_ rect: CGRect, highlights: [Square]) -> UIView { let view = SudokuView(frame: rect) view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = rect.width / content.count let h = rect.height / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let holder = UIView(frame: CGRect(x: c * w, y: r * h, width: w, height: h)) let label = UILabel(frame: CGRect(x: 0, y: 0, width: w, height: h)) label.text = "\(item)" label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 15, weight: .regular) label.textColor = colors.foreground for highlight in highlights { guard highlight.0 == r && highlight.1 == c else { continue } label.textColor = colors.highlight holder.backgroundColor = colors.foreground } holder.addSubview(label) view.addSubview(holder) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/TicTacToe.swift ================================================ import UIKit public struct TicTacToe { public static var board: Grid { return Grid(3 ✕ (3 ✕ EmptyPiece)) } public static let playerPieces = ["○","✕"] public static func validateMove(_ s1: Square, _ p1: Piece, _ grid: Grid, _ player: Int) throws { guard p1 == EmptyPiece else { throw MoveError.invalidmove } grid[s1.0,s1.1] = playerPieces[player] // place my piece in target square } } extension Grid { public func ttt(_ rect: CGRect) -> UIView { let view = TicTacToeView(frame: rect) view.p = padding view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - padding * 2) / content.count let h = (rect.height - padding * 2) / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let piece = "\(item)" let label = UILabel(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h)) label.text = piece label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 10, weight: .thin) label.textColor = player(piece) == 0 ? colors.player1 : colors.player2 view.addSubview(label) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Boards/Words.swift ================================================ import UIKit public struct Words { public enum PieceType: String { static var all: [PieceType] { return [.start,.doubleword,.doubleletter,.tripleword,.tripleletter] } static var names: [String] { return all.map { $0.rawValue } } case empty = " " case start = "★" case doubleword = "DW" case tripleword = "TW" case doubleletter = "DL" case tripleletter = "TL" var backgroundColor: UIColor { switch self { case .empty: return .clear case .start: return #colorLiteral(red: 0.4582899487, green: 0.1780638536, blue: 0.4886863426, alpha: 1) case .doubleletter: return #colorLiteral(red: 0.0121835285, green: 0.524357516, blue: 0.8901960784, alpha: 1) case .tripleletter: return #colorLiteral(red: 0.1972305775, green: 0.7161869407, blue: 0.3783127069, alpha: 1) case .doubleword: return #colorLiteral(red: 0.8, green: 0, blue: 0.2666666667, alpha: 1) case .tripleword: return #colorLiteral(red: 0.968627451, green: 0.5960784314, blue: 0.2666666667, alpha: 1) } } var textColor: UIColor { return .white } } public enum Letter: String { static var all: [Letter] { return [.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z,.blank,.none] } static var bag: [Letter] { return all.reduce([]) { $0 + Array(repeating: $1, count: $1.count) }.randomize().randomize() } case a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z case blank = "_" case none = " " var count: Int { switch self { case .none: return 0 case .j, .k, .q, .x, .z: return 1 case .blank, .b, .c, .f, .m, .p, .v, .w, .y: return 2 case .g: return 3 case .h, .l, .u: return 4 case .d, .n, .s: return 5 case .r: return 6 case .t: return 7 case .i, .o: return 8 case .a: return 9 case .e: return 13 } } var point: Int { switch self { case .blank, .none: return 0 case .a, .e, .i, .o, .r, .s, .t: return 1 case .d, .l, .n, .u: return 2 case .g, .h, .y: return 3 case .b, .c, .f, .m, .p, .w: return 4 case .k, .v: return 5 case .x: return 8 case .j, .q, .z: return 10 } } } public static var board: Grid { let grid = Grid([ [" "," "," ","TW"," "," ","TL"," ","TL"," "," ","TW"," "," "," "], [" "," ","DL"," "," ","DW"," "," "," ","DW"," "," ","DL"," "," "], [" ","DL"," "," ","DL"," "," "," "," "," ","DL"," "," ","DL"," "], ["TW"," "," ","TL"," "," "," ","DW"," "," "," ","TL"," "," ","TW"], [" "," ","DL"," "," "," ","DL"," ","DL"," "," "," ","DL"," "," "], [" ","DW"," "," "," ","TL"," "," "," ","TL"," "," "," ","DW"," "], ["TL"," "," "," ","DL"," "," "," "," "," ","DL"," "," "," ","TL"], [" "," "," ","DW"," "," "," ","★"," "," "," ","DW"," "," "," "], ["TL"," "," "," ","DL"," "," "," "," "," ","DL"," "," "," ","TL"], [" ","DW"," "," "," ","TL"," "," "," ","TL"," "," "," ","DW"," "], [" "," ","DL"," "," "," ","DL"," ","DL"," "," "," ","DL"," "," "], ["TW"," "," ","TL"," "," "," ","DW"," "," "," ","TL"," "," ","TW"], [" ","DL"," "," ","DL"," "," "," "," "," ","DL"," "," ","DL"," "], [" "," ","DL"," "," ","DW"," "," "," ","DW"," "," ","DL"," "," "], [" "," "," ","TW"," "," ","TL"," ","TL"," "," ","TW"," "," "," "] ]) return grid } public static let playerPieces = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ_"] public static func validate(_ tile: Letter, _ s1: Square, _ grid: Grid) throws { guard PieceType(rawValue: grid[s1.0,s1.1]) != nil else { throw MoveError.invalidmove } grid[s1.0,s1.1] = tile.rawValue.uppercased() } } extension Grid { public func words(_ rect: CGRect) -> UIView { let view = WordsView(frame: rect) view.p = padding view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 6 view.layer.masksToBounds = true let w = (rect.width - padding * 2) / content.count let h = (rect.height - padding * 2) / content.count for (r,row) in content.enumerated() { for (c,item) in row.enumerated() { let piece = Words.PieceType(rawValue: "\(item)") let holder = UIView(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h).insetBy(dx: 1, dy: 1)) holder.backgroundColor = piece?.backgroundColor ?? colors.player2 holder.layer.cornerRadius = 4 holder.layer.masksToBounds = true let label = UILabel(frame: CGRect(x: 0, y: 0, width: w - 2, height: h - 2)) label.text = "\(item)" label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / ("\(item)".count > 1 ? 3 : 2) - 5, weight: .black) label.textColor = piece?.textColor ?? colors.player1 label.backgroundColor = .clear holder.addSubview(label) view.addSubview(holder) } } return view } } ================================================ FILE: Gameboards.playground/Sources/Core/Extensions.swift ================================================ import UIKit public typealias Square = (c: Int, r: Int) public typealias ChessSquare = (c: String, r: Int) public typealias Piece = String public typealias Guess = String public typealias Card = String extension String { public func array() -> [String] { return map { "\($0)" } } public func randomize() -> [String] { return array().sorted { _,_ in arc4random() % 2 == 0 } } } extension Int { public func within(_ r: CountableRange) -> Bool { return self >= r.lowerBound && self < r.upperBound } public func set(_ advanced: Int, _ count: Int) -> [Int] { var set: [Int] = [] for i in 0.. [Element] { return sorted { _,_ in arc4random() % 2 == 0 } } static func *(lhs: [Element], rhs: Int) -> [Element] { var multipliedArray = lhs for _ in 1.. { return 0.. { return content.count > 0 ? 0.. String { get { return content[c][r] } set { content[c][r] = newValue } } public subscript(c: Int) -> [String] { get { return content[c] } set { content[c] = newValue } } public subscript(c: Int) -> String { get { return content[c / colRange.endIndex][c % colRange.endIndex] } set { content[c / colRange.endIndex][c % colRange.endIndex] = newValue } } public func matrix(_ rect: CGRect) -> UIView { let view = MatrixView(frame: rect) view.p = padding view.backgroundColor = colors.background view.lineColor = colors.foreground view.layer.cornerRadius = 10 view.layer.masksToBounds = true let w = (rect.width - padding * 2) / content.count let h = (rect.height - padding * 2) / content.count for (c,col) in content.enumerated() { for (r,item) in col.enumerated() { let label = UILabel(frame: CGRect(x: c * w + padding, y: r * h + padding, width: w, height: h)) label.text = "\(item)" label.textAlignment = .center label.font = .systemFont(ofSize: (w + h) / 2 - 10, weight: .thin) view.addSubview(label) } } return view } public func onBoard(_ s1: Square, _ s2: Square) -> Bool { return s1.0.within(rowRange) && s1.1.within(colRange) && s2.0.within(rowRange) && s2.1.within(colRange) } public func onBoard(_ s1: Square) -> Bool { return s1.0.within(rowRange) && s1.1.within(colRange) } func player(_ piece: Piece) -> Int { for (p,player) in playerPieces.enumerated() { if player.contains(piece) { return p } } return -1 } public func piecesOnBoard() -> [Piece] { return content.reduce([]) { $0 + $1 }.filter { $0 != EmptyPiece } } } class HintLabel: UILabel { var highlight: Bool = false { didSet { setNeedsDisplay() } } var highlightColor: UIColor = .systemRed override func drawText(in rect: CGRect) { guard highlight else { return super.drawText(in: rect) } let c = UIGraphicsGetCurrentContext() highlightColor.set() c?.setLineJoin(.round) c?.setLineWidth(1) c?.stroke(rect.insetBy(dx: 3, dy: 3)) super.drawText(in: rect) } } ================================================ FILE: Gameboards.playground/Sources/Core/Operators.swift ================================================ import UIKit public func * (lhs: CGFloat, rhs: Int) -> CGFloat { return lhs * CGFloat(rhs) } public func * (lhs: Int, rhs: CGFloat) -> CGFloat { return CGFloat(lhs) * rhs } public func / (lhs: CGFloat, rhs: Int) -> CGFloat { return lhs / CGFloat(rhs) } public func / (lhs: Int, rhs: CGFloat) -> CGFloat { return CGFloat(lhs) / rhs } public func + (lhs: CGFloat, rhs: Int) -> CGFloat { return lhs + CGFloat(rhs) } public func + (lhs: Int, rhs: CGFloat) -> CGFloat { return CGFloat(lhs) + rhs } public func - (lhs: CGFloat, rhs: Int) -> CGFloat { return lhs - CGFloat(rhs) } public func - (lhs: Int, rhs: CGFloat) -> CGFloat { return CGFloat(lhs) - rhs } // MATRIX infix operator ✕: AdditionPrecedence public func ✕ (lhs: Int, rhs: (Int) -> String) -> [String] { var a: [String] = [] for i in 0.. [String]) -> [[String]] { var a: [[String]] = [] for i in 0.. [String] { return [String](repeating: rhs, count: lhs) } public func ✕ (lhs: Int, rhs: [String]) -> [[String]] { return [[String]](repeating: rhs, count: lhs) } infix operator %% : AssignmentPrecedence public func %% (lhs: String, rhs: String) -> (Int) -> String { return { $0 % 2 == 0 ? lhs : rhs } } public func %% (lhs: [String], rhs: [String]) -> (Int) -> [String] { return { $0 % 2 == 0 ? lhs : rhs } } ================================================ FILE: Gameboards.playground/Sources/Core/Views/BackgammonView.swift ================================================ // // BackgammonView.swift // Games // // Created by Jo Albright on 4/20/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class BackgammonView: UIView { public var p: CGFloat = 15 public var backgroundColor2: UIColor = .black public override func draw(_ rect: CGRect) { let lineColor1 = backgroundColor2.withAlphaComponent(0.3) let lineColor2 = backgroundColor2.withAlphaComponent(0.7) let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) let vG: CGFloat = 20 let hG: CGFloat = 50 backgroundColor2.set() context?.addPath(UIBezierPath(roundedRect: rect.insetBy(dx: 15, dy: 15), cornerRadius: 0).cgPath) context?.fillPath() backgroundColor?.set() context?.addRect(CGRect(x: rect.midX - vG / 2, y: 0, width: vG, height: rect.height)) context?.fillPath() context?.setBlendMode(.multiply) let w = (rect.width - p * 2 - vG) / 12 let h = (rect.height - p * 2 - hG) / 10 for c in 0..<12 { let x = c > 5 ? 35 : 15 (c % 2 == 0 ? lineColor1 : lineColor2).set() context?.move(to: CGPoint(x: w * c + x, y: 15)) context?.addLine(to: CGPoint(x: w * c + x + w, y: 15)) context?.addLine(to: CGPoint(x: w * c + x + w / 2, y: h * 5 + 15)) context?.closePath() context?.fillPath() (c % 2 == 1 ? lineColor1 : lineColor2).set() context?.move(to: CGPoint(x: w * c + x, y: rect.height - 15)) context?.addLine(to: CGPoint(x: w * c + x + w, y: rect.height - 15)) context?.addLine(to: CGPoint(x: w * c + x + w / 2, y: h * 5 + 65)) context?.closePath() context?.fillPath() } } } ================================================ FILE: Gameboards.playground/Sources/Core/Views/DotsView.swift ================================================ // // DotsView.swift // Games // // Created by Jo Albright on 4/22/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class DotsView: UIView { public var p: CGFloat = 10 public var lineColor: UIColor = .black public override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) context?.setLineWidth(6) lineColor.set() let w = (rect.width - p * 2) / 8 let h = (rect.height - p * 2) / 8 for r in 0...8 { for c in 0...8 { context?.move(to: CGPoint(x: w * c + p, y: h * r + p)) context?.addLine(to: CGPoint(x: w * c + p, y: h * r + p)) context?.strokePath() } } } } class DotsLineView: UIView { public var playerColor: UIColor = .white public var lineColor: UIColor = .black override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) context?.setLineWidth(1) lineColor.set() let h: Bool = rect.width > rect.height let p: CGFloat = 2 let d: CGFloat = h ? rect.height - p * 2 : rect.width - p * 2 context?.strokeEllipse(in: CGRect(x: p, y: p, width: d, height: d)) context?.strokeEllipse(in: CGRect(x: rect.width - (d + p), y: rect.height - (d + p), width: d, height: d)) let x = h ? d + 7 : d / 2 + p let y = h ? d / 2 + p : d + 7 context?.setLineWidth(2) context?.setLineDash(phase: 0, lengths: [0,4]) context?.move(to: CGPoint(x: x, y: y)) context?.addLine(to: CGPoint(x: rect.width - x, y: rect.height - y)) context?.strokePath() } } class DotsSquareView: UIView { public var playerColor: UIColor = .white override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() playerColor.set() context?.fillEllipse(in: rect.insetBy(dx: 5, dy: 5)) } } ================================================ FILE: Gameboards.playground/Sources/Core/Views/FourView.swift ================================================ // // FourView.swift // Games // // Created by Jo Albright on 4/21/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class FourView: UIView { public var p: CGFloat = 15 public var holeColor: UIColor = .white public var spotColor: UIColor = UIColor(white: 0.90, alpha: 1) public override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) context?.setLineWidth(2) holeColor.set() let w = (rect.width - p * 2) / 7 let h = (rect.height - p * 2) / 7 context?.fill(CGRect(x: 0, y: 0, width: rect.width, height: h + 10)) backgroundColor?.set() context?.addPath(UIBezierPath(roundedRect: CGRect(x: 0, y: h, width: rect.width, height: 20), cornerRadius: 10).cgPath) context?.fillPath() for c in 0..<7 { holeColor.set() context?.addPath(UIBezierPath(roundedRect: CGRect(x: w * c + p + 5, y: 10, width: w - 10, height: h), cornerRadius: 10).cgPath) context?.fillPath() for r in 0..<6 { context?.fillEllipse(in: CGRect(x: w * c + p, y: h * r + p + h, width: w, height: h).insetBy(dx: 5, dy: 5)) } spotColor.set() context?.strokeEllipse(in: CGRect(x: w * c + p, y: 0, width: w, height: h).insetBy(dx: 10, dy: 10)) context?.fillEllipse(in: CGRect(x: w * c + p, y: 0, width: w, height: h).insetBy(dx: 15, dy: 15)) } } } ================================================ FILE: Gameboards.playground/Sources/Core/Views/GoView.swift ================================================ import UIKit public class GoView: UIView { public var p: CGFloat = 20 public var lineColor: UIColor = .black public override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) lineColor.set() let w = (rect.width - p * 2) / 8 let h = (rect.height - p * 2) / 8 for r in 0...8 { for c in 0...8 { context?.move(to: CGPoint(x: w * c + p, y: p)) context?.addLine(to: CGPoint(x: w * c + p, y: rect.height - p)) context?.move(to: CGPoint(x: p, y: h * r + p)) context?.addLine(to: CGPoint(x: rect.width - p, y: h * r + p)) } } context?.strokePath() } } ================================================ FILE: Gameboards.playground/Sources/Core/Views/MancalaView.swift ================================================ // // MancalaView.swift // Games // // Created by Jo Albright on 4/22/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class MancalaView: UIView { public var p: CGFloat = 15 public var holeColor: UIColor = .white public var spotColor: UIColor = UIColor(white: 0.90, alpha: 1) public override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setLineCap(.round) context?.setLineJoin(.round) context?.setLineWidth(4) let w = (rect.width - p * 2) / 6 let h = (rect.height - p * 2) / 6 holeColor.set() for c in 0..<6 { if c % 2 == 1 { context?.move(to: CGPoint(x: w * c + p, y: h / 2 + p)) context?.addLine(to: CGPoint(x: w * c + p, y: h / 2 + p)) context?.strokePath() } context?.fillEllipse(in: CGRect(x: w * c + p, y: p, width: w, height: h).insetBy(dx: 5, dy: 5)) if c % 2 == 0, c > 0 { context?.move(to: CGPoint(x: w * c + p, y: h + h / 2 + p)) context?.addLine(to: CGPoint(x: w * c + p, y: h + h / 2 + p)) context?.strokePath() } context?.fillEllipse(in: CGRect(x: w * c + p, y: h + p, width: w, height: h).insetBy(dx: 5, dy: 5)) context?.move(to: CGPoint(x: w * c + w / 2 + p, y: h + p)) context?.addLine(to: CGPoint(x: w * c + w / 2 + p, y: h + p)) context?.strokePath() if c == 0 || c == 5 { context?.move(to: CGPoint(x: w * c + w / 2 + p, y: h * 2 + p)) context?.addLine(to: CGPoint(x: w * c + w / 2 + p, y: h * 2 + p)) context?.strokePath() context?.move(to: CGPoint(x: w * c + w / 2 + p, y: rect.height - (h * 2 + p))) context?.addLine(to: CGPoint(x: w * c + w / 2 + p, y: rect.height - (h * 2 + p))) context?.strokePath() } context?.move(to: CGPoint(x: w * c + w / 2 + p, y: rect.height - (h + p))) context?.addLine(to: CGPoint(x: w * c + w / 2 + p, y: rect.height - (h + p))) context?.strokePath() context?.fillEllipse(in: CGRect(x: w * c + p, y: rect.height - (h * 2 + p), width: w, height: h).insetBy(dx: 5, dy: 5)) if c % 2 == 0, c > 0 { context?.move(to: CGPoint(x: w * c + p, y: rect.height - (h + h / 2 + p))) context?.addLine(to: CGPoint(x: w * c + p, y: rect.height - (h + h / 2 + p))) context?.strokePath() } context?.fillEllipse(in: CGRect(x: w * c + p, y: rect.height - (h + p), width: w, height: h).insetBy(dx: 5, dy: 5)) if c % 2 == 1 { context?.move(to: CGPoint(x: w * c + p, y: rect.height - (h / 2 + p))) context?.addLine(to: CGPoint(x: w * c + p, y: rect.height - (h / 2 + p))) context?.strokePath() } } context?.addPath(UIBezierPath(roundedRect: CGRect(x: p, y: h * 2 + p, width: w * 2, height: h * 2).insetBy(dx: 5, dy: 5), cornerRadius: w / 4).cgPath) context?.addPath(UIBezierPath(roundedRect: CGRect(x: rect.width - (w * 2 + p), y: h * 2 + p, width: w * 2, height: h * 2).insetBy(dx: 5, dy: 5), cornerRadius: w / 4).cgPath) context?.fillPath() } } class MancalaSpotView: UILabel { var stoneColor: UIColor = .black var stones: Int = 0 { didSet { setNeedsDisplay() // text = stones == 0 ? EmptyPiece : "\(stones)" } } override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() let d: CGFloat = rect.width / 4 let r: CGFloat = stones > 4 ? d / 2 : (CGFloat(stones) / 4) * (d / 2) let s: CGFloat = 360 / CGFloat(stones) var a: CGFloat = 0 stoneColor.withAlphaComponent(0.4).set() context?.setLineCap(.round) context?.setLineWidth(d) context?.setBlendMode(.multiply) for _ in 0.. Void = { _ in } override func draw(_ rect: CGRect) { super.draw(rect) letterLabel?.frame = rect valueLabel?.frame = CGRect(x: 0, y: 4, width: frame.width - 4, height: 10) let context = UIGraphicsGetCurrentContext() context?.clear(rect) guard tile != .none else { return } color.set() context?.addPath(UIBezierPath(roundedRect: rect.insetBy(dx: 1, dy: 1), cornerRadius: 6).cgPath) context?.fillPath() } override func touchesEnded(_ touches: Set, with event: UIEvent?) { guard tile != .none else { return } selected(tile) color = selectedColor } } ================================================ FILE: Gameboards.playground/Sources/Gameboard.swift ================================================ import UIKit enum Difficulty { case easy, medium, hard var name: String { switch self { case .easy: return "Easy" case .medium: return "Medium" case .hard: return "Hard" } } init(_ i: Int) { switch i { case 1: self = .medium case 2: self = .hard default: self = .easy } } } public struct Gameboard { public enum BoardType: String { case backgammon, bombsweeper, checkers, chess, dots, doubles, four, go, mancala, memory, pegs, sudoku, tictactoe, words static var playable: [BoardType] = [ .backgammon, .bombsweeper, .checkers, .chess, .dots, .doubles, .four, .go, .memory, .pegs, .sudoku, .tictactoe, .words ] public var name: String { switch self { case .backgammon: return "Backgammon" case .bombsweeper: return "Bombsweeper" case .checkers: return "Checkers" case .chess: return "Chess" case .dots: return "Dots" case .doubles: return "Doubles" case .four: return "Four" case .go: return "Go" case .mancala: return "Mancala" case .memory: return "Memory" case .pegs: return "Pegs" case .sudoku: return "Sudoku" case .tictactoe: return "Tic Tac Toe" case .words: return "Words" } } public var emblem: String { switch self { case .backgammon: return "⚄" case .bombsweeper: return "⚑" case .checkers: return "●" case .chess: return "♞" case .dots: return "⦿" case .doubles: return "⚭" case .four: return "◉" case .go: return "●" case .mancala: return "✾" case .memory: return "🂠" case .pegs: return "✜" case .sudoku: return "9" case .tictactoe: return "⌗" case .words: return "☐" } } var controller: UINavigationController? { guard let vc = UIStoryboard(name: name.replacingOccurrences(of: " ", with: ""), bundle: nil).instantiateInitialViewController() as? BoardViewController else { return nil } return UINavigationController(rootViewController: vc) } } public var padding: CGFloat = 0 { didSet { grid.padding = padding } } public var colors = BoardColors() { didSet { grid.colors = colors } } var _type: BoardType var playerCount: Int = 2 var playerTurn: Int = 0 { didSet { playerChange?(playerTurn + 1) } } var playerPieces: [Piece] = [] { didSet { grid.playerPieces = playerPieces playerCount = playerPieces.count } } var grid: Grid = Grid(1 ✕ (1 ✕ EmptyPiece)) var solution: Grid = Grid(1 ✕ (1 ✕ EmptyPiece)) var gridSize: Int { return grid.content.count } var totalSpaces: Int { return grid.content.count == 0 ? 0 : grid.content.count * grid.content[0].count } var difficulty: Difficulty = .easy { didSet { reset() } } var _size: Int? public var playerChange: ((Int) -> Void)? public var showAlert: ((String, String) -> Void)? public init(_ type: BoardType) { _type = type reset() } public init(_ type: BoardType, testing: Bool) { _type = type reset(testing) } public init(_ type: BoardType, size: Int) { _type = type _size = size reset() } mutating func changePlayer() { playerTurn = playerTurn < playerCount - 1 ? playerTurn + 1 : 0 } public mutating func showAvailable(_ s1: Square) { highlights = [] switch _type { case .chess, .checkers, .pegs: selected = nil for r in grid.rowRange { for c in grid.colRange { guard let _ = try? validateMove(s1, (r,c), true) else { continue } selected = s1 highlights.append((r,c)) } } default: break } } public mutating func showAvailable(_ s1: ChessSquare) { let cols: [String] = "abcdefgh".map { "\($0)" } guard let c1 = cols.firstIndex(of: s1.0) else { return } let r1 = 8 - s1.1 showAvailable((r1,c1)) } public mutating func drop(pieceAt s1: Square) throws { try validateDrop(s1) } public mutating func place(tile t1: Words.Letter, at s1: Square) throws { try validate(t1, s1) } public mutating func guess(toSquare s1: Square) throws { try validateGuess(s1) } public mutating func guess(toSquare s1: Square, withGuess g1: Guess) throws { try validateGuess(s1, g1) } public mutating func mark(toSquare s1: Square) throws { try validateMark(s1) } public mutating func move(toSquare s1: Square) throws { try validateMove(s1) changePlayer() } public mutating func move(pieceAt s1: Square, toSquare s2: Square) throws -> Piece? { let piece = try validateMove(s1,s2) changePlayer() return piece } public mutating func move(pieceAt s1: ChessSquare, toSquare s2: ChessSquare) throws -> Piece? { let cols = "abcdefgh".array() guard let c1 = cols.firstIndex(of: s1.0), let c2 = cols.firstIndex(of: s2.0) else { return nil } let r1 = 8 - s1.1, r2 = 8 - s2.1 let piece = try validateMove((r1,c1), (r2,c2)) changePlayer() return piece } public mutating func select(cardAt s1: Square) throws { return try validateSelection(s1) } public mutating func match(cardAt s1: Square, withCard s2: Square, reset: Bool = false) throws -> Card? { return try validateMatch(s1, s2, reset) } public mutating func reset(_ testing: Bool = false) { highlights = [] selected = nil switch _type { case .backgammon: grid = Backgammon.board playerPieces = Backgammon.playerPieces case .bombsweeper: solution = Bombsweeper.board grid = Bombsweeper.field playerPieces = Bombsweeper.playerPieces guard testing else { break } solution = Bombsweeper.staticboard playerPieces = Bombsweeper.playerPieces case .checkers: grid = Checkers.board playerPieces = Checkers.playerPieces case .chess: grid = Chess.board playerPieces = Chess.playerPieces case .dots: grid = Dots.board playerPieces = Dots.playerPieces case .doubles: grid = Doubles.board playerPieces = Doubles.playerPieces let _ = Doubles.random(grid) let _ = Doubles.random(grid) guard testing else { break } grid = Doubles.staticboard playerPieces = Doubles.playerPieces case .four: grid = Four.board playerPieces = Four.playerPieces guard testing else { break } grid = Four.staticboard playerPieces = Four.playerPieces case .go: grid = Go.board playerPieces = Go.playerPieces playerTurn = 0 case .mancala: grid = Mancala.board playerPieces = Mancala.playerPieces guard testing else { break } grid = Mancala.staticboard playerPieces = Mancala.playerPieces case .memory: solution = Memory.solution(difficulty) grid = Memory.puzzle(difficulty) playerPieces = Memory.playerPieces case .pegs: grid = Pegs.board playerPieces = Pegs.playerPieces case .sudoku: solution = Sudoku.board grid = Sudoku.puzzle(solution, difficulty: difficulty) playerPieces = Sudoku.playerPieces guard testing else { break } solution = Sudoku.staticboard grid = Sudoku.staticpuzzle(solution) playerPieces = Sudoku.playerPieces case .tictactoe: grid = TicTacToe.board playerPieces = TicTacToe.playerPieces case .words: grid = Words.board playerPieces = Words.playerPieces } } public var highlights: [Square] = [] public var selected: Square? public func visualize(_ rect: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200)) -> UIView { switch _type { case .backgammon: return grid.backgammon(rect) case .bombsweeper: return grid.bomb(rect) case .checkers, .chess: return grid.checker(rect, highlights: highlights, selected: selected) case .four: return grid.four(rect) case .dots: return grid.dots(rect) case .doubles: return grid.doubles(rect) case .go: return grid.go(rect) case .mancala: return grid.mancala(rect) case .memory: return grid.memory(rect) case .pegs: return grid.pegs(rect, highlights: highlights, selected: selected) case .sudoku: return grid.sudoku(rect, highlights: highlights) case .tictactoe: return grid.ttt(rect) case .words: return grid.words(rect) } } } public struct BoardColors { public var background: UIColor = .white public var foreground: UIColor = .black public var player1: UIColor = .systemRed public var player2: UIColor = .systemBlue public var highlight: UIColor = .systemGreen public var selected: UIColor = .systemGreen public init() { } } ================================================ FILE: Gameboards.playground/Sources/Validation.swift ================================================ import UIKit public enum MoveError: Error { /// Good try. Need a hint? case incorrectguess /// Seriously??? There is no reason to go off the board. case outofbounds /// Piece cannot move to that square case invalidmove /// Cannot take out your own piece case friendlyfire /// Another piece is in the way case blockedmove /// Piece is not of the current player case notyourturn /// Ummm... I think you may be lost case noplayer /// What type of game are you playing??? case incorrectpiece /// Validation is unfinished... not letting you cheat. case validationfailed } public enum GameStatus: Error { /// Ouch. Why don't you try again? case gameover /// You win! Don't let it go to your head. case winner /// This is awkward. case stalemate } public enum FunctionalityError: Error { /// Can't do this... maybe a future feature if you bug me enough. case unavailable } extension Gameboard { func validateNotFriendlyFire(_ p1: Piece, _ p2: Piece) throws -> Bool { var _player1: Int? var _player2: Int? for (p,pieces) in playerPieces.enumerated() { if pieces.contains(p1) { _player1 = p } if pieces.contains(p2) { _player2 = p } } guard let player1 = _player1 else { throw MoveError.noplayer } if let player2 = _player2 { guard player1 != player2 else { throw MoveError.friendlyfire } } return true } func validatePlayer(_ piece: Piece) -> Bool { guard playerPieces.count > 0 else { return true } return playerPieces[playerTurn].contains(piece) } // moves, guesses, etc mutating func validate(_ t1: Words.Letter, _ s1: Square) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } switch _type { case .words: try Words.validate(t1, s1, grid) default: throw MoveError.incorrectpiece } } mutating func validateGuess(_ s1: Square) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } switch _type { case .bombsweeper: try Bombsweeper.validateGuess(s1, grid, solution) default: throw MoveError.incorrectpiece } } mutating func validateGuess(_ s1: Square, _ g1: Guess) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } switch _type { case .sudoku: try Sudoku.validateGuess(s1, g1, grid, solution) default: throw MoveError.incorrectpiece } highlights.append(s1) } mutating func validateSelection(_ s1: Square) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } let c1 = solution[s1.0,s1.1] switch _type { case .memory: return try Memory.validateSelection(s1, c1, grid) default: throw MoveError.incorrectpiece } } mutating func validateMatch(_ s1: Square, _ s2: Square, _ reset: Bool = false) throws -> Card? { guard grid.onBoard(s1, s2) else { throw MoveError.outofbounds } let c1 = solution[s1.0,s1.1] let c2 = solution[s2.0,s2.1] switch _type { case .memory: return try Memory.validateMatch(s1, s2, c1, c2, grid, reset) default: throw MoveError.incorrectpiece } } mutating func validateDrop(_ s1: Square) throws { guard grid.onBoard((s1.0 + 1,s1.1)) else { throw MoveError.outofbounds } var p1 = playerPieces[playerTurn] if grid.onBoard(s1) { p1 = grid[s1.0][s1.1] } if s1.0 == 0 { changePlayer() } switch _type { case .four: try Four.validateDrop(s1, p1, grid) default: throw MoveError.incorrectpiece } } mutating func validateMark(_ s1: Square) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } switch _type { case .bombsweeper: try Bombsweeper.validateMark(s1, grid, solution) default: throw MoveError.incorrectpiece } } mutating func validateMove(_ s1: Square) throws { guard grid.onBoard(s1) else { throw MoveError.outofbounds } let p1 = grid[s1.0,s1.1] switch _type { case .go: try Go.validateMove(s1, p1, grid, playerTurn) case .tictactoe: try TicTacToe.validateMove(s1, p1, grid, playerTurn) case .dots: try Dots.validateMove(s1, p1, grid, playerTurn) default: throw MoveError.incorrectpiece } } mutating func validateMove(_ s1: Square, _ s2: Square, _ hint: Bool = false) throws -> Piece? { guard grid.onBoard(s1, s2) else { throw MoveError.outofbounds } let p1 = grid[s1.0,s1.1] let p2 = grid[s2.0,s2.1] guard validatePlayer(p1) else { throw MoveError.notyourturn } if playerCount > 1 { _ = try validateNotFriendlyFire(p1, p2) } switch _type { case .checkers: return try Checkers.validateMove(s1, s2, p1, p2, grid, hint) case .chess: return try Chess.validateMove(s1, s2, p1, p2, grid, hint) case .doubles: return try Doubles.validateMove(s1, s2, p1, p2, grid) case .pegs: return try Pegs.validateMove(s1, s2, p1, p2, grid, hint) default: throw MoveError.incorrectpiece } } } ================================================ FILE: Gameboards.playground/contents.xcplayground ================================================ ================================================ FILE: Games/AppDelegate.swift ================================================ // // AppDelegate.swift // Games // // Created by Jo Albright on 2/3/16. // Copyright © 2016 Jo Albright. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? } ================================================ FILE: Games/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "20x20", "idiom": "iphone", "filename" : "icon-20@2x.png", "scale": "2x" }, { "size" : "20x20", "idiom": "iphone", "filename" : "icon-20@3x.png", "scale": "3x" }, { "size" : "20x20", "idiom": "ipad", "filename" : "icon-20.png", "scale": "1x" }, { "size" : "20x20", "idiom": "ipad", "filename" : "icon-20@2x.png", "scale": "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "icon-29@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "icon-29@3x.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "icon-40@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "icon-40@3x.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "icon-60@2x.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "icon-60@3x.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "icon-29.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "icon-29@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "icon-40.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "icon-40@2x.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "icon-76.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "icon-76@2x.png", "scale" : "2x" }, { "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "icon-83.5@2x.png", "scale" : "2x" }, { "size" : "1024x1024", "idiom" : "ios-marketing", "filename" : "icon-1024.png", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Games/Assets.xcassets/Colors/Accent.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0xCC", "alpha" : "1.000", "blue" : "0xCC", "green" : "0xCC" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0x55", "alpha" : "1.000", "blue" : "0x55", "green" : "0x55" } } } ] } ================================================ FILE: Games/Assets.xcassets/Colors/Background.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0xEE", "alpha" : "1.000", "blue" : "0xEE", "green" : "0xEE" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0x33", "alpha" : "1.000", "blue" : "0x33", "green" : "0x33" } } } ] } ================================================ FILE: Games/Assets.xcassets/Colors/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Games/Assets.xcassets/Colors/Offest.colorset/Contents.json ================================================ { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0xAA", "green" : "0xAA", "red" : "0xAA" } }, "idiom" : "universal" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0x77", "green" : "0x77", "red" : "0x77" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Games/Assets.xcassets/Colors/Text.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0x55", "alpha" : "1.000", "blue" : "0x55", "green" : "0x55" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0xCC", "alpha" : "1.000", "blue" : "0xCC", "green" : "0xCC" } } } ] } ================================================ FILE: Games/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Games/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Games/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Games/BoardController/BoardViewController.swift ================================================ // // ViewController.swift // Games // // Created by Jo Albright on 2/3/16. // Copyright © 2016 Jo Albright. All rights reserved. // import UIKit enum Direction: UInt { case right = 1 case left = 2 case down = 8 case up = 4 var name: String { switch self { case .down: return "Down" case .up: return "Up" case .left: return "Left" case .right: return "Right" } } } class BoardViewController: UIViewController { @IBOutlet weak var boardView: BoardView! @IBOutlet weak var playerLabel: UILabel! override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) boardView?.board?.playerChange = { [weak self] player in self?.playerLabel?.text = "Player \(player)" } boardView?.board?.showAlert = { [weak self] title, message in let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) let resetAction = UIAlertAction(title: "Reset", style: .default) { [weak self] _ in self?.boardView?.board?.reset() self?.boardView?.updateBoard() } alertVC.addAction(resetAction) // present VC self?.present(alertVC, animated: true) } } override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } guard let square = boardView?.coordinate(touch) else { return } boardView?.selectSquare(square) } @IBAction func swipe(_ sender: UISwipeGestureRecognizer) { guard let direction = Direction(rawValue: sender.direction.rawValue) else { return } boardView?.swipe(direction) } // @IBAction func chooseDifficulty(sender: UISegmentedControl) { // // boardView?.board?.difficulty = Difficulty(sender.selectedSegmentIndex) // boardView?.updateBoard() // // } @IBAction func resetBoard(sender: Any) { boardView?.board?.reset() boardView?.updateBoard() } } @IBDesignable class GradientView: UIView { @IBInspectable var startColor: UIColor = .white @IBInspectable var endColor: UIColor = .black override func draw(_ rect: CGRect) { let startPoint = CGPoint(x: 0, y: 0) let endPoint = CGPoint(x: 1, y: 1) let context = UIGraphicsGetCurrentContext() let colors: CFArray = [startColor.cgColor,endColor.cgColor] as CFArray guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors, locations: [0, 1]) else { return } let s = CGPoint(x: frame.width * startPoint.x, y: frame.height * startPoint.y) let e = CGPoint(x: frame.width * endPoint.x, y: frame.height * endPoint.y) context?.drawLinearGradient(gradient, start: s, end: e, options: .drawsAfterEndLocation) } } @IBDesignable class BoardView : UIView { var board: Gameboard! var boardView: UIView? @IBInspectable var padding: CGFloat = 0 @IBInspectable var bgColor: UIColor = .white @IBInspectable var fgColor: UIColor = .black @IBInspectable var player1Color: UIColor = .systemRed @IBInspectable var player2Color: UIColor = .systemBlue @IBInspectable var selectedColor: UIColor = .white @IBInspectable var highlightColor: UIColor = .white override func prepareForInterfaceBuilder() { updateBoard() } override func didMoveToWindow() { updateBoard() } override func layoutSubviews() { updateBoard() } func updateBoard() { board?.padding = padding board?.colors.background = bgColor board?.colors.foreground = fgColor board?.colors.player1 = player1Color board?.colors.player2 = player2Color board?.colors.selected = selectedColor board?.colors.highlight = highlightColor boardView?.removeFromSuperview() boardView = board?.visualize(bounds) guard let boardView = boardView else { return } addSubview(boardView) } func coordinate(_ touch: UITouch) -> Square? { guard let board = board else { return nil } let w = (frame.width - board.padding * 2) / board.gridSize let h = (frame.height - board.padding * 2) / board.gridSize let loc = touch.location(in: self) let c = Int((loc.x - board.padding) / w) let r = Int((loc.y - board.padding) / h) return (r,c) } func selectSquare(_ square: Square) { } func swipe(_ direction: Direction) { } func checkDone() { } } ================================================ FILE: Games/BoardController/BoardViews/BackgammonBoardView.swift ================================================ // // BackgammonBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class BackgammonBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.backgammon) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.backgammon) super.awakeFromNib() } override func selectSquare(_ square: Square) { // do { // // try board.move(toSquare: square) // // } catch { // // print(error) // // } updateBoard() } override func coordinate(_ touch: UITouch) -> Square { let p = 30 let w = (frame.width - p * 2) / (board.gridSize - 1) let h = (frame.height - p * 2) / (board.gridSize - 1) let loc = touch.location(in: self) let c = Int((loc.x - (w / 2)) / w) let r = Int((loc.y - (w / 2)) / h) return (r,c) } } ================================================ FILE: Games/BoardController/BoardViews/BombsweeperBoardView.swift ================================================ // // BombsweeperBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class BombsweeperBoardView : BoardView { var bombsweeperGuess: Bool = true override func prepareForInterfaceBuilder() { board = Gameboard(.bombsweeper, testing: true) _ = try? board.guess(toSquare: (4,4)) _ = try? board.mark(toSquare: (7,4)) _ = try? board.guess(toSquare: (9,0)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.bombsweeper) super.awakeFromNib() } override func selectSquare(_ square: Square) { do { if bombsweeperGuess { try board.guess(toSquare: square) } else { try board.mark(toSquare: square) } } catch { print(error) } updateBoard() checkDone() } override func checkDone() { guard let board = board else { return } let noMoves = !(board.grid.content.reduce([]) { $0 + $1 }.contains("•")) let boom = (board.grid.content.reduce([]) { $0 + $1 }.contains("✘")) if boom { board.showAlert?("Game Over", "You stepped on a mine.") } else if noMoves { board.showAlert?("Game Over", "You flagged all mines.") } } } extension BoardViewController { @IBAction func chooseBombsweeperMark(sender: UISegmentedControl) { (boardView as? BombsweeperBoardView)?.bombsweeperGuess = sender.selectedSegmentIndex == 0 } } ================================================ FILE: Games/BoardController/BoardViews/CheckersBoardView.swift ================================================ // // CheckersBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class CheckersBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.checkers) board.showAvailable((2,3)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.checkers) super.awakeFromNib() } override func selectSquare(_ square: Square) { if let selected = board.selected { do { _ = try board.move(pieceAt: selected, toSquare: square) board.selected = nil board.highlights = [] } catch { print(error) board.showAvailable(square) } } else { board.showAvailable(square) } updateBoard() checkDone() } override func checkDone() { if checkLost(player: 0) { board?.showAlert?("Game Over", "Player 2 Wins") } else if checkLost(player: 1) { board?.showAlert?("Game Over", "Player 1 Wins") } } private func checkLost(player: Int) -> Bool { return board.grid.piecesOnBoard().filter({ board.playerPieces[player].contains($0) }).isEmpty } } ================================================ FILE: Games/BoardController/BoardViews/ChessBoardView.swift ================================================ // // ChessBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class ChessBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.chess) board.showAvailable(C7) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.chess) super.awakeFromNib() } override func selectSquare(_ square: Square) { if let selected = board.selected { do { _ = try board.move(pieceAt: selected, toSquare: square) board.selected = nil board.highlights = [] } catch { print(error) board.showAvailable(square) } } else { board.showAvailable(square) } updateBoard() } override func checkDone() { } } ================================================ FILE: Games/BoardController/BoardViews/DotsBoardView.swift ================================================ // // DotsBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class DotsBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.dots, testing: true) _ = try? board.move(toSquare: (3,6)) _ = try? board.move(toSquare: (2,5)) _ = try? board.move(toSquare: (2,7)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.dots, testing: true) super.awakeFromNib() } override func selectSquare(_ square: Square) { do { try board.move(toSquare: square) } catch { print(error) } updateBoard() } } ================================================ FILE: Games/BoardController/BoardViews/DoublesBoardView.swift ================================================ // // DoublesBoardView.swift // Games // // Created by Jo Albright on 4/27/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class DoublesBoardView: BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.doubles, testing: true) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.doubles) super.awakeFromNib() } var initialgrid: [[String]] = [] override func swipe(_ direction: Direction) { initialgrid = board.grid.content swipe(direction, 0) } func swipe(_ direction: Direction, _ loop: Int, _ changed: Bool = false) { var moved: Bool = changed switch direction { case .left: for r in board.grid.colRange { for c in board.grid.rowRange { do { _ = try board.move(pieceAt: (c,r), toSquare: (c,r-1)) moved = true } catch { print(error) } } } case .right: for r in board.grid.colRange { let row = board.grid.rowRange.upperBound - 1 - r print(r,row) for c in board.grid.colRange { do { _ = try board.move(pieceAt: (c,row), toSquare: (c,row+1)) moved = true } catch { print(error) } } } case .up: for c in board.grid.colRange { for r in board.grid.rowRange { do { _ = try board.move(pieceAt: (c,r), toSquare: (c-1,r)) moved = true } catch { print(error) } } } case .down: for c in board.grid.colRange { let col = board.grid.colRange.upperBound - 1 - c print(c,col) for r in board.grid.rowRange { do { _ = try board.move(pieceAt: (col,r), toSquare: (col+1,r)) moved = true } catch { print(error) } } } } updateBoard() guard loop == 3 else { DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { self.swipe(direction, loop + 1, moved) } return } guard moved else { return } let _ = Doubles.random(board.grid) updateBoard() } } ================================================ FILE: Games/BoardController/BoardViews/FourBoardView.swift ================================================ // // FourBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class FourBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.four, testing: true) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.four) super.awakeFromNib() } override func selectSquare(_ square: Square) { do { try board.drop(pieceAt: square) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.selectSquare((square.0 + 1,square.1)) } } catch MoveError.invalidmove { checkDone() } catch MoveError.outofbounds { checkDone() } catch { print(error) } updateBoard() } override func coordinate(_ touch: UITouch) -> Square { let w = (frame.width - board.padding * 2) / 7 let loc = touch.location(in: self) let c = Int((loc.x - board.padding) / w) return (-1,c) } lazy var combinations: [[Int]] = { var combinations: [[Int]] = [] // Side to Side (+1) for i in [1,2,3,4,8,9,10,11,15,16,17,18,22,23,24,25,29,30,31,32,36,37,38,39] { combinations += [i.set(1, 4)] } // Up & Down (+7) for i in [1,8,15,2,9,16,3,10,17,4,11,18,5,12,19,6,13,20,7,14,21] { combinations += [i.set(7, 4)] } // Diagnol from Left (+8) for i in [1,8,15,2,9,16,3,10,17,4,11,18] { combinations += [i.set(8, 4)] } // Diagnol from Right (+6) for i in [4,11,18,5,12,19,6,13,20,7,14,21] { combinations += [i.set(6, 4)] } return combinations }() override func checkDone() { for set in combinations { let (winner,gameover) = checkWin(indexes: set) guard gameover else { continue } if let w = winner { board?.showAlert?("Game Over", "Player \(w) Wins") } else { board?.showAlert?("Game Over", "Stalemate") } return } } private func checkWin(indexes: [Int]) -> (Int?,Bool) { let p1: String = board.grid[indexes[0] - 1] let p2: String = board.grid[indexes[1] - 1] let p3: String = board.grid[indexes[2] - 1] let p4: String = board.grid[indexes[3] - 1] if p1 == p2, p2 == p3, p3 == p4, p1 != EmptyPiece { guard let index = board?.playerPieces.firstIndex(of: p1) else { return (nil,false) } return (index + 1,true) } return (nil,false) } } ================================================ FILE: Games/BoardController/BoardViews/GoBoardView.swift ================================================ // // GoBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class GoBoardView : BoardView { var passes: Int = 0 override func prepareForInterfaceBuilder() { board = Gameboard(.go) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.go) super.awakeFromNib() } @IBAction func pass() { passes += 1 board.changePlayer() checkDone() } override func selectSquare(_ square: Square) { do { try board.move(toSquare: square) } catch { print(error) } updateBoard() checkDone() } override func coordinate(_ touch: UITouch) -> Square { let p = 30 let w = (frame.width - p * 2) / (board.gridSize - 1) let h = (frame.height - p * 2) / (board.gridSize - 1) let loc = touch.location(in: self) let c = Int((loc.x - (w / 2)) / w) let r = Int((loc.y - (w / 2)) / h) return (r,c) } override func checkDone() { guard board.grid.piecesOnBoard().count == board.totalSpaces || passes == 2 else { return } let pieces1 = board.grid.piecesOnBoard().filter { board.playerPieces[0].contains($0) } let pieces2 = board.grid.piecesOnBoard().filter { board.playerPieces[1].contains($0) } if pieces1.count == pieces2.count { board?.showAlert?("Game Over", "Stalemate") } else { board?.showAlert?("Game Over", "Player \(pieces1.count > pieces2.count ? 1 : 2) Wins") } passes = 0 } } ================================================ FILE: Games/BoardController/BoardViews/MancalaBoardView.swift ================================================ // // MancalaBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class MancalaBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.mancala, testing: true) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.mancala, testing: true) super.awakeFromNib() } override func selectSquare(_ square: Square) { // do { // // try board.move(toSquare: square) // // } catch { // // print(error) // // } updateBoard() } // override func coordinate(_ touch: UITouch) -> Square { // // let p = 30 // let w = (frame.width - p * 2) / (board.gridSize - 1) // let h = (frame.height - p * 2) / (board.gridSize - 1) // // let loc = touch.location(in: self) // // let c = Int((loc.x - (w / 2)) / w) // let r = Int((loc.y - (w / 2)) / h) // // return (r,c) // // } } ================================================ FILE: Games/BoardController/BoardViews/MemoryBoardView.swift ================================================ // // MemoryBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class MemoryBoardView : BoardView { var guesses: Int = 0 override func prepareForInterfaceBuilder() { board = Gameboard(.memory) _ = try? board?.select(cardAt: (0,2)) _ = try? board?.match(cardAt: (2,1), withCard: (0,2)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.memory) super.awakeFromNib() } override func selectSquare(_ square: Square) { let clean: () -> Void = { self.board.highlights = [self.board.selected, square].compactMap { $0 } self.board.selected = nil DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { [weak self] in guard let highlights = self?.board.highlights, highlights.count > 1 else { return } _ = try? self?.board.match(cardAt: highlights[1], withCard: highlights[0], reset: true) self?.board.highlights = [] self?.updateBoard() self?.checkDone() } } if board.highlights.count > 1 { return } else if let selected = board.selected { guesses += 1 do { _ = try board.match(cardAt: square, withCard: selected) clean() } catch MemoryError.badmatch { clean() } catch { } } else { guard let _ = try? board.select(cardAt: square) else { return } board.selected = square } updateBoard() } override func checkDone() { let cardCount = board.grid.content.reduce(0) { $0 + $1.reduce(0) { $0 + ($1 != EmptyPiece ? 1 : 0) } } if cardCount == 0 { board?.showAlert?("Game Over", "It took \(guesses) Guesses") guesses = 0 } } } ================================================ FILE: Games/BoardController/BoardViews/PegsBoardView.swift ================================================ // // PegSolitaireBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class PegsBoardView: BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.pegs) _ = try? board.validateMove((3,1), (3,3)) _ = try? board.validateMove((3,4), (3,2)) _ = try? board.validateMove((1,3), (3,3)) _ = try? board.validateMove((3,3), (3,1)) _ = try? board.validateMove((3,0), (3,2)) _ = try? board.validateMove((3,6), (3,4)) _ = try? board.validateMove((5,3), (3,3)) board.showAvailable((3,3)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.pegs) super.awakeFromNib() } override func selectSquare(_ square: Square) { if let selected = board.selected { do { _ = try board.move(pieceAt: selected, toSquare: square) board.selected = nil board.highlights = [] } catch { print(error) board.showAvailable(square) } } else { board.showAvailable(square) } updateBoard() } } ================================================ FILE: Games/BoardController/BoardViews/SudokuBoardView.swift ================================================ // // SudokuBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class SudokuBoardView : BoardView { var sudokuNumber: String = "1" override func prepareForInterfaceBuilder() { board = Gameboard(.sudoku, testing: true) _ = try? board.guess(toSquare: (2,7), withGuess: "4") _ = try? board.guess(toSquare: (8,8), withGuess: "4") super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.sudoku) super.awakeFromNib() } override func selectSquare(_ square: Square) { do { try board.guess(toSquare: square, withGuess: sudokuNumber) } catch { print(error) } updateBoard() } } extension BoardViewController { @IBAction func chooseSudokuNymber(sender: UISegmentedControl) { (boardView as? SudokuBoardView)?.sudokuNumber = "\(sender.selectedSegmentIndex + 1)" } } ================================================ FILE: Games/BoardController/BoardViews/TicTacToeBoardView.swift ================================================ // // TicTacToeBoardView.swift // Games // // Created by Jo Albright on 4/25/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class TicTacToeBoardView : BoardView { override func prepareForInterfaceBuilder() { board = Gameboard(.tictactoe) _ = try? board.move(toSquare: (1,1)) _ = try? board.move(toSquare: (0,1)) _ = try? board.move(toSquare: (1,0)) _ = try? board.move(toSquare: (1,2)) _ = try? board.move(toSquare: (2,0)) super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.tictactoe) super.awakeFromNib() } override func selectSquare(_ square: Square) { do { try board.move(toSquare: square) } catch { print(error) } updateBoard() checkDone() } lazy var combinations: [[Int]] = { var combinations: [[Int]] = [] // Side to Side (+1) for i in [1,4,7] { combinations += [i.set(1, 3)] } // Up & Down (+3) for i in [1,2,3] { combinations += [i.set(3, 3)] } // Diagonals combinations += [ 1.set(4, 3), 3.set(2, 3) ] return combinations }() override func checkDone() { for combination in combinations { let (winner,gameover) = checkWin(indexes: combination) guard gameover else { continue } if let w = winner { board?.showAlert?("Game Over", "Player \(w) Wins") } else { board?.showAlert?("Game Over", "Stalemate") } return } } private func checkWin(indexes: [Int]) -> (Int?,Bool) { let p1: String = board.grid[indexes[0] - 1] let p2: String = board.grid[indexes[1] - 1] let p3: String = board.grid[indexes[2] - 1] if p1 == p2, p2 == p3, p1 != EmptyPiece { guard let index = board?.playerPieces.firstIndex(of: p1) else { return (nil,false) } return (index + 1,true) } return (nil,false) } } ================================================ FILE: Games/BoardController/BoardViews/WordsBoardView.swift ================================================ // // WordsBoardView.swift // Games // // Created by Jo Albright on 4/23/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit @IBDesignable class WordsBoardView : BoardView { var selectedTile: Words.Letter? var bag: [Words.Letter] = [] var rack: [Words.Letter] = [] { didSet { rackUpdated(rack) } } var rackUpdated: ([Words.Letter]) -> Void = { _ in } override func prepareForInterfaceBuilder() { board = Gameboard(.words) bag = Words.Letter.bag super.prepareForInterfaceBuilder() } override func awakeFromNib() { board = Gameboard(.words) bag = Words.Letter.bag super.awakeFromNib() } override func selectSquare(_ square: Square) { guard let tile = selectedTile else { return } do { try board.place(tile: tile, at: square) if let index = rack.index(of: tile) { rack.remove(at: index) rack.append(.none) selectedTile = nil } } catch { print(error) } updateBoard() } func reset() { bag = Words.Letter.bag rack = [] fillRack() } @IBAction func fillRack() { rack = rack.filter { $0 != .none } let tiles = 7 - rack.count print(rack.count,tiles,bag.count) rack += bag.prefix(tiles) bag.removeFirst(tiles) print(rack.count,bag.count) } } class WordsBoardViewController: BoardViewController { @IBOutlet var rackHolder: UIStackView! var selected: Words.Letter? override func viewDidLoad() { super.viewDidLoad() (boardView as? WordsBoardView)?.rackUpdated = { rack in for view in self.rackHolder.arrangedSubviews { self.rackHolder.removeArrangedSubview(view) } for tile in rack { let tileView = TileView() tileView.tile = tile tileView.color = self.boardView.player2Color tileView.selectedColor = self.boardView.selectedColor tileView.backgroundColor = .clear tileView.letterLabel?.textColor = self.boardView.player1Color tileView.valueLabel?.textColor = self.boardView.highlightColor tileView.selected = { tile in (self.boardView as? WordsBoardView)?.selectedTile = tile for view in self.rackHolder.arrangedSubviews { guard let tileView = view as? TileView else { continue } tileView.color = self.boardView.player2Color } } self.rackHolder.addArrangedSubview(tileView) } } (boardView as? WordsBoardView)?.fillRack() } @IBAction override func resetBoard(sender: Any) { super.resetBoard(sender: sender) (boardView as? WordsBoardView)?.reset() } } ================================================ FILE: Games/BoardController/Boards/Backgammon.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Bombsweeper.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Checkers.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Chess.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Dots.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Doubles.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Four.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Go.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Mancala.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Memory.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Pegs.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Sudoku.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/TicTacToe.storyboard ================================================ ================================================ FILE: Games/BoardController/Boards/Words.storyboard ================================================ ================================================ FILE: Games/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName Gameboard CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UIAppFonts Apple Symbols.ttf UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Games/MainViewController.swift ================================================ // // MainViewController.swift // Games // // Created by Jo Albright on 4/20/18. // Copyright © 2018 Jo Albright. All rights reserved. // import UIKit class MainViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() guard UIDevice.current.userInterfaceIdiom == .pad, let vc = Gameboard.BoardType.playable.first?.controller else { return } splitViewController?.showDetailViewController(vc, sender: nil) tableView?.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .none) } } extension MainViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return Gameboard.BoardType.playable.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) as? GameCell else { fatalError() } let view = UIView() view.backgroundColor = UIColor(white: 0.95, alpha: 1) cell.selectedBackgroundView = view cell.game = Gameboard.BoardType.playable[indexPath.item] return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let game = Gameboard.BoardType.playable[indexPath.item] guard let vc = game.controller else { return } splitViewController?.showDetailViewController(vc, sender: nil) guard UIDevice.current.userInterfaceIdiom != .pad else { return } tableView.deselectRow(at: indexPath, animated: true) } } class GameCell: UITableViewCell { var game: Gameboard.BoardType! { didSet { emblem.text = game.emblem name.text = game.name } } @IBOutlet weak var emblem: UILabel! @IBOutlet weak var name: UILabel! } @IBDesignable class SimpleButton: UIButton { @IBInspectable var cornerRadius: CGFloat = 0 override func draw(_ rect: CGRect) { layer.cornerRadius = cornerRadius layer.masksToBounds = true } } @IBDesignable class SimpleView: UIView { @IBInspectable var cornerRadius: CGFloat = 0 override func draw(_ rect: CGRect) { layer.cornerRadius = cornerRadius layer.masksToBounds = true } } ================================================ FILE: Games.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 0E6C166E233B98B1003DE849 /* Bombsweeper.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C166D233B98B1003DE849 /* Bombsweeper.storyboard */; }; 0E6C1670233B98F3003DE849 /* Chess.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C166F233B98F3003DE849 /* Chess.storyboard */; }; 0E6C1672233B98FC003DE849 /* Checkers.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1671233B98FC003DE849 /* Checkers.storyboard */; }; 0E6C1674233B9AA0003DE849 /* Sudoku.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1673233B9AA0003DE849 /* Sudoku.storyboard */; }; 0E6C1676233B9AAD003DE849 /* Words.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1675233B9AAD003DE849 /* Words.storyboard */; }; 0E6C1678233B9AB7003DE849 /* TicTacToe.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1677233B9AB7003DE849 /* TicTacToe.storyboard */; }; 0E6C167A233B9ACB003DE849 /* Four.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1679233B9ACB003DE849 /* Four.storyboard */; }; 0E6C167C233B9E82003DE849 /* Mancala.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C167B233B9E82003DE849 /* Mancala.storyboard */; }; 0E6C167E233B9E8F003DE849 /* Dots.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C167D233B9E8F003DE849 /* Dots.storyboard */; }; 0E6C1680233B9EA0003DE849 /* Pegs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C167F233B9EA0003DE849 /* Pegs.storyboard */; }; 0E6C1682233BA8B0003DE849 /* Go.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1681233BA8B0003DE849 /* Go.storyboard */; }; 0E6C1684233BA8C1003DE849 /* Memory.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1683233BA8C1003DE849 /* Memory.storyboard */; }; 0E6C1686233BA8D0003DE849 /* Doubles.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1685233BA8D0003DE849 /* Doubles.storyboard */; }; 0E6C1688233BA8E7003DE849 /* Backgammon.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6C1687233BA8E7003DE849 /* Backgammon.storyboard */; }; 0E96540923390BA40075BF13 /* Four.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540023390BA30075BF13 /* Four.swift */; }; 0E96540A23390BA40075BF13 /* Chess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540123390BA30075BF13 /* Chess.swift */; }; 0E96540B23390BA40075BF13 /* Dots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540223390BA40075BF13 /* Dots.swift */; }; 0E96540C23390BA40075BF13 /* Backgammon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540323390BA40075BF13 /* Backgammon.swift */; }; 0E96540D23390BA40075BF13 /* Alquerque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540423390BA40075BF13 /* Alquerque.swift */; }; 0E96540E23390BA40075BF13 /* Doubles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540523390BA40075BF13 /* Doubles.swift */; }; 0E96540F23390BA40075BF13 /* Bombsweeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540623390BA40075BF13 /* Bombsweeper.swift */; }; 0E96541023390BA40075BF13 /* Go.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540723390BA40075BF13 /* Go.swift */; }; 0E96541123390BA40075BF13 /* Checkers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96540823390BA40075BF13 /* Checkers.swift */; }; 0E96541923390BFD0075BF13 /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541223390BFC0075BF13 /* Memory.swift */; }; 0E96541A23390BFD0075BF13 /* Sudoku.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541323390BFC0075BF13 /* Sudoku.swift */; }; 0E96541B23390BFD0075BF13 /* Mancala.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541423390BFC0075BF13 /* Mancala.swift */; }; 0E96541C23390BFD0075BF13 /* Pegs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541523390BFC0075BF13 /* Pegs.swift */; }; 0E96541D23390BFD0075BF13 /* TicTacToe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541623390BFC0075BF13 /* TicTacToe.swift */; }; 0E96541E23390BFD0075BF13 /* Ludo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541723390BFC0075BF13 /* Ludo.swift */; }; 0E96541F23390BFD0075BF13 /* Words.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96541823390BFC0075BF13 /* Words.swift */; }; 0E96542623390C0E0075BF13 /* Pilare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542023390C0E0075BF13 /* Pilare.swift */; }; 0E96542723390C0E0075BF13 /* Tetris.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542123390C0E0075BF13 /* Tetris.swift */; }; 0E96542823390C0E0075BF13 /* Trouble.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542223390C0E0075BF13 /* Trouble.swift */; }; 0E96542923390C0E0075BF13 /* Dominos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542323390C0E0075BF13 /* Dominos.swift */; }; 0E96542A23390C0E0075BF13 /* Majong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542423390C0E0075BF13 /* Majong.swift */; }; 0E96542B23390C0E0075BF13 /* Battleship.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542523390C0E0075BF13 /* Battleship.swift */; }; 0E96543623390C2A0075BF13 /* DotsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542C23390C2A0075BF13 /* DotsView.swift */; }; 0E96543723390C2A0075BF13 /* WordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542D23390C2A0075BF13 /* WordsView.swift */; }; 0E96543823390C2A0075BF13 /* FourView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542E23390C2A0075BF13 /* FourView.swift */; }; 0E96543923390C2A0075BF13 /* BackgammonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96542F23390C2A0075BF13 /* BackgammonView.swift */; }; 0E96543A23390C2A0075BF13 /* MancalaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543023390C2A0075BF13 /* MancalaView.swift */; }; 0E96543B23390C2A0075BF13 /* GoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543123390C2A0075BF13 /* GoView.swift */; }; 0E96543C23390C2A0075BF13 /* MatrixView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543223390C2A0075BF13 /* MatrixView.swift */; }; 0E96543D23390C2A0075BF13 /* PegsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543323390C2A0075BF13 /* PegsView.swift */; }; 0E96543E23390C2A0075BF13 /* SudokuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543423390C2A0075BF13 /* SudokuView.swift */; }; 0E96543F23390C2A0075BF13 /* TicTacToeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96543523390C2A0075BF13 /* TicTacToeView.swift */; }; 0E96544323390C360075BF13 /* Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96544023390C360075BF13 /* Grid.swift */; }; 0E96544423390C360075BF13 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96544123390C360075BF13 /* Extensions.swift */; }; 0E96544523390C360075BF13 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96544223390C360075BF13 /* Operators.swift */; }; 0E96544823390C470075BF13 /* Gameboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96544623390C470075BF13 /* Gameboard.swift */; }; 0E96544923390C470075BF13 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96544723390C470075BF13 /* Validation.swift */; }; A90F20AC2089229C00C3F6A4 /* Apple Symbols.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A90F20AB2089229C00C3F6A4 /* Apple Symbols.ttf */; }; A90F20B1208A6D7200C3F6A4 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90F20B0208A6D7200C3F6A4 /* MainViewController.swift */; }; A9DBACA7208EDE2600C03C12 /* WordsBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACA6208EDE2600C03C12 /* WordsBoardView.swift */; }; A9DBACD72090BCFB00C03C12 /* BombsweeperBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACD62090BCFB00C03C12 /* BombsweeperBoardView.swift */; }; A9DBACD92090BD4B00C03C12 /* MemoryBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACD82090BD4B00C03C12 /* MemoryBoardView.swift */; }; A9DBACDB2090BD6E00C03C12 /* ChessBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACDA2090BD6E00C03C12 /* ChessBoardView.swift */; }; A9DBACDD2090BD8A00C03C12 /* CheckersBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACDC2090BD8A00C03C12 /* CheckersBoardView.swift */; }; A9DBACDF2090BDB500C03C12 /* BackgammonBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACDE2090BDB500C03C12 /* BackgammonBoardView.swift */; }; A9DBACE12090BDD700C03C12 /* FourBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACE02090BDD700C03C12 /* FourBoardView.swift */; }; A9DBACE32090BDFA00C03C12 /* TicTacToeBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACE22090BDFA00C03C12 /* TicTacToeBoardView.swift */; }; A9DBACE52090BE1500C03C12 /* SudokuBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACE42090BE1500C03C12 /* SudokuBoardView.swift */; }; A9DBACE72090BE3800C03C12 /* DotsBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACE62090BE3800C03C12 /* DotsBoardView.swift */; }; A9DBACE92090BE5B00C03C12 /* GoBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACE82090BE5B00C03C12 /* GoBoardView.swift */; }; A9DBACEB2090BE7700C03C12 /* MancalaBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACEA2090BE7700C03C12 /* MancalaBoardView.swift */; }; A9DBACED2090BE9A00C03C12 /* PegsBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACEC2090BE9A00C03C12 /* PegsBoardView.swift */; }; A9DBACF320936DA200C03C12 /* DoublesBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DBACF220936DA200C03C12 /* DoublesBoardView.swift */; }; A9DD740E1C62A34600219C78 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DD740D1C62A34600219C78 /* AppDelegate.swift */; }; A9DD74101C62A34600219C78 /* BoardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DD740F1C62A34600219C78 /* BoardViewController.swift */; }; A9DD74131C62A34600219C78 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9DD74111C62A34600219C78 /* Main.storyboard */; }; A9DD74151C62A34600219C78 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9DD74141C62A34600219C78 /* Assets.xcassets */; }; A9DD74181C62A34600219C78 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9DD74161C62A34600219C78 /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0E6C166D233B98B1003DE849 /* Bombsweeper.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Bombsweeper.storyboard; sourceTree = ""; }; 0E6C166F233B98F3003DE849 /* Chess.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Chess.storyboard; sourceTree = ""; }; 0E6C1671233B98FC003DE849 /* Checkers.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Checkers.storyboard; sourceTree = ""; }; 0E6C1673233B9AA0003DE849 /* Sudoku.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Sudoku.storyboard; sourceTree = ""; }; 0E6C1675233B9AAD003DE849 /* Words.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Words.storyboard; sourceTree = ""; }; 0E6C1677233B9AB7003DE849 /* TicTacToe.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TicTacToe.storyboard; sourceTree = ""; }; 0E6C1679233B9ACB003DE849 /* Four.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Four.storyboard; sourceTree = ""; }; 0E6C167B233B9E82003DE849 /* Mancala.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Mancala.storyboard; sourceTree = ""; }; 0E6C167D233B9E8F003DE849 /* Dots.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Dots.storyboard; sourceTree = ""; }; 0E6C167F233B9EA0003DE849 /* Pegs.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Pegs.storyboard; sourceTree = ""; }; 0E6C1681233BA8B0003DE849 /* Go.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Go.storyboard; sourceTree = ""; }; 0E6C1683233BA8C1003DE849 /* Memory.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Memory.storyboard; sourceTree = ""; }; 0E6C1685233BA8D0003DE849 /* Doubles.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Doubles.storyboard; sourceTree = ""; }; 0E6C1687233BA8E7003DE849 /* Backgammon.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Backgammon.storyboard; sourceTree = ""; }; 0E96540023390BA30075BF13 /* Four.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Four.swift; path = Gameboards.playground/Sources/Boards/Four.swift; sourceTree = SOURCE_ROOT; }; 0E96540123390BA30075BF13 /* Chess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Chess.swift; path = Gameboards.playground/Sources/Boards/Chess.swift; sourceTree = SOURCE_ROOT; }; 0E96540223390BA40075BF13 /* Dots.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dots.swift; path = Gameboards.playground/Sources/Boards/Dots.swift; sourceTree = SOURCE_ROOT; }; 0E96540323390BA40075BF13 /* Backgammon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Backgammon.swift; path = Gameboards.playground/Sources/Boards/Backgammon.swift; sourceTree = SOURCE_ROOT; }; 0E96540423390BA40075BF13 /* Alquerque.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Alquerque.swift; path = Gameboards.playground/Sources/Boards/Alquerque.swift; sourceTree = SOURCE_ROOT; }; 0E96540523390BA40075BF13 /* Doubles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Doubles.swift; path = Gameboards.playground/Sources/Boards/Doubles.swift; sourceTree = SOURCE_ROOT; }; 0E96540623390BA40075BF13 /* Bombsweeper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bombsweeper.swift; path = Gameboards.playground/Sources/Boards/Bombsweeper.swift; sourceTree = SOURCE_ROOT; }; 0E96540723390BA40075BF13 /* Go.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Go.swift; path = Gameboards.playground/Sources/Boards/Go.swift; sourceTree = SOURCE_ROOT; }; 0E96540823390BA40075BF13 /* Checkers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Checkers.swift; path = Gameboards.playground/Sources/Boards/Checkers.swift; sourceTree = SOURCE_ROOT; }; 0E96541223390BFC0075BF13 /* Memory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Memory.swift; path = Gameboards.playground/Sources/Boards/Memory.swift; sourceTree = SOURCE_ROOT; }; 0E96541323390BFC0075BF13 /* Sudoku.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Sudoku.swift; path = Gameboards.playground/Sources/Boards/Sudoku.swift; sourceTree = SOURCE_ROOT; }; 0E96541423390BFC0075BF13 /* Mancala.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Mancala.swift; path = Gameboards.playground/Sources/Boards/Mancala.swift; sourceTree = SOURCE_ROOT; }; 0E96541523390BFC0075BF13 /* Pegs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Pegs.swift; path = Gameboards.playground/Sources/Boards/Pegs.swift; sourceTree = SOURCE_ROOT; }; 0E96541623390BFC0075BF13 /* TicTacToe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TicTacToe.swift; path = Gameboards.playground/Sources/Boards/TicTacToe.swift; sourceTree = SOURCE_ROOT; }; 0E96541723390BFC0075BF13 /* Ludo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Ludo.swift; path = Gameboards.playground/Sources/Boards/Ludo.swift; sourceTree = SOURCE_ROOT; }; 0E96541823390BFC0075BF13 /* Words.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Words.swift; path = Gameboards.playground/Sources/Boards/Words.swift; sourceTree = SOURCE_ROOT; }; 0E96542023390C0E0075BF13 /* Pilare.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Pilare.swift; path = Gameboards.playground/Sources/Boards/InProgress/Pilare.swift; sourceTree = SOURCE_ROOT; }; 0E96542123390C0E0075BF13 /* Tetris.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Tetris.swift; path = Gameboards.playground/Sources/Boards/InProgress/Tetris.swift; sourceTree = SOURCE_ROOT; }; 0E96542223390C0E0075BF13 /* Trouble.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Trouble.swift; path = Gameboards.playground/Sources/Boards/InProgress/Trouble.swift; sourceTree = SOURCE_ROOT; }; 0E96542323390C0E0075BF13 /* Dominos.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dominos.swift; path = Gameboards.playground/Sources/Boards/InProgress/Dominos.swift; sourceTree = SOURCE_ROOT; }; 0E96542423390C0E0075BF13 /* Majong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Majong.swift; path = Gameboards.playground/Sources/Boards/InProgress/Majong.swift; sourceTree = SOURCE_ROOT; }; 0E96542523390C0E0075BF13 /* Battleship.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Battleship.swift; path = Gameboards.playground/Sources/Boards/InProgress/Battleship.swift; sourceTree = SOURCE_ROOT; }; 0E96542C23390C2A0075BF13 /* DotsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DotsView.swift; path = Gameboards.playground/Sources/Core/Views/DotsView.swift; sourceTree = SOURCE_ROOT; }; 0E96542D23390C2A0075BF13 /* WordsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WordsView.swift; path = Gameboards.playground/Sources/Core/Views/WordsView.swift; sourceTree = SOURCE_ROOT; }; 0E96542E23390C2A0075BF13 /* FourView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FourView.swift; path = Gameboards.playground/Sources/Core/Views/FourView.swift; sourceTree = SOURCE_ROOT; }; 0E96542F23390C2A0075BF13 /* BackgammonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BackgammonView.swift; path = Gameboards.playground/Sources/Core/Views/BackgammonView.swift; sourceTree = SOURCE_ROOT; }; 0E96543023390C2A0075BF13 /* MancalaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MancalaView.swift; path = Gameboards.playground/Sources/Core/Views/MancalaView.swift; sourceTree = SOURCE_ROOT; }; 0E96543123390C2A0075BF13 /* GoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GoView.swift; path = Gameboards.playground/Sources/Core/Views/GoView.swift; sourceTree = SOURCE_ROOT; }; 0E96543223390C2A0075BF13 /* MatrixView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MatrixView.swift; path = Gameboards.playground/Sources/Core/Views/MatrixView.swift; sourceTree = SOURCE_ROOT; }; 0E96543323390C2A0075BF13 /* PegsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PegsView.swift; path = Gameboards.playground/Sources/Core/Views/PegsView.swift; sourceTree = SOURCE_ROOT; }; 0E96543423390C2A0075BF13 /* SudokuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SudokuView.swift; path = Gameboards.playground/Sources/Core/Views/SudokuView.swift; sourceTree = SOURCE_ROOT; }; 0E96543523390C2A0075BF13 /* TicTacToeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TicTacToeView.swift; path = Gameboards.playground/Sources/Core/Views/TicTacToeView.swift; sourceTree = SOURCE_ROOT; }; 0E96544023390C360075BF13 /* Grid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Grid.swift; path = Gameboards.playground/Sources/Core/Grid.swift; sourceTree = SOURCE_ROOT; }; 0E96544123390C360075BF13 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Gameboards.playground/Sources/Core/Extensions.swift; sourceTree = SOURCE_ROOT; }; 0E96544223390C360075BF13 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Gameboards.playground/Sources/Core/Operators.swift; sourceTree = SOURCE_ROOT; }; 0E96544623390C470075BF13 /* Gameboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Gameboard.swift; path = Gameboards.playground/Sources/Gameboard.swift; sourceTree = SOURCE_ROOT; }; 0E96544723390C470075BF13 /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Gameboards.playground/Sources/Validation.swift; sourceTree = SOURCE_ROOT; }; A90F20AB2089229C00C3F6A4 /* Apple Symbols.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Apple Symbols.ttf"; sourceTree = ""; }; A90F20B0208A6D7200C3F6A4 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; A9DBACA6208EDE2600C03C12 /* WordsBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordsBoardView.swift; sourceTree = ""; }; A9DBACD62090BCFB00C03C12 /* BombsweeperBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BombsweeperBoardView.swift; sourceTree = ""; }; A9DBACD82090BD4B00C03C12 /* MemoryBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryBoardView.swift; sourceTree = ""; }; A9DBACDA2090BD6E00C03C12 /* ChessBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChessBoardView.swift; sourceTree = ""; }; A9DBACDC2090BD8A00C03C12 /* CheckersBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckersBoardView.swift; sourceTree = ""; }; A9DBACDE2090BDB500C03C12 /* BackgammonBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgammonBoardView.swift; sourceTree = ""; }; A9DBACE02090BDD700C03C12 /* FourBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FourBoardView.swift; sourceTree = ""; }; A9DBACE22090BDFA00C03C12 /* TicTacToeBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicTacToeBoardView.swift; sourceTree = ""; }; A9DBACE42090BE1500C03C12 /* SudokuBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SudokuBoardView.swift; sourceTree = ""; }; A9DBACE62090BE3800C03C12 /* DotsBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotsBoardView.swift; sourceTree = ""; }; A9DBACE82090BE5B00C03C12 /* GoBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoBoardView.swift; sourceTree = ""; }; A9DBACEA2090BE7700C03C12 /* MancalaBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MancalaBoardView.swift; sourceTree = ""; }; A9DBACEC2090BE9A00C03C12 /* PegsBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PegsBoardView.swift; sourceTree = ""; }; A9DBACF220936DA200C03C12 /* DoublesBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoublesBoardView.swift; sourceTree = ""; }; A9DD740A1C62A34600219C78 /* Games.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Games.app; sourceTree = BUILT_PRODUCTS_DIR; }; A9DD740D1C62A34600219C78 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A9DD740F1C62A34600219C78 /* BoardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardViewController.swift; sourceTree = ""; }; A9DD74121C62A34600219C78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; A9DD74141C62A34600219C78 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A9DD74171C62A34600219C78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; A9DD74191C62A34600219C78 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ A9DD74071C62A34600219C78 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0E6C166C233B989C003DE849 /* Boards */ = { isa = PBXGroup; children = ( 0E6C1687233BA8E7003DE849 /* Backgammon.storyboard */, 0E6C166D233B98B1003DE849 /* Bombsweeper.storyboard */, 0E6C166F233B98F3003DE849 /* Chess.storyboard */, 0E6C1671233B98FC003DE849 /* Checkers.storyboard */, 0E6C167D233B9E8F003DE849 /* Dots.storyboard */, 0E6C1685233BA8D0003DE849 /* Doubles.storyboard */, 0E6C1679233B9ACB003DE849 /* Four.storyboard */, 0E6C1681233BA8B0003DE849 /* Go.storyboard */, 0E6C167B233B9E82003DE849 /* Mancala.storyboard */, 0E6C1683233BA8C1003DE849 /* Memory.storyboard */, 0E6C167F233B9EA0003DE849 /* Pegs.storyboard */, 0E6C1673233B9AA0003DE849 /* Sudoku.storyboard */, 0E6C1677233B9AB7003DE849 /* TicTacToe.storyboard */, 0E6C1675233B9AAD003DE849 /* Words.storyboard */, ); path = Boards; sourceTree = ""; }; A9DBACA5208EADE500C03C12 /* InProgress */ = { isa = PBXGroup; children = ( 0E96540423390BA40075BF13 /* Alquerque.swift */, 0E96542523390C0E0075BF13 /* Battleship.swift */, 0E96542323390C0E0075BF13 /* Dominos.swift */, 0E96541723390BFC0075BF13 /* Ludo.swift */, 0E96542423390C0E0075BF13 /* Majong.swift */, 0E96542023390C0E0075BF13 /* Pilare.swift */, 0E96542123390C0E0075BF13 /* Tetris.swift */, 0E96542223390C0E0075BF13 /* Trouble.swift */, ); name = InProgress; sourceTree = ""; }; A9DBACA8208EDE2B00C03C12 /* BoardViews */ = { isa = PBXGroup; children = ( A9DBACDE2090BDB500C03C12 /* BackgammonBoardView.swift */, A9DBACD62090BCFB00C03C12 /* BombsweeperBoardView.swift */, A9DBACDC2090BD8A00C03C12 /* CheckersBoardView.swift */, A9DBACDA2090BD6E00C03C12 /* ChessBoardView.swift */, A9DBACE62090BE3800C03C12 /* DotsBoardView.swift */, A9DBACF220936DA200C03C12 /* DoublesBoardView.swift */, A9DBACE02090BDD700C03C12 /* FourBoardView.swift */, A9DBACE82090BE5B00C03C12 /* GoBoardView.swift */, A9DBACEA2090BE7700C03C12 /* MancalaBoardView.swift */, A9DBACD82090BD4B00C03C12 /* MemoryBoardView.swift */, A9DBACEC2090BE9A00C03C12 /* PegsBoardView.swift */, A9DBACE42090BE1500C03C12 /* SudokuBoardView.swift */, A9DBACE22090BDFA00C03C12 /* TicTacToeBoardView.swift */, A9DBACA6208EDE2600C03C12 /* WordsBoardView.swift */, ); path = BoardViews; sourceTree = ""; }; A9DBACA9208EDE3D00C03C12 /* BoardController */ = { isa = PBXGroup; children = ( 0E6C166C233B989C003DE849 /* Boards */, A9DD740F1C62A34600219C78 /* BoardViewController.swift */, A9DBACA8208EDE2B00C03C12 /* BoardViews */, ); path = BoardController; sourceTree = ""; }; A9DC4FA81C62B89700E7920E /* System */ = { isa = PBXGroup; children = ( A90F20AB2089229C00C3F6A4 /* Apple Symbols.ttf */, A9DD740D1C62A34600219C78 /* AppDelegate.swift */, A9DD74141C62A34600219C78 /* Assets.xcassets */, A9DD74161C62A34600219C78 /* LaunchScreen.storyboard */, A9DD74191C62A34600219C78 /* Info.plist */, ); name = System; sourceTree = ""; }; A9DD74011C62A34600219C78 = { isa = PBXGroup; children = ( A9DD740C1C62A34600219C78 /* Games */, A9DD740B1C62A34600219C78 /* Products */, ); sourceTree = ""; }; A9DD740B1C62A34600219C78 /* Products */ = { isa = PBXGroup; children = ( A9DD740A1C62A34600219C78 /* Games.app */, ); name = Products; sourceTree = ""; }; A9DD740C1C62A34600219C78 /* Games */ = { isa = PBXGroup; children = ( A9DD741F1C62A35300219C78 /* Gameboards */, A9DD74111C62A34600219C78 /* Main.storyboard */, A90F20B0208A6D7200C3F6A4 /* MainViewController.swift */, A9DBACA9208EDE3D00C03C12 /* BoardController */, A9DC4FA81C62B89700E7920E /* System */, ); path = Games; sourceTree = ""; }; A9DD741F1C62A35300219C78 /* Gameboards */ = { isa = PBXGroup; children = ( A9DD74201C62A35300219C78 /* Boards */, A9DD74281C62A35300219C78 /* Core */, 0E96544623390C470075BF13 /* Gameboard.swift */, 0E96544723390C470075BF13 /* Validation.swift */, ); name = Gameboards; sourceTree = ""; }; A9DD74201C62A35300219C78 /* Boards */ = { isa = PBXGroup; children = ( 0E96540323390BA40075BF13 /* Backgammon.swift */, 0E96540623390BA40075BF13 /* Bombsweeper.swift */, 0E96540823390BA40075BF13 /* Checkers.swift */, 0E96540123390BA30075BF13 /* Chess.swift */, 0E96540223390BA40075BF13 /* Dots.swift */, 0E96540523390BA40075BF13 /* Doubles.swift */, 0E96540023390BA30075BF13 /* Four.swift */, 0E96540723390BA40075BF13 /* Go.swift */, 0E96541423390BFC0075BF13 /* Mancala.swift */, 0E96541223390BFC0075BF13 /* Memory.swift */, 0E96541523390BFC0075BF13 /* Pegs.swift */, 0E96541323390BFC0075BF13 /* Sudoku.swift */, 0E96541623390BFC0075BF13 /* TicTacToe.swift */, 0E96541823390BFC0075BF13 /* Words.swift */, A9DBACA5208EADE500C03C12 /* InProgress */, ); name = Boards; sourceTree = ""; }; A9DD74281C62A35300219C78 /* Core */ = { isa = PBXGroup; children = ( 0E96544123390C360075BF13 /* Extensions.swift */, 0E96544023390C360075BF13 /* Grid.swift */, 0E96544223390C360075BF13 /* Operators.swift */, A9DD742C1C62A35300219C78 /* Views */, ); name = Core; sourceTree = ""; }; A9DD742C1C62A35300219C78 /* Views */ = { isa = PBXGroup; children = ( 0E96542F23390C2A0075BF13 /* BackgammonView.swift */, 0E96542C23390C2A0075BF13 /* DotsView.swift */, 0E96542E23390C2A0075BF13 /* FourView.swift */, 0E96543123390C2A0075BF13 /* GoView.swift */, 0E96543023390C2A0075BF13 /* MancalaView.swift */, 0E96543223390C2A0075BF13 /* MatrixView.swift */, 0E96543323390C2A0075BF13 /* PegsView.swift */, 0E96543423390C2A0075BF13 /* SudokuView.swift */, 0E96543523390C2A0075BF13 /* TicTacToeView.swift */, 0E96542D23390C2A0075BF13 /* WordsView.swift */, ); name = Views; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ A9DD74091C62A34600219C78 /* Games */ = { isa = PBXNativeTarget; buildConfigurationList = A9DD741C1C62A34600219C78 /* Build configuration list for PBXNativeTarget "Games" */; buildPhases = ( A9DD74061C62A34600219C78 /* Sources */, A9DD74071C62A34600219C78 /* Frameworks */, A9DD74081C62A34600219C78 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Games; productName = Games; productReference = A9DD740A1C62A34600219C78 /* Games.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ A9DD74021C62A34600219C78 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Jo Albright"; TargetAttributes = { A9DD74091C62A34600219C78 = { CreatedOnToolsVersion = 7.2; DevelopmentTeam = 965FHW76SM; }; }; }; buildConfigurationList = A9DD74051C62A34600219C78 /* Build configuration list for PBXProject "Games" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( English, en, Base, ); mainGroup = A9DD74011C62A34600219C78; productRefGroup = A9DD740B1C62A34600219C78 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( A9DD74091C62A34600219C78 /* Games */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ A9DD74081C62A34600219C78 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0E6C167E233B9E8F003DE849 /* Dots.storyboard in Resources */, 0E6C1688233BA8E7003DE849 /* Backgammon.storyboard in Resources */, 0E6C166E233B98B1003DE849 /* Bombsweeper.storyboard in Resources */, 0E6C1678233B9AB7003DE849 /* TicTacToe.storyboard in Resources */, 0E6C1672233B98FC003DE849 /* Checkers.storyboard in Resources */, A90F20AC2089229C00C3F6A4 /* Apple Symbols.ttf in Resources */, 0E6C167C233B9E82003DE849 /* Mancala.storyboard in Resources */, A9DD74181C62A34600219C78 /* LaunchScreen.storyboard in Resources */, 0E6C1674233B9AA0003DE849 /* Sudoku.storyboard in Resources */, 0E6C1670233B98F3003DE849 /* Chess.storyboard in Resources */, 0E6C1682233BA8B0003DE849 /* Go.storyboard in Resources */, A9DD74151C62A34600219C78 /* Assets.xcassets in Resources */, 0E6C1676233B9AAD003DE849 /* Words.storyboard in Resources */, 0E6C1686233BA8D0003DE849 /* Doubles.storyboard in Resources */, 0E6C167A233B9ACB003DE849 /* Four.storyboard in Resources */, A9DD74131C62A34600219C78 /* Main.storyboard in Resources */, 0E6C1684233BA8C1003DE849 /* Memory.storyboard in Resources */, 0E6C1680233B9EA0003DE849 /* Pegs.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ A9DD74061C62A34600219C78 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0E96543A23390C2A0075BF13 /* MancalaView.swift in Sources */, 0E96541923390BFD0075BF13 /* Memory.swift in Sources */, A9DBACED2090BE9A00C03C12 /* PegsBoardView.swift in Sources */, A9DD74101C62A34600219C78 /* BoardViewController.swift in Sources */, 0E96544923390C470075BF13 /* Validation.swift in Sources */, A9DBACEB2090BE7700C03C12 /* MancalaBoardView.swift in Sources */, 0E96543823390C2A0075BF13 /* FourView.swift in Sources */, 0E96541E23390BFD0075BF13 /* Ludo.swift in Sources */, 0E96544323390C360075BF13 /* Grid.swift in Sources */, 0E96541B23390BFD0075BF13 /* Mancala.swift in Sources */, A9DBACA7208EDE2600C03C12 /* WordsBoardView.swift in Sources */, A9DBACD92090BD4B00C03C12 /* MemoryBoardView.swift in Sources */, 0E96541F23390BFD0075BF13 /* Words.swift in Sources */, A9DBACDD2090BD8A00C03C12 /* CheckersBoardView.swift in Sources */, 0E96543E23390C2A0075BF13 /* SudokuView.swift in Sources */, 0E96541123390BA40075BF13 /* Checkers.swift in Sources */, A9DBACF320936DA200C03C12 /* DoublesBoardView.swift in Sources */, 0E96542723390C0E0075BF13 /* Tetris.swift in Sources */, 0E96544523390C360075BF13 /* Operators.swift in Sources */, 0E96541D23390BFD0075BF13 /* TicTacToe.swift in Sources */, 0E96540F23390BA40075BF13 /* Bombsweeper.swift in Sources */, 0E96542A23390C0E0075BF13 /* Majong.swift in Sources */, 0E96541C23390BFD0075BF13 /* Pegs.swift in Sources */, 0E96540D23390BA40075BF13 /* Alquerque.swift in Sources */, A9DBACE52090BE1500C03C12 /* SudokuBoardView.swift in Sources */, 0E96540B23390BA40075BF13 /* Dots.swift in Sources */, A9DBACE32090BDFA00C03C12 /* TicTacToeBoardView.swift in Sources */, A90F20B1208A6D7200C3F6A4 /* MainViewController.swift in Sources */, 0E96544823390C470075BF13 /* Gameboard.swift in Sources */, A9DBACE12090BDD700C03C12 /* FourBoardView.swift in Sources */, A9DBACE72090BE3800C03C12 /* DotsBoardView.swift in Sources */, 0E96540923390BA40075BF13 /* Four.swift in Sources */, 0E96543723390C2A0075BF13 /* WordsView.swift in Sources */, 0E96540A23390BA40075BF13 /* Chess.swift in Sources */, 0E96541023390BA40075BF13 /* Go.swift in Sources */, 0E96540E23390BA40075BF13 /* Doubles.swift in Sources */, 0E96542623390C0E0075BF13 /* Pilare.swift in Sources */, 0E96543623390C2A0075BF13 /* DotsView.swift in Sources */, A9DBACD72090BCFB00C03C12 /* BombsweeperBoardView.swift in Sources */, 0E96543B23390C2A0075BF13 /* GoView.swift in Sources */, A9DD740E1C62A34600219C78 /* AppDelegate.swift in Sources */, 0E96542823390C0E0075BF13 /* Trouble.swift in Sources */, A9DBACDB2090BD6E00C03C12 /* ChessBoardView.swift in Sources */, 0E96542923390C0E0075BF13 /* Dominos.swift in Sources */, A9DBACE92090BE5B00C03C12 /* GoBoardView.swift in Sources */, 0E96543D23390C2A0075BF13 /* PegsView.swift in Sources */, 0E96542B23390C0E0075BF13 /* Battleship.swift in Sources */, 0E96543923390C2A0075BF13 /* BackgammonView.swift in Sources */, 0E96541A23390BFD0075BF13 /* Sudoku.swift in Sources */, 0E96543C23390C2A0075BF13 /* MatrixView.swift in Sources */, A9DBACDF2090BDB500C03C12 /* BackgammonBoardView.swift in Sources */, 0E96543F23390C2A0075BF13 /* TicTacToeView.swift in Sources */, 0E96544423390C360075BF13 /* Extensions.swift in Sources */, 0E96540C23390BA40075BF13 /* Backgammon.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ A9DD74111C62A34600219C78 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( A9DD74121C62A34600219C78 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; A9DD74161C62A34600219C78 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( A9DD74171C62A34600219C78 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ A9DD741A1C62A34600219C78 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; 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; IPHONEOS_DEPLOYMENT_TARGET = 14.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; A9DD741B1C62A34600219C78 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; 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; IPHONEOS_DEPLOYMENT_TARGET = 14.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; A9DD741D1C62A34600219C78 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 965FHW76SM; INFOPLIST_FILE = Games/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = co.jo2.boards; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; A9DD741E1C62A34600219C78 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 965FHW76SM; INFOPLIST_FILE = Games/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = co.jo2.boards; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ A9DD74051C62A34600219C78 /* Build configuration list for PBXProject "Games" */ = { isa = XCConfigurationList; buildConfigurations = ( A9DD741A1C62A34600219C78 /* Debug */, A9DD741B1C62A34600219C78 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; A9DD741C1C62A34600219C78 /* Build configuration list for PBXNativeTarget "Games" */ = { isa = XCConfigurationList; buildConfigurations = ( A9DD741D1C62A34600219C78 /* Debug */, A9DD741E1C62A34600219C78 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = A9DD74021C62A34600219C78 /* Project object */; } ================================================ FILE: Games.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Games.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # Gameboard Gameboards built in a playground ### Games *Links send you to the readme for each game.* - [x] [Checkers](RM_Checkers.md) - [x] [Chess](RM_Chess.md) - [x] [Go](RM_Go.md) - [ ] Mancala - [x] [MineSweeper](RM_Minesweeper.md) - [x] [Sudoku](RM_Sudoku.md) - [x] [TicTacToe](RM_TicTacToe.md) ### Features - [x] Switch Player Per Turn - [x] Coordinate System - [x] Ability to Reset - [x] Available Moves : *hint system* - [x] Custom Colors - [ ] Piece Collection - [ ] Time Control : *timer for moves* - [ ] Simple Move AI : *need a friend?* ### Overall Validation - [x] Stop friendly fire : *checks if target piece is yours* - [x] Only play on your turn : *checks against pieces for player* - [x] No piece available : *checks for no piece* - [x] Off the board : *checks if target square is on board* ### Images [![Checkers](./images/checkers_sm.png?raw=true)](RM_Checkers.md) [![Chess](./images/chess_sm.png?raw=true)](RM_Chess.md) [![Go](./images/go_sm.png?raw=true)](RM_Go.md) [![Minesweeper](./images/minesweeper_sm.png?raw=true)](RM_Minesweeper.md) [![Sudoku](./images/sudoku_sm.png?raw=true)](RM_Sudoku.md) [![TicTacToe](./images/tictactoe_sm.png?raw=true)](RM_TicTacToe.md) ================================================ FILE: RM_Checkers.md ================================================ # Checkers ![Checkers](./images/checkers.png?raw=true) ```swift var checkers = GameBoard(.Checkers) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.66, green:0.62, blue:0.48, alpha:1) colors.foreground = UIColor(red:0.62, green:0.58, blue:0.44, alpha:1) colors.player1 = UIColor(red:0.8, green:0.13, blue:0, alpha:1) colors.player2 = UIColor(red:0.13, green:0.13, blue:0.13, alpha:1) colors.selected = UIColor.whiteColor() colors.highlight = UIColor.whiteColor() checkers.boardColors = colors // collection of moves let moves: [(Square,Square)] = [ ((2,1),(3,2)), // move ((5,2),(4,3)), // move ((2,3),(3,4)), // move ((4,3),(2,1)), // jump ((2,5),(4,3)), // cannot jump yourself ((2,5),(3,4)), // cannot land on your own piece ((1,0),(2,1)), // cannot land on another piece ] // loop moves for move in moves { do { try checkers.move(pieceAt: move.0, toSquare: move.1) } catch { error } } // show available moves for a square checkers.showAvailable((1,2)) checkers.visualize() ``` #### General - [wikipedia](https://simple.wikipedia.org/wiki/Checkers) - [x] Coordinates - columns 0 - 7 - rows 0 - 7 #### Validation - [ ] Win / Lose - [x] Standard Moves - [x] Diagonal -> 1 - [x] Diagonal Jump - [ ] Special Moves - [ ] Multiple Jumps - [ ] Promotion ================================================ FILE: RM_Chess.md ================================================ # Chess ![Chess](./images/chess.png?raw=true) ```swift var chess = GameBoard(.Chess) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.52, green:0.68, blue:0.43, alpha:1) colors.foreground = UIColor(red:0.48, green:0.64, blue:0.39, alpha:1) colors.player1 = UIColor.whiteColor() colors.player2 = UIColor.blackColor() colors.selected = UIColor(red:0.06, green:0.46, blue:0.71, alpha:1) colors.highlight = UIColor(red:0.06, green:0.46, blue:0.71, alpha:1) chess.boardColors = colors // collection of moves let moves: [(ChessSquare,ChessSquare)] = [ (B7,B5), // pawn leaps (C2,C4), // pawn leaps (B5,C4), // pawn takes pawn (B1,C3), // knight charges (C8,A6), // bishop advances (E2,E4), // pawn leaps (G7,G6), // pawn creeps (F1,C4), // bishop take pawn (A6,D3), // blocked move throws error ] // loop moves for move in moves { do { try chess.move(pieceAt: move.0, toSquare: move.1) } catch { error } } // show available moves for a square chess.showAvailable(A6) chess.visualize() ``` #### General - [wikipedia](https://en.wikipedia.org/wiki/Chess) - [x] Coordinates - columns A - H - rows 8 - 1 #### Validation - [ ] Win / Lose - [x] Standard Moves - [x] Pawn - [x] Rook - [x] Knight - [x] Bishop - [x] Queen - [x] King - [ ] Special Moves - [ ] Castling - [ ] En passant : *pawn take down in passing* - [ ] Pawn Promotion - [ ] Check & Checkmate ================================================ FILE: RM_Go.md ================================================ # Go ![Go](./images/go.png?raw=true) ```swift var go = Gameboard(.Go) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.36, green:0.29, blue:0.16, alpha:1) colors.foreground = UIColor(red:0.11, green:0.08, blue:0.03, alpha:1) colors.player1 = UIColor.whiteColor() colors.player2 = UIColor.blackColor() go.boardColors = colors // collection of moves let moves: [Square] = [ // moves (1,1), (6,7), (1,7), (6,0), (4,4), (4,5), (1,2), ] // loop moves for move in moves { do { try go.move(toSquare: move) } catch { print(error) } } go.visualize() ``` #### General - [wikipedia](https://en.wikipedia.org/wiki/Go_(game)) - [x] Coordinates - columns 0 - 9 - rows 0 - 9 - [ ] Adjustable Board Size : *9, 13, 17, 19* #### Validation - [ ] Win / Lose - [x] Standard Moves - [x] Capture - [ ] Special Moves - [ ] KO - [ ] Suicide ================================================ FILE: RM_Minesweeper.md ================================================ # Minesweeper ![Minesweeper](./images/minesweeper.png?raw=true) ```swift enum MoveType { case Guess, Mark } var minesweeper = Gameboard(.Minesweeper) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.5, green:0.5, blue:0.5, alpha:1) colors.foreground = UIColor(red:0.6, green:0.6, blue:0.6, alpha:1) colors.player1 = UIColor.yellowColor() colors.player2 = UIColor.blackColor() colors.highlight = UIColor.blueColor() colors.selected = UIColor.redColor() minesweeper.boardColors = colors // collection of guesses let guesses: [(Square,MoveType)] = [ ((4,3),.Guess), // guess ((9,0),.Mark), // mark ((7,4),.Mark), // mark ((4,1),.Mark), // mark ((4,0),.Guess), // guess ((0,9),.Guess), // guess ((2,7),.Mark), // mark ((6,9),.Guess), // guess ((1,0),.Guess), // game over ] // loop guesses for guess in guesses { do { switch guess.1 { case .Guess: try minesweeper.guess(toSquare: guess.0) case .Mark: try minesweeper.mark(toSquare: guess.0) } } catch { print(error) } } minesweeper.visualize() ``` #### General - [wikipedia](https://en.wikipedia.org/wiki/Microsoft_Minesweeper) - [x] Coordinates - columns 0 - 9 - rows 0 - 9 #### Validation - [ ] Win / Lose - [x] Standard Moves - [x] Mark : *flag* - [x] Guess - [x] Boom : *mine* ================================================ FILE: RM_Sudoku.md ================================================ # Sudoku ![Sudoku](./images/sudoku.png?raw=true) ```swift var sudoku = Gameboard(.Sudoku) // setup colors var colors = BoardColors() colors.background = UIColor(red:0.19, green:0.78, blue:0.71, alpha:1) colors.foreground = UIColor(red:0.09, green:0.4, blue:0.44, alpha:1) sudoku.boardColors = colors // collection of guesses let guesses: [(Square,Guess)] = [ // guesses ((0,2),"5"), ((8,8),"4"), ((6,5),"4"), ((2,7),"4"), ((3,0),"4"), ((5,3),"1"), ((6,2),"1"), ((7,8),"1"), ((2,5),"2"), ] // loop guesses for guess in guesses { do { try sudoku.guess(toSquare: guess.0, withGuess: guess.1) } catch { print(error) } } sudoku.visualize(CGRect(x: 0, y: 0, width: 198, height: 198)) ``` #### General - [wikipedia](https://en.wikipedia.org/wiki/Sudoku) - [x] Coordinates - columns 0 - 8 - rows 0 - 8 - [x] Solution Generator : *randomizes solution* - [x] Puzzle Generator : *hides numbers* - [ ] Alphabetical Option #### Validation - [ ] Win / Lose - [x] Standard Moves ================================================ FILE: RM_TicTacToe.md ================================================ # TicTacToe ![TicTacToe](./images/tictactoe.png?raw=true) ```swift var tictactoe = GameBoard(.TicTacToe) // setup colors var colors = BoardColors() colors.player1 = UIColor(red:0.43, green:0.98, blue:0.7, alpha:1) colors.player2 = UIColor(red:1, green:0.15, blue:0.18, alpha:1) tictactoe.boardColors = colors // collection of moves let moves: [Square] = [ (1,1), (0,0), (0,2), (2,0), (1,0), (1,2), (0,1), (1,1), // cant play filled spot (2,1), (2,2), // stalemate ] // loop moves for move in moves { do { try tictactoe.move(toSquare: move) } catch { error } } tictactoe.visualize() ``` #### General - [wikipedia](https://en.wikipedia.org/wiki/Tic-tac-toe) - [x] Coordinates - columns 0 - 2 - rows 0 - 2 #### Validation - [ ] Win / Lose - [x] Standard Moves ================================================ FILE: icon.ai ================================================ %PDF-1.5 % 1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream application/pdf icon Adobe Illustrator CC 22.0 (Macintosh) 2018-05-02T08:47:44-04:00 2018-05-02T08:47:44-04:00 2018-05-02T08:47:44-04:00 256 256 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8Aj35hfmFrXm/Wri4uLh10 1XIsbEEiOOMH4SV6FyN2Y/qxVG/lZ+YmseVvMVpGLh30a5lSK9s3YmMI7AGRVJorp1qPkcVfXH1q 1/38n/BD+uKu+tWv+/k/4If1xV31q1/38n/BD+uKu+tWv+/k/wCCH9cVd9atf9/J/wAEP64q761a /wC/k/4If1xV31q1/wB/J/wQ/rirvrVr/v5P+CH9cVd9atf9/J/wQ/rirvrVr/v5P+CH9cVd9atf 9/J/wQ/rirvrVr/v5P8Agh/XFXfWrX/fyf8ABD+uKvkH8yvzE1jzbrty8lw66RFIyWFkrERCNWor so2Z2pUsf1Yq78tfzE1jylrts8dw7aRLIqX9kzExGNmozqp2V1rUMP1Yq+vvrVr/AL+T/gh/XFXf WrX/AH8n/BD+uKu+tWv+/k/4If1xV31q1/38n/BD+uKu+tWv+/k/4If1xV31q1/38n/BD+uKu+tW v+/k/wCCH9cVd9atf9/J/wAEP64q761a/wC/k/4If1xV31q1/wB/J/wQ/rirvrVr/v5P+CH9cVd9 atf9/J/wQ/rirvrVr/v5P+CH9cVfI/5p/mHrHmnzFeRm5ddGtpXisbNGIj4IxUSMo2Z3pWp+Q2xV Bfl7+YWteUNat7i3uHbTWcC+sSSY5IyfiIXoHA3Vh+rFUb+Yf5WeYvK2sXIjs5bnRmdns76JGdPT JqqyFQeDqNjX5jbFUb+Vn5WeYPMHmCyu7uylttCtpVmubmZSiyKh5COMNQvzIoSNgMVfVf1W1/3y n/Aj+mKu+q2v++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/wCBH9MVd9Vtf98p/wACP6Yq76ra/wC+ U/4Ef0xV31W1/wB8p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/ wI/pirvqtr/vlP8AgR/TFXfVbX/fKf8AAj+mKvk38y/yr8weWtcu5beyludDlkaS0u4ULqqMeQST jXgydN+vUYq78tPyr8weZdctJbiylttDikWS7u5kKKyKeRSPlTmz9NunU4q+svqtr/vlP+BH9MVd 9Vtf98p/wI/pirvqtr/vlP8AgR/TFXfVbX/fKf8AAj+mKu+q2v8AvlP+BH9MVd9Vtf8AfKf8CP6Y q76ra/75T/gR/TFXfVbX/fKf8CP6Yq76ra/75T/gR/TFXfVbX/fKf8CP6Yq76ra/75T/AIEf0xV3 1W1/3yn/AAI/pirvqtr/AL5T/gR/TFXyp+af5WeYPL/mC9u7SyludCuZWmtrmFS6xq55GOQLUpwJ oCdiMVQX5d/lZ5i806xbiS0lttGR1e8vZUZEMYILLGSPjdhsKfM4q+uLr/eWb/Ub9RxV1r/vLD/q L+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2Ku xV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv8AvLD/AKi/qGKuuv8AeWb/AFG/UcVda/7yw/6i /qGKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMVVcVdirsVdirs VdirsVdirsVdirsVdirsVUrr/eWb/Ub9RxV1r/vLD/qL+oYq66/3lm/1G/UcVda/7yw/6i/qGKqu KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMVVcVdirsVdirsVdirsV dirsVdirsVdirsVUrr/eWb/Ub9RxV1r/ALyw/wCov6hirrr/AHlm/wBRv1HFXWv+8sP+ov6hiqri rsVdirsVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FVK6/3lm/1G/UcVda/7yw/6i/qGKuuv95Zv9Rv1HFXWv+8sP+ov6hiqrirsVdir sVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FVK6/3lm/1G/UcVda/wC8sP8AqL+oYq66/wB5Zv8AUb9RxV1r/vLD/qL+oYqq4q7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxVSuv95Zv9Rv1HFXWv+8sP+ov6hirrr/eWb/Ub9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7F XYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku xVSuv95Zv9Rv1HFXWv8AvLD/AKi/qGKuuv8AeWb/AFG/UcVda/7yw/6i/qGKquKuxV2KuxV2KuxV 2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMVVcVdirsVdirsVdirsVdirsVdirsVdirs VUrr/eWb/Ub9RxV1r/vLD/qL+oYq66/3lm/1G/UcVda/7yw/6i/qGKquKuxV2KuxV2KuxV2KuxV2 KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMVVcVdirsVdirsVdirsVdirsVdirsVdirsVUrr/e Wb/Ub9RxV1r/ALyw/wCov6hirrr/AHlm/wBRv1HFXWv+8sP+ov6hiqrirsVdirsVdirsVdirsVdi rsVdirsVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK6/3l m/1G/UcVda/7yw/6i/qGKuuv95Zv9Rv1HFXWv+8sP+ov6hiqrirsVdirsVdirsVdirsVdirsVdir sVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK6/3lm/1G/U cVda/wC8sP8AqL+oYq66/wB5Zv8AUb9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYqpXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1H FXWv+8sP+ov6hirrr/eWb/Ub9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpX X+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv8A vLD/AKi/qGKvkD8w/wAytd83axcSPcyRaQrstlYIxWNYq0Uuo+07D7RP6sVRv5V/mXrnlrzBZW8t 3JLodzKkN3aSMWRVc8fUQN9hkrXbr0OKvrL61a/7+T/gh/XFXfWrX/fyf8EP64q761a/7+T/AIIf 1xV31q1/38n/AAQ/rirvrVr/AL+T/gh/XFXfWrX/AH8n/BD+uKu+tWv+/k/4If1xV31q1/38n/BD +uKu+tWv+/k/4If1xV31q1/38n/BD+uKu+tWv+/k/wCCH9cVd9atf9/J/wAEP64q761a/wC/k/4I f1xV8g/mL+ZWuebdZuZGupI9HV2SxsUYpGIgSFZ1B+J2G7E/LpiqK/K78zNc8s6/ZwyXck2iTyrF d2UjlkVHPH1Iw1eLJXlt16HFX1p9atf9/J/wQ/rirvrVr/v5P+CH9cVd9atf9/J/wQ/rirvrVr/v 5P8Agh/XFXfWrX/fyf8ABD+uKu+tWv8Av5P+CH9cVd9atf8Afyf8EP64q761a/7+T/gh/XFXfWrX /fyf8EP64q761a/7+T/gh/XFXfWrX/fyf8EP64q761a/7+T/AIIf1xV31q1/38n/AAQ/rir5N/NT 8y9c8y+YL23iu5ItDtpXhtLSNiqMqHj6jhfts9K79OgxVB/l3+ZWu+UtYt5EuZJdIZ1W9sHZmjMR NGZFNeLqN1I/VirvzE/LXXfKWsXEb20kukM7NZX6KzRmImqq7CvF1GzA/qxVGflX+WmueZfMFlcS 2kkWh20qTXd3IpVGVDy9NC322elNunU4q+svqtr/AL5T/gR/TFXfVbX/AHyn/Aj+mKu+q2v++U/4 Ef0xV31W1/3yn/Aj+mKu+q2v++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/wCBH9MVd9Vtf98p/wAC P6Yq76ra/wC+U/4Ef0xV31W1/wB8p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/wI/pirvqtr/vlP+BH 9MVfIP5i/lprvlLWblDayy6Ozs9lforPGYiSVV3A+F1GzA/PpiqK/K78s9c8za/ZzSWkkOiQSrLd 3siFUZEPL04y1OTPTjt06nFX1p9Vtf8AfKf8CP6Yq76ra/75T/gR/TFXfVbX/fKf8CP6Yq76ra/7 5T/gR/TFXfVbX/fKf8CP6Yq76ra/75T/AIEf0xV31W1/3yn/AAI/pirvqtr/AL5T/gR/TFXfVbX/ AHyn/Aj+mKu+q2v++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/4Ef0xV31W1/3yn/Aj+mKvk381Py0 1zy15gvbiK0kl0O5lea0u41LIqueXpuV+wyVpv16jFUH+Xf5a675t1i3jS2ki0hXVr2/dWWMRA1Z UY05Ow2UD9WKvr66/wB5Zv8AUb9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq pXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv +8sP+ov6hirrr/eWb/Ub9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s 3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv8AvLD/ AKi/qGKuuv8AeWb/AFG/UcVda/7yw/6i/qGKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN /qN+o4q61/3lh/1F/UMVVcVdirsVdirsVdirsVdirsVdirsVdirsVUrr/eWb/Ub9RxV1r/vLD/qL +oYq66/3lm/1G/UcVda/7yw/6i/qGKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4 q61/3lh/1F/UMVVcVdirsVdirsVdirsVdirsVdirsVdirsVUrr/eWb/Ub9RxV1r/ALyw/wCov6hi rrr/AHlm/wBRv1HFXWv+8sP+ov6hiqrirsVdirsVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOK utf95Yf9Rf1DFVXFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK6/3lm/1G/UcVda/7yw/6i/qGKuuv 95Zv9Rv1HFXWv+8sP+ov6hiqrirsVdirsVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOKutf95Y f9Rf1DFVXFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK6/3lm/1G/UcVda/wC8sP8AqL+oYq66/wB5 Zv8AUb9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH /UX9QxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv+8sP+ov6hirrr/eWb/Ub 9RxV1r/vLD/qL+oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH/UX9Qx VVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv8AvLD/AKi/qGKuuv8AeWb/AFG/ UcVda/7yw/6i/qGKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMV VcVdirsVdirsVdirsVdirsVdirsVdirsVUrr/eWb/Ub9RxV1r/vLD/qL+oYq66/3lm/1G/UcVda/ 7yw/6i/qGKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqV1/vLN/qN+o4q61/3lh/1F/UMVVcVdir sVdirsVdirsVdirsVdirsVdirsVUrr/eWb/Ub9RxV1r/ALyw/wCov6hirrr/AHlm/wBRv1HFXWv+ 8sP+ov6hiqrirsVdirsVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FVK6/3lm/1G/UcVda/7yw/6i/qGKuuv95Zv9Rv1HFXWv+8sP+ov 6hiqrirsVdirsVdirsVdirsVdirsVdirsVdiqldf7yzf6jfqOKutf95Yf9Rf1DFVXFXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FVK6/3lm/1G/UcVda/wC8sP8AqL+oYq66/wB5Zv8AUb9RxV1r/vLD/qL+ oYqq4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqpXX+8s3+o36jirrX/eWH/UX9QxVVxV2KuxV2KuxV 2KuxV2KuxV2KuxV2KuxVSuv95Zv9Rv1HFXWv+8sP+ov6hir5H/MP80/MXmnWLkx3kttoyuyWdjE7 Inpg0VpApHN2G5r8htiqN/Kz80/MHl/zBZWl3ey3OhXMqw3NtMxdY1c8RJGWqU4E1IGxGKvqv61a /wC/k/4If1xV31q1/wB/J/wQ/rirvrVr/v5P+CH9cVd9atf9/J/wQ/rirvrVr/v5P+CH9cVd9atf 9/J/wQ/rirvrVr/v5P8Agh/XFXfWrX/fyf8ABD+uKu+tWv8Av5P+CH9cVd9atf8Afyf8EP64q761 a/7+T/gh/XFXfWrX/fyf8EP64q761a/7+T/gh/XFXyb+Zf5qeYPMuuXcVvey22hxSNHaWkLlFZFP EPJxpzZ+u/ToMVd+Wn5qeYPLWuWkVxey3OhyyLHd2kzl1VGPEvHyrwZOu3XocVfWX1q1/wB/J/wQ /rirvrVr/v5P+CH9cVd9atf9/J/wQ/rirvrVr/v5P+CH9cVd9atf9/J/wQ/rirvrVr/v5P8Agh/X FXfWrX/fyf8ABD+uKu+tWv8Av5P+CH9cVd9atf8Afyf8EP64q761a/7+T/gh/XFXfWrX/fyf8EP6 4q761a/7+T/gh/XFXfWrX/fyf8EP64q+VPzT/NPzB5g8wXtpaXsttoVtK0NtbQsUWRUPEySFaF+Z FQDsBiqC/Lz80/MXlbWLYyXktzozOqXljK7Onpk0ZowxPB1G4p8jtiqC/ML8vda8oa1cW9xbu2ms 5NjfAExyRk/CC3QOBsyn9WKo38rPy71jzT5itJBbumjW0qS3t46kRlEYExqxFGd+lB8zir64+q2v ++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/wCBH9MVd9Vtf98p/wACP6Yq76ra/wC+U/4Ef0xV31W1 /wB8p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/wI/pirvqtr/v lP8AgR/TFXfVbX/fKf8AAj+mKu+q2v8AvlP+BH9MVfIP5lfl3rHlLXblJLd20iWRnsL1VJiMbNVU ZhsrrWhU/qxV35a/l3rHm3XbZI7d10iKRXv71lIiEatVkVjsztSgUfqxV9ffVbX/AHyn/Aj+mKu+ q2v++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/4Ef0xV31W1/3yn/Aj+mKu+q2v++U/wCBH9MVd9Vt f98p/wACP6Yq76ra/wC+U/4Ef0xV31W1/wB8p/wI/pirvqtr/vlP+BH9MVd9Vtf98p/wI/pirvqt r/vlP+BH9MVd9Vtf98p/wI/pir5H/NP8u9Y8reYruQ27vo1zK8tleIpMYR2JEbMBRXTpQ/MYqgvy 9/L3WvN+tW9vb27rpquDfXxBEccYPxAN0LkbKo/Vir//2Q== uuid:C1BCCE1871B8DB11993190FCD52B4E9F xmp.did:1a1fa89a-cf96-4c07-bde1-c245ef28d4f5 uuid:61b4c6e7-933f-aa45-a8a5-f20311e424eb proof:pdf uuid:3475da6f-f821-6b47-9d03-3157c8745d3c xmp.did:3eb636b9-3c86-4540-8758-407a64fdb135 uuid:C1BCCE1871B8DB11993190FCD52B4E9F proof:pdf saved xmp.iid:1a1fa89a-cf96-4c07-bde1-c245ef28d4f5 2018-05-02T08:47:41-04:00 Adobe Illustrator CC 22.0 (Macintosh) / Document Mobile 1 False False 1024.000000 1024.000000 Pixels Cyan Magenta Yellow Black Default Swatch Group 0 White RGB PROCESS 255 255 255 Black RGB PROCESS 0 0 0 RGB Red RGB PROCESS 255 0 0 RGB Yellow RGB PROCESS 255 255 0 RGB Green RGB PROCESS 0 255 0 RGB Cyan RGB PROCESS 0 255 255 RGB Blue RGB PROCESS 0 0 255 RGB Magenta RGB PROCESS 255 0 255 R=193 G=39 B=45 RGB PROCESS 193 39 45 R=237 G=28 B=36 RGB PROCESS 237 28 36 R=241 G=90 B=36 RGB PROCESS 241 90 36 R=247 G=147 B=30 RGB PROCESS 247 147 30 R=251 G=176 B=59 RGB PROCESS 251 176 59 R=252 G=238 B=33 RGB PROCESS 252 238 33 R=217 G=224 B=33 RGB PROCESS 217 224 33 R=140 G=198 B=63 RGB PROCESS 140 198 63 R=57 G=181 B=74 RGB PROCESS 57 181 74 R=0 G=146 B=69 RGB PROCESS 0 146 69 R=0 G=104 B=55 RGB PROCESS 0 104 55 R=34 G=181 B=115 RGB PROCESS 34 181 115 R=0 G=169 B=157 RGB PROCESS 0 169 157 R=41 G=171 B=226 RGB PROCESS 41 171 226 R=0 G=113 B=188 RGB PROCESS 0 113 188 R=46 G=49 B=146 RGB PROCESS 46 49 146 R=27 G=20 B=100 RGB PROCESS 27 20 100 R=102 G=45 B=145 RGB PROCESS 102 45 145 R=147 G=39 B=143 RGB PROCESS 147 39 143 R=158 G=0 B=93 RGB PROCESS 158 0 93 R=212 G=20 B=90 RGB PROCESS 212 20 90 R=237 G=30 B=121 RGB PROCESS 237 30 121 R=199 G=178 B=153 RGB PROCESS 199 178 153 R=153 G=134 B=117 RGB PROCESS 153 134 117 R=115 G=99 B=87 RGB PROCESS 115 99 87 R=83 G=71 B=65 RGB PROCESS 83 71 65 R=198 G=156 B=109 RGB PROCESS 198 156 109 R=166 G=124 B=82 RGB PROCESS 166 124 82 R=140 G=98 B=57 RGB PROCESS 140 98 57 R=117 G=76 B=36 RGB PROCESS 117 76 36 R=96 G=56 B=19 RGB PROCESS 96 56 19 R=66 G=33 B=11 RGB PROCESS 66 33 11 Grays 1 R=0 G=0 B=0 RGB PROCESS 0 0 0 R=26 G=26 B=26 RGB PROCESS 26 26 26 R=51 G=51 B=51 RGB PROCESS 51 51 51 R=77 G=77 B=77 RGB PROCESS 77 77 77 R=102 G=102 B=102 RGB PROCESS 102 102 102 R=128 G=128 B=128 RGB PROCESS 128 128 128 R=153 G=153 B=153 RGB PROCESS 153 153 153 R=179 G=179 B=179 RGB PROCESS 179 179 179 R=204 G=204 B=204 RGB PROCESS 204 204 204 R=230 G=230 B=230 RGB PROCESS 230 230 230 R=242 G=242 B=242 RGB PROCESS 242 242 242 Mobile Color Group 1 R=136 G=168 B=13 RGB PROCESS 136 168 13 R=127 G=71 B=221 RGB PROCESS 127 71 221 R=251 G=174 B=23 RGB PROCESS 251 174 23 Adobe PDF library 15.00 21.0.0 endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 12 0 R/TrimBox[0.0 0.0 1024.0 1024.0]/Type/Page>> endobj 8 0 obj <>stream HUKN0s8_lo1UAU 6IHU{I-BhІ'V?gYAe~}G[,FIܠޟK)FήQMzvyR'Q j1Az)+$Bte%s*˽6{}X%-7erCYϖ8'Ns b 5. rCkAg -z5/ۙeml&MسұbY, Mo_OfNԅЛ7W`~żx_a?Og fgt endstream endobj 12 0 obj <>stream 8;Z]"d1(a_$pt9B51ui4G+C/@7i`"o`Y`3E0a3[o(U9:e,VZ7H ^`NN7bp'L%G'6q*2(>tZK'*j3/Eh0?nFu=I`]o")?nK(2e'^h]oOQD5ZDZnX0meeL 'XO_$8MPRLSlTHA1sp$FX>FZmU7PP&>VAFGGT;Vp@WI9):lk>!+UsB`gBQm )ZUeC!kJ~> endstream endobj 13 0 obj [/Indexed/DeviceRGB 255 14 0 R] endobj 14 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 5 0 obj <> endobj 15 0 obj [/View/Design] endobj 16 0 obj <>>> endobj 11 0 obj <> endobj 10 0 obj [/ICCBased 17 0 R] endobj 17 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 9 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 17.0 %%AI8_CreatorVersion: 22.0.1 %%For: (Jo Albright) () %%Title: (Untitled-3) %%CreationDate: 5/2/18 8:47 AM %%Canvassize: 16383 %%BoundingBox: 221 -804 805 -220 %%HiResBoundingBox: 221 -804 805 -220 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 13.0 %AI12_BuildNumber: 249 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 -1024 1024 0 %AI3_TemplateBox: 512.5 -512.5 512.5 -512.5 %AI3_TileBox: 224 -868 800 -134 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI17_Begin_Content_if_version_gt:17 1 %AI9_OpenToView: -378.601966752517 1.64106766565146 0.8542 1668 998 18 0 0 6 42 0 0 0 1 1 0 1 1 0 0 %AI17_Alternate_Content %AI9_OpenToView: -378.601966752517 1.64106766565146 0.8542 1668 998 18 0 0 6 42 0 0 0 1 1 0 1 1 0 0 %AI17_End_Versioned_Content %AI5_OpenViewLayers: 7 %%PageOrigin:424 -616 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 20 0 obj <>stream %%BoundingBox: 221 -804 805 -220 %%HiResBoundingBox: 221 -804 805 -220 %AI7_Thumbnail: 128 128 8 %%BeginData: 6076 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FFA852275252FD37FFA852275252FD37FFA87D275252A8FFA8FD06 %27FD35FFA852FD0527A8FD34FFA852FD0527A8522752275227277DFD34FF %7D27522752272752FD34FF7D275227522752522727275227272752A8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF2727275227272752A8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF52272752FD042752275227 %52275252FD05FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFF %FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFF522752275227 %5252FD05FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8 %FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFF5227522752275227 %522727522727277DFD34FF7D2727522727277DFD34FF7D27275227272752 %FF522727522752FD36FF522727522752A8FD35FF522727522752A8FFFF52 %52527DFD38FF7D52527DA8FD37FF7D52277DA8FD04FFA8FD3BFFA8FD44FF %A8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD %3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FF %A8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD %3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FF %A8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD %3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FF %A8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD %3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FF %A8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD %3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FF %A8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD %3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FF %A8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD %3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FF %A8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD %3BFFA8FD04FFA87D52527DFD37FFA87D52527DFD38FF7D525252FFFFA827 %2752272727FD35FFA8272752272727FD35FFA852FD0527A8522752275227 %277DFD34FF7D2752275227277DFD34FF7D27522752272752275227272752 %2752A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8A8FD0527522752 %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF52FD042752272752 %27522752275252FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFF %A8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA852 %27522752275252FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFF %A8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA852 %2752275227522752FD042752277DFD34FF52FD042752277DFD34FF7DFD04 %27522752FF272727522752FD36FF272727522752FD36FF522727522752A8 %FFA85252277DA8FD36FFA85252277DA8FD37FF52522752A8FD04FFA8FD3B %FFA8FD44FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8 %FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3B %FFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8 %FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3B %FFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8 %FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3B %FFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8 %FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3B %FFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8 %FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3B %FFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD06FFA8 %FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3B %FFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8 %FD3BFFA8FD3BFFA8FD06FFA8FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3B %FFA8FD87FFA8FD3BFFA8FD3BFFA8FD87FFA8FD3BFFA8FD3BFFA8FD06FFA8 %FD3BFFA8FD3BFFA8FD08FFA8FD3BFFA8FD3BFFA8FD05FF7D527D7DFD38FF %7D52527DFD38FFA852527DFFFFA852FD042752FD35FFA852FD042752FD35 %FFA852FD0527FF7D2752275227277DFD34FF7D2752275227277DFD34FF7D %275227522727522727275227272752A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FF2727275227272752A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FF52272752FD04275227522752275252FD05FFA8FFFFFFA8 %FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFF %FFA8FFFFFFA8FFFFFFA8FFA85227522752275252FD05FFA8FFFFFFA8FFFF %FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8 %FFFFFFA8FFFFFFA8FFFF5227522752275227522727522727277DFD34FF52 %2727522727277DFD34FF7D27275227272752A8272727522752A8FD35FF27 %2727522752A8FD35FF522727522727A8FFA852272752A8FD36FFA8522727 %52A8FD37FF52272752A8FF %%EndData endstream endobj 21 0 obj <>stream %AI12_CompressedDataxGvȬ{mޱ5tif$A$h ZGfdUlvs9?Eß/޿}˃?=}~v5j/ݷ޾!jp߽?g?:wo~O{^'럧?o~o_:9ywozw7o_n;~尌xo^~Qwoo/_~o}xsәx=ܼ~ zë/_x2q)}qݫ_w_K{*+/|ɿVe7/~>t!򛿸?>{˾{/ =bS91N˯y)]_XM 5#%Ο__훗16}Ǚ|廿{p5f~'t;w{^+wMbv_,6/n4Qot7_/~rj1loyoW/r[qZTt5i<׹Ne>WK-IY !>ܞz:=G~7Zq~{߿ꋾ%_~6sMtkQhz4^}u"іK{]i!5kZ??m2$fNt^-vvgUoXG@R.3wĴሮ9,_w:~ͯg/D_~>_8ūo~޽}gZײ4״\ic2ٛepm)e^gh>[t:Tˊ]޽Ji>5zyrc\uܭ7 eyUGD/rdjZ=[zͭܦAǝ=eγE8A}q[otNuMGEGiMЃe|:R˘򽎻|C/׼hYcV9ϔ!ǽtFu%5-n?ra,zxgoyO?^[77s}h!oz337Y>q"EŔ9ɘhw"[:D &H'~q'qc2^ii^*&UAW(ںowsoUw^ ]b!a՟H"7*N.#?χ/(%Y(Em7ӽ$:W}EU3vDCi(b r_ǚEmzWz 4GYu,މ'YpyBe-\[pւ/"F4ϵH^7iMRӢw.k;]뻛A'W}?4IU0Ŭ7xiYF- p "Bz) :Aww@;)BP?Zpc3_|` O&ob ]5/O֝vσln&o|Vx6g{^߹nyAےohu>>h4i%`</1wL?'̃E\hxc-tZ"h|BuK}I};{hSj%Ysn.q/+ۄՋ^X~屭$3g"u`:y'Ѝ=t/!ܖ6xjsc wgNtw ۓn0GnOy/gjhSӑ;=zY"k]9kBH&xFҽ(Vu{.zY^DȚ1rH-M,7u'dƵ%F 5QO~q5 ^@D'õa`l [- 0[<GcqUpV82e d;鍵kź`U++,BXQԮML]FXsZQH<<O@遇'zIGM?½ѵQ{ρ$b> um!;EV̔u0U'a{GOhD$w>&߽y_~?8d8]Xa>u,Ii$ũɣ:Jj<mU[@ ц5iWA ΊTXBH)1*KDIi7f2PK6dlH`fwG9p?tQ0u$A!B(h8 &ˊM^~gaq >> +׵{< da[Cd[#ZnZQv| hSsY}qn ߃:>~ Ok6V{쳃m6XbB ?t-|dۦrWݮR6[P';io-sҺ݅q]N+jTmNw+wN߻{gz'yqXȳݻ8wqŭNp9pvW{qS;7{?n-ܹ n8uíݛJ^!ީG]/{lǙ[58.HCv\8X.8m7mnl r< @է|vm3 C7g| O9g? l~L>)6RFϵq#33b#E,cG|b z~&e8}7$7|bϝX|Blá :0hp:M:YcG}0b8zDNn:Gt`kKopxLoQ{ç?xp? }G9z|<9֡ïǙqw?T1<(}Gv|.€K/ѿH@U9`&mnņCG9Ѝy EJ@0RAb&caM/\WHGd lSegD<#{6 1i&K#w [o8flY![^cH8!&BL8=\Sp/٣~O;څcy| O?~>e9_IZ> :+xJe4-gK]2|Jr'wiǴ&TK70O3px | <x|vs%_c7\||99p9ww kZϏGt ?c| ϝa/7J.sQ"_GwN}#-Hzg*hvrzSOin#mxpdBgn[ۖSDB|ijsJ|Ιg=/FLE&~}mX??~ 2a/x,4w!XEr6r }k ak_3$D/*͞tWk͹uk6#*Djwl߹'w1f.hvlGl1-S cc8a=sαg\C/W?.]4{"MqS-;n8%}qcwG?|O=>$@#1 "+y8'lv1.Ɗp$3my0sY]Ǩz6ȟYl^Lv50jF'tX]y~g7 ; VeS(Ch"XҢKԾB ^lg$U0L&u~Wm){1W QQ~jMeMyb:}v#/mzcyr'Ǚb?\W^x"8z+sw7zUM;u:Nz5ცr핺KLG].4ۡ+qG,kjmZQ{\+_/[z3UY:^`ѾîQXݨ6B@8LhqsNUr+\6*# `ZJRWp9oYŸQRd\RmܐZi&㖎(&-e⧻C<~nmǎ?EGbN,`8e#؜x%a n;d=6KddlK&ɤ jޞ\[#6j˙et*-QiyN} (w9,hY᤻|\][e|e`Z(h:/}nùo^5ԭaG58G~hr{+UVʄ^ReՖW-]h֬\C4")Q+b%[I/(Ȇ_ֹкpyi"wg?[Ê9mS4CZ  gu k7F% ^ɦ8GݱVfՎ6\gtLE.؋:^֩c 7nv)N5\^bp\bw[FS*}|YƧӱ`c>ϏX Gǰ+3+;)wu0.;N.c(3~ټO n{\۫f!oCGmCRsģX11xŋl. Ab08~bZ|a0QOˎp=^T qʸY)pyd>wM<.|;<|d;47Gi蕙Yu8l=n=8;hٶE l^yF~C//#\96yF:},1"Yr8ISD<`l3vg042F0w,)O1 gⅻ`؇Y?{A/Jw^QSq ճpnGS8q >{|s~U̞Sڿ.|8ca_x}yi'wm2nON;?l;BG}ZŻ=[H퐟gӗ2NgʴJ}5K~›˽JuybFuuůة\\zG2ڣGx>/'k$I',eZ<2RidII&dA.9>W|M7Q] zbClu9,Y'sc>|-;flYݼȞy٣^wȥ}|Eo̚%C>m>%Dm ;qvhn]>t-5Zhky`5'Abd d.$BuŌ?C ҨMzVW z{?]퓎Rv]},-H<NDQ>5;mi>["|e֔ϲsncohw awa箰#l|`GX9Y-_/8e愽9G<<ȣ3t _zCc>_|eܮyoN=* c|miѨݎc] r5'z9G3=cins }|ʝfzkIDn7W^⣟x)8H%$r?6#[ucl-h gJe?}p&L.J3Nw +mӴ%1r[ NϛL;e7Nwt?'CkFgQ$F*CML|x}=wj3>OxWĖe/)THJU_)8ƺ&ZE'҆K.|{e|;jgFD>+*ƀ\X~n˹aޡ K;5Sتo]x;}%^ƟN^[}xg!ސ^;fhYK=?uھl5+__[ӮSCV]1BfCUFdqT/;i쉣7TX*9sBOݾ lO ڍ[΀'9,SSC$7DS;2)[E% nz v:v|zvqiwbNkԾ,${Ϊ2˙u:oxmSC{~ٟv!7yNf~7֯a`1 O4)v}Ӿř>67:W1g[׾5L #.)w_WS=z%,nU9[E6ʶ[^iDkQ>*!qgl1 ?F{}wᇷ7.vLӃ.=3-ٷbR$(1xD40|@ÏIPK? )ۯ~_W oZVlS󸢃իmQ˺^_Wp,-~/Wzp+q}Z}|.-~CJ `O{a72F emi*3J:W'k &Oӣ'͒uť6-U94}\.G ayvs9w$2Wl1#c[2țెz;~1Hrs|]>m-78y>Nzh@ \yҥWsOuz^-Z&sycdzRM{*^<=Nvz]܏Ejx<艛;sZ7<ԩO39-&k+es51uIojQX2y%B^WWx]B.XO{q>wvMO[xvXݸ9hQɰܻQ)/~QVgKee;/ Ե>̉zLݹT')\F'xpM<ɷYn|zBEb.&mQRxk 0l]͟ˌc3'OzO\"%zaG-{_ͥٺ)Ln$=iċ=/9Y=G#zPg'(x i6ww{nqQUR,9]إ?T?T/T?gRL IaG q\cb6䵇'=C.C)y?{@4ɋ;~ڨw|?~ݫ/߿zŻ;Mׇ]_z7//~??'}ҟrwoS]$~|?:k-ڴm|U eR!d,;] lK^SHj`EbrJb.UؘFҳ*Pb:W2e[ֈ^i_t5iu.};74v7JUj1ANzwAMϥ]ZȺuE{~Ф]aHqqfqYVwb4kqo=DgMi]]KW;r]R^#EtMK-J1bw#JնޘtZĤA,[Z"3{^IB.te 8!"8^[rX+=Ө2Y4[.b:!b*eT.B ү ,G_*WsJb EK*[\̬^Jee '2}FӺsN/i2ښk΍GtB/\ -WZџMf!yShmDZE +<~('ͼ6(AUFIhT^n]vZ[ 25 tƼ&b'c,4FYdF:>I|h\DuI7֖ĈEbg5V4HsL ơAt 8-G̾̄*)VD1E/cm;'I9]igR =u`3Ghy!'qu%-y([;&2c [l0Fh9|Sf6z7 /(֐Qڵz^IeQ6]6M>>o𬸬N'GepubxUf5hq2PQkS*ֻy;@!NεLLEDv6SrakvZŻ5V5()Q6:!VY'4-sbqѼ.#o#'6J^★š_L_5EL/\{ MuU@ ]-_D{Aǝ3<`L^ڂ:l$K)Jͱ:"'QҕLɕ2<&uԣ0J@U^#DFH91ە?SH;zel ۘD&*ƙҌ& I1UD'E%9MiڃLc٢{kWګeʡ﬷ s7f}C,'zٮ U,lVJ&М+GʊvEҧR 5Co\Ckʤn(Zq.Lan.Ԥ &M;{4CHKӍi}%"`d={MX|fKd D[dMSw9Z-24B/!7}u#UZ I0CO+ h4u57tY W`0}Juf)SѦͬiDVIƈyZ\urbXD\vZ0/$dGb80U]M$nki:eP7GTUS:G'aۍEj7]ѤZ3Ҥu8A(wtidz 7֓+B+/X5qyY 7i1G81FԎRo04cш A"'f|3)>V8]9VIjtKyVFҭ3UnPa@Q3JHKieNY(\fEAO*2i~G,HDtDILvǘ% KxmV4󊒓ҋ8/>BL>#g4 sy qT9fD5Q!ΨpcftJ$JwШmtt^3&,T*L'KTWWbf7rLB@(d'FI,In1bA% c#9Ki(rFR GORю*-E6cDCgb%إWez X(* jG]@Ăxء;bAP4Źly\[Y^e&XFz$ 3!irW*tgЂ)g* NPՈ,BqQ?d!`#tV̗_FN8ik;UNwԓdT0&>MFR}p`p7j&pfd_2{D 4b#4F;m?)GqN \k%(M%\UV,g! w ,]aŞLc;ԕp);ڔZ"S44MWzEfOTJ3>hTH( {i ۆQ޳zD&L.5FGT Mصe욘vkՙKL./ɤzl x{N} Q, ?ţ,g4*>_ն12}]aJ\}0_jqڧ$bqӊKh1pXxuSQ:uD6@ɴHoAo!DoxOĆ'FmTR+R8آ_Kwz~״kU_F _I qb4 BYY%TB *^۝f[}x70J:7żpbȾA@,a-Et}"hh+a`\&9S-:)$3M 8"ۉm i'K-Rr2iHUɹ+K#S,̰5U T?ʽkHH\`*g" N}{GLx^3 |'s(7ReuT^ڕe q-F_4!k&GC҈k 1^сFEeƕFӵ͐L≺N^X&AXuNU*h[ $B>Rޟ01)<uMX0DuRB!Æ&jW. *^mJ4$DQa.c*~sX rغ/ngh15b.yr[cs f~*q Ë02)+K+*~j`jCgRTgVq|2\GqU`Xp{`;{elf XX-ZIdS0%f˥i* 2x W[3K+Bkhx >A|ɌGh4B4lBGU0*<):!:&!50t`zi30`Nt~Gz3G-tF4~`4٠T~ҧ|6l'6 uEj~F ̈́\Hǜ)f^TF'rL9Nv4(;'yͤԘ1Gms 'n}l&c|<ur\g)fßWo~w_~o޾G;O8> S2I ̖#`>4(*iD5Y&@`.,".vIiZv+8E Z=,s%WS,D{sE? @pz\i`-ĀVO`خG YJ $N aFb8 g::'QV֘ #&i/8V(q1YN,L(1KDVm0![ǸP]CMWCm >=N`SNR_zlNfT$)bOІ$:h-l*8JEus  I& »\pXd"1 _cQ"+tG56%.Hw~݉1];yՆQܴ>A WTu%L&JXv!eg /ZPxEG/,Z(,9lln!'YP/M'~%H%$8E3(D+,a'27'D) =F~20!l !X|87Stu&L/,Hp WOCvfC E?i4 ƯF@ v])|r݇m*|'ؚ$F -+O1`&'x&ؐyݛÓ)bdabTɣ2Fiz_'yl5bɉ'9Qq JT1/&jI+ @¢fG&hlI:od Pqd"VJ]eqtVpľ0W,b!(֡A0'% hTZ6DT@E&`L[ ~1T%U=n߸KZL)p?iA&Lr]Rk7ppבni(%Op (:61G-B"Av 3;.XQBNf$#zA"N5Ceber$QAgJ P,#l_%\%U8ari,vRB %ze#ECR 0K"ny2yxS 7Iv+b/$#4l#ZHB =ZP j5 BWyܤ/(L Ӎ)>7ȈeF,nDX0OEO( *H1$Q4ks]˕fOD!dU(q4<`, <dbI.e?Lvm4Ĺ]ݱ#tך.op%݀+njdI>#3cYjch6ЌD;m4}b \+Js,-8<{y/xg;#05f i(l ǹ-b Tm=Q'])!?f}-],dcb^1MipW`j3XT7O0*̤kElc$x6g>OHsqN9v:U7ZmeyAzcbFhjzD>@Y6Po\!-H帼(67t6ͩ="m4Qr݉A6PJgo')ˎNmJT_boAl<_Aյ{D4qk24h봂-I)`) J '${uъ'"ͥ,8J7mJ\49\P\$sd qD x7_痉j xt"p(YjNb[S! VS828@ U} \MIbӀ1` 6,L3B4Ǩ 4_qLDoH%'3X Kz*Pp-`(f-sB"#b^MpMzk"w\moV#u%SM ipw0-h .] $oJw$A)~ ѵMˬ-p!'RB$cIע[5A^i[F~K #2 ='pȋho h0 N PqB@Yz0Pl=mD8cgkLj>_/=jYH ]55GA5WYI/u{& 1j`Crn{vx:bt@g鋃7/cRA;ZR JuX#&ɲx!R-m2Z#`[\ SZ/fO )< +>xvq2`pX8T+.ts]Kp'FW/Sآ媫-ɘ ۮ9\QU2P£xalLQg4Bᑕ Ӯg >H BA/O_y=QG22Fi3r֬ga-I٫0xbml𠱋xBE”+1vGXoNkSWb$- )9/:L c3+bO'wȏxYCd)>\GܭN@9:P%Mw}Tpە^v@il&fe̸vu[D7IZfdh>׆$c_2iZX"#%,x9 (:P 7~ꌛض+qUabgA<R?/9׋L G~ۘXL, u ˎJ_0E7>GY#pqIbQ:jWء6-^N;$sg5N#n8@$IA-}*2-!Z˅C<$@Q[ hCP+GC]&d}/W""T"BBm% Z 422!!*}2B&<#x)l-p2k-T1zQ dXp~5TW@.@p٩}QhӀ^즧#w `7\xb1bM[t~Lx>Bi0`19j^CI͎|=BlݫBۋH#k3$#pVRlOt8DY00]<2VKn\Y?JF(l'՚T,I/N$ $¦e| 7̀ei41gB"C)XZa)R'Izej¼5@O aabWΏ1Rii-b$_ iͨn̯%1/Hr3)4 JI beJ3  5@FFIfģ_~Sc}t3%$zU yLCDp6f<>'xC$:PrXXY.`bx$*o,/_;2@ ò  Nm1B"YRwnbVdP=>@$<,8 #Qyz%J?6>| b@0}" f7!ɨæMVg4O9tB2>ۣOIF4v";:uTP|'<,1"e˔DJ&[DȘ4uu.".ɉpPohed3lergVzw18 ufg ;]ZwhFg'W1h).IfnɅ%|nUlmmR.$wN >h_cP'v@8U4(d'.Yؖ 5Sb+ K/"L9`֠f=hhyyH4g0Ȩ#w\RbWyO:r9:ug8InVȆ*yr}#TY>y,q#+  2T L!Fiب]XAs޵ijnY{ig,%-= C^k힑G˫^P_9y"zv':i["%72=)~T:#Ө"d.v"ZqY<#/XFm!]s<"5 sLCm!,xQJC"Q8L*o6ݐf|9)ym [lJ';f4LL}Gm=K d±@oYoW@#kHnpX1~ *?A8:ϛqXl#+D$^}=_|m@p G|.f_v3 $|$ gFUYɮ>jlG Sj2t.*u盩(4ZGL e(xɜɩ;쌗0̣=la0sWRnRH"G[y.zT:g [o8 +{q4]rj: ɽsG(tV+#:Zd4o87q-;B;͍,!3ɞ-NMc# d1uv !c8Z2+'f|-KY#j.kV+1fWi!r%3V.s X!ie {R-pS [\y6NGXCMdjcx jGzh ^G (ND" /C>TF!p1yzD%b#1v䠦MOA,/P'X& / Υle #%9I%C)|GeZəlTÕźB?v6"6}Y`0m'%d[di]j~ߡXCuP7b6r]S(od jhĂ?2m@@HMPʞ<-vCSoڽELMJ9r´X&C%:"fVP efV┢ x0]7fAuJqE0~aFA#BL$f"džܒiDF6Tv09ӒR"x@?LKII<7c*KT(-Z" ˣ&֜`T`æ-gl (JT 5߰bR6n5[GOd3Mڃ!ȰSi6(lF B&lr2xc(׃gR#eq }KRV6d%=047G\Y@&Ip]mI ZbEh.$arv!/@]Q ȓ++Q"w>Mԑ칠OuS}3MhkDy:3/ O0>BZ >M#Q]#ԏƐ<6*/kg0teKr#,9(Z_`-Tȋ`QJ:5粀DZX 8oB1vI#3Q_.~ I|kIa넋xࠦll ?H25E>j, Yd+D:(KwrB= ,c"#)FؼupbLƝ<yiP xDGd̓rGѩԛӛ/u^khۿӑHuqkeޢĂ_#m>#,D<3<\pbfK+<DLY^.0EMAf𺑶lWK%z,66F?-:S Kdz`oJ2p0(bN.Hk뾌1W]d#63$0EDoS/x5;1z uEG&y/a$ns)Na3V-cRg[ ^*v6ۺU4 )`eW = 8mə-v6,y<^Dl, ]@oZBsu4%1 lt; !DYW4;/4bbc H:ލ8!%H0ꢪ\P8¿YoH { }{-+}1o)H%6M #u:$VTe/9Hђ EK LƤ[zN++J0~znQU?e;Pʨ1 4+\(wjxxSx~0Y(޻>GTdR<gxC*Ph]t: 8bYNZYcV֡h!@]RN)Z$%LqGųѹTjgIw@iMRd/ns1n%Hͤ^|0[T_ÔSQ2 5g?&,1H|ABfc$RvQeR&'k(iԋD2,űN(1*gcFF}$L/ O#)$ @lKt#sh!$Q"vE+ʓ.6D d$Y}4!@o(|cN!)Ixsd΁m!ih力9NzIZF:'"yƎɟ3^d% %HOOPeqfQ)"yM;E2WGBžՓS}%ۘ6' zfQ!o. UĖY@O:,4Û|kp#f"N5q]&2+Q!QDk)쏾hyS-bm zZKg4?z'>we0$l,5Jj-v_+ո<I Z=սBp;6, B3K"18fP0E-OwWq2EvBA2/ֻ}F܎;]ld;*e յb& }K"U2]&}[xAX*]Q)93ml$T5/Q!*4ڂ5{a6lsu#;yL 06TdAI'wBP'l6cHഌp%iS!f++H Cy5LTm&ƎF˰W`9[#W6ƔH YԎw5!&qslV]p}%C 3,i1W=nSZ4+$!Qі"1jP1n/. r=S,1Ͻ<5oQ"/ԓE^lg s(@2K$ׇf?Q</[)B>G2 {H9P 5`5(b.Dt3@6X2piK/BgU#1C-L 7GM3mLv,-16s2ESM~lA&^|]#2֞B(9Rאhh-<`?"YU߯,5F CXAG(zNj ܺ&M<ŭp]8Q;[w2wM^)d`\yD,,(!֞Z0E;D)> _nTDSRb]Jz``51[.8r\HV6/}Nh˫ ZĠ8bv HHK:RhrI(r9<%L#35KP je[}IF13Hp%mb𓬭'\S a ֹ/嚺{QJǨ0E:4f G))8X|v[!C'\N Q^;Fu?x\\g TW"6(稗qq{w@d͑Ҩf(G~2UqRM.2/,dlPFu9F-^ҏ\8y <#]zեߠRY ޶lV"5Z-°$J`hIߘG;_Hc{'(02TҜre(*!E'Y>G 3IĊ88la W h3HtɥEMrQoTqM~kJ#oR"*07 x ,0DYm0_0mYԘ#\ {XsuB w*Uс/CA#F;;nӚʳLe@X| H_ B)57ZR `獫+fZ7V,2"Su4Dtm.FX"8qd)4Of8bMT\a1iVNjF Z _*d& GD-mX%if@!EZTu':%㸘`.d{s|lQ~L:x>=/u.?շ_7/_B\zo/&Ż}sv kۦ b .\ r/4эR'%gM,8*pDQO(S (˵O*(!,ׁ4i 8U\45< CCJ(ZS#LoKJ[s7Q魸.MQ}5]ao!4yi;.ETψvt touיM]5*dSk{%n(ָWKP˜Tcsѹ>]q21`L>P!H^g'I^ϑegus_@5HՆL5xU]US$P'V&񠻔2Eĩ;˩7~͸oaL|$#ҳWuҔ=bm IrH3p_3}K9fq0*TRΧGGK!'f* TM_ dTv<+&CԽ&͆<b1,+n']Kw$E~0Afm^f gzkŅ00juͫMd\!jF(%H.%GBP-.Qa([֜zb#S[suX %S41(Jv{l‘OVI8"LdHW2j8`j(q1L!jTk$$:J,HjjeǰTwJ-hY"LӮL[x26nJv#q`dQW tKPY.v1vQ ҹYv9 Ӌz9O,Q]a ?Ov؅bdH{C'6$xڶ.8#':]=VNHq8[,09AUUbD#$Fgx8 EHfqsɐD\-_V<+ŋκD{j~-\-HoIct^@fNP-1"z74%Z$hKE(<,> =DurX(]..;B`=+Z|w` Z#N,Xg6SY}I:Sɨ '5ᐍܘNq!vvy 7$) `pzn % >L)r9.MfB4^q=U܉DU8-!!c}iJӧ(4-e2ZwcR1|׈dD_JZP\d J >.VD*nP.S ,;r9A^D  d1soeOT*h*Ёl.k4;G<AΆ!X!V˽"eE`%>A$W{WD|Q5jQJL}%f}ۧݵh2']Qؾė$dr|dʠSE- Mq`-P %teaGfv#b7 n?q1,[L&1-&jkA'z4 P{vhmp,Ҡ;zh5mY[9;׵fch`/5(_9S?YT*g oBNQ˺iຉMz1e}@,'5(]Y툹]z6.m\(ptF}3KŴsWyj 'r2*mk ^=G1n3K1 Cv,pp`#3~Wպ qXst(.: K6AcNSd/ gI+_os79Lz`:PIS3 ;ȚP+x? PQS͟[kq*Pp~D~54%"J/7F ګ7PL4\q:Eݚ@ ,=Xyz,VԴWCQoׇTu] )~Iul΅9HU2BnL {q"!4GM/c ' TQS㌐4mU+'\vۇU2VeyupAy[LS拹] dSuBPbF W 5E؝_jX"}+96;Qdш"*RSկR-|7]xC ,h~i V oI^>4"_P4+Q;+vZ?g

WXO>>.TRhSnZ*pּUKLo6g6xB+/* o-݇aI:\Ե-2Xv_ #MxuYFc(K&WȂI\X3j6FDRM R+<-M"Zϱ|gĹpD+ZT\ewVvtAX waA-T;p]kX*[acA;py>g{aW}Ot\r"4LDpțPMªsEaV2>A@zVu"%* i@{p  CJ*PD@ ꍜR.].hq5p]sp`Tfj%vBđ]"sАtC/m/2ʱw431'>$ku4CqhSCvYtxW8f0 j@ e_^NOHȓ[kPh@C~v1.lX,Ck"QTivz f^gH?|'$e/Qh L|(Q:p.D[!~NRܛf.ZNƴˆsuDs䜯v~>ݰHt$2J0voES+BCd҄2g0[gЀŷ27V*L]:-nTόЕEs^fuco޼T\Ѯ9V fѪzc/] VȅaQKq}{Hン궲alAp߼)8?[`FLI4۩g`TczHSWM>aH16u|ӘX+Ew=@<57[@2 @҈"4FꜪryh2\Ugs\bnsJ KPEWT"ȹ熅E]U/ FcUlh(#Hľ֯t<ʈ,1AmZA^i)JMF̣"+Ia͕z^39J ֳ;TR fa:!z9}J7>PkmDmzA@pKɛ.cG[Q<}X}=YC**u\|xMLgQp 4R)>TC5jJ1R I}"\]4l@~o7YxFFt2A()`b.W1qp=G$<ȝ$Mx(ʰ69D.^@vg*6&@GWBl?ΙxV>;"= y @w&8ʥ;_<ѵ*:| gb=CO_o.PvC{S=ʊs2}zOCkw:Bfca*(H eE ȫ~A?F?q$?ݙߑL,rRhuhy#!-DƌDm-i)+ps桵dL_v~~rvRe|5/Xzp=:GHb*y̦F+@#@0%*0 IH9M~UUm%kAջ@1=%MtTpґa䃄sYgꈁ8,Q.}'ޓ:O{e z!^X&GD]\)ZKŢE\ENV1 7J-Gvg^"#o.ᾹQcMv |@=) Dԯq48;_wBB+ \הޗFzث8 3DcրZN<Я*SnxGUAq$E@d2l`s]hzoؔl r?_^UZX#s}(4Gb8#6L]缃2R@JD͟3 mo_o&rB S !i 64F KYz:9PM\dN=B%o8r8_bŤ$9x d0B3b:\Ȳ8gyDLG3+l~g<J B,.jٯpx Uڿu<$ܝj[g~MѾ|ӗƟ{3 x{qjGwM7q^AQ\w~Il#_lGÈ5lm!>$d9J8zS*h˧x ţv]m£ mZe;jq'k<K/ec . ~0ͭNN00t[0  I4MuOFmy e&kK1h@J[!kpf-Ǣk(H5W#$@N7V8NI?KޥÇzÅ_:i ~ 5+y73ƅrx->ťkOkN w'a rc^IڛM0MBM`#Zԫ~􃀀X>7n `, }?7"< bb>{Y*O+axuN2x`sq;ӡ_4SzH*u%/(] kvPЗX>vbAE˨e{0kWی:U#D ۉ![_ ?U͛82)xasy[߈L[(a6PQjFaTTI^9 =Όm@PppzjLcӹiPi=B sG 4&&U'@[z`'+[ˣe{H$Zoq {%>-_K#3_U,W嶫=HEM/f;П%BPgg 銞ea칺$MSo1՛:=-:^0VU~`驍5T(gRN򟍠0o/9ymy 2#!eWiNo#ha)wCΌOנ <#ROIqܣ?qm -;{%-F!HJMߎ9!vpK'}M EwaSlL)WD FsN6S=p+;e]p4Ol2>tkE?t9kjM>za_ Euy;5̪!cƣ,"<3u ҉N'۸j$',|,RlXF#]Ԅp!Dp0G6U%V&hܕ2 :`[[%' ~\"j!UhTE3b|s!xi6DQ$V=(L#w6Uj@tҟ⻲"֮)X7aDakei|ͱOdmU=U/QS(7X=IK̹bR|J /856`Z4^! ;iUNПh<SG4~f-E5Zl .e{@O(>xTWޢF Pn'H`B\<Γ^?B֟!c%J'qFztj!DdX -23bzG ֯N Phě.S("K݃,{ԇr`izJB7IƬE`_2Q}WH(k>eǿ xˎmѯv (oEwvA!H>gKAMF h,nt >9'rbf؄n߈ Ys_]Fݠ]%do䂆Y R3aPcÛ'!Yxnv6&׺/O>?T( 2cr}aX+bFNJk4#\-Th#%Lqκf7T?ˌK,)FQ5v hh$i6{¼x]hQظ4"6}<-@8D '| fjRm[^m&}WVǴXAOzJ,*E_nVՒ`Q L77đ"Hv<yx6h`h_JVh/tsJ%_-۴58jqLƃCݣ(5 ,_9Ro|n,m0tEF/n.Bʍ}zm!Pٵ*Ti [v}lwg-*-ԍ"N0s wfrgNDX"Sg@IC 4/rJ|#Μ$^'=jmvbCEP㒤ѐv[jSe*`1v /)cE,4ʭĊq\[ 7*/?[;q'DRL o*G"QyG\_S_SO*TmNydY -l;U/]4or p߂P3k[ {>Q=zgr5"ͧmTqs1cg< du|3GsfzYAro챆JkX4-ӌ{]hN2N=uc?$QI6 !+0gqy,S쬊(g=pb>D{jluEq!|xT(OoBwzrX! ^g7=E="ZK߰8B,$'ۧ:RT JPr) &Y(;W[>PNWW9@톅 jY$>!uT6"BɑFÂo{ +!8҅N+ j-_4crsȉ x]B`v]v=%uKset4hnPN)%:Vk@O#(qU0 seZ,tr2D;EA KG-*w\j3lp}Q5qSFe"t NA67^jC#WrFeME;&dM>&jJߞ TA4j\]JJ5`%lqq2~(qPw!X0EȱJ#DƑ{@t/a\3ニ#?@,c߁~~AUzDvzf;-q0W{*!i t֥AIZiUtʃN钜Rd+9diuNHysmwFG.+RhK%)ep?-V=}Ӭ UFV3!v5څڌhGrt,6П^>Vv1_#5MC0;S_^="yfb fN>,>P!(cHI6fz #1@11`SQgqm%*OU鹄=gWow>hcQhm5dhNRSjj,{-6MC$*YJY#iX׳פ7J\_DP$o~Xgʱ11:Y.~{Q72w: QR8uff/7-`ͯ#~€,F?{x"(Psy^7a![zh$("(8ջQj#:17p-̎P{\J];nzͱXyFT&EV!hA7L%կ|M'S{8Axv´rnYt2@r}DHSڠP%1g#A3/XF6Ȋ2R7D`@]^V L#m(PACN ]<(d𠰉g@ϴJngaCcMRy,B\ڥHwP"m0Tfu1Mph6x:!6t8?m!ɋx͆RJqJ.r9Tq>T yb rB|T Ma'%iCrR?|Z;``L&hO o$3ڝ)4BMǩ ̴-M=Ciȫb#B&ȗYH֊R@o+zV ;7m`_ɿr=|_OXeRI?Z-{Vx(@pt_ʬ8tym(ҋ Ns^6hȞ-s1o!Q46X\"$8ijF$j#XS:'ۃu0zt]sObͰסm}Qzsuat X4JJ_|RvAµ"8/} xςIRc%A$uѺƮaVa/\9\y[3DyMN`%ՂSK(Q(x d1w_~f|N+Q|J`{;T'pg*c$1(Z|엕)DC'\+ pL)-WKA)R2V<ҍ杋@P<;o7:e$+4 cPnSR}ZoZ״@f(k; =3$>#! Je$4 ~ݠ$& se@J!nAYB 8*| ݵirl ^xUخ^iH5Yb)ݒhOA ~v_)eFzh$!5 q*#1(Jbo)'Duڸ2&W5|>+dXCcU [}tkO8@!ټI4`O"(Z!:*TKw2 殱cK0V m_|7MV=ExvѪk.{t)M+_Q“[v+>=50r)xƷ,>[:U?T e*eDyby"Yצ0'ȃQ|jLrw?z9>NWgGlEt hK 70*ޱیLV#T{:V Vapijg;"L A9P* MJ|0A:[tJ591PP)4x^ʠfxsbu9xt PQjF]A Zb4: 6Aj eknKɅ #fN\RY0WJ=M>-rrDB'Q o7H['Et.DDLr6s\5magDWi$%8Ʌ+c޹60lgWQݔA+ S=?z%XX_cBgzr', j&+V!b">l6SO_nr++g9mNcAw*fC `2! q!Slozs#wv4ֵBЮ]m樍]1~R#(8H;RxR#I"py2@}Qoq.0P~|xdgQDBG] k͛o Y64Kr\H+`,e=n0)ʵӨԎ.BPUCFy"֠=2#vK=M{"V[o2+n#7t% )PָJukAFbC %E+ɚ[g*+K(]JO"-T?=sVY ^%=9Lp`>pf\{J8Q ]L5W]kΐOc6;NЊ}) =iQ [&^}8+"y)$~;l%ULw8 rDal鮾c0-TTS,{V<hJH z!)|.$N)$pHjqS@J)E˷'Pj:% VB'қR+ #D`))+eҝK*CNSJ(EsA.qA9 8 aZUԁ{^pݷ8] j ~ GO; h\p_$aun糆0f!{IDXu% 10C]UGԵFw'Bl@fܷ_q%TYxz@z@fz%MpֈMR0$bSvlȯ* D{68"ec<~o " aԉ@ut$Fw(Isd l=\v"p\5B—SA|`dg@ut“ءE֪x(3 O,?B M&4x kԖK鑱y5O<;/,wT::aB[ `GsM`_}i %K@m @>HT\l@0RoC_w5\Qαs25}s%/lD[8R]/4:aet&8=&oԿ$:EJTu' u@;!$)XyR9Nak>6Fo 15,ܐQENIu|" /4h,γl$Asx8Wvt`(3C^:)zuhΥj$i(YGwgh#9E!B~XĆk85DsV= >ny+ѯz@Wm.]ǿl.~!Yt;ϳ)񛶖 W):o0rk` :ɡU˛MU(#;uh#j%O1Ax)FMpXνGA!nZwy(V~P<[ +J6g>ՖmxC28!F@]%fQ?Wpx8+JM6Jl'B S+"1B_{ޏE߼#SpOZ9z-p^`ѧ&|?}cPAý vZaP A #8hFCԁG!j*]߫mЯ%9@ŵ;*Wb-: xGPpd6)g-ީ~eF iJ,Gm] 6R`:kle9Vs),PqSyO#ط-Kv $S>YcF|f ȏO[x׌ZؘY*q ]\L/]1.Q$U8!k;NASNm#A]PI#ݕi9,Qx L$l`_H~#vخPSE'F29W%W) 7ʺ̞;A-} [U VmЌo93ZoeZIִ)>B`E%TytJ̣$]Р{"([SW˾'WGث ?qIPjkt6 i*Ŗyǘ9KRlYhEVs=!:POB]OB/UoV͵hai9"?xAq)c T kCS&`72@XZ<'A8#ZvPjR_(!gAPN5JygXD.Z.1WkybW3-LK(5%vӯ{2J)'*8ԳeXPj8֖!F!,g4-0Bs94'ÚۛoSt˵arS?ԫ-QZhhpva0W}V.u\|,wD"!}~hFS/M5!w=My>G/$.*RN#=T* w=$`[O(ny9ՈQ)T$󀼅yb.~a|o1?@ׇ*Z>2 ̂n/qVG`Ն23e:wz/=G nD[lv^JX.~v:ǵXD{aIJ8oEЙj~5$vR'Y MYm3vP-U%}O  hJ4#`{f|fﺢINp5cb#x/]d.0I"2SfO; ]ca6#Y7[2ugYgG|^&R>MRR̀-1`(sn 0nБq9(tY*JDt|#lJs(+,^V7PIl}b\>J9^ND;=29Ao1n.)O _-WVzgN4 v'RD5y!D#YH|=$2ni+!k$ZQ`-qj"*|}SVGϝ[!I.죙 Z|օǎ~EE/&IhA2i:"v-Q鸜(\EhOv:v$ @~0/%nmi 殁sG8|e.OgmS[xO'N# &F ?-;bk} f ,@0Iq(5I*x)aĢc ="f'@m 0Ku4Xt, ŸȮ98טBFϗny6&wn.PtZx1qApA5oO+ 2s(,EtzuM<`Stю?rC;N#beKs+ƪ$6\ZTæN.^XypgrFM`hkU.P ;#ю >큝w3y{qMwDLãR{WzWE<{*MQm>"' 脏%LiN DZ[<6~QVH)kG5 {)v<0=&HBOۣI3w}OjREɛB&l@ 6!PC'j3w܌)8 d¥GdxLܽ2 x u ETk!Joi֌@H:f Tãz% O?cVwӱzռ_evzz47V+ǷdI$R+"JV@`d.gǨ9݊PPm 켾רF*Xjdlwsz>dRARaH+ g#pc/W4(#.T1/ؿ[6dh+uJ,Ff!D,] bߙPAj쯾v?XTPZqT23d!x?TejFE*[}YGJau2&u$XՙmbEĥCrG4kipIS%hSccBN!SjL S},XpXkA+T`.}:Q?Sۧ!al4B"> Ze1IڥoaWSI; @(W]㗜5ŢFN%o+!S'%%M+`Sb8&q`NmPJ*v5:y [#A6E&#L8Vl5?tnFg2*#d~)pܑd<=ȺwrF"( =xNY3LC:܀7eiHI{MEcKoRfݐnܣemhBP^]Ig(In #b.Iػu#S=7?׍з9zZ)a@Sx?~Th^ץ}Fk}pܻ&5g}q.nKw"_~wj%^s:jo|<1H@LdH3n?4݁:#PRᜡC<}h;7w~'&㙼k&MC9Os|͐&AmcD`F׀8^(oHNG (@Qn;ʆP6aqgEΊߨmC̹ŝxz{Uq[7q8y$(4LϹ/ *>}4,'uyB"4Q+q Tū_yfW0\W9Es>6t|P+wBu~04@cHJݶ(!Ò$͡:ӽbnkBZZ-~cbi"fca s7GI86@7yʦu .#׹Fad0>#QEJTp5ra9i ތM1Ac$#T< e#4@3¹B,1Kb5$`NO1@Hü/ϼ4nΤEth_0)* x+7r2 .$ h~;(^_|$YF}#T/q,;obfF(쉝C1˲Ɣ!Oc+g^ԛzå~PݙH,1bG1r0{Ll>= ~9f^IXY@b@ֶfkafa`ڊT}oW ;I6VghA?-|"/޴ݘ ޣ{c ИoAL }JZ#Htl!ټe}"jf_S|zKuwD0HM'Vˮ8*Zpet,ta6 FN1l5kEL_6 A+s`D`통>YpWG46"V@ۖ#G0QqE ݳϒ3Οи Es )+ʓ[TQ^?{xGRh:l4#?!Q[ z \h8L>ij2Yru2E)_!ԈA #am W0 i9.,npH_mܶ%(J>= 5`GAqʹ[dziI>4o0Nj~ȯЗOd4hP~9l \|Qz"h2 ]&Clceå_ 9U~3WU1Bdm 5:BE) 1_6_]N(eJ 7 N5(R,6]?Csߋgl,lH @QߩN"H!ty>D3f$2!LOARN g!Lm_"+p혽<FOOA1d$Y~ni`OY\ \,6`M5m ~6-QE1 (TSEX/ cG4mTܺ!=1QDD T \5a0:=@RYEEsLfmO{@֘ki*I#_ѽYS2)罵uo&Sѱ#6Sί]SWU|9HP"ED T+ee@kjl2 H!^m&mn7oGd镚(w/vr럛˷ ߲PP-}PIH“*㴅+k(GmqA׺+*>T}%QCnʍKGpӸ [.a*R,cggr @b{CjREl)nEBx9;:<ցZsa ÛdsdJ2WGz*\{%I_p !]RYTϭ?˙>=;Ļ|]U*$Sr!+{{ j;F6Z/ !SZ*C<ǟGRcxNUZa}&v0-K/: ,ULZ[A4Ι_v(]lXRŅ癣g[-?E K|)Q@c4)YdBnʡI`äѩue3IgR#TjBธZ&gAاJ]޳g˟4zU-URU4VN. RP-*C|FZ^2 褐|2 #fcM睉#%7 vT;Kf%ImRM @{.cs|9В+Z~ m#o7vEr tpm*Gxe$p%՟_RՋ Y:.}ږ ;`ٕqa5+ SEz ƺ6η z_pwI#"##s/z 4èԔ0Su9<_ʧe[t ia;:G] V9JSh95OMQύi(hLpLm!ĕl 4WHLK@ZNr@OhF9 UilP+b>T4L .Z~C,h&_tX>Kp.6&A=-xE{z:j' >pɆ,{?n?Np[JLUI$k3&ѶA|w /%Y*U|ѲUkQFT|`S]Rx68' [G= eN4*/U)5! ~7<r4}>=U._R4ҡJp/+D AúVPS-E:tU Qj(Cp,=LT!pѴscA 6n~u1±CHX/4eyI  @B60(TmXPUpnP"aDSXY "PAfP-Ў@SS¼ %`AD۠gE3WzZtwRϼ-rn: \׽G@ 7*`_QF:)`0zUr ]amkSߐtE̩= |EC+ͩ&ȔE@?<ͲQX73:9T/)g}<6: 잳{Ʋ$裧 hM`t.HԹLHA58oѐ=^S=B+z7A5Xs^ I2pbP2Äi3uWMD3lŵ{`]J&h|$"@\KվD*p:ݒqYC fФpd?i ?i8#iԚqg]7sՋvy)gN2ISizW{ aG;{qqM8;Z9'":lár-w$sv抎 \+'4T#3 9#5- ?@Qti4(tqѬ`\ rI^ -(N_/UOMND' RUSl8;C}elsK!#jN8: i%[(X*°n/`Ms6P^LY+)m 6ԢP@ B:x3WPw:VH۠Dt^nW(9 Jb]mbc-&6>Wxѩ+P\x":lRNJ<,>Z,R$M}^BK`%qo INr򀖁{ :{_!I'ے(IQIE3+Qp!b=|d-G6QQ^1^Uk;K?/W`jӔs[) No݄5IRl&EFh^ -#)f5I"̌0fўí̗V-$9 ~;Y} nT1U_+\/ ??G2ec.jq1=h~ɿrˬ98 )S5@u1a\:1}%DL+80LbZb :7@~%[^*GuXt3EԒz7W:靽osdq>gxj+F]-ɵ_~tL-_WlRI}d0S#ԙ",TpW)n2XPyHA\ _Y0\jP"*~X3渘<ìN55!Z?/3CmĆĦ5[52w, R:0QJٸE]GewC$w9R,ϫ5?vD~6z)[̅5ecSz58]-Poö_1D?l{yBR2IsiZBwNS&RKfѸ\M+8y(ψ!s[^|T9[o(GTFPSbW:*fJ|Z1ʄx@p͘bGQ;GiÔJwm.Ah_2&F-3vT4!e6>St!TiJ77cPO-A͹ݧ]*1em,]Mh ={'aHkux4kH/`2j Ο,kiǰ6}QG@W:Y>7aGl1Bqu-$vq0۠ h|忞G9s $ %m`ЊM w"ƽΊůVUg1Bā W'[䠐tCtnW/ꁤv,3Ξݴft#/Z" ŽP3 =F(}bgh(H0Q#'`\g8UWhJ8ze1tn {'Wzx>62(d>]_`\z>#+u*ׅbYSъ‡])#OިU@ᕔCcwчJ()= ;r5G);P0_02Z^1GL8ddZ*hǼo -0qOa`y|;xl'a##p'`pUk#p\Lw^ LO ˙%ȸ~Cԟ}\ hzvܭ8}wQ3%Qgu☘Τ_=-ȳ5#++'6~t4*~o6,D `P`۠Z 뙊|ľJd념9%O;ک Wcg"-,3- ]Н(uS{- P?4Q7u֧Ҹ=/:/s;V#)8kUCZɂZ~VbzFWt9fAȠh[`YH^:q%ڌ З#JcD-WAlП1V9stqR0E*xw3v;kF3i[mt1kaΜ@d+H{Bp1Pڴqf:g{]0{#%M pRBy])}QV7IʎI1g_,(GQ70]nEe&7q=vb9`o`_f[XȚ2]+`_h^UxSR;STh9wk.E/K2VZt50H/6 T)Q>~(ȯ`|B\o&Z) `Th6F4ZtйE`V++VtRZ(^rhS 07]K6RL/alʪޘ:#ِ$=C S5je*T J+@YW>o7uU-bvMD!!S|0D hʝ,4*?'~|OMby:i(bx4c'*0j HDeՎ9SPMT_GŢWEx#.1zĚ#t#>8j+S%-ߕ^P,{KT> OwunŧGlI@;*PxwԄm_t'*J6;}2=Vo !)xfקhY?IvS++>Jg;!^G$8`JB]0nju.[gZ#fKTX- uC#1 +HԘ6\ѮzD4Hyd[SŨJ"Kafp$΢| >{d2%4S["gl-#=~u-:s*ʥ% D謗b 1skn?/D7%]UCn5ȉHoYm s@$z[kv'ߪ֌ݮ8Uxia艋e%򨖼1SAI`I:OZ+V.K mQ߿L]UӌBu uA}c%U{V橹0˨Socy\T #^“8 ^eo% =yzv!ˆB0+51l-7B|""ňoe|9ȭ`d!|QgP dk Jb7w{`ђ"mbdĴD2YMڎ9] $"|Ha \QT*I{LW;t"g~7=DŽA6J |K ,v;:#DގOWY*U4FqFW:KXd`Y3K%Zf;EW6qpntA#̲rIkNDT&_!|4L1c>|v?--86yלaLTL%ɿ ͍T3")B41 cBj)@wt uL^5CO?WpPb.N eĿ%"ά8=I7Nϊ$%#-Ӥxrfl:Mp(`{n?J_y!~J(4 iԌOe#Z;YQAa1L< YYA y&EvFĺZcAƼu)ٴq d$V0Y5f8hbTڭ=O޵9n>CDӟeΌl11n)RCQn~Nԅ*6ΥBeH /Z <2i{äQ)[`O^֪ά4>*b7gA{F>A(A{}!=&5"ƕzSB ҸR/F|FQNbt-'pmȫ"/Z*ؽb K*tĽEv[WkHQʊV"$z^mds!m!^ 1H%e* LUhsJPAlDx*;/ZS}ڍ7$ a#HLJNRlPbP(B+2A;'YB@TC1 n-hD5+Q\1wruR a`ͮ[טET[E?s#¸UTw!AxxS) uH ]s5` JğUEjԜ$+@`2*/cOr]VUYvz6Е:${B4>5Bv0gHpsX"CĸmEϗHX9?G6@- ՄM8xFThZ(2jJx# a4&^lԸb&PT7*HڒdqD1[BQnTrY}=tͭ/YGᣦT5%@X}ZC"\k&g'ÓP宾@eX׃ZDՠ( fS$ܦP@`9 k&Z Rd=nfh*Q7OVt DUh۱' FBh8{,HJ XPܐ1wJRv7N6abF2(r$@:D[U81QqrP8G)T.\7h|P\, Y$ШWbpHv +hGXj ƴLqJӊOhsO#s6XbAt@I,HFU7#Gu۝*=BJHAL#x^!*"9}@rt*9#DǦT*.h׿|O|˜^"-{L K^u% H> .ԽC(P F{ VR=-y/Fj@!"Pq<^%<VTsR Ғb]zLB>ҌT'G)w$*HF4MVvC/lv\@y3VrT͢t<):3*:8^V9uwt?RU W^W({33$)3 QdT;\{7%ɿl!%% _ #^}w9gEBje8 -VldG-S0#XZ!VnL^PB(?- i{I(\U<>,H9t"&cGMARκItT~yx30St@p4xpk 8$pZ6PPH"!갵^\2St<\# 4YDycsM brHu"֥NhQ Q V[%-E,P-PSpg3H=9=QY'GH KYO٠ ĵ8x,88 QKR>?([ 3#0g8T>"WБ`N9J=FzS!8iI3VAR/:HuA~Zj2E WJhQMWV+NWزC$U} v$(m)UH{|Y㠖@;:\Ň"ѢVM4ư4J&PkUjCfpeU< )ݐ&X,3vrj22f}O0sq3^ȵ F_3 teA/d' |% uۼ^aYU9FٌLGB0`:<2xiDA [ZfH*HH0nB\ %Ke_!( V*54d_fS&W@@hף,%9Q%VGZ{16(⤣":)xA\ ȝCJMV|k #t2%{ O68qJ`x`oyӏBm =Er"N?9t#80k=!}1h^ Nq2t0h{ҫ/Ē+EukQ c#:vD7p)geAR{MWEJ0 (0zh;ɨ*`T g*0Ə:PP qӍGE zR\C(^JpT:`:Uږ-} Ehy@^~+M 'd[J]JCh!c- 5LlȐV%%D-[ZdW‘ޥE5T]u!x&u+H51R6XtuײFٴc/Hbo+szoPe&ۑ*ŏODy cr"Tk%,+gR 2LFy=V4[ۨB,h{oefU"a.d&Z"]RiE5wשNH ~+}YH\q%쑊#b&ZĸP!7S9b w^!i@}W (7-=H߃R!;G&Q_ %NB$f"/$\ 54 ` p| lĝ5[V$XH5u.B "Um[U0fG& =dz bb h4`!Q$‰[=2$hQp7f# ݍUE7;/Ax,\5(mQ3pv hl'il>nr'6xZudS̕X;J,;E6JQMX*A9xS4 D[G>k(3&M.0"x cM$!TQ#4_R#Hsb6HBH|"HҪ ΑiqPȟ¯v#bpW~$(efbQ&@➎gb#mDCh[a*ڟL6'Yߠ7Vj,#QtGf-1\ݭ7*0lq5G5HLNCITj(ET PE3ΆH jÝW3%|9٣-B Y/ K4#MEZ`4RǀΞF#V(=྅, pE\4,]%1$aGg6k:T鼍6f yQjW2פFz}}tyLVrQ7ת*F*XJ\qorl`8:ǖ0-SLREI҇#d^p Ut1`>k) ӈ+/U6جTm+~P$?]{R5e-@8:xּ+!W /:BG񋊧2w$|օ,6a`n)| Z*JCoA>fC**)*.wJGJ2'jGzmW5;Ubu'^ ҺAu|WTS<Y^E:V1W 靅dk)/Te|IZ~|kk4K@Ȗ -u/%X`D# $j=TcRbVp_b "A9,kQj,cxOZB\ItCCE"2zEQ. RGP]JZ};(j4MA)Ί Csj}PV}-;I9/mƥb0U=^tRTpnʼn ՠ0 ƱrFf oտSD`+&E'3gQh5jbR2cUdXODbҷ Y"vr~ݡ˯B+tRΐُiTEE^Bx&"zzJh[v7s]rGT8)a"*0<¤WZ̠YbBP2c#uJ T`RBÛ! lxd*(+3V(#HbѵsSA cP-:hP vGR-k)H%rB$,=8K< A-< Ѡ *AE1NLJחpA TK(\p|D{Ss8tWD )3"QH"]4䇘95]?jb!7j,JJs4]OAo),V+/w,:o-2ZPa #h # gͨk*(OLk Z,F=u\%qk&rU6r!,/E QOJNSV.0MQbԓdakٍ򨐈PcP.l< .ڒ{OT͢=>87C4N*!;JHR㕂S~<p]/FΫBSɅу@Va9P)V$ns=0:G`oKU el8-$kd׼؁jq_TS)ɩd`zS(3mZ^X¢Gu[OuJjt<$hAIFv+R_p~ ޷ِ3z PIV}+Ojh3p,."MK\WkNt\ԏm/<7Ic$[DžH<9D7CwtBM=o$Q 8`R]ݱQ- ;qn0wn#ڵaqCSBc5={䋃x~]VEZGJO &܊T∙|aCtSҦ *^x D* xnب`{0 )_j:Q(+HS+`QTybA<1>stream &k""d )w6!SW6q< ȺLJAű}Q8"4M-uq8Z |@E ިE(ndRv%R&T"WX+v+v $;+<.]X"k|G^€T>CC"D$ 9c-93C[h+~ps@*=d49Th& `@C) 4dp|(KX^Q R xVJ!B)X v5Z븡9Py TKP AՂTSLvQ-ocwAz or\޾ԩWB]Țn"+Gi]жWj_(E[<\e%ei1%^ING ۵8]^1+A@2 "'{D^Ig Mm6dkݕ+N<9j=?XU [b`Iif.e.\H J3> KFnL\x21#K{#նhuc ;gNf6}2"P+]onvBM#uTNyLRD%hݼ*[$k_F9bZ$ցVo=^a9k>Q0 ED,mPEDQ} E@6JG[ Us6 $Dq^ՑvE)1EI۹\9a .s,2%[w!s֢ dg);yKIg -%Ԟpr qŬs:ZoSX: )hߒE=1}SK#ӤHƙl\ 9k34Hx@hw%;xӸOFQ`@V'Ep Aw$l@]u -oA_:C:U 'Sp6FPvbppb@i} $4lB֭*UWc-Q00MIaou Kz"{ѯKꛇ/O[ ,Z)had`W09G>= FRMM <"jP2vHPg:$ Ŕp Ȩi]=Pf3jȵ@.D;}J:]azL+xH{ "Vvu z(a,Gn:"y$+ |2UT#'RA`JJIoE[FwXJhMmi3ʏ3TW }tTCs蝽P}Nɠ[ | P# JU Vݸ $,)=OQOQ`XYNRmv9\*9Bl[U i 0y87zAO`ֈP?;mÚA9Md?<ٯd0DI5QbDրp~GKHU`9dy5m#jctp^3E~Z]. \b-BԾQHM._ >w2 V F,`jG'ʰ:O1zz`c޸RA5/M4W+E+lڃǬ ])Q-0I$xp.Hz%Cr՟ C!ƷMatsзq$N qF(^-l`ŒfTl)Iik D )n/1ԍ9 n p4FA88 b[ ,(r(kYq T;Qc^V"eD% >1/ʦXZ Z%.P,hRL13lQccp mcٺ_ 3b6+!SɎ̯C"4pU#.r|jUJE!$j:`/{2w+). q cf1ْ)J*0P'5-8T$b/a"I* eYآM,Mt  e99- dY: lo5Z,Gˢd_TXH6/ _:.aopqJᦈoϕo%% Ia@{*k\߈H#^ JeO쇠HMlVNb>f&9)jgHd*KNV-J*N2̀3[`ThZ[(NN+0SqBUF2][mVXPr^!Y$03zǨfG qsI&#ӵc\'\\-@m&3a4i+taU iA=ܕhxnCT[7mz)1!YjMT57b=ņ@ɚ֢O|=6Ie<0¿!hR'9ox`s[`'f1V%҄յ]F JOܟhW=?[~^as忡T7B*j/ZD]}Fh~~#@_Ѓ*EDGJbkKǞ^+AwQiVLuHːO_b 7[%aZe+l D8Y! ^1c'/PIz:A^x$P)򑭿D>j-%Pl{.:~J!E!aʔfwG鄪%g&8q;-j"V5 R RE!B H~vGRw…@=5 4h+ŮP"Q슨ݐsRf:x#h "% l} P c^x6qVWA-$%ۃ#E[Xj00TUx-ʬX4"M/t z[BdH<ëoW}2ϯx sg1ƑƴK P. ')E*~ <.|r|ޑQvg;p=]"NJ^2Ɂ9oXZ{I*X\4C9PwOqB oPc!"<%C$ h{-Yr۶A T.!3V{ c8ߔj ,d|"VY|"sY} DљЂSww:bP:aAʽY|*fG~8IrTmbcċ6AҮ6nz',r+u؋(XP[S)xI|Vv"NRx<Q|lJZ.aR_Q$++e:hFKbUT"ԝx $.2j.xQR"H`kW.p!TaDr:Q(*g5{wI/lxEUIޙW-< /6hp;X׼(] V\3Hq&0Q2W?d PҁĢD%tɯ#,?z~cP e>Y)y1TZ)f7?uvTB@Bl_MvhZSJ<@`^AJZKDB8^{=@pLm%Z45׺ )؇9LQ3Pb΋}Pm0#Pz3Vydvoש.5A įZRDGЗ\b~|հ~A.B$Yo.hEPҖ Eg OZ{a",zip?߬x2 SWdR! aER^Aqў%AlG9:Jx0P HbA^WFD!FPRAsQYd4Dk)FLe ^Vi:\CHpANPx[JFۭդơx O5n E w 8cD0f+rCNS c2wO2hY\ oZelo,2"$,b'1míVv $N47Er9HduE,/e /(ܱK)t\>XȗR]TkP-PAIL)=Ҳ:yЖRb3,r+H <)$iP,^ D Aj<=a!+ˡ\M[^*<9E}T a#]5ZbR6`۳1=f $ knt8#ΠqB!J1eL!$myN(%CZtUF+}cvkUR4Qp*:^\Ȏ^a筍t s%גGjyhkqIN'K1M@.ͩ(l\Rhopc<]5-ƪ* 0nTzD;` uP.IivmLK-0x Cu)"%яS Mi?5 M^J$Hm:aG? a{p"OkVePmРHCklINf 6si g"\HDjJeqZ<Ѡ n࣯lDle"XZ'4Qb>c:S騰. !E U%1+ODl~'4Ub T5SH'B 6ɒwMr{و TlaQż푇95(Qn(9DN[*cĒdE#GEpv!?eA/!$J`AIDWP]`lsAΕ=R( W!:B"v9{ŶE筄@hF%80aHDa߹2='+EF2jS 9oBh[ GN5 0U%i'5Fo&-r?3$Fȇ@2\Vd͔LH_CV(Jc. u_CQ[$dTV5Σ)5\z9p]kb E$9v˕522B8dRLzUd&rU uVsRs>Pҡd+a=WAjLMw6 t][+"yP3ܻ$z)eԧlV+^s'!#[A2Æ̘sj!@P:O\2݅JG12,d:{f{(r\KnLM)Y^*FLfJ M Tl7Na{! kCl#3 :r5`I|ܱxrP.lVrN+ſlf(pgW?QKt^:,e@j5h!:*kX{n}EUoR`pd+|W׌:eRs>Xq#k[E1-Iqc/4!ULR" &#@<]+7l'1DKZ\ɋxM@.%^9$U"+ц꿵!&b.Qv7YlԠbX ] ;Q%v.;eS4%3dqecjN~|I:iFQ'ΎH5:#t،JoR 1N6(IάM'C {C#ުf'NN^c?<@mwso<& %ů|rtvOrzv?9olz./mƿ9}ˣ9=y<[//j_8,ߏ?N__}?Lw]zuʣ/?;5}k}uq֋Λ_\__ }_{o_9?^eCF~y=/^?Co/ů a:>_{l~vEZaOxo{]~ڷWɒ&wZ?ϫ Fsl&G0x I\BEH>H]Ri\MnΪEsX++]׷Sƞ7:@w}=\ۯgi7}1k<aǍ Q3i0))|t0)(ٔ~U{86gݽAѯ;8V2a9z>5ǁ Ro<6l䄰e5~yX>Sv2J3y 9$2r>DQnx#Cޚsӻ_kD6n`N7 C,_HlܸÛѭvNb0>Z?ؗ:G?aAۓ eiX7W;w||{!1UTqlZxXcvQڭ0;ܣkab_O{~ q=9zyˋ{vխ7nѻg?&L뵸}pǻL^}ԧ?(;=ڗw'GW;'dM_ܽ.cvwL?gYg8}̜ɞ~G>>>LܢGnӓ9zyϓ?z};K[|϶oNyZ3&[?z}{k}x}E{{=}X緙+]yOw'Ύ.8ut>goz '{ucdz{8;団㓯Ə'̞GYwe5i&/C9YSax\ oѕ-Od|uqz~jU|]Wuc? s)[n9'SveM[j}q;rOLe#)ܻiwLc[֋GW߿=ާq2μN{nIN.;IIT{Ư L:0Gaؕf+$ћӳmRgOZ{ &Tӣ+V&/-/G^5'#$?)XO?=yTNm/'r&ׯONŋZ`w7oj^t쇣'O 3(.2@M.:>:;K/l+EN'%38s3{/{ 3O~~gX&v8|gޣ}َp&\3[ _(pgh_l:v|?kLѾ"r5^nH_zϻ|l(#F:lxf A,yr*g*)<6i}~ۼ^o鼺ͫۼiuyuWyu{nzlUpD[LsD;?Oyy7<Ƶ_;v_}w?}gze/>ߺ09{Skܑ{WIOakO= ;ݑ?b$zwΣӫߞ\Mi]gvOG I|ҫ\8G:8^9<O~Dm07@拭L9缻ݎ99<⇩c:'?4όVv7ޟ|yqn߲|cvoG$~\s۵3%Cpsn!m,9qYyyq}f]|aw_˓_\\ߗgt$CGy=z Gc7O'ga/,Syů0K;wS^U i'ɱY.[3n ,m{3KzDx 8ǥ$.5·(oٕv93s`εec ЍsjdG19 C.i"S_WkCSϲys|?/UM"fzȎsLȎsl ySs<ʣ[W_ޗF'Bg]Bg]B?ɯѧFqe֯ufx{x6V?/ꧯ?z}[wL v@br'Ѫg{Xȿv|v+f뭖}[|OYؕg'9q!5:N8}8~0s;GߟlZ6>M >f/pR /p (nnnۗ ggnѣ}qsq +G?YCLfVݨ"ي=`OTac&XGfhh<>Ɠi3\}{vt/~ՏmϦkL6Ծ̩1oJ9v$e[͞قx3|KUgU%`[{7G[XDݮU//_'^ݣwq27G'۽^O˖Rqm|yaB;y.["vNV_%g1~sm6I_^Uflɶ(pO9߶׻Oꇏ˟?{b~ gćr;[O{f=g3KZO3zo2[쓗`qT}g{Z>~܃7XHCޑv;2}Sl5ۆUw~<<>K`WGz- ܼQ[I~ͼ>63gݟP,*Ög_B|ؿa ؟+ӳ5i*tMZ96߃A{#;dB}~o?}?j .a{ŅϚQKhB7d#~9E ȣOz'Zc'"pk[u  ;ہW+ x{ _Co_/89/οS<||s$~{q/~X:yso/ůp?Xa}C?,Bf3ӆüx ö>CYm_ld׸o_ۻ_y]>5kͼ8h&4Ѽ6Nc05Mm vfLl6Zwl02((mе;֯4n|l~{cMFfۉd6S ocD_PP%a~+~M>?ebj>jj>y`e ̅=_ۀ?mbp6?o9Ez2_~3bXng<f5e0f%{j [}^z}DXn|֛c24Fsun(1C< `KDIG-:i̮Ɍ&3=H rt}t@h[_-]u&"boy]ww㿵xvn1Mo_?^ʫWwHXřlNξ:|sr|1~tlӮ2\$Gͱ\ُFzwqy~8$6$_̋ۍ _:=_Iͻw.V0.o g9=GHɞƄ ׂtZ${xHRکL hG h۴zY~fo[} a-þP YSc>ZXyvwvo܈ain_bd& O|)2H)W>D 9`>Y<ݓ"'MێmfwjcvNdMFZvYhžcvJ{6سIdkYlvWuXv$붵8/ 8956?BFrp.acunR#]1I#aeRwdCWc>玽=?͇ݑ-y#=$kۖvmYB6F|ZTl\$,bGO[CTw=֓](|V̄ cFIo{p(hsX.Z&v>,Hڍ߮%쮧%p-s7D}BҎ:L EܜhBFbx蔞όޔ2+dZ˳Ue**yݕUQ7,Sy37F,bk럭^̴yBmT=ˇ"._!6O GMUJW3k37qnN7Vy!JbndK lf)997KTi M7 i=W -d7ʩd`7ʩ<.nY3}2p-cSݶuRc^v"G{̘SFVNYwWƬ6)JS/V˩{1wK0[2:IˇX):fI>[!L`d-1.7k ?t-m&4lmQ]eZIO33pR6q_fl~6;hIMT;C%#jb+76J2Mwk96j9b~en Dk~0vv=ayvOtRr^ i>V^>dޚd+lr,}OMjZHNXiSzսFk08n$ LDb$ K Kc;pOʡ4a ˷GnЩTemo؃bI6Y+0l㧉G'l}3&`}%g :O%$'|X|My&?M\[Mܝ*v_Kyx`$/ ohg(/8VdB_\])sq\SfYb,fYb,fNeS٣:-`q3[d9Iu M[$Ndb֘+q ~ZK[a_[]fO {hd t#oe(YsV9d'~)gm1 #V>8ŗ~Y+}"G4DXx_jRF?n7%JY̫m;0q*Oz#X-ǧ^)dI2U:$A "48xtt.׷`K\qcElDYM>i/ [Z/c؈٭:Y QL.wdM!UN)rl ->)reRdөm! خH7 PFM3 BN|'v%aQ])Bm 1 U\!%C0.ܥ ۤO$妝k&FJh$X|\{YoV}G-P߈|6RIцBAHA CZ8+7*몇U]58klTFWU0{Gn3L$G A 64-CnUi20Yֿ, n:DĤa?1GA~sRj u܀+$>GJ*mi=qBـ" >yP7o0;eCKlJѬ]zE^ _bjnzufשTէxǡdKR` G1ūDLc~報 _tbI~58jHC}] jsB ^Z@ޫԢԮzdaҰ,=b g/ȳJG$ȳ,n)v, ?$dpg3T`roL}VO>}K([)[Z436DuOtWcŕo9V5Ze5ı϶o4V.t^+'J5lɽC7"qXYSo`kTۗZFCA _(=_^:xMUaK+}^KE,AzNFC*,}F0IYǫAWN\8mmU`, :H;vPи[8auVI=y)TB{eMsl1ʪ벳G(k%oc ϛtsxDں砃}7~VeHH fD*t4^uﴧ+ӞPt\pq-W5<%H=rQ$=?bn~Ok2X7p(zW@q N6urK nRպqܱlfe\/[Z,BWc˭zO|5JjP%fe0V+RkUKm̢<6tdBhVieTа |DtĘx7k1-f-Yibf-Yibf-Yibf-Yib"L$֕kWq;_ -2(N2&(sWѤw|d;r8[ٚ7?}Ȍ n&y9^b}U_ fh0+&V8a#, *VqBhF6-.X(d<ܖhH -(UQ2SshrriXr+4A6iYhdιy^"ѳٳJwv`X{bz.Th4u\v>JƌYjN7y(VFf*G1hTo7wC9& R?ްB7lf!@TA%^ӷyzs(!5vb3b5Ià "]@~zd h_5I4lw6$Ml@{ !z,@?ptqD k[g_=:jX,!mˢd$Q'Q/>́1IRL j ,F!H" Qߤ<ꊘ,:M#yID<ZImUa4zK$5".ij]:$'_k boIX@p+|!mYa}UP/z Zo{5*RCr|W555u\q_~Wk¯ +:G ~W55u\8P(=!zG@.ƫr Zb򓣯hPP@˅>`-]яWuX -&qV~s ƂWE&9IoCNEa|sp9 U`'V!tHB왜!fQ\GXr4%h'w9EԻe<KDAqSt8<|tõD ~ |R46e{eAx;gSLFԏRc3b>O\ MK-=:I9oG{ƽ;rD48(J#?rj~ $w< +;qɁxb!N;m/u֗,Z'#yex`|/}$ xi%`5G/0eZdr\*<7ot/JuxX >ox=8̮0O6Sxx,0ę&xfZ:a-x^K6FXc(rdc+V*o,-f~shI= O+,TǐӴ? !-r&]B͚2<)ize~,Z;Cg#@U@sź*CuVM@y$Or'5)1Ha@<,r\*0Kic/ Xhh t(0񺢡Pdᐪh@m *dG#7ՕӠ#K%Ȼ#V5oU̍TTTTȪs8=| DsnL#eR4"{4"nZ_e4Yr5NPn0j>H.3b0#ʈ`NE"~n擈߀L.?E3$yNa$ uS! J.736,zYh.+_\Q/CӄV"+sĹaQ rEa<)Z>k1(^@!C܊eL|;,R>=kv =Y$Z6FːŨe<͟tUgi8W1?sӧi5>hꁔ"7& 9A@GEt\3Dύ#fF2QC-qo ""f gӫ3nG;"35rNUR"ϓzPfGyC#`B10(PB˄{Ot뽑E-FK+f=pv yS-|+\UWpmƪ;ӑxZtdDϥ};SR(BExAM`hd&YmBBH.QOGoJs8ATgU(>,җE,ֵM_Fob`/3eЗKз2Cc ԗ2B_FE[qCQd2=_0=Jlٗ-7 KMSmarX*zșn Kq@-LfG)W\Ĺ[O{d!eϡL^V<Y>bȸXdoc_Qg ,^'yY|\k䪡5.YDbY`(mFf%8chyYƒ)✲Q4.`h^gdEb >k d8NJG-t;5-PApwE;Bk\ ^K3,#,IbDq4/QHV?3'Cx9j7x3!ovsnv2㖿H]k.Q1ZKw@9] GeXG >n<=J+6n՗UV[%MlinI3?m'PzϿN' )J5~jDⒿKU_$ߚ!#\fѩ7L_#?@|g:ꯓf@iYɳ?|LO 9V%eI%r,u)u*Q04?M%㉥ܭƿ~i/mOBXoZMuܒmRWb*әPٴI YOEMh ӵGwufRwe:71~z8u67~mKcQWiů4_1(QEr'b6^=у 2٩ 8Ctci}Ӈ N :pixIHu6؂ r˃0xiߌ̠̠ͤgf&kVoY/.1pvrC34<!)+`5+%<'΍D^i>d{fT,xo H%dJWXU82$=Aɠy[iuZ7sMйMFj$LςLO*ND.Q ҅a-I M>XˣQ u!x/*2#DR_[M0E cP\rhA)쇡DT^ˊcsir ;E Ї#2uS,x|/XQ t(@ CHF?C=Cl2AryM@e(rZ=rK,O֔̇o4}$@ %K[Hdz"2n0"LB `pdZPM/Q㴴"țs2u^8BqN;񊂨q(4aU`v-{ϯ3}ER`fb Zv^ waIdt0!2pm&U W3yi/qx4ʊ`܀uSlabctW,ǎ (xgĻ\QkNKu n$ <N5$gP4FP՜%šI,]^(HˠxpT АF3Ao/\ ^0&hj9@P (qxU{`}A,!8p@4%@tCG le =_iJap}W# "-Xx֕ " D$[aZ ]KH+b+~-9Wܒpq=%C/ <*D/D2%y'K(@`Suq0_Jq#@1dh^YM#laE&j EYq/AWWb$2 XX>p cY0uo٭ c4Ļ]`|wRD x<#O8 ڱ$r*xBvSͣ<$YFy  d& MChr@\x{~T98*WL7FzО H"R#ήm)J*蹡C0[ڌ0[wTWčRյH5edR-Z/j#]o)ؒPF*U;eZifq1 VaZuO[(|eYՑsgg sOsƁzAЄQOL#ǀ?!сC/x`@?BLE\5=fΑ;إ'/5X]@d0a@\KV0[PX'&S"O."T x7 )1A{<8[^}UW) }GE &jdI֒ߨ8D`F\ZL\0Cc$` LTBzA(Eb ([k/]mB"H\ih!ҚhUb-$i;- 88Xb+s"Q-_{cy L:B0%4a1('AX:2cB!P$koDKodJ5q{ȧo4}F)CYHEJہ)5Nd8  $Lƍ` %a |uM7zŒhƋRSXh5m[Wl(jH!2s%83hYj<^V`ܖDzow 2 F`jUriKP2+a}iPOVU{^/'yoV<כ͍3'/l'6} N[S\NSx_Gt:ՒS=pW{-eLn.|'_qr]CKtX{`y>mv=p@zw4"'Mhk4O#kH'4>sbm8@}>9EEоTH-oտEvfԧSƢqP-g;^l_JFoԂXVׄ SXs8vl`+CA'{OAګ7!H䗃Ed.5xs5_LJ':.]BPv墌1cP/H?d1 z.AkEtp?WС]i?K\]I~?;`BvY?FiCz]`뉒+Ӧǭ?D#u鰾K xX8eدy}Q`JdMһ:ĻlR)?wn4Tz:9tk-5RVe4<[{qbumOa矂?2Cx[!2g(7Q~lx0ݐ7x5+VУI+֤ܨ9KbYKl1[J(zJZJl,ROhg,%DbL'85_9M,1L`su5#_|<4sr RM9&b3J}Ee:(H̴z-a0_ =濗Үx1t~`ˌ=9(<.)JGoCt͒@72$詩UXK"O=gVBEiJC\<_VuudɠM D{s9i'Ѫ=W`ew'k4댧d*m֗A22oS>()_cLֺf8cyH8/n kI`Vz?"Ifx -xv3&fkb2ni"nI"a^w`,j Εt\ Rz^ nQz E}usc`f4J3Q6lX!BH[N#Œwz1x<0L~K%Y=-ń˱xyw㢒Y D`X=MøF^Wcv0hų`R@|ᶍv8Tmڋp2~)FU*.@0>a듕f(rblW)r0@뿠hU$$wd!Ѝ0nAl-/M%|W-Q|S-i=Y%H@Mh36fӇvJҒ6 '?#!uXA^DQ: oǘs﬷C>oN›mr1XՈ+]k0s 8࿙j)cZoj]yPhr/ZPnh-E멻ogkvzaZ '[f3<Tf֊issUb-&kUDzeHmv~ GKMPj($ƹ 2yX-Oe 桍9?ʉlV fOjM-ezPB>MӳxkuZN=)Dr/C*/ig&7?V[jlVv\D7Y~O;ḁ?6)*h?\.zkf/VW0^,d+ek~v*ћUBHR`?4N(|@-u#qۘG0{:~>Cx&TB"ds^6 c#{98>yL]Ղ}!$#deY6$O*]x^G5u>GKﶭ 4|=i6z^vSx/ [pK|.OA:(Jsg<s;x%#s|ğx~VZZ>v o9ḳHق \5ʼn/яK{hGyeESQ$VGG^5?rçH0C;\CWm' lE!ՠս #du9耍xGCC66/?xn.rn+/]_<Ŵ31zEFL mvM搪c?vCֺ͡xZ5xvZ/爧M+f\}X'8W(IiXj/:t$@޶:6O^[_AtgxuBeՓ*ޫӠwc[՗5z5YaJDVvq+Xyg*/V~Zhizrk}60B3gMF"ych5~MZWk9=Qcd&vU`Wc-:Y2-40Om(s׭`v'w޵{z=P.4{/y'K~;mۣ{`(Si{?n8{,`{\gyb׋]锇wa] c?9d1oOǑ8]l,pvv\ dBqx_uod :#yws'}]9y_;#8.;2ݛ#uq`qo9^G'8_ߊI9tZ[unApD۩H?:+g?rV3MS=QG(Y :Osqslr"Z=q,yW[kޏsEά`[s p>{TΝ讌NSn]NrA+MO(+z\.9=S [D6^yz @| $H~A+<'?\{++/Ơ<i;5|7'Znf{^ 涒x_q?^Á?5K.Z6GO@ǁ*ޞj@?X*?惂1Fkj.|_^AvɆCRZv>X\ 's6p{FCŗv{kS|=M]΋vI֨ S#K;ܑ=!:o6Ę]c3S*ryE#gwdy MolQ_q}u$V+PG.4O.}6{<7cgrMD߆ P+ ^o\ !J*4!;E'{y/c8kmjRb"]%9zWw^22\帳:oRݰKawsrYAggNW9K9NS.I&i%;m.]:U Sn;&{b ץ'Ov8OE-u<{_M.y6\M#gsʛ>a#y$ya+tRB&+Zv;ְ?Y_1؋ZXSk]n0~5xL#G"xe1&ID&X%/ `֍%'or*RqI>YcwxvgPWN_+t0e' ^2Y5@<;̇3E8_uSw+Wly>6w93,ˈb]+nU\ UIˠ/{[^#Wvżn=|O+=&?^N:&kz~Ji!B1G_H]E[Oe~c}n/nvSݎ#M'xJP*b*KrHqޖ3RRL*Vۓ"UÕ≻Pۍ[oj?^>7iʿ|7'^X}[{ڴI w7}o}qHzp͝?T'aʓ{3yyl4"O)=<=5&gsyx>>i<ޔs@|`}hDk6uk{p[bD^qT?Vkh\_d7Rب(5y,*[o {IM[G.)^)=ĕAf/>BnlelPvvތNа9ܞ߅b3^~ 'Y_vZj2&g  Za4\_]~5C/ 66" Cbm2Y/[e붩 *h:}іWD~ifjQ$Yvz-j7"pd‶e\G˻&K,,UYNԑ"PU]L7.[Ƶ8)lӥ2̤! *@n`]سpk&#uOhKŇ#nhkk<-ܷS>Ǧj%f:$ J9o]SJd%ZabP_}TLdV#D3epgrȌ]=srםT@%9;L,XƫǭNW>i6XI1\Nm 8-*D&[WYNjQP=t0v2tp۫ `Y3L2Uc\SWKan4{g|Xzˢ lX Wp4 ayԟ>"cO149~M2t?$aIn`2bf.'hk5A2: W?-g'={coZf֜ y&zh?KkniD7G}?4/d<"yoh2i=fzj,nl5)Ud&bzEji1{2H`o,LoW;~IZ`(kXOg vn$[[6 hQl\.Ǎ,8[Q7$AF `-&Y*iaPBZ!ETÖo_$~k+xŸzSc,DR~&y9کO[/d$C{8AB%+hY )y[WBj\ {3VIG^wDž\t x1.>'#G|ZWWswB:^{Mޅi{m/ b5^ëX!J'=+6:UއџO7jCPD\]۠@MzSeBb~H=-|OQ@-NwxX~uVsλH\&zI &oiw*•sVY'(Jx]OrSAǨaʩrhj脮=N75O6Ǡx1mˤl6$r_J9b|&"'\^lФpmΙӞ4_$8rjө$/ƅ@JN dm⍿[`T*ݻchʷl`򏯙3 >J =;/U jTBW¦;To׭nz9gTyH@h!(K*'t8C iĠiH>j+K8=;R.N6h증Ӌcvоʱh""oU9g~Xʋ J yTG(y!1ū?F+0Zqn_g2zI b,Y*^; ;EV[66ICzOE=,dl@&70 qo%5lT9 Pn)O#"P&(oͅ5@{/ڗB ݞßǘ%S1|c J׵KlEY>PC1 K(5 Պ?Ft:C-zTpBaG_ޢuecakثl|@(fRJ(g,lJT>k\RlPGlb}Tw"/ZUvآZ(a&*@b5#Po%BH~vmcˁ_QŰ.6$CuPH2vC). 6~Xz5JG]J<6c4rlKOVo4N$sI5m'{yj ?/gnFh𜫮_1yZٸhG..JɫCW%V% ˴A6 u(>;\Sp-=y!NRM%L7F'xS+ =˅"=ICft" 4Vm=jjq(_z!^)?=?h^ߒC55d^}_E:tw'nN[ x)y\ӈDeMdEG`2.Wd ?6\%VvTsȮBgDKBxGRHf&I&3[C}zAJإjL ,?T`/Fb?:]^?5TJ_=./h<=0~;svJ`\SccMEԒK]k0eX@|&*_z B(8n'zY#FDfo`ueLj@Вds_Ǯ:gW׭dk(us N? jck$&Y?QޜFb(>hh<"nǑCx}+=^_|;к±(!gtRCo݇!uSkIyv%0RR/+&pڞTG=8|&Gdv{ջ`[J_őy}55*A9 %9/:%RslTqӯv} cqf)ē8}\f)Wh5K9UJ0eҾ m>nc׬kkxߑyP+^7O ENON;@fXO?gs ˽f3%Ȝ wuznG 9a 69r 1|^ Y" ݚlƉ Q$;6!d¸9+0 Ӏ@ϭd[~ sUrw ^:,%B:$r&f "cJLKm|Mof`UUa{aO'bϝt")]٘̋ļj%[Os6IMD%pQ{hOU7opf"brXPo;ޝj0.` [ 9(M,#``˛Nj3㢰H^fI# {a!oˍ W=S zsV %ە){T{h-H[S<O& +xpn.h+tDLĪ.VG1|D `ՇٲӴ;۪ap:._0TXV>z+ʼ t0 G&v,nj Tf!r˥b| "MsT@cB,WL,08vC_IpDuN>,ii=HR^]LSs(F1`7*ռC/C'ġ].. cJhN.I.{jLA/.J9(N'2SA2eOqZK .%\$bxu A3G%Z4fcr"NoĠ"yA[b7{PC=IXshBAH) ]'29N}@5Agʉs}.y#CEN>o*r$pS_oLtSɐrb0͑C2t:&//*W A/y`H8L5W p s4R\UI$V5%\k_|+ёJ&Β^ ~ƍKTMNL *NYT%@9![0x*yj5+'L[t%sp7qW8g*ތ.pst`PqPO;DԻ#ǽ]Nޤp[x|4L.g~:N5{<=qa$OR{jl0'VIμU bc7Waz޲K| l`s`oh+ O17O*.cCR30SJ_i58\HZϐ<$12Ks.'ۻ?6:og)yimS+__Nprwۀ(iEVӴ%Ձ7z|"^~>R4u˷cS0|%V6nҸնOlO-ƒGbWmKreRt;SҊa7 {d+Xt,t^~u d^#בzqUO?lk^tA݌ tekhNCK_e fkY%WTOOα7,?܄P㽃ѯOOQ0v98rF܎7 @PFs1֍,DX5NoxhĚq>WMA k`4nvr>?]KZ4Ed[0vo/گY )ޟ} m*tt#pȦ9m׆Qve*5zyo@*P zݒEUor7i6,gq*[woTwEgއ쵛>W*Ű1#M8ֹ}$aWo މȇJJe"C0ZJ^eU,qL?׮23#'AgzmGl,H΄}L,hN7 :-16T,=rT2'agfˎdH̲G_U`sڛƁi[|>Vrn_э7>{Opлdo* -raVlgٽ 7cfy@. o6. Y}.^đc:5beJEp-?pJ>+Y*(3C]K 1o֋wuFD8 šY`#3+ɸ"F`h&es ' H\ƥhkN(S/Cq zX3{h9I<^6|=>}4͛[±X\ҕZrV;Xl7*c5 D]F ({]]QeO ,HF1 >rgr#+;m h}tT̀9n)\KnŞ]@/h枹DE[;] /|ZSnXWzk:7w-,E1Mw׮ƅ_s?̽NފMAbqxWɽnrѷs]X}ݦ؋] =1Q檁gqv= ';8'ם_N_׿N(f=GƦ2Tru)-P%E&S~)z)z<_FgdNC],'}:+SD)ZEoЀݵb5<0hҚY1^z.hrHT+9F,F.hFa3O@I`?r`i'Ppm'nq:Xt::4lrc#y5 f_pt Yc WPԐ\ mh 1eغ-d;| ) I3/lM~ 퍿c\b}i000 03Kd{lmbyfW3EJ%6N[Y(-'ƃ~SP/;sgTbU?C?Xh?^G#)F81?b쟣f5Fo 媧G%GxLe9]zl~n=6z$gr~~25g871SR꯷ }yDBd-CSTLDp71- 1H6IlX3&&ꂤ`-5N,3Z9x,| jlٛP͡꒥;l.Y:4[ކC3I%Au[7X|4)= Xt/s~Pā1_mdž L)#1ҘvlaJvz5yq^T#NLq4ywgQ-_^Gh]t%"Ώ}O瘧Y<EN0Mas͋l)"qelk%ز0Xe8ϸR;W᷄<_vTu."/VHl{['/<),<.6=&kL%#(4*Zn;R=3bS52(9nqYM,× Ĥ`N5dńJʼ(W]}5xFhsaI@ٰFnؒ*k4@ EՐF?}[Jf>4bh5HǵF-%  R4$C/gFn 'wƁC7NR<Ɠv%̹j~ZP(\RF|YBb9Q5eĹyw> j]AҲS qA :BGj/$sJǬݏ}7\'>Dz~O| FKnM GO^_A`({] þ'h{!7Nپg텬()!/4inMGb4qvC2KCG]I${F2HǢ}b[l" $[I Q}[8p9 ټz!q]CJ)ljZe[B<̓q#%Z}5)ysv>%bm yP#u<ͅLVYp.BɷĀkZpKa *p"%„+]ڿi]]"Eۜ=l`: 1t|t_3cP$ AgC zt@[#hAo:>ΔECGЙ#':x %!پ_k9󱗴Amys֥OfR)#v1&uloSyt32FoO-]L1>?) 3CΉm+Ltg3sŕ79L]l[@ q{hZT+]+=HEcs!1@J0PU2  ݜ<^v\_1B[39+Z&#br: K{QD!!G3guI qhy{,so$Zbv1ggjE8.ݘ4͜hɁw.]Zcvɘ:1q|f&jL.]X>_|L -]߂ EO S1cP{Y4AZ5gw#]5MG  ᄖ;ԻDH}㠝usTa?i ca,,L3aa4CjN٤K9˾3;p(%缽b r#)?Ȋbv$dg+~3k>ksw"bs%o0HF@";ސqN]2yzNpdS<2S#[|dCWi*kկ8Yxs m.9U9qhƹзGШw)zX#yf ;'\sBV! R=]Xq;C6 l5#L"uj7Lߑg104V( YX(u>a8<\-ij]8OI1B\ țJL[QDpñ$+".h<5GPH`p~2Tp *'gշj(ќP|^kX!q:>0 9Eu&!ܦ>y0!xfBZi`8 GA&#Rx6./E^17@H {311Vؕu/\ !^]Sr0z"U^uDE*Zύ>j^CVp": Du@@״k~nz6%bE3" kQ:گ2 'gpNJ3oOp%g7|S ,YYקsap> 3#3RjMRqg[rq4aR^z[톃v]0ݐϬF_;+~A4j h~ֿZv(`6a'*/FqvJ$?eSr3a='q34Wq!lTjՋ'FC\n`xFv{6 WOFGF1XƪH_ט֊={SQmeVhQ\'kc'afGl3;5YN䜲Y*XӍ/K<;Ql2e!VV1X1F t(*i7LE=IO4j&$OQ ¨4wUX b=m7FʦL+Щ{ӻdRPBWkQVV3y1RVlӕs_LkWάCe#~ZZݾHpױݳLhK1Kh{%`LPW6BRz#Z[@i+eIF4h E燐;v֜;G@v>Y]Nig% /mڭ ?(̮+tG|lVz?bo=N\65k)wr}a(Cƅ b"Isec_E݁ʮ/؀_ǜ<8t3HUñ1SGLm2PY$/Q@[5X$+=0 nQP P'ڑ=E13; XdF(@7Q V)g^-EL'|T/\ESо0M/Z (_ +Yuc;8 廮_Hr'~7~Y׏^yR׏.c+WYB)ܪ~[= "[QErDbUsW]υG]? Vgp]}~48s.ۺ~N+9~.Z'J ߢWsƼ8[VD]?>]XOsB㾮fϵV׏.ZU?gܫX]?>+H~⭮TZPkU?m鲮DžI& D/:[<\ g]? -l|Wò○WS׏qU?.bE]?~Đ z]?_@]?w7x?Pg]?EWQ4AW  W?oQ_]? *RO O{zI1vnKǯGD}⾮cb>0OlL׺~.oE C+겮eb U0HV:b-~4,>z]sFQ׏\ SOOjU?Vۺ~AxGE_W7W6 AhKkWvoy]N=[-Kpa=F>MbM6=+fq-,vmprطN9X6ab27fm^bȤGR+8 +Ջgm`>-*5MsRܕZZKRV?Jc{Ӽi[TN4Mv#E0Zolk?d 82xẘ g ~8N/ &Bmi ;g\G*2\1e >'3I^-BykM+_ 6=ȁL&kk[kh,kI@KiGGMz?;ڨNV3T#{G}4)F/~*vk7:?xQ`T}Y[,1Js|WFy}#ʢNI2(Zy=O2OJrzO/yDBڏsD1 0UV>=?S[S#~xX*"X>h˹!Vl?keB@qTQix@?9 좟}VAn |ڧx(s0şj),Zxmt^=n=G*hm %鰝,/ؗl)V6t/ҭlAu2 沊.)SK\' 7mbH)ً!TT}e38t̊AU7\hKjK>3N"܉ct? U{4PeбX["j9@5>%RYf6ޫ"PNF`K5КA:iZXxNcjc8u%2b+Y{= or59+vw[r"a='uv$'a1)F5+-C Xt{My6GN\7v&^"}`=~'HY[i|G ru'5ՐD=K[؃u^p:מ]h3xz=$s[[h%gp^5ivl׹m >67M`1T)tY9:~vn?M}! W:PkPasXʞW7078gY}p_2rD%ƽ!>Tgbv3dɄq7G ~X,@WtgNcעy}Dzn$L@U,_uo7\(#-zHgB>1!fh5.VeBBkGFZ0#s{ԇj,%@@=I42iUTz h'D4QG%ш$$Y^-)6y_\&ILcHV"^dDbԓe3Yx]{GE>ƺu+]cѷ5&MU/IL5fI6;VXu;g6vw**LŒ@br!5L6PDxQU [ Z_[s/D*>V:;Î<=D#8Lbx}z)O[팓q݇ }G(;;.g:a`3Uk:I~-Vȯ;NF~=owߟ{χ::HقC52䰿ʚ( ?8ݾqH!wTk޾FVl>TԓxfieyI!o*>k~/ @qL7ZMi_c'.a"3f9X9 lOk84IT!~PW=` 0U W|֋=i/>kE!N|PCJ8d*;(d)s&֙FYUONo<@ Pe.v9ߜbig8}Je-nV6s;g5wt0 ȝnjN IPD)Ϻ:zaq>nߔ_.͛S/vՉJ2J"LUzt žpvVsjhn#~z\CO=Zcg|59k=^ rj_57WA?UH|k/-*KO|9_'+f(w ˽wk6_sO+ՕE\?WжX"7xnJv=- jVztzd.6RZXg_ؿX1Zv. :d%[;uS‘|{v>L#l"NkP+Fָl6nvul*kMwRk^:SG*$&'?JPASU#g(` XHREb QXB[DOr H `+R[X X-B#a+˸DKoC}c?hW,:^hŇ!j>cև!j> y6 DLELfUl@a@B| ?E )ACǴ-`)EJy]2[ޜ9ӪZ5NnxKT{zJI핫3ǐYZ]Z@[g{jіyD:ԉg.t7h}p5 4UoYDvg}԰YrMRj+TfO,L2xAg(hu`fmX IúQVf OGPeTYG_fI[2Yd!Z6H*Doz1NI18iZȚiY9ݹ ܥ2CjaGh{Q-!^uҤňQ"4lt4 iXhXL|2 K`X]R(jT]ޛCdvߢ[FLN_TO0~K&rO 27n^4&dLU?-lݻrF @+<&dBػKՠBԢ:\.".C`NG%XI<}mi;kN6]GT;2B9-%+a$ ;osԒkU !udS;}7)Q} ~/ݴ?þ|QӌFĉ0޼XcK15#ȅ92S|6oT7NLg3l$>U}Y[sޑA"z I<= llj}x#Y[rcW[>MӸӏUv2m]Bd'MDpm"pg 'تH:%£iV=Eld䆒ħeνoͩq1>Fc*ׯ"[NY|<1ӟqB  t@_4- {Յv>96J"37F`qZ#M*7퇃,  Cu3>p+lG0 wi}>P+G0B~E``ϟF9.~Uu^9zC[b#XSnzj) . q+:)S `; o)͠+n`e; @PÔG=Pz8R=ICԺ栳Oz2f8O/ֿCxYZ9^V^`Cri%]*WBJ?vG%W Gœ1{z>~^&J7y}~m4Ws<5cLcB7T6gߐj2W??>WԄoeezx>Qzj<)J`s0y_>ϖhp{..ˌuojӈ磗=#RMEW/rn,X9nMM7]2`&ߧkNլOC&r>WRh)\ɕ2|R` @p>CXid4sO. 4mTJyУ%YʗY*VrIY*s|)m[|NΕ@Ix.S+r, \QQ*+E@ R )_hTV%+ϡdJ+h/M,P* Y콡<ɕ\zi#Ӵb{ ;;[ms跋>Y7e+%04Jr`_ R%iRĮ@s$*2Pզ}yצS;}30-jJYwed!/e @ E.W5m40Ms)ye_W hmuPm/)L TK+(u e;l o"ɎdE@+4RNn`k싺,h2 F򊵇4,|!*˒\eX5|_֧4CO`':~+Rl~= i!)+ly) SjeڷaPLRd OQoGlrf4Sxci`%])Qd^{~^~i+ lg+\ɕ-[El/KS$Umg!E|dpSf+綁Z۷3ș90K_1l7b)l=Rް#)/Yϣs,ekes#> Xrp[9E%S[%? ~n2J+@ )Dk<˶MފJ/Yfkg?COe놏۬Fy9dq@ٚ98ۭ%M[mcEy_pݺ}ўk[]s $'N;k (/` n :Pc:DNGu>h2#O/+|=߾?^D' endstream endobj 6 0 obj [5 0 R] endobj 23 0 obj <> endobj xref 0 24 0000000000 65535 f 0000000016 00000 n 0000000144 00000 n 0000040840 00000 n 0000000000 00000 f 0000042658 00000 n 0000173795 00000 n 0000040891 00000 n 0000041280 00000 n 0000045640 00000 n 0000042957 00000 n 0000042844 00000 n 0000041680 00000 n 0000042097 00000 n 0000042145 00000 n 0000042728 00000 n 0000042759 00000 n 0000042992 00000 n 0000045713 00000 n 0000045909 00000 n 0000047199 00000 n 0000053461 00000 n 0000119049 00000 n 0000173818 00000 n trailer <<786BA22E66854F3FBC5B52869C6F5349>]>> startxref 174028 %%EOF