Варианты заданий (1114804), страница 7
Текст из файла (страница 7)
Кроме того, следует описать структуру “игрок”, которая будет содержать данные, связанные с одним игроком.Переменные типа “игрок” можно объединить в массив либо в односвязный список. Скорее всего, более сложные структуры данных нам здесь непотребуются.Можно сделать список игроков частью структуры “банкир”, в результате чего вся игровая информация окажется доступной через однуединственную переменную.Если теперь все функции, осуществляющие операции над игровымиданными, снабдить параметром “указатель на структуру банкир”, потребность в глобальных переменных полностью отпадет и можно будет описатьединственный экземпляр структуры “банкир” как локальную переменнуюфункции main().3.4О таблице смены уровней рынкаПри реализации смены состояния рынка в соответствии с таблицей 2(см.
стр. 6) студенты часто пользуются вложенными конструкциями34switch/case. Такая реализация требует существенных затрат времени исил, а итоговый код получается громоздким, неудобным для восприятия ичасто содержит ошибки.Гораздо проще реализовать выбор нового уровня рынка через двумерную матрицу 5 × 5, в которой номер строки представляет текущее значениеуровня рынка, номер столбца – новое значение, а соответствующая клетка таблицы определяет вероятность соответствующего перехода (именнотаким образом организована информация в таблице 2).Чтобы избежать использования приближенных вычислений, можно всезначения вероятности, приведённые в таблице, домножить на 12.
Получимследующую матрицу:const int level_change[5][5] = {{ 4, 4, 2, 1, 1 },{ 3, 4, 3, 1, 1 },{ 1, 3, 4, 3, 1 },{ 1, 1, 3, 4, 3 },{ 1, 1, 2, 4, 4 }};Получив с помощью функции rand() случайное число r от 1 до 12 включительно, делаем цикл по соответствующей строке матрицы, суммируя элементы, до тех пор, пока сумма не окажется больше либо равна r. Например,если текущий уровень – 2 и выпало число 8, то новый уровень будет 3, поскольку 3 + 4 = 7 < 8, а 3 + 4 + 3 = 10 >= 8. Будьте внимательны синдексацией: если уровни рынка нумеруются с 1 до 5, то соответствующиеим индексы в матрице будут на единицу меньше.Случайное число r от 1 до 12 проще всего получить с помощью выражения rand()%12+1, но так делать не рекомендуется, поскольку распределение младших бит псевдослучайных чисел может быть неравномерным.В книге [6] рекомендуется использовать в подобных ситуациях следующий оператор:r = 1 + (int)(12.0*rand()/(RAND_MAX+1.0));3.5О проведении аукционовРеализация проведения аукционов является одним из наиболее алгоритмически сложных фрагментов программы.
Наибольшее количество ошибоксвязано с обработкой ситуации, при которой в игре имеется несколько заявок с одинаковой установленной ценой, которые при этом не могут быть35удовлетворены одновременно (т.е. их сумма превышает доступное количество лотов). Необходимо помнить, что в этой ситуации победитель долженопределяться по жребию.Один из возможных алгоритмов реализации аукциона выглядит следующим образом10:1. Скопировать данные о полученных заявках во временную структуруданных (например, список) и рассортировать их по убыванию предложенной цены. Структура данных должна содержать размер заявки, цену и указатель на оригинал заявки (либо просто указатель наобъект игрока, подавшего заявку).2.
Если список заявок пуст, закончить.3. Рассматривая максимальную цену (т.е. цену, указанную в первом элементе временного списка заявок), сосчитать общее количество единицсырья, которые игроки готовы купить по этой цене. Это означает, чтонеобходимо просуммировать размеры тех (нескольких первых в списке) заявок, в которых цена равна максимальной.4. Если полученное количество не превышает количество доступных(продаваемых банком) единиц сырья, то удовлетворить все максимальные заявки, т.е.
пометить все заявки, имеющие максимальнуюцену, как полностью удовлетворенные11, уменьшить количество доступного сырья на сумму удовлетворенных заявок и убрать максимальные заявки из временного списка. После этого перейти к шагу2.5. В противном случае распределить оставшиеся единицы сырья по жребию между подателями максимальных заявок и на этом закончить.3.6Диалоги с пользователемВ некоторых случаях возникает потребность провести с пользователем (игроком) обмен несколькими последовательными репликами, т.е. диалог. Особенность здесь в том, что ответ пользователя на реплику сервера должен10Здесь рассматривается аукцион продажи сырья; аукцион покупки готовой продукции реализуетсяаналогично с учетом того, что наиболее выгодной для банка является не максимальная, а минимальнаяпредложенная цена.11Важно понимать, что пометку об удовлетворении следует делать не во временной структуре данных, а в структурах, содержащих оригинальные данные; например, можно сделать соответствующуюпометку в структуре соответствующего игрока.36быть воспринят именно как ответ на реплику, а не как очередная команда.Например, можно встроить в сервер запрос дополнительного подтверждения на исполнение некоторых “странных” команд; так, если пользовательзаявил о готовности покупать сырье по цене выше 3000 (что заведомо неможет окупиться), логично поинтересоваться, действительно ли это то, чтоон имел в виду12 .Для реализации подобных возможностей студенты часто применяют самый очевидный (и заведомо недопустимый в условиях многопользовательского сервера) подход: выдать сообщение в сокет и после этого дождатьсяответа пользователя (т.е.
сразу за вызовом write ставится вызов read).Проблема здесь состоит в том, что пользователь, разумеется, ответитна вопрос не сразу, и все это время сервер не будет обрабатывать командыдругих игроков, поскольку процесс сервера будет заблокирован на вызовеread в ожидании ответа одного пользователя на заданный сервером вопрос. Игрок, знакомый с этой особенностью сервера, вполне может вызватьситуацию диалога намеренно, чтобы приостановить игру.С другой стороны, если после выдачи сообщения пользователю серверпродолжит обычное выполнение главного цикла с вызовом select, возникает проблема, как после очередного возврата из select’а проинтерпретировать данные, пришедшие от одного конкретного клиента, находящегосяв режиме диалога, не как команду, а как ответ на заданный этому пользователю вопрос.Очевидно, что информацию о том, что данному пользователю был задан некий вопрос, следует сохранить в структуре, соответствующей данному игроку.
Получается, что при налиычии заданного вопроса, на которыйеще не получен ответ, ближайшая прочитанная с сокета данного клиентастрока должна интерпретироваться как ответ на соответствующий вопрос,в противном же случае – как обычная команда. Заметим, что после того,как ответ на вопрос получен, необходимо вернуть соответствующего игрокав обычный режим, в котором его ввод будет восприниматься как команды.Вышесказанное логично подводит нас к понятию состояния. Состояниесвязывается с каждым отдельным игроком (клиентом). В нормальном состоянии весь ввод, получаемый от клиента, воспринимается как команда.При задании игроку специфического вопроса необходимо изменить состояние на соответствующее данному конкретному вопросу, после чего продолжить выполнение главного цикла программы как обычно.
Количествотаких возможных состояний в точности равно количеству различных вопросов, которые в тех или иных ситуациях может задать игроку сервер,плюс еще одно состояние для “нормального режима”. Более сложные диа12Этот параграф можно пропустить, если такой потребности у вас не возникло37логи, остоящие из более чем одной реплики, используют столько состояний,сколько в них имеется реплик, последовательно (при получении на очередной итерации главного цикла очередного ответа от пользователя) переходяот одного состояния к другому.Программированиевтакомстиленазываетсяавтоматориентированным.Реализовать состояние рекомендуется в виде переменной перечислимого типа.3.7Рекомендации по тестированиюТестирование игрового сервера следует проводит в микрогруппах по 3-4 человека. Участники группы играют несколько многопользовательских партий с использованием сервера очередного участника.Предусмотрите в программе-сервере генерацию отчета по каждому ходу, включающего список поданных на аукционы заявок с индикацией обих удовлетворении/неудовлетворении, количество произведенной каждымигроком продукции и остаток средств на счету каждого игрока на моментконца хода.