Rewrote basically half the app

+ reamde
+ noDebug.xcscheme
+ ui improvements
+max die is 100+overflow protextion
+fix giant padding in diceview
+rewrite rng2()
+ui friedlification
+rewrite arrCombine
This commit is contained in:
neon443
2024-12-01 11:18:15 +00:00
parent b94536cea1
commit 64a517333b
7 changed files with 236 additions and 194 deletions

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
# RNG_Swift
A fully featured Randomizer written in Swift with UI in SwiftUI!
## Features
- Number generator
- Password generator
- Die Generator

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A9794C7E2CB70ADA00527984"
BuildableName = "RNG.app"
BlueprintName = "RNG"
ReferencedContainer = "container:RNG.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A9794C7E2CB70ADA00527984"
BuildableName = "RNG.app"
BlueprintName = "RNG"
ReferencedContainer = "container:RNG.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A9794C7E2CB70ADA00527984"
BuildableName = "RNG.app"
BlueprintName = "RNG"
ReferencedContainer = "container:RNG.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -12,44 +12,46 @@ struct ContentView: View {
VStack { VStack {
NavigationSplitView { NavigationSplitView {
List { List {
NavigationLink { Section("Generate...") {
NumberView() NavigationLink {
.navigationTitle("Random Number Generator") NumberView()
} label: { .navigationTitle("Numbers")
HStack { } label: {
Text("Random Number Generator") HStack {
Spacer() Text("Numbers")
Image(systemName: "textformat.123") Spacer()
} Image(systemName: "textformat.123")
} }
NavigationLink { }
DiceView() NavigationLink {
.navigationTitle("Dice") DiceView()
} label: { .navigationTitle("Die")
HStack { } label: {
Text("Dice") HStack {
Spacer() Text("Die")
Image(systemName: "die.face.1") Spacer()
Image(systemName: "die.face.2.fill") Image(systemName: "die.face.1")
Image(systemName: "die.face.3") Image(systemName: "die.face.2.fill")
Image(systemName: "die.face.4.fill") Image(systemName: "die.face.3")
Image(systemName: "die.face.5") Image(systemName: "die.face.4.fill")
Image(systemName: "die.face.6.fill") Image(systemName: "die.face.5")
} Image(systemName: "die.face.6.fill")
} }
NavigationLink { }
PasswordView() NavigationLink {
.navigationTitle("Password") PasswordView()
} label: { .navigationTitle("Passwords")
HStack { } label: {
Text("Password") HStack {
Spacer() Text("Passwords")
Image(systemName: "rectangle.and.pencil.and.ellipsis") Spacer()
Image(systemName: "rectangle.and.pencil.and.ellipsis")
}
} }
} }
.navigationTitle("RNG")
.navigationBarTitleDisplayMode(.inline)
} }
.navigationTitle("RNG")
.navigationBarTitleDisplayMode(.inline)
} detail: { } detail: {
Image(systemName: "dice.fill") Image(systemName: "dice.fill")
} }
@@ -57,3 +59,6 @@ struct ContentView: View {
} }
} }
#Preview {
ContentView()
}

View File

