Статистика созданных ChatGPT алгоритмов Expeсtimax и Monte Carlo для игры 2048

В предыдущих постах — iOS приложения игры 2048 в SwiftUI  с ChatGPT 4-o. Часть 1, iOS приложения игры 2048 в SwiftUI  с ChatGPT 4-o. Часть 2. Анимация и UI, iOS приложения игры 2048 в SwiftUI  с ChatGPT 4-o. Часть 3. ИИ, — я рассказала о том, как ChatGPT помог создать эффективные ИИ алгоритмы Expectimax  и Monte Carlo для игры 2048. Это стохастические алгоритмы, то есть их результаты — максимальное значение value плитки maxTile и счет score — случайные величины. Хотелось бы иметь экспериментальное распределение этих случайных величин в виде гистограмм для того, чтобы выбрать их оптимальные параметры.

Приложение Game2048ChatGPT было расширено c целью сохранения результатов многократных запусков алгоритмов Expectimax  и Monte Carlo  в базе данных (БД) SwiftData для последующего статистического анализа. При написании кода максимально использовался ИИ ChatGPT, который иногда, ломая все стереотипы программирования, предлагает очень оригинальные решения, и именно это помогло получить такой лаконичный и читабельный код для нашей статистической задачи. Этот код находится на GitHub.

Я не буду утомлять вас протоколом взаимодействия с ChatGPT, a сразу приведу результаты статистических исследований, которые оптимальным образом помогли настроить параметры ИИ алгоритмов  Expectimax  и Monte Carlo.

Читать далее

iOS приложение игры 2048 в SwiftUI с ChatGPT. Часть 3. ИИ (AI) для игры 2048.

В двух предыдущих постах мы рассмотрели создание логики игры 2048 и разработку UI с анимацией. В этом посте мы добавим ИИ (искусственный интеллект ) для игры 2048 в виде алгоритмов Expectimax и Monte Carlo. Код находится на Github.

ШАГ 16.  Добавление AI в игру 2048

Добавление ИИ в игру 2048 подразумевает реализацию логики, которая может автоматически выбирать лучший ход на каждом шаге. ИИ будет, например, использовать функцию bestMoveDirection(), которую мы ранее обсуждали, чтобы определить, какой ход выполнить, основываясь на максимальном увеличении счета. В этом случае ИИ может автоматически играть в игру 2048, делая оптимальные ходы.

Таким образом, нам понадобится метод выполнения хода ИИ, возможность запуска его автоматически с определенной периодичностью, и, переключатель для переключения между ручным  режимом со swipe жестом и воспроизведением ИИ.

Но давайте сначала поймем, какие в SwiftUI есть средства запуска определенного кода автоматически через равные промежутки времени:

Читать далее

iOS приложения игры 2048 в SwiftUI  с ChatGPT 4-o. Часть 2. Анимация и UI.

В прошлом посте «iOS приложение игры 2048 в SwiftUI с ChatGPT 4-o. Часть 1. Логика игры» показано, как реализовать логику игры 2048 c помощью ChatGPT. В этом посте мы рассмотрим проектирование UI игры 2048 с помощью ChatGPT и особое внимание уделим анимации перемещения плиток на игровой доске. Код находится на Github.

Анимация и UI

Шаг 8. Анимация

Читать далее

iOS приложения игры 2048 в SwiftUI  с ChatGPT 4-o. Часть 1. Введение. Логика игры 2048.

Я хочу поделиться с вами опытом создания «с нуля» iOS приложения известной игры 2048 с элементами ИИ (искусственного интеллекта) в SwiftUI с помощью ChatGPT . Код находится на Github.

В своем классическом варианте, когда играет пользователь с помощью жестов (вверх, вниз, вправо, влево),  это довольно простая игра и создать полноценное iOS приложение для такой игры 2048 можно за короткое время, при этом  код будет понятен каждому. Но простые правила игры только подталкивают к созданию оптимальных алгоритмов решения игры 2048, то есть к созданию ИИ, который мог бы играть в эту игру автоматически и максимизировать счет игры в разумные сроки. 

Мне хотелось написать игру 2048 именно на SwiftUI, пользуясь его прекрасной и мощной анимацией и приличным быстродействием , a также  предоставить в распоряжения пользователя не только “ручной” способ игры, когда Вы руководите тем, каким должен быть следующий ход: вверх, вниз, влево и вправо, но и ряд алгоритмов с оптимальной стратегией (метода Монте-Карлостратегий поиска по деревьям (Minimax, Expectimax) ), позволяющих АВТОМАТИЧЕСКИ выполнять ходы — вверх, вниз, влево и вправо — и добиться  плитки с числом 2048 и более (эти алгоритмы и называют алгоритмами “искусственного интеллекта” (ИИ)).  Необходимым элементом ИИ является алгоритм поиска, который позволяет смотреть вперед на возможные будущие позиции, прежде чем решить, какой ход он хочет сделать в текущей позиции. 

