前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >日拱一卒,伯克利Python入门项目,手把手教你写个简单ai

日拱一卒,伯克利Python入门项目,手把手教你写个简单ai

作者头像
TechFlow-承志
发布2022-08-26 14:42:56
7070
发布2022-08-26 14:42:56
举报
文章被收录于专栏:TechFlow

作者 | 梁唐

大家好,日拱一卒,我是梁唐。

咱们继续来聊聊伯克利的CS61A,这次分享的是这门课的第一个project。虽然说是project,但其实代码量不大。难度也不高,需要了解一点Python的基础语法和基本的函数式编程的思想。如果对于函数式编程还不太熟悉的,可以去历史记录里回看一下上一篇文章。

整个项目的代码量并不大,但代码质量很高,非常严谨符合规范。非常适合新手学习,既可以加深理解,也可以提高代码质量和设计规范。

由于原文档为英文,对于一些小伙伴来说可能比较吃力。所以我把它做了一个翻译,有些地方加上了一些说明。想要查看完整的代码+实现的同学可以点击【阅读原文】访问我的GitHub仓库。

项目原始文档:https://inst.eecs.berkeley.edu/~cs61a/sp18/proj/hog/

简介

我们需要实现一个小游戏Hog的核心代码,Hog是两名玩家轮流掷骰子拼点数的比拼游戏。玩家每次可以选择掷0至10颗骰子,不同的策略可以获取不同的积分,积分先到100的玩家获胜。

计算骰子点数有以下规则:

  • Pig Out:如果玩家掷出的骰子当中有骰子是1点,那么该回合只能获取1点,否则获取所有骰子点数之和
    • 案例1:玩家掷出7枚骰子,其中第三枚为1点,玩家此回合获取1分
    • 案例2:玩家掷出4枚骰子,均为3点,玩家此回合获取12分
  • Free Bacon:当玩家选择掷出0枚骰子时,触发Free Bacon规则。获取积分为对手玩家积分个位和十位分差绝对值再加2。
    • 案例1:对手有42分,free bacon可以获得|4-2|+2=4分
    • 案例2:对手有48分,free bacon可以获得|4-8|+2=6分
    • 案例3:对手有7分,free bacon可以获得|0-7|+2=9分
  • Swine Swap:当两名玩家积分均大于1,并且其中一名为另外一名倍数时,两人积分互换
    • 案例1:玩家0当前有37分,对手有92分,此回合获得9分之后变成了46分,92是46的两倍。触发Swine Swap,于是两人分数互换
    • 案例2:玩家0当前有91分,对手有37分,此回合获得20分,积分变成111。但由于111是37的三倍,于是触发Swine Swap,两人积分互换,玩家1拥有超过100分,玩家1获胜

整个项目的框架已经搭好,只需要我们按照要求实现其中一些具体的函数。

最后当我们实现完成所有功能以及策略,可以和指定的策略进行比拼,统计获胜率。全项目一共有三个阶段11题,可以理解成一共有11个函数需要我们实现。

每一个阶段完成之后,都会有一个阶段性成果。难度不大,但很有意思,非常推荐刚入门的同学练习。

阶段1

阶段1主要实现游戏功能的主体,完成之后我们可以通过项目里提供的gui进行双人比赛。

Problem 1:

实现roll_dice函数,roll_dice函数拥有两个参数,第一个参数是一个整数,表示投掷的次数。第二个参数是一个函数,表示骰子的类型。返回投掷num_rolls次之后获得的积分,当出现Pig Out获得1分,否则为骰子点数之和。

调用dice()函数获取骰子掷出的点数,必须要投掷num_rolls次骰子,即使中途出现了Pig Out。

代码语言:javascript
复制
def roll_dice(num_rolls, dice=six_sided):
    """Simulate rolling the DICE exactly NUM_ROLLS > 0 times. Return the sum of
    the outcomes unless any of the outcomes is 1. In that case, return 1.

    num_rolls:  The number of dice rolls that will be made.
    dice:       A function that simulates a single dice roll outcome.
    """
    # These assert statements ensure that num_rolls is a positive integer.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls > 0, 'Must roll at least once.'
    # BEGIN PROBLEM 1
    "*** YOUR CODE HERE ***"
    # END PROBLEM 1

我们只需要使用循环重复执行num_rolls次,然后判断一下当中有没有出现1即可。

代码语言:javascript
复制
def roll_dice(num_rolls, dice=six_sided):
    """Simulate rolling the DICE exactly NUM_ROLLS > 0 times. Return the sum of
    the outcomes unless any of the outcomes is 1. In that case, return 1.

    num_rolls:  The number of dice rolls that will be made.
    dice:       A function that simulates a single dice roll outcome.
    """
    # These assert statements ensure that num_rolls is a positive integer.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls > 0, 'Must roll at least once.'
    # BEGIN PROBLEM 1
    "*** YOUR CODE HERE ***"
    ret = 0
    pigout = False
    for _ in range(num_rolls):
        score = dice()
        if score == 1:
            pigout = True
            continue
        ret += score
    return 1 if pigout else ret
    # END PROBLEM 1

Problem 2:

实现free_bacon函数,返回当掷出0颗骰子时获取的点数。输入为对手玩家的分数,可以假设分数一定小于100,如果分数小于10,那么十位视作0。

代码语言:javascript
复制
def free_bacon(score):
    """Return the points scored from rolling 0 dice (Free Bacon).

    score:  The opponent's current score.
    """
    assert score < 100, 'The game should be over.'
    # BEGIN PROBLEM 2
    "*** YOUR CODE HERE ***"
    # END PROBLEM 2

计算出分数的十位和个位,作差之后求绝对值,再加上2即可。

代码语言:javascript
复制
def free_bacon(score):
    """Return the points scored from rolling 0 dice (Free Bacon).

    score:  The opponent's current score.
    """
    assert score < 100, 'The game should be over.'
    # BEGIN PROBLEM 2
    "*** YOUR CODE HERE ***"
    return abs(score // 10 - score % 10) + 2
    # END PROBLEM 2

Problem 3:

实现take_turn函数,给定num_rollsdice,返回玩家此回合获得的分数。

你需要先实现free_bacon函数,你实现的take_turn函数当中必须要调用roll_dicefree_bacon函数。

代码语言:javascript
复制
def take_turn(num_rolls, opponent_score, dice=six_sided):
    """Simulate a turn rolling NUM_ROLLS dice, which may be 0 (Free Bacon).
    Return the points scored for the turn by the current player.

    num_rolls:       The number of dice rolls that will be made.
    opponent_score:  The total score of the opponent.
    dice:            A function that simulates a single dice roll outcome.
    """
    # Leave these assert statements here; they help check for errors.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls >= 0, 'Cannot roll a negative number of dice in take_turn.'
    assert num_rolls <= 10, 'Cannot roll more than 10 dice.'
    assert opponent_score < 100, 'The game should be over.'
    # BEGIN PROBLEM 3
    "*** YOUR CODE HERE ***"
    # END PROBLEM 3

根据num_rolls判断是投掷骰子,还是执行free bacon。只要看懂了题意,同样没有难度。

代码语言:javascript
复制
def take_turn(num_rolls, opponent_score, dice=six_sided):
    """Simulate a turn rolling NUM_ROLLS dice, which may be 0 (Free Bacon).
    """
    # Leave these assert statements here; they help check for errors.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls >= 0, 'Cannot roll a negative number of dice in take_turn.'
    assert num_rolls <= 10, 'Cannot roll more than 10 dice.'
    assert opponent_score < 100, 'The game should be over.'
    # BEGIN PROBLEM 3
    "*** YOUR CODE HERE ***"
    if num_rolls == 0:
        return free_bacon(opponent_score)
    else:
        return roll_dice(num_rolls, dice)
    # END PROBLEM 3

Problem 4:

实现is_swap函数,返回当前玩家的分数是否会触发交换

代码语言:javascript
复制
def is_swap(score0, score1):
    """Return whether one of the scores is an integer multiple of the other."""
    # BEGIN PROBLEM 4
    "*** YOUR CODE HERE ***"
    # END PROBLEM 4

考虑分数为0的情况,分数为0时不应该触发is_swap

判断两个人的分数是否成倍数关系即可:

代码语言:javascript
复制
def is_swap(score0, score1):
    """Return whether one of the scores is an integer multiple of the other."""
    # BEGIN PROBLEM 4
    "*** YOUR CODE HERE ***"
    if score0 <= 1 or score1 <= 1:
        return False
    return score0 % score1 == 0 or score1 % score0 == 0
    # END PROBLEM 4

Problem 5:

实现play函数,它模拟了一整局游戏。玩家轮流行动,每次行动时都会使用它们指定的策略。如玩家0,使用的策略就是strategy0,直到有玩家分数达到目标goal。当游戏结束时,按顺序返回两名玩家的分数,玩家0在先,玩家1在后。

提示:

  • 必须要调用刚刚开发的函数,需要调用take_turn并传入对应的三个参数
  • 每一回合只能调用一次take_turn
  • 考虑所有边界情况
  • 当回合结束时,你可以调用函数other,获取下一名玩家
  • 你可以无视say参数
  • strategy0strategy1本身都是函数,传入当前玩家和对手玩家的分数之后,它会返回玩家需要投掷的骰子数量。每回合只能调用strategy一次。
代码语言:javascript
复制
def play(strategy0, strategy1, score0=0, score1=0, dice=six_sided,
         goal=GOAL_SCORE, say=silence):
    """Simulate a game and return the final scores of both players, with Player
    0's score first, and Player 1's score second.

    A strategy is a function that takes two total scores as arguments (the
    current player's score, and the opponent's score), and returns a number of
    dice that the current player will roll this turn.

    strategy0:  The strategy function for Player 0, who plays first.
    strategy1:  The strategy function for Player 1, who plays second.
    score0:     Starting score for Player 0
    score1:     Starting score for Player 1
    dice:       A function of zero arguments that simulates a dice roll.
    goal:       The game ends and someone wins when this score is reached.
    say:        The commentary function to call at the end of the first turn.
    """
    player = 0  # Which player is about to take a turn, 0 (first) or 1 (second)
    # BEGIN PROBLEM 5
    "*** YOUR CODE HERE ***"
    # END PROBLEM 5

照着题目意思实现逻辑即可,基本上没有难度。

代码语言:javascript
复制
def play(strategy0, strategy1, score0=0, score1=0, dice=six_sided,
         goal=GOAL_SCORE, say=silence):
    """Simulate a game and return the final scores of both players, with Player
        0's score first, and Player 1's score second.
    """
    player = 0  # Which player is about to take a turn, 0 (first) or 1 (second)
    # BEGIN PROBLEM 5
    "*** YOUR CODE HERE ***"
    while True:
        if score0 >= goal or score1 >= goal:
            break
        if player == 0:
            num = strategy0(score0, score1)
            score0 += take_turn(num, score1, dice)
        else:
            num = strategy1(score1, score0)
            score1 += take_turn(num, score0, dice)
        if is_swap(score0, score1):
            score0, score1 = score1, score0
        player = other(player)
    # END PROBLEM 5
    return score0, score1

阶段2

阶段2主要实现一些评论函数,可以在一些关键节点打印出日志给玩家以提示。

类似这种"22 points! That's the biggest gain yet for Player 1.",玩家1打破了记录,获得了22分。

老师给了评论函数的一个例子:

代码语言:javascript
复制
def say_scores(score0, score1):
    """A commentary function that announces the score for each player."""
    print("Player 0 now has", score0, "and Player 1 now has", score1)
    return say_scores

注意这个say_scores函数最后返回了它自己,我们每一轮调用的都是同样的函数。

announce_lead_changes是使用函数式编程返回领先玩家的例子:

代码语言:javascript
复制
def announce_lead_changes(previous_leader=None):
    """Return a commentary function that announces lead changes.

    >>> f0 = announce_lead_changes()
    >>> f1 = f0(5, 0)
    Player 0 takes the lead by 5
    >>> f2 = f1(5, 12)
    Player 1 takes the lead by 7
    >>> f3 = f2(8, 12)
    >>> f4 = f3(8, 13)
    >>> f5 = f4(15, 13)
    Player 0 takes the lead by 2
    """
    def say(score0, score1):
        if score0 > score1:
            leader = 0
        elif score1 > score0:
            leader = 1
        else:
            leader = None
        if leader != None and leader != previous_leader:
            print('Player', leader, 'takes the lead by', abs(score0 - score1))
        return announce_lead_changes(leader)
    return say

在开发之前,你需要先看懂下面这个both函数:

代码语言:javascript
复制
def both(f, g):
    """Return a commentary function that says what f says, then what g says.

    >>> h0 = both(say_scores, announce_lead_changes())
    >>> h1 = h0(10, 0)
    Player 0 now has 10 and Player 1 now has 0
    Player 0 takes the lead by 10
    >>> h2 = h1(10, 6)
    Player 0 now has 10 and Player 1 now has 6
    >>> h3 = h2(6, 18) # Player 0 gets 8 points, then Swine Swap applies
    Player 0 now has 6 and Player 1 now has 18
    Player 1 takes the lead by 12
    """
    def say(score0, score1):
        return both(f(score0, score1), g(score0, score1))
    return say

它接收两个评论函数fg,并且返回一个全新的评论函数。这个返回的函数中的fg这两个参数,又是调用fg之后获得的结果。

这一段涉及函数式编程的嵌套,会显得有些绕,最好根据这个例子理解:

代码语言:javascript
复制
>>> h0 = both(say_scores, announce_lead_changes())
>>> h1 = h0(10, 0)
Player 0 now has 10 and Player 1 now has 0
Player 0 takes the lead by 10
>>> h2 = h1(10, 6)
Player 0 now has 10 and Player 1 now has 6
>>> h3 = h2(6, 18) # Player 0 gets 8 points, then Swine Swap applies
Player 0 now has 6 and Player 1 now has 18
Player 1 takes the lead by 12

Problem 6:

更新的你play函数,使得在每一个回合的结束都调用一下评论函数。评论函数调用之后会返回一个新的评论函数用在下一个回合。

比如说say(score0, score1)将在第一个回合结束时调用,它会返回另外一个评论函数,返回的这个函数需要在第二个回合调用。也就是说每个回合调用的评论函数都是上一个回合返回的。

看懂了say函数之后,我们只需要在play函数当中加上say方法的调用即可。

由于say函数是一个高阶函数,它运行之后会返回一个函数,注意这点,不难实现。

代码语言:javascript
复制
def play(strategy0, strategy1, score0=0, score1=0, dice=six_sided,
         goal=GOAL_SCORE, say=silence):
    """Simulate a game and return the final scores of both players, with Player
    0's score first, and Player 1's score second.
    """
    player = 0  # Which player is about to take a turn, 0 (first) or 1 (second)
    # BEGIN PROBLEM 5
    "*** YOUR CODE HERE ***"
    while True:
        if score0 >= goal or score1 >= goal:
            break
        if player == 0:
            num = strategy0(score0, score1)
            score0 += take_turn(num, score1, dice)
        else:
            num = strategy1(score1, score0)
            score1 += take_turn(num, score0, dice)
        if is_swap(score0, score1):
            score0, score1 = score1, score0
        player = other(player)
        say = say(score0, score1)
    # END PROBLEM 5
    return score0, score1

Problem 7:

实现announce_highest函数,它是一个高阶函数,返回一个评论函数。这个评论函数会在指定玩家单个回合获得新的最大得分时打印日志。要实现这点,它必须要计算当前得分,并且和历史最高得分进行比较。函数的第一个参数who指定了需要跟踪记录的玩家,其他玩家的得分可以忽略。

需要注意,在打印得分时,需要注意单复数。得分大于1时,使用points,只有1分时,使用point

代码语言:javascript
复制
def announce_highest(who, previous_high=0, previous_score=0):
    """Return a commentary function that announces when WHO's score
    increases by more than ever before in the game.

    >>> f0 = announce_highest(1) # Only announce Player 1 score gains
    >>> f1 = f0(11, 0)
    >>> f2 = f1(11, 1)
    1 point! That's the biggest gain yet for Player 1
    >>> f3 = f2(20, 1)
    >>> f4 = f3(5, 20) # Player 1 gets 4 points, then Swine Swap applies
    19 points! That's the biggest gain yet for Player 1
    >>> f5 = f4(20, 40) # Player 0 gets 35 points, then Swine Swap applies
    20 points! That's the biggest gain yet for Player 1
    >>> f6 = f5(20, 55) # Player 1 gets 15 points; not enough for a new high
    """
    assert who == 0 or who == 1, 'The who argument should indicate a player.'
    # BEGIN PROBLEM 7
    "*** YOUR CODE HERE ***"
    # END PROBLEM 7

这题稍稍有些难,多关注一下注释中的测试样例。

主要思路是在announce_highest中实现一个函数,我这里函数名叫say。然后通过这两个函数的互相调用来实现功能。

如果想不明白可以看一下公开课的视频,老师专门开了一个小节讲这题。

代码语言:javascript
复制
def announce_highest(who, previous_high=0, previous_score=0):
    """Return a commentary function that announces when WHO's score
    increases by more than ever before in the game.
    """
    assert who == 0 or who == 1, 'The who argument should indicate a player.'
    # BEGIN PROBLEM 7
    "*** YOUR CODE HERE ***"
    def say(score0, score1):
        if who == 0:
            gain = score0 - previous_score
            new_score = score0
        else:
            gain = score1 - previous_score
            new_score = score1

        new_high = previous_high
        if gain > previous_high:
            new_high = gain
            print("{} {}! That's the biggest gain yet for Player {}".format(gain, 'points' if gain > 1 else 'point', who))
        return announce_highest(who, new_high, new_score)
    return say
    # END PROBLEM 7

阶段3

在第三个阶段,需要实现一些游戏策略,设计一些简单的ai。

Problem 8:

实现make_averaged函数,它是一个高阶函数。接收一个参数fnfn是一个函数。make_averaged函数返回一个函数,它接收和fn同样的参数。这个返回的参数和fn不同,它返回调用fn函数num_samples之后结果的均值。

说起来有些拗口,稍微解释一下。即我们要实现一个高阶函数make_averaged,它返回一个函数,我们把这个返回的函数称为rr函数接收的参数和fn一样,调用r会得到将fn函数调用num_samples次之后的均值。

这里最大的问题在于我们不知道fn会接收什么样的参数,那又怎么来确定r的接收参数格式呢?

这里需要用到一个小技巧,在Python传参的过程当中,当我们不知道具体传入的参数数量时,我们可以把若干个连续的必填参数写成*arg。这里的arg表示一个列表,*arg表示将列表展开。其实*符号后面跟什么名字都行,都表示一个列表名。

作业当中提供了一个例子:

代码语言:javascript
复制
>>> def printed(fn):
...     def print_and_return(*args):
...         result = fn(*args)
...         print('Result:', result)
...         return result
...     return print_and_return
>>> printed_pow = printed(pow)
>>> printed_pow(2, 8)
Result: 256
256
>>> printed_abs = printed(abs)
>>> printed_abs(-10)
Result: 10
10

在这个例子当中,print_and_return函数接收的参数就是*args,它可以接收若干个参数。在print_and_return函数当中,我们又调用了fn函数,将*args又传给了fn。通过这种方式,其实就把fn函数的接收参数通过print_and_return传递了进来。

在本题当中,我们也需要使用类似的方法实现:

代码语言:javascript
复制
def make_averaged(fn, num_samples=1000):
    """Return a function that returns the average value of FN when called.
    """
    # BEGIN PROBLEM 8
    "*** YOUR CODE HERE ***"
    def process(*args):
        tot = 0
        for _ in range(num_samples):
            tot += fn(*args)
        return tot / num_samples
    return process
    # END PROBLEM 8

Problem 9:

实现max_scoring_num_rolls函数,它接收两个参数dicenum_samples,其中dice表示骰子的类型,num_samples表示实验的次数。它返回num_samples实验之后,收益均值最大的骰子的数量。

你需要调用你刚刚实现的make_averagedroll_dice函数来完成。返回的结果在1到10之间(包括1和10)。当两种情况得分相同时,返回骰子数量少的。比如3个骰子和6个骰子的得分相同,返回3。

只要开发出了make_averaged,基本上没有难度,枚举一下所有骰子的可能,选出平均收益(收益期望)最大的即可。

代码语言:javascript
复制
def max_scoring_num_rolls(dice=six_sided, num_samples=1000):
    """Return the number of dice (1 to 10) that gives the highest average turn
    score by calling roll_dice with the provided DICE over NUM_SAMPLES times.
    Assume that the dice always return positive outcomes.

    >>> dice = make_test_dice(1, 6)
    >>> max_scoring_num_rolls(dice)
    1
    """
    # BEGIN PROBLEM 9
    "*** YOUR CODE HERE ***"
    ret, max_score = 1, 0
    for i in range(1, 11):
        avearge_dice = make_averaged(roll_dice, num_samples)
        score = avearge_dice(i, dice)
        if score > max_score:
            ret, max_score = i, score
    return ret
    # END PROBLEM 9

Problem 10:

实现bacon_strategy函数,这个策略会在投掷0收益最优时尽可能投掷0颗骰子。

具体逻辑为:当投掷0颗骰子的收益大于等参数margin时,投掷0颗骰子,否则投掷num_rolls颗。

代码语言:javascript
复制
def bacon_strategy(score, opponent_score, margin=8, num_rolls=4):
    """This strategy rolls 0 dice if that gives at least MARGIN points, and
    rolls NUM_ROLLS otherwise.
    """
    # BEGIN PROBLEM 10
    gain = free_bacon(opponent_score)
    if gain >= margin:
        return 0
    return num_rolls
    # END PROBLEM 10

Problem 11:

实现swap_strategy策略,这个策略会尽可能利用Swine Swap规则。当投掷0会触发Swine Swap,并且获得很大收益时投掷0。如果投掷0颗收益大于等于参数margin,并且不会触发Swine Swap时也投掷0。否则投掷num_rolls

代码语言:javascript
复制
def swap_strategy(score, opponent_score, margin=8, num_rolls=4):
    """This strategy rolls 0 dice when it triggers a beneficial swap. It also
    rolls 0 dice if it gives at least MARGIN points. Otherwise, it rolls
    NUM_ROLLS.
    """
    # BEGIN PROBLEM 11
    gain = free_bacon(opponent_score)
    score += gain
    if gain >= margin and not is_swap(score, opponent_score):
        return 0
    if is_swap(score, opponent_score) and opponent_score >= score + margin:
        return 0
    return num_rolls
    # END PROBLEM 11

Problem 12:

附加题,发挥你个人的才智将上述实现的策略进行结合实现终极策略,尽可能和在始终投掷4的ai比拼当中获得更高的胜率。一些建议:

  • swap_strategy是一个很好的默认策略
  • 获得超过100分没有意义,查看能否通过掷出0、1、2枚骰子获胜,如果领先,可以投掷少一些的骰子降低风险
  • 尝试获得有收益的交换
  • 谨慎选择num_rollsmargin

这题没有标准答案,大家可以自由发挥。提供一个我根据提示实现的版本:

代码语言:javascript
复制
def final_strategy(score, opponent_score):
    """Write a brief description of your final strategy.

    *** YOUR DESCRIPTION HERE ***
    """
    # BEGIN PROBLEM 12
    # 落后时,如果获得1分可以与对手交换,那么就投10个骰子
    if opponent_score > score and is_swap(score+1, opponent_score):
        return 10
    # 领先时,如果获得1分会被交换,返回0
    if score > opponent_score and is_swap(score+1, opponent_score):
        return 0
    # 判断free bacon是否可以获胜
    max_roll = max_scoring_num_rolls(num_samples=100)
    num = swap_strategy(score, opponent_score, 100-score, max_roll)
    if num == 0:
        return 0
    # 不投0颗,是否有获胜可能
    avg_dice = make_averaged(roll_dice, 100)
    for i in range(1, 4):
        gain = avg_dice(i)
        if score + gain >= 100:
            return i
    # 判断投掷0颗是否可以反超,如果领先,则以获得6分以上为期望
    margin = opponent_score - score + 1 if opponent_score > score else 6
    return swap_strategy(score, opponent_score, margin, max_roll)
    # END PROBLEM 12

到这里整个项目就算是开发完了,我们可以调用一下老师提供的工具,计算一下我们开发的策略对战一直投掷4个骰子策略的胜率。

目前我在基于规则的情况下只能获得60%左右的胜率,我后来使用强化学习训练了模型,但是最好情况下也只获得了61%的胜率,比基于策略的略高一点而已。大家不妨也试试看,看看有没有更好的策略。

我们也可以调用现成的gui界面,和我们开发的策略对战:

代码语言:javascript
复制
python3 hog_gui.py -f

这个游戏看起来虽然简单,但是完整地顺着老师的思路把代码一点点实现下来,还是很有成就感的,并且难度梯度做得很好,虽然项目本身不小,但实现难度并不大。

另外,游戏本身也很值得我们深挖,由于Pig Out规则的存在,导致收益存在一个巨大的波动。投掷的骰子多了,很容易影响收益。投的少了,收益也少。在这种情况下想要设计出一个厉害的ai还是挺有难度的。非常非常强烈推荐大家亲自试试。

好了,关于这个项目就先聊到这里,感谢大家的阅读。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coder梁 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 阶段1
    • Problem 1:
      • Problem 2:
        • Problem 3:
          • Problem 4:
            • Problem 5:
            • 阶段2
              • Problem 6:
                • Problem 7:
                • 阶段3
                  • Problem 8:
                    • Problem 9:
                      • Problem 10:
                        • Problem 11:
                          • Problem 12:
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档