Starting from:

$30

Assignment 3: JingleNet

Assignment 3: JingleNet

> JingleNet has sprinkled a touch of magic into our communication at the North
> Pole, making it as smooth as freshly fallen snow. -- Santa Claus

At the North Pole, Santa and his elves use the **JingleNet announcement system**
to play announcements over the North Pole loud speakers for all to hear.

Your task is to write a program named [a3.cpp](a3.cpp) that processes a sequence
of JingleNet commands, and outputs the resulting announcements in the proper
order.

For example, here are the JingleNet commands in
[jinglenet_input1.txt](jinglenet_input1.txt):

```
SEND greenie elf2 send candy canes
ANNOUNCE 1
SEND yumyum elf2 I love candy canes!
ANNOUNCE 2
```

`SEND` puts an announcement into the JingleNet queue system, and `ANNOUNCE n`
prints the next `n` announcements to `announcements.txt`. To run the program on
this input you would type:

```
> ./a3 jinglenet_input1.txt
2 announcements written to announcements.txt
```

And the contents of `announcements.txt` is:

```
1: {greenie, elf2, "send candy canes"}
2: {yumyum, elf2, "I love candy canes!"}
```

The details of all the commands and how they work are given below.


## JingleNet Architecture

The JingleNet system is essentially one large queue built from 5 internal
queues:

![JingleNet Architecture](JingleNetArchitecture.png)

When an announcement is added to JingleNet it is placed in the queue for its
**rank**. Every announcement has one of these 5 ranks:

- **santa**: the highest rank
- **reindeer**: the second highest rank
- **elf2**: the third highest rank
- **elf1**: the fourth highest rank
- **snowman**: the lowest rank

Ranks are per-announcement. There could be announcements from the same user in
different queues at the same time.

Here's a textual representation of the queues when they are empty:

```
(left is front of queue, right is back of queue)
   santa: []
reindeer: []
    elf2: []
    elf1: []
 snowman: []
```

And here's what they look like with a total of 5 announcements in the system:

```
(left is front of queue, right is back of queue)
   santa: [{yumyum, santa, "I love Christmas!"}, {lili, santa, "Hi!!"}]
reindeer: [{yumyum, reindeer, "mmm, peppermint"}]
    elf2: []
    elf1: []
 snowman: [{choco, snowman, "choco4ever"}, {yumyum, snowman, "I'm hungry!"}]
```

As noted, the left-most item is at the *front* of each queue, and the right-most
item is at the *back*. So items are enqueued at the right end and de-queued at
the left.

## JingleNet Commands

JingleNet is controlled by 4 commands:

- **SEND** *username* *rank* *message*: *username* sends an announcement of rank
  *rank* with text *message*
- **REMOVE_ALL** *username*: all announcements from *username* are removed from
  the JingleNet system
- **PROMOTE_ANNOUNCEMENTS** *username*: all announcements from *username* are
  moved to the next highest rank
- **ANNOUNCE** *n*: the next *n* announcements are printed to
  `announcements.txt`

The `Announcement` class is in [Announcement.h](Announcement.h). `Announcement`
objects are *immutable*, i.e. once created they cannot be changed. Here are
three different ways to create the same announcement (see
[announcement_demo.cpp](announcement_demo.cpp)):

```c++
Announcement a1("yumyum", Rank::SANTA, "I love Christmas!");
cout << a1 << endl; // prints: {yumyum, santa, "I love Christmas!"}

// copy another Announcement
Announcement a2(a1);
cout << a2 << endl; // prints: {yumyum, santa, "I love Christmas!"}

// parse a string of the form "sender_name rank text"
Announcement a3("yumyum santa I love Christmas!");
cout << a3 << endl; // prints: {yumyum, santa, "I love Christmas!"}
```

### Sending Announcements

**SEND** *username* *rank* *message*

This adds an announcement from *username* of rank *rank* with text *message* to
the JingleNet system.

For example, suppose the JingleNet queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{yumyum, santa, "I love Christmas!"}, {lili, santa, "Hi!!"}]
reindeer: [{yumyum, reindeer, "mmm, peppermint"}]
    elf2: []
    elf1: []
 snowman: [{choco, snowman, "choco4ever"}, {yumyum, snowman, "I'm hungry!"}]
```

Then after this command:

```
SEND yumyum Snowman yaaay!
```

The queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{yumyum, santa, "I love Christmas!"}, {lili, santa, "Hi!!"}]
reindeer: [{yumyum, reindeer, "mmm, peppermint"}]
    elf2: []
    elf1: []
 snowman: [{choco, snowman, "choco4ever"}, {yumyum, snowman, "I'm hungry!"}, 
           {yumyum, snowman, "yaaay!"}]
```

### Removing Announcements

**REMOVE_ALL** *username*

This removes all announcements from *username*, whatever queue(s) they are in.
If there are no announcements from *username*, then nothing happens.

For example, suppose the JingleNet queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{lili, santa, "Hi!!"}, {yumyum, santa, "I love Christmas!"}]
reindeer: []
    elf2: []
    elf1: []
 snowman: [{yumyum, snowman, "yaaay!"}]
