给我打些高尔夫


15

如果您以前从未打过高尔夫球,这里是我在此问题中使用的高尔夫相关术语的列表

  • 击球,也称为击球:每次击球,即为击球。
  • :高尔夫球场被分成多个洞,目标是以尽可能少的击球次数将球从一个指定位置打到另一个位置。
  • T恤:从哪里开始打洞。
  • 固定国旗:完成孔的位置
  • 球道粗糙果岭:高尔夫球场上的功能会影响人们在现实生活中的打球方式。(下面说明它们如何影响程序)

我明天要去打高尔夫球,我发现有时候我很难弄清楚该用哪个球杆打一定的码数。所以我决定写下我的球杆及其杆数。

第一个假设:所有孔都位于发球盒的北部。

所有这些码数都测量了球向北移动多远的可能性。球将在为每个球杆(包括两端)指定的边界之间移动一个随机整数距离。

作为一名高水平的高尔夫球手,我的投篮没有任何水平移动。这意味着我所有的投篮都直接在旗线上成一直线。

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

我将添加与高尔夫相关的条款列表。打高尔夫球时,球并不一定总是直行,所以我只是说球总是直接朝着洞前进,因此没有任何水平移动。
Arcturus

假设图钉位于301码处,并且距离0~299院子有球道,距离院子有绿色,300~315距离院子有水316~330。将选择哪个俱乐部?如果用粗糙的水代替水怎么办?
lirtosiast

理想情况下,该程序应该能够提出自己的策略。
Arcturus

“最佳策略”是什么意思?减少平均笔划数?至于获胜标准,我会选择代码高尔夫。
lirtosiast

Answers:


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

在打完该孔后,此程序将计算每个孔的笔划数。它通过在每个注水之后追加一个称为water的数组来求和,从而增加了对粗射的惩罚,并添加了对射入水中的惩罚。这利用了这样的事实:球道在路线中的每个孔处总是引水或引向果岭。对于球道中部崎courses不平的球场,必须对其进行更改。

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

这是加载漏洞并玩游戏的代码的主要部分。初始化某些条件后,代码将“击中”球击向球洞,如果球洞被击球,则反向击球,直到遇到水或果岭为止。如果遇到水,它将添加到罚分计数器并运行程序水,然后将球移回被击中的位置。如果遇到绿色,则调用put并终止该孔。在对距离和球杆进行分析以确定每个击球的状况之后,再对这些击球进行计数。

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

这是许多试验中最低的分数之一,绝对最低的是100,000次运行中的26次。但即使在第4洞打了8杆,仍然低于标准杆34-36。

我将包含用于查找上述指定俱乐部的游戏分布的代码。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.