Napisz mi golfa

15

Jeśli wcześniej nie grałeś w golfa, oto lista terminów związanych z golfem, których używam w tym pytaniu

  • Strzał , zwany także uderzeniem : za każdym razem, gdy piłka zostaje uderzona, jest to strzał.
  • Dziura : pole golfowe jest podzielone na dziury, w których celem jest uderzenie piłki z jednego wyznaczonego miejsca do drugiego w jak najmniejszej liczbie rzutów.
  • Tee : Gdzie zaczynasz dołek.
  • Przypinka lub flaga : Gdzie kończysz dziurę
  • Fairway , Rough , Water i Green : Funkcje na polu golfowym, które wpływają na sposób gry w piłkę w prawdziwym życiu. (Ich wpływ na program jest określony poniżej)

Jutro idę grać w golfa i czasami mam problem z ustaleniem, jakiego klubu użyć, aby osiągnąć określony dystans. Postanowiłem więc zapisać moje kluby i ich dystanse na strzał.

Pierwsze założenie: wszystkie dziury są na północ od ich tee boxów.

Wszystkie te odcinki mierzą możliwości, jak daleko piłka podróżuje na północ. Piłka pokona losową liczbę całkowitą między granicami określonymi dla każdego klubu (włącznie).

Jako mistrz golfisty żadne z moich ujęć nie ma przesunięcia w poziomie. Oznacza to, że wszystkie moje strzały padają w linii prostej bezpośrednio na flagę.

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)

Jako osoba, która lubi programować, postanawiam, że chcę wymodelować rundę golfa i wyznaczyć sobie cel, jak dobrze chcę jutro. Jednak, jak każdy programista amator, po dziesięciu minutach poddałem się i poprosiłem o pomoc w stosie przepełnienia (tylko żartuję). Oto kilka innych danych na temat kursu.

Drugie założenie: Geografia otworów

  • Wszystkie liczby opisujące odległości na kursie są liczbami całkowitymi.

  • Każdy otwór jest linią prostą. Odległość w linii prostej między każdym otworem a sworzniem (koniec otworu) wynosi Length.

  • Tor wodny to odcinek o długości określonej przez flen. Podana wartość flento zakres odcinków na północ od tee, na której znajduje się tor wodny.

  • Zagrożenia wodne to segmenty, które mają zdefiniowaną długość wlen, która ma takie same właściwości jak flen.

  • Kolor zielony ma długość określoną przez glen.

  • Wszystkie części pola, które nie są torami wodnymi, wodnymi lub zielonymi, są nierówne.

Oto tabela opisująca każdą dziurę na kursie.

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

Jak grać w golfa (dla tego programu)

  • Zawsze celuj dokładnie w flagę.
  • Uderz piłkę jak najbliżej sworznia, starając się utrzymać piłkę na torze wodnym lub (najlepiej) na zieleni.
  • Kiedy wylądujesz w wodzie, twój następny strzał musi zostać rozegrany z tego samego miejsca, co strzał, który trafił do wody.
  • Gdy piłka wyląduje na zieleni, można użyć tylko miotacza. Jeśli piłka wyląduje dokładnie ponad 5 jardów od sworznia, wtedy uderzam dwa razy. W przeciwnym razie raz postawię.
  • Możliwe jest trafienie za pinezkę.

Punktacja

Mój wynik na dołku to liczba wykonanych przeze mnie strzałów plus jedno uderzenie za każdym razem, gdy ląduję na nierówności lub w wodzie.

Program

Okej, to było dużo zasad, teraz porozmawiajmy o programie.

Kurs należy zdefiniować jak wyżej w programie , ponieważ kurs jest stały. Różni golfiści mają jednak różne odległości dla każdego strzału, więc wejściem do STDIN powinien być zestaw zakresów metrażów, ułożonych w kolejności numerów klubów i oddzielonych przecinkami (bez białych znaków).