```

Then after this command:

```
REMOVE_ALL yumyum
```

The queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{lili, santa, "Hi!!"}]
reindeer: []
    elf2: []
    elf1: []
 snowman: []
```

### Promoting Announcements

**PROMOTE_ANNOUNCEMENTS** *username*

This moves all announcements from *username* to the next highest queue.
Announcements are dequeued from their current queue and then enqueued in the
queue one rank higher. Nothing happens to announcements in the **santa** queue:
they are already at the highest rank.

When promoting announcements, start with **reindeer** queue and work downwards.
First any announcements in the **reindeer** are promoted to the **santa** queue,
and then any announcements in the **elf2** queue are promoted to the
**reindeer** queue, and so on.

When items are put into another queue, they are enqueued at the back in the
usual way. If multiple announcements are promoted from a queue, then they are
enqueued so they appear in the same order as in the queue they were promoted
from.

For example, suppose the queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{lili, santa, "Hi!!"}, {yumyum, santa, "I love Christmas!"}]
reindeer: [{yumyum, reindeer, "Rocking horse repair needed!"}, 
           {rudolph, reindeer, "I'm hungry!"}]
    elf2: []
    elf1: []
 snowman: [{yumyum, snowman, "yaaay!"}, {choco, snowman, "choco4ever"}, 
           {yumyum, snowman, "I'm hungry"}]
```

Then after the command:

```
PROMOTE_ANNOUNCEMENTS yumyum
```

The queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{lili, santa, "Hi!!"}, {yumyum, santa, "I love Christmas!"}, 
           {yumyum, santa, "Rocking horse repair needed!"}]
reindeer: [{rudolph, reindeer, "I'm hungry!"}]
    elf2: []
    elf1: [{yumyum, elf1, "yaaay!"}, {yumyum, elf1, "I'm hungry"}]
 snowman: [{choco, snowman, "choco4ever"}]
```

### Announcing Announcements

**ANNOUNCE** *n*

De-queues and announces the next *n* announcements from the queues in this
order:

- All announcements in the **santa** queue, in the order they were enqueued.
- Then all announcements in the **reindeer** queue, in the order they were
  enqueued.
- Then all announcements in the **elf2** queue, in the order they were enqueued.
- Then all announcements in the **elf1** queue, in the order they were enqueued.
- Then all announcements in the **snowman** queue, in the order they were
  enqueued.

If there are fewer then *n* announcements in the queues, then all announcements
are removed (in the proper order). You can assume that *n* is always a positive
integer.

To announce a announcement, use `jnet.announce` from
[JingleNet_announcer.h](JingleNet_announcer.h):

```
jnet.announce(a);  // a is an Announcement
```

The announcements are automatically printed to the file `announcements.txt` when
the program ends. To use `jnet.announce` all you need to do is #include
[JingleNet_announcer.h](JingleNet_announcer.h) in your program.

> **Be careful!** `announcements.txt` gets over-written at the end of every
> program that uses [JingleNet_announcer.h](JingleNet_announcer.h).

Please be sure to announce announcements in this exact way using
`jnet.announce`.

Here's an example of how announcing works. Suppose the queues look like this:

```
(left is front of queue, right is back of queue)
   santa: [{lili, santa, "Hi!!"}, {yumyum, santa, "I love Christmas!"}]
reindeer: [{yumyum, reindeer, "Rocking horse repair needed!"}, 
           {yumyum, reindeer, "I'm hungry!"}]
    elf1: [{yumyum, elf1, "yaaay!"}, {yumyum, elf1, "I'm hungry"}]
    elf2: []
 snowman: [{choco, snowman, "choco4ever"}]
```

Then after this command:

```
ANNOUNCE 3
```

This removes the next 3 announcements from the queues. The queues then look like
this:

```
(left is front of queue, right is back of queue)
   santa: []
reindeer: [{yumyum, reindeer, "I'm hungry!"}]
    elf1: [{yumyum, elf1, "yaaay!"}, {yumyum, elf1, "I'm hungry"}]
    elf2: []
 snowman: [{choco, snowman, "choco4ever"}]
```

When the program ends, `announcements.txt` looks like this (assuming no other
announcements were made):

```
1: {lili, santa, "Hi!!"}
2: {yumyum, santa, "I love Christmas!"}
3: {yumyum, reindeer, "Rocking horse repair needed!"}
```

## Special Requirements

- Put all your code for this assignment into a single file called
  [a3.cpp](a3.cpp). It should have a `main` function that reads the name of a
  file and processes it like this:

  ```
  > ./a3 input1.txt
  ...
  ```

  See [getline_demo.cpp](getline_demo.cpp) for an example of reading
  command-line arguments and processing a file line-by-line.

- Inside [a3.cpp](a3.cpp), implement your own queue class called `Queue` that
  inherits from [Queue_base.h](Queue_base.h). Also:
  - Your `Queue` does *not* need to be a template class, e.g. it can inherit
    from `Queue_base<Announcement>`.
  - For this assignment, you must implement your `Queue` using either a
    singly-linked or doubly-linked list that you code yourself. Do **not** use
    arrays, vectors, or other containers to implement `Queue`.
  - Make sure that all methods meet the performance requirements listed in
    [Queue_base.h](Queue_base.h).

