├── .gitignore ├── .travis.yml ├── CardKit.podspec.json ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Card.swift ├── Deck.swift ├── Suit.swift └── Value.swift └── Tests └── CardKitTests ├── CardSpec.swift ├── DeckSpec.swift ├── SuitSpec.swift ├── ValueSpec.swift └── XCTest.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | Packages 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | script: 4 | - swift test 5 | -------------------------------------------------------------------------------- /CardKit.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CardKit", 3 | "version": "1.0.0", 4 | "summary": "CardKit provides model structures for building playable card games", 5 | "homepage": "https://github.com/kylef/CardKit", 6 | "authors": { "Kyle Fuller": "kyle@fuller.li" }, 7 | "source": { "git": "https://github.com/kylef/CardKit.git", "tag": "1.0.0" }, 8 | "license": { "type": "BSD", "file": "LICENSE" }, 9 | "platforms": { 10 | "ios": "8.0", 11 | "osx": "10.7", 12 | "watchos": "2.0", 13 | "tvos": "9.0" 14 | }, 15 | "source_files": "Sources/*.swift" 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Kyle Fuller 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | 4 | let package = Package( 5 | name: "CardKit", 6 | dependencies: [ 7 | .Package(url: "https://github.com/kylef/Spectre.git", majorVersion: 0, minor: 7), 8 | ] 9 | ) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CardKit 2 | 3 | CardKit provides model structures for building playable card games. 4 | 5 | ## Usage 6 | 7 | Creating a card 8 | 9 | ```swift 10 | let card = Card(suit: .Heart, value: .Ace) 11 | ``` 12 | 13 | Creating a deck of cards 14 | 15 | ```swift 16 | let deck = Card.all() 17 | ``` 18 | 19 | Shuffling (in-place) 20 | 21 | ```swift 22 | deck.shuffle() 23 | ``` 24 | 25 | New shuffled array of cards 26 | 27 | ```swift 28 | let shuffledDeck = deck.shuffled() 29 | ``` 30 | 31 | ## License 32 | 33 | CardKit is licensed under the BSD license. See [LICENSE](LICENSE) for more 34 | info. 35 | -------------------------------------------------------------------------------- /Sources/Card.swift: -------------------------------------------------------------------------------- 1 | public struct Card : Equatable { 2 | public let suit: Suit 3 | public let value: Value 4 | 5 | public init(suit: Suit, value: Value) { 6 | self.suit = suit 7 | self.value = value 8 | } 9 | 10 | /// Returns all possible cards 11 | public static func all() -> [Card] { 12 | return Suit.all.reduce([]) { accumulator, suit in 13 | accumulator + Value.all.map { 14 | Card(suit: suit, value: $0) 15 | } 16 | } 17 | } 18 | } 19 | 20 | public func == (lhs: Card, rhs: Card) -> Bool { 21 | return lhs.suit == rhs.suit && lhs.value == rhs.value 22 | } 23 | 24 | extension Card : Hashable { 25 | public var hashValue: Int { 26 | return suit.hashValue ^ value.hashValue 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Deck.swift: -------------------------------------------------------------------------------- 1 | import Darwin 2 | 3 | extension Collection { 4 | /// Returns a shuffled array 5 | public func shuffled() -> [Generator.Element] { 6 | var array = Array(self) 7 | array.shuffle() 8 | return array 9 | } 10 | } 11 | 12 | extension MutableCollection where Index == Int { 13 | /// Shuffle elements in-place 14 | mutating public func shuffle() { 15 | for index in startIndex ..< (endIndex - 1) { 16 | let newIndex = Int(arc4random_uniform(UInt32(endIndex - index))) + index 17 | 18 | if newIndex != index { 19 | swap(&self[index], &self[newIndex]) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Suit.swift: -------------------------------------------------------------------------------- 1 | public enum Suit { 2 | case spade 3 | case heart 4 | case diamond 5 | case club 6 | 7 | /// Returns all possible suits 8 | static var all: [Suit] { 9 | return [.spade, .heart, .diamond, .club] 10 | } 11 | } 12 | 13 | extension Suit : CustomStringConvertible { 14 | public var description: String { 15 | switch self { 16 | case .spade: 17 | return "Spade" 18 | case .heart: 19 | return "Heart" 20 | case .diamond: 21 | return "Diamond" 22 | case .club: 23 | return "Club" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Value.swift: -------------------------------------------------------------------------------- 1 | public enum Value { 2 | case ace 3 | case two 4 | case three 5 | case four 6 | case five 7 | case six 8 | case seven 9 | case eight 10 | case nine 11 | case ten 12 | case jack 13 | case queen 14 | case king 15 | 16 | /// Returns all possible values 17 | static var all: [Value] { 18 | return [ 19 | .ace, .two, .three, .four, .five, .six, .seven, .eight, .nine, .ten, 20 | .jack, .queen, .king 21 | ] 22 | } 23 | } 24 | 25 | extension Value : CustomStringConvertible { 26 | public var description: String { 27 | switch self { 28 | case .ace: 29 | return "A" 30 | case .two: 31 | return "2" 32 | case .three: 33 | return "3" 34 | case .four: 35 | return "4" 36 | case .five: 37 | return "5" 38 | case .six: 39 | return "6" 40 | case .seven: 41 | return "7" 42 | case .eight: 43 | return "8" 44 | case .nine: 45 | return "9" 46 | case .ten: 47 | return "10" 48 | case .jack: 49 | return "J" 50 | case .queen: 51 | return "Q" 52 | case .king: 53 | return "K" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/CardKitTests/CardSpec.swift: -------------------------------------------------------------------------------- 1 | import Spectre 2 | import CardKit 3 | 4 | 5 | func testCard() { 6 | describe("Card") { 7 | let card = Card(suit: .spade, value: .ace) 8 | 9 | $0.it("has a suit") { 10 | try expect(card.suit) == .spade 11 | } 12 | 13 | $0.it("has a value") { 14 | try expect(card.value) == .ace 15 | } 16 | 17 | $0.describe("equatable") { 18 | $0.it("returns true when the card has the same suit and value") { 19 | try expect(card) == Card(suit: .spade, value: .ace) 20 | } 21 | 22 | $0.it("returns false when the card has a different suit") { 23 | try expect(card) != Card(suit: .heart, value: .ace) 24 | } 25 | 26 | $0.it("returns false when the card has a different value") { 27 | try expect(card) != Card(suit: .spade, value: .two) 28 | } 29 | } 30 | 31 | $0.it("is hashable") { 32 | try expect(card.hashValue) == Card(suit: .spade, value: .ace).hashValue 33 | } 34 | 35 | $0.it("provides all cards") { 36 | let deck = Card.all() 37 | 38 | // Ensure that all cards are unique 39 | try expect(Set(deck).count) == 52 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/CardKitTests/DeckSpec.swift: -------------------------------------------------------------------------------- 1 | import Spectre 2 | import CardKit 3 | 4 | 5 | func testDeck() { 6 | describe("Deck") { 7 | $0.describe("shuffling") { 8 | $0.it("returns a new deck in a different order") { 9 | let deck = Card.all() 10 | let shuffledDeck = deck.shuffled() 11 | 12 | try expect(deck.count) == shuffledDeck.count 13 | } 14 | 15 | $0.it("shuffles a mutable collection in-place") { 16 | let deck = Card.all() 17 | var shuffledDeck = deck 18 | shuffledDeck.shuffle() 19 | 20 | try expect(deck.count) == shuffledDeck.count 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/CardKitTests/SuitSpec.swift: -------------------------------------------------------------------------------- 1 | import Spectre 2 | import CardKit 3 | 4 | 5 | func testSuit() { 6 | describe("Suit") { 7 | $0.it("is equatable") { 8 | try expect(Suit.spade) == Suit.spade 9 | try expect(Suit.spade) != Suit.heart 10 | } 11 | 12 | $0.it("is hashable") { 13 | try expect(Suit.spade.hashValue) == Suit.spade.hashValue 14 | } 15 | 16 | $0.describe("custom string convertible") { 17 | $0.it("describes spade") { 18 | try expect(Suit.spade.description) == "Spade" 19 | } 20 | 21 | $0.it("describes heart") { 22 | try expect(Suit.heart.description) == "Heart" 23 | } 24 | 25 | $0.it("describes diamond") { 26 | try expect(Suit.diamond.description) == "Diamond" 27 | } 28 | 29 | $0.it("describes club") { 30 | try expect(Suit.club.description) == "Club" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/CardKitTests/ValueSpec.swift: -------------------------------------------------------------------------------- 1 | import Spectre 2 | import CardKit 3 | 4 | 5 | func testValue() { 6 | describe("Value") { 7 | $0.it("is equatable") { 8 | try expect(Value.ace) == Value.ace 9 | try expect(Value.ace) != Value.two 10 | } 11 | 12 | $0.it("is hashable") { 13 | try expect(Value.ace.hashValue) == Value.ace.hashValue 14 | } 15 | 16 | $0.describe("custom string convertible") { 17 | $0.it("describes an ace") { 18 | try expect(Value.ace.description) == "A" 19 | } 20 | 21 | $0.it("describes a two") { 22 | try expect(Value.two.description) == "2" 23 | } 24 | 25 | $0.it("describes a three") { 26 | try expect(Value.three.description) == "3" 27 | } 28 | 29 | $0.it("describes a four") { 30 | try expect(Value.four.description) == "4" 31 | } 32 | 33 | $0.it("describes a five") { 34 | try expect(Value.five.description) == "5" 35 | } 36 | 37 | $0.it("describes a six") { 38 | try expect(Value.six.description) == "6" 39 | } 40 | 41 | $0.it("describes a seven") { 42 | try expect(Value.seven.description) == "7" 43 | } 44 | 45 | $0.it("describes an eight") { 46 | try expect(Value.eight.description) == "8" 47 | } 48 | 49 | $0.it("describes a nine") { 50 | try expect(Value.nine.description) == "9" 51 | } 52 | 53 | $0.it("describes a ten") { 54 | try expect(Value.ten.description) == "10" 55 | } 56 | 57 | $0.it("describes a jack") { 58 | try expect(Value.jack.description) == "J" 59 | } 60 | 61 | $0.it("describes a queen") { 62 | try expect(Value.queen.description) == "Q" 63 | } 64 | 65 | $0.it("describes a king") { 66 | try expect(Value.king.description) == "K" 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/CardKitTests/XCTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | 4 | class CardKitTests: XCTestCase { 5 | func testRunCardKit() { 6 | testSuit() 7 | testValue() 8 | testCard() 9 | testDeck() 10 | } 11 | } 12 | --------------------------------------------------------------------------------