cs193p-memorize/Memorize/MemoryGame.swift
Ching 21ed5d55cb feat(view): 增加动画效果
增加发牌动效,增加倒计时动效

Signed-off-by: Ching <loooching@gmail.com>
2023-02-16 23:21:20 +08:00

126 lines
3.4 KiB
Swift

//
// MemoryGame.swift
// Memorize
//
// Created by ching on 2023/2/12.
//
import Foundation
// model
struct MemoryGame<CardContent> where CardContent: Equatable {
private(set) var cards: [Card]
private var indexOfTheOneAndOnlyFaceUpCard: Int? {
get { cards.indices.filter { cards[$0].isFaceUp }.oneAndOnly }
set { cards.indices.forEach { cards[$0].isFaceUp = ($0 == newValue) } }
}
mutating func choose(_ card: Card) {
if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched {
if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
if cards[chosenIndex].content == cards[potentialMatchIndex].content {
cards[chosenIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
}
cards[chosenIndex].isFaceUp.toggle()
} else {
indexOfTheOneAndOnlyFaceUpCard = chosenIndex
}
}
}
mutating func shuffle() {
cards.shuffle()
}
func index(of card: Card) -> Int? {
for index in 0 ..< cards.count {
if cards[index].id == card.id {
return index
}
}
return nil
}
init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
cards = []
for pairIndex in 0 ..< numberOfPairsOfCards {
let content: CardContent = createCardContent(pairIndex)
cards.append(Card(content: content, id: pairIndex * 2))
cards.append(Card(content: content, id: pairIndex * 2 + 1))
}
cards.shuffle()
}
struct Card: Identifiable {
var isFaceUp = false {
didSet {
if isFaceUp {
startUsingBonusTime()
} else {
stopUsingBonusTime()
}
}
}
var isMatched = false {
didSet {
stopUsingBonusTime()
}
}
let content: CardContent
let id: Int
var bonusTimeLimit = const.DrawingConstants.bonusTimeLimit
private var faceUpTime: TimeInterval {
if let lastFaceUpDate = lastFaceUpDate {
return pastFaceUpTime + Date().timeIntervalSince(lastFaceUpDate)
} else {
return pastFaceUpTime
}
}
var lastFaceUpDate: Date?
var pastFaceUpTime: TimeInterval = 0
var bonusTimeRemaining: TimeInterval {
max(0, bonusTimeLimit - faceUpTime)
}
var bonusRemaining: Double {
(bonusTimeLimit > 0 && bonusTimeRemaining > 0) ? bonusTimeRemaining / bonusTimeLimit : 0
}
var hasEarnedBonus: Bool {
isMatched && bonusTimeRemaining > 0
}
var isConsumingBonusTime: Bool {
isFaceUp && !isMatched && bonusTimeRemaining > 0
}
private mutating func startUsingBonusTime() {
if isConsumingBonusTime, lastFaceUpDate == nil {
lastFaceUpDate = Date()
}
}
private mutating func stopUsingBonusTime() {
pastFaceUpTime = faceUpTime
lastFaceUpDate = nil
}
}
}
extension Array {
var oneAndOnly: Element? {
if count == 1 {
return first
} else {
return nil
}
}
}