Codifique-me um pouco de golfe


15

Se você nunca jogou golfe antes, aqui está uma lista de termos relacionados ao golfe que eu uso nesta pergunta

  • Chute , também chamado de tacada : Toda vez que a bola é atingida, é um chute.
  • Buraco : Um campo de golfe é dividido em buracos, nos quais o objetivo é acertar uma bola de um local designado para outro com o menor número de arremessos possível.
  • Tee : Onde você começa um buraco.
  • Alfinete ou bandeira : onde você termina um buraco
  • Fairway , Rough , Water e Green : recursos em um campo de golfe que afetam a maneira como alguém joga a bola na vida real. (Como eles afetam o programa é especificado abaixo)

Amanhã vou jogar golfe e acho que, às vezes, tenho dificuldade em descobrir qual clube usar para atingir uma certa distância. Então decidi anotar meus tacos e suas jardas por tiro.

Primeira Suposição: Todos os buracos estão ao norte de suas caixas de tee.

Todas essas jardas medem as possibilidades de quão longe a bola viaja para o norte. A bola percorrerá uma distância inteira aleatória entre os limites especificados para cada clube (inclusive).

Como jogador mestre, nenhum dos meus tiros tem mudança horizontal. Isso significa que todas as minhas fotos vão em linha reta diretamente para a bandeira.

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 uma pessoa que gosta de programar, decido que quero modelar uma partida de golfe e definir uma meta de quão bem quero amanhã. No entanto, como qualquer programador amador, depois de dez minutos, desisti e pedi ajuda no Stack Overflow (apenas brincando). Aqui estão mais alguns dados sobre o curso.

Segunda Suposição: Geografia do Buraco

  • Todos os números que descrevem distâncias no percurso são inteiros.

  • Cada buraco é uma linha reta. A distância da linha reta entre cada orifício e o pino (a extremidade do orifício) é Length.

  • Fairways são segmentos com comprimento definido por flen. O valor listado paraflen é o intervalo de jardas ao norte do tee onde fica o fairway.

  • Riscos de água são segmentos com comprimento definido por wlen, que possui as mesmas propriedades de flen.

  • O verde tem um comprimento definido por glen.

  • Todas as partes do percurso que não são fairway, água ou verde são irregulares.

Aqui está um gráfico descrevendo cada furo no percurso.

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

Como jogar golfe (para este programa)

  • Sempre aponte exatamente para a bandeira.
  • Bata na bola o mais próximo possível do pino, tentando manter a bola no fairway ou (de preferência) no green.
  • Quando você pousa um tiro na água, seu próximo tiro deve ser jogado do mesmo local que o tiro que entrou na água.
  • Quando a bola cair no verde, somente o taco poderá ser usado. Se a bola cair estritamente a mais de 5 jardas do pino, eu toco duas vezes. Caso contrário, eu coloquei uma vez.
  • É possível acertar um tiro após o pino.

Pontuação

Minha pontuação em um buraco é o número de disparos que dou, mais um golpe por cada vez que aterro em águas agitadas ou na água.

O programa

Ok, isso foi um monte de regras, agora vamos falar sobre o programa.

O curso deve ser definido como acima no programa , porque o curso é constante. Golfistas diferentes, no entanto, têm distâncias diferentes para cada tacada, portanto a entrada para o STDIN deve ser um conjunto de faixas de jardas, organizadas em ordem crescente do número do clube e separadas por vírgulas (sem espaço em branco).

A saída deve ser como eu "jogo" a partida de golfe. O número de espera deve ser especificado no início de cada linha como Hole #:onde #está o furo atual. Cada tiro que não é um putt é da forma seguinte: {club,distance of shot,condition of ball,distance to pin}. Os detalhes da foto devem ser separados por vírgulas, mas sem espaços em branco na ordem acima. As tomadas em si devem ser escritas na ordem em que são tocadas e separadas por um espaço. Quando a bola cair no verde, o programa deve imprimir quantos tacadas eu tomo, no formato . Cada furo deve estar em sua própria linha e escrito em ordem. Finalmente, na última (décima) linha do programa, o número total de tiros para a rodada deve ser impresso como .{# putts} . No final de cada linha, o número de fotos que tirei no buraco deve ser separado das outras fotos por um espaço e impresso como(#)Total: # shots

Não existe uma "estratégia" definida que seu programa precise executar. Você pode escrever um programa com qualquer estratégia que desejar. Estratégias de exemplo incluem maximizar a chance percentual de aterrissar no verde e maximizar a distância de cada tiro até chegar ao buraco.

ENTRADA DE AMOSTRA

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

SAÍDA DA AMOSTRA

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 é um desafio bastante ambicioso para um primeiro post no CG.SE, por isso ficaria feliz em falar sobre como melhorar esse desafio nos comentários. Obrigado pela ajuda.


2
Eu realmente apreciaria se, para nós, não jogadores, você não usasse tantos termos de golfe (por exemplo, "caixas de chá" e "deslocamento horizontal"). :)
kirbyfan64sos

Vou adicionar uma lista de termos relacionados ao golfe. Ao jogar golfe, a bola nem sempre vai reta, então eu apenas disse que a bola sempre vai diretamente em direção ao buraco e, portanto, não tem nenhum deslocamento horizontal.
Arcturus

Digamos que o pino esteja a 301 jardas, e haja fairway de 0~299jardas, verde de 300~315jardas e água de 316~330jardas. Qual clube será escolhido? E se a água for substituída por áspera?
lirtosiast 13/09/2015

