Напишите мне немного гольфа

15

Если вы раньше не играли в гольф, вот список терминов, связанных с гольфом, которые я использую в этом вопросе.

  • Выстрел , также называемый ударом : каждый раз, когда по мячу бьют, это выстрел.
  • Отверстие : поле для гольфа разбито на отверстия, цель которых состоит в том, чтобы поразить мяч из одного назначенного места в другое за как можно меньшее количество выстрелов.
  • Ти : Где вы начинаете отверстие.
  • Булавка или Флаг : Где Вы заканчиваете отверстие
  • Fairway , Rough , Water и Green : особенности поля для гольфа, которые влияют на то, как каждый играет в мяч в реальной жизни. (Как они влияют на программу, указано ниже)

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

Первое предположение: все отверстия находятся к северу от их тройников.

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

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

Club #     Club        Yardage
1          Driver      300-330
2          3-Wood      270-299
3          5-Wood      240-269
4          3-Iron      220-239
5          4-Iron      200-219
6          5-Iron      180-199
7          6-Iron      160-179
8          7-Iron      140-159
9          8-Iron      120-139
10         9-Iron      100-119
11         P-Wedge     80-99
12         S-Wedge     50-79
13         L-Wedge     0-49
14         Putter      (only on green)

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

Второе предположение: дырка география

  • Все числа, которые описывают расстояния на курсе, являются целыми числами.

  • Каждое отверстие - это прямая линия. Расстояние по прямой линии между каждым отверстием и штифтом (концом отверстия) составляет Length.

  • Дорожки сегменты с длиной , определяемой flen. Значение, указанное для, flenпредставляет собой диапазон дворов к северу от тройника, где находится фарватер.

  • Водные преграды - это сегменты, длина которых определяется wlen, и имеет те же свойства, что и flen.

  • Зеленый имеет длину, определяемую glen.

  • Все части трассы, которые не являются фарватером, водой или зеленью, являются шероховатыми.

Вот диаграмма, описывающая каждую лунку на курсе.

Hole #     Length      flen               wlen        glen   
1          401         54-390                         391-425
2          171                            1-165       166-179
3          438         41-392             393-420     421-445
4          553         30-281,354-549     282-353     550-589
5          389         48-372                         373-404
6          133                                        125-138
7          496         37-413             414-484     484-502
8          415         50-391                         392-420
9          320         23-258             259-303     304-327

Как играть в гольф (для этой программы)

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

счет

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

Программа

Хорошо, это было много правил, теперь давайте поговорим о программе.

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

Выход должен быть таким, каким я «играю» в гольф. Количество удержания должно быть указано в начале каждой строки , как , Hole #:где #находится текущее отверстие. Каждый кадр , который не удар имеет следующий вид: {club,distance of shot,condition of ball,distance to pin}. Детали снимка должны быть разделены запятыми, но без пробелов в указанном выше порядке. Сами снимки должны быть написаны в порядке их воспроизведения и разделены пробелом. Как только мяч приземлится на грин, программа должна напечатать, сколько я принимаю паттов, в формате {# putts}. В конце каждой строки количество снимков, сделанных мною в отверстии, должно быть отделено от остальных снимков пробелом и напечатано как(#), Каждое отверстие должно быть на отдельной линии и написано по порядку. Наконец, в последней (десятой) строке программы общее количество выстрелов за раунд должно быть напечатано как Total: # shots.

Не существует установленной «стратегии», которую ваша программа должна принять. Вы можете написать программу с любой стратегией, которую вы хотите. Примеры стратегий включают максимизацию процентной вероятности посадки на грин и увеличение расстояния каждого выстрела до достижения лунки.

ОБРАЗЕЦ ВХОДА

300-330,270-299,240-269,220-239,200-219,180-199,160-179,140-159,120-139,100-119,80-99,50-79,0-49

ОБРАЗЕЦ ВЫХОДА

Hole 1: {Driver,324,Fairway,77} {S-Wedge,70,Green,7} {Two putts} (4)
Hole 2: {6-Iron,162,Water,171} {6-Iron,168,Green,3} {One putt} (4)
Hole 3: {Driver,301,Fairway,137} {8-Iron,131,Green,6} {Two putts} (4)
Hole 4: {3-Wood,288,Water,553} {3-Wood,276,Fairway,277} {3-Wood,291,Green,14} {Two putts} (6)
Hole 5: {Driver,322,Fairway,67} {S-Wedge,62} {One putt} (3)
Hole 6: {8-Iron,120,Rough,18} {L-Wedge,10,Green,8} {Two putts} (5)
Hole 7: {Driver,325,Fairway,171] {6-Iron,170,Green,1} {One putt} (3)
Hole 8: {Driver,306,Fairway,109} {9-Iron,100,Green,9} {Two putts} (4)
Hole 9: {Driver,308,Green,12} {Two putts} (3)
Total: 36 shots

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