- Implement a class called `JingleNet` that stores the 5 queues. The details of
  this class are up to you. Implement it in a sensible way that makes your
  program easy to understand. For instance, `JingleNet` could have a method for
  each JingleNet command above.

## Sample Runs

Here are a few sample input files and their corresponding output files:

- [jinglenet_input1.txt](jinglenet_input1.txt), [output1.txt](output1.txt)
- [jinglenet_input2.txt](jinglenet_input2.txt), [output2.txt](output2.txt)
- [jinglenet_input3.txt](jinglenet_input3.txt), [output3.txt](output3.txt)
- [jinglenet_input4.txt](jinglenet_input4.txt), [output4.txt](output4.txt)
- [jinglenet_input5.txt](jinglenet_input5.txt), [output5.txt](output5.txt)

You can run them like this:

```
❯ ./a3 jinglenet_input1.txt
2 announcements written to announcements.txt

❯ cat announcements.txt
1: {greenie, elf2, "send candy canes"}
2: {yumyum, elf2, "I love candy canes!"}
```

The `diff` command is useful for comparing files:

```
❯ ./a3 jinglenet_input1.txt
2 announcements written to announcements.txt

> diff announcements.txt output1.txt

```

`diff` only prints differences between the files, so no output means the files
are the same.


## What to Submit

When you're done, submit your finished [a3.cpp](a3.cpp) file on Canvas. Don't
submit anything else. 

## Grading

The marker will compile and run your program on Ubuntu Linux using
[makefile](makefile) like this:

```bash
> make a3
g++ -std=c++17 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g a3.cpp -o a3

> valgrind ./a3 some_input_file.txt
... testing output ...
```

Your program will be tested on a variety of inputs, including some you haven't
seen before.

## Marking Scheme

### The Queue Class: 10 marks

- **1 mark**: [a3.cpp](a3.cpp) contains a class named `Queue` that inherits from
  [Queue_base.h](Queue_base.h).
- **4 marks**: All the methods in `Queue` listed in [Queue_base.h](Queue_base.h)
  work correctly.
- **2 marks**: All the methods in `Queue` meet the performance requirements
  listed in [Queue_base.h](Queue_base.h).
- **3 marks** The `Queue` class is implemented using either a singly-linked or
  doubly-linked list that you code yourself. Do **not** use arrays, vectors, or
  other containers to implement `Queue`.

### The JingleNet Class: 4 marks

- **2 marks**: [a3.cpp](a3.cpp) contains a class named `JingleNet` that stores
  the 5 queues.
- **2 marks**: The `JingleNet` class is used in a sensible way that makes your
  program easier to understand.

### Correctness: 13 marks

- **5 marks**: valgrind reports no memory leaks or other errors when the program
  runs.
- **5 marks**: The program produces the correct output for
  [jinglenet_input1.txt](jinglenet_input1.txt),
  [jinglenet_input2.txt](jinglenet_input2.txt),
  [jinglenet_input3.txt](jinglenet_input3.txt),
  [jinglenet_input4.txt](jinglenet_input4.txt), and
  [jinglenet_input5.txt](jinglenet_input5.txt).
- **3 marks**: The program produces the correct output for another input file,
  in the same style as the jinglenet_input files supplied by the markers.


### Overall source code readability: 5 marks

- All code is sensibly and consistently indented, and all lines are 100
  characters in length, or less. **Hint**: In the Linux command-line you can
  print all the lines in a file with more than 100 characters with this command
  (the initial `>` is the prompt character, so don't type it):

  ```bash
  > awk 'length > 100' some_file.cpp
  ```

  If this prints nothing, then the file has no lines over 100 characters long.

- Whitespace is used to group related pieces of a code to make it easier for
  humans to read. All whitespace has a purpose.
- Variable and function names are self-descriptive.
- Appropriate features of C++ are used, as discussed in the course. **Note** If
  you use a feature that we haven't discussed in class, **you must explain it in
  a comment**, even if you think it's obvious.
- Comments are used when needed to explain code whose purpose is not obvious
  from the code itself. There should be *no* commented-out code from previous
  versions.
- Source code readability marks may be deducted for code that is unreadable in
  some way not covered by the above. The deduction is proportional to how
  serious the problem is.

### Deductions

- **-5 marks** if valgrind reports any memory leaks or other errors during the
  markers test runs.
- **-5 marks** if your program crashes on any valid test.
- **a score of 0** if you:
  - have no statement of originality, or it's modified in any way.
- At least **-1 mark** if your file has an incorrect name, or you submit it in
  an incorrect format, etc.; possibly multiple deductions if there are multiple
  problems.
- At least **-1 mark** if you submit a non-working file.
  - If the marker can easily fix your file and make it work, then there is only a
    small deduction.
  - If the marker has to spend a lot of time fixing your file, then there is a
    larger deduction; if they can't make it work, then they you will get 0.

There may be other deductions, depending upon the circumstances.

More products