Project 1: The Game of Hog* I know! I'll use my Higher-order functions to Order higher rolls. * This project is a modified version of UCB CIS 61A - Hog project, available onlink(외부 사이트로 연결합니다.)...

1 answer below »

Project 1: The Game of Hog*



I know! I'll use my
Higher-order functions to
Order higher rolls.


* This project is a modified version of UCB CIS 61A - Hog project, available onlink(외부 사이트로 연결합니다.)



Introduction


In this project, you will develop a simulator and multiple strategies for the dice game Hog. You will need to use control and higher-order functions together, from Sections 1.1 through 1.6 of theComposing Programs(외부 사이트로 연결합니다.)online text.


In Hog, two players alternate turns trying to reach 100 points first. On each turn, the current player chooses some number of dice to roll, up to 10. Her turn score is the sum of the dice outcomes, unless any of the dice come up a 1, in which case the score for her turn is only 1 point (thePig outrule).


To spice up the game, we will play with some special rules:




  1. Free bacon. If a player chooses to roll zero dice, she scores one more than the largest digit in her opponent's score. For example, if Player 1 has 42 points, Player 0 gains 1 + max(4, 2) = 5 points by rolling zero dice. If Player 1 has 48 points, Player 0 gains 1 + max(4, 8) = 9 points.


  2. Hog wild. If the sum of both players' total scores is a multiple of seven (e.g., 14, 21, 35), then the current player rolls four-sided dice instead of the usual six-sided dice.


  3. Swine swap. If at the end of a turn one of the player's total score is exactly double the other's, then the players swap total scores.Example 1:Player 0 has 20 points and Player 1 has 5; it is Player 1's turn. She scores 5 more, bringing her total to 10. The players swap scores: Player 0 now has 10 points and Player 1 has 20. It is now Player 0's turn.Example 2: Player 0 has 90 points and Player 1 has 50; it is Player 0's turn. She scores 10 more, bringing her total to 100. The players swap scores, and Player 1 wins the game 100 to 50.


This project includes six files, but all of your changes will be made to the first one, and it is the only one you should need to read and understand.


To get started,downloadall of the project code as azip archive.



Download the project file































hog.py



A starter implementation of Hog.




dice.py



Functions for rolling dice.




ucb.py



Utility functions for CS 61A.




hog_gui.py



A graphical user interface for Hog.




hog_grader.py



Tests to check the correctness of your implementation.




autograder.py



Utility functions for grading.



Logistics


This is a two-week project. You are strongly encouraged to complete this project with a partner, although you may complete it alone.


Start early! The amount of time it takes to complete a project (or any program) is unpredictable.


You are not alone! Ask for help early and often, and your fellow students are here to help. Try attending office hours or posting on Piazza.


In the end, you and your partner will submit the same project separately. The project is worth 10 points. 17 points are assigned for correctness, and 3 points for the overall compositionof your program.


The only file that you are required to work on ishog.py. You do not need to modify or turn in any other files to complete the project. To submit the project, .......


For the functions that you are asked to complete, there may be some initial code provided. If you would rather not use that code, feel free to delete it and start from scratch. You may also add new function definitions as you see fit.


However, please donotmodify any other functions. Doing so may result in your code failing our autograder tests. Also, do not change any function signatures (names, argument order, or number of arguments).


Graphical User Interface


Agraphical user interface(GUI, for short) is provided for you. At the moment, it doesn't work, because you haven't implemented the game logic. Once you finish Problem 4 (theplayfunction), you will be able to play a fully interactive version of Hog!


In order to render the graphics, make sure you have Tkinter, Python's main graphics library, installed on your computer. Once you've done that, you can run the GUI from your terminal:



python3 hog_gui.py

Once you're done with Problem 9, you can play against the final strategy that you've created!



python3 hog_gui.py -f

Testing


Throughout this project, you should be testing the correctness of your code. It is good practice to test often, so that it is easy to isolate any problems.


Many of the tests are contained within the docstrings ofhog.py. Additional tests are implemented inhog_grader.py. To run all tests until a problem is found, run



python3 hog_grader.py

The command above runs all the tests until an error occurs, at which point it will stop and print some error messages. You can also run tests for a specific question with-q:



python3 hog_grader.py -q 1

Withinhog.py, we've also provided a way to call certain functions interactively from the terminal:



python3 hog.py -i roll_dice

Phase 1: Simulator


In the first phase, you will develop a simulator for the game of Hog.



Problem 1(1 pt). Implement theroll_dicefunction inhog.py, which returns the number of points scored by rolling a fixed positive number of dice: either the sum of the dice or 1. To obtain a single outcome of a dice roll, calldice(). You should call this functionexactly
num_rollstimes in your implementation. The only rule you need to consider for this problem isPig out.


As you work, you can addprintstatements to see what is happening in your program. Remove them when you are finished.


Test your implementation before moving on:



python3 hog_grader.py -q 1

You can also run an interactive test, which allows you to type in the dice outcome, which is helpful for catching cases that are not handled inhog_grader.py:



python3 hog.py -i roll_dice


Problem 2(1 pt). Implement thetake_turnfunction, which returns the number of points scored for the turn. You will need to implement theFree baconrule here. You can assume thatopponent_scoreis less than 100. Your implementation should callroll_dice.


Test your implementation before moving on:



python3 hog_grader.py -q 2

You can also runtake_turninteractively, which allows you to choose the number of rolls, the opponent's score, and the result of rolling the dice.



python3 hog.py -i take_turn


Problem 3(1 pt). Implementselect_dice, a helper function that will simplify the implementation ofplay(next problem). The functionselect_dicehelps enforce theHog wildspecial rule. This function takes two arguments: the scores for the current and opposing players.


Test your implementation before moving on:



python3 hog_grader.py -q 3


Problem 4(2 pt). Finally, implement theplayfunction, which simulates a full game of Hog. Players alternate turns, each using the strategy originally supplied, until one of the players reaches thegoalscore. When the game ends,playreturns the final total scores of both players, with Player 0's score first, and Player 1's score second.


Here are some hints:



  • Remember to enforce all the special rules! You should enforce theHog wildspecial rule here (by usingselect_dice), as well as theSwine swapspecial rule here.

  • You should use thetake_turnfunction that you've already written.

  • You can get the value of the other player (either 0 or 1) by calling the provided functionother. For example,other(0)evaluates to 1.

  • Astrategyis a function that determines how many dice a player wants to roll, depending on the scores of both players. A strategy function (such asstrategy0andstrategy1) takes two arguments: scores for the current player and opposing player. A strategy function returns the number of dice that the current player wants to roll in the turn. Don't worry about details of implementing strategies yet. You will develop them in Phase 2.


Test your implementation before moving on:



python3 hog_grader.py -q 4

You can also run an interactive test, where you can choose how many dice to roll for both players. You will want to addprintstatements to show the result of playing the game, but be sure to remove them before moving on to Phase 2.



python3 hog.py -i play

Once you are finished, you will be able to play a graphical version of the game. We have provided a file calledhog_gui.pythat you can run from the terminal:



python3 hog_gui.py

If you don't already have Tkinter (Python's graphics library) installed, you'll need to install it first before you can run the GUI.


The GUI relies on your implementation, so if you have any bugs in your code, they will be reflected in the GUI. This means you can also use the GUI as a debugging tool; however, it's better to run the tests first.


Congratulations! You have finished Phase 1 of this project!


Phase 2: Strategies


In the second phase, you will experiment with ways to improve upon the basic strategy of always rolling a fixed number of dice. First, you need to develop some tools to evaluate strategies.



Problem 5(1 pt). Implement themake_averagedfunction. This higher-order function takes a functionfnas an argument. It returns another function that takes the same number of arguments as the original. This returned function differs from the input function in that it returns the average value of repeatedly callingfnon the same arguments. This function should callfna total ofnum_samplestimes and return the average of the results.



Note:If the input functionfnis a non-pure function (for instance, therandomfunction), thenmake_averagedwill also be a non-pure function.


To implement this function, you need a new piece of Python syntax! You must write a function that accepts an arbitrary number of arguments, then calls another function using exactly those arguments. Here's how it works.


Instead of listing formal parameters for a function, we write*args. To call another function using exactly those arguments, we call it again with*args. For example,



>>> 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

Read the docstring formake_averagedcarefully to understand how it is meant to work.


Test your implementation before moving on:



python3 hog_grader.py -q 5


Problem 6(1 pt). Implement themax_scoring_num_rollsfunction, which runs an experiment to determine the number of rolls (from 1 to 10) that gives the maximum average score for a turn. Your implementation should usemake_averagedandroll_dice. It should print out the average for each possible number of rolls, as in the doctest formax_scoring_num_rolls.