Арктур
источник
2
Я был бы очень признателен, если бы для нас, не являющихся игроками в гольф, вы не использовали так много терминов для игры в гольф (например, «тройники» и «горизонтальное смещение»). :)
kirbyfan64sos
Я добавлю список терминов, связанных с гольфом. При игре в гольф мяч не всегда идет прямо, поэтому я просто сказал, что мяч всегда идет прямо к лунке и, следовательно, не имеет горизонтального смещения.
Арктур
Скажите, что булавка находится на расстоянии 301 ярда, и от 0~299двора есть фарватер , зеленый от 300~315двора и вода от 316~330дворов. Какой клуб будет выбран? Что делать, если вода заменяется грубой?
Lirtosiast
В идеале программа должна быть в состоянии придумать свою собственную стратегию.
Арктур
Что вы подразумеваете под «оптимальной стратегией»? Минимизировать среднее количество ударов? Что касается критерия выигрыша, я бы выбрал код-гольф.
lirtosiast

Ответы:

9

Питон 2,7: 43 в среднем 40,5 выстрелов

Это мой первый пост здесь, так что терпите меня.

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

Мой код

Несколько вещей, о которых следует подумать во время чтения: программа создает список клубов, которые называются «клубы», а список, называемый «расстояния», представляет собой расстояние, пройденное мячом от тройника, hlen - длина отверстия, d1s - это расстояние. расстояние, которое проходит каждый выстрел.

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

from random import randint
import numpy as np

#Hole      Length     flen               wlen           glen    Name 
hole1 = [    401,     54, 390,       390.5, 390.5,    391, 425, 'Hole 1']
hole2 = [    171,    0.5, 0.5,           1, 165,      166, 179, 'Hole 2']
hole3 = [    438,     41, 392,         393, 420,      421, 445, 'Hole 3']
hole4 = [    553,     30, 549,         282, 353,      550, 589, 'Hole 4']
hole5 = [    389,     48, 372,         1.5, 1.5,      373, 404, 'Hole 5']
hole6 = [    133,    0.5, 0.5,         1.5, 1.5,      125, 138, 'Hole 6']
hole7 = [    496,     37, 413,         414, 484,      484, 502, 'Hole 7']
hole8 = [    415,     50, 391,         1.5, 1.5,      392, 420, 'Hole 8']
hole9 = [    320,     23, 258,         259, 303,      304, 327, 'Hole 9']

holes = [hole1, hole2, hole3, hole4, hole5, hole6, hole7, hole8, hole9]

Здесь я определил основную логику выбора клуба. Программа пытается максимизировать расстояние, выбирая драйвер для всех длин, превышающих максимальное расстояние водителя, и выбирает булаву с диапазоном, который в противном случае содержит расстояние до отверстия. Это требует, чтобы диапазон, обеспечиваемый входом клюшки, был непрерывным, то есть не было разрывов в расстоянии выстрела. Реалистичное требование, поскольку можно ударить по клюшке без полного обратного удара, чтобы ограничить дистанцию ​​своего выстрела до максимальной дистанции следующего самого мощного клюшки.

def stroke(distance):
    Length = abs(hlen - distance)
    if Length >= Driver_a:
        club = 'Driver'
        d = randint(Driver_a,Driver_b)
    elif Length >= Wood3_a and Length <= Wood3_b:
        club = '3-Wood'
        d = randint(Wood3_a,Wood3_b)
    elif Length >= Wood5_a and Length <= Wood5_b:
        club = '5-Wood'
        d = randint(Wood5_a,Wood5_b)
    elif Length >= Iron3_a and Length <= Iron3_b:
        club = '3-Iron'
        d = randint(Iron3_a,Iron3_b)
    elif Length >= Iron4_a and Length <= Iron4_b:
        club = '4-Iron'
        d = randint(Iron4_a,Iron4_b)
    elif Length >= Iron5_a and Length <= Iron5_b:
        club = '5-Iron'
        d = randint(Iron5_a,Iron5_b)
    elif Length >= Iron6_a and Length <= Iron6_b:
        club = '6-Iron'
        d = randint(Iron6_a,Iron6_b)
    elif Length >= Iron7_a and Length <= Iron7_b:
        club = '7-Iron'
        d = randint(Iron7_a,Iron7_b)
    elif Length >= Iron8_a and Length <= Iron8_b:
        club = '8-Iron'
        d = randint(Iron8_a,Iron8_b)
    elif Length >= Iron9_a and Length <= Iron9_b:
        club = '9-Iron'
        d = randint(Iron9_a,Iron9_b)
    elif Length >= Pwedge_a and Length <= Pwedge_b:
        club = 'P wedge'
        d = randint(Pwedge_a,Pwedge_b)
    elif Length >= Swedge_a and Length <= Swedge_b:
        club = 'S wedge'
        d = randint(Swedge_a,Swedge_b)
    elif Length >= Lwedge_a and Length <= Lwedge_b:
        club = 'L wedge'
        d = randint(Lwedge_a,Lwedge_b)        
    else : print 'stroke error'
    return club, d

