Codifícame un poco de golf

15

Si no ha jugado golf antes, aquí hay una lista de términos relacionados con el golf que uso en esta pregunta

  • Tiro , también llamado golpe : cada vez que se golpea la pelota, este es un tiro.
  • Hoyo : Un campo de golf se divide en hoyos, en los cuales el objetivo es golpear una pelota de un lugar designado a otro en la menor cantidad de golpes posibles.
  • Tee : donde comienzas un hoyo.
  • Pin o bandera : donde terminas un hoyo
  • Fairway , Rough , Water y Green : características en un campo de golf que afectan la forma en que uno juega la pelota en la vida real. (La forma en que afectan el programa se especifica a continuación)

Mañana saldré a jugar al golf, y me doy cuenta de que, a veces, tengo problemas para determinar qué palo usar para alcanzar cierto yardaje. Así que decidí escribir mis palos y sus yardas por disparo.

Primera suposición: Todos los agujeros se deben al norte de sus cajas de te.

Todos estos yardas miden las posibilidades de qué tan al norte viaja la pelota. La pelota viajará una distancia entera aleatoria entre los límites especificados para cada club (inclusive).

Como maestro de golf, ninguno de mis tiros tiene desplazamiento horizontal. Esto significa que todos mis disparos van en línea recta directamente a la bandera.

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)

Como persona que disfruta de la programación, decido que quiero modelar una ronda de golf y establecer una meta para lo bien que quiero hacerlo mañana. Sin embargo, como cualquier programador aficionado, después de diez minutos, me di por vencido y pedí ayuda sobre Stack Overflow (es broma). Aquí hay algunos datos más sobre el curso.

Segunda Asunción: Geografía del Hoyo

  • Todos los números que describen distancias en el curso son enteros.

  • Cada hoyo es una línea recta. La distancia en línea recta entre cada orificio y el pasador (el extremo del orificio) es Length.

  • Las calles son segmentos con longitud definida por flen. El valor indicado flenes el rango de yardas al norte desde el tee donde se encuentra la calle.

  • Los peligros del agua son segmentos que tienen una longitud definida por wlen, que tiene las mismas propiedades que flen.

  • El verde tiene una longitud definida por glen.

  • Todas las partes del curso que no son de calle, agua o verde son ásperas.

Aquí hay una tabla que describe cada hoyo en el campo.

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

Cómo jugar golf (para este programa)

  • Apunte siempre exactamente a la bandera.
  • Golpee la pelota lo más cerca posible del alfiler, tratando de mantener la pelota en la calle o (preferiblemente) en el green.
  • Cuando aterrizas un tiro en el agua, tu próximo tiro debe jugarse desde el mismo lugar que el tiro que cayó al agua.
  • Una vez que la pelota cae en el green, solo se puede usar el putter. Si la pelota cae estrictamente a más de 5 yardas del pin, entonces lo golpeo dos veces. De lo contrario, lo puse una vez.
  • Es posible golpear un tiro más allá del pin.

Puntuación

Mi puntaje en un hoyo es la cantidad de disparos que tomo, más un golpe por cada vez que aterrizo en el mar o en el agua.

El programa

Bien, esas eran muchas reglas, ahora hablemos sobre el programa.

El curso debe definirse como anteriormente en el programa , porque el curso es constante. Sin embargo, diferentes jugadores de golf tienen diferentes distancias para cada tiro, por lo que la entrada a STDIN debe ser un conjunto de rangos de yardas, dispuestos en orden creciente de número de palo y separados por comas (sin espacios en blanco).

La salida debería ser cómo "juego" la ronda de golf. El número de espera debe especificarse al comienzo de cada línea como Hole #:donde #está el hoyo actual. Cada tiro que no es un putt es de la forma siguiente: {club,distance of shot,condition of ball,distance to pin}. Los detalles de la toma deben estar separados por comas pero sin espacios en blanco en el orden anterior. Los disparos en sí deben escribirse en orden de cómo se juegan y separados por un espacio. Una vez que la pelota cae en el green, el programa debe imprimir cuántos putts tomo, en el formato {# putts}. Al final de cada línea, el número de disparos que hice en el hoyo debe estar separado de los otros disparos por un espacio e impreso como(#). Cada hoyo debe estar en su propia línea y escrito en orden. Finalmente, en la última (décima) línea del programa, el número total de disparos para la ronda debe imprimirse como Total: # shots.

No hay una "estrategia" establecida que su programa deba tomar. Puede escribir un programa con cualquier estrategia que desee. Las estrategias de ejemplo incluyen maximizar el porcentaje de posibilidades de aterrizar en el green y maximizar la distancia de cada disparo hasta llegar al hoyo.

ENTRADA DE MUESTRA

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

SALIDA DE MUESTRA

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

Admito que este es un desafío bastante ambicioso para una primera publicación en CG.SE, por lo que me alegraría hablar sobre cómo mejorar este desafío en los comentarios. Gracias por tu ayuda.

Arcturus
fuente
2
Realmente agradecería que, para nosotros, los no golfistas, no utilizasen tantos términos de golf (por ejemplo, "tee boxes" y "desplazamiento horizontal"). :)
kirbyfan64sos
Agregaré una lista de términos relacionados con el golf. Cuando se juega al golf, la pelota no siempre va en línea recta, por lo que acabo de decir que la pelota siempre va directamente hacia el hoyo y, por lo tanto, no tiene ningún desplazamiento horizontal.
Arcturus
Digamos que el pin está a 301 yardas, y hay calle de 0~299yardas, verde de 300~315yardas y agua de 316~330yardas. ¿Qué club será elegido? ¿Qué pasa si el agua es reemplazada por agua?
lirtosiast
Idealmente, el programa debería poder proponer su propia estrategia.
Arcturus
¿Qué quiere decir con "estrategia óptima"? ¿Minimizar el número promedio de golpes? En cuanto al criterio ganador, iría con code-golf.
lirtosiast

