Π’Π°Π·ΠΈ ΡΡΠ°ΡΠΈΡ ΠΎΠΏΠΈΡΠ²Π° ΠΏΡΠΎΡΠ΅ΡΠ° Π½Π° ΡΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΏΡΠΎΡΡΠ° ΠΈΠ³ΡΠ° Π·Π° ΡΡΠ΅Π½ΠΈΡΠ°Π½Π΅ Π½Π° ΠΏΠ°ΠΌΠ΅ΡΡΠ°, ΠΊΠΎΡΡΠΎ Π½Π°ΠΈΡΡΠΈΠ½Π° Ρ
Π°ΡΠ΅ΡΠ²Π°ΠΌ. ΠΡΠ²Π΅Π½ ΡΠ΅ Π΅ Π΄ΠΎΠ±ΡΡ ΡΠ°ΠΌ ΠΏΠΎ ΡΠ΅Π±Π΅ ΡΠΈ, ΡΠ΅ Π½Π°ΡΡΠΈΡΠ΅ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎΠ²Π΅ΡΠ΅ Π·Π° ΠΊΠ»Π°ΡΠΎΠ²Π΅ΡΠ΅ ΠΈ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»ΠΈΡΠ΅ Π½Π° Swift, Π΄ΠΎΠΊΠ°ΡΠΎ Π²ΡΡΠ²ΠΈΡΠ΅. ΠΠΎ ΠΏΡΠ΅Π΄ΠΈ Π΄Π° Π·Π°ΠΏΠΎΡΠ½Π΅ΠΌ, Π½Π΅ΠΊΠ° ΡΠ°Π·Π±Π΅ΡΠ΅ΠΌ ΡΠ°ΠΌΠ°ΡΠ° ΠΈΠ³ΡΠ°.
ΠΠ°ΠΏΠΎΠΌΠ½ΡΠΌΠ΅ Π²ΠΈ: Π·Π° Π²ΡΠΈΡΠΊΠΈ ΡΠΈΡΠ°ΡΠ΅Π»ΠΈ Π½Π° "Habr" - ΠΎΡΡΡΡΠΏΠΊΠ° ΠΎΡ 10 000 ΡΡΠ±Π»ΠΈ ΠΏΡΠΈ Π·Π°ΠΏΠΈΡΠ²Π°Π½Π΅ Π²ΡΠ² Π²ΡΠ΅ΠΊΠΈ ΠΊΡΡΡ Skillbox, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΏΡΠΎΠΌΠΎΡΠΈΠΎΠ½Π°Π»Π½ΠΈΡ ΠΊΠΎΠ΄ Π½Π° "Habr".
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°: ΠΠ±ΡΠ°Π·ΠΎΠ²Π°ΡΠ΅Π»Π΅Π½ ΠΎΠ½Π»Π°ΠΉΠ½ ΠΊΡΡΡ
βΠΡΠΎΡΠ΅ΡΠΈΡ Java ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊβ .
ΠΠ°ΠΊ ΡΠ΅ ΠΈΠ³ΡΠ°Π΅ ΠΊΠ°ΡΡΠ° Ρ ΠΏΠ°ΠΌΠ΅Ρ
ΠΠ³ΡΠ°ΡΠ° Π·Π°ΠΏΠΎΡΠ²Π° Ρ Π΄Π΅ΠΌΠΎΠ½ΡΡΡΠ°ΡΠΈΡ Π½Π° Π½Π°Π±ΠΎΡ ΠΎΡ ΠΊΠ°ΡΡΠΈ. ΠΠ΅ΠΆΠ°Ρ Ρ Π»ΠΈΡΠ΅ΡΠΎ Π½Π°Π΄ΠΎΠ»Ρ (ΡΡΠΎΡΠ²Π΅ΡΠ½ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ Π½Π°Π΄ΠΎΠ»Ρ). ΠΠΎΠ³Π°ΡΠΎ ΡΡΠ°ΠΊΠ½Π΅ΡΠ΅ Π²ΡΡΡ Ρ ΠΊΠΎΠΉΡΠΎ ΠΈ Π΄Π° Π΅, ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ ΡΠ΅ ΠΎΡΠ²Π°ΡΡ Π·Π° Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΡΠ΅ΠΊΡΠ½Π΄ΠΈ.
ΠΠ°Π΄Π°ΡΠ°ΡΠ° Π½Π° ΠΈΠ³ΡΠ°ΡΠ° Π΅ Π΄Π° Π½Π°ΠΌΠ΅ΡΠΈ Π²ΡΠΈΡΠΊΠΈ ΠΊΠ°ΡΡΠΈ Ρ Π΅Π΄Π½Π°ΠΊΠ²ΠΈ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ. ΠΠΊΠΎ ΡΠ»Π΅Π΄ ΠΊΠ°ΡΠΎ ΠΎΡΠ²ΠΎΡΠΈΡΠ΅ ΠΏΡΡΠ²Π°ΡΠ° ΠΊΠ°ΡΡΠ°, ΠΎΠ±ΡΡΠ½Π΅ΡΠ΅ Π²ΡΠΎΡΠ°ΡΠ° ΠΈ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈΡΠ΅ ΡΡΠ²ΠΏΠ°Π΄Π½Π°Ρ, Π΄Π²Π΅ΡΠ΅ ΠΊΠ°ΡΡΠΈ ΠΎΡΡΠ°Π²Π°Ρ ΠΎΡΠ²ΠΎΡΠ΅Π½ΠΈ. ΠΠΊΠΎ Π½Π΅ ΡΡΠ²ΠΏΠ°Π΄Π°Ρ, ΠΊΠ°ΡΡΠΈΡΠ΅ ΡΠ΅ Π·Π°ΡΠ²Π°ΡΡΡ ΠΎΡΠ½ΠΎΠ²ΠΎ. Π¦Π΅Π»ΡΠ° Π΅ Π΄Π° ΡΠ΅ ΠΎΡΠ²ΠΎΡΠΈ Π²ΡΠΈΡΠΊΠΎ.
Π‘ΡΡΡΠΊΡΡΡΠ° Π½Π° ΠΏΡΠΎΠ΅ΠΊΡΠ°
ΠΠ° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ ΠΏΡΠΎΡΡΠ° Π²Π΅ΡΡΠΈΡ Π½Π° ΡΠ°Π·ΠΈ ΠΈΠ³ΡΠ°, ΠΈΠΌΠ°ΡΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΡΠ»Π΅Π΄Π½ΠΈΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΈ:
- ΠΠ΄ΠΈΠ½ ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅Ρ: GameController.swift.
- ΠΠ΄ΠΈΠ½ ΠΈΠ·Π³Π»Π΅Π΄: CardCell.swift.
- ΠΠ²Π° ΠΌΠΎΠ΄Π΅Π»Π°: MemoryGame.swift ΠΈ Card.swift.
- Main.storyboard, Π·Π° Π΄Π° ΡΠ΅ Π³Π°ΡΠ°Π½ΡΠΈΡΠ°, ΡΠ΅ ΡΠ΅Π»ΠΈΡΡ Π½Π°Π±ΠΎΡ ΠΎΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΈ Π΅ Π½Π°Π»ΠΈΡΠ΅Π½.
ΠΠ°ΠΏΠΎΡΠ²Π°ΠΌΠ΅ Ρ Π½Π°ΠΉ-ΠΏΡΠΎΡΡΠΈΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°, ΠΊΠ°ΡΡΠΈΡΠ΅.
Card.swift
ΠΠΎΠ΄Π΅Π»ΡΡ Π½Π° ΠΊΠ°ΡΡΠ°ΡΠ° ΡΠ΅ ΠΈΠΌΠ° ΡΡΠΈ ΡΠ²ΠΎΠΉΡΡΠ²Π°: id Π·Π° ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΡΠΈΡΠ°Π½Π΅ Π½Π° Π²ΡΡΠΊΠ° Π΅Π΄Π½Π°, ΠΏΠΎΠΊΠ°Π·Π°Π½Π° Π±ΡΠ»Π΅Π²Π° ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π°, Π·Π° Π΄Π° ΡΡΠΎΡΠ½ΠΈ ΡΡΠ°ΡΡΡΠ° Π½Π° ΠΊΠ°ΡΡΠ°ΡΠ° (ΡΠΊΡΠΈΡΠ° ΠΈΠ»ΠΈ ΠΎΡΠ²ΠΎΡΠ΅Π½Π°) ΠΈ artworkURL Π·Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡΠ° Π½Π° ΠΊΠ°ΡΡΠΈΡΠ΅.
class Card {
var id: String
var shown: Bool = false
var artworkURL: UIImage!
}
Π©Π΅ Π²ΠΈ ΡΡΡΠ±Π²Π°Ρ ΠΈ ΡΠ΅Π·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ΠΈ, Π·Π° Π΄Π° ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΠ°ΡΠ΅ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ΡΠΎ Π½Π° ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΠΈΡΠ΅ Ρ ΠΊΠ°ΡΡΠΈΡΠ΅:
ΠΠ΅ΡΠΎΠ΄ Π·Π° ΠΏΠΎΠΊΠ°Π·Π²Π°Π½Π΅ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π²ΡΡΡ Ρ ΠΊΠ°ΡΡΠ°. Π’ΡΠΊ Π½ΡΠ»ΠΈΡΠ°ΠΌΠ΅ Π²ΡΠΈΡΠΊΠΈ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΏΠΎ ΠΏΠΎΠ΄ΡΠ°Π·Π±ΠΈΡΠ°Π½Π΅. ΠΠ° id Π³Π΅Π½Π΅ΡΠΈΡΠ°ΠΌΠ΅ ΡΠ»ΡΡΠ°Π΅Π½ id ΡΡΠ΅Π· ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅ Π½Π° NSUUIS().uuidString.
init(image: UIImage) {
self.id = NSUUID().uuidString
self.shown = false
self.artworkURL = image
}
ΠΠ΅ΡΠΎΠ΄ Π·Π° ΡΡΠ°Π²Π½ΡΠ²Π°Π½Π΅ Π½Π° Π»ΠΈΡΠ½ΠΈ ΠΊΠ°ΡΡΠΈ.
func equals(_ card: Card) -> Bool {
return (card.id == id)
}
ΠΠ΅ΡΠΎΠ΄ Π·Π° ΡΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΊΠΎΠΏΠΈΠ΅ Π½Π° Π²ΡΡΠΊΠ° ΠΊΠ°ΡΡΠ° - Ρ ΡΠ΅Π» ΠΏΠΎΠ»ΡΡΠ°Π²Π°Π½Π΅ Π½Π° ΠΏΠΎ-Π³ΠΎΠ»ΡΠΌ Π±ΡΠΎΠΉ Π΅Π΄Π½Π°ΠΊΠ²ΠΈ. Π’ΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ Π²ΡΡΠ½Π΅ ΠΊΠ°ΡΡΠ° Ρ ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΈ ΡΡΠΎΠΉΠ½ΠΎΡΡΠΈ.
func copy() -> Card {
return Card(card: self)
}
init(card: Card) {
self.id = card.id
self.shown = card.shown
self.artworkURL = card.artworkURL
}
Π ΠΎΡΠ΅ Π΅Π΄ΠΈΠ½ ΠΌΠ΅ΡΠΎΠ΄ Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌ Π·Π° ΡΠ°Π·Π±ΡΡΠΊΠ²Π°Π½Π΅ Π½Π° ΠΊΠ°ΡΡΠΈΡΠ΅ Π² Π½Π°ΡΠ°Π»ΠΎΡΠΎ. Π©Π΅ Π³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ ΡΠ°Π·ΡΠΈΡΠ΅Π½ΠΈΠ΅ Π½Π° ΠΊΠ»Π°ΡΠ° Array.
extension Array {
mutating func shuffle() {
for _ in 0...self.count {
sort { (_,_) in arc4random() < arc4random() }
}
}
}
Π Π΅ΡΠΎ ΠΈ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡΡΠ° Π½Π° ΠΊΠΎΠ΄Π° Π·Π° ΠΌΠΎΠ΄Π΅Π»Π° Card Ρ Π²ΡΠΈΡΠΊΠΈ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΈ ΠΌΠ΅ΡΠΎΠ΄ΠΈ.
class Card {
var id: String
var shown: Bool = false
var artworkURL: UIImage!
static var allCards = [Card]()
init(card: Card) {
self.id = card.id
self.shown = card.shown
self.artworkURL = card.artworkURL
}
init(image: UIImage) {
self.id = NSUUID().uuidString
self.shown = false
self.artworkURL = image
}
func equals(_ card: Card) -> Bool {
return (card.id == id)
}
func copy() -> Card {
return Card(card: self)
}
}
extension Array {
mutating func shuffle() {
for _ in 0...self.count {
sort { (_,_) in arc4random() < arc4random() }
}
}
}
ΠΡΠΎΠ΄ΡΠ»ΠΆΠ°Π²Π°ΠΉ.
ΠΡΠΎΡΠΈΡΡ ΠΌΠΎΠ΄Π΅Π» Π΅ MemoryGame, ΡΡΠΊ ΡΠΌΠ΅ Π·Π°Π΄Π°Π»ΠΈ ΡΠ΅ΡΠ΅ΡΠΊΠ° 4*4. ΠΠΎΠ΄Π΅Π»ΡΡ ΡΠ΅ ΠΈΠΌΠ° ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΊΠ°ΡΠΎ ΠΊΠ°ΡΡΠΈ (ΠΌΠ°ΡΠΈΠ² ΠΎΡ ΠΊΠ°ΡΡΠΈ Π² ΡΠ΅ΡΠ΅ΡΠΊΠ°), ΠΌΠ°ΡΠΈΠ² cardsShown Ρ Π²Π΅ΡΠ΅ ΠΎΡΠ²ΠΎΡΠ΅Π½ΠΈ ΠΊΠ°ΡΡΠΈ ΠΈ Π±ΡΠ»Π΅Π²Π° ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π° isPlaying Π·Π° ΠΏΡΠΎΡΠ»Π΅Π΄ΡΠ²Π°Π½Π΅ Π½Π° ΡΡΡΡΠΎΡΠ½ΠΈΠ΅ΡΠΎ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°.
class MemoryGame {
var cards:[Card] = [Card]()
var cardsShown:[Card] = [Card]()
var isPlaying: Bool = false
}
ΠΠΈΠ΅ ΡΡΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΈΠΌ ΠΌΠ΅ΡΠΎΠ΄ΠΈ Π·Π° ΠΊΠΎΠ½ΡΡΠΎΠ» Π½Π° Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ΡΠΎ Π½Π° ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΠΈΡΠ΅ Ρ ΠΌΡΠ΅ΠΆΠ°ΡΠ°.
ΠΠ΅ΡΠΎΠ΄, ΠΊΠΎΠΉΡΠΎ ΡΠ°Π·Π±ΡΡΠΊΠ²Π° ΠΊΠ°ΡΡΠΈ Π² ΠΌΡΠ΅ΠΆΠ°.
func shuffleCards(cards:[Card]) -> [Card] {
var randomCards = cards
randomCards.shuffle()
return randomCards
}
ΠΠ΅ΡΠΎΠ΄ Π·Π° ΡΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° Π½ΠΎΠ²Π° ΠΈΠ³ΡΠ°. Π’ΡΠΊ ΠΈΠ·Π²ΠΈΠΊΠ²Π°ΠΌΠ΅ ΠΏΡΡΠ²ΠΈΡ ΠΌΠ΅ΡΠΎΠ΄ Π·Π° ΡΡΠ°ΡΡΠΈΡΠ°Π½Π΅ Π½Π° ΠΏΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΎΡΠΎ ΠΎΡΠΎΡΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΈ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠ°Π½Π΅ Π½Π° ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π°ΡΠ° isPlaying Π½Π° true.
func newGame(cardsArray:[Card]) -> [Card] {
cards = shuffleCards(cards: cardsArray)
isPlaying = true
return cards
}
ΠΠΊΠΎ ΠΈΡΠΊΠ°ΠΌΠ΅ Π΄Π° ΡΠ΅ΡΡΠ°ΡΡΠΈΡΠ°ΠΌΠ΅ ΠΈΠ³ΡΠ°ΡΠ°, ΡΠ»Π΅Π΄ ΡΠΎΠ²Π° Π·Π°Π΄Π°Π²Π°ΠΌΠ΅ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π°ΡΠ° isPlaying Π½Π° false ΠΈ ΠΏΡΠ΅ΠΌΠ°Ρ Π²Π°ΠΌΠ΅ ΠΏΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΎΡΠΎ ΠΎΡΠΎΡΠΌΠ»Π΅Π½ΠΈΠ΅ Π½Π° ΠΊΠ°ΡΡΠΈΡΠ΅.
func restartGame() {
isPlaying = false
cards.removeAll()
cardsShown.removeAll()
}
ΠΠ΅ΡΠΎΠ΄ Π·Π° ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° Π½Π° ΠΊΠ»ΠΈΠΊΠ½Π°ΡΠΈ ΠΊΠ°ΡΡΠΈ. ΠΠΎΠ²Π΅ΡΠ΅ Π·Π° Π½Π΅Π³ΠΎ ΠΏΠΎ-ΠΊΡΡΠ½ΠΎ.
func cardAtIndex(_ index: Int) -> Card? {
if cards.count > index {
return cards[index]
} else {
return nil
}
}
ΠΠ΅ΡΠΎΠ΄, ΠΊΠΎΠΉΡΠΎ Π²ΡΡΡΠ° ΠΏΠΎΠ·ΠΈΡΠΈΡΡΠ° Π½Π° ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½Π° ΠΊΠ°ΡΡΠ°.
func indexForCard(_ card: Card) -> Int? {
for index in 0...cards.count-1 {
if card === cards[index] {
return index
}
}
return nil
}
ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π½Π° ΡΡΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠ΅ΡΠΎ Π½Π° ΠΈΠ·Π±ΡΠ°Π½Π°ΡΠ° ΠΊΠ°ΡΡΠ° ΡΡΡ ΡΡΠ°Π½Π΄Π°ΡΡΠ°.
func unmatchedCardShown() -> Bool {
return cardsShown.count % 2 != 0
}
Π’ΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ΡΠ΅ ΠΏΠΎΡΠ»Π΅Π΄Π½ΠΈΡ Π΅Π»Π΅ΠΌΠ΅Π½Ρ Π² ΠΌΠ°ΡΠΈΠ²Π° **cardsShown** ΠΈ Π²ΡΡΡΠ° Π½Π΅ΡΡΠΎΡΠ²Π΅ΡΡΡΠ²Π°ΡΠ°ΡΠ° ΠΊΠ°ΡΡΠ°.
func didSelectCard(_ card: Card?) {
guard let card = card else { return }
if unmatchedCardShown() {
let unmatched = unmatchedCard()!
if card.equals(unmatched) {
cardsShown.append(card)
} else {
let secondCard = cardsShown.removeLast()
}
} else {
cardsShown.append(card)
}
if cardsShown.count == cards.count {
endGame()
}
}
Main.storyboard ΠΈ GameController.swift
Main.storyboard ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΏΠΎ ΡΠ»Π΅Π΄Π½ΠΈΡ Π½Π°ΡΠΈΠ½:
ΠΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΎ ΡΡΡΠ±Π²Π° Π΄Π° Π·Π°Π΄Π°Π΄Π΅ΡΠ΅ Π½ΠΎΠ²Π°ΡΠ° ΠΈΠ³ΡΠ° ΠΊΠ°ΡΠΎ viewDidLoad Π² ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅ΡΠ°, Π²ΠΊΠ»ΡΡΠΈΡΠ΅Π»Π½ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡΠ° Π·Π° ΠΌΡΠ΅ΠΆΠ°ΡΠ°. Π ΠΈΠ³ΡΠ°ΡΠ° Π²ΡΠΈΡΠΊΠΎ ΡΠΎΠ²Π° ΡΠ΅ Π±ΡΠ΄Π΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π΅Π½ΠΎ ΠΎΡ 4*4 collectionView. ΠΠΊΠΎ Π²ΡΠ΅ ΠΎΡΠ΅ Π½Π΅ ΡΡΠ΅ Π·Π°ΠΏΠΎΠ·Π½Π°ΡΠΈ Ρ collectionView, Π΅ΡΠΎ Π³ΠΎ
ΠΠΈΠ΅ ΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΠΌΠ΅ GameController ΠΊΠ°ΡΠΎ ΠΎΡΠ½ΠΎΠ²Π΅Π½ ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅Ρ Π½Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ. GameController ΡΠ΅ ΠΈΠΌΠ° collectionView, ΠΊΠΎΠΉΡΠΎ ΡΠ΅ Π½Π°ΡΠΈΡΠ°ΠΌΠ΅ IBOutlet. ΠΡΡΠ³Π° ΠΏΡΠ΅ΠΏΡΠ°ΡΠΊΠ° Π΅ ΠΊΡΠΌ Π±ΡΡΠΎΠ½Π° IBAction onStartGame(), ΡΠΎΠ²Π° Π΅ UIButton, ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π³ΠΎ Π²ΠΈΠ΄ΠΈΡΠ΅ Π² ΡΡΠ΅Π½Π°ΡΠΈΡ, Π½Π°ΡΠ΅ΡΠ΅Π½ PLAY.
ΠΠ°Π»ΠΊΠΎ Π·Π° Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ΡΠΎ Π½Π° ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅ΡΠΈ:
- ΠΡΡΠ²ΠΎ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠ°ΠΌΠ΅ Π΄Π²Π° ΠΎΡΠ½ΠΎΠ²Π½ΠΈ ΠΎΠ±Π΅ΠΊΡΠ° - ΠΌΡΠ΅ΠΆΠ°ΡΠ°: game = MemoryGame() ΠΈ Π½Π°Π±ΠΎΡ ΠΎΡ ΠΊΠ°ΡΡΠΈ: cards = [Card]().
- ΠΠ°Π΄Π°Π²Π°ΠΌΠ΅ ΠΏΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΈΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ ΠΊΠ°ΡΠΎ viewDidLoad, ΡΠΎΠ²Π° Π΅ ΠΏΡΡΠ²ΠΈΡΡ ΠΌΠ΅ΡΠΎΠ΄, ΠΊΠΎΠΉΡΠΎ ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π°, Π΄ΠΎΠΊΠ°ΡΠΎ ΠΈΠ³ΡΠ°ΡΠ° ΡΠ°Π±ΠΎΡΠΈ.
- collectionView Π΅ Π½Π°ΡΡΡΠΎΠ΅Π½ Π½Π° ΡΠΊΡΠΈΡ, Π·Π°ΡΠΎΡΠΎ Π²ΡΠΈΡΠΊΠΈ ΠΊΠ°ΡΡΠΈ ΡΠ° ΡΠΊΡΠΈΡΠΈ, Π΄ΠΎΠΊΠ°ΡΠΎ ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΡΡ Π½Π΅ Π½Π°ΡΠΈΡΠ½Π΅ PLAY.
- ΠΠ΅Π΄Π½Π°Π³Π° ΡΠ»Π΅Π΄ ΠΊΠ°ΡΠΎ Π½Π°ΡΠΈΡΠ½Π΅ΠΌ PLAY, ΡΠ΅ΠΊΡΠΈΡΡΠ° onStartGame IBAction Π·Π°ΠΏΠΎΡΠ²Π° ΠΈ Π½ΠΈΠ΅ Π·Π°Π΄Π°Π²Π°ΠΌΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎΡΠΎ collectionView isHidden Π½Π° false, ΡΠ°ΠΊΠ° ΡΠ΅ ΠΊΠ°ΡΡΠΈΡΠ΅ Π΄Π° ΠΌΠΎΠ³Π°Ρ Π΄Π° ΡΡΠ°Π½Π°Ρ Π²ΠΈΠ΄ΠΈΠΌΠΈ.
- ΠΡΠ΅ΠΊΠΈ ΠΏΡΡ, ΠΊΠΎΠ³Π°ΡΠΎ ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΡΡ ΠΈΠ·Π±Π΅ΡΠ΅ ΠΊΠ°ΡΡΠ°, ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π° ΠΌΠ΅ΡΠΎΠ΄ΡΡ didSelectItemAt. Π ΠΌΠ΅ΡΠΎΠ΄Π°, ΠΊΠΎΠΉΡΠΎ ΠΈΠ·Π²ΠΈΠΊΠ²Π°ΠΌΠ΅ didSelectCard, Π·Π° Π΄Π° ΡΠ΅Π°Π»ΠΈΠ·ΠΈΡΠ°ΠΌΠ΅ ΠΎΡΠ½ΠΎΠ²Π½Π°ΡΠ° Π»ΠΎΠ³ΠΈΠΊΠ° Π½Π° ΠΈΠ³ΡΠ°ΡΠ°.
ΠΡΠΎ ΠΎΠΊΠΎΠ½ΡΠ°ΡΠ΅Π»Π½ΠΎΡΠΎ Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ Π½Π° GameController:
class GameController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
let game = MemoryGame()
var cards = [Card]()
override func viewDidLoad() {
super.viewDidLoad()
game.delegate = self
collectionView.dataSource = self
collectionView.delegate = self
collectionView.isHidden = true
APIClient.shared.getCardImages { (cardsArray, error) in
if let _ = error {
// show alert
}
self.cards = cardsArray!
self.setupNewGame()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if game.isPlaying {
resetGame()
}
}
func setupNewGame() {
cards = game.newGame(cardsArray: self.cards)
collectionView.reloadData()
}
func resetGame() {
game.restartGame()
setupNewGame()
}
@IBAction func onStartGame(_ sender: Any) {
collectionView.isHidden = false
}
}
// MARK: - CollectionView Delegate Methods
extension GameController: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cards.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCell
cell.showCard(false, animted: false)
guard let card = game.cardAtIndex(indexPath.item) else { return cell }
cell.card = card
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! CardCell
if cell.shown { return }
game.didSelectCard(cell.card)
collectionView.deselectItem(at: indexPath, animated:true)
}
}
Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° ΠΏΠΎΠ³ΠΎΠ²ΠΎΡΠΈΠΌ ΠΌΠ°Π»ΠΊΠΎ Π·Π° Π²Π°ΠΆΠ½ΠΈΡΠ΅ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»ΠΈ.
ΠΏΡΠΎΡΠΎΠΊΠΎΠ»ΠΈ
Π Π°Π±ΠΎΡΠ°ΡΠ° Ρ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»ΠΈ Π΅ ΡΠ΄ΡΠΎΡΠΎ Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΡΠ°Π½Π΅ΡΠΎ Π½Π° Swift. ΠΡΠΎΡΠΎΠΊΠΎΠ»ΠΈΡΠ΅ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²ΡΡ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ Π·Π° Π΄Π΅ΡΠΈΠ½ΠΈΡΠ°Π½Π΅ Π½Π° ΠΏΡΠ°Π²ΠΈΠ»Π° Π·Π° ΠΊΠ»Π°Ρ, ΡΡΡΡΠΊΡΡΡΠ° ΠΈΠ»ΠΈ ΠΈΠ·Π±ΡΠΎΡΠ²Π°Π½Π΅. Π’ΠΎΠ·ΠΈ ΠΏΡΠΈΠ½ΡΠΈΠΏ Π²ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° ΠΏΠΈΡΠ΅ΡΠ΅ ΠΌΠΎΠ΄ΡΠ»Π΅Π½ ΠΈ ΡΠ°Π·ΡΠΈΡΡΠ΅ΠΌ ΠΊΠΎΠ΄. ΠΡΡΡΠ½ΠΎΡΡ ΡΠΎΠ²Π° Π΅ ΠΌΠΎΠ΄Π΅Π», ΠΊΠΎΠΉΡΠΎ Π²Π΅ΡΠ΅ ΠΏΡΠΈΠ»Π°Π³Π°ΠΌΠ΅ Π·Π° collectionView Π² GameController. Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ Π½Π°ΡΠ° ΡΠΎΠ±ΡΡΠ²Π΅Π½Π° Π²Π΅ΡΡΠΈΡ. Π‘ΠΈΠ½ΡΠ°ΠΊΡΠΈΡΡΡ ΡΠ΅ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΡΠ°ΠΊΠ°:
protocol MemoryGameProtocol {
//protocol definition goes here
}
ΠΠ½Π°Π΅ΠΌ, ΡΠ΅ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»ΡΡ Π½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° Π΄Π΅ΡΠΈΠ½ΠΈΡΠ°ΠΌΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΈΠ»ΠΈ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΈ Π·Π° Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ Π½Π° ΠΊΠ»Π°Ρ, ΡΠ°ΠΊΠ° ΡΠ΅ Π½Π΅ΠΊΠ° ΠΏΠΎΠΌΠΈΡΠ»ΠΈΠΌ ΠΊΠ°ΠΊΠ²ΠΈ ΡΡΡΠ±Π²Π° Π΄Π° Π±ΡΠ΄Π°Ρ ΡΠ΅. Π’ΡΡΠ±Π²Π°Ρ Π²ΠΈ ΠΎΠ±ΡΠΎ ΡΠ΅ΡΠΈΡΠΈ.
- ΠΠ°ΡΠ°Π»ΠΎ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°: memoryGameDidStart.
- Π’ΡΡΠ±Π²Π° Π΄Π° ΠΎΠ±ΡΡΠ½Π΅ΡΠ΅ ΠΊΠ°ΡΡΠ°ΡΠ° Ρ Π»ΠΈΡΠ΅ΡΠΎ Π½Π°Π΄ΠΎΠ»Ρ: memoryGameShowCards.
- Π’ΡΡΠ±Π²Π° Π΄Π° ΠΎΠ±ΡΡΠ½Π΅ΡΠ΅ ΠΊΠ°ΡΡΠ°ΡΠ° Ρ Π»ΠΈΡΠ΅ΡΠΎ Π½Π°Π΄ΠΎΠ»Ρ: memoryGameHideCards.
- ΠΡΠ°ΠΉ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°: memoryGameDidEnd.
Π ΡΠ΅ΡΠΈΡΠΈΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Π° ΠΌΠΎΠ³Π°Ρ Π΄Π° Π±ΡΠ΄Π°Ρ ΡΠ΅Π°Π»ΠΈΠ·ΠΈΡΠ°Π½ΠΈ Π·Π° ΠΎΡΠ½ΠΎΠ²Π½ΠΈΡ ΠΊΠ»Π°Ρ, ΠΊΠΎΠΉΡΠΎ Π΅ GameController.
memoryGameDidStart
ΠΠΎΠ³Π°ΡΠΎ ΡΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΠΈ, ΠΈΠ³ΡΠ°ΡΠ° ΡΡΡΠ±Π²Π° Π΄Π° Π·Π°ΠΏΠΎΡΠ½Π΅ (ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»ΡΡ Π½Π°ΡΠΈΡΠΊΠ° PLAY). Π’ΡΠΊ ΠΏΡΠΎΡΡΠΎ ΡΠ΅ ΠΏΡΠ΅Π·Π°ΡΠ΅Π΄ΠΈΠΌ ΡΡΠ΄ΡΡΠΆΠ°Π½ΠΈΠ΅ΡΠΎ, ΠΊΠ°ΡΠΎ ΠΈΠ·Π²ΠΈΠΊΠ°ΠΌΠ΅ collectionView.reloadData(), ΠΊΠΎΠ΅ΡΠΎ ΡΠ΅ ΡΠ°Π·Π±ΡΡΠΊΠ° ΠΊΠ°ΡΡΠΈΡΠ΅.
func memoryGameDidStart(_ game: MemoryGame) {
collectionView.reloadData()
}
memoryGameShowCards
ΠΠ·Π²ΠΈΠΊΠ²Π°ΠΌΠ΅ ΡΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΠΎΡ collectionSDViewSelectItemAt. ΠΡΡΠ²ΠΎ ΠΏΠΎΠΊΠ°Π·Π²Π° ΠΈΠ·Π±ΡΠ°Π½Π°ΡΠ° ΠΊΠ°ΡΡΠ°. Π‘Π»Π΅Π΄ ΡΠΎΠ²Π° ΠΏΡΠΎΠ²Π΅ΡΡΠ²Π° Π΄Π°Π»ΠΈ ΠΈΠΌΠ° Π½Π΅ΡΡΠ²ΠΏΠ°Π΄Π°ΡΠ° ΠΊΠ°ΡΡΠ° Π² ΠΌΠ°ΡΠΈΠ²Π° cardsShown (Π°ΠΊΠΎ Π±ΡΠΎΡΡ Π½Π° cardsShown Π΅ Π½Π΅ΡΠ΅ΡΠ΅Π½). ΠΠΊΠΎ ΠΈΠΌΠ° ΡΠ°ΠΊΠ°Π²Π°, ΠΈΠ·Π±ΡΠ°Π½Π°ΡΠ° ΠΊΠ°ΡΡΠ° ΡΠ΅ ΡΡΠ°Π²Π½ΡΠ²Π° Ρ Π½Π΅Ρ. ΠΠΊΠΎ ΡΠ½ΠΈΠΌΠΊΠΈΡΠ΅ ΡΠ° Π΅Π΄Π½Π°ΠΊΠ²ΠΈ, Π΄Π²Π΅ΡΠ΅ ΠΊΠ°ΡΡΠΈ ΡΠ΅ Π΄ΠΎΠ±Π°Π²ΡΡ ΠΊΡΠΌ cardsShown ΠΈ ΠΎΡΡΠ°Π²Π°Ρ Ρ Π»ΠΈΡΠ΅ΡΠΎ Π½Π°Π³ΠΎΡΠ΅. ΠΠΊΠΎ Π΅ ΡΠ°Π·Π»ΠΈΡΠ½Π°, ΠΊΠ°ΡΡΠ°ΡΠ° ΠΎΡΡΠ°Π²Ρ ΠΊΠ°ΡΡΠΈΡΠ΅ ΠΠΎΠΊΠ°Π·Π°Π½ΠΈ ΠΈ Π΄Π²Π΅ΡΠ΅ ΡΠ΅ ΠΎΠ±ΡΡΡΠ°Ρ Ρ Π»ΠΈΡΠ΅ΡΠΎ Π½Π°Π΄ΠΎΠ»Ρ.
memoryGameHideCards
ΠΠΊΠΎ ΠΊΠ°ΡΡΠΈΡΠ΅ Π½Π΅ ΡΡΠ²ΠΏΠ°Π΄Π°Ρ, ΡΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π° ΠΈ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡΠ° Π½Π° ΠΊΠ°ΡΡΠΈΡΠ΅ ΡΠ΅ ΡΠΊΡΠΈΠ²Π°Ρ.
ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ = Π½Π΅Π²ΡΡΠ½ΠΎ.
memoryGameDidEnd
ΠΠΎΠ³Π°ΡΠΎ ΡΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ Π±ΡΠ΄Π΅ ΠΈΠ·Π²ΠΈΠΊΠ°Π½, ΡΠΎΠ²Π° ΠΎΠ·Π½Π°ΡΠ°Π²Π°, ΡΠ΅ Π²ΡΠΈΡΠΊΠΈ ΠΊΠ°ΡΡΠΈ Π²Π΅ΡΠ΅ ΡΠ° ΡΠ°Π·ΠΊΡΠΈΡΠΈ ΠΈ ΡΠ° Π² ΡΠΏΠΈΡΡΠΊΠ° cardsShown: cardsShown.count = cards.count, ΡΠ°ΠΊΠ° ΡΠ΅ ΠΈΠ³ΡΠ°ΡΠ° Π΅ ΠΏΡΠΈΠΊΠ»ΡΡΠΈΠ»Π°. ΠΠ΅ΡΠΎΠ΄ΡΡ ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π° ΡΠΏΠ΅ΡΠΈΠ°Π»Π½ΠΎ, ΡΠ»Π΅Π΄ ΠΊΠ°ΡΠΎ ΡΠΌΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ°Π»ΠΈ endGame(), Π·Π° Π΄Π° Π·Π°Π΄Π°Π΄Π΅ΠΌ isPlaying var Π½Π° false, ΡΠ»Π΅Π΄ ΠΊΠΎΠ΅ΡΠΎ ΡΠ΅ ΠΏΠΎΠΊΠ°Π·Π²Π° ΡΡΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ΡΠΎ Π·Π° ΠΊΡΠ°ΠΉ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°. Π‘ΡΡΠΎ ΡΠ°ΠΊΠ° alertController ΡΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΊΠ°ΡΠΎ ΠΈΠ½Π΄ΠΈΠΊΠ°ΡΠΎΡ Π·Π° ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅ΡΠ°. viewDidDisappear ΡΠ΅ ΠΈΠ·Π²ΠΈΠΊΠ²Π° ΠΈ ΠΈΠ³ΡΠ°ΡΠ° ΡΠ΅ Π½ΡΠ»ΠΈΡΠ°.
ΠΡΠΎ ΠΊΠ°ΠΊ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° Π²ΡΠΈΡΠΊΠΎ ΡΠΎΠ²Π° Π² GameController:
extension GameController: MemoryGameProtocol {
func memoryGameDidStart(_ game: MemoryGame) {
collectionView.reloadData()
}
func memoryGame(_ game: MemoryGame, showCards cards: [Card]) {
for card in cards {
guard let index = game.indexForCard(card)
else { continue
}
let cell = collectionView.cellForItem(
at: IndexPath(item: index, section:0)
) as! CardCell
cell.showCard(true, animted: true)
}
}
func memoryGame(_ game: MemoryGame, hideCards cards: [Card]) {
for card in cards {
guard let index = game.indexForCard(card)
else { continue
}
let cell = collectionView.cellForItem(
at: IndexPath(item: index, section:0)
) as! CardCell
cell.showCard(false, animted: true)
}
}
func memoryGameDidEnd(_ game: MemoryGame) {
let alertController = UIAlertController(
title: defaultAlertTitle,
message: defaultAlertMessage,
preferredStyle: .alert
)
let cancelAction = UIAlertAction(
title: "Nah", style: .cancel) {
[weak self] (action) in
self?.collectionView.isHidden = true
}
let playAgainAction = UIAlertAction(
title: "Dale!", style: .default) {
[weak self] (action) in
self?.collectionView.isHidden = true
self?.resetGame()
}
alertController.addAction(cancelAction)
alertController.addAction(playAgainAction)
self.present(alertController, animated: true) { }
resetGame()
}
}
ΠΡΡΡΠ½ΠΎΡΡ ΡΠΎΠ²Π° Π΅ Π²ΡΠΈΡΠΊΠΎ. ΠΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ ΡΠΎΠ·ΠΈ ΠΏΡΠΎΠ΅ΠΊΡ, Π·Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ ΡΠ²ΠΎΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π° Π²Π΅ΡΡΠΈΡ Π½Π° ΠΈΠ³ΡΠ°ΡΠ°.
ΠΡΠΈΡΡΠ½ΠΎ ΠΊΠΎΠ΄ΠΈΡΠ°Π½Π΅!
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°:
- ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠΠΎΠ±ΠΈΠ»Π΅Π½ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ PROβ .- ΠΡΠΈΠ»ΠΎΠΆΠ΅Π½ ΠΎΠ½Π»Π°ΠΉΠ½ ΠΊΡΡΡ
βΠΠ½Π°Π»ΠΈΠ·Π°ΡΠΎΡ Π½Π° Π΄Π°Π½Π½ΠΈ Π½Π° Pythonβ .- ΠΠ²ΡΠ³ΠΎΠ΄ΠΈΡΠ΅Π½ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠΠ· ΡΡΠΌ PRO ΡΠ΅Π± ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊβ .
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com