-
-
Save heygarrett/0788f7d32c459b719f027fd8db69ba1e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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