Guest User

Untitled

a guest
Jan 22nd, 2018
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.77 KB | None | 0 0
  1. """The Game of Pig"""
  2. #Christopher
  3. #Mark
  4. from dice import make_fair_die, make_test_die
  5. from ucb import main, trace, log_current_line, interact
  6.  
  7. goal = 100 # The goal of pig is always to score 100 points.
  8.  
  9. # Taking turns
  10.  
  11. def roll(turn_total, outcome):
  12. """Performs the roll action, which adds outcome to turn_total, or loses the
  13. turn on outcome == 1.
  14.  
  15. Arguments:
  16. turn -- number of points accumulated by the player so far during the turn
  17. outcome -- the outcome of the roll (the number generated by the die)
  18.  
  19. Returns three values in order:
  20. - the number of points the player scores after the roll
  21. Note: If the turn is not over after this roll, this return value is 0.
  22. No points are scored until the end of the turn.
  23. - the player turn point total after the roll
  24. - a boolean; whether or not the player's turn is over
  25.  
  26. >>> roll(7, 3)
  27. (0, 10, False)
  28. >>> roll(99, 1)
  29. (1, 0, True)
  30. """
  31. #print("roll")
  32. if (outcome==1): return 1, 0, True
  33. else: return (0, turn_total+outcome, False)
  34.  
  35. def hold(turn_total, outcome):
  36. """Performs the hold action, which adds turn_total to the player's score.
  37.  
  38. Arguments:
  39. turn -- number of points accumulated by the player so far during the turn
  40. outcome -- the outcome of the roll, ie. the number generated by the die
  41.  
  42. Returns three values in o-09ijrder:
  43. - the number of points the player scores after holding
  44. - the player turn total after the roll (always 0)
  45. - a boolean; whether or not the player's turn is over
  46.  
  47. >>> hold(99, 1)
  48. (99, 0, True)
  49. """
  50. #print("hold")
  51. return (turn_total, 0, True)
  52.  
  53. def take_turn(plan, dice=make_fair_die(), who='Someone', comments=False):
  54. """Simulate a single turn and return the points scored for the whole turn.
  55.  
  56. Important: The d function should be called once, **and only once**, for
  57. every action taken! Testing depends upon this fact.
  58.  
  59. Arguments:
  60. plan -- a function that takes the turn total and returns an action function
  61. dice -- a function that takes no args and returns an integer outcome.
  62. Note: dice is non-pure! Call it exactly once per action.
  63. who -- name of the current player
  64. comments -- a boolean; whether commentary is enabled
  65. """
  66. score_for_turn = 0 # Points scored in the whole turn
  67. turn_total=0
  68. endofturn= False
  69. while(not endofturn):
  70. action = plan(turn_total)
  71. outcome= dice()
  72. score_for_turn ,turn_total, endofturn = action(turn_total, outcome )
  73. if comments==True: commentate(action, outcome, score_for_turn, turn_total, endofturn, who)
  74. return score_for_turn
  75.  
  76. def take_turn_test():
  77. """Test the take_turn function using deterministic test dice."""
  78. plan = make_roll_until_plan(10) # plan is a function (see problem 2)
  79. assert take_turn(plan,make_test_die(4,6,1),"Someone",False)==10, "Test die should roll 10"
  80. assert take_turn(plan,make_test_die(4,4,5),"Someone",False)==13, "Test die should roll 13"
  81. assert take_turn(plan,make_test_die(4,1,5),"Someone",False)==1, "Test die should roll 1"
  82. #print(take_turn(plan)) # Not deterministic
  83.  
  84.  
  85. # Commentating
  86.  
  87. def commentate(action, outcome, score_for_turn, turn_total, over, who):
  88. """Print descriptive comments about a game event.
  89.  
  90. action -- the action function chosen by the current player
  91. outcome -- the outcome of the die roll
  92. score_for_turn -- the points scored in this turn by the current player
  93. turn_total -- the cu turn_total -- the current turn total
  94. rrent turn total
  95. over -- a boolean that indicates whether the turn is over
  96. who -- the name of the current player
  97. """
  98. print(draw_number(outcome))
  99. print(who, describe_action(action))
  100. if over:
  101. print(who, 'scored', score_for_turn, 'point(s) on this turn.')
  102. else:
  103. print(who, 'now has a turn total of', turn_total, 'point(s).')
  104.  
  105. def describe_action(action):
  106. """Generate a string that describes an action.
  107.  
  108. action -- a function, which should be either hold or roll
  109.  
  110. If action is neither the hold nor roll function, the description should
  111. announce that cheating has occurred.
  112.  
  113. >>> describe_action(roll)
  114. 'chose to roll.'
  115. >>> describe_action(hold)
  116. 'decided to hold.'
  117. >>> describe_action(commentate)
  118. 'took an illegal action!'
  119. """
  120. if (action==roll):
  121. return 'chose to roll.'
  122. elif (action==hold):
  123. return 'decided to hold.'
  124. else: return 'took an illegal action!'
  125.  
  126. def draw_number(n, dot='*'):
  127. """Return an ascii art representation of rolling the number n.
  128.  
  129. >>> print(draw_number(5))
  130. -------
  131. | * * |
  132. | * |
  133. | * * |
  134. -------
  135. """
  136. c =f =b =s = False
  137. if n==1:
  138. c=True
  139. if n==2:
  140. b=True
  141. if n==3:
  142. f=c=True
  143. if n==4:
  144. f=b=True
  145. if n==5:
  146. f=b=c=True
  147. if n==6:
  148. f=b=s=True
  149.  
  150. return draw_die(c, f, b, s, dot)
  151.  
  152. def draw_die(c, f, b, s, dot):
  153. """Return an ascii art representation of a die.
  154.  
  155. c, f, b, & s are boolean arguments. This function returns a multi-line
  156. string of the following form, where the letters in the diagram are either
  157. filled if the corresponding argument is true, or empty if it is false.
  158.  
  159. -------
  160. | b f |
  161. | s c s |
  162. | f b |
  163. -------
  164.  
  165. Note: The sides with 2 and 3 dots have 2 possible depictions due to
  166. rotation. Either representation is acceptable.
  167.  
  168. Note: This function uses Python syntax not yet covered in the course.
  169.  
  170. c, f, b, s -- booleans; whether to place dots in corresponding positions
  171. dot -- A length-one string to use for a dot
  172. """
  173. border = ' -------'
  174. def draw(b):
  175. return dot if b else ' '
  176. c, f, b, s = map(draw, [c, f, b, s])
  177. top = ' '.join(['|', b, ' ', f, '|'])
  178. middle = ' '.join(['|', s, c, s, '|'])
  179. bottom = ' '.join(['|', f, ' ', b, '|'])
  180. return '\n'.join([border, top, middle, bottom, border])
  181.  
  182.  
  183. # Game simulator
  184.  
  185. def play(strategy, opponent_strategy):
  186. """Simulate a game and return 0 if the first player wins and 1 otherwise.
  187.  
  188. strategy -- The strategy function for the first player (who plays first)
  189. opponent_strategy -- The strategy function for the second player
  190. """
  191. who = 0 # Which player is about to take a turn, 0 (first) or 1 (second)
  192. player_total = 0
  193. opponent_total = 0
  194. player_strategy = strategy
  195. o_strategy = opponent_strategy
  196. sides = 6
  197.  
  198. while player_total < 100 and opponent_total < 100:
  199. '''decides whose move it is '''
  200.  
  201. if who == 0:
  202. #print("start player turn, die= ",sides)
  203. player_total += take_turn(player_strategy(player_total, opponent_total), make_fair_die(sides), "Player 0", False)
  204. #print("Total for player 0:",player_total)
  205. #print("end player turn")
  206. else:
  207. #print("start opponenet, die= ",sides)
  208. opponent_total += take_turn(o_strategy(opponent_total,player_total), make_fair_die(sides), "Player 1", False)
  209. #print("Total for player 1:",opponent_total)
  210. #print("end opponent turn")
  211. ''' alternate player'''
  212. if who==0: who=1
  213. elif who==1: who=0
  214. if (( player_total + opponent_total ) % 7 == 0):
  215. #print("foursided die in play")
  216. sides = 4
  217. else:
  218. sides = 6
  219. #print("end turn")
  220.  
  221. if(player_total>opponent_total):
  222. who=0
  223. else:
  224. who=1
  225. #print("Player ",who," won with ",max(player_total,opponent_total)," points.")
  226. #print("Other Player lost with ",min(player_total,opponent_total)," points.")
  227. return who
  228.  
  229. def other(who):
  230. """Return the other player, for players numbered 0 and 1.
  231.  
  232. >>> other(0)
  233. 1
  234. >>> other(1)
  235. 0
  236. """
  237. return (who + 1) % 2
  238.  
  239.  
  240. # Basic Strategies
  241.  
  242. def make_roll_until_plan(turn_goal=20):
  243. """Return a plan to roll until turn total is at least turn_goal."""
  244. def plan(turn):
  245. if turn >= turn_goal:
  246. return hold
  247. else:
  248. return roll
  249. return plan
  250.  
  251. def make_roll_until_strategy(turn_goal):
  252. """Return a strategy to always adopt a plan to roll until turn_goal.
  253.  
  254. A strategy is a function that takes two game scores as arguments and
  255. returns a plan (which is a function from turn totals to actions).
  256. """
  257.  
  258. def simple_strategy(player_score, opponent_score):
  259. return make_roll_until_plan(turn_goal)
  260.  
  261. return simple_strategy
  262.  
  263. def make_roll_until_strategy_test():
  264. """Test that make_roll_until_strategy gives a strategy that returns correct
  265. roll-until plans."""
  266. strategy = make_roll_until_strategy(15)
  267. plan = strategy(0, 0)
  268. assert plan(14) == roll, 'Should have returned roll'
  269. assert plan(15) == hold, 'Should have returned hold'
  270. assert plan(16) == hold, 'Should have returned hold'
  271.  
  272.  
  273. # Experiments (Phase 2)
  274.  
  275. def average_value(fn, num_samples):
  276. """Compute the average value returned by fn over num_samples trials.
  277.  
  278. >>> d = make_test_die(1, 3, 5, 7)
  279. >>> average_value(d, 100)
  280. 4.0
  281. """
  282. total = 0
  283. sample_size = num_samples
  284. while sample_size > 0:
  285. total += fn()
  286. sample_size-= 1
  287.  
  288. return total/ num_samples
  289.  
  290. def averaged(fn, num_samples=50000):
  291.  
  292. def average_function(*args):
  293. total = 0
  294. samples = num_samples
  295. while samples > 0:
  296. total += fn(*args)
  297. samples -= 1
  298. #print("[",num_samples-samples,"/",num_samples,"]")
  299.  
  300. return total / num_samples
  301.  
  302.  
  303. return average_function
  304.  
  305. """Return a function that returns the average_value of fn when called.
  306.  
  307. Note: To implement this function, you will have to use *args syntax, a new
  308. Python feature introduced in this project. See the project
  309. description for details.
  310.  
  311. >>> die = make_test_die(3, 1, 5, 7)
  312. >>> avg_die = averaged(die)
  313. >>> avg_die()
  314. 4.0
  315. >>> avg_turn = averaged(take_turn)
  316. >>> avg_turn(make_roll_until_plan(4), die, 'The player', False)
  317. 3.0
  318.  
  319. In this last example, two different turn scenarios are averaged.
  320. - In the first, the player rolls a 3 then a 1, receiving a score of 1.
  321. - In the other, the player rolls a 5 (then holds on the 7), scoring 5.
  322. Thus, the average value is 3.0
  323.  
  324. Note: If this last test is called with comments=True in take_turn, the
  325. doctests will fail because of the extra output.
  326. """
  327.  
  328.  
  329.  
  330.  
  331. def compare_strategies(strategy, baseline=make_roll_until_strategy(20)):
  332. """Return the average win rate (out of 1) of strategy against baseline."""
  333. as_first = 1 - averaged(play)(strategy, baseline)
  334.  
  335. as_second = averaged(play)(baseline, strategy)
  336. print(as_first)
  337. print(as_second)
  338. return (as_first + as_second) / 2 # Average the two results
  339.  
  340. def eval_strategy_range(make_strategy, lower_bound, upper_bound):
  341. """Return the best integer argument value for make_strategy to use against
  342. the roll-until-20 baseline, between lower_bound and upper_bound (inclusive).
  343.  
  344. make_strategy -- A one-argument function that returns a strategy.
  345. lower_bound -- lower bound of the evaluation range
  346. upper_bound -- upper bound of the evaluation range
  347. """
  348. best_value, best_win_rate = 0, 0
  349. value = lower_bound
  350. while value <= upper_bound:
  351. strategy = make_strategy(value)
  352. win_rate = compare_strategies(strategy)
  353. print("-----------------------------------",value,'win rate against the baseline:', win_rate)
  354. if win_rate > best_win_rate:
  355. best_win_rate, best_value = win_rate, value
  356. value += 1
  357. return best_value
  358.  
  359. def run_strategy_experiments():
  360. """Run a series of strategy experiments and report results."""
  361. strategy = make_die_specific_strategy(4)
  362. plan=strategy(7,7)
  363. assert plan(3) == roll, 'Should have returned roll'
  364. assert plan(4) == hold, 'Should have returned hold'
  365. assert plan(5) == hold, 'Should have returned hold'
  366. specificstrat=eval_strategy_range(make_die_specific_strategy, 5, 15)
  367. #pridestrat=eval_strategy_range(make_pride_strategy, 0, 15)
  368. #makeuntil=eval_strategy_range(make_roll_until_strategy,1,20)
  369. print("Best Value for specific_strategy: ",specificstrat)
  370. #print("Best Value for makeuntil: ",makeuntil)
  371. #print("Best Value for pride_strategy: ",pridestrat)
  372.  
  373. def test_die_strat():
  374. die = make_test_die(2,3,5,7)
  375. assert take_turn(make_die_specific_strategy(5)(8,12), die) == 22, 'Should be 22'
  376. die = make_test_die(2,3,5,7)
  377. assert take_turn(make_die_specific_strategy(5,14)(8,12), die) == 17, 'Should be 17'
  378. die = make_test_die(2,3,5,7)
  379. assert take_turn(make_die_specific_strategy(6,30)(9,12), die) == 10, 'Should be 10'
  380. die = make_test_die(2,3,5,7)
  381. assert take_turn(make_die_specific_strategy(18,30)(20,7), die) == 34, 'Should be 34'
  382. die = make_test_die(2,3,5,7)
  383. assert take_turn(make_die_specific_strategy(18,30)(20,8), die) == 19, 'Should be 19'
  384.  
  385. def make_die_specific_strategy(four_side_goal = 8, six_side_goal=20):
  386.  
  387. def die_specific_strategy(player_score, opponent_score):
  388. return make_specific_plan(four_side_goal, six_side_goal, player_score, opponent_score)
  389. def make_specific_plan(four_side_goal, six_side_goal, player_score, opponent_score):
  390. def plan(turn_total):
  391. turn_goal = six_side_goal
  392. if ( player_score + opponent_score ) % 7 == 0:
  393. turn_goal = four_side_goal
  394. if turn_total >= turn_goal:
  395. return hold
  396. else:
  397. return roll
  398. return plan
  399. return die_specific_strategy
  400. """Return a strategy that returns a die-specific roll-until plan.
  401.  
  402. four_side_goal -- the roll-until goal whenever the turn uses a 4-sided die
  403. six_side_goal -- the roll-until goal whenever the turn uses a 6-sided die
  404.  
  405. """
  406.  
  407.  
  408. def test_pride_strategy() :
  409. die = make_test_die(2,3,5,7)
  410. assert take_turn(make_pride_strategy(5,0)(8,11), die) == 10, 'Should be 18'
  411. die = make_test_die(2,3,5,7)
  412. assert take_turn(make_pride_strategy(6,0)(8,11), die) == 10, 'Should be 18'
  413. die = make_test_die(2,3,5,7)
  414. assert take_turn(make_pride_strategy(9,0)(8,11), die) == 17, 'Should be 25'
  415. die = make_test_die(2,3,5,7)
  416. assert take_turn(make_pride_strategy(1,15)(8,11), die) == 17, 'Should be 25'
  417. die = make_test_die(2,3,5,7)
  418. assert take_turn(make_pride_strategy(9,20)(20,8), die) == 22, 'Should be 22'
  419.  
  420.  
  421.  
  422. def make_pride_strategy(margin = 4, turn_goal=20):
  423. """Return a strategy that wants to finish a turn winning by at least margin.
  424.  
  425. margin -- the size of the lead that the player requires
  426. turn_goal -- the minimum roll-until turn goal, even when winning
  427. """
  428.  
  429. def make_pride_plan(p_total, o_total, margin, turn_goal):
  430.  
  431. def plan(turn):
  432. if turn < turn_goal or p_total + turn < o_total + margin:
  433. return roll
  434. else:
  435. return hold
  436.  
  437. return plan
  438.  
  439.  
  440. def pride_strategy(p_total, o_total):
  441. return make_pride_plan(p_total, o_total, margin, turn_goal)
  442.  
  443.  
  444. return pride_strategy
  445.  
  446.  
  447. #~.61 chance of winning
  448. def final_strategy(four_side_goal=12, six_side_goal=21, margin=-14):
  449. def implement_final_strategy(player_score, opponent_score): #return this in the end
  450. return make_final_plan(four_side_goal, six_side_goal, margin, player_score, opponent_score)
  451.  
  452. def make_final_plan(four_side_goal, six_side_goal, margin, player_score, opponent_score):
  453. def plan(turn_total):
  454. turn_goal = six_side_goal
  455. if (player_score + turn_total >= 100):
  456. return hold
  457. if player_score >= 95: # end game if almost won
  458. return roll
  459. if 100-opponent_score <= 5: #if opponent is about to win, keep going
  460. return roll
  461. 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
  462. return roll
  463. if ( player_score + opponent_score ) % 7 == 0:
  464. turn_goal = four_side_goal
  465. if ((turn_total+player_score+ opponent_score) % 7 == 0) and turn_total >= .75 *turn_goal: #give opponenet more 4 dice
  466. #print("will be 4 sided")
  467. return hold
  468. if (turn_total >= turn_goal ) and (player_score + turn_total >= opponent_score + margin):
  469. return hold
  470. else:
  471. return roll
  472. return plan
  473. return implement_final_strategy
  474.  
  475. def interactive_strategy(score, opponent_score):
  476. """Prints total game scores and returns an interactive plan.
  477.  
  478. Note: this function uses Python syntax not yet covered in the course.
  479. """
  480. print('You have', score, 'and they have', opponent_score, 'total score')
  481. def plan(turn):
  482. if turn > 0:
  483. print('You now have a turn total of', turn, 'points')
  484. while True:
  485. response = input('(R)oll or (H)old?')
  486. if response.lower()[0] == 'r':
  487. return roll
  488. elif response.lower()[0] == 'h':
  489. return hold
  490. print('Huh?')
  491. return plan
  492.  
  493. @main
  494. def run():
  495. #take_turn_test()
  496.  
  497. # Uncomment the next line to play an interactive game
  498. #play(interactive_strategy, make_roll_until_strategy(20))
  499. #print(play(make_roll_until_strategy(10),make_roll_until_strategy(10)))
  500. # Uncomment the next line to test make_roll_until_strategy
  501. #make_roll_until_strategy_test()
  502. #play(final_strategy(), make_roll_until_strategy(20))
  503. a = compare_strategies(final_strategy())
  504. #b = compare_strategies(make_roll_until_strategy(1))
  505. #b = compare_strategies(make_pride_strategy())
  506. #c = compare_strategies(make_die_specific_strategy())
  507. print("winrate= ",a)
  508. #print("winrate= ",b)
  509. #print("winrate= ",c)
  510. #print("winrate= ",compare_strategies(make_roll_until_strategy(20)))
  511. #run_strategy_experiments()
Add Comment
Please, Sign In to add comment