// // ContentView.swift // CowsAndBulls // // Created by pat on 11/20/22. // import SwiftUI struct ContentView: View { var body: some View { VStack(spacing: 0) { HStack { TextField("Enter a guess…", text: $guess) .onSubmit(submitGuess) Button("Go", action: submitGuess) } .padding() List(0..<guesses.count, id: \.self) { index in let guess = guesses[index] let shouldShowResult = (enableHardMode == false) || (enableHardMode && index == 0) HStack { Text(guess) Spacer() if shouldShowResult { let res = result(for: guess) if res.first == "0" { Text(result(for: guess)) .foregroundColor(.red) } else if res.first == "4" { Text(result(for: guess)) .foregroundColor(.green) } else { Text(result(for: guess)) .foregroundColor(.yellow) } } } } .listStyle(.sidebar) if showGuessCount { Text("Guesses: \(guesses.count)/\(maximumGuesses)") .padding() } } .frame(width: 250) .frame(minHeight: 300) .navigationTitle("Cows and Bulls") .onAppear(perform: startNewGame) .onChange(of: answerLength) { _ in startNewGame() } .alert("You win!", isPresented: $isGameOver) { Button("Play Again", action: startNewGame) } message: { if guesses.count < 10 { Text("Congratulations!\n Score: \(guesses.count)") } else if guesses.count < 20 { Text("Almost sweaty.\n Score: \(guesses.count)") } else { Text("Get güd nüb!\n Score: \(guesses.count)") } } .alert("You lose!", isPresented: $isGameLost) { Button ("Play Again", action: startNewGame) } message: { Text("You are a big chungus. Have you tried sucking less?\n\nThe correct answer is \(answer).") } // Removed .touchBar modifier. } @State private var guesses: [String] = [] @State private var guess = "" @State private var answer = "" @State private var isGameOver = false @State private var isGameLost = false @AppStorage("maximumGuesses") var maximumGuesses = 100 @AppStorage("answerLength") private var answerLength = 4 @AppStorage("enableHardMode") var enableHardMode = false @AppStorage("showGuessCount") var showGuessCount = false func submitGuess() { // 1. Validate current guess. // 1.1 all chars are unique guard !Set(guesses).contains(guess) else { return } guard Set(guess).count == answerLength else { return } // 1.2 length is 4 guard guess.count == answerLength else { return } // 1.3 all chars are in 0...9 let badCharacters = CharacterSet(charactersIn: "0123456789").inverted guard guess.rangeOfCharacter(from: badCharacters) == nil else { return } // 2. Add current guess to guesses array guesses.insert(guess, at: 0) // 3. If guess is answer, show "You win!" dialogue if result(for: guess).contains("\(answerLength)b") { isGameOver = true } if guess == answer { // Present a dialogue, popup, or some message. } else if guesses.count == maximumGuesses { isGameLost = true } // 4. Reset value in guess guess = "" } func result(for guess: String) -> String { // 1. Initialize bulls and cows to 0 var bulls: Int = 0 var cows: Int = 0 // 2. Convert guess and answer to arrays let guessLetters = Array(guess) let answerLetters = Array(answer) // 3. Iterate across the array(guess) // 3.1) if guess item is answer item: bulls++ // 3.2) else if guess item is in answer: cows++ for (index, letter) in guessLetters.enumerated() { if letter == answerLetters[index] { bulls += 1 } else if answerLetters.contains(letter) { cows += 1 } } // 4. Return bulls and cows as "part of a string"(?). return "\(bulls)b \(cows)c" } func startNewGame() { guard answerLength >= 3 && answerLength <= 8 else { return } guess = "" guesses.removeAll() // `keepingCapacity: Bool` parameter maintains array capacity even while emptying it. answer = "" let shuffledNumbers: [Int] = (0...9).shuffled() for i in 0..<answerLength { answer.append(String(shuffledNumbers[i])) // Strings are mutable in Swift?? } } } struct SettingsView: View { var body: some View { TabView { Form { TextField("Maximum guesses", value: $maximumGuesses, format: .number) .help("The maximum number of answers you can submit. Changing this will immediately restart your game.") TextField ("Answer length", value: $answerLength, format: .number) .help("The length of the number string to guess. Changing this will immediately restart your game.") if answerLength < 3 || answerLength > 8 { Text("Must be between 3 and 8") .foregroundColor(.red) } } .padding() .tabItem { Label("Game", systemImage: "number.circle") } Form { Toggle("Enable hard mode", isOn: $enableHardMode) .help("Hard mode shows only the result for the most recent guess.") Toggle("Show guess count", isOn: $showGuessCount) .help("Adds a footer below your guesses showing how many guessses you have made.") } .padding() .tabItem { Label("Advanced", systemImage: "gearshape.2") } } .frame(width: 400) } @AppStorage("maximumGuesses") var maximumGuesses = 100 @AppStorage("answerLength") private var answerLength = 4 @AppStorage("enableHardMode") var enableHardMode = false @AppStorage("showGuessCount") var showGuessCount = false } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }