0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Всем привет! Хочу поделиться опытом участия в олимпиаде по веб-разработке на PHP "Волга-IT XXI" и рассказать, какую пользу такие мероприятия могут принести начинающим программистам.</p>
1
<p>Всем привет! Хочу поделиться опытом участия в олимпиаде по веб-разработке на PHP "Волга-IT XXI" и рассказать, какую пользу такие мероприятия могут принести начинающим программистам.</p>
2
<h2>Содержание</h2>
2
<h2>Содержание</h2>
3
<ul><li><a>Кратко о формате олимпиады</a></li>
3
<ul><li><a>Кратко о формате олимпиады</a></li>
4
<li><a>Проектирование</a></li>
4
<li><a>Проектирование</a></li>
5
<li><a>Реализация</a></li>
5
<li><a>Реализация</a></li>
6
<li><a>Вывод</a></li>
6
<li><a>Вывод</a></li>
7
</ul><h2>Кратко о формате олимпиады</h2>
7
</ul><h2>Кратко о формате олимпиады</h2>
8
<p>Олимпиада состояла из двух этапов: дистанционного отборочного тура и очного финала, который проходил в Ульяновскому техническом университете. Кроме PHP, проводились соревнования по прикладному программированию на Java и C#, системному программированию на С++ и веб-дизайну. Задачи были максимально приближены к производственным: их составлением занимались программисты и дизайнеры из нескольких IT-компаний.</p>
8
<p>Олимпиада состояла из двух этапов: дистанционного отборочного тура и очного финала, который проходил в Ульяновскому техническом университете. Кроме PHP, проводились соревнования по прикладному программированию на Java и C#, системному программированию на С++ и веб-дизайну. Задачи были максимально приближены к производственным: их составлением занимались программисты и дизайнеры из нескольких IT-компаний.</p>
9
<p>В отборочном туре участникам нужно было настроить сервер для игры Filler (или 7 colors) - аркады, выпущенной в 80-х годах. На сервере нужно было через фреймворк Laravel реализовать API для создания новой игры, отправки хода и получения текущего состояния. За дополнительные баллы можно было написать тесты, фронтенд и настроить окружение через Docker. Весь текст задания можно посмотреть здесь.</p>
9
<p>В отборочном туре участникам нужно было настроить сервер для игры Filler (или 7 colors) - аркады, выпущенной в 80-х годах. На сервере нужно было через фреймворк Laravel реализовать API для создания новой игры, отправки хода и получения текущего состояния. За дополнительные баллы можно было написать тесты, фронтенд и настроить окружение через Docker. Весь текст задания можно посмотреть здесь.</p>
10
<p>В финале нужно было доработать игру - создать искусственный интеллект, способный анализировать текущее состояние игры и отправлять на сервер информацию о самом удачном ходе в каждый момент времени. На финальный этап давалось восемь часов - исправления, внесенные в код по истечении этого времени, не принимались.</p>
10
<p>В финале нужно было доработать игру - создать искусственный интеллект, способный анализировать текущее состояние игры и отправлять на сервер информацию о самом удачном ходе в каждый момент времени. На финальный этап давалось восемь часов - исправления, внесенные в код по истечении этого времени, не принимались.</p>
11
<h2>Проектирование</h2>
11
<h2>Проектирование</h2>
12
<p>Самой сложной задачей на отборочном этапе было проектирование игры - нужно было вникнуть в игровую логику и понять, каким способом реализовать хранение данных. У меня было два варианта:</p>
12
<p>Самой сложной задачей на отборочном этапе было проектирование игры - нужно было вникнуть в игровую логику и понять, каким способом реализовать хранение данных. У меня было два варианта:</p>
13
<p>Сделать одну сущность Game со столбцом game_data, который будет содержать в себе данные о поле, игроках и клетках в формате JSON. Не нарушать принцип единой ответственности и сделать четкое разделение по сущностям: Game, User, Field, Cell.</p>
13
<p>Сделать одну сущность Game со столбцом game_data, который будет содержать в себе данные о поле, игроках и клетках в формате JSON. Не нарушать принцип единой ответственности и сделать четкое разделение по сущностям: Game, User, Field, Cell.</p>
14
<p>Первый способ показался мне неправильным: данные при его реализации были бы похожи на один большой ком. С одной стороны, размеры игры по условиями могли быть 99x99 и не факт, что данные уместились бы в базе. С другой, логика операций чтения и записи была бы не понятной. В итоге я остановился на втором варианте с разделением сущностей.</p>
14
<p>Первый способ показался мне неправильным: данные при его реализации были бы похожи на один большой ком. С одной стороны, размеры игры по условиями могли быть 99x99 и не факт, что данные уместились бы в базе. С другой, логика операций чтения и записи была бы не понятной. В итоге я остановился на втором варианте с разделением сущностей.</p>
15
<p>При реализации логики я поместил весь функционал в класс App/Models/Field. Это было не лучшим решением: оно нарушается принцип единой ответственности, поскольку модель отвечает за работу с БД, описание сущности и логику приложения. Правильнее было бы разделить модель App/Models/Field на три части: описание сущности оставить там же, логику работы с БД вынести в App/Repositories/FieldRepository, а игровую логику вынести в App/Services/GameService.</p>
15
<p>При реализации логики я поместил весь функционал в класс App/Models/Field. Это было не лучшим решением: оно нарушается принцип единой ответственности, поскольку модель отвечает за работу с БД, описание сущности и логику приложения. Правильнее было бы разделить модель App/Models/Field на три части: описание сущности оставить там же, логику работы с БД вынести в App/Repositories/FieldRepository, а игровую логику вынести в App/Services/GameService.</p>
16
<p>На следующем этапе нужно было определить механизм захвата клеток. Здесь тоже было два варианта:</p>
16
<p>На следующем этапе нужно было определить механизм захвата клеток. Здесь тоже было два варианта:</p>
17
<ol><li>Захватываются только соседние клетки;</li>
17
<ol><li>Захватываются только соседние клетки;</li>
18
<li>Захватываются все клетки, до которых противник не сможет дотянуться - то есть области, которые полностью перекрыты по периметру нашими клетками.</li>
18
<li>Захватываются все клетки, до которых противник не сможет дотянуться - то есть области, которые полностью перекрыты по периметру нашими клетками.</li>
19
</ol><p>Я выбрал первый вариант, поскольку реализовать его значительно проще и он более распространен.</p>
19
</ol><p>Я выбрал первый вариант, поскольку реализовать его значительно проще и он более распространен.</p>
20
<h2>Реализация</h2>
20
<h2>Реализация</h2>
21
<p>Прежде, чем начать описывать реализацию логики игры, расскажу, как она устроена. Filler проходит на прямоугольном поле, состоящем из ромбических клеток разных цветов, раскрашенных случайным образом. Каждый игрок начинает игру в левом нижнем или правом верхнем углу поля.</p>
21
<p>Прежде, чем начать описывать реализацию логики игры, расскажу, как она устроена. Filler проходит на прямоугольном поле, состоящем из ромбических клеток разных цветов, раскрашенных случайным образом. Каждый игрок начинает игру в левом нижнем или правом верхнем углу поля.</p>
22
<p>Ход в игре производится путем выбора цвета, в который перекрашиваются все текущие клетки игрока. При этом все клетки выбранного цвета, которые находятся по соседству с клетками игрока переходят к нему в собственность.</p>
22
<p>Ход в игре производится путем выбора цвета, в который перекрашиваются все текущие клетки игрока. При этом все клетки выбранного цвета, которые находятся по соседству с клетками игрока переходят к нему в собственность.</p>
23
<p>Сначала расскажу о реализации игрового поля. В цикле определяется индекс клетки, который отвечает за ее координаты на игровом поле. В этом же цикле создаются экземпляры клеток, которые объединяются в массив для вставки в базу данных. Важно, что речь идет об одном запросе на вставку с множеством строк, а не о множестве отдельных запросов на вставку в цикле. Это сильно влияет на производительность, особенно в условиях больших размеров поля.</p>
23
<p>Сначала расскажу о реализации игрового поля. В цикле определяется индекс клетки, который отвечает за ее координаты на игровом поле. В этом же цикле создаются экземпляры клеток, которые объединяются в массив для вставки в базу данных. Важно, что речь идет об одном запросе на вставку с множеством строк, а не о множестве отдельных запросов на вставку в цикле. Это сильно влияет на производительность, особенно в условиях больших размеров поля.</p>
24
<p>Мой вариант реализации выглядит так:</p>
24
<p>Мой вариант реализации выглядит так:</p>
25
<p>Реализация хода построена на рекурсии и выглядит так:</p>
25
<p>Реализация хода построена на рекурсии и выглядит так:</p>
26
<p>Функция получения соседних клеток выглядит так:</p>
26
<p>Функция получения соседних клеток выглядит так:</p>
27
<h2>Вывод</h2>
27
<h2>Вывод</h2>
28
<p>Что дало мне участие в олимпиаде?</p>
28
<p>Что дало мне участие в олимпиаде?</p>
29
<ul><li>Я прокачал свои знания в Laravel, который изучал до этого несколько месяцев;</li>
29
<ul><li>Я прокачал свои знания в Laravel, который изучал до этого несколько месяцев;</li>
30
<li>Сделал хороший кейс для портфолио, который смело можно указывать в резюме;</li>
30
<li>Сделал хороший кейс для портфолио, который смело можно указывать в резюме;</li>
31
<li>Получил опыт написания юнит- и интеграционных тестов в Laravel;</li>
31
<li>Получил опыт написания юнит- и интеграционных тестов в Laravel;</li>
32
<li>Получил опыт реализации фронтенд-части приложения и улучшил знания в JS. Между этапами я неоднократно возвращался к своей реализации и всячески пытался оптимизировать ее, в результате чего я на порядок увеличил скорость работы приложения.</li>
32
<li>Получил опыт реализации фронтенд-части приложения и улучшил знания в JS. Между этапами я неоднократно возвращался к своей реализации и всячески пытался оптимизировать ее, в результате чего я на порядок увеличил скорость работы приложения.</li>
33
</ul>
33
</ul>