Efektem powinno być to, jak „gram” w golfa. Numer wstrzymania należy podać na początku każdej linii, Hole #:gdzie #jest bieżący otwór. Każdy strzał, który nie jest putt ma postać następującą:{club,distance of shot,condition of ball,distance to pin} . Szczegóły ujęcia powinny być oddzielone przecinkami, ale bez spacji w powyższej kolejności. Same ujęcia powinny być napisane w kolejności ich odtwarzania i oddzielone spacją. Gdy piłka wyląduje na zielonym, program powinien wydrukować liczbę puttów, które wezmę, w formacie {# putts}. Na końcu każdej linii liczba zdjęć, które wykonałem na dołku, powinna być oddzielona spacją od innych zdjęć i wydrukowana jako(#). Każda dziura powinna znajdować się w osobnej linii i pisać w kolejności. Wreszcie w ostatnim (dziesiątym) wierszu programu należy wydrukować całkowitą liczbę strzałów w rundzie Total: # shots.

Nie ma określonej „strategii”, którą Twój program musi podjąć. Możesz napisać program z dowolną strategią. Przykładowe strategie obejmują maksymalizację procentowej szansy na lądowanie na zielonym polu i maksymalizację odległości każdego strzału, aż do dołka.

PRZYKŁADOWE WEJŚCIE

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

PRZYKŁADOWA WYDAJNOŚĆ

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

Przyznaję, że jest to dość ambitne wyzwanie dla pierwszego postu na CG.SE, więc chętnie porozmawiam o tym, jak poprawić to wyzwanie w komentarzach. Dziękuję za pomoc

Arktur
źródło
2
Byłbym bardzo wdzięczny, gdybyśmy, dla nas, nie-golfistów, nie używali tylu terminów golfowych (np. „Tee boxy” i „shift poziomy”). :)
kirbyfan64sos
Dodam listę terminów związanych z golfem. Podczas gry w golfa piłka nie zawsze idzie prosto, więc właśnie powiedziałem, że piłka zawsze idzie prosto w kierunku dołka, a zatem nie ma żadnego przesunięcia w poziomie.
Arcturus,
Powiedzmy, że pinezka znajduje się na 301 jardach, i jest tor wodny od 0~299jardów, zielony od 300~315jardów i woda z 316~330jardów. Który klub zostanie wybrany? Co jeśli woda zostanie zastąpiona przez szorstką?
lirtosiast
W idealnym przypadku program powinien mieć możliwość opracowania własnej strategii.
Arcturus,
Co rozumiesz przez „optymalną strategię”? Minimalizujesz średnią liczbę uderzeń? Jeśli chodzi o kryterium wygranej, wybrałbym golf kodowy.
lirtosiast

Odpowiedzi:

9

Python 2.7: 43 Średnia 40,5 strzałów

To jest mój pierwszy post tutaj, więc proszę o wyrozumiałość.

Ponieważ plakat myślał o traktowaniu tego jak wyzwania programistycznego, a nie golfa kodowego, podniosłem to jako wyzwanie programistyczne. Starałem się, aby moje rozwiązanie i logika były proste, ale okazało się to bardziej brzydkie, ponieważ sprawy szybko się skomplikowały.

Mój kod

Kilka rzeczy do przemyślenia podczas czytania: program tworzy listę używanych pałek zwanych „pałkami” i listę zwaną „odległościami”, czyli odległością, jaką piłka pokonała od tee, hlen jest długością dołka, d1s jest odległość, jaką pokonuje każdy strzał.

Najpierw określam kurs. Każda toru wodnego, wody i długości zieleni musiała zostać zdefiniowana, aby później program mógł sprawdzić stan kuli, dlatego dodałem wartości nie całkowite dla części kursu, które nie istniały.

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]

Tutaj zdefiniowałem główną logikę wyboru klubu. Program próbuje zmaksymalizować odległość, wybierając kierowcę dla wszystkich długości większych niż maksymalna odległość kierowcy i wybiera kij o zasięgu, który zawiera odległość do dołka w przeciwnym razie. Wymaga to ciągłego zasięgu zapewnianego przez sygnał pałki, tzn. Braku odstępów w odległości strzału. Realistyczny wymóg, ponieważ można uderzyć w kij bez pełnego backswinga, aby ograniczyć odległość strzału do maksymalnej odległości następnego najsilniejszego kija.

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

Następnie definiuję funkcję put, w której dwa putty dla wszystkich długości większych niż 5 jardów do otworu i jeden putt dla 5 i mniej. Mam również opcję uderzenia piłki bezpośrednio w otwór o nazwie „chip in”.

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

