It is assumed you are keeping up to date with the lectures and labs.
Some additional git information is available through the
git quick notes,
git project/lab submission, and
lecture resources pages.
This lab focuses on introducing our use of git for obtaining and submitting this year's labs and project, and also as a refresher on basic C++ programming.
In part 1 of lab 1, you'll be following the lab instructions to:
Once we've run through that cycle once, we'll start on part 2, the actual design and coding
portion of the lab.
Details on the product requirements/specifications and design are in the
second portion of this page.
| If you've taken 161 in the past then you probably already have a csci161
directory. You're not going to want to mix up this year's 161 stuff with the old stuff,
so before you mkdir csci161 I'd suggest doing something like mv csci161 old161 |
When doing the lab, take some time to ensure you understand what each command is supposed to do before you run it.
After running each command take some time to ensure you understand what actually did happen. (In the great scheme of things, it doesn't do anyone much good if you're just typing commands without understanding them.)
| To reiterate: for this to work correctly, you will need to follow the lab instructions carefully and in the sequence given: take your time (we have lots!), read the description, run the command, and look at the results each command gives back before moving on to the next one. |
Git instructions for obtaining, updating, and submitting the lab
0. double-check that the instructor has posted the repo
Run the command below, and check to make sure that the csci 161 lab1
repository appears in the list (if it isn't posted yet then don't proceed yet)
ssh -x csci info
This lists all the repositories you currently have access to, and the one you're specifically looking for will look like csci161/lab1.
A WARNING AGAINST TRYING THE REMAINING STEPS BEFORE YOUR LAB SESSION:
none of the remaining lab instructions will work correctly until the repository
has been set up by the instructor, which may not happen until the day of the lab
1. create the server-side copy of the repo
To do this, enter the following command:
ssh -x csci fork csci161-01/lab1 csci161-01/$USER/lab1
(actually type $USER, as it is a pre-existing variable that contains your linux userid)
You can check if this worked correctly by running the ssh -x csci info command
again, and it should now show an additional repository:
csci161-01/yourusername/lab1
2. clone your own local working copy
If you haven't previously done so, you'll want to create a csci161 directory
in your home directory, i.e.
cd
mkdir csci161
cd csci161
now clone your server-side copy, creating a working lab1 repository here
git clone csci:csci161-01/$USER/lab1 lab1
We can check if this has been successful by entering our lab1 directory and checking which files are present:
cd lab1 lsThis should show lab1.cpp, lab1.h, ranking.h, ranking.cpp, test1.h, test1.cpp, makefile, and README
3. compile/run the two skeletal lab1x and test1x programs, and try a git push
We'll use the supplied makefile to compile the files and create a lab1x executable and then a test1x executable:
make all
You can try running the executables (though they'll do nothing yet) using
./lab1x
./test1x
If you edit lab1.cpp and test1.cpp (use whatever editor you like), you'll see they each contain just an empty main and the #include for the lab1.h or test1.h.
In each of them, add the #include for the iostream and have a cout in main,
e.g. std::cout << "Hi!" << std::endl;
Save the files then from the command line recompile with
make all
Hopefully it recompiles test1.o, lab1.o, test1x, and lab1x
We'll try submitting your repository, just to make sure everything has been set up correctly. We'll git add the two files we edited and use git commit to take a snapshot of the current project state:
git add lab1.cpp git add test1.cpp git commit -m "added trial output to lab1, test1" git push(The commit will produce a couple of lines of messages about how many files were changed leading up to the commit and the create mode for lab1x.
This will have copied your work back to the server where the instructor can access/mark it. You can repeat this cycle as often as you like, each time it sends the updated content as long as you remember to git add each of the files you've updated and do a git commit before your git push. (Thus the instructor will always have your most up to date version for marking.)
If anything goes wrong see step 4 for some basic trouble-shooting, and if that doesn't solve the problem then talk to your instructor for help.
The structural restrictions for the lab1 code are as follows:
Be sure you read, understand, and follow the code standards for CSCI 161, as they will most likely be different than the standards you were given in your CSCI 159/160 course. Marks will be deducted for failing to follow standards.
I also strongly recommend that after each successful series of modifications to your .h/.cpp files
you do a
git add filename for each of the edited files
git commit -m "some message clearly and uniquely describing the changes you just made"
These use git to take and store a snapshot of your latest set of changes, which will subsequently allow you to roll back to the current state of your code if you ever need to (e.g. if you accidentally trash your lab1.cpp file later, or make changes you wish to undo).
4. Push your finished lab back to the git server
Assuming you've done the git adds and commits as discussed above,
we need to push all your updates to the server for marking:
git push
Note that if you forget any of those steps (the adds, commit, or push) then the changes you just made will not reach the instructor for marking.
If you're ever in doubt about the current state of your repository you can run the command git status, which should eventually give a message like "up-to-date with origin master". Here are the most common other messages it may give you:
What to do if, on a push, you get an error like this:
FATAL: W any csci161-01/lab1 yourusername DENIED by fallthru (or you mis-spelled the reponame) fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.Note the push should be trying to go to "csci161-01/yourusername/lab1", not to "csci161-01/lab1" (which is the instructor repo), so this error message suggests that in step 2 you may have cloned the wrong repo, e.g. you may have done git clone csci:csci161-01/lab1 instead of the correct git clone csci:csci161-01/$USER/lab1 thus now in step 4 when you push it's trying to overwrite the instructor's version, so is giving you that permission error.
It's not an uncommon mistake, and easily fixed.
We need your lab1 repo to think it was cloned from *your* space
on the git server, not from the instructor's, so we'll reset where
your repository thinks it came from using the following command:
After that, you should be able to push normally. If you ever want to completely start over:
|
Here we want to go over how/why we've broken the files up the way we have, how the #includes work to glue the parts together, and how the makefile compiles the individual .cpp files and links the resulting .o (object) files together to form working executables.
First, the organization of the files:
Next, the organization of the makefile:
| Copy of the makefile contents |
options= -Wall -Wextra -pedantic -g
all: lab1x test1x
lab1x: lab1.o ranking.o
g++ lab1.o ranking.o -o lab1x ${options}
test1x: test1.o ranking.o
g++ test1.o ranking.o -o test1x ${options}
lab1.o: lab1.cpp lab1.h ranking.h
g++ lab1.cpp -c ${options}
test1.o: test1.cpp test1.h ranking.h
g++ test1.cpp -c ${options}
ranking.o: ranking.cpp ranking.h
g++ ranking.cpp -c ${options}
clean:
rm -f lab1.o test1.o ranking.o lab1x test1x
|
Testing the makefile:
Second, a unit-testing program:
for lab1 this testing portion is optional, worth up to 30% bonus marks
Once those are taken care of, our only remaining challenge will be to actually write, test, and debug the code!
For lab1, the user will run the program, using a command line argument to provide
the name of the file to be used, e.g.
./lab1x foods
The program will read the items/rankings stored in the file, with the assumption that each line of the file contains the item's rank as a positive integer and then the item name is the rest of the text on the line, e.g.
1830 hawaiian pizza 12 brussel sprouts 1900 salmon sashimi 1750 those dutch olie bols things 900 capsicum(no personal bias at all there anyplace in that example, nothing to see here, move along, move along)
The program will then randomly pick two different items from the list and ask the user which of the two they would prefer right now (they can pick the first, the second, or a tie/draw). The rankings of both items will then be adjusted up/down based on the user's response, and the updated rankings written back to the original file.
(i) When asking the user about the head-to-head matchup, they should be presented with the two choices and the choice of which is preferred: First (f), second (s), or draw (d). The user will respond with one of f, s, or d.
(ii) In a match of X vs Y, the actual score based on the user's response is calculated as 1 for X preferred, 0 for Y preferred, and 0.5 for a draw.
An expected score, based on the relative rankings of X and Y, is used as part of
the formula for updating the rankings of X and Y.
The expected score if X has rank Rx and Y has rank Ry is calculated using the formula
1 / (1 + pow(10, (Ry - Rx)/400.0))
After we have the user's response and know the actual result of the matchup,
the change to X's rank is calculated using the formula
16 * (actualScore - expectedScoreForX)
X's rank goes up if the adjustment is positive, down if the adjustment is negative.
(Y's score would be calculated similarly, with the roles reversed)
(iii) The three main possible sources of error are in the filename (provided as the command line argument to main), the file's content, and the user response to the head-to-head matchup.
The desired error-handling behaviour is as follows:
Note that 'appropriate' means the error message clearly identifies the nature of the problem, giving the user a better chance to use the program correctly in the future.
To decompose this into an appropriate suite of functions, divided in a suitable way across our files, we need to consider which actions/support are more associated with the rankings specifically (hence going into ranking.h/cpp) and which are more associated with the specific use of the rankings in this program (hence going into lab1.h/cpp).
With that in mind, we can think of the actions we would like our lab1's main to perform, and sketch out what that might look like, e.g.
int main(int argc, char* argv[])
{
// quit if the user didn't pass valid arguments
if (!checkUsage(argc, argv)) {
cerr << "Program terminated early, unable to proceed\n";
return 0;
}
string filename = argv[1]; // filename is more intuitive later
welcome(); // program intro for user
// load the rankings from the named file
ItemRank rankings[MaxItems];
int numItems = readRankings(filename, rankings, MaxItems);
// run a head-to-head matchup, quit if the matchup is unsuccessful
if (!runMatchup(rankings, numItems)) {
cerr << "Program terminated early, rankings file " << filename << " unchanged\n";
return 0;
}
// write the updated rankings back to the file
if (!writeRankings(filename, rankings, numItems)) {
cerr << "Unable to updated rankings file " << filename << endl;
}
return 0;
}
|
If we decide the checkUsage and welcome belong in lab1.h/cpp and the others belong in ranking.h/cpp, and the actual definition of the struct needs to be in ranking.h, then our two .h files might look something like
// the lab1.h, including a const for the maximum array size/number of items supported #pragma once #include "ranking.h" const int MaxItems = 50; // upper limit on the number of items to be read/stored/used // give the user a description of the program behaviour void welcome(); // check a filename is non-empty and contains only valid characters // return true iff good bool okfilename(string fname); // check the user passed one argument, to be used as a filename bool checkUsage(int numArgs, char *arguments[]); |
You're not required to use the design above, feel free to use it, or your own, or some hybrid.
runXtests(); runYtests(); runZtests();Then you could separate the different issues associated with X, Y, and Z, and still have a relatively clean design.
Implementation and submission
Your task, by the lab submission deadline, is to complete all the functions detailed above and conforming to their specifications, and to do a final submission before the deadline.
Note that for the final submission, to be certain you're submitting all the relevant changes, I recommend the following:
git add lab1.cpp git add lab1.h git add test1.cpp git add test1.h git add ranking.cpp git add ranking.h git commit -m "final lab1 submission" git pushHelpful starter code: command line arguments and file i/o
The code below is only a snippet of what's needed (and not arranged into our .h/.cpp design style), but it does show some of the use and error checking surrounding the use of command line arguments and relevant file reading.
#include <iostream>
#include <fstream>
#include <string>
// read rankings from contents of specified file (just echoes to screen)
void readRankings(char* fname)
{
// attempt to open the specified file
std::ifstream rfile;
rfile.open(fname);
// handle cases where it failed to open
if (!rfile.is_open()) {
std::cerr << "Unable to open input file " << fname << ", ending program" << std::endl;
}
// read file contents line by line, assuming each line is rank then the description
do {
std::string rank;
std::string description;
rfile >> rank; // get the next rank (the first word on the next non-empty line)
if (!rfile.eof()) {
// not end-of-file, so we did actually get a rank, now get the description
getline(rfile, description); // rest of the line
if (!rfile.eof()) {
// not end-of-file, so we did actually get something (though maybe just eoln)
std::cout << "Rank: " << rank << ", description: " << description << std::endl;
} else {
// hit end of file in mid-line
std::cerr << "Error: end of file after rank " << rank << std::endl;
}
}
} while (!rfile.eof());
rfile.close();
}
int main(int argc, char* argv[])
{
// check the correct number of arguments are passed (at least one filename)
if (argc < 2) {
std::cerr << "Incorrect usage, expecting: " << argv[0] << " filename" << std::endl;
} else {
// pass the filename to readRankings to attempt a read
readRankings(argv[1]);
}
}
|
SAMPLE RUNS
A number of sample runs of the lab1x program are shown below, starting with the command the user enters to start the lab1x program running and showing the file content before and after the run. (For the sake of clarity in the example, user input is shown in bold italics - this is of course not something your program actually could/would do.)
For these sample runs I've created a directory named "tests" in my lab1 directory (mkdir tests) and created a handful of different testfiles to try out the program. You can do the same using mkdir tests and then your preferred editor to make a bunch of files in it.
Your screen output doesn't have to exactly match what's shown below as long as the general level of information provided for the user is similar, but the file content changes should match as shown (the order of items and values).
| Program run | Original file | File afterward |
./lab1x tests/posted Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Given the choice between the following two items: First: capsicum Second: salmon sashimi please enter F for first, S for second, or D for draw (tied) F Updated rankings written back to file tests/posted | 1830 hawaiian pizza 12 brussel sprouts 1900 salmon sashimi 1750 those dutch olie bols things 900 capsicum | 1830 hawaiian pizza 12 brussel sprouts 1885 salmon sashimi 1750 those dutch olie bols things 915 capsicum |
./lab1x tests/single Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Error: unable to run a head-to-head matchup as there are fewer than two items Program terminated early, rankings file tests/single unchanged | 1200 justthis | 1200 justthis |
./lab1x tests/nothing Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Error: unable to run a head-to-head matchup as there are fewer than two items Program terminated early, rankings file tests/nothing unchanged | (the file is empty) | (the file is empty) |
./lab1x tests/nosuchfile Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Error: unable to open file tests/nosuchfile for reading Error: unable to run a head-to-head matchup as there are fewer than two items Program terminated early, rankings file tests/nosuchfile unchanged | (the file doesn't exist) | (the file doesn't exist) |
./lab1x tests/nodesc Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Error: invalid description, discarding line 1750 Given the choice between the following two items: First: salmon sashimi Second: brussel sprouts please enter F for first, S for second, or D for draw (tied) S Updated rankings written back to file tests/nodesc | 1830 hawaiian pizza 12 brussel sprouts 1900 salmon sashimi 1750 900 capsicum | 1830 hawaiian pizza 27 brussel sprouts 1885 salmon sashimi 900 capsicum |
./lab1x tests/norank Welcome to the lab1 item ranking program This program reads a collection of item rankings from a file (whose name is provided as a command line argument, e.g. ./lab1x foods) Presents you with a choice between two items, and uses your preference to update their rankings (rewriting the file) Error: invalid rank, discarding line brussel sprouts Given the choice between the following two items: First: capsicum Second: salmon sashimi please enter F for first, S for second, or D for draw (tied) D Updated rankings written back to file tests/norank | 1830 hawaiian pizza brussel sprouts 1900 salmon sashimi 1750 those dutch olie bols things 900 capsicum | 1830 hawaiian pizza 1893 salmon sashimi 1750 those dutch olie bols things 907 capsicum |