Code-moi du golf

15

Si vous n'avez pas joué au golf auparavant, voici une liste de termes liés au golf que j'utilise dans cette question

  • Tir , également appelé coup : chaque fois que la balle est frappée, c'est un tir.
  • Trou : Un parcours de golf est divisé en trous, dans lesquels le but est de frapper une balle d'un endroit désigné à un autre en aussi peu de coups que possible.
  • Tee : où vous commencez un trou.
  • Épinglette ou drapeau : où vous terminez un trou
  • Fairway , Rough , Water et Green : Caractéristiques d'un parcours de golf qui affectent la façon dont on joue le ballon dans la vraie vie. (Comment ils affectent le programme est spécifié ci-dessous)

Je vais jouer au golf demain, et je trouve que parfois, j'ai du mal à déterminer quel club utiliser pour atteindre un certain métrage. J'ai donc décidé d'écrire mes clubs et leurs distances par coup.

Première hypothèse: tous les trous sont dus au nord de leurs tertres de départ.

Tous ces métrages mesurent les possibilités de parcourir le nord du ballon. Le ballon parcourra une distance entière aléatoire entre les limites spécifiées pour chaque club (inclus).

En tant que maître golfeur, aucun de mes coups n'a de décalage horizontal. Cela signifie que tous mes clichés vont en ligne droite directement sur le drapeau.

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)

En tant que personne qui aime la programmation, je décide que je veux modéliser une ronde de golf et fixer un objectif pour la façon dont je veux faire demain. Cependant, comme tout programmeur amateur, après dix minutes, j'ai abandonné et demandé de l'aide sur Stack Overflow (je plaisante). Voici quelques données supplémentaires sur le cours.

Deuxième hypothèse: géographie des trous

  • Tous les nombres qui décrivent les distances sur le parcours sont des nombres entiers.

  • Chaque trou est une ligne droite. La distance en ligne droite entre chaque trou et la goupille (l'extrémité du trou) est Length.

  • Les fairways sont des segments dont la longueur est définie par flen. La valeur indiquée flenest la plage de mètres au nord du tee où se trouve le fairway.

  • Les dangers liés à l'eau sont des segments dont la longueur est définie par wlen, qui a les mêmes propriétés que flen.

  • Le vert a une longueur définie par glen.

  • Toutes les parties du parcours qui ne sont pas du fairway, de l'eau ou du vert sont rugueuses.

Voici un tableau décrivant chaque trou sur le parcours.

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

Comment jouer au golf (pour ce programme)

  • Visez toujours exactement le drapeau.
  • Frappez la balle aussi près que possible de la goupille, en essayant de garder la balle sur le fairway ou (de préférence) sur le green.
  • Lorsque vous décrochez un coup dans l'eau, votre coup suivant doit être joué au même endroit que le coup qui est allé dans l'eau.
  • Une fois que la balle atterrit sur le green, seul le putter peut être utilisé. Si la balle atterrit strictement à plus de 5 mètres de la goupille, je fais deux coups roulés. Sinon, j'ai mis une fois.
  • Il est possible de frapper un tir au-delà de la broche.

Notation

Mon score sur un trou est le nombre de coups que je prends, plus un coup pour chaque fois que j'atterris dans la mer ou dans l'eau.

Le programme

D'accord, c'était beaucoup de règles, parlons maintenant du programme.

Le cours doit être défini comme ci-dessus dans le programme , car le cours est constant. Différents golfeurs, cependant, ont des distances différentes pour chaque tir, donc l'entrée dans STDIN devrait être un ensemble de plages de mètres, disposées par ordre croissant de numéro de club et séparées par des virgules (sans espace).

La sortie devrait être la façon dont je "joue" la partie de golf. Le numéro de retenue doit être spécifié au début de chaque ligne comme Hole #:où se #trouve le trou actuel. Chaque tir qui n'est pas un putt est de la forme suivante: {club,distance of shot,condition of ball,distance to pin}. Les détails de la prise de vue doivent être séparés par des virgules mais pas d'espaces dans l'ordre ci-dessus. Les plans eux-mêmes doivent être écrits par ordre de lecture et séparés par un espace. Une fois que la balle atterrit sur le green, le programme devrait imprimer le nombre de putts que je prends, au format {# putts}. À la fin de chaque ligne, le nombre de photos que j'ai prises sur le trou doit être séparé des autres photos par un espace et imprimé comme(#). Chaque trou doit être sur sa propre ligne et écrit dans l'ordre. Enfin, sur la dernière (dixième) ligne du programme, le nombre total de prises de vue pour le tour doit être imprimé comme Total: # shots.

Il n'y a pas de «stratégie» définie que votre programme doit suivre. Vous pouvez écrire un programme avec la stratégie que vous souhaitez. Exemples de stratégies: maximiser le pourcentage de chances d'atterrir sur le green et maximiser la distance de chaque tir jusqu'à atteindre le trou.

ÉCHANTILLON D'ENTRÉE

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

EXEMPLE DE SORTIE

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

J'avoue que c'est un défi plutôt ambitieux pour un premier article sur CG.SE, donc je serais heureux de parler de comment améliorer ce défi dans les commentaires. Merci de votre aide.

Arcturus
la source
2
J'apprécierais vraiment si, pour nous non-golfeurs, vous n'utilisiez pas autant de termes de golf (par exemple "boîtes à tee" et "décalage horizontal"). :)
kirbyfan64sos
J'ajouterai une liste de termes liés au golf. Lorsque vous jouez au golf, la balle ne va pas toujours directement, alors je viens de dire que la balle va toujours directement vers le trou et n'a donc pas de décalage horizontal.
Arcturus
Disons que la goupille est à 301 yards, et il y a un fairway depuis les 0~299yards, le green depuis les 300~315yards et l'eau depuis les 316~330yards. Quel club sera choisi? Et si l'eau est remplacée par de l'eau brute?
lirtosiast
Idéalement, le programme devrait être en mesure d'élaborer sa propre stratégie.
Arcturus
Qu'entendez-vous par «stratégie optimale»? Minimiser le nombre moyen de coups? Quant au critère de victoire, j'irais avec le code-golf.
lirtosiast