Затем я определяю функцию пута: два патта для всех длин больше 5 ярдов до отверстия и один патт для 5 и меньше. Я также включаю опцию для удара по мячу прямо в лунку, называемую «фишка».

def putt(distance):
    Length = abs(hlen - distance)
    if Length > 5:
        club = '2 putts'
    elif Length == 0:
        club = 'chip in'
    else:
        club = '1 putt'
    return club

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

def water():
    club = 'S wedge'
    d = randint(50,79)
    return club, d

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

def countstrokes(clubs, distances, waters):
    distances = np.array(distances)
    mask1 = distances < flen1
    mask2 = distances > grn2
    extra = sum(mask1*1)+sum(mask2*1) + sum(waters)
    if clubs[-1] == 'chip in' : strokes = len(clubs)-1+extra
    elif clubs[-1] == '2 putts' : strokes = len(clubs) +1+extra
    elif clubs[-1] == '1 putt' : strokes = len(clubs)+extra
    else : print 'strokes error'
    return strokes

После выполнения основного кода условие просматривает расстояния, на которых мяч находился во время лунки, и сообщает о состоянии шара. Я столкнулся с одной проблемой с условием из-за того, как я лечил попадание мяча в воду в основной программе. В программе, если мяч попал в воду, он был немедленно перемещен обратно в место, где был произведен удар. Расстояние записывалось после перемещения мяча назад, поэтому состояние мяча не может быть «водой». Если вы ударите мяч по тройнику в отверстии 4 в воду, программа напечатает расстояние, в которое вы ударили по мячу и клюшке, но длина до отверстия останется неизменной, и условие будет «грубым», поскольку мяч упал в 0 расстояние, которое в грубой. Вы можете раскомментировать печать "вода"

def condition(distances):
    conditions=[]
    for distance in distances:
        if distance >= grn1 and distance <= grn2:
            conditions.append('green')
        elif distance >= flen1 and distance <= flen2:
            conditions.append('fair')
        else:
            conditions.append('rough')
    return conditions

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

def golf(driver_a, driver_b, wood3_a, wood3_b, wood5_a, wood5_b, iron3_a, iron3_b, iron4_a, iron4_b, iron5_a, iron5_b, iron6_a, iron6_b, iron7_a, iron7_b, iron8_a, iron8_b, iron9_a, iron9_b, pwedge_a, pwedge_b, swedge_a, swedge_b, lwedge_a, lwedge_b):
    global Driver_a, Driver_b, Wood3_a, Wood3_b, Wood5_a, Wood5_b, Iron3_a, Iron3_b, Iron4_a, Iron4_b, Iron5_a, Iron5_b, Iron6_a, Iron6_b, Iron7_a, Iron7_b, Iron8_a, Iron8_b, Iron9_a, Iron9_b, Pwedge_a, Pwedge_b, Swedge_a, Swedge_b, Lwedge_a, Lwedge_b
    Driver_a, Driver_b, Wood3_a, Wood3_b, Wood5_a, Wood5_b, Iron3_a, Iron3_b, Iron4_a, Iron4_b, Iron5_a, Iron5_b, Iron6_a, Iron6_b, Iron7_a, Iron7_b, Iron8_a, Iron8_b, Iron9_a, Iron9_b, Pwedge_a, Pwedge_b, Swedge_a, Swedge_b, Lwedge_a, Lwedge_b = driver_a, driver_b, wood3_a, wood3_b, wood5_a, wood5_b, iron3_a, iron3_b, iron4_a, iron4_b, iron5_a, iron5_b, iron6_a, iron6_b, iron7_a, iron7_b, iron8_a, iron8_b, iron9_a, iron9_b, pwedge_a, pwedge_b, swedge_a, swedge_b, lwedge_a, lwedge_b
    totals =[]
    for hole in holes:
        distance = 0
        strokes = 0
        clubs = []
        distances = []
        d1s = []
        waters=[]
        global hlen, flen1, flen2, wtr1, wtr2, grn1, grn2
        hlen, flen1, flen2, wtr1, wtr2, grn1, grn2, name = hole
        while True:
            club1, d1 = stroke(distance)
            clubs.append(club1)
            if distance > hlen:
                d1 = -d1
            distance = distance + d1
            d1s.append(d1)
            if distance >= wtr1 and distance <= wtr2:
                #print 'water'
                waters.append(1)
                distance = distance - d1
                distances.append(distance)
                club1, d1 = water()
                if distance < wtr1:
                    d1 = - d1
                distance = distance + d1
                d1s.append(d1)
                clubs.append(club1)
            distances.append(distance)
            if distance >= grn1 and distance <= grn2:
                club1 = putt(distance)
                clubs.append(club1)
                break
        strokes =  countstrokes(clubs, distances, waters)
        totals.append(strokes)
        conditions = condition(distances)
        shots = len(d1s)
        print name, ':',
        for x in xrange(0,shots):
            print '{', clubs[x], ',', d1s[x],',', conditions[x],',', hlen-distances[x], '}',
        print '{',clubs[-1], '}', '{',strokes ,'}'
    print 'Total:', sum(totals), 'shots'
    return sum(totals)

