revew and cert

This commit is contained in:
4 changed files with 116 additions and 0 deletions
+2
View File
@@ -6,3 +6,5 @@
- [Умный дом](smart-house/README.md) - [Умный дом](smart-house/README.md)
- [Умный дом WEB](smart-house-web/README.md) - [Умный дом WEB](smart-house-web/README.md)
- [Игра "Змейка"](snake-game/README.md) -- проектная работа - [Игра "Змейка"](snake-game/README.md) -- проектная работа
[Сертификат](https://otus.ru/certificate/aa2140d27fdd4561aa8890bfdc26eb87/) - файл `otus_cert.pdf`
BIN
View File
Binary file not shown.
+2
View File
@@ -2,6 +2,8 @@
Проектная работа по курсу **Rust Developer. Professional** образовательной платформы [OTUS](https://otus.ru/). Проектная работа по курсу **Rust Developer. Professional** образовательной платформы [OTUS](https://otus.ru/).
[Код-ревью проекта «Змейка»](REVIEW.md) от преподавателя Кирилла Федченко
## Запуск ## Запуск
Сразу после запуска игра будет на паузе. Чтобы начать игру нужно снять её с паузы клавишей "Пробел". Сразу после запуска игра будет на паузе. Чтобы начать игру нужно снять её с паузы клавишей "Пробел".
+112
View File
@@ -0,0 +1,112 @@
# Код-ревью проекта «Змейка»
## Общая оценка
Крепкая, аккуратно организованная работа. Автор хорошо владеет идиомами Bevy ECS:
разделение на модули по расписаниям (`startup` / `update` / `fixed_update` / `post_update`),
использование событий и обзёрверов, разнесение доменной модели в `domain/`. Логика движения
по сетке с разбиением шага на границе (`split_by_grid`) — нетривиальная и покрыта хорошим
юнит-тестом. Документирующие комментарии присутствуют почти везде.
Есть один реальный функциональный баг (таймер спавна еды) и несколько игровых/стилистических
замечаний.
---
## Критичные замечания
### 1. Таймер спавна еды тикается общим временем вместо дельты кадра
`src/fixed_update.rs:92`
```rust
if meal_timer.tick(time.elapsed()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize {
```
`Timer::tick` принимает **дельту** времени за кадр, а `time.elapsed()` возвращает **суммарное**
время с момента старта (постоянно растущее). В результате:
- Первые ~2 секунды таймер копит `elapsed`, затем на каждом fixed-шаге тикается на всё
накопленное время (2.0s, 2.03s, 2.06s …). Для `Repeating`-таймера это значит `just_finished()`
становится `true` практически **каждый кадр** после первых 2 секунд.
- Как следствие поле мгновенно заполняется до `MAX_MEALS_ON_FIELD` (5 штук), а после поедания
еды тут же добивается обратно — заявленной механики «одна еда раз в 2 секунды» нет.
**Исправление:**
```rust
if meal_timer.tick(time.delta()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize {
```
---
## Средние замечания
### 2. Еда может появиться прямо под змеёй
`src/fixed_update.rs:93-103`
`used_positions` собирается **только из других объектов еды** — позиции головы и сегментов хвоста
не учитываются. Поэтому еда легко спавнится под телом змеи, что портит игровой процесс
(съедается «бесплатно» либо появляется в недосягаемом месте). Стоит добавить в исключаемые
позиции клетки, занятые `Head`/`Tail`.
### 3. Столкновение с границей триггерится каждый кадр
`src/post_update.rs:6-16`
После выхода за границу скорость обнуляется, но координаты головы остаются за пределами поля,
поэтому `Collision::Bounds` генерируется на **каждом** `PostUpdate`, и `handle_collisions`
бесконечно переустанавливает скорость/цвет. У столкновения с хвостом такой проблемы нет
(там есть отсечка `velocity.0 == 0.0`). Логичнее ввести явное состояние «игра окончена»
(например, компонент-маркер `GameOver` или `State`), которое остановит проверки и позволит
сделать рестарт. Сейчас после проигрыша игру можно только перезапустить процессом.
---
## Мелкие замечания и стиль
### 4. Диапазон случайной позиции еды меньше поля и смещён
`src/fixed_update.rs:96-99`
```rust
const ABS: i8 = (FIELD_SIZE as f32 / 2.0) as i8; // = 10
let rand_x = rng.random::<i8>() % ABS; // диапазон -9..=9
```
При `FIELD_SIZE = 20` поле по логике занимает клетки `-10..=10`, но еда никогда не появляется
в крайних клетках `±10`. Также остаток от деления даёт чуть неравномерное распределение и `0`
встречается вдвое чаще краёв. Для равномерного распределения удобнее
`rng.random_range(-ABS..=ABS)`.
### 5. Неограниченное ускорение/замедление времени
`src/update.rs:30-40`
`Equal`/`Minus` умножают `relative_speed` без верхней/нижней границы — можно довести скорость до
неиграбельных значений (или почти до нуля). Стоит ограничить, например, диапазоном `[0.125, 8.0]`.
### 6. Опечатки в именах (для читаемости кода)
- `check_bounds_colisions`, `check_tail_colisions``collisions` (`src/post_update.rs`, `src/game.rs`)
- `GridBarier``GridBarrier` (`src/domain/events.rs` и все использования)
- `splited``split` (`src/fixed_update.rs:23`)
Не влияет на работу, но публичные имена типов/систем лучше писать без ошибок.
### 8. Учебный «мусор» в репозитории поставки
- `tests/rust_learning.rs` — это личные эксперименты (`dbg!` по `saturating_sub`, `ceil`,
`floor` и т.п.), а не тесты проекта. В сдаваемой работе их лучше убрать или вынести за пределы
`tests/`.
- `examples/` содержит туториалы по Bevy (`bevy_learning.rs`, `paddle_demo.rs` …), не относящиеся
к игре. Для учебного проекта допустимо, но стоит хотя бы упомянуть их статус в README.
---
## Что сделано хорошо
- **Чистая модульная структура**: доменная модель отделена от систем, системы сгруппированы
по расписаниям Bevy.
- **Идиоматичный ECS**: события (`Collision`, `GrowUp`, `GridBarier`), обзёрверы, `Single`-запросы,
ресурсы (`SnakePath`, `MealRespawnTimer`).
- **Аккуратная механика поворотов**: змея не может развернуться в себя (только `rotate_left`/
`rotate_right`), а первый сегмент за головой исключён из проверки столкновений — грамотное
решение классических проблем «Змейки».
- **`split_by_grid`** — сложная логика движения через узлы сетки покрыта развёрнутым
юнит-тестом со множеством сценариев.
- Настроен `rustfmt.toml`, продуманный релизный профиль (`opt-level="z"`, `lto`, `strip`),
ведётся логирование в файл и stdout.