2048 — это очень известная игра, и мне не нужно было объяснять ChatGPT ее правила, он сам всё про неё знает. Кроме того, оказалось, что ChatGPT прекрасно осведомлен об ИИ алгоритмах для игры 2048, так что мне вообще не пришлось описывать ChatGPT контекст решаемой задачи. И он предлагал мне множество таких неординарных решений, которые мне пришлось бы долго выискивать в научных журналах.

Читать далее

Лекция 15. Документо-ориентированная архитектура. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 15 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 15 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

DocumentGroup в демонстрационном примере

Вот так обычно выглядит наше приложение.

И куда нам двигаться дальше? 
Что ж, чтобы наше приложение перестало быть тем, чем оно является сейчас, давайте, кстати, посмотрим, на что наше приложение способно сейчас, пока еще не добавили поддержку документов.
Вот мое приложение, и я могу сказать: “Add another window” (“Добавить еще одно окно”), чтобы получить красивое фоновое изображение. Можно добавить сюда еще эмодзи (смайлики). 
Я  еще раз могу сказать: “Add another window” и посмотрите, что произойдет, если я кликну на  иконке нашего приложения.
Я получаю второй Emoji Art.
Итак, теперь у меня фактически есть два окна, смотрящих на один и тот же документ. Вы видите здесь два документа. Это то же самое. И у обоих есть “грузовик” 🚚. 

Это потому, что оба этих окна смотрят на один и тот же @StateObject.

Это документ EmojiArt по умолчанию — defaultDocument. Поскольку они видят одну и ту же ViewModel, они показывают вам один и тот же документ.
У каждого из них есть собственное масштабирование zoom и смещение pan, поскольку это отдельные View, они немного отличаются, но оба они просматривают один и тот же документ.
Так что это в некотором смысле довольно удобно, что вы можете иметь несколько окон, просматривающих один и тот же документ, но это не так удобно, как иметь возможность просматривать множество документов.
Итак, первое, что мы собираемся сделать, это заменить WindowGroup на DocumentGroup. Это то, что лежит в основе поведения всех документов.
И вы помните, что у DocumentGroup был аргумент config, который является его конфигурацией.
И вместо того, чтобы использовать наш документ по умолчанию defaultDocument, который у нас есть, мы просто возьмем нашу ViewModel из этой конфигурации — config.document:

Читать далее

Лекция 14. Многопоточность, Обработка ошибок. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 14 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 14 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Демонстрационный пример: конечный автомат Background

Итак, давайте поговорим о том, как я собираюсь загружать фоновое изображение background концептуально. Я собираюсь использовать конечный автомат (state machine).
Сколько человек реально написали код, который использует конечный автомат (state machine)?
Ну, не так уж и много. Интересно.
Итак идея программирования конечных автоматов заключается в том, что я собираюсь подумать обо всех состояниях (states), в которых я могу оказаться, и выполняю некоторый процесс. На самом деле я собираюсь закодировать их и делать каким-то образом пометки в моем коде для каждого из этих шагов. И отличный ТИП данных для конечных автоматов — это перечисление enum. Потому что по определению вы перемещаетесь по этим различимым состояниях (states), a перечисление enum как раз и представляет различимые состояния (states).
Вот что представляет собой мой конечный автомат (state machine). Я размещу весь этот код отдельно, в разделе // MARK: — Background Image. Это будет перечисление enum с именем Background:

И каковы состояния (states) выборки чего-либо из Интернета?
Вы можете быть в состоянии none, то есть вы ничего не делаете. Нет, фонового изображения  background, которое Drag & Drop (перетаскивается и сбрасывается), вы просто нигде:

Возможно, я сейчас выбираю данные из Интернета, так что я мог бы назвать состояние fetching. Но когда я выбираю, возможно, я хочу знать URL, по которому идет выборка, и я могу получить URL в качестве ассоциированных данных:

Итак, я сделал выборку по этому URL, и либо у меня есть изображение, либо мне не удается этого сделать из-за какого-то сетевого сбоя или чего-то в этом роде.
В результате у меня действительно добавляются еще два состояния в моем конечном автомате.
В случае успешного завершения выборки я нахожусь в состоянии found с изображением UIImage, в противном случае я нахожусь в состоянии failed, и в этом случае я мог бы сохранить ошибку, которую я получил:

