I have created a simple calculator that will assist in creating magic items from the tabletop RPG Pathfinder. Here are the full requirements, and if you want a more concise version, here is a player generated summary of the process.
I have been trying to get into F# and functional programming in general. I come from a strong object-oriented background and it has been difficult in ignoring those habits. Please focus on whether or not this is idiomatic F#. I would love to hear if there is a better way to write any of it. And, if you find bug, I'd be happy to know that too.
Questions
A couple of things specifically that I feel could be better would be the DC calculation. I wasn't sure how to approach applying the rush function only if the item was rushed. I also tried finding a place where I could apply functional composition, but I think I needed all the intermediate steps that I stop on.
Code
let baseDC casterLevel =
5 + casterLevel
let rush dc =
dc + 5
let ignoreRequirements requirements dc =
dc + (5 * requirements)
let getBuildPrice basePrice =
basePrice * 0.5
let getTime basePrice =
floor (8.0 * basePrice / 1000.0)
let getMinBonusForNoCursed dc =
dc - 6
let buildDaysWhileAdventuring isRushed buildTime =
let adventuringBuildTime = buildTime / 4.0
floor (if isRushed then adventuringBuildTime else adventuringBuildTime * 2.0)
[<EntryPoint>]
let main argv =
printfn "%A" argv
let isRushed = true
let casterLevel = 1
let requirementsIgnored = 1
let basePrice = 5000.0
let buildPrice = getBuildPrice basePrice
let buildTime = getTime basePrice
let daysAdventuring = buildDaysWhileAdventuring isRushed buildTime
let DC =
match isRushed with
| false -> baseDC casterLevel |> ignoreRequirements requirementsIgnored
| true -> baseDC casterLevel |> ignoreRequirements requirementsIgnored |> rush
let minDC = getMinBonusForNoCursed DC
printfn "Min/DC: %i/%i Cost: %.2f Days/Hours: %.1f/%.1f" minDC DC
buildPrice daysAdventuring buildTime
0 // Return code
1 Answer 1
I see a few problems with this, most notably, you have a plethora of magic numbers.
let baseDC casterLevel = 5 + casterLevel let rush dc = dc + 5 let ignoreRequirements requirements dc = dc + (5 * requirements) let getBuildPrice basePrice = basePrice * 0.5 let getTime basePrice = floor (8.0 * basePrice / 1000.0) let getMinBonusForNoCursed dc = dc - 6 let buildDaysWhileAdventuring isRushed buildTime = let adventuringBuildTime = buildTime / 4.0 floor (if isRushed then adventuringBuildTime else adventuringBuildTime * 2.0)
In order, I see:
5 + casterLevel
: what is5
?dc + 5
: what is5
? Is it the same as #1?5 * requirements
: what is5
? Is it the same as #1 or #2?basePrice * 0.5
: what is0.5
? Is it1 / 2
or something different?8.0 * basePrice
: what is8.0
?#5 / 1000.0
: what is1000.0
?dc - 6
: what is6
?buildTime / 4.0
: what is4.0
?adventuringBuildTime * 2.0
: what is2.0
, is it4.0 / 2
, is it the4.0
from #8 / 2?
For a functional program, it's not too bad. I don't like how many times you use if
(I always prefer match
), but when dealing with boolean types it does quite fine.