Без лишних предисловий - давайте сделаем два абсолютно одинаковых запроса для создания приложения на SwiftUI и сравним, какая модель лучше справится с этими задачами.
Я решила дать два задания. Начнём с первого: нужно написать игру "Змейка", вот мой промпт:
Напиши полностью с нуля приложение на SwiftUI. В приложении должен быть один экран. Сделай простейший вариант игры змейка. Изначально зеленая змея из 3-х кваратиков, по мере съедания квадратика она увеличивается на 1 квадрат (квадратики которые она съедает - разных цветов)
Если лень читать - в конце ссылка на короткое видео сравнения (в телеграм канале).
Посмотрим, что нам выдал ChatGPT:
Ответ ChatGPT
Вот готовый код для простейшей игры «Змейка» на SwiftUI. Змея стартует с длиной в 3 квадрата и растёт при поедании еды разного цвета. Управление происходит свайпами.
import SwiftUI
struct SnakeGameView: View {
@State private var snake: [CGPoint] = [CGPoint(x: 100, y: 100), CGPoint(x: 80, y: 100), CGPoint(x: 60, y: 100)]
@State private var food: CGPoint = CGPoint(x: 200, y: 200)
@State private var direction: CGPoint = CGPoint(x: 20, y: 0)
@State private var timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
let gridSize: CGFloat = 20
let colors: [Color] = [.red, .blue, .yellow, .purple, .orange]
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
ForEach(snake.indices, id: \..self) { index in
Rectangle()
.fill(Color.green)
.frame(width: gridSize, height: gridSize)
.position(snake[index])
}
Rectangle()
.fill(colors.randomElement()!)
.frame(width: gridSize, height: gridSize)
.position(food)
}
.gesture(DragGesture()
.onEnded { gesture in
let xMove = gesture.translation.width
let yMove = gesture.translation.height
if abs(xMove) > abs(yMove) {
direction = xMove > 0 ? CGPoint(x: gridSize, y: 0) : CGPoint(x: -gridSize, y: 0)
} else {
direction = yMove > 0 ? CGPoint(x: 0, y: gridSize) : CGPoint(x: 0, y: -gridSize)
}
}
)
.onReceive(timer) { _ in
moveSnake()
}
}
private func moveSnake() {
var newHead = snake.first!
newHead.x += direction.x
newHead.y += direction.y
if newHead == food {
snake.insert(newHead, at: 0)
food = generateNewFood()
} else {
snake.insert(newHead, at: 0)
snake.removeLast()
}
}
private func generateNewFood() -> CGPoint {
let x = CGFloat(Int.random(in: 0..<Int(UIScreen.main.bounds.width / gridSize))) * gridSize
let y = CGFloat(Int.random(in: 0..<Int(UIScreen.main.bounds.height / gridSize))) * gridSize
return CGPoint(x: x, y: y)
}
}
struct ContentView: View {
var body: some View {
SnakeGameView()
}
}
@main
struct SnakeGameApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Этот код создаёт простейшую змейку, которая управляется свайпами. Когда змея съедает еду, она увеличивается в длине, а еда появляется в новом случайном месте и меняет цвет. Игра бесконечна, но можно добавить столкновение с границами или самой собой для усложнения. ?
И вот что мы получили:
В целом, всё максимально соответствует описанию, код работает с первого раза. Есть недочеты, что змейка может уйти за пределы экрана, например. Но для базы сойдёт.
Дальше такой же промпт вставляем в DeepSeek, вот что получаем:
Ответ DeepSeek
Создадим простейшую версию игры "Змейка" на SwiftUI. В этом приложении будет один экран, на котором змейка будет двигаться по экрану и "съедать" квадратики, увеличиваясь в длину.
Шаги для создания приложения:
Создадим структуру для змейки и еды.
Реализуем логику движения змейки.
Добавим обработку касаний для управления змейкой.
Отрисуем змейку и еду на экране.
Полный код приложения:
import SwiftUI
struct ContentView: View {
@State private var snake: [CGPoint] = [
CGPoint(x: 100, y: 100),
CGPoint(x: 90, y: 100),
CGPoint(x: 80, y: 100)
]
@State private var food: CGPoint = CGPoint(x: 200, y: 200)
@State private var direction: CGPoint = CGPoint(x: 10, y: 0)
@State private var foodColor: Color = .red
@State private var score: Int = 0
let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
// Рисуем змейку
ForEach(snake, id: \.self) { segment in
Rectangle()
.fill(Color.green)
.frame(width: 10, height: 10)
.position(segment)
}
// Рисуем еду
Rectangle()
.fill(foodColor)
.frame(width: 10, height: 10)
.position(food)
// Отображаем счет
Text("Score: \(score)")
.foregroundColor(.white)
.font(.largeTitle)
.padding()
}
.onReceive(timer) { _ in
moveSnake()
}
.gesture(
DragGesture()
.onEnded { gesture in
let delta = gesture.translation
if abs(delta.width) > abs(delta.height) {
// Горизонтальный свайп
if delta.width > 0 {
direction = CGPoint(x: 10, y: 0) // Вправо
} else {
direction = CGPoint(x: -10, y: 0) // Влево
}
} else {
// Вертикальный свайп
if delta.height > 0 {
direction = CGPoint(x: 0, y: 10) // Вниз
} else {
direction = CGPoint(x: 0, y: -10) // Вверх
}
}
}
)
}
func moveSnake() {
var newSnake = snake
let head = newSnake[0]
// Новое положение головы
let newHead = CGPoint(x: head.x + direction.x, y: head.y + direction.y)
// Проверяем, съела ли змейка еду
if abs(newHead.x - food.x) < 10 && abs(newHead.y - food.y) < 10 {
score += 1
generateFood()
} else {
newSnake.removeLast()
}
newSnake.insert(newHead, at: 0)
snake = newSnake
}
func generateFood() {
let maxX = UIScreen.main.bounds.width - 10
let maxY = UIScreen.main.bounds.height - 10
food = CGPoint(
x: CGFloat.random(in: 0...maxX),
y: CGFloat.random(in: 0...maxY)
)
foodColor = Color(
red: Double.random(in: 0...1),
green: Double.random(in: 0...1),
blue: Double.random(in: 0...1)
)
}
}
@main
struct SnakeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Описание кода:
-
Змейка и еда:
Змейка представлена массивом
CGPoint
, где каждый элемент — это координата одного сегмента змейки.Еда — это случайная точка на экране, которая меняет цвет при каждом появлении.
-
Движение змейки:
Змейка движется с постоянной скоростью, обновляя свое положение каждые 0.1 секунды.
Если змейка "съедает" еду, ее длина увеличивается на один сегмент, и генерируется новая еда.
-
Управление:
Если пользователь свайпает вправо, змейка поворачивает вправо.
Если пользователь свайпает влево, змейка поворачивает влево.
Аналогично для свайпов вверх и вниз.
-
Отрисовка:
Змейка и еда отрисовываются с помощью
Rectangle
в SwiftUI.
Как запустить:
Создайте новый проект в Xcode с использованием SwiftUI.
Замените содержимое файла
ContentView.swift
на приведенный выше код.Запустите приложение на симуляторе или устройстве.
Теперь у вас есть простая версия игры "Змейка" на SwiftUI!
Что тут интересного: змейка по размерам сильно меньше, скорость чуть выше. При изначально одинаковом запросе, DeepSeek добавил от себя лейбл с подсчётом очков. Так же проблема с тем, что змейка уходит за рамки экрана и не возвращается.
И кстати, я нигде не указывала, каким должно быть управление и по умолчанию обе модели сделали управление по свайпу (это логично, но с другой стороны, вариантов много: стрелочки, гироскоп и пр.)
Считаю что с первым заданием обе модели справились. Счёт 1:1.
Далее в преддверии 14 февраля в голову ударили сердечки, поэтому попросила сделать обе модели простенькую анимацию. Вот такой был промпт:
Напиши полностью с нуля приложение на SwiftUI. В приложении должен быть один экран. На нём на тёмном фоне должна быть такая анимация:
Сердечки плавно падают сверху экрана, но при клике по экрану они разлетаются в стороны. Выглядит ярко и визуально привлекательно.
Сердечки должны быть разных оттенков розового и разного размера.
Начнём с ChatGPT. Вот результат:
Ответ ChatGPT
Вот полный код приложения на SwiftUI, которое реализует эффект падающих сердечек с разлетом при клике.
Что делает этот код?
• Сердечки падают сверху экрана с разной скоростью и размером.
• Клик по экрану заставляет сердечки разлететься в стороны.
• Разные оттенки розового и размеры сердечек делают анимацию визуально привлекательной.
import SwiftUI
struct Heart: Identifiable {
let id = UUID()
var position: CGPoint
var size: CGFloat
var color: Color
var velocity: CGFloat
}
struct ContentView: View {
@State private var hearts: [Heart] = []
@State private var explode = false
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
ForEach(hearts) { heart in
HeartShape()
.fill(heart.color)
.frame(width: heart.size, height: heart.size)
.position(heart.position)
.opacity(explode ? 0.5 : 1)
.animation(.easeInOut(duration: 1.5), value: explode)
}
}
.onAppear {
startDroppingHearts()
}
.onTapGesture {
explodeHearts()
}
}
func startDroppingHearts() {
Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { timer in
let newHeart = Heart(
position: CGPoint(x: CGFloat.random(in: 0...screenWidth), y: -50),
size: CGFloat.random(in: 20...50),
color: Color(red: Double.random(in: 0.8...1), green: Double.random(in: 0...0.3), blue: Double.random(in: 0.5...0.7)),
velocity: CGFloat.random(in: 2...5)
)
hearts.append(newHeart)
withAnimation(.linear(duration: 3)) {
for index in hearts.indices {
hearts[index].position.y += screenHeight + 50
}
}
hearts.removeAll { $0.position.y > screenHeight + 50 }
}
}
func explodeHearts() {
explode = true
withAnimation(.easeOut(duration: 0.5)) {
for index in hearts.indices {
hearts[index].position.x += CGFloat.random(in: -100...100)
hearts[index].position.y += CGFloat.random(in: -100...100)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
explode = false
}
}
}
struct HeartShape: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let width = rect.width
let height = rect.height
path.move(to: CGPoint(x: width / 2, y: height))
path.addCurve(to: CGPoint(x: 0, y: height / 3),
control1: CGPoint(x: width * 0.3, y: height * 1.1),
control2: CGPoint(x: 0, y: height * 0.6))
path.addArc(center: CGPoint(x: width * 0.25, y: height / 3),
radius: width * 0.25,
startAngle: .degrees(180),
endAngle: .degrees(0),
clockwise: false)
path.addArc(center: CGPoint(x: width * 0.75, y: height / 3),
radius: width * 0.25,
startAngle: .degrees(180),
endAngle: .degrees(0),
clockwise: false)
path.addCurve(to: CGPoint(x: width / 2, y: height),
control1: CGPoint(x: width, y: height * 0.6),
control2: CGPoint(x: width * 0.7, y: height * 1.1))
return path
}
}
@main
struct HeartsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Как это работает?
• Падающие сердечки создаются каждые 0.3 секунды и плавно движутся вниз.
• Сердечки разного размера и оттенка розового.
• При клике по экрану сердечки разлетаются в случайном направлении.
• Анимации делают разлет плавным и красивым.
Попробуй запустить этот код в Xcode, и ты увидишь магию ?✨!
Получилась какая-то хрень. Но я решила дать ему ещё один шанс и попросила исправить, чтобы сердечки падали на весь экран.
Со второго раза:
Нуууу, почти. Сердечки падают, они разного цвета и размера. При нажатии происходит "улёт" вниз и дальше опять глюк, что они только в первой четверти экрана появляются.
В целом ок, с учётом того, что код я не смотрела вообще.
Теперь посмотрим ответ от DeepSeek
Ответ DeepSeek
Создадим приложение на SwiftUI с анимацией падающих сердечек, которые разлетаются при нажатии на экран. Вот полный код:
import SwiftUI
struct Heart: Identifiable {
let id = UUID()
var position: CGPoint
var color: Color
var size: CGFloat
var isFlying: Bool = false
}
struct ContentView: View {
@State private var hearts: [Heart] = []
@State private var timer: Timer?
var body: some View {
ZStack {
Color.black
.edgesIgnoringSafeArea(.all)
ForEach(hearts) { heart in
Image(systemName: "heart.fill")
.font(.system(size: heart.size))
.foregroundColor(heart.color)
.position(heart.position)
.animation(.linear(duration: heart.isFlying ? 0.5 : 10), value: heart.position)
}
}
.onAppear {
startHeartsAnimation()
}
.onDisappear {
timer?.invalidate()
}
.onTapGesture {
explodeHearts()
}
}
private func startHeartsAnimation() {
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
let size = CGFloat.random(in: 20...50)
let heart = Heart(
position: CGPoint(x: CGFloat.random(in: 0...UIScreen.main.bounds.width), y: -size),
color: Color(red: Double.random(in: 0.7...1), green: Double.random(in: 0.2...0.5), blue: Double.random(in: 0.4...0.7)),
size: size
)
hearts.append(heart)
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
if let index = hearts.firstIndex(where: { $0.id == heart.id }) {
hearts.remove(at: index)
}
}
withAnimation(.linear(duration: 10)) {
if let index = hearts.firstIndex(where: { $0.id == heart.id }) {
hearts[index].position.y = UIScreen.main.bounds.height + size
}
}
}
}
private func explodeHearts() {
for index in hearts.indices {
let angle = Double.random(in: 0..<2 * .pi)
let distance = CGFloat.random(in: 100...300)
let newX = hearts[index].position.x + cos(angle) * distance
let newY = hearts[index].position.y + sin(angle) * distance
withAnimation(.easeOut(duration: 0.5)) {
hearts[index].position = CGPoint(x: newX, y: newY)
hearts[index].isFlying = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if let index = hearts.firstIndex(where: { $0.id == hearts[index].id }) {
hearts.remove(at: index)
}
}
}
}
}
@main
struct HeartAnimationApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
DeepSeek с первого раза выдал довольно пристойную реализацию. Максимально соответствует запросу.
Единственный минус, что после того как я нажимаю на экран и сердечки разлетаются - превью крашится.
Я считаю, что во второй задаче победил DeepSeek. К моему удивлению, я была скептически настроена, но посмотрев и сравнив их на практике - кажется, что DeepSeek вообще ничем не уступает ChatGPT, по крайней мере в написании кода. И ещё надо учитывать то, что DeepSeek работает бесплатно и без ВПН и для регистрации мне не потребовалось ничего, кроме почты.
Кто ещё тестировал на других языках, пишите как вам?
Комментарии (8)
Alex-Freeman
30.01.2025 17:11Какой ChatGPT использовали? GPT-4o, о1 или o1 pro? А то, если сравнивать с GPT-4o то можно и не сравнивать, уровень совершенно разный
kosyakus Автор
30.01.2025 17:11Действительно, даже не подумала, что по-хорошему надо было написать это в статье. Чат GPT-4o и Deepseek-V3.
griha_shershen
30.01.2025 17:11жду сравнения o1 и r1, если к первому нет доступа, могу от своего имени закинуть
kujoro
30.01.2025 17:11у меня наоборот получилось, пользоваться им невозможно, пишет вообще какую-то ерунду, ну только если супер простенькое что-то спрашивать
по-моему qwen сейчас даже лучше дипсика, и пользоваться тоже можно без впн, хотя тоже ерунду выдает, даже если с 4о сравнивать
мой вопрос
Мне нужна таблица с навигацией по ячейкам написанная на go, bubble tea и таблицей из пакета "github.com/evertras/bubble-table/table"
Viktor9354
Интересный результат. Пробовал такие тесты устраивать по вопросам Frontend разработки и хочу сказать, что в большинстве кейсов ChatGPT побеждает. В редких случаях, по отдельным темам наоборот, DeepSeek выдаёт более удовлетворительный ответ. Но и у вас и у меня всё-таки несистемный подход и по-хорошему необходимо тестировать на большем количестве запросов разных форматов. Чисто по ощущениям после недели пользования, DeepSeek по IQ больше походит на Gemini, нежели на ChatGPT.