Для простоты я сохраняю ошибку в виде String, которая описывает, вероятно, localizedDescription полученной ошибки.
Итак, это состояния моего конечного автомата.
Я собираюсь пройти через эти состояния шаг за шагом и выполнить все необходимые действия.
Теперь, поскольку у меня есть этот маленький конечный автомат enum Background, я также создал небольшие удобные функции.
Давайте посмотрим на них:

Это маленькие переменные var, которые просто возвращают ассоциированные данные определенного состояния моего конечный автомат enum Background, если я нахожусь в этом состоянии.
Итак, вы видите вычисляемую переменную var uiImage:

Внутри мы переключаемся switch по self, и если я нахожусь в состоянии found с ассоциированным значение uiImage в виде выбранного изображения, то возвращает это изображение uiImage. В противном случае он просто возвращает nil.
То же самое происходит с получением URL-адреса urlBeingFetched, по которому осуществляется выборка:

Внутри мы переключаемся switch по self, и если я нахожусь в состоянии fetching с ассоциированным значение url, то возвращает этот url, по которому идет выборка изображения. В противном случае он просто возвращает nil.
То же самое с причиной неудачной выборки failureReason:

Если я нахожусь в состоянии failed, дайте мне причину reason моей неудачи.
Это всего лишь удобные переменные.
Так что я могу просто спросить, какое у меня фоновое изображение uiImage прямо сейчас?
И это будет nil, если у меня его просто нет. Если я не в состоянии found, когда я получаю изображение uiImage, a в любом другом состоянии: none или fetching или failed, я также получу nil.
Я разместил также маленькую Bool переменную var isFetching:

Эта переменная равна true только в том случае, когда мой urlBeingFetched не равен nil, то есть когда я действительно выбираю изображение из Интернета. Потому что когда я нахожусь; в состоянии fetching, у меня есть URL-адрес, и это единственное состояние, когда у меня есть этот URL-адрес.
Теперь, когда у меня есть мой компактный конечный автомат enum Background, я собираюсь избавиться от переменной var background, я закомментирую это, чтобы запомнить, как это было:

Вместо этого я собираюсь создать новую @Published переменную var, которую назову background, её ТИП будет Background и её начальное значение будет .none:

Я делаю её @Published, потому что именно так мой UI будет видеть фоновое изображение background. UI всегда имеет возможность увидеть, в каком состоянии я нахожусь, потому что смотрите — я даю ему это перечисление enum Background, в котором вы также можете использовать удобные функции.

Читать далее

Лекция 13. Представляющие (Presenting) Views. Навигация. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 13 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 13 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

TextField и @Binding в действии

Следующее, самое важное — я хочу иметь возможность редактировать название палитры, а также добавлять эмодзи (смайлики).
Как мне сделать это редактируемым текстовым полем?
Редактируемые текстовые поля в Swift — это View отличные от обычного текста Text. Вместо Text они называются TextField.

TextField имеет два аргумента.
Первый аргумент — это то, что мы называем Placeholder (Заполнитель) текста или слово, которое можно использовать, чтобы помочь пользователю понять, о чем мы здесь просим.
Второй аргумент, он называется text — это тот текст, который мы редактируем внутри этого текстового поля TextField.
И еще этот второй аргумент является привязкой Binding.

Если подумать о том, что здесь происходит, то у нас есть текстовое поле TextField, в котором текстом text является “Vehicles”. Мы хотим иметь возможность передать текстовому полю “Vehicles” как начальное значение, и всякий раз, когда что-то меняется в нем, мы хотим знать об этом.
И то, как мы собираемся это сделать, заключается в создании единственного “источника истины” (single source of truth) для этого текста text. Мы будем делать это с помощью привязки Binding.
В частности, этот второй аргумент text текстового поля TextField является привязкой Binding.
TextField знает, что он не хочет поддерживать копию того, что редактируется, он хочет редактировать эту вещь напрямую. Поэтому он просит вас дать ему привязка Binding к этому “источнику истины” (source of truth).
Ну a что является “источником истины” (source of truth) для этого palette.name?
Он находится в нашей ViewModel, в нашем PaletteStore. Следовательно, нам нужно дать здесь нашему TextField обратную привязку Binding к нашей ViewModel.
Для этого нам нужна привязка Binding к палитре palette, которую нам дали отредактировать в верхней части нашего PaletteEditor:

Сделав эту переменную var @Binding, мы заставляем того, кто создаст этот PaletteEditor, дать нам привязку Binding к “источнику истины” (source of truth) для этой палитры palette.
Теперь каждый раз, когда мы ссылаемся на эту палитру palette где угодно в нашем коде здесь, в редакторе PaletteEditor, на самом деле мы будем ссылаться в обратном порядке на палитру в нашей ViewModel.
И мы также можем использовать эту привязку Binding для передачи привязки Binding к имени палитры palette.name в нашем TextField:

