Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """The Game of Pig"""
- #Christopher
- #Mark
- from dice import make_fair_die, make_test_die
- from ucb import main, trace, log_current_line, interact
- goal = 100 # The goal of pig is always to score 100 points.
- # Taking turns
- def roll(turn_total, outcome):
- """Performs the roll action, which adds outcome to turn_total, or loses the
- turn on outcome == 1.
- Arguments:
- turn -- number of points accumulated by the player so far during the turn
- outcome -- the outcome of the roll (the number generated by the die)
- Returns three values in order:
- - the number of points the player scores after the roll
- Note: If the turn is not over after this roll, this return value is 0.
- No points are scored until the end of the turn.
- - the player turn point total after the roll
- - a boolean; whether or not the player's turn is over
- >>> roll(7, 3)
- (0, 10, False)
- >>> roll(99, 1)
- (1, 0, True)
- """
- #print("roll")
- if (outcome==1): return 1, 0, True
- else: return (0, turn_total+outcome, False)
- def hold(turn_total, outcome):
- """Performs the hold action, which adds turn_total to the player's score.
- Arguments:
- turn -- number of points accumulated by the player so far during the turn
- outcome -- the outcome of the roll, ie. the number generated by the die
- Returns three values in o-09ijrder:
- - the number of points the player scores after holding
- - the player turn total after the roll (always 0)
- - a boolean; whether or not the player's turn is over
- >>> hold(99, 1)
- (99, 0, True)
- """
- #print("hold")
- return (turn_total, 0, True)
- def take_turn(plan, dice=make_fair_die(), who='Someone', comments=False):
- """Simulate a single turn and return the points scored for the whole turn.
- Important: The d function should be called once, **and only once**, for
- every action taken! Testing depends upon this fact.
- Arguments:
- plan -- a function that takes the turn total and returns an action function
- dice -- a function that takes no args and returns an integer outcome.
- Note: dice is non-pure! Call it exactly once per action.
- who -- name of the current player
- comments -- a boolean; whether commentary is enabled
- """
- score_for_turn = 0 # Points scored in the whole turn
- turn_total=0
- endofturn= False
- while(not endofturn):
- action = plan(turn_total)
- outcome= dice()
- score_for_turn ,turn_total, endofturn = action(turn_total, outcome )
- if comments==True: commentate(action, outcome, score_for_turn, turn_total, endofturn, who)
- return score_for_turn
- def take_turn_test():
- """Test the take_turn function using deterministic test dice."""
- plan = make_roll_until_plan(10) # plan is a function (see problem 2)
- assert take_turn(plan,make_test_die(4,6,1),"Someone",False)==10, "Test die should roll 10"
- assert take_turn(plan,make_test_die(4,4,5),"Someone",False)==13, "Test die should roll 13"
- assert take_turn(plan,make_test_die(4,1,5),"Someone",False)==1, "Test die should roll 1"
- #print(take_turn(plan)) # Not deterministic
- # Commentating
- def commentate(action, outcome, score_for_turn, turn_total, over, who):
- """Print descriptive comments about a game event.
- action -- the action function chosen by the current player
- outcome -- the outcome of the die roll
- score_for_turn -- the points scored in this turn by the current player
- turn_total -- the cu turn_total -- the current turn total
- rrent turn total
- over -- a boolean that indicates whether the turn is over
- who -- the name of the current player
- """
- print(draw_number(outcome))
- print(who, describe_action(action))
- if over:
- print(who, 'scored', score_for_turn, 'point(s) on this turn.')
- else:
- print(who, 'now has a turn total of', turn_total, 'point(s).')
- def describe_action(action):
- """Generate a string that describes an action.
- action -- a function, which should be either hold or roll
- If action is neither the hold nor roll function, the description should
- announce that cheating has occurred.
- >>> describe_action(roll)
- 'chose to roll.'
- >>> describe_action(hold)
- 'decided to hold.'
- >>> describe_action(commentate)
- 'took an illegal action!'
- """
- if (action==roll):
- return 'chose to roll.'
- elif (action==hold):
- return 'decided to hold.'
- else: return 'took an illegal action!'
- def draw_number(n, dot='*'):
- """Return an ascii art representation of rolling the number n.
- >>> print(draw_number(5))
- -------
- | * * |
- | * |
- | * * |
- -------
- """
- c =f =b =s = False
- if n==1:
- c=True
- if n==2:
- b=True
- if n==3:
- f=c=True
- if n==4:
- f=b=True
- if n==5:
- f=b=c=True
- if n==6:
- f=b=s=True
- return draw_die(c, f, b, s, dot)
- def draw_die(c, f, b, s, dot):
- """Return an ascii art representation of a die.
- c, f, b, & s are boolean arguments. This function returns a multi-line
- string of the following form, where the letters in the diagram are either
- filled if the corresponding argument is true, or empty if it is false.
- -------
- | b f |
- | s c s |
- | f b |
- -------
- Note: The sides with 2 and 3 dots have 2 possible depictions due to
- rotation. Either representation is acceptable.
- Note: This function uses Python syntax not yet covered in the course.
- c, f, b, s -- booleans; whether to place dots in corresponding positions
- dot -- A length-one string to use for a dot
- """
- border = ' -------'
- def draw(b):
- return dot if b else ' '
- c, f, b, s = map(draw, [c, f, b, s])
- top = ' '.join(['|', b, ' ', f, '|'])
- middle = ' '.join(['|', s, c, s, '|'])
- bottom = ' '.join(['|', f, ' ', b, '|'])
- return '\n'.join([border, top, middle, bottom, border])
- # Game simulator
- def play(strategy, opponent_strategy):
- """Simulate a game and return 0 if the first player wins and 1 otherwise.
- strategy -- The strategy function for the first player (who plays first)
- opponent_strategy -- The strategy function for the second player
- """
- who = 0 # Which player is about to take a turn, 0 (first) or 1 (second)
- player_total = 0
- opponent_total = 0
- player_strategy = strategy
- o_strategy = opponent_strategy
- sides = 6
- while player_total < 100 and opponent_total < 100:
- '''decides whose move it is '''
- if who == 0:
- #print("start player turn, die= ",sides)
- player_total += take_turn(player_strategy(player_total, opponent_total), make_fair_die(sides), "Player 0", False)
- #print("Total for player 0:",player_total)
- #print("end player turn")
- else:
- #print("start opponenet, die= ",sides)
- opponent_total += take_turn(o_strategy(opponent_total,player_total), make_fair_die(sides), "Player 1", False)
- #print("Total for player 1:",opponent_total)
- #print("end opponent turn")
- ''' alternate player'''
- if who==0: who=1
- elif who==1: who=0
- if (( player_total + opponent_total ) % 7 == 0):
- #print("foursided die in play")
- sides = 4
- else:
- sides = 6
- #print("end turn")
- if(player_total>opponent_total):
- who=0
- else:
- who=1
- #print("Player ",who," won with ",max(player_total,opponent_total)," points.")
- #print("Other Player lost with ",min(player_total,opponent_total)," points.")
- return who
- def other(who):
- """Return the other player, for players numbered 0 and 1.
- >>> other(0)
- 1
- >>> other(1)
- 0
- """
- return (who + 1) % 2
- # Basic Strategies
- def make_roll_until_plan(turn_goal=20):
- """Return a plan to roll until turn total is at least turn_goal."""
- def plan(turn):
- if turn >= turn_goal:
- return hold
- else:
- return roll
- return plan
- def make_roll_until_strategy(turn_goal):
- """Return a strategy to always adopt a plan to roll until turn_goal.
- A strategy is a function that takes two game scores as arguments and
- returns a plan (which is a function from turn totals to actions).
- """
- def simple_strategy(player_score, opponent_score):
- return make_roll_until_plan(turn_goal)
- return simple_strategy
- def make_roll_until_strategy_test():
- """Test that make_roll_until_strategy gives a strategy that returns correct
- roll-until plans."""
- strategy = make_roll_until_strategy(15)
- plan = strategy(0, 0)
- assert plan(14) == roll, 'Should have returned roll'
- assert plan(15) == hold, 'Should have returned hold'
- assert plan(16) == hold, 'Should have returned hold'
- # Experiments (Phase 2)
- def average_value(fn, num_samples):
- """Compute the average value returned by fn over num_samples trials.
- >>> d = make_test_die(1, 3, 5, 7)
- >>> average_value(d, 100)
- 4.0
- """
- total = 0
- sample_size = num_samples
- while sample_size > 0:
- total += fn()
- sample_size-= 1
- return total/ num_samples
- def averaged(fn, num_samples=50000):
- def average_function(*args):
- total = 0
- samples = num_samples
- while samples > 0:
- total += fn(*args)
- samples -= 1
- #print("[",num_samples-samples,"/",num_samples,"]")
- return total / num_samples
- return average_function
- """Return a function that returns the average_value of fn when called.
- Note: To implement this function, you will have to use *args syntax, a new
- Python feature introduced in this project. See the project
- description for details.
- >>> die = make_test_die(3, 1, 5, 7)
- >>> avg_die = averaged(die)
- >>> avg_die()
- 4.0
- >>> avg_turn = averaged(take_turn)
- >>> avg_turn(make_roll_until_plan(4), die, 'The player', False)
- 3.0
- In this last example, two different turn scenarios are averaged.
- - In the first, the player rolls a 3 then a 1, receiving a score of 1.
- - In the other, the player rolls a 5 (then holds on the 7), scoring 5.
- Thus, the average value is 3.0
- Note: If this last test is called with comments=True in take_turn, the
- doctests will fail because of the extra output.
- """
- def compare_strategies(strategy, baseline=make_roll_until_strategy(20)):
- """Return the average win rate (out of 1) of strategy against baseline."""
- as_first = 1 - averaged(play)(strategy, baseline)
- as_second = averaged(play)(baseline, strategy)
- print(as_first)
- print(as_second)
- return (as_first + as_second) / 2 # Average the two results
- def eval_strategy_range(make_strategy, lower_bound, upper_bound):
- """Return the best integer argument value for make_strategy to use against
- the roll-until-20 baseline, between lower_bound and upper_bound (inclusive).
- make_strategy -- A one-argument function that returns a strategy.
- lower_bound -- lower bound of the evaluation range
- upper_bound -- upper bound of the evaluation range
- """
- best_value, best_win_rate = 0, 0
- value = lower_bound
- while value <= upper_bound:
- strategy = make_strategy(value)
- win_rate = compare_strategies(strategy)
- print("-----------------------------------",value,'win rate against the baseline:', win_rate)
- if win_rate > best_win_rate:
- best_win_rate, best_value = win_rate, value
- value += 1
- return best_value
- def run_strategy_experiments():
- """Run a series of strategy experiments and report results."""
- strategy = make_die_specific_strategy(4)
- plan=strategy(7,7)
- assert plan(3) == roll, 'Should have returned roll'
- assert plan(4) == hold, 'Should have returned hold'
- assert plan(5) == hold, 'Should have returned hold'
- specificstrat=eval_strategy_range(make_die_specific_strategy, 5, 15)
- #pridestrat=eval_strategy_range(make_pride_strategy, 0, 15)
- #makeuntil=eval_strategy_range(make_roll_until_strategy,1,20)
- print("Best Value for specific_strategy: ",specificstrat)
- #print("Best Value for makeuntil: ",makeuntil)
- #print("Best Value for pride_strategy: ",pridestrat)
- def test_die_strat():
- die = make_test_die(2,3,5,7)
- assert take_turn(make_die_specific_strategy(5)(8,12), die) == 22, 'Should be 22'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_die_specific_strategy(5,14)(8,12), die) == 17, 'Should be 17'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_die_specific_strategy(6,30)(9,12), die) == 10, 'Should be 10'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_die_specific_strategy(18,30)(20,7), die) == 34, 'Should be 34'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_die_specific_strategy(18,30)(20,8), die) == 19, 'Should be 19'
- def make_die_specific_strategy(four_side_goal = 8, six_side_goal=20):
- def die_specific_strategy(player_score, opponent_score):
- return make_specific_plan(four_side_goal, six_side_goal, player_score, opponent_score)
- def make_specific_plan(four_side_goal, six_side_goal, player_score, opponent_score):
- def plan(turn_total):
- turn_goal = six_side_goal
- if ( player_score + opponent_score ) % 7 == 0:
- turn_goal = four_side_goal
- if turn_total >= turn_goal:
- return hold
- else:
- return roll
- return plan
- return die_specific_strategy
- """Return a strategy that returns a die-specific roll-until plan.
- four_side_goal -- the roll-until goal whenever the turn uses a 4-sided die
- six_side_goal -- the roll-until goal whenever the turn uses a 6-sided die
- """
- def test_pride_strategy() :
- die = make_test_die(2,3,5,7)
- assert take_turn(make_pride_strategy(5,0)(8,11), die) == 10, 'Should be 18'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_pride_strategy(6,0)(8,11), die) == 10, 'Should be 18'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_pride_strategy(9,0)(8,11), die) == 17, 'Should be 25'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_pride_strategy(1,15)(8,11), die) == 17, 'Should be 25'
- die = make_test_die(2,3,5,7)
- assert take_turn(make_pride_strategy(9,20)(20,8), die) == 22, 'Should be 22'
- def make_pride_strategy(margin = 4, turn_goal=20):
- """Return a strategy that wants to finish a turn winning by at least margin.
- margin -- the size of the lead that the player requires
- turn_goal -- the minimum roll-until turn goal, even when winning
- """
- def make_pride_plan(p_total, o_total, margin, turn_goal):
- def plan(turn):
- if turn < turn_goal or p_total + turn < o_total + margin:
- return roll
- else:
- return hold
- return plan
- def pride_strategy(p_total, o_total):
- return make_pride_plan(p_total, o_total, margin, turn_goal)
- return pride_strategy
- #~.61 chance of winning
- def final_strategy(four_side_goal=12, six_side_goal=21, margin=-14):
- def implement_final_strategy(player_score, opponent_score): #return this in the end
- return make_final_plan(four_side_goal, six_side_goal, margin, player_score, opponent_score)
- def make_final_plan(four_side_goal, six_side_goal, margin, player_score, opponent_score):
- def plan(turn_total):
- turn_goal = six_side_goal
- if (player_score + turn_total >= 100):
- return hold
- if player_score >= 95: # end game if almost won
- return roll
- if 100-opponent_score <= 5: #if opponent is about to win, keep going
- return roll
- if (player_score > 90 and opponent_score > 90 and ( player_score + opponent_score ) % 7 != 0 ): #try to win if you're both about to win
- return roll
- if ( player_score + opponent_score ) % 7 == 0:
- turn_goal = four_side_goal
- if ((turn_total+player_score+ opponent_score) % 7 == 0) and turn_total >= .75 *turn_goal: #give opponenet more 4 dice
- #print("will be 4 sided")
- return hold
- if (turn_total >= turn_goal ) and (player_score + turn_total >= opponent_score + margin):
- return hold
- else:
- return roll
- return plan
- return implement_final_strategy
- def interactive_strategy(score, opponent_score):
- """Prints total game scores and returns an interactive plan.
- Note: this function uses Python syntax not yet covered in the course.
- """
- print('You have', score, 'and they have', opponent_score, 'total score')
- def plan(turn):
- if turn > 0:
- print('You now have a turn total of', turn, 'points')
- while True:
- response = input('(R)oll or (H)old?')
- if response.lower()[0] == 'r':
- return roll
- elif response.lower()[0] == 'h':
- return hold
- print('Huh?')
- return plan
- @main
- def run():
- #take_turn_test()
- # Uncomment the next line to play an interactive game
- #play(interactive_strategy, make_roll_until_strategy(20))
- #print(play(make_roll_until_strategy(10),make_roll_until_strategy(10)))
- # Uncomment the next line to test make_roll_until_strategy
- #make_roll_until_strategy_test()
- #play(final_strategy(), make_roll_until_strategy(20))
- a = compare_strategies(final_strategy())
- #b = compare_strategies(make_roll_until_strategy(1))
- #b = compare_strategies(make_pride_strategy())
- #c = compare_strategies(make_die_specific_strategy())
- print("winrate= ",a)
- #print("winrate= ",b)
- #print("winrate= ",c)
- #print("winrate= ",compare_strategies(make_roll_until_strategy(20)))
- #run_strategy_experiments()
Add Comment
Please, Sign In to add comment