Idealmente, o programa deve ser capaz de apresentar sua própria estratégia.
Arcturus

O que você quer dizer com "estratégia ideal"? Minimizar o número médio de traços? Quanto ao critério de ganhar, eu iria com o código-golfe.
lirtosiast 13/09/15

Respostas:


9

Python 2.7: 43 40.5 disparos em média

Este é o meu primeiro post aqui, então tenha paciência comigo.

Como o pôster estava pensando em tratar isso como um desafio de programação, não como um código de golfe, eu o enfrentei como um desafio de programação. Tentei manter minha solução e simplificar a lógica, mas ficou mais feia à medida que as coisas se complicavam rapidamente.

My Code

Algumas coisas em que pensar durante a leitura: o programa cria uma lista de clubes usados ​​chamados 'clubes' e uma lista chamada 'distâncias' que são a distância que a bola percorreu do tee, hlen é o comprimento do buraco, d1s é o distância que cada tiro viaja.

Primeiro eu defino o curso. Cada fairway, água e comprimento do verde precisavam ser definidos para que, posteriormente, o programa pudesse verificar a condição da bola, então adicionei valores não inteiros para as partes do percurso que não existiam.

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]

Aqui eu defini a lógica principal para escolher um clube. O programa tenta maximizar a distância escolhendo o motorista para todos os comprimentos maiores que a distância máxima do motorista e escolhe um clube com alcance que contenha a distância até o buraco. Isso requer que o alcance fornecido pela entrada do taco seja contínuo, ou seja, não há lacunas na distância do arremesso. Um requisito realista, já que é possível atingir um clube sem um retrocesso completo para limitar a distância do tiro à distância máxima do próximo clube mais 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

Em seguida, defino uma função de put com dois putts para todos os comprimentos maiores que 5 jardas do buraco e um putt com 5 ou menos. Também incluo uma opção para acertar a bola diretamente no buraco chamado '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

Aqui é onde a estratégia fica um pouco descolada. Para mantê-lo simples e também evitar ficar preso em um ciclo de dirigir para a água e soltar a bola no local do tiro anterior e entrar na água novamente, na verdade eu retrocedo, batendo na bola para trás com a cunha de areia e faça com que o código avalie o tiro novamente desta vez, esperançosamente, atirando apenas em frente à água para que o próximo tiro possa limpá-lo. Essa estratégia é penalizada pela penalidade aproximada, mas é eficaz para limpar a água.

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

Este programa conta o número de tacadas por buraco depois que esse buraco foi jogado. Ele adiciona as penalidades para os tiros brutos e adiciona as penalidades por bater na água, somando uma matriz chamada água que é anexada após cada tiro na água. Isso tira proveito do fato de que o fairway sempre leva à água ou ao verde de todos os buracos no percurso. Teria que ser mudado para cursos que continham bruto no meio do 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

Depois que o código principal é executado, a condição examina as distâncias em que a bola estava durante o buraco e relata a condição da bola. Eu tive um problema com a condição por causa da maneira como tratava de bater a bola na água no programa principal. No programa, se a bola foi atingida na água, ela foi imediatamente movida de volta ao local onde o tiro foi atingido. A distância foi registrada após a bola ser movida para trás, para que a condição da bola não possa ser 'água'. Se você acertar a bola do tee no buraco 4 na água, o programa imprime a distância em que você bate na bola e no taco, mas o comprimento até o buraco permanecerá inalterado e a condição será "áspera" desde que a bola caia no chão. 0 distância que está em bruto. Você pode remover o comentário de uma 'água' impressa

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

Aqui está a parte principal do código que carrega os buracos e joga o jogo. Depois de inicializar algumas condições, o código executa um 'golpe' batendo na bola em direção ao buraco, incluindo inverter se o buraco foi ultrapassado, até encontrar água ou verde. Se a água for encontrada, ela adiciona um contador de penalidades e executa a água do programa e depois de mover a bola de volta para o local em que foi atingida. Se o verde for encontrado, o put será chamado e o furo será encerrado. Depois que as distâncias e tacos são analisados ​​para determinar a condição de cada tiro e os tiros são computados.

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)

O código é executado 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)

e a saída é assim:

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

Essa foi uma das pontuações mais baixas de muitos ensaios, com uma menor absoluta de 26 em 100.000 execuções. Mas ainda abaixo de um par típico de 34-36, mesmo com 8 tacadas no buraco 4.

Incluirei o código que usei para encontrar a distribuição de jogos com os clubes especificados acima.

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

Corrida

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

dá o seguinte histograma Histograma de golfe

e a média e mediana podem ser encontradas usando

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

uma média de cerca de 43 e uma mediana de 41. Nada mal para 9 buracos com otimização simples do tiro.

É todo seu agora

Vá em frente, copie e aprimore meu programa e avalie-o usando minhas ferramentas para diminuir o número médio de fotos. Informe-me se houver algo que não expliquei ou vá em frente e crie uma versão para golfe. Eu acho que o melhor programa seria aquele que retornasse a menor média de disparos para várias contribuições do clube. Meu código não é a melhor opção para isso, mas pensei em fazer a bola rolar.

Atualizar

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

Ao alterar a lógica da água para tentar acertar a bola um pouco depois de encontrar a água em vez de para trás, se o clube anterior usado não fosse a cunha de areia, melhorou a média para 40,5 e a mediana para 39 após o teste com um milhões de execuções. Mínimo de 23, máximo de 135. Às vezes você tem sorte, às vezes não. Confira o novo histograma.

Histograma2

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.