Это потому, что для $, то есть projectedValue, для @Binding — это еще одна привязка Binding к той привязке @Binding.
Итак, $palette.name здесь означает привязку Binding к этой @Binding var palette, которая будет привязана в обратный порядке в конечном итоге к нашей ViewModel. Теперь наше текстовое поле TextField будет редактировать имя name палитры palette напрямую в ViewModel.
Вы видите, что эти $, эти привязки Binding, проходят через всю нашу систему Views.

Читать далее

Лекция 12. Постоянное хранение (Persistence). Обертки свойства (Property Wrappers). CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 12 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 12 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

UserDefaults для сохранения палитр palettes 

Давайте займемся нашими палитрами. 
Вы видите палитры прямо здесь?
Если я зайду в свои палитры и скажу New (новая), то добавляется математическая палитра  Math. Если я заново перезапущу приложения, то мы обнаружим, что  математическая палитра  Math исчезла. 

Приложение не помнит, что я добавил палитру  Math (Математика). 
Или, если я удалю что-то, например, Sports, ну, нам не нравятся виды спорта. 
Если мы вернемся и перезапустим наше приложение, то палитра Sports возвращается. 

Итак, мы хотим сделать так, чтобы все, что мы здесь делаем с палитрами, новые и удаленные палитры запоминались.
Мы запомним это в UserDefaults, главным образом потому, что я уже показал вам, как это сделать в файловой системе, теперь я хочу показать, как это делать в UserDefaults. Вероятно, как мы говорили ранее, это не совсем уместно делать это в UserDefaults. Но учитывая довольно маленькое количество данных, мы собираемся сделать это в UserDefaults.
Мы сделаем это очень крутым способом.
Видите мои @Published палитры palettes, которые находятся в моем PaletteStore?

Позвольте мне избавиться от этого кода и превратить palettes в вычисляемое свойство:

Читать далее

Лекция 11. Жесты, вторая MVVM. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 11 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 11 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Демо: масштабирование и перемещение по экрану EmojiArt

Мы хотим иметь возможность масштабировать и перемещать по экрану наш документ EmojiArtDocument с помощью движения пальцев.
Вот как это выглядит, это немного устаревшая версия приложения Emoji Art, но я собираюсь выполнить жест pinch. Кстати, когда вы используете такой симулятор, вы можете выполнить жест pinch, удерживая клавишу option. И видите, у меня сразу на симуляторе появляются тут два пальца в виде серых кружков. Удерживаю option — и они появляются. Можно также перемещать с помощью жеста drag документ по экрану.
Итак, я увеличиваю масштаб. Видите? Я выполняю жест pinch, чтобы увеличить масштаб. И, конечно, я могу уменьшить масштаб. Возможно, я захочу переместить мой документ и я выполняю жест drag.


Мы хотим перемещать документ с помощью жеста drag и изменять его масштаб с помощью жеста pinch, я собираюсь реализовать эти две вещи.
Что мы должны перетаскивать drag и масштабировать pinch.?
Содержимое нашего документа, это фоновое изображение и все эти эмодзи (смайлики). Все это увеличивается и перемещается, вы видите это представлено синим цветом:

Я собираюсь взять этот код и разместить его в отдельной переменной var с именем documentContents, и это то, что я собираюсь масштабировать и перемещать по экрану.

Читать далее

Лекция 10. EmojiArt. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 10 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 10 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Демо. “Сброс” Drop фонового изображения background

Давай сделаем “сброс”  (drop) URL-адреса фонового изображения background. Это действительно очень очень легко. 
Куда мы хотим это “сбросить”?
По сути, мы хотим “сбросить” его поверх нашего ZStack, это наш документ.
Итак, модификатор .dropDestination.
Что мы сбрасываем?
URL-адрес:

Обратите внимание на этот URL.self, когда вы пишите .self на нижнем регистре, то это означает сам ТИП. Таким образом, я передаю .dropDestination ТИП URL.self в качестве аргумента, так что мой документ знает, что мы ожидаем при “сбросе”. В замыкании у нас есть массив URL-адресов urls и место “сброса” location.
Для фонового изображения background нам неважно место “сброса” location, но это нас явно интересует при “сбросе” эмодзи (смайликов), и мы должны поработать с этим location.
Это замыкание должно вернуть информацию о том, был ли “сброс” успешным, потому что некоторые “сбросы” на этот View могут вас не устраивать. Тогда вам придется сказать:”НЕТ, лети обратно, туда, откуда пришел”.
Я собираюсь вернуть return функцию func drop (urls, at: location, in: geometry), которую я собираюсь написать через секунду:

Читать далее