Коди мене гольф


15

Якщо ви раніше не грали в гольф, ось список термінів, пов'язаних з гольфом, я використовую в цьому запитанні

  • Постріл , який також називають ударом : Кожен раз, коли м'яч потрапляє, це постріл.
  • Отвір : Поле для гольфу розбивається на отвори, в яких мета - вдарити м'яч з одного призначеного місця в інший якомога менше пострілів.
  • Трійник : Там, де ви починаєте дірку.
  • Шпилька або прапор : там, де ви закінчите лунку
  • Фарватер , Груба , Вода та Зелений : Особливості на майданчику для гольфу, які впливають на те, як можна грати в м'яч у реальному житті. (Як вони впливають на програму, вказано нижче)

Я збираюся завтра пограти в гольф, і мені здається, що іноді у мене виникають труднощі з'ясувати, який клуб використовувати, щоб потрапити на певний ярда. Тож я вирішив записувати свої клуби та їхні ярди за постріл.

Перше припущення: всі отвори розташовані на північ від трійника.

Усі ці ярди вимірюють можливості того, наскільки далеко на північ рухається куля. М'яч пройде випадкове ціле відстань між межами, визначеними для кожного клубу (включно).

Як майстер-гольфіст, жоден з моїх пострілів не має горизонтального зсуву. Це означає, що всі мої знімки йдуть по прямій лінії прямо під прапором.

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)

Як людина, яка полюбляє програмування, я вирішую, що хочу моделювати тур з гольфу і поставила собі мету того, наскільки добре я хочу зробити завтра. Однак, як і будь-який аматорський програміст, через десять хвилин я здався і попросив допомоги щодо Stack Overflow (просто жартую). Ось ще кілька даних про курс.

Друге припущення: Географія лунки

  • Усі числа, що описують відстані на курсі, є цілими числами.

  • Кожен отвір - пряма лінія. Відстань прямої лінії між кожним отвором і шпилькою (кінцем отвору) дорівнює 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

Python 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. Іноді пощастить, іноді - ні. Ознайомтеся з новою гістограмою.

Гістограма2

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.