Ok, so we made this View. Let's refactor it.
//
// SoftCountsView.swift
// BlackJackPractice
//
// Created by Victor Quach on 3/22/23.
//
import SwiftUI
struct SoftCountsView: View {
@State private var playerCard1: Int
@State private var playerCard2: Int
@State private var dealerCard: Int
@State private var feedbackMessage: String
init() {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
}
var body: some View {
VStack(spacing: 16) {
Text(feedbackMessage)
HStack {
Text("Dealer: ")
CardView(card: dealerCard)
}
HStack {
Text("You: ")
CardView(card: playerCard1)
CardView(card: playerCard2)
}
VStack(spacing: 16) {
HStack{
ActionButton(title: "Stand", backgroundColor: Color.blue, action: {
let (action, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard);
feedbackMessage = action == Utility.Action.stand ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
ActionButton(title: "Hit", backgroundColor: Color.blue, action: {
let (action, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard);
feedbackMessage = action == Utility.Action.hit ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
}
HStack(spacing: 16) {
ActionButton(title: "Split", backgroundColor: Color.blue, action: {
let (action, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard);
feedbackMessage = action == Utility.Action.split ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
ActionButton(title: "Double", backgroundColor: Color.blue, action: {
let (action, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard);
feedbackMessage = action == Utility.Action.double ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
}
}
ActionButton(title: "Deal", backgroundColor: Color.green, action: {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
})
}
.padding()
}
}
struct SoftCountsView_Previews: PreviewProvider {
static var previews: some View {
SoftCountsView().preferredColorScheme(.dark)
}
}
To refactor the SoftCountsView
, you can consider the following steps:
Extract the buttons into a separate view to make the code more modular and easier to read.
Use a ForEach
loop for creating the action buttons to avoid repetition.
Create a function to handle the button actions and update the feedbackMessage
.
Here's the refactored code:
import SwiftUI
struct SoftCountsView: View {
@State private var playerCard1: Int
@State private var playerCard2: Int
@State private var dealerCard: Int
@State private var feedbackMessage: String
init() {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
}
var body: some View {
VStack(spacing: 16) {
Text(feedbackMessage)
HStack {
Text("Dealer: ")
CardView(card: dealerCard)
}
HStack {
Text("You: ")
CardView(card: playerCard1)
CardView(card: playerCard2)
}
ActionButtonsView(playerCard1: $playerCard1, playerCard2: $playerCard2, dealerCard: $dealerCard, feedbackMessage: $feedbackMessage)
ActionButton(title: "Deal", backgroundColor: Color.green, action: {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
})
}
.padding()
}
}
struct ActionButtonsView: View {
@Binding var playerCard1: Int
@Binding var playerCard2: Int
@Binding var dealerCard: Int
@Binding var feedbackMessage: String
let actions: [Utility.Action] = [.stand, .hit, .split, .double]
var body: some View {
VStack(spacing: 16) {
ForEach(0..<actions.count / 2) { index in
HStack {
ForEach(0..<2) { subIndex in
let action = actions[index * 2 + subIndex]
ActionButton(title: action.rawValue.capitalized, backgroundColor: Color.blue, action: {
handleAction(action: action)
})
}
}
}
}
}
private func handleAction(action: Utility.Action) {
let (correctAction, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
feedbackMessage = action == correctAction ? "Correct! \(reason)" : "Incorrect. \(reason)"
}
}
struct SoftCountsView_Previews: PreviewProvider {
static var previews: some View {
SoftCountsView().preferredColorScheme(.dark)
}
}
This refactoring makes the code more maintainable and easier to understand.
User: Non-constant range: argument must be an integer literal
To resolve the "Non-constant range: argument must be an integer literal" issue, you can use the Array
initializer with a range instead of using ForEach
directly with a range. Here's the updated ActionButtonsView
:
struct ActionButtonsView: View {
@Binding var playerCard1: Int
@Binding var playerCard2: Int
@Binding var dealerCard: Int
@Binding var feedbackMessage: String
let actions: [Utility.Action] = [.stand, .hit, .split, .double]
var body: some View {
VStack(spacing: 16) {
ForEach(Array(0..<(actions.count / 2)), id: \.self) { index in
HStack {
ForEach(Array(0..<2), id: \.self) { subIndex in
let action = actions[index * 2 + subIndex]
ActionButton(title: action.rawValue.capitalized, backgroundColor: Color.blue, action: {
handleAction(action: action)
})
}
}
}
}
}
private func handleAction(action: Utility.Action) {
let (correctAction, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
feedbackMessage = action == correctAction ? "Correct! \(reason)" : "Incorrect. \(reason)"
}
}
By using Array(0..<(actions.count / 2))
and Array(0..<2)
, you create arrays with the desired ranges, which can be used in the ForEach
loops without causing the "Non-constant range: argument must be an integer literal" error.
User: Make the background red when the player makes a mistake
To make the background red when the player makes a mistake, you can add a Color
property to the SoftCountsView
and update it based on the feedbackMessage
. Here's the updated code:
import SwiftUI
struct SoftCountsView: View {
@State private var playerCard1: Int
@State private var playerCard2: Int
@State private var dealerCard: Int
@State private var feedbackMessage: String
@State private var backgroundColor: Color
init() {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
backgroundColor = .clear
}
var body: some View {
VStack(spacing: 16) {
Text(feedbackMessage)
HStack {
Text("Dealer: ")
CardView(card: dealerCard)
}
HStack {
Text("You: ")
CardView(card: playerCard1)
CardView(card: playerCard2)
}
ActionButtonsView(playerCard1: $playerCard1, playerCard2: $playerCard2, dealerCard: $dealerCard, feedbackMessage: $feedbackMessage, backgroundColor: $backgroundColor)
ActionButton(title: "Deal", backgroundColor: Color.green, action: {
playerCard1 = 1
playerCard2 = Utility.randomCard()
dealerCard = Utility.randomCard()
feedbackMessage = ""
backgroundColor = .clear
})
}
.padding()
.background(backgroundColor)
}
}
struct ActionButtonsView: View {
@Binding var playerCard1: Int
@Binding var playerCard2: Int
@Binding var dealerCard: Int
@Binding var feedbackMessage: String
@Binding var backgroundColor: Color
let actions: [Utility.Action] = [.stand, .hit, .split, .double]
var body: some View {
VStack(spacing: 16) {
ForEach(Array(0..<(actions.count / 2)), id: \.self) { index in
HStack {
ForEach(Array(0..<2), id: \.self) { subIndex in
let action = actions[index * 2 + subIndex]
ActionButton(title: action.rawValue.capitalized, backgroundColor: Color.blue, action: {
handleAction(action: action)
})
}
}
}
}
}
private func handleAction(action: Utility.Action) {
let (correctAction, reason) = Utility.getActionOnSoftCount(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
if action == correctAction {
feedbackMessage = "Correct! \(reason)"
backgroundColor = .clear
} else {
feedbackMessage = "Incorrect. \(reason)"
backgroundColor = .red
}
}
}
struct SoftCountsView_Previews: PreviewProvider {
static var previews: some View {
SoftCountsView().preferredColorScheme(.dark)
}
}
Now, when the player makes a mistake, the background will turn red. It will reset to its original color when the "Deal" button is pressed.
Great, now this sister view needs to be modified accordingly.
//
// SplitsView.swift
// BlackJackPractice
//
// Created by Victor Quach on 3/22/23.
//
import SwiftUI
struct SplitsView: View {
@State private var playerCard1: Int
@State private var playerCard2: Int
@State private var dealerCard: Int
@State private var feedbackMessage: String
init() {
// Generate new random cards for the player and dealer
let card = Utility.randomCard()
playerCard1 = card
playerCard2 = card
dealerCard = Utility.randomCard()
// Clear the feedback message
feedbackMessage = ""
}
var body: some View {
VStack(spacing: 16) {
Text(feedbackMessage)
HStack {
Text("Dealer: ")
CardView(card: dealerCard)
}
HStack {
Text("You: ")
CardView(card: playerCard1)
CardView(card: playerCard2)
}
HStack {
ActionButton(title: "Split", backgroundColor: Color.blue, action: {
let (shouldSplit, reason) = Utility.shouldSplitCards(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
feedbackMessage = shouldSplit ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
ActionButton(title: "Don't Split", backgroundColor: Color.blue, action: {
let (shouldSplit, reason) = Utility.shouldSplitCards(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
feedbackMessage = !shouldSplit ? "Correct! \(reason)" : "Incorrect. \(reason)"
})
}
ActionButton(title: "Deal", backgroundColor: Color.green, action: {
playerCard1 = Utility.randomCard()
playerCard2 = playerCard1
dealerCard = Utility.randomCard()
feedbackMessage = ""
})
}
.padding()
}
}
struct SplitsView_Previews: PreviewProvider {
static var previews: some View {
SplitsView().preferredColorScheme(.dark)
}
}
To modify the SplitsView
accordingly, you can follow the same approach as in the SoftCountsView
refactoring. Extract the buttons into a separate view, use a ForEach
loop for creating the action buttons, and update the background color based on the player's mistake. Here's the updated code:
import SwiftUI
struct SplitsView: View {
@State private var playerCard1: Int
@State private var playerCard2: Int
@State private var dealerCard: Int
@State private var feedbackMessage: String
@State private var backgroundColor: Color
init() {
let card = Utility.randomCard()
playerCard1 = card
playerCard2 = card
dealerCard = Utility.randomCard()
feedbackMessage = ""
backgroundColor = .clear
}
var body: some View {
VStack(spacing: 16) {
Text(feedbackMessage)
HStack {
Text("Dealer: ")
CardView(card: dealerCard)
}
HStack {
Text("You: ")
CardView(card: playerCard1)
CardView(card: playerCard2)
}
SplitButtonsView(playerCard1: $playerCard1, playerCard2: $playerCard2, dealerCard: $dealerCard, feedbackMessage: $feedbackMessage, backgroundColor: $backgroundColor)
ActionButton(title: "Deal", backgroundColor: Color.green, action: {
let newCard = Utility.randomCard()
playerCard1 = newCard
playerCard2 = newCard
dealerCard = Utility.randomCard()
feedbackMessage = ""
backgroundColor = .clear
})
}
.padding()
.background(backgroundColor)
}
}
struct SplitButtonsView: View {
@Binding var playerCard1: Int
@Binding var playerCard2: Int
@Binding var dealerCard: Int
@Binding var feedbackMessage: String
@Binding var backgroundColor: Color
let actions: [String] = ["Split", "Don't Split"]
var body: some View {
HStack {
ForEach(actions, id: \.self) { action in
ActionButton(title: action, backgroundColor: Color.blue, action: {
handleAction(action: action)
})
}
}
}
private func handleAction(action: String) {
let (shouldSplit, reason) = Utility.shouldSplitCards(playerCard1: playerCard1, playerCard2: playerCard2, dealerCard: dealerCard)
if (action == "Split" && shouldSplit) || (action == "Don't Split" && !shouldSplit) {
feedbackMessage = "Correct! \(reason)"
backgroundColor = .clear
} else {
feedbackMessage = "Incorrect. \(reason)"
backgroundColor = .red
}
}
}
struct SplitsView_Previews: PreviewProvider {
static var previews: some View {
SplitsView().preferredColorScheme(.dark)
}
}
Now, the SplitsView
has been refactored to include the same improvements as the SoftCountsView
, making the code more maintainable and easier to understand. The background color will also change to red when the player makes a mistake and reset to its original color when the "Deal" button is pressed.