Tutaj strategia staje się trochę funky. Aby zachować prostotę, a także zapobiec utknięciu w pętli wbijania się w wodę tylko po to, aby upuścić piłkę w miejscu poprzedniego strzału i ponownie wjechać do wody, faktycznie cofam się, uderzając piłkę do tyłu za pomocą klina piasku i a następnie niech kod ponownie oceni strzał, tym razem strzelając tuż nad wodą, aby następny strzał mógł go usunąć. Ta strategia jest karana surową karą, ale jest skuteczna w oczyszczaniu wody.

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

Ten program zlicza liczbę uderzeń na dołek po odtworzeniu tego dołka. Dodaje kary za strzały z grubsza i dodaje kary za uderzenie w wodę, sumując tablicę zwaną wodą, która jest dodawana po każdym strzale z wody. Wykorzystuje to fakt, że tor wodny zawsze prowadzi do wody lub zieleni na każdą dziurę w trasie. Trzeba by to zmienić w przypadku kursów, które zawierały nierówne warunki na środku toru wodnego.

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

Po uruchomieniu głównego kodu warunek sprawdza odległości, na których znajdowała się piłka podczas dołka i zgłasza stan piłki. Natknąłem się na jeden problem z warunkiem, ponieważ traktowałem uderzanie piłki do wody w głównym programie. W programie, jeśli piłka została trafiona w wodę, natychmiast została przeniesiona z powrotem do miejsca, w którym uderzono. Odległość została zapisana po cofnięciu piłki, więc stan piłki nie może być „wodny”. Jeśli uderzysz piłkę z tee na otworze 4 do wody, program wydrukuje odległość uderzenia piłki i kija, ale długość dołka pozostanie niezmieniona, a warunek będzie „szorstki”, ponieważ piłka upuszczona jest na 0 odległość, która jest nierówna. Możesz odkomentować nadruk „woda”

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

Oto główna część kodu, która ładuje dziury i gra. Po zainicjowaniu niektórych warunków kod uruchamia „uderzenie” uderzając piłkę w kierunku dołka, w tym odwrotnie, jeśli otwór został przekroczony, aż napotka wodę lub zieleń. Jeśli napotka się wodę, dodaje się do licznika kar i uruchamia wodę z programu, a po przesunięciu piłki z powrotem do miejsca, z którego została trafiona. W przypadku napotkania zieleni put jest wywoływany, a dziura zostaje zakończona. Po przeanalizowaniu odległości i maczug, aby określić stan każdego strzału, a strzały są wyliczane.

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)

Kod jest uruchamiany jak

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)

i wygląda to tak:

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

Był to jeden z najniższych wyników wielu prób, z absolutnie najniższym wynikiem 26 na 100 000 przebiegów. Ale nadal poniżej typowej wartości 34-36, nawet przy 8 uderzeniach w otworze 4.

Dołączę kod, którego użyłem do znalezienia dystrybucji gier z wyżej wymienionych klubów.

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()

Bieganie

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

daje następujący histogram Histogram golfowy

a średnią i medianę można znaleźć za pomocą

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

średnio około 43 i mediana 41. Nieźle jak na 9 dołków z prostą optymalizacją strzału.

Teraz wszystko jest twoje

Śmiało, skopiuj i popraw mój program i oceń go za pomocą moich narzędzi, aby obniżyć średnią liczbę zdjęć. Daj mi znać, jeśli istnieje jakikolwiek scenariusz, którego nie wziąłem pod uwagę ani nie przygotowałem wersji golfowej. Myślę, że najlepszym programem byłby ten, który zwrócił najniższą średnią liczbę strzałów dla wielu wkładów klubowych. Mój kod nie jest najlepszą opcją do tego, ale myślałem, że zacznę grać.

Aktualizacja

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

Zmieniając logikę wody, aby próbowała uderzyć piłkę do przodu niewielką ilością po napotkaniu wody zamiast do tyłu, jeśli poprzedni używany kij nie był klinem piasku, poprawiła średnią do 40,5 i medianę do 39 po testowaniu z jednym milion biegów. Minimum 23, maksimum 135. Czasami masz szczęście, a czasem nie. Sprawdź nowy histogram.

Histogram 2

Adam
źródło