The goal of this lab is to practice using classes and inheritance in C++
by implementing
Chicken,
a simple strategy game.
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 macho (read: stupid) 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 neither pure strategy (either always drive straight or always
swerve) is optimal; depending on your opponent's strategy, both pure
strategies can result in a large negative score if many games are played.
In this assignment you will implement the Chicken game and use C++
inheritance to implement player classes to play 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.
When you are done, your automated player will compete
against the automated players for all other students in the course;
specifically, each student's AwesomePlayer will play every other
student's AwesomePlayer 10000 consecutive rounds, and the student
with the greatest total score (after the 420000 rounds against all other
students) will win a prize.
Implementation details
Unlike the previous lab, for this lab we've provided much
code that you need to understand, implement, and/or extend. Our code
includes:
- chicken.h and chicken.cpp: Header and implementation
files for a player's move (defined as either SWERVE or drive STRAIGHT)
and a single
multi-round Chicken game. We've declared the Chicken class for
you, and you should not need to edit the header file. You must complete
the Chicken implementation in chicken.cpp.
- player.h and player.cpp: Header and implementation
files for a default Player class. We've defined and implemented the
(poorly playing) default Player for you, and you should not edit these
files. The default Player simply swerves or drives straight randomly
with equal (50%) probability.
- awesomeplayer.h and awesomeplayer.cpp: Header
and implementation files for your automated AwesomePlayer class. The
AwesomePlayer inherits from the Player
class, and therefore can be used as a Player in the Chicken game.
You will implement your best strategy in this class. To do so, you should
extend awesomeplayer.h to define any data members
your AwesomePlayer needs to s.cppeed, and then implement your awesome
AwesomePlayer strategy in awesomeplayer.cpp.
- humanplayer.h and humanplayer.cpp: Header and
implementation files for a HumanPlayer class, which 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.
- main.cpp: 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.
- 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 .cppessor 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.