Skip to content

Commit

Permalink
Add Dice class to create dice rolls directly without parsing strings
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoconti83 committed Jul 24, 2016
1 parent fd94f14 commit ae39813
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 12 deletions.
20 changes: 19 additions & 1 deletion DiceExpression.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
54D7C7B71D44F8AA000E1522 /* RandomTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7B51D44F8A8000E1522 /* RandomTable.swift */; };
54D7C7B91D44FC74000E1522 /* RandomTableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7B81D44FC74000E1522 /* RandomTableTests.swift */; };
54D7C7BA1D44FC74000E1522 /* RandomTableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7B81D44FC74000E1522 /* RandomTableTests.swift */; };
54D7C7BC1D4507EB000E1522 /* Dice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7BB1D4507EB000E1522 /* Dice.swift */; };
54D7C7BD1D4507EB000E1522 /* Dice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7BB1D4507EB000E1522 /* Dice.swift */; };
54D7C7BF1D450886000E1522 /* Rollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7BE1D450886000E1522 /* Rollable.swift */; };
54D7C7C01D450886000E1522 /* Rollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7BE1D450886000E1522 /* Rollable.swift */; };
54D7C7D51D450F81000E1522 /* DiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7D41D450F81000E1522 /* DiceTests.swift */; };
54D7C7D61D450F81000E1522 /* DiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D7C7D41D450F81000E1522 /* DiceTests.swift */; };
54E38A0F1C9206D100EF9F89 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E38A0E1C9206D100EF9F89 /* Random.swift */; };
54E38A101C9206D100EF9F89 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E38A0E1C9206D100EF9F89 /* Random.swift */; };
54E38A121C92320A00EF9F89 /* RandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E38A111C92320A00EF9F89 /* RandomTests.swift */; };
Expand Down Expand Up @@ -68,6 +74,9 @@
54B181D51C863E940089C077 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
54D7C7B51D44F8A8000E1522 /* RandomTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTable.swift; sourceTree = "<group>"; };
54D7C7B81D44FC74000E1522 /* RandomTableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTableTests.swift; sourceTree = "<group>"; };
54D7C7BB1D4507EB000E1522 /* Dice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dice.swift; sourceTree = "<group>"; };
54D7C7BE1D450886000E1522 /* Rollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rollable.swift; sourceTree = "<group>"; };
54D7C7D41D450F81000E1522 /* DiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiceTests.swift; sourceTree = "<group>"; };
54E38A0E1C9206D100EF9F89 /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = "<group>"; };
54E38A111C92320A00EF9F89 /* RandomTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -132,11 +141,13 @@
isa = PBXGroup;
children = (
54134BCA1C85832300556F55 /* Collections.swift */,
54D7C7BB1D4507EB000E1522 /* Dice.swift */,
54D7C7BE1D450886000E1522 /* Rollable.swift */,
54134BCB1C85832300556F55 /* DiceExpression.swift */,
54134BCC1C85832300556F55 /* ResultWithLog.swift */,
54134BB31C85826A00556F55 /* DiceExpression.h */,
54E38A0E1C9206D100EF9F89 /* Random.swift */,
54D7C7B51D44F8A8000E1522 /* RandomTable.swift */,
54134BB31C85826A00556F55 /* DiceExpression.h */,
54134BB51C85826A00556F55 /* Info.plist */,
);
path = Source;
Expand All @@ -151,6 +162,7 @@
54134BD21C85832E00556F55 /* ResultWithLogTests.swift */,
54E38A111C92320A00EF9F89 /* RandomTests.swift */,
54D7C7B81D44FC74000E1522 /* RandomTableTests.swift */,
54D7C7D41D450F81000E1522 /* DiceTests.swift */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -334,6 +346,8 @@
54D7C7B61D44F8A8000E1522 /* RandomTable.swift in Sources */,
54E38A0F1C9206D100EF9F89 /* Random.swift in Sources */,
54134BCF1C85832300556F55 /* ResultWithLog.swift in Sources */,
54D7C7BF1D450886000E1522 /* Rollable.swift in Sources */,
54D7C7BC1D4507EB000E1522 /* Dice.swift in Sources */,
54134BCD1C85832300556F55 /* Collections.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -346,6 +360,7 @@
54134BD71C85833F00556F55 /* DiceExpressionTests.swift in Sources */,
54D7C7B91D44FC74000E1522 /* RandomTableTests.swift in Sources */,
54134BD81C85833F00556F55 /* ResultWithLogTests.swift in Sources */,
54D7C7D51D450F81000E1522 /* DiceTests.swift in Sources */,
54134BD61C85833F00556F55 /* CollectionsTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -358,6 +373,8 @@
54D7C7B71D44F8AA000E1522 /* RandomTable.swift in Sources */,
54E38A101C9206D100EF9F89 /* Random.swift in Sources */,
54B181CB1C863B7C0089C077 /* ResultWithLog.swift in Sources */,
54D7C7C01D450886000E1522 /* Rollable.swift in Sources */,
54D7C7BD1D4507EB000E1522 /* Dice.swift in Sources */,
54B181C91C863B7C0089C077 /* Collections.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -369,6 +386,7 @@
54B181CE1C863B820089C077 /* DiceExpressionTests.swift in Sources */,
54D7C7BA1D44FC74000E1522 /* RandomTableTests.swift in Sources */,
54B181CD1C863B820089C077 /* CollectionsTests.swift in Sources */,
54D7C7D61D450F81000E1522 /* DiceTests.swift in Sources */,
54B181CF1C863B820089C077 /* ResultWithLogTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
74 changes: 74 additions & 0 deletions Source/Dice.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2016 Marco Conti
//
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation

/// A die with faces, which can be rolled multiple times
public struct Dice : Rollable {

/// Number of faces on the die (e.g. 6 for a cubic, 1-to-6 die)
public let faces: UInt32

/// Number of rolls for this die (e.g. 2 if rolling the die twice and adding the results)
public let repetitions: UInt32

/// Dice expression used to calculate the roll result
private let expression : DiceExpression

/// - param faces: number of faces on the die (e.g. 6 for a cubic, 1-to-6 die)
/// - param repetitions: number of rolls for this die (e.g. 2 if rolling the die twice and adding the results)
public init(faces: UInt32, repetitions: UInt32 = 1) {
self.faces = faces
self.repetitions = repetitions
self.expression = DiceExpression(tokens: [
SignedDiceExpressionComponent(
sign: Sign.Plus,
component: DiceExpressionComponent.Dice(faces: self.faces, repetitions: self.repetitions
))
])
}

public func roll() -> ResultWithLog<Int> {
return self.expression.roll()
}
}

/// A coin (two-sided die)
public let D2 = Dice(faces: 2)
/// A die with 4 faces, a tetrahedron
public let D4 = Dice(faces: 4)
/// A die with 6 faces, a cube
public let D6 = Dice(faces: 6)
/// A die with 8 faces, an octahedron
public let D8 = Dice(faces: 8)
/// A die with 10 faces, a decahedron
public let D10 = Dice(faces: 10)
/// A die with 12 faces, a dodecahedron
public let D12 = Dice(faces: 12)
/// A die with 20 faces, an icosahedron
public let D20 = Dice(faces: 20)
/// A die with 100 faces, also known as "percentage die"
public let D100 = Dice(faces: 100)
24 changes: 13 additions & 11 deletions Source/DiceExpression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public func -(lhs: DiceExpression, rhs: DiceExpression) -> DiceExpression {
// MARK: - Private implementation

/// A sign and a die roll
private struct SignedDiceExpressionComponent {
struct SignedDiceExpressionComponent {
let sign : Sign
let component: DiceExpressionComponent
}
Expand All @@ -129,7 +129,7 @@ extension SequenceType where Generator.Element == SignedDiceExpressionComponent
extension DiceExpression {

/// Initializes from already parsed signed dice expression components
private init(tokens: [SignedDiceExpressionComponent]) {
init(tokens: [SignedDiceExpressionComponent]) {
self.components = tokens
}
}
Expand Down Expand Up @@ -161,7 +161,7 @@ private func parseSignAndRollTokens(array: [String]) throws -> [SignedDiceExpres
}

/// Operations
private enum Sign : String {
enum Sign : String {
case Plus = "+"
case Minus = "-"

Expand All @@ -187,21 +187,23 @@ private enum Sign : String {
}

/// An individual token inside a dice expression
private enum DiceExpressionComponent {
enum DiceExpressionComponent {

/// A proper dice roll, such as 3d6
case Dice(sides: UInt32, repetitions: UInt)
case Dice(faces: UInt32, repetitions: UInt32)

/// A constant int value
case Constant(value: Int)

/// Roll and returns result
func roll() -> Int {
switch(self) {
case let .Dice(sides, repetitions):
case let .Dice(faces, repetitions):
var total = 0
for _ in 0..<repetitions {
total += Int(arc4random_uniform(sides))+1
if faces > 0 {
total += Int(arc4random_uniform(faces))+1
}
}
return total
case let .Constant(value):
Expand All @@ -220,8 +222,8 @@ private enum DiceExpressionComponent {
if(splitted.count != 2) {
return nil
}
if let sides = UInt32(splitted[0]), let repetitions = UInt(splitted[1]) {
self = .Dice(sides: sides, repetitions: repetitions)
if let sides = UInt32(splitted[0]), let repetitions = UInt32(splitted[1]) {
self = .Dice(faces: sides, repetitions: repetitions)
return
}
else {
Expand All @@ -232,8 +234,8 @@ private enum DiceExpressionComponent {

var description : String {
switch(self) {
case let .Dice(sides, repetitions):
return "\(sides)d\(repetitions)"
case let .Dice(faces, repetitions):
return "\(faces)d\(repetitions)"
case let .Constant(value):
return "\(value)"
}
Expand Down
2 changes: 2 additions & 0 deletions Source/Random.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Returns a random integer between two integer, extremes included.
- parameter from: one of the extremes of the range to pick from
- parameter to: the other exteme of the range to pick from
- note: The randomness generated by this function is not guaranteed to be cryptographically secure
- precondition: due to the limitation of `arc4random`, the difference between from and to can not be
greated than UInt32.max
*/
public func randomInteger(from: Int, to: Int) -> Int {
let (lowerLimit, higherLimit) = from < to ? (from, to) : (to, from)
Expand Down
34 changes: 34 additions & 0 deletions Source/Rollable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2016 Marco Conti
//
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation

/// Can be rolled to return a random Int
public protocol Rollable {

/// Randomly select an integer
func roll() -> ResultWithLog<Int>
}
Loading

0 comments on commit ae39813

Please sign in to comment.