Right now, I have my code in a way that makes the cards with the same images are matched. I want to change my code so that 2 specific cards are match - ex: Image("Beagle") and Image("BeagleInformation").
For my code, I have the content view file and 2 other files - FinalMemoryGame and MemoryGame. I think that my problem mainly focuses on the MemoryGame file. I have included all 3 below
CONTENTVIEW
import SwiftUI
struct ContentView: View {
@State private var progress = 0.0
@State private var timeRemaining = 30
@State private var fadeInOut = false
let timer = Timer.publish(every: 1, on: .main, in: .com
mon).autoconnect()
@ObservedObject var viewModel: FinalMemoryGame
var body: some View {
ScrollView {
HStack{Text("Final Game").font(.system(size:36,design: .rounded))
.foregroundColor(.purple)
.bold()
Image(systemName: "arrow.clockwise").onTapGesture{
viewModel.reset()
progress = Double(viewModel.matches)
timeRemaining = 30
}
}
LazyVGrid(columns:
[GridItem(
.adaptive(minimum: 100),
spacing:10)]){
ForEach(viewModel.cards) { card in DogCard(
card: card).aspectRatio(1, contentMode: .fit).frame(width:100, height: 125).onTapGesture{
viewModel.choose(card)
progress = Double(viewModel.matches)
}
}
}
}.onReceive(timer) { time in
if timeRemaining > 0 {
timeRemaining -= 1
}
if timeRemaining == 0 && progress < 6{
viewModel.lose()
}
}
if timeRemaining == 0 && progress < 6{
VStack {
ProgressView("You lose!", value: progress, total:6)
.foregroundColor(.red)
.font(.system(size: 20, weight: .bold))
.onAppear () {
withAnimation(Animation.easeInOut(duration:0.6)
.repeatForever(autoreverses:true)){
fadeInOut.toggle()
}
}.opacity(fadeInOut ? 0 : 1)
}
}
else if progress == 6{
VStack{
ProgressView("You win!", value: progress, total:6)
.foregroundColor(.green)
.font(.system(size: 20, weight: .bold))
.onAppear () {
withAnimation(Animation.easeInOut(duration:0.6)
.repeatForever(autoreverses:true)){
fadeInOut.toggle()
}
}.opacity(fadeInOut ? 0 : 1)
}
}
else{
ProgressView("Time Remaining: \(timeRemaining)", value: progress, total:6)
}
}
}
struct DogCard: View {
var card: MemoryGame.Card
var body: some View {
ZStack{
let shape = RoundedRectangle(cornerRadius: 10)
if card.isFaceUp {
shape.stroke(lineWidth: 3).foregroundColor(.blue)
Image(card.content).resizable().scaledToFit().padding()
}
else {
shape.fill().foregroundColor(.pink)
}
}.animation(.easeInOut(duration: 0.5), value: card.animationAmount)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(viewModel: FinalMemoryGame())
}
}
FINALMEMORYGAME
import SwiftUI
class FinalMemoryGame: ObservableObject {
@Published private var model: MemoryGame = CreateMemoryGame()
static func CreateMemoryGame() -> MemoryGame {
return MemoryGame(numberOfPairsOfCards: 6, contentFactory: makeContent)
}
static func makeContent(index: Int)->String {
let images = ["Beagle", "BeagleInformation", "Dutch Shepherd", "Havanese", "Leonberger", "Pharaoh Hound"]
return images[index]
}
var cards: Array<MemoryGame.Card> {
model.cards
}
var pairs: Int{
model.numberOfPairs
}
func choose(_ card: MemoryGame.Card){
model.chooseCard(card)
}
func reset()
{
model.reset()
}
func lose(){
model.lose()
}
var matches: Int{
model.matches
}
}
MEMORYGAME
import Foundation
import SwiftUI
struct MemoryGame {
private(set) var cards: Array<Card>
private(set) var numberOfPairs: Int
private(set) var upCards: Array<Int>
private(set) var matches: Int
private(set) var lost: Bool
struct Card: Identifiable {
var content: String
var isFaceUp: Bool = false
var isMatched: Bool = false
var id: Int
var animationAmount = 0.0
}
mutating func chooseCard(_ card: Card) {
if lost{
return
}
for index in cards.indices {
if cards[index].id == card.id{
cards[index].animationAmount = 180
if cards[index].isFaceUp{
return
}
//If 2 cards are picked, they get faced down
else if upCards[0] != -1 && upCards[1] != -1{
cards[upCards[0]].isFaceUp.toggle()
cards[upCards[1]].isFaceUp.toggle()
cards[index].isFaceUp.toggle()
upCards[0] = index
upCards[1] = -1
}
else if upCards[0] != -1{
cards[index].isFaceUp.toggle()
if cards[index].content == cards[upCards[0]].content{
cards[index].isMatched.toggle()
cards[upCards[0]].isMatched.toggle()
matches = 1
upCards[0] = -1
}
else{
upCards[1] = index
}
}
else{
cards[index].isFaceUp.toggle()
upCards[0] = index
}
}
}
}
mutating func reset() {
lost = false
cards.shuffle()
matches = 0
upCards = [-1,-1]
for index in cards.indices{
chooseCard(cards[index])
upCards = [-1,-1]
cards[index].isFaceUp = false
cards[index].isMatched = false
}
}
mutating func lose(){
lost = true
}
init(numberOfPairsOfCards: Int, contentFactory: (Int)->String){
cards = []
numberOfPairs = numberOfPairsOfCards
upCards = [-1,-1]
matches = 0
lost = false
for index in 0..<numberOfPairsOfCards {
let content = contentFactory(index)
cards.append(Card(content: content, id: index * 2))
cards.append(Card(content: content, id: index * 2 1))
}
cards.shuffle()
}
//THIS IS WHERE I PUT IT
func cardsMatch(card1: Card, card2: Card) -> Bool {
switch (card1.content, card2.content) {
case ("Beagle", "BeagleInformation"), ("BeagleInformation", "Beagle"):
return true
case let (a, b):
return a == b
}
}
else if upCards[0] != -1 { //ERROR
cards[index].isFaceUp.toggle()
if cardsMatch(card1: cards[index], card2: cards[upCards[0]]) {
cards[index].isMatched.toggle()
cards[upCards[0]].isMatched.toggle()
matches = 1
upCards[0] = -1
}
else{
upCards[1] = index
}
}
As you can see, I tried adding
if Image("Beagle") == Image("BeagleInformation") {
cards[index].isMatched.toggle()
cards[upCards[0]].isMatched.toggle()
matches = 1
upCards[0] = -1
at the end of my MemoryGame file but I do not think that that's correct
CodePudding user response:
This code:
if Image("Beagle") == Image("BeagleInformation") {
cards[index].isMatched.toggle()
cards[upCards[0]].isMatched.toggle()
matches = 1
upCards[0] = -1
is incorrect because:
- It contains SwiftUI image code which does not belong in your memory game model code.
- It isn't looking at the current 2 up cards to decide if they match.
Your current algorithm for deciding if 2 cards match is to compare their .content
values which are String
s. I would suggest making a separate cardsMatch()
function that takes 2 Card
s and returns a Bool
:
func cardsMatch(card1: Card, card2: Card) -> Bool {
switch (card1.content, card2.content) {
case ("Beagle", "BeagleInformation"), ("BeagleInformation", "Beagle"):
return true
case let (a, b):
return a == b
}
}
This function declares a match if the two cards are "Beagle"
and "BeagleInformation"
in either order, or if the two cards are the same. You can customize this function to whatever you want a match to be.
Call this function when you have 2 up cards to compare:
Replace:
if cards[index].content == cards[upCards[0]].content{
with:
if cardsMatch(card1: cards[index], card2: cards[upCards[0]]) {
Changes for Swift Playgrounds on iPad
I ran this on Swift Playgrounds on iPad with these mods:
I use these image names:
let images = ["clock", "car", "house", "tree", "airplane", "globe"]
And modified DogCard
to use an Image(systemName:)
in an overlay
.
struct DogCard: View {
var card: MemoryGame.Card
var body: some View {
ZStack{
let shape = RoundedRectangle(cornerRadius: 10)
if card.isFaceUp {
shape.stroke(lineWidth: 3).foregroundColor(.blue).overlay {
Image(systemName: card.content).resizable().scaledToFit().padding()
.foregroundColor(.red)
}
}
else {
shape.fill().foregroundColor(.pink)
}
}.animation(.easeInOut(duration: 0.5), value: card.animationAmount)
}
}
Here it is running on the iPad:
Here is my MemoryGame:
struct MemoryGame {
private(set) var cards: [Card]
private(set) var numberOfPairs: Int
private(set) var upCards: [Int]
private(set) var matches: Int
private(set) var lost: Bool
struct Card: Identifiable {
var content: String
var isFaceUp: Bool = false
var isMatched: Bool = false
var id: Int
var animationAmount = 0.0
}
func cardsMatch(card1: Card, card2: Card) -> Bool {
switch (card1.content, card2.content) {
case ("globe", "airplane"), ("airplane", "globe"):
return true
case let (a, b):
return a == b
}
}
mutating func chooseCard(_ card: Card) {
if lost {
return
}
for index in cards.indices {
if cards[index].id == card.id {
cards[index].animationAmount = 180
if cards[index].isFaceUp {
return
}
//If 2 cards are picked, they get faced down
else if upCards[0] != -1 && upCards[1] != -1 {
cards[upCards[0]].isFaceUp.toggle()
cards[upCards[1]].isFaceUp.toggle()
cards[index].isFaceUp.toggle()
upCards[0] = index
upCards[1] = -1
}
else if upCards[0] != -1 {
cards[index].isFaceUp.toggle()
if cardsMatch(card1: cards[index], card2: cards[upCards[0]]) {
cards[index].isMatched.toggle()
cards[upCards[0]].isMatched.toggle()
matches = 1
upCards[0] = -1
}
else{
upCards[1] = index
}
}
else{
cards[index].isFaceUp.toggle()
upCards[0] = index
}
}
}
}
mutating func reset() {
lost = false
cards.shuffle()
matches = 0
upCards = [-1, -1]
for index in cards.indices {
chooseCard(cards[index])
upCards = [-1, -1]
cards[index].isFaceUp = false
cards[index].isMatched = false
}
}
mutating func lose() {
lost = true
}
init(numberOfPairsOfCards: Int, contentFactory: (Int) -> String){
cards = []
numberOfPairs = numberOfPairsOfCards
upCards = [-1, -1]
matches = 0
lost = false
for index in 0..<numberOfPairsOfCards {
let content = contentFactory(index)
cards.append(Card(content: content, id: index * 2))
cards.append(Card(content: content, id: index * 2 1))
}
cards.shuffle()
}
}