CS193P Assignment #3 Set Game
각 task 별 solution을 정리했습니다.
Required Task
1. Implement a game of solo (i.e. one player) Set
2. As the game play progresses, try to keep all the cards visible and as large as possible. In other words, cards should get smaller (or larger) as more (or fewer) appear onscreen at the same ti
me. It’s okay if you want to enforce a minimum size for your cards and then revert to scrolling when there are a very large number of cards. Whatever way you deal with “lots of cards” on screen, it must always still be possible to play the game (i.e. cards must always be recognizable, even when all 81 are in play at the same time).
3. Cards can have any aspect ratio you like, but they must all have the same aspect ratio at all times (no matter their size and no matter how many are on screen at the same time). In other words, cards can be appearing to the user to get larger and smaller as the game goes on, but the cards cannot be “stretching” into different aspect ratios as the game is played.
👉 제공되는AspectVGrid를 이용하여 해결했습니다.
4. The symbols on cards should be proportional to the size of the card (i.e. large cards should have large symbols and smaller cards should have smaller symbols).
👉 cardView에서 geometryReader를 이용하고, geometry의 size에 따라 symbol 크기를 결정하도록 하였습니다.
카드의 사이즈에 따라 symbol 크기가 유동적으로 변경됩니다.
var body: some View {
GeometryReader(content: { geometry in
ZStack {
let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: DrawingConstants.lindWidth)
VStack {
ForEach(0..<card.numberOfSymbol, id:\.self) { _ in
symbol
.frame(width: geometry.size.width/2, height: geometry.size.height/6)
}
}
.foregroundColor(cardColor)
if card.isChoose {
shape.foregroundColor(.gray).opacity(0.5)
}
if let isSet = card.isSet {
shape.foregroundColor(isSet ? .green : .red).opacity(0.6)
}
}
})
}
5. Users must be able to select up to 3 cards by touching on them in an attempt to make a Set (i.e. 3 cards which match, per the rules of Set). It must be clearly visible to the user which cards have been selected so far.
6. After 3 cards have been selected, you must indicate whether those 3 cards are a match or mismatch. You can show this any way you want (colors, borders, backgrounds, whatever). Anytime there are 3 cards currently selected, it must be clear to the user whether they are a match or not (and the cards involved in a non-matching trio must look different than the cards look when there are only 1 or 2 cards in the selection).
👉 선택한 카드(gray), not match(red), match(green), 선택하지 않은 카드(white)로 표시됩니다.
Card 구조체에 isChoose, isSet 프로퍼티를 이용해서 위 상태를 결정합니다.
struct Card: Identifiable {
var isChoose = false
var isSet: Bool?
var numberOfSymbol: Int
var symbol: Symbol
var shading: Shading
var color: Color
var id: Int
}
7. Support “deselection” by touching already-selected cards (but only if there are 1 or 2 cards (not 3) currently selected).
👉 card의 isChoose property를 toggle 하는 방법으로 deselection을 지원합니다.
세 번째 카드 선택은 Set 검증 단계 때문에 deselection 되지 않고, 첫, 두 번째 카드 선택에 대해서만 deselection 됩니다.
if let chosenIndex = table.firstIndex(where: {$0.id == card.id}) {
table[chosenIndex].isChoose.toggle()
}
8. When any card is touched on and there are already 3 matching Set cards selected, then …
a. as per the rules of Set, replace those 3 matching Set cards with new ones from the deck
b. if the deck is empty then the space vacated by the matched cards (which cannot be replaced since there are no more cards) should be made available to the remaining cards (i.e. which may well then get bigger)
c. if the touched card was not part of the matching Set, then select that card
d. if the touched card was part of a matching Set, then select no card
👉 3번째 카드를 선택할 때 Set 검증을 하지 않고, 3개의 카드를 선택한 상태에서 새로운 카드를 선택할 때 Set 검증을 한다.
카드 선택의 로직인 Choose에서 먼저 카드 Set여부를 확인합니다.
mutating func choose(_ card: Card) {
if indexOfchosen.count == GameConstant.numberOfMatchCard {
if isMatch {
replaceNewCard()
}
table.indices.forEach {
table[$0].isChoose = false
table[$0].isSet = nil
}
}
if let chosenIndex = table.firstIndex(where: {$0.id == card.id}) {
table[chosenIndex].isChoose.toggle()
if indexOfchosen.count == GameConstant.numberOfMatchCard {
let isMatch = isMatch
indexOfchosen.forEach({ i in
table[i].isSet = isMatch
})
}
}
}
9. When any card is touched and there are already 3 non-matching Set cards selected, deselect those 3 non-matching cards and select the touched-on card (whether or not it was part of the non-matching trio of cards).
👉 Choose 함수에서 처리한다. 카드 3장 선택이지만 not Set인 경우에 모든 카드 선택과 관련된 프로퍼티를 초기화합니다.
10.You will need to have a “Deal 3 More Cards” button (per the rules of Set).
a. when it is touched, replace the selected cards if the selected cards make a Set
b. or, if the selected cards do not make a Set (or if there are fewer than 3 cards selected, including none), add 3 new cards to join the ones already on screen (and do not affect the selection)
c. disable this button if the deck is empty
👉 dealThreeMoreCard 함수에서 처리된다.
Set 여부에 따라 카드 추가 위치가 다르기 때문에 구분되어 처리됩니다.
Set인 경우 기존 위치에 카드가 추가되고, Set가 아닌 경우에는 table에 가장 뒤쪽에 카드가 추가됩니다.
mutating func dealThreeMoreCard() {
if indexOfchosen.count == GameConstant.numberOfMatchCard {
if isMatch {
replaceNewCard()
return
}
}
for _ in 0..<GameConstant.numberOfDealCard {
table.append(deck.popLast()!)
}
}
11.You also must have a “New Game” button that starts a new game (i.e. back to 12 randomly chosen cards).
👉 New Game 버튼에 대한 행위를 viewModel에서 model을 새로 생성하여 해결했습니다.
func newGame() {
model = Self.createSetGame()
}
12.To make your life a bit easier, you can replace the “squiggle” appearance in the Set game with a rectangle.
👉 Rectangle을 이용했습니다.
13.You must author your own Shape struct to do the diamond.
👉 Diamond를 Shape 구조체로 생성했습니다.
14.Another life-easing change is that you can use a semi-transparent color to represent the “striped” shading. Be sure to pick a transparency level that is clearly distinguishable from “solid”.
👉 striped 속성을 도형의 opcacity를 0.5로 주는 것으로 변경했습니다.
15.You can use any 3 colors as long as they are clearly distinguishable from each other.
👉 red, green, purple 3개의 색깔을 사용했습니다.
16.You must use an enum as a meaningful part of your solution.
👉 카드를 정의하는 속성인 Symbol, Shading, Color을 enum을 이용했습니다.
enum Symbol: CaseIterable {
case diamond
case rectangle
case oval
}
enum Shading: CaseIterable {
case solid
case striped
case open
}
enum Color: CaseIterable {
case red
case green
case purple
}
17.You must use a closure (i.e. a function as an argument) as a meaningful part of your solution.
👉 closure를 사용한 부분이 없습니다.
18.Your UI should work in portrait or landscape on any iOS device. This probably will not require any work on your part (that’s part of the power of SwiftUI), but be sure to experiment with running on different simulators/Previews in Xcode to be sure.
Screenshots
'iOS > CS193p - Developing Apps for iOS' 카테고리의 다른 글
[CS193p] Lecture 11 - Persistence에 대해서 (0) | 2021.12.28 |
---|---|
[CS193p] GCD를 async/await로 변경하기 (0) | 2021.12.01 |
[CS193p] Assignment 4 - Animated Set 솔루션 (0) | 2021.11.19 |
[CS193p] Assignment 4 - Animated Set 문제 번역 (0) | 2021.11.16 |
[CS193p] Assignment 3 - Set Game 문제 번역 (0) | 2021.11.08 |