Test your implementation before moving on:



python3 hog_grader.py -q 6

To run this experiment on randomized dice, callrun_experimentsusing the-roption:



python3 hog.py -r


Running experiments


For the remainder of this project, you can change the implementation ofrun_experimentsas you wish. By callingaverage_win_rate, you can evaluate various Hog strategies. For example, change the firstif False:toif True:in order to evaluatealways_roll(8)against the baseline strategy ofalways_roll(5). You should find that it loses more often than it wins, giving a win rate below 0.5.


Some of the experiments may take up to a minute to run. You can always reduce the number of samples inmake_averagedto speed up experiments.



Problem 7(1 pt). A strategy can take advantage of theFree baconrule by rolling 0 when it is most beneficial to do so. Implementbacon_strategy, which returns 0 whenever rolling 0 would giveat least
BACON_MARGINpoints and returnsBASELINE_NUM_ROLLSotherwise (these two global variables are located right above thealways_rollfunction).


Test your implementation before moving on:



python3 hog_grader.py -q 7

Once you have implemented this strategy, changerun_experimentsto evaluate your new strategy against the baseline. You should find that it wins more than half of the time.



Problem 8(1 pt). A strategy can also take advantage of theSwine swaprule. Implementswap_strategy, which



  1. Rolls 0 if it would cause a beneficial swap that gains points.

  2. RollsBASELINE_NUM_ROLLSif rolling 0 would cause a harmful swap that loses points.

  3. If rolling 0 would not cause a swap, then do so if it would giveat least
    BACON_MARGINpoints and rollBASELINE_NUM_ROLLSotherwise.


Test your implementation before moving on:



python3 hog_grader.py -q 8

Once you have implemented this strategy, updaterun_experimentsto evaluate your new strategy against the baseline. You should find that it performs even better thanbacon_strategy, on average.


At this point, run the entire autograder to see if there are any tests that don't pass.



python3 hog_grader.py


Problem 9(1 pt). Implementfinal_strategy, which combines these ideas and any other ideas you have to achieve a win rate of at least 0.59 against the baselinealways_roll(5)strategy. Some ideas:



  • Find a way to leave your opponent with four-sided dice more often.

  • If you are in the lead, you might take fewer risks. If you are losing, you might take bigger risks to catch up.

  • Vary your rolls based on whether you will be rolling four-sided or six- sided dice.



Note: You may want to increase the number of samples to improve the approximation of your win rate. The course autograder will compute your exact average win rate (without sampling error) for you once you submit your project, and it will send it to you in an email.


You can also play against your final strategy with the graphical user interface:



python3 hog_gui.py -f

The GUI will alternate which player is controlled by you.


Congratulations, you have reached the end of your first CIS 61 project!



Submission:Use below form to submit your project. Submit the completion of phase one before Feb 21 (this is a progress checkpoint). Re submit the entire project before the deadline.


======================================================


please write the submission form according to descriptions

Answered Same DayMar 03, 2021

Answer To: Project 1: The Game of Hog* I know! I'll use my Higher-order functions to Order higher rolls. * This...

Abr Writing answered on Mar 05 2021
139 Votes
hog_starter.zip
hog_starter/.DS_Store
hog_starter/__pycache__/autograder.cpython-38.pyc
hog_starter/__pycache__/dice.cpython-38.pyc
hog_starter/__pycache__/hog.cpython-38.pyc
hog_starter/__pycache__/ucb.cpython-38.pyc
hog_starter/autograder.py
"""Common utility functions for automatic grading."""
import sys, os, traceback
from doctest import DocTestFinder, DocTestRunner
from collections import namedtuple, defaultdict
import urllib.request, urllib.error
import re
import argparse
Test = namedtuple('Test', ['name', 'fn'])
TESTS = []
# set path for autograder to test current working directory
sys.path[0:0] = [ os.getcwd() ]
def test(fn):
"""Decorator to register a test. A test returns a true value on failure."""
TESTS.append(Test(fn.__name__, fn))
return fn
def test_all(project_name, tests=TESTS):
"""Run all TESTS. Exits with a useful code: 0 for ok, 1 for problems."""
for test in tests:
underline('Test {0}'.format(test.name))
try:
failure = test.fn(None)
except Exception as inst:
traceback.print_exc()
failure = True
if failure:
sys.exit(1)
print('All released tests passed')
print()
sys.exit(0)
class Timeout
Error(Exception):
pass
TIMEOUT = 20
def test_eval(func, inputs, timeout=TIMEOUT, **kwargs):
if type(inputs) is not tuple:
inputs = (inputs,)
result = timed(func, timeout, inputs, kwargs)
return result
def timed(func, timeout, args=(), kwargs={}):
"""Calls FUNC with arguments ARGS and keyword arguments KWARGS. If it takes
longer than TIMEOUT seconds to finish executing, a TimeoutError will be
raised."""
from threading import Thread
class ReturningThread(Thread):
"""Creates a daemon Thread with a result variable."""
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.result = None
def run(self):
self.result = func(*args, **kwargs)
submission = ReturningThread()
submission.start()
submission.join(timeout)
if submission.is_alive():
raise TimeoutError("Evaluation timed out!")
return submission.result
def check_func(func, tests,
comp = lambda x, y: x == y,
in_print = repr, out_print = repr):
"""Test FUNC according to sequence TESTS. Each item in TESTS consists of
(I, V, D=None), where I is a tuple of inputs to FUNC (if not a tuple,
(I,) is substituted) and V is the proper output according to comparison
COMP. Prints erroneous cases. In case of error, uses D as the test
description, or constructs a description from I and V otherwise.
Returns 0 for all correct, or the number of tests failed."""
code = 0
for input, output, *desc in tests:
try:
val = test_eval(func, input)
except:
fail_msg = "Function {0} failed".format(func.__name__)
if desc:
print(fail_msg, desc[0])
else:
print(fail_msg, "with input", in_print(input))
traceback.print_exception(*sys.exc_info(), limit=2)
code += 1
continue
if not comp(val, output):
wrong_msg = "Wrong result from {0}:".format(func.__name__)
if desc:
print(wrong_msg, desc[0])
else:
print(wrong_msg, "input", in_print(input))
print(" returned", val, "not", out_print(output))
code += 1
return code
def check_doctest(func_name, module, run=True):
"""Check that MODULE.FUNC_NAME doctest passes."""
func = getattr(module, func_name)
tests = DocTestFinder().find(func)
if not tests:
print("No doctests found for " + func_name)
return True
fn = lambda: DocTestRunner().run(tests[0])
result = test_eval(fn, tuple())
if result.failed != 0:
print("A doctest example failed for " + func_name + ".")
return True
return False
def underline(s):
"""Print string S, double underlined in ASCII."""
print(s)
print('='*len(s))
def check_for_updates(index, filenames, version):
print('You are running version', version, 'of the autograder')
try:
remotes = {}
for file in filenames:
remotes[file] = urllib.request.urlopen(
os.path.join(index, file)).read().decode('utf-8')
except urllib.error.URLError:
print("Couldn't check remote autograder")
return
remote_version = re.search("__version__ = '(.*)'",
remotes[filenames[0]])
if remote_version and remote_version.group(1) != version:
print('Version', remote_version.group(1),
'is available with new tests.')
prompt = input('Do you want to automatically download these files? [y/n]: ')
if 'y' in prompt.lower():
for file in filenames:
with open(file, 'w') as new:
new.write(remotes[file])
print('\t', file, 'updated')
exit(0)
else:
print('You can download the new autograder from the following links:')
for file in filenames:
print('\t' + os.path.join(index, file))
print()
def run_tests(name, remote_index, autograder_files, version, **kwargs):
parser = argparse.ArgumentParser(
description='A subset of the autograder tests for Hog.')
parser.add_argument('-q', '--question', type=int,
help='Run tests for the specified question')
parser.add_argument('-v', '--version', action='store_true',
help='Prints autograder version and exits')
args = parser.parse_args()
check_for_updates(remote_index, autograder_files, version)
if args.version:
exit(0)
elif args.question and 0 < args.question <= len(TESTS):
tests = [TESTS[args.question-1]]
else:
tests = TESTS
test_all(name, tests)
hog_starter/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.
Types of dice:
- Dice can be fair, meaning that they produce each possible outcome with equal
probability.
- For testing functions that use dice, we use deterministic dice that always
cycle among a fixed set of values when rolled.
"""
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.
This function uses Python syntax/techniques not yet covered in this course.
>>> dice = make_test_dice(1, 2, 3)
>>> dice()
1
>>> dice()
2
>>> dice()
3
>>> dice()
1
>>> dice()
2
"""
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
index = (index + 1) % len(outcomes)
return outcomes[index]
return dice
hog_starter/hog.py
"""The Game of Hog."""
from dice import four_sided, six_sided, make_test_dice
from ucb import main, trace, log_current_line, interact
GOAL_SCORE = 100 # The goal of Hog is to score 100 points.
######################
# Phase 1: Simulator #
######################
# Taking turns
def roll_dice(num_rolls, dice=six_sided):
"""Roll DICE for NUM_ROLLS times. Return either the sum of the outcomes,
or 1 if a 1 is rolled (Pig out). This calls DICE exactly NUM_ROLLS times.
num_rolls: The number of dice rolls that will be made; at least 1.
dice: A zero-argument function that returns an integer 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.'
"*** YOUR CODE HERE ***"
k ,new_roll= 0,0
score = [new_roll]
while k < num_rolls:
new_roll = dice()
score.append(new_roll)
k = k + 1
if 1 in score:
return 1
return sum(score)
def take_turn(num_rolls, opponent_score, dice=six_sided):
"""Simulate a turn rolling NUM_ROLLS dice, which may be 0 (Free bacon).
num_rolls: The number of dice rolls that will be made.
opponent_score: The total score of the opponent.
dice: A function of no args that returns an integer outcome.
"""
assert type(num_rolls) == int, 'num_rolls must be an integer.'
assert num_rolls >= 0, 'Cannot roll a negative number of dice.'
assert num_rolls <= 10, 'Cannot roll more than 10 dice.'
assert opponent_score < 100, 'The game should be over.'
"*** YOUR CODE HERE ***"
if num_rolls == 0:
return int(max(str(opponent_score))) + 1
elif num_rolls > 0:
return roll_dice(num_rolls, dice)
# Playing a game
def select_dice(score, opponent_score):
"""Select six-sided dice unless the sum of SCORE and OPPONENT_SCORE is a
multiple of 7, in which case select four-sided dice (Hog wild).
>>> select_dice(4, 24) == four_sided
True
>>> select_dice(16, 64) == six_sided
True
>>> select_dice(0, 0) == four_sided
True
"""
"*** YOUR CODE HERE ***"
if (score + opponent_score) % 7 == 0:
return four_sided
return six_sided
def other(who):
"""Return the other player, for a player WHO numbered 0 or 1.
>>> other(0)
1
>>> other(1)
0
"""
return 1 - who
def play(strategy0, strategy1, goal=GOAL_SCORE):
"""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.
"""
who = 0 # Which player is about to take a turn, 0 (first) or 1 (second)
score, opponent_score = 0, 0
"*** YOUR CODE HERE ***"
strategy_list = [strategy0,strategy1]
score_list = [score,opponent_score]
while score_list[who] < goal and score_list[other(who)] < goal:
dice = select_dice(score_list[who], score_list[other(who)])
num_rolls = strategy_list[who](score_list[who], score_list[other(who)])
last_roll = take_turn(num_rolls, score_list[other(who)], dice)
score_list[who] = score_list[who] + last_roll
if (score_list[who] == (2 * score_list[other(who)])) or \
(score_list[who] == ((1/2) * score_list[other(who)])):
score_list[who], score_list[other(who)] = score_list[other(who)], score_list[who]
who = other(who)
return score_list[0], score_list[1]
#######################
# Phase 2: Strategies #
#######################
# Basic Strategy
BASELINE_NUM_ROLLS = 5
BACON_MARGIN = 8
def always_roll(n):
"""Return a strategy that always rolls N dice.
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.
>>> strategy = always_roll(5)
>>> strategy(0, 0)
5
>>> strategy(99, 99)
5
"""
def strategy(score, opponent_score):
return n
return strategy
# Experiments
def make_averaged(fn, num_samples=1000):
"""Return a function that returns the average_value of FN when called.
To implement this function, you will have to use *args syntax, a new Python
feature introduced in this project. See the project description.
>>> dice = make_test_dice(3, 1, 5, 6)
>>> averaged_dice = make_averaged(dice, 1000)
>>> averaged_dice()
3.75
>>> make_averaged(roll_dice, 1000)(2, dice)
6.0
In this last example, two different turn scenarios are averaged.
- In the first,...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here