Respuestas:

9

Python 2.7: 43 40.5 tiros promedio

Esta es mi primera publicación aquí, así que tengan paciencia conmigo.

Como el póster estaba pensando en tratar esto como un desafío de programación, no como un código de golf, lo aborde como un desafío de programación. Intenté mantener mi solución y la lógica de disparo simple, pero resultó más feo ya que las cosas se complicaron rápidamente.

Mi código

Algunas cosas para pensar mientras lee: el programa crea una lista de palos usados ​​llamados 'clubes', y una lista llamada 'distancias' que es la distancia que la pelota ha recorrido desde el tee, hlen es la longitud del hoyo, d1s es el la distancia que recorre cada disparo.

Primero defino el curso. Cada calle, el agua y la longitud verde tenían que definirse para que luego el programa pudiera verificar la condición de la pelota, por lo que agregué valores no enteros para las partes del curso que no existían.

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]

Aquí definí la lógica principal para elegir un club. El programa intenta maximizar la distancia eligiendo el controlador para todas las longitudes mayores que la distancia máxima del conductor y elige un palo con un rango que contenga la distancia al hoyo de lo contrario. Esto requiere que el rango proporcionado por la entrada del palo sea continuo, es decir, sin espacios en la distancia de disparo. Un requisito realista ya que uno puede golpear un palo sin un backswing completo para limitar la distancia de su disparo a la distancia máxima del siguiente palo más poderoso.

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

A continuación, defino una función put que consiste en dos putts para todas las longitudes mayores de 5 yardas al hoyo y un putt para 5 y menos. También incluyo una opción para golpear la pelota directamente en el hoyo llamada '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

Aquí es donde la estrategia se pone un poco cobarde. Para que sea simple y también evite quedar atrapado en un ciclo de conducción en el agua solo para dejar caer la pelota en el lugar del disparo anterior y conducir nuevamente al agua, en realidad retrocedo, golpeando la pelota hacia atrás con la cuña de arena y luego haga que el código evalúe el disparo nuevamente, esta vez con suerte disparando justo en frente del agua para que el próximo disparo pueda despejarlo. Esta estrategia es penalizada por la penalización aproximada, pero es efectiva para limpiar el agua.

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

Este programa cuenta el número de golpes por hoyo después de que se haya jugado ese hoyo. Agrega las penalizaciones por los disparos en bruto y las penalizaciones por golpear el agua sumando una matriz llamada agua que se agrega después de cada disparo de agua. Esto aprovecha el hecho de que la calle siempre conduce al agua o al green por cada hoyo en el campo. Tendría que cambiarse para cursos que contenían rudo en el medio de la calle.

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

Después de que se ejecuta el código principal, la condición observa las distancias a las que se encontraba la pelota durante el hoyo e informa la condición de la pelota. Me encontré con un problema con la condición debido a la forma en que traté golpear la pelota en el agua en el programa principal. En el programa, si la pelota fue golpeada en el agua, inmediatamente se movió de regreso al lugar donde fue golpeado. La distancia se registró después de que la pelota se movió hacia atrás, por lo que la condición de la pelota no puede ser 'agua'. Si golpea la pelota desde el tee en el hoyo 4 hacia el agua, el programa imprime la distancia que golpeó la pelota y el palo, pero la longitud hasta el hoyo permanecerá sin cambios y la condición será 'rugosa' ya que la pelota se cae a 0 distancia que está en bruto. Puede descomentar una impresión 'agua'

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

Aquí está la parte principal del código que carga los agujeros y juega el juego. Después de inicializar algunas condiciones, el código ejecuta 'golpe' golpeando la pelota hacia el hoyo, incluso en reversa si el hoyo fue sobrepasado, hasta que se encuentre agua o verde. Si se encuentra agua, se agrega a un contador de penalización y ejecuta el agua del programa y después de mover la pelota de regreso al lugar desde donde fue golpeada. Si se encuentra el green, se llama put y se termina el hoyo. Después de analizar las distancias y los palos para determinar la condición de cada disparo y los disparos se cuentan.

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)

El código se ejecuta como

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)

y la salida se ve así:

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

Este fue uno de los puntajes más bajos de muchos ensayos, con un puntaje absoluto más bajo de 26 en 100,000 carreras. Pero aún bajo un par típico de 34-36 incluso con 8 golpes en el hoyo 4.

Incluiré el código que usé para encontrar la distribución de juegos con los clubes especificados anteriormente.

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

Corriendo

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

da el siguiente histograma Histograma de golf

y la media y la mediana se pueden encontrar usando

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

una media de aproximadamente 43 y una mediana de 41. No está mal para 9 hoyos con la optimización de disparo simple.

Ahora es todo tuyo

Continúe, copie y modifique mi programa y evalúelo usando mis herramientas para reducir el número promedio de disparos. Avíseme si hay algo que no haya tenido en cuenta o continúe y haga una versión de golf. Creo que el mejor programa sería aquel que devolviera los tiros promedio más bajos para una cantidad de entradas del club. Mi código no es la mejor opción para eso, pero pensé en poner en marcha la pelota.

Actualizar

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

Al cambiar la lógica del agua para que intente golpear la pelota hacia adelante una pequeña cantidad después de encontrar agua en lugar de hacia atrás si el palo anterior utilizado no era la cuña de arena, mejoró la media a 40.5 y la mediana a 39 después de probar con una millones de carreras. Mínimo de 23, máximo de 135. A veces tienes suerte, a veces no. Mira el nuevo histograma.

Histograma2

Adán
fuente