The goal of this lab is to practice using classes and inheritance in C++
by implementing
Chicken,
a simple strategy game. You will also gain experience with Makefiles,
generating random numbers, and utilizing polymorphism.
To get started, run update35 from a computer science lab
computer. The program update35 will create a cs35/labs/02
directory in your CS home directory, and the program handin35
will subsequently submit files from that directory.
Introduction
The game of Chicken models two drivers heading
toward each other at high speed. Each driver faces the same choice: to
either swerve or continue driving straight. If both drivers drive
straight then they collide and both drivers badly lose.
If a driver Alice drives
straight and her competitor Bob swerves, then Alice wins and Bob loses.
The outcome is neutral if both drivers swerve.
Chicken can be formalized mathematically as a two-person
non-zero-sum
game. Specifically, on a given turn each player must commit to swerving or
driving straight without knowing her competitor's decision. Points can
then be awarded based on both players' actions:
- If both drivers drive straight (i.e., crash), then both drivers are
awarded -10 points.
- If a driver Alice drives straight and her competitor Bob swerves,
then Alice is awarded +1 points and Bob is awarded -1 points.
- If both drivers swerve, then both drivers are awarded 0 points.
Chicken is easy if players may collude to determine their individual
strategies, but players may not collude: each player must choose her move
without knowing what her competitor will choose. If the game is repeated
then strategies become complicated
because a pure strategy (either always drive straight or always
swerve) could result in a large negative score depending on your
opponents strategy.
In this assignment you will implement the Chicken game and use C++
inheritance to implement player classes to play the game.
That is, you will implement the Chicken game such that different
types of players can join the game. One of your
player classes (the HumanPlayer)
will allow a human player to type input to play the game,
and another player class (the AwesomePlayer) will enable a computer
player to automatically play the game. You could theoretically
have many different types of Players join in the game, each with their
own set of strategies.
Implementation details
This lab is more complicated than the previous, but we have provided much of
the implementation already. An overview of the different components:
- main.cpp: the main function for running the entire chicken
simulation. main.cpp defines a text-based interface that allows a user to
repeatedly play the Chicken game. The user first chooses what type of
players to match-up and how many rounds to simulate.
This program is mostly implemented for you; you need only to
extend main.cpp to allow the user to create HumanPlayers and
AwesomePlayers, but only after you've implemented those two classes.
- The Chicken class (chicken.h and chicken.cpp): Defines an object that plays a game of chicken. A Chicken object
keeps track of the 2 players in the game, their respective, scores,
and the total number of rounds played so far. The header file
should not be modified, but you must implement in chicken.cpp
the class methods to get the game working.
- The Player class (player.h and player.cpp):
The default Player class from which all other chicken players inherit from.
You should not edit these files as they it has been implemented
for you. The default Player flips a coin to make its move: that is, it swerves or drives straight randomly with equal (50%) probability.
- The AwesomePlayer class (awesomeplayer.h and
awesomeplayer.cpp): inherting from the base Player class,
this class is where you will define your strategy. To do so, you should
extend awesomeplayer.h to define any data members
your AwesomePlayer needs to succeed. Then, implement your awesome
AwesomePlayer strategy in the appropriate class methods
in awesomeplayer.cpp. You may add additional class methods
if necessary, but they should have private access.
- The HumanPlayer class (humanplayer.h and
humanplayer.cpp): Uses
standard input and output to allow a lowly human to play Chicken.
You may call this class anything you want (it does not need to be
HumanPlayer) but you must declare and define your class so that it can
be a Player in the Chicken game.
- Makefile: A make file to help build your program. The
Makefile is nearly complete; you will need only to extend
it slightly to incorporate your HumanPlayer and AwesomePlayer
implementations.
An implementation strategy
We recommend the following implementation strategy which allows you
to continually test each part of your program as you implement it:
- After you've run update35, compile and run the initial code
using the make program and the resulting ./chicken
executable program. The program should compile and allow you to create
default players, but result in a Segmentation fault because the
Chicken game is not implemented.
- Implement the Chicken game (i.e., the Chicken class)
and test your implementation using the
default players. When you are done
the default players should run without error but play
poorly against each other. To implement the Chicken game, first analyze
the Chicken class interface as defined in
Chicken.h, paying attention to the data it stores.
Then implement the Chicken constructor in
Chicken.cpp, which initializes the two Player (pointers)
as well as their scores and the current round number of
the game. Finally implement the .accessor methods as well as the
method playOneRound. This last function simulates one round of
chicken by obtaining a move from each player, determining the outcome
of those moves, updating the players' scores and round number, and finally
sending the results back to each player.
- Extend main.cpp to allow the creation and use of
AwesomePlayers, extend the Makefile to compile the AwesomePlayer
class, and then implement and test a simple AwesomePlayer
strategy. Your initial AwesomePlayer implementation need not be so
awesome; just make sure you understand C++ inheritance and that your
program behaves as expected.
- Extend main.cpp to allow the creation and use of
HumanPlayers, extend the Makefile to compile the HumanPlayer
class, and then implement and test a HumanPlayer class.
At this point you should be able to play (as a human user) against
either the default player or your initial AwesomePlayer strategy.
- Once everything is working, make your AwesomePlayer live up to its name.
I will be pitting your AwesomePlayer against your classmates' AwesomePlayers to see who's AwesomePlayer is...awesomest!
There may be more than pride on the line.
Advice and hints
- Can you easily beat the default Player strategy? For a given
simple strategy (either all swerve or all straight) what outcome
do you expect against the default Player? These simple strategies
are sensible choices for your initial not-so-awesome AwesomePlayer
implementation.
- To win the inter-student competition, you don't need to beat
each individual opponent; you just need to do well overall.
- Using pseudorandomness in C++ isn't as straightforward as using
pseudorandomness in Python, but the default Player class initializes
a pseudorandom number generator. Because you inherit from the
Player class and initialize with the Player(string nam)
constructor, the pseudorandom number generator will be initialized for
your AwesomePlayer implementation, too.
You can get a random double from 0.0 to 1.0 (much like Python's
random() function) by using the drand48() function.
If you want to know more, type man drand48 at your terminal
prompt for details.
Submit
Once you are satisfied with your program, hand it in by typing
handin35 at the unix prompt.
You may run handin35 as many times as you like, and only the
most recent submission will be recorded. This is useful if you realize
after handing in some programs that you'd like to make a few more
changes to them.