Réponses:

9

Python 2.7: 43 40,5 coups en moyenne

Ceci est mon premier message ici, alors restez avec moi.

Étant donné que l'affiche pensait à traiter cela comme un défi de programmation, pas de golf de code, je l'ai abordé comme un défi de programmation. J'ai essayé de garder ma solution et ma logique de prise de vue simples, mais cela s'est avéré plus laid à mesure que les choses se compliquaient rapidement.

Mon code

Quelques choses à penser en lisant: le programme crée une liste de clubs utilisés appelés `` clubs '' et une liste appelée `` distances '' qui est la distance parcourue par le ballon depuis le tee, hlen est la longueur du trou, d1s est le distance parcourue par chaque tir.

Je définis d'abord le cours. Chaque fairway, eau et longueur verte devait être défini pour que plus tard le programme puisse vérifier l'état de la balle, j'ai donc ajouté des valeurs non entières pour les parties du parcours qui n'existaient pas.

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]

Ici, j'ai défini la logique principale pour choisir un club. Le programme tente de maximiser la distance en choisissant le conducteur pour toutes les longueurs supérieures à la distance maximale du conducteur et choisit un club avec une plage qui contient la distance au trou autrement. Cela nécessite que la plage fournie par l'entrée du club soit continue, c'est-à-dire qu'il n'y ait pas d'espace dans la distance de tir. Une exigence réaliste car on peut frapper un club sans un backswing complet pour limiter la distance de son tir à la distance maximale du prochain club le plus puissant.

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

Ensuite, je définis une fonction de put qui consiste en deux putts pour toutes les longueurs supérieures à 5 mètres du trou et un putt pour 5 et moins. J'inclus également une option pour frapper la balle directement dans le trou appelé «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

