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
|
### 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.
|
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
|
## 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" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "40.png",
|
||||||
"platform" : "ios",
|
"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"
|
"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
|
import SwiftUI
|
||||||
|
|
||||||
struct SetGame {
|
struct SetGame {
|
||||||
private var deck: Array<Card>
|
private(set) var cards: Array<Card>
|
||||||
private(set) var cardsOnTable: 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() {
|
init() {
|
||||||
var deck = SetGame.createCards()
|
var deck = SetGame.createCards()
|
||||||
deck.shuffle()
|
deck.shuffle()
|
||||||
let cardsOnTable = Array(deck[0..<12])
|
for i in 0..<GameConstants.cardsOnTableInTheBeggining {
|
||||||
deck.removeAll { card in
|
deck[i].isOnTheTable = true
|
||||||
cardsOnTable.contains { $0 == card }
|
|
||||||
}
|
}
|
||||||
self.deck = deck
|
self.cards = deck
|
||||||
self.cardsOnTable = cardsOnTable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func createCards() -> Array<SetGame.Card> {
|
private static func createCards() -> Array<SetGame.Card> {
|
||||||
@@ -30,7 +119,7 @@ struct SetGame {
|
|||||||
var id = 0
|
var id = 0
|
||||||
for symbol in CardSymbol.allCases {
|
for symbol in CardSymbol.allCases {
|
||||||
for shading in CardShading.allCases {
|
for shading in CardShading.allCases {
|
||||||
for number in 1..<4 {
|
for number in 1..<GameConstants.numberOfDifferences {
|
||||||
for symbolColor in colors.indices {
|
for symbolColor in colors.indices {
|
||||||
let newCard = SetGame.Card(id: id, shading: shading, symbol: symbol, color: colors[symbolColor], numberOnCard: number)
|
let newCard = SetGame.Card(id: id, shading: shading, symbol: symbol, color: colors[symbolColor], numberOnCard: number)
|
||||||
deck.append(newCard)
|
deck.append(newCard)
|
||||||
@@ -42,17 +131,12 @@ struct SetGame {
|
|||||||
return deck
|
return deck
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func newGame() {
|
private func randomColor() -> Color {
|
||||||
var deck = SetGame.createCards()
|
let red = Double.random(in: 0...1)
|
||||||
deck.shuffle()
|
let blue = Double.random(in: 0...1)
|
||||||
let cardsOnTable = Array(deck[0..<12])
|
let green = Double.random(in: 0...1)
|
||||||
deck.removeAll { card in
|
return Color(red: red, green: green, blue: blue).opacity(0.5)
|
||||||
cardsOnTable.contains { $0 == card }
|
|
||||||
}
|
}
|
||||||
self.deck = deck
|
|
||||||
self.cardsOnTable = cardsOnTable
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Card: Identifiable, Equatable {
|
struct Card: Identifiable, Equatable {
|
||||||
@@ -62,7 +146,15 @@ struct SetGame {
|
|||||||
var color: Color
|
var color: Color
|
||||||
var isMatched: Bool = false
|
var isMatched: Bool = false
|
||||||
var isSelected: Bool = false
|
var isSelected: Bool = false
|
||||||
|
var isOnTheTable: Bool = false
|
||||||
var numberOnCard: Int
|
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 Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
class SetGameModelView: ObservableObject {
|
class SetGameModelView: ObservableObject {
|
||||||
@@ -19,12 +20,32 @@ class SetGameModelView: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cardsOnTable: Array<Card> {
|
var cardsOnTable: Array<Card> {
|
||||||
model.cardsOnTable
|
model.displayedCards.filter(showCard)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showCard(card: SetGameModelView.Card) -> Bool {
|
||||||
|
!card.isMatched
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGame() {
|
func newGame() {
|
||||||
self.model = SetGame()
|
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
|
@ObservedObject var game: SetGameModelView
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
AspectVGrid(items: game.cardsOnTable, aspectRatio: 2/3) { card in
|
ScrollOrAspectVGrid()
|
||||||
CardView(card: card).foregroundColor(.red)
|
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 {
|
struct CardView: View {
|
||||||
|
var colors: [Int : Color]
|
||||||
var card: SetGameModelView.Card
|
var card: SetGameModelView.Card
|
||||||
var numberOfSymbols: Int
|
var numberOfSymbols: Int
|
||||||
var color: Color
|
var color: Color
|
||||||
var symbol: CardSymbol
|
var symbol: CardSymbol
|
||||||
var shading: CardShading
|
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.card = card
|
||||||
self.numberOfSymbols = card.numberOnCard
|
self.numberOfSymbols = card.numberOnCard
|
||||||
self.color = card.color
|
self.color = card.color
|
||||||
self.symbol = card.symbol
|
self.symbol = card.symbol
|
||||||
self.shading = card.shading
|
self.shading = card.shading
|
||||||
|
self.colors = colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -44,14 +76,16 @@ struct CardView: View {
|
|||||||
ZStack {
|
ZStack {
|
||||||
let cardShape = RoundedRectangle(cornerRadius: 10)
|
let cardShape = RoundedRectangle(cornerRadius: 10)
|
||||||
cardShape.aspectRatio(2/3, contentMode: .fit)
|
cardShape.aspectRatio(2/3, contentMode: .fit)
|
||||||
|
if (card.isSelected) {
|
||||||
|
cardShape.foregroundColor(selectedGreen)
|
||||||
|
} else {
|
||||||
cardShape.foregroundColor(.white)
|
cardShape.foregroundColor(.white)
|
||||||
|
}
|
||||||
cardShape.strokeBorder(lineWidth: 5)
|
cardShape.strokeBorder(lineWidth: 5)
|
||||||
|
|
||||||
if (shading == CardShading.open) {
|
|
||||||
}
|
|
||||||
VStack {
|
VStack {
|
||||||
ForEach (0..<numberOfSymbols, id: \.self) { _ in
|
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
|
@ViewBuilder
|
||||||
private func createShape() -> some View {
|
private func createShape(height: CGFloat) -> some View {
|
||||||
switch symbol {
|
switch symbol {
|
||||||
case .diamond:
|
case .diamond:
|
||||||
colorShape(Diamond())
|
colorShape(Diamond())
|
||||||
|
.frame(height: height)
|
||||||
case .squiggles:
|
case .squiggles:
|
||||||
colorShape(Rectangle())
|
colorShape(Rectangle())
|
||||||
|
.frame(height: height)
|
||||||
default:
|
default:
|
||||||
colorShape(RoundedRectangle(cornerRadius: 100))
|
colorShape(RoundedRectangle(cornerRadius: 100))
|
||||||
|
.frame(height: height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
resources/AppIcon.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |