Моделирование футбола для игры

Я хотел бы создать механизм моделирования, который может имитировать матч футбола (ассоциации футбола). Было бы здорово, если бы вы могли мне помочь. Для меня важно решить, какие действия происходят. Слушатели событий для каждого действия могут быть реализованы позже легко. Функция должна только имитировать результаты игры и комментарии к происходящим действиям. Нет необходимости в 2D / 3D-графике. Мы говорим о таких играх, как Хаттрик .


Я бы предположил, что сначала у вас есть несколько минут с действиями.

$ minutes = array (1, 3, 4, 7, 11, 13, …, 90, 92);

Для каждой из этих минут вы могли бы имитировать атаку.

Атакующая команда определяется кубиками до: $ attacking = mt_rand (1, 2);

Поэтому самая важная для меня роль – это функция атаки.

Измените мой подход или используйте его в качестве образца. Можете ли вы помочь мне улучшить это? Функция должна быть сложной, чтобы результаты были максимально реалистичными. Но вам нужно найти что-то между высокой предсказуемостью и слишком случайными результатами. Я хочу только улучшить эту функцию.

Мой подход:

<?php function Chance_Percent($chance, $universe = 100) { $chance = abs(intval($chance)); $universe = abs(intval($universe)); if (mt_rand(1, $universe) <= $chance) { return true; } return false; } function simulate_attack($teamname_att, $teamname_def, $strength_att, $strength_def) { global $minute, $goals, $_POST, $matchReport, $fouls, $yellowCards, $redCards, $offsides, $schuesse, $taktiken; // input values: attacker's name, defender's name, attacker's strength array, defender's strength array // players' strength values vary from 0.1 to 9.9 // ADJUSTMENT START switch ($taktiken[$teamname_att][0]) { case 1: $strength_att['defenders'] *= 1.1; $strength_att['forwards'] *= 0.9; break; case 3: $strength_att['defenders'] *= 0.9; $strength_att['forwards'] *= 1.1; break; } switch ($taktiken[$teamname_def][0]) { case 1: $strength_def['defenders'] *= 1.1; $strength_def['forwards'] *= 0.9; break; case 3: $strength_def['defenders'] *= 0.9; $strength_def['forwards'] *= 1.1; break; } // ADJUSTMENT END $matchReport .= '<p>'.$minute.'\': '.comment($teamname_att, 'attack'); $offense_strength = $strength_att['forwards']/$strength_def['defenders']; $defense_strength = $strength_def['defenders']/$strength_att['forwards']; if (Chance_Percent(50*$offense_strength*($taktiken[$teamname_att][2]/2)*($taktiken[$teamname_att][3]/2))) { // attacking team passes 1st third of opponent's field side $matchReport .= ' '.comment($teamname_def, 'attack_advance'); if (Chance_Percent(25*($taktiken[$teamname_def][4]/2))) { // the defending team fouls the attacking team $fouls[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul'); if (Chance_Percent(43)) { // yellow card for the defending team // chance is correct for my purpose $yellowCards[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_yellow'); } elseif (Chance_Percent(3)) { // red card for the defending team // chance is correct for my purpose (only 1.43% because it's an alternative way) $redCards[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_red'); } // indirect free kick // only 58.23% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick'); if (Chance_Percent(25)) { // shot at the goal $schuesse[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot'); if (Chance_Percent(25)) { // attacking team scores (6.25% chance) $goals[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_score'); } else { // defending goalkeeper saves // only 18.75% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_save'); } } else { // defending team cleares the ball // only 75% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_clear'); } } elseif (Chance_Percent(17)) { // attacking team is caught offside // only 4.25% because it's an alternative way $offsides[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_offside'); } else { if (Chance_Percent(25*($taktiken[$teamname_def][5]/2))) { // the defending team fouls the attacking team $fouls[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul'); if (Chance_Percent(43)) { // yellow card for the defending team // chance is correct for my purpose $yellowCards[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_yellow'); } elseif (Chance_Percent(3)) { // red card for the defending team // chance is correct for my purpose (only 1.43% because it's an alternative way) $redCards[$teamname_def]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_red'); } if (Chance_Percent(19)) { // penalty for the attacking team $schuesse[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty'); if (Chance_Percent(77)) { // attacking team scores (77% chance according to Wikipedia) $goals[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_score'); } elseif (Chance_Percent(50)) { // shot misses the goal // only 11.5% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_miss'); } else { // defending goalkeeper saves // only 11.5% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_penalty_save'); } } elseif (Chance_Percent(28)) { // direct free kick // only 22.68% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick'); if (Chance_Percent(33)) { // shot at the goal $schuesse[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot'); if (Chance_Percent(33)) { // attacking team scores (10.89% chance) $goals[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot_score'); } else { // defending goalkeeper saves $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_shot_save'); } } else { // defending team cleares the ball // only 77% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_dFreeKick_clear'); } } else { // indirect free kick // only 58.23% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick'); if (Chance_Percent(25)) { // shot at the goal $schuesse[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot'); if (Chance_Percent(25)) { // attacking team scores (6.25% chance) $goals[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_score'); } else { // defending goalkeeper saves // only 18.75% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_shot_save'); } } else { // defending team cleares the ball // only 75% because it's an alternative way $matchReport .= ' '.comment($teamname_def, 'attack_advance_foul_iFreeKick_clear'); } } } else { // attack passes the 2nd third of the opponent's field side - good chance $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance'); if (Chance_Percent(62*($taktiken[$teamname_att][6]/2)*($taktiken[$teamname_att][7]/2)/($taktiken[$teamname_att][8]/2)*($taktiken[$teamname_att][9]/2)/($taktiken[$teamname_def][10]/2))) { // shot at the goal $schuesse[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot'); if (Chance_Percent(30*$strength_def['goalkeeper']/7/($taktiken[$teamname_att][11]/2))) { // the attacking team scores // only 8.78% because it's an alternative way // if goalkeeper has strenth 7 then chance is 8.78% otherwise lower/higher $goals[$teamname_att]++; $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_score'); } else { if (Chance_Percent(50)) { // the defending defenders block the shot $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_block'); } else { // the defending goalkeeper saves $matchReport .= ' '.comment($teamname_def, 'attack_advance_advance_shot_save'); } } } } } } // attacking team doesn't pass 1st third of opponent's field side elseif (Chance_Percent(15*$defense_strength*($taktiken[$teamname_att][12]/2)*($taktiken[$teamname_att][13]/2))) { // quick counter attack - playing on the break // only 7.5% because it's an alternative way // if defense has strength 7 then chance is 7.5% otherwise lower/higher $strength_att['defenders'] = $strength_att['defenders']*0.8; // weaken the current attacking team's defense $matchReport .= ' '.comment($teamname_def, 'attack_quickCounterAttack'); $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line return simulate_attack($teamname_def, $teamname_att, $strength_def, $strength_att); // new attack - this one is finished } else { // ball goes into touch - out of the field $matchReport .= ' '.comment($teamname_def, 'attack_throwIn'); if (Chance_Percent(33)) { // if a new chance is created if (Chance_Percent(50)) { // throw-in for the attacking team $matchReport .= ' '.comment($teamname_def, 'attack_throwIn_att'); $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line return simulate_attack($teamname_att, $teamname_def, $strength_att, $strength_def); // new attack - this one is finished } else { // throw-in for the defending team $matchReport .= ' '.comment($teamname_def, 'attack_throwIn_def'); $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line return simulate_attack($teamname_def, $teamname_att, $strength_def, $strength_att); // new attack - this one is finished } } } $matchReport .= ' ['.$goals[$_POST['team1']].':'.$goals[$_POST['team2']].']</p>'; // close comment line return TRUE; // finish the attack } ?> 

Тактические настройки, которые должны влиять на случайность:

  • (1 = оборонительный, 2 = нейтральный, 3 = оскорбительный): чем выше значение, тем слабее защита и тем сильнее нарушение
  • скорость игры (1 = медленная, 2 = средняя, ​​3 = быстро): чем выше значение, тем лучше возможности, но тем выше риск получения быстрой встречной атаки
  • расстояние от проходов (1 = короткое, 2 = среднее, 3 = длительное): чем выше значение, тем меньше, тем лучше, чем вы получаете, и чем чаще вы находитесь вне игры
  • создание изменений (1 = безопасный, 2 = средний, 3 = рискованный): чем выше значение, тем лучше ваши возможности, но тем выше риск получения быстрой встречной атаки
  • давление в обороне (1 = низкий, 2 = средний, 3 = высокий): чем выше значение, тем более быстрые встречные атаки у вас будут
  • агрессивность (1 = низкая, 2 = средняя, ​​3 = высокая): чем выше значение, тем больше атак вы остановите фолами

Интеграция тактических настроек:

Все тактические настройки имеют значение, которое может быть «1», «2» или «3». «2» всегда нейтрально / среднее. Поэтому я делю значения на 2. Я получаю соотношение, равное 0,5 или 1 или 1,5. Я подумал, что тогда я мог бы умножать шансы на это, чтобы интегрировать тактическое влияние. Но возникла одна проблема: если я умножу шанс на 2 или более тактических значения, он может быть выше 100% (например, 60 х 1,5 х 1,5). Поэтому я не могу интегрировать тактику таким образом. Что еще я могу сделать?


Большое спасибо!

Обновление (2014 год): Несколько лет спустя я выпустил полную базу кода игры в качестве открытого источника на GitHub . Вы найдете конкретную реализацию этого моделирования в этом файле , если кому-то это интересно.

    Постройте симуляцию на основе веса (да, я только что придумал этот термин). Каждая переменная (независимо от ее типа) имеет «вес». Например, игроки имеют вес. Хороший игрок имеет дополнительный вес. Игрок с травмой имеет меньше веса или даже не ждет вообще (или, может быть, отрицательный вес?).

    Вы добавляете весь вес (обе команды, потому что это футбольный матч). Этот вес напоминает выигрышную процентную ставку. Например;

    Вес команды A = 56 , вес команды B = 120

    Вес уже показывает, что одна команда намного лучше (независимо от того, как вес был установлен .. может быть, у них очень круглые шары, кто заботится), чем другой.

    Основываясь на весе, вы можете рассчитать выигрышный шанс; Победный шанс команды A = 32% , выигрышный шанс команды B = 68% .

    Теперь вы можете написать алгоритм, который имитирует соответствие, на который влияет процент выигрыша. Однажды я написал такой алгоритм, чтобы нарисовать рекламу. В моем случае количество кликов рекламы было весом. Чем больше вес, тем больше вероятность, что реклама была выбрана моим алгоритмом.

    Я написал алгоритм, взяв большое количество (например, 1000), а затем присвоил диапазон этого числа каждому рекламному объявлению в зависимости от веса. В этом случае команда A получает диапазон 32% от 1000 , что составляет 0 – 320, команда B получает 68%, что составляет 321 – 1000 . Тогда мой алгоритм нарисовал бы число (случайным образом) между 0 и 1000. Реклама (или ваши команды) с наибольшим диапазоном (и, таким образом, самым большим шансом на выигрыш) имеет наибольшую вероятность выбора алгоритмом, хотя может получиться иначе ,

    Этот алгоритм отличный (хотя и не идеальный) для сбалансированного результата (если пользователи могут создавать собственные команды, покупать лучших игроков и т. Д.). Вы также можете делать любые события в игре, нарисованные этим алгоритмом, просто добавляя вес к событию.

    Вы можете добавить вес к событию (например, травму товарища по команде) по команде, исходя из других весовых коэффициентов в этой команде (сколько матчей сыграно подряд, насколько хорошо (или сколько весит) их медицинский персонал , и т.д). Если вы правильно относитесь к весу, вы можете получить очень сбалансированный (и легко расширяемый) алгоритм моделирования, который может быть предсказуемым (точно так же, как некоторые матчи в реальной жизни) или совершенно неожиданным (опять же, как и в реальной жизни).

    ОБНОВЛЕНИЕ: Тактические влияния Вы добавили тактические влияния, а также вопрос «как вы это сделаете?», Поэтому я уточню. То, что вы сейчас делаете (насколько я понимаю), вы принимаете процент (вероятность чего-то происходит) и умножайте это с коэффициентом, чтобы оно происходило больше / меньше.

    Однако, поскольку у вас может быть несколько коэффициентов, вы получаете шанс более 100%.

    Прежде всего, для каждого тактического преимущества команды есть (возможно) встречное преимущество для другой команды. Например, если команда A имеет вес в достижении целей, команда B имеет счетчик веса при остановке целей. Эта сумма – вселенная (100%). Теперь вес обоих тактических преимуществ составляет часть этой вселенной или общий вес (как я объяснил выше).

    Скажите, что команда A на 80% наверняка забивает гол за определенную минуту, а команда B на 20% наверняка остановит ее (на основе весовой системы). Но, поскольку команда B просто приобрела очень хорошего вратаря, на стороне команды Б есть тактическое влияние. Это влияние должно изменить шансы на событие, но не сама Вселенная! Другими словами, у вас не должно быть общей вероятности более 100% (хотя в некоторых случаях это не обязательно плохо)

    Таким образом, вы должны добавить вес к команде B , основываясь на тактическом влиянии, а затем пересчитать шансы, основанные на новых весах.

    Назначение веса

    Теперь, как вы прокомментировали, назначение веса непросто. Конечно, нет, если вам нужно «взвешивать» игроков по их качествам. Взвешивание – это нечто большее, чем просто говорящее, что игрок «плохой» или «хороший», вы должны его оценивать (например, в старшей школе!). Чем выше максимальный класс, тем более точна система взвешивания.

    Теперь присвоение весов тактическим влияниям немного легче. Скажите, что у вас есть следующие влияния;

    • Остановка целей
    • Целевые показатели
    • Защита
    • Атака

    Теперь создайте пул с общим весом (скажем, 1000, мне нравится этот номер). Это «тактические точки», которые вы могли бы назначить. Эти четыре влияния составляют совпадение, поэтому вы можете назначить 250 очков каждому влиянию. Это число (250) – это вселенная каждого влияния.

    Назначение этих очков за каждую команду зависит от весовых коэффициентов команды (например, у них есть хороший хранитель?)

    Хранитель, например, весит против вратаря оппонентов (и, возможно, также людей, которые находятся между вратарем и противником, но давайте держать его в силе). Голосование игрока команды A составляет 80% от общего количества, а игрок команды B – 20% . Это показывает, насколько они хороши, что напрямую связано с тактическими моментами, которые они получают. Таким образом, команда А получает 80% из 250 очков остановки, а команда Б получает 20% очков.

    Остальные точки могут быть назначены одинаково. В моем примере я взял только двух хранителей как вселенную, когда цель останавливается или нет. В действительности, может быть намного больше весовых факторов (для вас, чтобы понять).

    Как только все они разделены, вы можете использовать тактические очки, чтобы разобраться в матче. За каждую минуту вы можете пересчитать шансы на выигрыш. Каждую минуту вы также можете пересчитать тактические влияния (например, другой игрок входит в поле, или игрок получает травму).

    Да, вы получите много переменных. Но чем больше вы получаете, тем лучше играет матч. Чем больше переменных (или весов / счетных весов), тем больше он ощущается как реальная жизнь.

    Ну, это будет сложно, но если вы хотите реалистично имитировать футбольный матч, вам понадобится гораздо больше переменных, введенных в игру. Не вся ваша команда будет атаковать, у вас будут защитники, и эти защитники уменьшат силу атаки противоположной команды.

    Я бы порекомендовал потоку нечто большее:

    1) Команда А имеет мяч на своей половине поля. Он попытается забить. Создайте таблицу с 1-100 (0-99) и заполните ее следующими факторами: Игрок из умения команды А, Защитные способности Против Игрока, расстояние от цели, количество усталости (продолжительность игры). Эта таблица будет выглядеть примерно так (представьте +1, чтобы сделать ее проще, поэтому 1-100 не 0-99):

    1. 1-50: Игрок успешно продвигает мяч
    2. 51-60: Игрок не сумел продвинуть мяч, но сохранил владение
    3. 61-73: Игрок не смог продвинуть мяч и потерял владение
    4. 74-82: Игрок передает мяч далеко вверх по полю другому злоумышленнику
    5. 83-95: Игрок теряет владение мячом, и противник пытается забить
    6. 95-100: Критический сбой: игрок теряет владение мячом и мгновенно дает цель противоположной команде

    2) В случае 1 снова сверните, но теперь у вас есть другой набор опций для нахождения на противоположной стороне. В четном из двух повторите то же рулон на столе. В случае трех, сделайте одинаковые броски для противоположной команды, но используйте другую таблицу, поскольку они ближе к цели. В случае 4 сделайте рулоны снова, но измените статистику игрока на игрока 2 в команде A и предположите, что числа ближе к цели. В случае 5 сверните таблицу, в которой противоположная команда либо преуспевает, либо не работает на основе умения случайного игрока. В случае шести мгновенно дают цель противоположной команде (в игре в футбол это происходит менее чем в 5% случаев, это больше похоже на 0,01%, но вы также можете перевернуть другую таблицу критические сбои, которые включают травмы, или выглядят глупо в течение десяти секунд).

    3) Повторите процесс, основываясь на результатах.

    Я мог бы привести примеры кода, но я думаю, что у вас есть основная идея.

    ОБНОВЛЕНИЕ: Я думаю, что другие ответили, как повлиять на весы, используя ваш метод. Мой метод на самом деле был бы другим, и я объясню различия здесь …

    1) Ваши расчеты в основном предполагают попытку цели каждый так много минут, и в этот момент существует сложный набор вычислений, которые пытаются смоделировать вероятность того, что игрок будет или не забьет. Мои расчеты предполагали, что мяч постоянно находится в игре, и переходит от одного набора возможных результатов к следующему, основываясь на серии статистических данных, а не рассматривается как серия повторных выстрелов по цели. Это будет концептуально отличаться от того, как он следует за мячом, а не игроками, и мяч будет привязан к определенному числу возможностей, которые будут взвешены по вышеупомянутым факторам.

    2) Метод, по которому вы взвешиваете вещи, включает в себя несколько весов, которые, как описывали другие, увеличивают ваш процент изменений на определенную сумму. В моем сценарии вы разбиваете шансы на стол, так что существует только абсолютный максимум 100 возможных результатов, а затем вы используете mt_rand для попадания числа внутри этого набора результатов, что часто называют таблицей ударов или в лотерею.

    Для этого у вас будут базовые шансы, а затем вес их, что даст вам возможность расти. Например, скажем, что базовый шанс забить мяч составляет 10/100. Если кто-то равен 1, это становится 5. 2, оно остается 10. 3, оно становится 15. Итак, теперь ролики 0-14 гибко привязаны к этим значениям, а остальные значения в таблице пострадали сдвигаются для размещения. Это вызывает вероятные проблемы позже, поскольку вы также должны гипотетически вырастить таблицу, но это пассивно уменьшает вероятность удара, поскольку вы будете идти от 10/100 до 15/105 (с учетом всех других возможных результатов).

    Если вы хотите продолжить путь, который вы уже начали, я думаю, что большинство из того, что я говорю здесь, не могло бы работать, если вы просто не выбросили эти значения в таблицу и не подсчитали их при каждой атаке, как вы уже это делали.

    Если вы хотите придерживаться текущих вложенных случайных утверждений, я бы сказал, что вы найдете одно из решений, которое предложили другие. Удачи!

    Я бы предложил вам преобразовать все ваши вероятности в проценты:

     function Chance($chance, $universe = 100) { $chance = abs(intval($chance)); $universe = abs(intval($universe)); if (mt_rand(1, $universe) <= $chance) { return true; } return false; } Chance(25); // 25% Chance(5, 1000); // 0.5% 

    Кроме того, я бы только экземпляр некоторых вероятностей с условиями других, например:

     if ($attack === true) { if (Chance(15 * $aggressivity) === true) { if (Chance(10 * $aggressivity) === true) { // red card } else { // yellow card } } } 

    EDIT: Я просто дома, и мне действительно нужно ложиться спать, но после быстрого взгляда на ваше редактирование у меня появилась идея, которая может вас заинтересовать. Что делать, если вместо использования значения корректировки 1, 2 или 3 вы не используете тактическое положение команды? К примеру, у команды с 4-4-2 были меньше шансов забить гол в матче с командой 5-3-2, чем в команде 3-3-4. Предполагая, что места размещения всегда являются триплетами (XYZ), было бы довольно легко сравнить, какая команда лучше всего подходит для защиты, прохождения и подсчета очков.

    Простая формула может быть примерно такой:

     A: 4-4-2 (Defending Team) B: 3-2-5 (Attacking Team) 

    Шанс B забить гол был бы (4/5) ^ -1 = 0,2 = 20%, а обратный (скоринг команды A) был бы (3/2) ^ -1 = 0,5 = 50%. PS: Сейчас это, похоже, не имеет большого смысла, но я постараюсь еще раз взглянуть на него утром.

    И еще: после красной карточки почему команда по разборке остается прежней? Он должен стать более слабым (на один игрок меньше) ИМО.

    Это настоящий трюк, не так ли? Возможно, начните думать о том, какие переменные играют игроки и команды, которые могут повлиять на успех и вероятность действия.

    Поток программы может выглядеть так: 1. Вычислить, какое событие происходит на основе переменных, и пусть там какая-то случайность. 2. Рассчитайте скорость успеха события.

    Боюсь, вам придется это делать и программировать, безусловно, самостоятельно;)