Сидоров Георгий Александрович - лекции, семинары
Почта: georgiy.sidorov@gmail.com
Telegram: @georgiysidorov80
Гамарник Андрей Янович - семинары
Почта: mvcdev@ya.ru
Telegram: @mvcdev
Анисимов Кирилл Александрович - семинары
Почта: anisimov_kirill@mail.ru
Telegram: @ankirsansan
@sharp_nsu_2026
Студент учится в университете. Каждый день шесть пар: Матанализ, Линейная алгебра, Дискретная математика, Программирование, Физика, Английский язык. Семестр длится 100 учебных дней.
Что происходит на паре:
На каждой паре преподаватель может спросить студента или не спросить. У каждого преподавателя — одно из трёх правил:
Спросить случайно с вероятностью 50%.
Спросить, если вчера спросили по предмету A иначе не спрашивать
Спросить, если вчера спросили по предмету A и не спросили по предмету Б либо не спросили по предмету A и спросили по предмету Б, иначе не спрашивать.
Предметы A и Б для правил 2 и 3 у каждого преподавателя свои, выбираются случайно в начле семестра и могут совпадать с его собственным предметом, но не могут совпадать друг с другом. В первый день семестра считается, что вчера никого не спрашивали.
У студента в жизни две радости: прогулять пару или съесть пирожок. Удовольствие со временем копится.
Если студент не пришёл на пару и его спросили, он вылетает — всё удовольствие, накопленное за семестр (и от прогулов, и от пирожков), обнуляется.
Кроме того, каждый день студент также съедает пирожок — удовольствие от него равно удовольствию от одного пропущенного занятия.
Цель: максимизировать удовольствие (пирожки + прпущенные пары) за 100 дней, не вылетев.
1. Консольное приложение
Прогоняет один семестр (100 дней) для одной стратегии студента. В конце работы печатает: общее удовольствие за семестр и среднее удовольствие в день.
2. Generic Host
Крутится бесконечно, каждые 5 секунд — новый день. После каждой итерации выводит общее удовольствие среднее адовольстиве в день. При вылете необходима остановка программы, также нужна возсожность преравть выполнение нажатиеем клавиши
На этом этапе нужно выделить значимые элементы логики в отдельные сервисы и подклбчить их через DI.
3. Тесты (unit-тесты)
Необходимо раработать тест, NUnit или XUnit/ Весь рандом подменяется управляемым источником (тесты детерминированы).
Правила — перподаватель ведет себя в соотвествии с выбранным правиломпоследовательности).
Стратегия студента — при заданной истории наблюдений и дне стратегия принимает то решение, которое должна (пустая история, стабильно не спрашивали, стабильно спрашивали, смешанная история — предсказуемость; независимость решений по каждому из 6 предметов).
Поведение эмулятора — при заданных правилах и решении студента: все 4 комбинации присутствие/вопрос дают верный исход; состояние «вчера» корректно обновляется; при вылете — обнуление и остановка, дальнейшие дни не обрабатываются.
4. База данных (PostgreSQL)
Каждый день симуляции сохраняется в БД: прогресс студента (накопленное удовольствие, дни, вылетел/нет) и все параметры сгенерированных правил преподавателей на эту сессию. Приложение поддерживает два режима запуска: начать новый семестр или продолжить сохранённый (незавершённый) с того дня, на котором остановились.
Дополнительно — интеграционные тесты на корректность сохранения/восстановления состояния: сохранённое на дне d состояние восстанавливается идентичным, продолжение после восстановления даёт тот же результат, что непрерывный прогон.
5. Микросервисы по HTTP
Обязательные сервисы:
Сервис Истории — хранит, кого и когда спросили; отдаёт ответ на "что было вчера по предмету X".
Сервис Преподавателя — по одному на предмет (6 штук), у каждого своё скрытое правило; принимает запрос от студента и отвечает, спросили или нет.
Студент — консольное приложение-клиент, каждый день по каждому предмету решает идти/прогулять и вызывает соответствующего Преподавателя.
6. Очереди
Студент по-прежнему вызывает Преподавателя по HTTP (иду/прогуливаю → результат сразу). Запись в Сервис Истории Преподаватель делает не напрямую, а кладёт результат («предмет, день, спросили или нет») в очередь — оттуда его асинхронно забирает Сервис Истории. Обратное чтение («что было вчера по предмету X») у Преподавателя остаётся синхронным вызовом к Сервису Истории — но раз запись идёт асинхронно через очередь, нужен механизм, гарантирующий, что к моменту такого чтения данные за вчерашний день в Сервисе Истории уже точно есть.
Стратегия присылается как отдельный C#-проект (.NET 10, актуальная LTS), реализующий интерфейс:
Результат будет вычисляться как срднее по 1000 ирераций. Производительность решение полжна обсеспечивать прого 1000 итераций в течение <10 мин.
csharp
public enum Subject
{
Calculus, // Матанализ
LinearAlgebra, // Линейная алгебра
DiscreteMath, // Дискретная математика
Programming, // Программирование
Physics, // Физика
English // Английский язык
}
public interface ISkipStrategy
{
string Name { get; }
/// <summary>
/// Решение на день: для каждого предмета — идти на пару (true) или прогулять (false).
/// Вызывается один раз в начале каждого дня, до того как станут известны сегодняшние исходы.
/// </summary>
bool[] DecideDay(int day, IReadOnlyStudentHistory history);
}
public interface IReadOnlyStudentHistory
{
/// <summary>Был ли студент на паре по предмету в указанный день.</summary>
bool Attended(int day, Subject subject);
/// <summary>
/// Спросили ли по предмету в указанный день — известно только если Attended(day, subject) == true.
/// Если информации еще нет то null
/// </summary>
bool? WasAsked(int day, Subject subject);
}
Формат сдачи: отдельный C#-проект (.csproj, .NET 10), один класс, реализующий ISkipStrategy, без внешних NuGet-зависимостей — чтобы все присланные решения собирались и запускались общим раннером одинаково. Таймаут на выполнение DecideDay не устанавливается.