Compare commits
10 Commits
5665c75030
...
448a4c9401
| Author | SHA1 | Date | |
|---|---|---|---|
| 448a4c9401 | |||
| b80723c964 | |||
| e9ee10b9c0 | |||
| d3d6b39213 | |||
| 595b82222c | |||
| 3278c18c89 | |||
| 5ad085d415 | |||
| 6d72b59426 | |||
| 8836411c6b | |||
| 85fb6a1bc9 |
@@ -1,3 +1,7 @@
|
||||

|
||||
|
||||
|
||||
|
||||
### Set Game
|
||||
This is an implementation of the Set card game in Swift using SwiftUI. The game is a matching game where players try to find sets of three cards that share the same properties (color, symbol, number, and shading) or differ in all of these properties. The game ends when there are no more sets on the table.
|
||||
## Usage
|
||||
|
||||
BIN
Set/Assets.xcassets/AppIcon.appiconset/100.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/102.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/1024.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/114.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/120.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/128.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/144.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/152.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/16.png
Normal file
|
After Width: | Height: | Size: 820 B |
BIN
Set/Assets.xcassets/AppIcon.appiconset/167.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/172.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/180.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/196.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/20.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/216.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/256.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/29.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/40.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/48.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/50.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/512.png
Normal file
|
After Width: | Height: | Size: 267 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/55.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/57.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/58.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/60.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/64.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/66.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/72.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/76.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/80.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/87.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/88.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
Set/Assets.xcassets/AppIcon.appiconset/92.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -1,8 +1,342 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "57.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "114.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "50.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "72.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "144.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"filename" : "16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "64.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "48.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "24x24",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "55.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "27.5x27.5",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "66.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "33x33",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "88.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "44x44",
|
||||
"subtype" : "40mm"
|
||||
},
|
||||
{
|
||||
"filename" : "92.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "46x46",
|
||||
"subtype" : "41mm"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"filename" : "102.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "51x51",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "54x54",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "172.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "86x86",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "196.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "98x98",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "216.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "108x108",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "117x117",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "129x129",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "watch-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -29,3 +29,19 @@ struct Diamond: Shape {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Diamond_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
GeometryReader {geometry in
|
||||
VStack {
|
||||
Diamond()
|
||||
.size(CGSize(width: geometry.size.width, height: geometry.size.height / 3))
|
||||
Diamond()
|
||||
.size(CGSize(width: geometry.size.width, height: geometry.size.height / 3))
|
||||
Diamond()
|
||||
.size(CGSize(width: geometry.size.width, height: geometry.size.height / 3))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,108 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct SetGame {
|
||||
private var deck: Array<Card>
|
||||
private(set) var cardsOnTable: Array<Card>
|
||||
private(set) var cards: Array<Card>
|
||||
private(set) var score: Int = 0
|
||||
private(set) var colors : [Int : Color] = [:]
|
||||
private var matches: Int = 0
|
||||
|
||||
|
||||
|
||||
mutating func choose(_ card: Card) {
|
||||
if let chosenIndex = cards.firstIndex(where: {card.id == $0.id}),
|
||||
!cards[chosenIndex].isMatched,
|
||||
!cards[chosenIndex].isSelected,
|
||||
cards[chosenIndex].isOnTheTable
|
||||
{
|
||||
cards[chosenIndex].isSelected = true
|
||||
var chosenCards: Array<Card> = Array()
|
||||
for c in cards {
|
||||
if c.isSelected {
|
||||
chosenCards.append(c)
|
||||
}
|
||||
}
|
||||
|
||||
if chosenCards.count > 2 {
|
||||
let chosenCard1 = chosenCards[0]
|
||||
let chosenCard2 = chosenCards[1]
|
||||
let chosenCard3 = chosenCards[2]
|
||||
let chosenCards = [chosenCard1, chosenCard2, chosenCard3]
|
||||
|
||||
let color = all3EqualOrAllDifferent(chosenCard1.color, chosenCard2.color, chosenCard3.color)
|
||||
let shading = all3EqualOrAllDifferent(chosenCard1.shading, chosenCard2.shading, chosenCard3.shading)
|
||||
let symbol = all3EqualOrAllDifferent(chosenCard1.symbol, chosenCard2.symbol, chosenCard3.symbol)
|
||||
let numberOnCard = all3EqualOrAllDifferent(chosenCard1.numberOnCard, chosenCard2.numberOnCard, chosenCard3.numberOnCard)
|
||||
|
||||
|
||||
if color && shading && symbol && numberOnCard {
|
||||
for chosenCard in chosenCards {
|
||||
cards[cards.firstIndex(where: {chosenCard.id == $0.id})!].isMatched = true
|
||||
cards[cards.firstIndex(where: {chosenCard.id == $0.id})!].matchId = matches
|
||||
}
|
||||
colors.updateValue(randomColor(), forKey: self.matches)
|
||||
score += 1
|
||||
matches += 1
|
||||
if (cardsOnTheTable < GameConstants.cardsOnTableInTheBeggining) {
|
||||
addCardsToTable(GameConstants.numberOfCardsToDraw)
|
||||
}
|
||||
} else {
|
||||
score -= 1
|
||||
}
|
||||
for chosenCard in chosenCards {
|
||||
cards[cards.firstIndex(where: {chosenCard.id == $0.id})!].isSelected = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if let chosenIndex = cards.firstIndex(where: {card.id == $0.id}),
|
||||
!cards[chosenIndex].isMatched,
|
||||
cards[chosenIndex].isSelected,
|
||||
cards[chosenIndex].isOnTheTable {
|
||||
cards[chosenIndex].isSelected = false
|
||||
}
|
||||
}
|
||||
|
||||
func all3EqualOrAllDifferent<EquatableObject: Equatable>(_ first: EquatableObject, _ second: EquatableObject, _ third: EquatableObject) -> Bool {
|
||||
(first == second && first == third) || ((first != second && first != third) && second != third)
|
||||
}
|
||||
|
||||
mutating func addCardsToTable(_ numberOfCards: Int) {
|
||||
var i = 0
|
||||
var cardsAdded = 0
|
||||
while cardsAdded < numberOfCards && i < cards.count {
|
||||
let card = cards[i]
|
||||
if (!card.isOnTheTable && !card.isMatched) {
|
||||
let cardIndexToBeModified = cards.firstIndex(where: {$0.id == card.id})
|
||||
if (cardIndexToBeModified != nil) {
|
||||
cards[cardIndexToBeModified!].isOnTheTable = true
|
||||
cardsAdded += 1
|
||||
}
|
||||
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
var displayedCards: Array<Card> {
|
||||
cards.filter { card in
|
||||
card.isOnTheTable
|
||||
// && !card.isMatched
|
||||
}.sorted {
|
||||
$0.matchId < $1.matchId
|
||||
}
|
||||
}
|
||||
|
||||
var cardsOnTheTable: Int {
|
||||
cards.filter({$0.isOnTheTable && !$0.isMatched}).count
|
||||
}
|
||||
|
||||
init() {
|
||||
var deck = SetGame.createCards()
|
||||
deck.shuffle()
|
||||
let cardsOnTable = Array(deck[0..<12])
|
||||
deck.removeAll { card in
|
||||
cardsOnTable.contains { $0 == card }
|
||||
for i in 0..<GameConstants.cardsOnTableInTheBeggining {
|
||||
deck[i].isOnTheTable = true
|
||||
}
|
||||
self.deck = deck
|
||||
self.cardsOnTable = cardsOnTable
|
||||
self.cards = deck
|
||||
}
|
||||
|
||||
private static func createCards() -> Array<SetGame.Card> {
|
||||
@@ -30,7 +119,7 @@ struct SetGame {
|
||||
var id = 0
|
||||
for symbol in CardSymbol.allCases {
|
||||
for shading in CardShading.allCases {
|
||||
for number in 1..<4 {
|
||||
for number in 1..<GameConstants.numberOfDifferences {
|
||||
for symbolColor in colors.indices {
|
||||
let newCard = SetGame.Card(id: id, shading: shading, symbol: symbol, color: colors[symbolColor], numberOnCard: number)
|
||||
deck.append(newCard)
|
||||
@@ -42,17 +131,12 @@ struct SetGame {
|
||||
return deck
|
||||
}
|
||||
|
||||
mutating func newGame() {
|
||||
var deck = SetGame.createCards()
|
||||
deck.shuffle()
|
||||
let cardsOnTable = Array(deck[0..<12])
|
||||
deck.removeAll { card in
|
||||
cardsOnTable.contains { $0 == card }
|
||||
private func randomColor() -> Color {
|
||||
let red = Double.random(in: 0...1)
|
||||
let blue = Double.random(in: 0...1)
|
||||
let green = Double.random(in: 0...1)
|
||||
return Color(red: red, green: green, blue: blue).opacity(0.5)
|
||||
}
|
||||
self.deck = deck
|
||||
self.cardsOnTable = cardsOnTable
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Card: Identifiable, Equatable {
|
||||
@@ -62,7 +146,15 @@ struct SetGame {
|
||||
var color: Color
|
||||
var isMatched: Bool = false
|
||||
var isSelected: Bool = false
|
||||
var isOnTheTable: Bool = false
|
||||
var numberOnCard: Int
|
||||
var matchId: Int = -1
|
||||
}
|
||||
|
||||
private struct GameConstants {
|
||||
static let numberOfDifferences = 4
|
||||
static let cardsOnTableInTheBeggining = 12
|
||||
static let numberOfCardsToDraw = 3
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class SetGameModelView: ObservableObject {
|
||||
@@ -19,12 +20,32 @@ class SetGameModelView: ObservableObject {
|
||||
}
|
||||
|
||||
var cardsOnTable: Array<Card> {
|
||||
model.cardsOnTable
|
||||
model.displayedCards.filter(showCard)
|
||||
}
|
||||
|
||||
private func showCard(card: SetGameModelView.Card) -> Bool {
|
||||
!card.isMatched
|
||||
}
|
||||
|
||||
func newGame() {
|
||||
self.model = SetGame()
|
||||
}
|
||||
|
||||
func choose(_ card: Card) {
|
||||
self.model.choose(card)
|
||||
}
|
||||
|
||||
var score: Int {
|
||||
model.score
|
||||
}
|
||||
|
||||
func deal() {
|
||||
self.model.addCardsToTable(3)
|
||||
}
|
||||
|
||||
var colors: [Int : Color] {
|
||||
self.model.colors
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,31 +12,63 @@ struct SetGameView: View {
|
||||
@ObservedObject var game: SetGameModelView
|
||||
var body: some View {
|
||||
VStack {
|
||||
AspectVGrid(items: game.cardsOnTable, aspectRatio: 2/3) { card in
|
||||
CardView(card: card).foregroundColor(.red)
|
||||
ScrollOrAspectVGrid()
|
||||
Spacer()
|
||||
Spacer()
|
||||
VStack {
|
||||
Button(action: {game.deal()}, label: { Text("Deal 3 more cards").font(.largeTitle) })
|
||||
HStack {
|
||||
Button(action: {game.newGame()}, label: {Text("New game").font(.largeTitle)}).padding()
|
||||
Spacer()
|
||||
Text("\(game.score)").font(.largeTitle).bold()
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Spacer()
|
||||
Button(action: {game.newGame()}, label: {Text("New game").font(.largeTitle)})
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func ScrollOrAspectVGrid() -> some View {
|
||||
if (game.cardsOnTable.count <= 21) {
|
||||
AspectVGrid(items: game.cardsOnTable, aspectRatio: 2/3) { card in
|
||||
CardView(card: card, colors: game.colors).foregroundColor(.red).onTapGesture {
|
||||
game.choose(card)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ScrollView {
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 70), spacing: 0)], spacing: 0) {
|
||||
ForEach(game.cardsOnTable) { card in
|
||||
CardView(card: card, colors: game.colors)
|
||||
.aspectRatio(2/3, contentMode: .fit)
|
||||
.foregroundColor(.red)
|
||||
.onTapGesture {
|
||||
game.choose(card)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CardView: View {
|
||||
var colors: [Int : Color]
|
||||
var card: SetGameModelView.Card
|
||||
var numberOfSymbols: Int
|
||||
var color: Color
|
||||
var symbol: CardSymbol
|
||||
var shading: CardShading
|
||||
var selectedGreen: Color = Color(hue: 0.355, saturation: 1.0, brightness: 1.0)
|
||||
|
||||
init(card: SetGameModelView.Card) {
|
||||
init(card: SetGameModelView.Card, colors: [Int : Color]) {
|
||||
self.card = card
|
||||
self.numberOfSymbols = card.numberOnCard
|
||||
self.color = card.color
|
||||
self.symbol = card.symbol
|
||||
self.shading = card.shading
|
||||
self.colors = colors;
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -44,14 +76,16 @@ struct CardView: View {
|
||||
ZStack {
|
||||
let cardShape = RoundedRectangle(cornerRadius: 10)
|
||||
cardShape.aspectRatio(2/3, contentMode: .fit)
|
||||
if (card.isSelected) {
|
||||
cardShape.foregroundColor(selectedGreen)
|
||||
} else {
|
||||
cardShape.foregroundColor(.white)
|
||||
}
|
||||
cardShape.strokeBorder(lineWidth: 5)
|
||||
|
||||
if (shading == CardShading.open) {
|
||||
}
|
||||
VStack {
|
||||
ForEach (0..<numberOfSymbols, id: \.self) { _ in
|
||||
createShape().frame(height: geometry.size.height/4)
|
||||
createShape(height: geometry.size.height / 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,14 +93,17 @@ struct CardView: View {
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func createShape() -> some View {
|
||||
private func createShape(height: CGFloat) -> some View {
|
||||
switch symbol {
|
||||
case .diamond:
|
||||
colorShape(Diamond())
|
||||
.frame(height: height)
|
||||
case .squiggles:
|
||||
colorShape(Rectangle())
|
||||
.frame(height: height)
|
||||
default:
|
||||
colorShape(RoundedRectangle(cornerRadius: 100))
|
||||
.frame(height: height)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
resources/AppIcon.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |