跳转至

Project1-hog

规则

目标:达成GOAL(默认100),双方轮流投骰子,本回合得分是骰子结果总和
- Sow Sad: 有任意一骰子为1,则总和为1
- Boar Brawl: 选择投骰子0个,获得对手得分十位数与自己得分个位数之差的绝对值的三倍,或1,取二者之间较大的
- Sus Fuss: 如果一个数字恰好有¾个因数,被称为可疑数。如果当前得分是可疑数,则增大到大于当前得分的最小质数

Phase1: Rules of the Game

Python
# problem0: 阅读dice.py,理解骰子的行为
"""Functions that simulate dice rolls.

A dice function takes no arguments and returns a number from 1 to n
(inclusive), where n is the number of sides on the dice.

Fair dice produce each possible outcome with equal probability.
Two fair dice are already defined, four_sided and six_sided,
and are generated by the make_fair_dice function.

Test dice are deterministic: they always cycles through a fixed
sequence of values that are passed as arguments.
Test dice are generated by the make_test_dice function.
"""

from random import randint

def make_fair_dice(sides):
    """Return a die that returns 1 to SIDES with equal chance."""
    assert type(sides) == int and sides >= 1, 'Illegal value for sides'
    def dice():
        return randint(1,sides)
    return dice

four_sided = make_fair_dice(4)
six_sided = make_fair_dice(6)

def make_test_dice(*outcomes):
    """Return a die that cycles deterministically through OUTCOMES.

    >>> dice = make_test_dice(1, 2, 3)
    >>> dice()
    1
    >>> dice()
    2
    >>> dice()
    3
    >>> dice()
    1
    >>> dice()
    2

    This function uses Python syntax/techniques not yet covered in this course.
    The best way to understand it is by reading the documentation and examples.
    """
    assert len(outcomes) > 0, 'You must supply outcomes to make_test_dice'
    for o in outcomes:
        assert type(o) == int and o >= 1, 'Outcome is not a positive integer'
    index = len(outcomes) - 1
    def dice():
        nonlocal index  #使用了nonlocal,这是说明里面提到的non-pure的原因,The `dice.py` file represents dice using non-pure zero-argument functions. These functions are non-pure because they may have different return values each time they are called, and so a side-effect of calling the function is changing what will be returned when the function is called again.
        index = (index + 1) % len(outcomes)
        return outcomes[index]
    return dice
Python
# problem1: 实现函数roll_dice: 接受num_rolls指定投骰子次数,接受dice函数(怎么样的骰子),返回投骰子获得分数。在roll_dice中恰好调用dice()函数num_rolls次。
def rool_dice(num_rolls, dice = six_sided):
    exist_one = 0
    total = 0
    while num_rolls > 0:
        a_dice_num = dice()
        total, num_rolls = total + a_dice_num, num_rolls - 1
        if a_dice_num == 1:
            exist_one = 1
    if exist_one:
        return 1
    return total
Python
1
2
3
4
# problem2: 实现boar_brawl函数,接受当前得分和对手得分,返回boar_brawl的得分
def boar_brawl(player_score, opponent_score):
    total = 3 * abs(opponent_score%100//10 - player_score%10)
    return max(1, total)
Python
1
2
3
4
5
6
# problem3: 实现take_turn函数,返回通过给定dice和num_rolls的得分
def take_turn(num_rolls, player_score, opponent_score, dice=six_sided):
    if num_rolls == 0:
        return boar_brawl(player_score, opponent_score)
    else:
        return roll_dice(num_rolls, dice)
Python
# problem4: num_factors,返回n的因子数量;sus_points,返回玩家在经过sus fuss;sus_update,返回玩家投掷num_rolls个骰子的总分数,同时考虑sus fuss和boar brawl规则
def is_prime(n):
    """Return whether N is prime."""
    if n == 1:
        return False
    k = 2
    while k < n:
        if n % k == 0:
            return False
        k += 1
    return True


def num_factors(n):
    """Return the number of factors of N, including 1 and N itself."""
    # BEGIN PROBLEM 4
    num = 0
    for i in range(1, n+1):
        if n % i == 0:
            num += 1
    return num
    # END PROBLEM 4


def sus_points(score):
    """Return the new score of a player taking into account the Sus Fuss rule."""
    # BEGIN PROBLEM 4
    if num_factors(score) == 3 or num_factors(score) == 4:
        while not is_prime(score):
            score += 1
    return score
    # END PROBLEM 4


def sus_update(num_rolls, player_score, opponent_score, dice=six_sided):
    """Return the total score of a player who starts their turn with
    PLAYER_SCORE and then rolls NUM_ROLLS DICE, *including* Sus Fuss.
    """
    # BEGIN PROBLEM 4
    score = simple_update(num_rolls, player_score, opponent_score, dice)
    return sus_points(score)

    # END PROBLEM 4
Python
# problem5: play函数:模拟一局完整的hog游戏
def play(strategy0, strategy1, update, score0=0, score1=0, dice=six_sided, goal=GOAL):
    who = 0 
    while score0 < goal and score1 < goal:
        if who == 0:
            num_rolls = strategy0(score0, score1) 
            score0 = update(num_rolls, score0, score1, dice)
            who = 1 - who 
        else:
            num_rolls = strategy1(score1, score0)
            score1 = update(num_rolls, score1, score0, dice)
            who = 1 - who 
    return score0, score1

Interlude: User Interfaces

1. Printing Game Events

可以使用高阶函数,在不对原有代码作出太多改动的情况下实现这点。阅读hog_ui.py

2. Accepting User Input

interactive_strategy函数返回一个通过调用input函数让玩家决定num_rolls的策略

3. Graphical User Interface

图形化界面

Phase2: Strategies

策略函数接受当前玩家分数和对手分数,返回要投的骰子的数量

Python
1
2
3
4
5
# problem 6: always_roll, 接受一个n,返回一个总是投骰子n次的策略
def always_roll(n):
    def always_roll_n(score, opponent_score):
        return n
    return always_roll_n
Python
1
2
3
4
5
6
7
8
# problem 7: is_always_roll,检测一个策略是否一直都是在roll相同的次数
def is_always_roll(strategy, goal=GOAL):
    num_rolls = strategy(0, 0)
    for i in range(0, goal):
        for j in range(0, goal):
            if strategy(i, j) != num_rolls:
                return False
    return True
Python
1
2
3
4
5
6
7
8
# problem 8: make_ageraged,调用一个original_function for times_called time,返回一个返回均值的函数
def make_averaged(original_function, times_called=1000):
    def averaged(*args): #为了让averaged和original_function接受相同参数
        result = 0
        for _ in range(times_called):
            result += original_function(*args)
        return result / times_called
    return averaged
Python
# problem 9: max_scoring_num_rolls,使用make_averaged和roll_dice实现,使用固定面数的骰子实验,确定在1-10次的投掷中,能使单会平均得分最高的投掷次数
def max_scoring_num_rolls(dice=six_sided, times_called=1000):
    max_n = 0
    max_agerage = 0
    for i in range(1, 11):
        current = make_averaged(roll_dice, times_called)(i, dice)
        if current > max_agerage:
            max_agerage = current
            max_n = i
    return max_n
Python
1
2
3
4
5
# problem 10: boar_strategy,滚动0次时最少能获得threshold分数,则返回0,否则返回num_rolls,不考虑sus fuss
def boar_strategy(score, opponent_score, threshold=11, num_rolls=6):
    if boar_brawl(score, opponent_score) >= threshold:
        return 0
    return num_rolls
Python
1
2
3
4
5
# problem 11: sus_stragety,在boar_strategy的基础上考虑sus
def sus_strategy(score, opponent_score, threshold=11, num_rolls=6):
    if sus_update(0, score, opponent_score) - score >= threshold:
        return 0
    return num_rolls