{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b00f86bf-37c8-4c1b-8d0b-e2729a5e60e3",
   "metadata": {},
   "source": [
    "# Lab: Collections\n",
    "\n",
    "Notes on Assignments:\n",
    "\n",
    "- **Notes on GAI**: Note that the course policy is that you should not use generative AI (GAI)\n",
    "  without authorization. GAI’s are great tools and you should learn how to use it, but\n",
    "  they are tools and should be used to facilitate but not replace your learning.\n",
    "  If you are suspected to have used GAI tools to generate answers\n",
    "  to the assignment questions instead of using it as a learning tool, you may be\n",
    "  called up to explain/reproduce your work. If you fail to demonstrate your\n",
    "  competence, all your **related assignments throughout the semester will be\n",
    "  regraded as 0**. For example, if you fail to produce good code in `while` loops\n",
    "  in midterm exam, your *lab06 while loop homework and lab* will be re-evaluated.\n",
    "\n",
    "1.  Create a dotnet console app project (see {ref}`create-project` if you need to) in your\n",
    "    `introcscs` directory (`C:\\Users\\*USERNAME*\\workspace\\introcscs` for Windows or `COMPUTER:introcscs USERNAME$`\n",
    "    for macOS) ; call it **Ch09CollectionsLab**.\n",
    "2.  Inside the folder, issue the command `dotnet new console` to create the project in the folder.\n",
    "3.  Still inside the project directory, type `code .` to start VS Code with the folder as the default folder.\n",
    "4.  Prepare your code in VS Code using the file `Program.cs` to code unless otherwise specified.\n",
    "5.  The namespace of this project is *IntroCSCS*.\n",
    "6.  The class name of this project is *Ch09CollectionsLab*.\n",
    "7.  When executing code, you will start with the Main() method in a designated class/file.\n",
    "8.  You will prepare methods in the same class or project to be called from the Main() method.\n",
    "9.  Use a Word document to prepare your assignment.\n",
    "10. Number the questions and **annotate** your answers (using // in code) to show your understanding.\n",
    "11. For coding questions, screenshot and paste 1) your code in VS Code and 2) the\n",
    "    results of the code’s execution (**command prompt** and **username** are part of the execution).\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53481d47",
   "metadata": {},
   "source": "## Overview"
  },
  {
   "cell_type": "markdown",
   "id": "9bface64",
   "metadata": {},
   "source": "### Goals for this lab:\n\n- Read a text file.\n- Work with loops.\n- Work with a Dictionary and a List.\n- Retrieve a random entry.\n\nThis project has a program called `We-Give-Answer!` as in [fake_help_verbose/fake_help_verbose.cs](https://github.com/mstbit/introcs-csharp-examples/blob/master/fake_help_verbose/fake_help_verbose.cs)\nthat has some fake AI/virtual assistant capacity. The program is working but all the response texts are\nhard-coded in the program and that is not ideal for maintenance. Your team want to use external text\nfiles so that you can manage response texts with ease. For that your colleagues have prepared a file\ncalled `fake_help.cs`. **Your job is to complete this new program**.\n\nYour team has designed this new program to be structured a little differently from the verbose version.\nA **helper class** `file_util.cs` ([file_util.cs](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/file_util.cs)) is added to\ncontain the functionalities of handling files. You need to work on the file to provide such functionalities.\nYou will need to complete short versions of methods `GetParagraphs` and `GetDictionary` in `file_util.cs`.\n\n**Testing**:\nWhen you complete this function, the program should behave\njust like the earlier **verbose** version with the hard-coded data: Using a **dictionary value**\nif it finds the right **key**, or choosing a **random response** if there is no key match.\n\nThis should also be an extremely short amount of coding!\nThink of following through the data file, and get the corresponding\nsequence of instructions to handle the data in the exact same sequence."
  },
  {
   "cell_type": "markdown",
   "id": "2f6abbee",
   "metadata": {},
   "source": "## Steps"
  },
  {
   "cell_type": "markdown",
   "id": "fbed0801",
   "metadata": {},
   "source": "### Download files\n\nDownload the following files by going to the github repositories and use the download raw files to download\nindividual files and move them to the project folder:\n\n1.  fake_help_verbose ([fake_help_verbose](https://github.com/mstbit/introcs-csharp-examples/tree/master/fake_help_verbose)),\n2.  fake_help.cs ([dict_lab_stub](https://github.com/mstbit/introcs-csharp-examples/tree/master/dict_lab_stub)) along with the following files:\n3.  file_util.cs\n4.  help_not_defaults.txt\n5.  help_not_responses.txt, and\n6.  the UI class (`ui.cs` at [ui](https://github.com/mstbit/introcs-csharp-examples/tree/master/ui)) so you don’t have to write the user input code.\n\n% #. help_not_responses2.txt, and\n\nNote that some of the files are data files (\\*.txt).\n\nFor now, you may keep the namespace `IntroCS` and change them later for organization purpose."
  },
  {
   "cell_type": "markdown",
   "id": "b0b22603",
   "metadata": {},
   "source": "### The FakeHelpVerbose Class\n\nOpen the working program, the downloaded take_help_verbose.cs ([fake_help_verbose/fake_help_verbose.cs](https://github.com/mstbit/introcs-csharp-examples/blob/master/fake_help_verbose/fake_help_verbose.cs)) and\nlook at how the methods `Main`, `Response`, `GetParagraphs()` and `GetDictionary()` work together\nto provide the **Fake Help** functionality.\n\nTo run the program (take_help_verbose.cs), you need to change the `Main` method in fake_help.cs to something\nelse such as `main` as you are allowed to have only one Main in a program and both fake_help.cs and\nfake_help_verbose.cs have a `Main` method. You have to rename or comment out one of them to run the other.\n\nAll the strings for the responses are pre-coded for you there, but if\nyou were writing your own methods, it would be a pain. There is all the\nrepetitious code to make multiline strings and then to add to the\n`List` and `Dictionary`. That’s why you will later provide simple versatile methods to\nfill a `List<string>` or a `Dictionary<string, string>` so that you only need\nyou to write the string data itself into a text file, with the only\noverhead being a few extra newlines.\n\nFor now, run the program to see how this fake AI/virtual look like:\n\n```bash\nPS C:\\Users\\[username]\\workspace\\introcscs\\Ch09CollectionLab> dotnet run\nWelcome to We-Give-Answers!               // this is the prompt for user action\nWhat do you have to say?\n\nEnter 'bye' to end our session.           // input \"bye\" to escape the while loop and exit\n>\n```\n\n\n**The Main method**: Look back into the code, in the `Main` method, you see that:"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "public static void Main(string[] args)\n",
    "{\n",
    "      List<string> guessList = GetParagraphs();                // create a filled string list guessList from GetParagraph\n",
    "      Dictionary<string, string> responses = GetDictionary();  // create a filled dictionary response from GetDictionary\n",
    "\n",
    "      string prompt = \"\\n> \";                                  // create a string variable and initialize it as prompt\n",
    "      Console.WriteLine(@\"Welcome to We-Give-Answers!          // prompt user input;\n",
    "What do you have to say?\");                                    // the @ means the string is a verbatim string literal; escape sequence is ignored\n",
    "      Console.Write(\"\\nEnter 'bye' to end our session.\");      // \\n means print a new line\n",
    "\n",
    "      string fromUser;                                         // create another string variable\n",
    "      do                                                       // use do-while loop to loop at least once and keep looping\n",
    "      {                                                        // until the condition is met\n",
    "         fromUser = UI.PromptLine(prompt).ToLower().Trim();    // user UI's PromptLine to read user input and save it to fromUser\n",
    "         if (fromUser != \"bye\")                                // condition: if user does not enter 'bye'...\n",
    "         {\n",
    "            string answer = Response(fromUser, guessList, responses);  // call Response(); throw the args to Response()\n",
    "            Console.WriteLine('\\n' + answer);                  // print returned string from Response in a new line\n",
    "         }\n",
    "      } while (fromUser != \"bye\");                             // condition: when user input \"bye\", terminate the while loop\n",
    "\n",
    "       Console.WriteLine(@\"                                    // Good bye with verbatim string literal\n",
    "We-Give-Answers\n",
    "thanks you for your patronage.\n",
    "Call again if we can help you\n",
    "with any other problem!\");\n",
    "   }\n"
   ],
   "id": "b69d6b88"
  },
  {
   "cell_type": "markdown",
   "id": "2f61e6bd",
   "metadata": {},
   "source": "\nBoth the `GetParagraphs()` and `GetDictionary()` in this class actually does one\nthing: **To add texts to the List/Dictionary variables**:\n\n**The GetParagraph method**: The `GetParagraph()` method, taking no arguments, returns a string list of paragraphs:\n\n    public static List<string> GetParagraphs()         // when called, return a string list\n    {\n       List<string> all = new List<string>();          // create the new \"all\" string list variable\n       all.Add(@\"No other customer has ever complained // add string to the variable using \"Add()\"\n    about this before.  What is your system\n    configuration?\");\n       all.Add(@\"That sounds odd. Could you describe   // add string to the variable using \"Add()\"\n    that problem in more detail?\");\n       // ...\n       all.Add(\"Could you elaborate on that?\");        // add string to the variable using \"Add()\"\n       return all;                                     // return the string list variable all\n\n**The GetDictionary method**: The `GetDictionary()` method, taking no arguments,\ncreates and returns a dictionary of keyword:paragraph (key:value) pairs:\n\n    public static Dictionary<string, string> GetDictionary()             // the method returns a Dictionary<string, string> type\n    {\n       Dictionary<string, string> d = new Dictionary<string, string>();  // create Dictionary variable d\n       d[\"crash\"] = @\"Well, it never crashes on our system.              // add key:value pair to d\n    It must have something to do with your system.\n    Tell me more about your configuration.\";\n       d[\"slow\"] = @\"I think this has to do with your hardware.          // add key:value pair to d\n       // ...                                                            // keep adding...\n       d[\"linux\"] = @\"We take Linux support very seriously.\n    But there are some problems.\n    Most have to do with incompatible glibc versions.\n    Can you be a bit more precise?\";\n       return d;                                                         // return dictionary d to caller\n\n**The Response method**: `Response()` takes three arguments and returns:\n\n> 1.  a paragraph value from the **responses** dictionary (created by `GetDictionary()`) if\n>     the user input matches the paragraph’s key; or\n> 2.  a **random** paragraph from the string list **guessList** (created by `GetParagraphs()`).\n\n``` none\npublic static string Response(string fromUser, List<string> guessList,\n                                 Dictionary<string, string> responses)\n{\n   char[] sep = \"\\t !@#$%^&*()_+{}|[]\\\\:\\\";<>?,./\".ToCharArray(); // define separators\n   string[] words = fromUser.ToLower().Split(sep);                // make fromUser lower case and split the words\n   foreach (string word in words) {                               // loop through the words\n      if (responses.ContainsKey(word)) {                          // if a word (keyword) matches a \"responses\" dictionary key\n         return responses[word];            }         }           // return the value (msg) by that key\n   return guessList[rand.Next(guessList.Count)];                  // if the word is not a key in \"response\", return a random \"guess\"\n}\n```"
  },
  {
   "cell_type": "markdown",
   "id": "e9b4a3dd",
   "metadata": {},
   "source": "### The FakeHelp Class\n\n**help_not_defaults.txt**: First, look into your lab project for the first data file:\n[help_not_defaults.txt](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/help_not_defaults.txt),\nand the beginning is shown below:\n\n```{literalinclude} ../../examples/dict_lab_stub/help_not_defaults.txt\n:language: none\n:lines: 1-15\n```\n\nYou can see that it includes the data for the welcome and goodbye strings, as seen in\n`FakeHelpVerbose (fake_help_verbose.cs)`, followed by all the data to go in\nthe list variable `guessList` of random answers.\n\nOne complication is that many of these strings take up several lines, in what we call\na *paragraph*. We follow a standard convention for putting paragraphs into plain text:\nPut a blank line after a paragraph to mark its end. As you can see, that is how\n[help_not_defaults.txt](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/help_not_defaults.txt) is set up.\n\nNow look in your copy of the class FakeHelp (`fake_help.cs`), you will see that it is\nvery similar to class FakeHelpVerbose in `fake_help_verbose.cs`:"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "mystnb": {
     "syntax_highlight_language": "none"
    }
   },
   "outputs": [],
   "source": [
    "public static void main()\n{\nStreamReader reader = new StreamReader(\"help_not_defaults.txt\");\n// special data is in the first two paragraphs\nstring welcome = FileUtil.ReadParagraph(reader);\nstring goodbye = FileUtil.ReadParagraph(reader);\nList<string> guessList = // rest of the file gives a\nFileUtil.GetParagraphs(reader); // list of random responses\nreader.Close();\nreader = new StreamReader(\"help_not_responses.txt\");\nDictionary<string, string> responses =\nFileUtil.GetDictionary(reader);\nreader.Close();\nConsole.Write(welcome);\nstring prompt = \">\";\nConsole.WriteLine(\"Enter 'bye' to end our session.\");\nstring fromUser;\ndo\n{\nfromUser = UI.PromptLine(prompt).ToLower().Trim();\nif (fromUser != \"bye\")\n{\nstring answer = Response(fromUser, guessList, responses);\nConsole.Write(\"\" + answer);\n}\n} while (fromUser != \"bye\");\nConsole.Write(\"\" + goodbye);\n}\n"
   ],
   "id": "7ecb5bbc"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n- The `StreamReader` is set up to read from the right file.\n- The the `FileUtil` methods `ReadParagraph`, `GetParagraphs`, and `GetDictionary` are used to provide\n  the text data needed.\n\nAll of the additions you need to make are in the bodies of method\ndefinitions in the class `FileUtil`. Look back to `Main` in `fake_help.cs` to\nsee how the methods from `FileUtil` are actually used:\n.. definitions in the class `FileUtil`. Look back to `Main` in `FakeAdvise` to\n\n- It creates the `List` `guessList` and the `Dictionary` `responses`\n  using more general functions that you need to fill in.\n"
   ],
   "id": "b2a54570"
  },
  {
   "cell_type": "markdown",
   "id": "79d0f76a",
   "metadata": {},
   "source": [
    "- The stubs are put in the class `FileUtil` for easy reuse.\n",
    "    - The `Main` calls these methods and chooses the files to read.\n",
    "    - The results will look the same as the original program to the user, but the second version will\n",
    "      be easier for a programmer to read and generalize: It will be easier in other situations\n",
    "      where you want lots of canned data in your program (like in a game you might write).\n",
    "    - The stub should run as is (mostly saying things are not implemented).\n",
    "    - Test out your work at every stage!\n",
    "\n",
    "    ### ReadParagraph\n",
    "\n",
    "    The first method to complete in\n",
    "    [file_util.cs](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/file_util.cs)\n",
    "    is useful by itself and later for use in the\n",
    "    `GetParagraphs` and `GetDictionary` that you will complete. See the stub:\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b68e61e3",
   "metadata": {},
   "source": "    ```{literalinclude} ../../examples/dict_lab_stub/file_util.cs\n    :dedent: 6\n    :end-before: chunk\n    :start-after: ReadParagraph chunk\n\nThe first call to `ReadParagraph`, using the file illustrated above, should\nreturn the following (showing the escape codes for the newlines):\n\n    \"Welcome to We-Give-Answers!\\nWhat do you have to say?\\n\"\n\nand then the reader should be set to read the goodbye paragraph\n(the next time `ReadParagraph` is called).\n\nTo code, you can read lines one at a time, and append them to the part of the\nparagraph read so far. There is one thing to watch out for: The\n`ReadLine` function *throws away* the following newline (`\"\\n\"`) in the input. You\nneed to preserve it, so be sure to explicitly add a newline, back onto\nyour paragraph string after each nonempty line is added. The returned\nparagraph should end with a single newline.\n\nThrow away the empty line\nin the input after the paragraph. Make sure you stop after reading\nthe empty line. It is very important that you advance the reader\nto the right place, to be ready to read the next paragraph.\n\nBe careful of a pitfall with files: You can only read a given chunk once:\nIf you read again, with the exact same syntax,\nyou get the *next* line of the file. The `ReadLine` method\nhas the *side effect* of advancing the reading position in the file.\n\n**Testing**: This first short `ReadParagraph` function should actually be most of\nthe code that you write for the lab! The program is set up so you can immediately\nrun the program and test `ReadParagraph`: It is called to read in the welcome string\nand the goodbye string for the program, so if those come correctly to the screen, you\ncan advance to the next two parts.\n\n### GetParagraphs\n\nSince you have `ReadParagraph` at your disposal, you now only need to\ninsert a *few remaining lines of code* to complete the next method\n`GetParagraphs`, that reads to the end of the file, and likely\nprocesses more than one paragraph.\n\n```{literalinclude} ../../examples/dict_lab_stub/file_util.cs\n:end-before: chunk\n:start-after: GetParagraphs chunk\n```\n\nLook again at\n[help_not_defaults.txt](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/help_not_defaults.txt),\nto see how the data is set up.\n\nThis lab requires very few lines of code. Be sure to read the examples\nand instructions carefully (several times). A lot of ideas get packed\ninto the few lines!\n\n**Testing**: After writing `GetParagraphs`, the random\nresponses in the lab project program should work as the user enters lines in the program."
  },
  {
   "cell_type": "markdown",
   "id": "4e100d9e",
   "metadata": {},
   "source": "### GetDictionary\n\nThe last stub to complete in [file_util.cs](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/file_util.cs)\nis `GetDictionary`. Its\nstub also takes a `StreamReader` as parameter. In\n`Main` this method is called to read from\n[help_not_responses.txt](https://github.com/mstbit/introcs-csharp-examples/blob/master/dict_lab_stub/help_not_responses.txt).\nHere are the first few lines:\n\n```{literalinclude} ../../examples/dict_lab_stub/help_not_responses.txt\n:language: none\n:lines: 1-15\n```\n\nHere is the stub of the function to complete, reading such data:\n\n```{literalinclude} ../../examples/dict_lab_stub/file_util.cs\n:dedent: 6\n:end-before: chunk\n:start-after: GetDictionary chunk\n```\n\n% Be careful to distinguish the data file\n\n% :repsrc:`help_not_responses.txt <dict_lab_stub/help_not_responses.txt>`\n\n% from\n\n% :repsrc:`help_not_responses2.txt <dict_lab_stub/help_not_responses2.txt>`,\n\n% used in the extra credit option.\n\n% Show the program output to a TA (after the extra credit if you like).\n\n% Extra credit\n\n%\n\n% #. (20%) Modify ReadParagragh so it *also* works if the paragraph ends\n\n% at the end of the file, with no blank line after it, or if the line after\n\n% the paragraph only has whitespace characters. Both changes are good to\n\n% bullet-proof the code, since\n\n% the added or removed whitespace is hard to see in print.\n\n% #. (20%) The crude word classification scheme would recognize “crash”, but not\n\n% “crashed” or “crashes”. You could make whole file entries\n\n% for each key variation, repeating the value paragraph.\n\n% A concise approach is to use a data file\n\n% like :repsrc:`help_not_responses2.txt <dict_lab_stub/help_not_responses2.txt>`.\n\n% Here are the first few lines:\n\n% .. literalinclude:: ../../examples/dict_lab_stub/help_not_responses2.txt\n\n% :language: none\n\n% :lines: 1-15\n\n% The line that used to have one key now may have several blank-separated keys.\n\n% Here is how the documentation for `GetDictionary` should be changed:\n\n% .. literalinclude:: ../../examples/dict_lab_stub/file_util.cs\n\n% :start-after: Extra credit documentation\n\n% :end-before: }\n\n% :dedent: 6\n\n% Modify the lab project to use this file effectively: Find\n\n% “help_not_responses.txt” on line 22 in `Main`. Change it to\n\n% “help_not_responses2.txt” (inserting ‘2’), so `Main` reads it.\n\n% In your test of the program, be sure to use several of the keys that apply to the\n\n% same response, and show to your TA."
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "csharp"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}