Voici où la stratégie devient un peu géniale. Afin de rester simple et d'éviter également de rester coincé dans une boucle de conduite dans l'eau pour laisser tomber la balle à l'emplacement du tir précédent et conduire à nouveau dans l'eau, je reviens en arrière, frappant la balle vers l'arrière avec le coin de sable et demandez ensuite au code de réévaluer le plan cette fois-ci, en espérant tirer juste devant l'eau afin que le plan suivant puisse l'effacer. Cette stratégie est pénalisée par la pénalité brutale mais est efficace pour nettoyer l'eau.

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

Ce programme compte le nombre de coups par trou après que ce trou a été joué. Il ajoute les pénalités pour les tirs bruts et ajoute les pénalités pour avoir frappé dans l'eau en additionnant un tableau appelé eau qui est ajouté après chaque tir d'eau. Cela profite du fait que le fairway mène toujours à l'eau ou au vert pour chaque trou du parcours. Il faudrait le modifier pour les parcours qui contenaient du rough au milieu du fairway.

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

Après l'exécution du code principal, la condition examine les distances sur lesquelles se trouvait la balle pendant le trou et signale la condition de la balle. J'ai rencontré un problème avec la condition à cause de la façon dont j'ai traité le fait de frapper la balle dans l'eau dans le programme principal. Dans le programme, si la balle était frappée dans l'eau, elle était immédiatement replacée à l'endroit où le tir avait été touché. La distance a été enregistrée après que le ballon a été reculé, de sorte que l'état du ballon ne peut pas être «eau». Si vous frappez la balle du tee du trou 4 dans l'eau, le programme imprime la distance entre la balle et le club, mais la longueur jusqu'au trou restera inchangée et la condition sera `` rude '' puisque la balle est lâchée à 0 distance qui est à l'état brut. Vous pouvez décommenter une impression «eau»

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

Voici la partie principale du code qui charge les trous et joue le jeu. Après avoir initialisé certaines conditions, le code exécute un «coup» frappant la balle vers le trou, y compris l'inverse si le trou a été dépassé, jusqu'à ce que de l'eau ou du vert soit rencontré. Si de l'eau est rencontrée, elle s'ajoute à un compteur de pénalités et exécute l'eau du programme et après avoir replacé le ballon à l'endroit où il a été touché. Si le vert est rencontré, put est appelé et le trou est terminé. Après les distances et les clubs sont analysés pour déterminer l'état de chaque tir et les coups sont comptabilisés.

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)

Le code est exécuté comme

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)

et la sortie ressemble à ceci:

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

C'était l'un des scores les plus bas de nombreux essais, avec un plus bas absolu de 26 en 100 000 essais. Mais toujours sous une normale de 34-36 même avec 8 coups sur le trou 4.

Je vais inclure le code que j'ai utilisé pour trouver la distribution des jeux avec les clubs spécifiés ci-dessus.

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

Fonctionnement

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

donne l'histogramme suivant Histogramme de golf

et la moyenne et la médiane peuvent être trouvées en utilisant

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

une moyenne d'environ 43 et une médiane de 41. Pas trop mal pour 9 trous avec une optimisation de tir simple.

Tout est à toi maintenant

Allez-y, copiez et peaufinez mon programme et évaluez-le à l'aide de mes outils afin de réduire le nombre moyen de prises de vue. Faites-moi savoir s'il y a quelque chose que je n'ai pas expliqué ou allez-y et faites une version golfée. Je pense que le meilleur programme serait celui qui a renvoyé les coups moyens les plus bas pour un certain nombre d'entrées de club. Mon code n'est pas la meilleure option pour cela, mais je pensais que je lancerais le bal.

Mise à jour

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

En changeant la logique de l'eau de manière à ce qu'elle tente de frapper le ballon vers l'avant après avoir rencontré de l'eau plutôt que vers l'arrière si le club précédent utilisé n'était pas le coin de sable, elle a amélioré la moyenne à 40,5 et la médiane à 39 après avoir testé avec un millions de courses. Minimum de 23, maximum de 135. Parfois, vous avez de la chance, parfois non. Découvrez le nouvel histogramme.

Histogramme2

Adam
la source