Код запускается как

golf(300,330,270,299,240,269,220,239,200,219,180,199,160,179,140,159,120,139,100,119,80,99,50,79,0,49)

и выглядит так:

Hole 1 : { Driver , 308 , fair , 93 } { P wedge , 96 , green , -3 } { 1 putt } { 3 }
Hole 2 : { 6-Iron , 166 , green , 5 } { 1 putt } { 2 }
Hole 3 : { Driver , 321 , fair , 117 } { 9-Iron , 105 , green , 12 } { 2 putts } { 4 }
Hole 4 : { Driver , 305 , rough , 553 } { S wedge , -62 , rough , 615 } { Driver , 326 , fair , 289 } { 3-Wood , 293 , green , -4 } { 1 putt } { 8 }
Hole 5 : { Driver , 323 , fair , 66 } { S wedge , 73 , green , -7 } { 2 putts } { 4 }
Hole 6 : { 8-Iron , 125 , green , 8 } { 2 putts } { 3 }
Hole 7 : { Driver , 314 , fair , 182 } { 5-Iron , 181 , green , 1 } { 1 putt } { 3 }
Hole 8 : { Driver , 324 , fair , 91 } { P wedge , 91 , green , 0 } { chip in } { 2 }
Hole 9 : { Driver , 317 , green , 3 } { 1 putt } { 2 }
Total: 31 shots

Это был один из самых низких показателей во многих исследованиях, абсолютный минимум - 26 из 100 000 пробежек. Но все еще под типичным номиналом 34-36 даже с 8 ударами на отверстии 4.

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

import matplotlib.pyplot as plt
class histcheck(object):

    def __init__(self):
        self = self

    def rungolf(self, n=10000):
        results=[]
        for x in xrange(0,n):
            shots = golf(300,330,270,299,240,269,220,239,200,219,180,199,160,179,140,159,120,139,100,119,80,99,50,79,0,49)
            results.append(shots)
        self.results = results

    def histo(self, n=20):
        plt.figure(figsize=(12,12))
        plt.hist(self.results, bins=(n))
        plt.title("Histogram")
        plt.xlabel("Shots")
        plt.ylabel("Frequency")
        plt.show()

Бег

play = histcheck()
play.rungolf()
play.hist()

дает следующую гистограмму Гистограмма Гольфа

и среднее значение и медиана могут быть найдены с помощью

np.mean(play.results)
np.meadian(play.results)

среднее значение около 43 и среднее значение 41. Неплохо для 9 лунок с простой оптимизацией выстрела.

Теперь все твое

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

Обновить

def water():
    if clubs[-1] =='S wedge':
        club = 'S wedge'
        d = randint(50,79)
    elif clubs[-1] !='S wedge':
        club = 'S wedge'
        d = -randint(50,79)
    else: print 'water error'
    return club, d

Изменяя водную логику так, чтобы она пыталась ударить по мячу вперед небольшим количеством после столкновения с водой, а не назад, если предыдущий использованный клюш не был песчаным клином, это улучшило среднее значение до 40,5 и медиану до 39 после испытания с одним миллион пробежек. Минимум 23, максимум 135. Иногда вам везет, иногда нет. Проверьте новую гистограмму.

Histogram2

Адам
источник