Это продолжение решения Задания 2. Здесь мы рассмотрим решение некоторых дополнительных пунктов.
Решение обязательных пунктов Задания 2 и дополнительного пункта 3 представлены в посте «Задание 2 Stanford CS 193P Fall 2017. Игра Set. Решение обязательных пунктов.».
Текст Домашнего задания на английском языке доступен на iTunes в пункте “Programming: Project 2: Set″. На русском языке вы можете скачать текст Задания 2 здесь:
Для решения Задания 2 необходимо ознакомиться с Лекциями 1 — 6.
Решение обязательных пунктов Задания 2 находится на Github для iOS 11 и на Github для iOS 12 в папке «Set II No Extra«.
Решение обязательных и дополнительных пунктов Задания 2 находится на Github для iOS 11 и на Github для iOS 12 в папке «Set II With Extra«.
Мы будем рассматривать выполнение дополнительных пунктов немного не в том порядке, как они представлены в тексте Задания 2, так как они частично уже описаны либо в решениях Задания № 1, либо в посте, посвященном выполнению обязательных пунктов Задания № 2. Мы сосредоточимся на выполнении самого интересного 4-го дополнительного пункта этого Задания, в котором необходимо предоставить пользователю играть напару с iPhone.
Пункты 1 дополнительный
Фактор “скорость игры» включается в счет пользователя. Вы можете определить сколько прошло времени (в долях секунды), используя структуру struct Date. Чем быстрее пользователь находит Sets, тем больше его или ее счет должен быть. Штраф для “несовпадение”, возможно, должен быть больше, чтобы отбить охоту слишком частому простому угадыванию.
Я не буду выполнять этот дополнительный пункт, так как он полностью аналогичен дополнительному пункту 2 Задания № 1, который описан в посте «Задание 1 Stanford CS 193P Fall 2017. Игра Концентрация. Решение.»
Пункты 3 дополнительный
Если вы напишете алгоритм определения Sets, то сможете добавить “мошенническую” кнопку для того, чтобы испытывающий трудности пользователь смог использовать ее для нахождения Set!
Алгоритм определения Sets, подробно описан в посте, посвященном выполнению обязательных пунктов Задания № 2. В структуре SetGame представлен алгоритм нахождения всех Sets для карт, лежащих на игровом столе cardsOnTable, принимая во внимание, что если мы можем находимся в «пограничном» состоянии, когда Set только что определен, и все карты, принадлежащие ему, пока находятся на игорном столе, но будут удалены на следующем шаге и не должны приниматься во внимание при определении Sets:
Дополнительно к этому, у нас в приложении организована «мошенническая» кнопка, которая последовательно подсвечивает на короткое время все работоспособные Sets, находящиеся на игорном столе :
Так что пользователь может смело идти по подсказкам и довольно быстро закончить игру
Пункты 2 дополнительный
Штрафуйте нажатие кнопки “Deal 3 More Cards”, если в видимых картах есть доступный Set. Вым нужно написать алгоритм для определения того, содержит ли кучка Set карт по крайней мере один Set.
Имея в распоряжении алгоритм определения Sets для карт, лежащих на игорном столе cardsOnTable, мы можем легко организовать штраф за «сдачу» 3-х карт, если имелся Set в видимых картах. Мы это будем выполнять в Модели в структуре SetGame:
Все штрафы и поощрительные очки находятся в структуре struct Points:
Пункты 4 дополнительный
Или даже у вас может быть режим “играй против iPhone”. Каждый раз, когда Set обнаружен, стартуйте таймер со случайной продолжительностью (вам придется для этого изучить Timer.scheduledTimer(withTimeInterval:repeats:) { }, использующий замыкание), по истечение которой iPhone выбирает Set, если пользователь еще не выбрал. Смотрите, кто набрал больше Sets! Может быть представить iPhone с помощью эмоджи где-то на экране (? пока отсчитывается время, затем, может быть ? за пару секунд перед тем, как наступает очередь iPhone и ? , если iPhone выиграл или ? ,если — нет)? Как всегда, убедитесь, что вы очень внимательно продумали свою MVC архитектуру, если вы используете этот шаблон конструирования.
Это очень интересный пункт, и мы посвятим ему основное внимание в этом посте. Мы начинаем с того, что в нашем ViewController создадим перечисление enum Player, которое имеет два значения: .me — играю я, .iPhone — играет iPhone. У меня будет также переменная var currentPlayer, представляющая текущего игрока и переключающаяся между двумя значениями : Player.me и Player.iPhone:
Как только мы установим значение переменной currentPlayer, изменится новая public переменная playerIndex в нашей Модели в структуре struct SetGame :
теперь в структуре struct SetGame переменные flipCount, score и numberSets стали массивами, состоящими из 2х элементов, каждый из которых соответственно предназначен для двух игроков. Подсчет очков и формирование счета теперь ведется для flipCount [playerIndex], score [playerIndex ] и numberSets[playerIndex] :
Больше никаких изменений в Модели нам не потребовалось.
На нашем UI мы добавим специальный Stack View с метками для iPhone и создадим для них Outlets:
будем обновлять счет и число «пойманных» Sets теперь уже для двух игроков:
Если игрок-пользователь получил Set, то появляется шанс у игрока-iPhone также получить Set , если в пределах Constants.iPhoneWaitTime секунд игрок-пользователь не продолжит игру, то есто не кликнет на какой-нибудь карте:
У игрока-iPhone такая же стратегия, как и у обычного игрока, но с некоторыми элементами вероятности:
- Сначала мы избавляемся от совпавших и составляющих Set карт, кликнув на любой случайной карте, отличной от карт сформированного игроком-пользователем Set, в результате эти карты будут заменены или удалены с игрового стола.
- Затем сделать все выделенные карты (скорее всего это будет одна карта, выделенная на предыдущем шаге) НЕвыделенными, последовательно кликая на них.
- Благодаря первым двум шагам, мы оказались в исходной ситуации для выбора нового Set, и здесь мы условно «бросаем монетку» на удачу с вероятностью 2/3 и даем игрока-iPhone выбрать Set из подсказок hints, что гарантирует успех. Или выбирать карты случайным образом, что почти наверняка приводит к неудаче.
- В случае удачи на предыдущем шаге, кликаем на картах первой из подсказок hints.
- В случае неудачи на предыдущем шаге, формируем карты Set случайным образом.
- Обновляем UI, на котором отображается результат работы игрока-iPhone и производится зачисление очков и Sets.
Вся эта стратегия игрока-iPhone представлена в методе tryiPhone ():
Надо сказать, что не так-то просто выполнить, например, пункт № 1 нашей стратегии, то есть кликнуть на любой случайной карте, отличной от карт, образовавших Set и находящихся в данный момент на игорном столе, так как в ViewController у меня нет доступа к изменению массивов карт, задействованных в Модели struct SetGame:
Соблюдая конвенцию MVC, я могу только считывать эти массивы карт и выбирать любую карту с помощью метода :
game.chooseCard(at: idx)
Поэтому методы, обеспечивающие стратегию игрока-iPhone, выглядят следующим образом:
И это все. Начнем игру. Вы выбираете Set, возможно с помощью «мошеннической» кнопки с подсказками, и ждете 2 секунды. Через 2 секунды сыграет игрок-iPhone, и вы увидите его удачу или неудачу:
В данном случае игрок-iPhone выиграл, он вам показывает выигранный Set, но показывает, что больше нет других Sets, в картах, лежащих в данный момент на игорном столе, так что вам придется кликнуть на случайной карте, чтобы карты выигранного Set заменились на новые, и у вас появилась бы возможность выбрать новый Set:
Теперь пользователь выбирает этот Set, и через 2 секунды ожидания игрок-iPhone пробует обнаружить свой Set, но терпит неудачу, так как на игровом столе совсем не осталось Sets:
В подобной ситуации мы вынуждены добавить 3 карты на игральный стол, и у нас в подсказках появляется Sets:
Далее мы уже действуем по обычной схеме. Пользователь подсвечивает подсказку, выбирает согласно ей Set и ждет 2 секунды, затем очередь игрока-iPhone, но iPhone проигрывает, так как это НЕ Set:
Надо сказать, что ситуация для игрока-iPhone, не всегда так трагично, как получилось у нас, чаще игроку-iPhone все таки удается выигрывать, но если игрок-пользователь будет следовать только подсказкам, то, естественно игрок-iPhone, будет проигрывать.
Решение Задания 2 находится на Github для iOS 11 и на Github для iOS 12 в папке «Set II With Extra«.
iPhone никогда «не получит ход», если после выбора сета игрок сразу же нажмёт любую кнопку. Да, он получит за отмену выбора в дальнейшем небольшой штраф, но и только. Что-то здесь не так…
Это было сделано специально, чтобы не мешать пользователю играть свою игру. Мне было интересно имитировать игру iPhone с точки зрения программирования на Swift и с точки зрения архитектуры iOS приложения. По-моему у меня получилось.
Задача разработки стратегии полностью равноправной версии игры с iPhone мной не ставилась.
На основе той версии, которая есть, можно развивать игру бесконечно в любую сторону.
Если позволит время, может быть вернусь к этому приложению.
Мой вариант где и волки (iPhone & Swift) сыты и овцы (game-play) целы: https://github.com/hiraethB/GameSet_Assingment2.git