Skip to content

Instantly share code, notes, and snippets.

@heygarrett
Created July 14, 2017 03:26
Show Gist options
  • Save heygarrett/0788f7d32c459b719f027fd8db69ba1e to your computer and use it in GitHub Desktop.
Save heygarrett/0788f7d32c459b719f027fd8db69ba1e to your computer and use it in GitHub Desktop.
import Foundation
import Darwin
public final class DiceRoll {
private let arguments: [String]
// Properties for storing the final results of the roll
var roll: [Int]
var modifier: Int
public init(arguments: [String] = CommandLine.arguments, roll: [Int] = [], mod: Int = 0) {
self.arguments = arguments
self.roll = roll
self.modifier = mod
}
// Get input from the user
func getInput() -> String? {
print("\nDice: ", terminator: "")
let newInput = readLine()
if newInput == "q" {
print("Thanks for rolling with me!")
exit(0)
}
return newInput
}
// Parse the input into usable tokens
func parseInput(_ input: String) -> [Int]? {
// Match the input to the pattern of a standard roll
guard input.range(of: "^[1-9]+[0-9]*d[1-9]+[0-9]*(\\+[1-9]+[0-9]*)*$", options: .regularExpression) != nil else {
print ("That is not a valid roll.")
return nil
}
// This is my attempt to avoid "magic numbers"
var rawRoll = input
var modifier = 0
var dice = 0
var sides = 0
if input.characters.contains("+") {
// This splits the input string on the plus sign into two strings
let match = input.characters.split(whereSeparator: { $0 == "+" })
.map(String.init)
modifier = Int(match[1])!
rawRoll = match[0]
}
// This splits the roll into the number of dice and number of sides
let match = rawRoll.characters.split(whereSeparator: { $0 == "d" })
.map(String.init)
dice = Int(match[0])!
sides = Int(match[1])!
return [dice, sides, modifier]
}
// We need to calculate the roll for each individual die
func calculateRoll(_ dice: Int, _ sides: Int) -> [Int]? {
var roll: [Int] = []
for _ in 1 ... dice {
// I realize this is ugly, but I've seen Apple do the same thing
// in their learning materials
roll.append(Int(arc4random_uniform(UInt32(sides))) + 1)
}
return roll
}
// This provides a centralized function for executing the roll.
// That way I'm not repeating any code by providing support for arguments
// or just by running the program in a loop.
func rollDice(_ input: String) -> String? {
guard let tokens = parseInput(input),
let finalRoll = calculateRoll(tokens[0], tokens[1]) else {
return nil
}
// Here, we save the results to the class properties
self.roll = finalRoll
if tokens[2] != 0 {
self.modifier = tokens[2]
}
// and format the output for the user
var output = "Roll: \(self.roll)"
if self.modifier != 0 {
output += "\nModifier: \(self.modifier)"
}
output += "\nTotal: \(self.roll.reduce(0, +))"
return output
}
func printInstructions() {
print("Enter your roll as XdY where X is the number of dice and Y is the number of sides.")
print("Optionally, you can add a modifier: XdY+Z where Z is the modifier you'd like to add.")
print("Enter q to quit.")
}
public func run() throws {
if self.arguments.count < 2 {
printInstructions()
while true {
if let input = getInput(),
let output = rollDice(input) {
print(output)
}
}
} else if self.arguments.count == 2 {
if let output = rollDice(self.arguments[1]) {
print(output)
}
} else {
print("That is not a valid roll.")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment