SETTINGS
Appearance
Language
About

Settings

Select a category to the left.

Appearance

Theme

Light or dark? Choose how the site looks to you by clicking an image below.

Light Dark

Language

Preferred Language

All content on utk.claranguyen.me is originally in UK English. However, if content exists in your preferred language, it will display as that instead. Feel free to choose that below. This will require a page refresh to take effect.

About

"utk.claranguyen.me" details

Domain Name: claranguyen.me
Site Version: 3.0.1
Last Updated: 2019/08/18
Synopsis

Getting Started

  • The lab writeup is here: http://web.eecs.utk.edu/~bvanderz/cs140/Labs/Lab6/
  • This is a lab assignment unique to BVZ's version of CS140, replacing the Keno lab. Do NOT go to Dr. Plank's site for the writeup.
  • This is a normal lab assignment that's due in a week (March 11th, 2020). No intermediate submissions or anything special.
  • Get familiar with #include <list>, as well as iterators. You'll need it.

Side files

Usually, for lab "lectures", I prepare a digital copy of my notes to write on the whiteboard. This time, because I'm in a good mood, I have decided to make those public. You may access them here:
  • 2020-03-04 - Candy Crush.pdf - Contains basically everything I write on the board (and more). Getting started procedure, submission command, suggested function order, function implementation details, examples (with diagrams), and more.
Getting Started

Copying files over

You only need to copy over two files for this lab assignment. But, to make debugging easier, you should also copy over the input.txtfile.
UNIX Command
UNIX> mkdir src include bin obj input
UNIX> cp ~bvanderz/cs140/Labs/Lab6/candyCrushTest.cpp src
UNIX> cp ~bvanderz/cs140/Labs/Lab6/candyCrush.hpp include
UNIX> cp ~bvanderz/cs140/Labs/Lab6/input.txt input
You'll be creating candyCrush.cpp on your own, and it will implement the methods given in candyCrush.hpp. Since you can modify both of those files (but you can't modify or delete any methods specified in candyCrush.hpp), I'm requiring that you submit both of them.

Additional Asset (makefile)

Because I'm in a good mood, there's a few additional things I'll give you to help you out. For starters, notice how there's no makefile in the lab directory. I'll supply my own, and you might like it. To grab this file, either get it from my server or by running the following command on Hydra/Tesla:
UNIX Command
UNIX> cp ~ssmit285/public/code/cs140_lab6_makefile makefile
You may run it like any other makefile. It also lets you do make clean to clean up the executable.

However, I made it also able to create a valid TAR file for submission. My treat. To do this, run the following:
UNIX Command
UNIX> make package
You'll see a shiny lab6.tar be created. Cool huh?

Additional Asset (candyCrush.cpp)

Here's a template file for your code. It outlines the overall structure of the functions you need to implement. It compiles by default. Obviously, you just have to implement the functions from there. To grab it, either get it from my server or from the following command on Hydra/Tesla:
UNIX Command
UNIX> cp ~ssmit285/public/code/candyCrush_template.cpp src/candyCrush.cpp
An introduction to std::list
In CS102, you learned how std::vector works. I'm not sure if you knew at the time, but that was your introduction to data structures. A vector is a dynamically resizable "array" where the memory is contiguous. You may access one with operator[] just like a normal array too. You get the idea (I hope).

Well, now let's talk about a new data structure... std::list. This is C++'s implementation of a doubly-linked list, complete with similar syntax to std::vector that you're used to coding with. This means, however, that you have to deal with the properties of a linked list as well.

The basics

Let's take a look at a simple program with std::list:
C++ Code (list_sample1.cpp)
#include <iostream>
#include <string>
#include <list>

using namespace std;

int main() {
	//Declare our list to be a list of integers (similar to vector)
	list<int> int_list;

	//Insert some elements at the end
	int_list.push_back(1);
	int_list.push_back(2);
	int_list.push_back(3);
	int_list.push_back(4);

	//Unlike vectors, we can push to the front as well (efficiently)!
	int_list.push_front(0);

	//Print out first and last elements
	cout << int_list.front() << " "
	     << int_list.back () << endl;

	//Lists also let us pop from both the front and the back
	int_list.pop_front();
	int_list.pop_back();

	//Print out new first and last elements
	cout << int_list.front() << " "
	     << int_list.back () << endl;

	return 0;
}
If we run this, we get the following output:
Output Text
0 4
1 3
Cool right? We can insert into both ends of the list, and also get the front and back values. Furthermore, we can even pop off the list in both directions.

"But Clara! I can push to the front in a vector and erase!"

Sure, it's possible with insert and erase, but is that efficient with a vector? if you insert into the front of a vector, you are having to copy all elements in the vector over by 1, which is costly. The same applies for deleting. You have to copy all elements over and then resize.

For list, we don't have such a penalty since it's node-based. We have pointers to the front and back of the list, and those nodes point to the next and previous ones, allowing us to insert and delete in the front and back without having to do any copying.

Educational rants aside, let's move on.

Traversing through linked lists: The iterator subclass

In that previous example, I only demonstrated two ways to grab values from std::list, being front and back. What about elements in the middle? Well, we must go through every element in the list until we reach the one we want. This is one of the tradeoffs of linked lists. Let's demo it.
C++ Code (list_sample2.cpp)
#include <iostream>
#include <string>
#include <list>

using namespace std;

int main() {
	//Declare our list to be a list of integers (similar to vector)
	list<int> int_list;

	//Insert {0..4}
	int_list.push_back(0);
	int_list.push_back(1);
	int_list.push_back(2);
	int_list.push_back(3);
	int_list.push_back(4);

	//Create an "iterator" to go through the list.
	list<int>::iterator it;

	for (it = int_list.begin(); it != int_list.end(); it++) {
		cout << *it << endl;
	}

	return 0;
}
Running this gives us this output:
Output Text
0
1
2
3
4

Confused? Let's break it down.
C++ Fragment
list<int>::iterator it;
This will create an iterator it that can traverse a list<int>. That's it. It's just a normal variable with a slightly longer name than you might've liked.

C++ Fragment
for (it = int_list.begin(); it != int_list.end(); it++) {
This isn't the same as your average for-loop. Iterators allow for a operator++ to proceed to the next element, kind of like doing i++ on an integer. They are bi-directional, which means you can also do it-- to go backwards. int_list.begin() returns an iterator pointing to the very first element in the list. int_list.end() is a little more confusing. It points to the very last element in the list... plus 1. For consistency with the lecture notes, the element beyond the last one and the element before the first one is known as the sentinel node. To visualise this,
[S][0][1][2][3][4][S]
    ^              ^
    |              |
    .begin()       .end()
That misalignment is intentional. It literally points to the element past the last one in the list. Take a look at that loop again, it's going until it == int_list.end(). If it pointed to the last element, we'd end up going through every element except for the last one. That's not what we want do we?

C++ Fragment
cout << *it << endl;
Most iterators can be dereferenced (more on that later in the course). If we do *it, it'll return the element in the list that the iterator is pointing to.

"So I can't use operator[]?"

Nope. And you wouldn't want to. It'll just start at list.begin() and go until the n-th element, which is painfully slow.
More on iterators in std::list
Here's some stuff you might actually need for the lab assignment at hand. With iterators, you are also allowed to perform deletion of one specific element, or an entire range of elements. This is done with list.erase. I'll give an example of both.

Before I start, take a look at the documentation for list.erase. I'll go in-depth slightly, but I want you to get used to reading pages like this. They'll tell you everything you need to know.

Single Element Deletion

Let's delete an element in std::list. Behold the following C++ program:
C++ Code (list_sample3.cpp)
#include <iostream>
#include <string>
#include <list>

using namespace std;

int main() {
	//Declare our list to be a list of integers (similar to vector)
	list<int> int_list;

	//Insert some elements at the end
	int_list.push_back(0);
	int_list.push_back(1);
	int_list.push_back(2);
	int_list.push_back(3);
	int_list.push_back(4);

	//Create an "iterator" to go through the list.
	list<int>::iterator it;

	//Let's delete the 3rd element. do a "++" two times so "it" points at it.
	it = int_list.begin();
	it++;
	it++;

	int_list.erase(it);

	//Finally, print out every element in "int_list".
	for (it = int_list.begin(); it != int_list.end(); it++) {
		cout << *it << endl;
	}

	return 0;
}
If we run this, we get the following output:
Output Text
0
1
2
4


Ranged Element Deletion

Let's delete multiple elements in std::list. Behold the following C++ program:
C++ Code (list_sample4.cpp)
#include <iostream>
#include <string>
#include <list>

using namespace std;

int main() {
	unsigned int i;

	//Declare our list to be a list of integers (similar to vector)
	list<int> int_list;

	//Insert some elements at the end
	int_list.push_back(0);
	int_list.push_back(1);
	int_list.push_back(2);
	int_list.push_back(3);
	int_list.push_back(4);

	//Create an "iterator" to go through the list.
	list<int>::iterator it, left, right;

	/*
	 * Let's delete the 2nd, 3rd, and 4th elements.
	 *
	 * To do this, we need the left iterator pointing to the left-most element to
	 * be deleted. The right iterator needs to be pointing ONE PAST the right-most
	 * element to be deleted. As such, it should look like this:
	 * 
	 * [0][1][2][3][4]
	 *     ^        ^
	 *     |        |
	 *     left     right
	 *
	 * The list will become { 0, 4 } post-deletion.
	 */

	//Setup left
	left = int_list.begin();
	left++;

	//Setup right
	right = int_list.begin();
	for (i = 0; i < 4; i++)
		right++;

	//Ranged deletion
	int_list.erase(left, right);

	//Finally, print out every element in "int_list".
	for (it = int_list.begin(); it != int_list.end(); it++) {
		cout << *it << endl;
	}

	return 0;
}
If we run this, we get the following output:
Output Text
0
4
This may prove useful when you are crushing candy in your lab assignment. ;)
Submission
You know the drill. I want a TAR file for submission. Here's the command you should run.

Submission Command

Traditional TAR command:
UNIX Command
UNIX> tar -cvf lab6.tar src/candyCrush.cpp include/candyCrush.hpp

If you use my makefile, this command is simplified:
UNIX Command
UNIX> make package

The name of the tar file doesn't actually matter, as long as it ends with .tar.