@@ -8,26 +8,36 @@
import SwiftUI import SwiftUI
struct DiceView: View { struct DiceView: View {
@State var generated: [Int] = [0] @State var advanced: Bool = false
@State var generated: [Int] = []
@State var displayDies: [Int] = [] @State var displayDies: [Int] = []
@State var displayMultiDieMode = "" @State var displayMultiDieMode = ""
@State var multiDieMode = "plus" @State var multiDieMode = "plus"
@State var result = 0 @State var result: UInt = 0
@State var resultDescription = "" @State var resultDescription = ""
@State var history: [Int] = []
let columns = [GridItem(.adaptive(minimum: 25, maximum: 75))] let columns = [GridItem(.adaptive(minimum: 25, maximum: 75))]
@State var dies: Double = 2 @State var dies: Double = 2
var body: some View { var body: some View {
VStack { VStack {
List { List {
Section("Number of die") { Section("Number of die") {
Toggle(isOn: $advanced) {
Text("Advanced Mode")
}
HStack { HStack {
Text( String( Int(dies) ) ) Text(String(Int(dies)))
.font(.system(size: 25, weight: .bold)) .font(.system(size: 20, weight: .heavy))
.frame(width: ((dies > 99) ? 40 : (dies > 9) ? 30 : 20))
Divider() Divider()
Text("1") Text("1")
Slider(value: $dies, in: 1...20, step: 1) if advanced {
Text("20") Slider(value: $dies, in: 1...100, step: 1)
Text("100")
} else {
Slider(value: $dies, in: 1...20, step: 1)
Text("20")
}
} }
} }
Section("Multi die mode") { Section("Multi die mode") {
@@ -37,36 +47,36 @@ struct DiceView: View {
}.pickerStyle(SegmentedPickerStyle()) }.pickerStyle(SegmentedPickerStyle())
} }
Section("Visual") { Section("Visual") {
HStack { if displayDies.isEmpty {
Spacer() Text("Tap Generate to get started!")
if displayDies.isEmpty { .font(.headline)
Text("Results are visualised here when you press the generate button") .fontWeight(.heavy)
.font(.subheadline) .frame(alignment: .center)
} else { } else {
LazyVGrid(columns: columns, spacing: 10) { LazyVGrid(columns: columns, spacing: 2.5) {
ForEach(0..<displayDies.count, id: \.self) { index in ForEach(0..<displayDies.count, id: \.self) { index in
Image(systemName: "die.face.\(displayDies[index])") Image(systemName: "die.face.\(displayDies[index])")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
if index != displayDies.count-1 {
Image(systemName: displayMultiDieMode)
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 50, height: 50) .frame(width: 15, height: 15)
if index != displayDies.count-1 {
Image(systemName: displayMultiDieMode)
.resizable()
.scaledToFit()
.frame(width: 10, height: 10)
}
} }
}.padding() }
} }
} }
} }
} }
Text(String(result)) Text(String(result))
.font(.system(size: 75, weight: .bold)) .font(.system(size: 50, weight: .bold))
.foregroundColor(.gray) .foregroundColor(.gray)
.frame(height: 50) .frame(height: 40)
Text(resultDescription) Text(resultDescription)
.frame(height: 20) .frame(height: 10)
.font(.system(size: 10))
Button { Button {
generated = rngN6DieArr(dies: Int(dies)) generated = rngN6DieArr(dies: Int(dies))
displayDies = generated displayDies = generated
@@ -90,12 +100,12 @@ func describeResult(inp: [Int], combineMode: String) -> String {
var result = "" var result = ""
var symbol = "" var symbol = ""
if inp.isEmpty { if inp.isEmpty {
return "0" return ""
} else { } else {
if combineMode == "plus" { if combineMode == "plus" {
symbol = " + " symbol = "+"
} else if combineMode == "multiply" { } else if combineMode == "multiply" {
symbol = " x " symbol = "x"
} }
for i in 0...len { for i in 0...len {
if i == len { if i == len {
@@ -104,7 +114,6 @@ func describeResult(inp: [Int], combineMode: String) -> String {
result += String(inp[i]) + symbol result += String(inp[i]) + symbol
} }
} }
result += " = " + String(arrCombine(arr: inp, combineMode: combineMode))
return result return result
} }
} }

View File

@@ -5,36 +5,17 @@
// Created by Nihaal on 09/10/2024. // Created by Nihaal on 09/10/2024.
// //
//fix date created
//TODO: readd ios 16 support
//TODO: try to use inject
import SwiftUI import SwiftUI
struct NumberView: View { struct NumberView: View {
@State var low: String = "1" @State var low: String = "1"
@State var high: String = "10" @State var high: String = "10"
@State var exclude: String = "none" @State var exclude: String = "none"
@State var generated = "5" @State var generated = ""
@State var prevGen = "" @State var prevGen = ""
@State var generatedInt: Int = 0 @State var generatedInt: Int = 0
@State var prevGenInt: Int = 0 @State var prevGenInt: Int = 0
@State var dispResult = 5 @State var dispResult = 0
var body: some View { var body: some View {
List { List {
HStack { HStack {
@@ -65,30 +46,18 @@ struct NumberView: View {
} }
Section("Description") { Section("Description") {
Text(describeInputs(min: low, max: high, exclude: exclude)) Text(describeInputs(min: low, max: high, exclude: exclude))
.font(.headline)
.fontWeight(.heavy)
.frame(alignment: .center)
} }
} }
Text(String(dispResult)) Text(generated)
.font(.system(size: 75, weight: .bold)) .font(.system(size: 50, weight: .bold))
.foregroundColor(.gray) .foregroundColor(.gray)
.frame(height: 50) .frame(height: 40)
Button { Button {
prevGen = generated prevGen = generated
generated = rng2(min: low, max: high, exclude: exclude) generated = rng3(min: low, max: high, exclude: exclude)
prevGenInt = Int(prevGen)!
generatedInt = Int(generated)!
if prevGenInt < generatedInt {
for _ in prevGenInt...generatedInt {
dispResult += 1
//sleep( UInt32( 1 / (generatedInt - prevGenInt) ) )
}
} else if prevGenInt > generatedInt {
for _ in generatedInt...prevGenInt {
dispResult -= 1
//sleep( UInt32( 1 / (prevGenInt - generatedInt) ) )
}
} else if prevGenInt == generatedInt {
dispResult = generatedInt
}
} label: { } label: {
Text("Generate") Text("Generate")
.padding(.horizontal) .padding(.horizontal)
@@ -96,26 +65,26 @@ struct NumberView: View {
} }
.buttonStyle(BorderedProminentButtonStyle()) .buttonStyle(BorderedProminentButtonStyle())
.cornerRadius(15) .cornerRadius(15)
.padding(.bottom) .padding(.vertical)
} }
} }
func describeInputs(min: String, max: String, exclude: String) -> String { func describeInputs(min: String, max: String, exclude: String) -> String {
let validExcludes = ["start", "end", "both", "none"] let validExcludes = ["start", "none", "end", "both"]
guard validExcludes.contains(exclude) else { guard validExcludes.contains(exclude) else {
print("invalid exclude: " + exclude) print("invalid exclude: " + exclude)
return "invalid exclude: " + exclude return "invalid exclude: " + exclude
} }
var result = "Any number from " var result = "Any number from "
result += min != "" ? min : "_" result += min
result += " to " result += " to "
result += max != "" ? max : "_" result += max
if min == "" || max == "" { if min == "" || max == "" {
return "Enter values above." return "Enter values above."
} else { }
switch exclude { switch exclude {
case "start": case "start":
result += ", excluding \(min)" result += ", excluding \(min)"
case "end": case "end":
@@ -126,48 +95,31 @@ func describeInputs(min: String, max: String, exclude: String) -> String {
break break
default: default:
result = "invalid 'exclude'" result = "invalid 'exclude'"
}
} }
result += "." return result + "."
return result
} }
enum rng2Errors: Error { func rng3(min: String, max: String, exclude: String) -> String {
case h var newMin = Int(min)!
} var newMax = Int(max)!
guard newMin <= newMax else {
func rng2(min: String, max: String, exclude: String) -> String { return "Invalid inputs"
guard min != "" || max != "" else {
return "Enter values above."
} }
guard var minInt = Int(min) else { guard newMin != newMax else {
print("invalid min") return "No numbers"
return "-1"
} }
guard var maxInt = Int(max), maxInt >= minInt else { guard newMin + 1 != newMax else {
print("invalid max or is less than min") return "No numbers"
return "-1"
} }
guard exclude != "both", min != max else {
print("excluding both but min == max")
return "No possible numbers, both bounds equal."
}
let minIntPlus1 = minInt + 1
guard exclude != "both", minIntPlus1 != maxInt else {
print("excluding both but min +1 == max")
return "No possible numbers."
}
var generated = 0
if exclude == "both" { if exclude == "both" {
maxInt -= 1 newMax -= 1
minInt += 1 newMin += 1
} else if exclude == "start" { } else if exclude == "start" {
minInt += 1 newMin += 1
} else if exclude == "end" { } else if exclude == "end" {
maxInt -= 1 newMax -= 1
} }
generated = rng(min: minInt, max: maxInt) return String(rng(min: newMin, max: newMax))
return String(generated)
} }
#Preview { #Preview {

View File

@@ -10,12 +10,12 @@ import SwiftUI
struct PasswordView: View { struct PasswordView: View {
@State var selectedOptions: [PassOption] = [] @State var selectedOptions: [PassOption] = []
let options: [PassOption] = [ let options: [PassOption] = [
PassOption(name: "0-9", chars: Array("0123456789")), PassOption(name: "Digits", chars: Array("0123456789")),
PassOption(name: "a-z", chars: Array("abcdefghijklmnopqrstuvwxyz")), PassOption(name: "Lowercase characters", chars: Array("abcdefghijklmnopqrstuvwxyz")),
PassOption(name: "A-Z", chars: Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) PassOption(name: "Uppercase characters", chars: Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
] ]
@State var presetLen = 4 @State var presetLen = 4
@State var customLen = 8 @State var customLen: Int = 0
@State var generated = "" @State var generated = ""
@State var result = "" @State var result = ""
@State var history: [Int] = [] @State var history: [Int] = []
@@ -23,16 +23,6 @@ struct PasswordView: View {
var body: some View { var body: some View {
VStack { VStack {
List { List {
Text("Hello, world!")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.background(Color.blue)
.padding()
.background(Color.mint)
.padding()
.background(Color.green)
Section("Include") { Section("Include") {
ForEach(options) { option in ForEach(options) { option in
Toggle(isOn: Binding( Toggle(isOn: Binding(
@@ -59,35 +49,30 @@ struct PasswordView: View {
Text("Custom: \(customLen)").tag(-1) Text("Custom: \(customLen)").tag(-1)
} }
.pickerStyle(SegmentedPickerStyle()) .pickerStyle(SegmentedPickerStyle())
if presetLen == -1 {
TextField("Custom", value: $customLen, formatter: NumberFormatter())
.keyboardType(.numberPad)
.frame(width: 50)
}
} }
if presetLen == -1 {
TextField("Custom", value: $customLen, formatter: NumberFormatter())
.keyboardType(.numberPad)
.frame(width: 50)
}
Text(String(result))
.font(.system(size: 75, weight: .bold))
.foregroundColor(.gray)
.frame(height: 50)
Text(String(generated))
.font(.system(size: 75, weight: .bold))
.foregroundColor(.gray)
.frame(height: 50)
Button {
generated = genPass(selectdOpts: selectedOptions, len: (presetLen == -1 ? customLen : presetLen))
} label: {
Text("Generate")
.padding(.horizontal)
.font(.system(size: 25, weight: .bold))
}
.buttonStyle(BorderedProminentButtonStyle())
.cornerRadius(15)
.padding(.vertical)
} }
Text(String(generated))
.font(.system(size: 50, weight: .bold))
.foregroundColor(.gray)
.frame(height: 40)
Button {
generated = genPass(selectdOpts: selectedOptions, len: (presetLen == -1 ? customLen : presetLen))
} label: {
Text("Generate")
.padding(.horizontal)
.font(.system(size: 25, weight: .bold))
}
.buttonStyle(BorderedProminentButtonStyle())
.cornerRadius(15)
.padding(.vertical)
} }
} }
} }
@@ -103,7 +88,7 @@ struct PassOption: Identifiable {
} }
func genPass(selectdOpts: [PassOption], len: Int) -> String { func genPass(selectdOpts: [PassOption], len: Int) -> String {
let characters = selectdOpts.flatMap { $0.chars } let characters = selectdOpts.flatMap { $0.chars }
guard !(characters.isEmpty) else { guard !(characters.isEmpty) else {
print("characters empty") print("characters empty")

View File

@@ -35,15 +35,22 @@ func totalDoubleArr(arr: [Double]) -> Double {
} }
// array combiner, adds or multiplies all ints in an array // array combiner, adds or multiplies all ints in an array
func arrCombine(arr: [Int], combineMode: String) -> Int { func arrCombine(arr: [Int], combineMode: String) -> UInt {
var output = 0 var newArr: [UInt] = []
for i in 0...(arr.count-1) {
newArr.append(UInt(arr[i]))
}
var output: UInt = 0
if combineMode == "plus" { if combineMode == "plus" {
for num in arr { for num in newArr {
output += num output += num
} }
} else if combineMode == "multiply" { } else if combineMode == "multiply" {
output = 1 output = 1
for num in arr { for num in newArr {
guard output <= UInt.max / num else {
return UInt.max //cap at uint.max 2^64
}
output *= num output *= num
} }
} else { } else {
@@ -53,11 +60,8 @@ func arrCombine(arr: [Int], combineMode: String) -> Int {
return output return output
} }
func rng(min: Int, max: Int) -> Int { func rng(min: Int, max: Int) -> Int {
var rng = 0 return Int.random(in: min...max)
rng = Int.random(in: min...max)
return rng
} }
func rng6Die() -> Int { func rng6Die() -> Int {
@@ -69,6 +73,7 @@ func rngCDie(min: Int, max: Int) -> Int {
} }
func rngN6DieArr(dies: Int) -> [Int] { func rngN6DieArr(dies: Int) -> [Int] {
guard dies > 0 else { return [] }
var output: [Int] = [] var output: [Int] = []
for _ in 1...dies { for _ in 1...dies {
output.append(rng6Die()) output.append(rng6Die())