We start to delve into algorithms and basic AI with our first CollegeBoard-issued lab. We will learn about String manipulation so we can have a conversation with our computer.
Learning Targets
I can use common String methods to modify a string.
I can evaluate the contents of a String.
I can use a Scanner to take inputs.
I can isolate an object in the user's input and use it in Magpie's response.
Magpie Lab
CollegeBoard used to have three official projects, Magpie, Elevens, and PicLab. A few years ago, they dropped those four and started four new ones: Celebrity, Consumer Review, DataLab, and Steganography. But I still think Magpie is the best lab for starters.
Strings are a special data type in Java. They're not primitives but they behave a little differently than most instantiated objects. In many ways, they're an array of primitive char objects. The biggest difference is that Strings also have many helpful methods to change their formatting, to search and examine the Strings, and lots of other helpful tools.
StringExplorer
Create a new class in our project called StringExplorer and drop this code in:
importjava.util.Scanner;publicclassStringExplorer{publicstaticvoidmain(String[] args) {// Count down with a "T minus 5"// Declare and instantiate a Scanner// infinite loop // take an input// repeat input + message// implement "equals" to stop with the word "stop"/* --------------------------- SAMPLE STUFF --------------------------- */String sample ="The quick brown fox jumped over the lazy dog.";// Print the sample and add a blank line afterSystem.out.println("OUR SAMPLE:");// Demonstrate the length method.int l =9999;System.out.println ("sample.length() = "+ l);// Demonstrate the indexOf method.int position =9999;System.out.println ("sample.indexOf(\"quick\") = "+ position);// Demonstrate the toLowerCase method.String lowerCase =sample.toLowerCase();System.out.println ("sample.toLowerCase() = "+ lowerCase);System.out.println ("After toLowerCase(), sample = "+ sample);// toUpperCase// lastIndexOf// substring// equals }}
Activity 2 Starter Code
Now we're going to add a few new classes to the project.
Magpie.java
importjava.util.Scanner;/** * A simple class to run the Magpie class. * @author Laurie White * @version April 2012 */publicclassMagpieRunner{ /** * Create a Magpie, give it user input, and print its replies. */publicstaticvoidmain(String[] args) {Magpie maggie =newMagpie();System.out.println (maggie.getGreeting());Scanner in =newScanner (System.in);String statement =in.nextLine();while (!statement.equals("Bye")) {System.out.println (maggie.getResponse(statement)); statement =in.nextLine(); } }}
/** * A program to carry on conversations with a human user. * This is the initial version that: * <ul><li> * Uses indexOf to find strings * </li><li> * Handles responding to simple words and phrases * </li></ul> * This version uses a nested if to handle default responses. * @author Laurie White * @version April 2012 */publicclassMagpie{ /** * Get a default greeting * @return a greeting */publicStringgetGreeting() {return"Hello, let's talk."; } /** * Gives a response to a user statement * * @param statement * the user statement * @return a response based on the rules given */publicStringgetResponse(String statement) {String response ="";if (statement.indexOf("no") >=0) { response ="Why so negative?"; }elseif (statement.indexOf("mother") >=0||statement.indexOf("father") >=0||statement.indexOf("sister") >=0||statement.indexOf("brother") >=0) { response ="Tell me more about your family."; }else { response =getRandomResponse(); }return response; } /** * Pick a default response to use if nothing else fits. * @return a non-committal string */privateStringgetRandomResponse() {finalint NUMBER_OF_RESPONSES =4;double r =Math.random();int whichResponse = (int)(r * NUMBER_OF_RESPONSES);String response ="";if (whichResponse ==0) { response ="Interesting, tell me more."; }elseif (whichResponse ==1) { response ="Hmmm."; }elseif (whichResponse ==2) { response ="Do you really think so?"; }elseif (whichResponse ==3) { response ="You don't say."; }return response; }}
Now write a commit message to bookmark these changes and push the new version to GitHub. Then following along with the exercises in Activity 2.
Activity 3: Better method
Magpie's current structure and use of .indexOf("something") >= 0 is full of logic errors. It's caps sensitive, it can't tell if you've entered no response at all, and it sees the word "no" inside of "know". Let's do better.
Let's drop these two methods into your Magpie class (they're overloaded):
/** * Search for one word in phrase. The search is not case * sensitive. This method will check that the given goal * is not a substring of a longer string (so, for * example, "I know" does not contain "no"). * * @param statement the string to search * @param goal the string to search for * @param startPos the character of the string to begin the search at * @return the index of the first occurrence of goal in * statement or -1 if it's not found */privateintfindKeyword(String statement,String goal,int startPos){String phrase =statement.trim().toLowerCase(); goal =goal.toLowerCase();// The only change to incorporate the startPos is in// the line belowint psn =phrase.indexOf(goal, startPos);// Refinement--make sure the goal isn't part of a// wordwhile (psn >=0) {// Find the string of length 1 before and after// the wordString before =" ", after =" ";if (psn >0) { before =phrase.substring(psn -1, psn); }if (psn +goal.length() <phrase.length()) { after =phrase.substring( psn +goal.length(), psn +goal.length() +1); }// If before and after aren't letters, we've// found the wordif (((before.compareTo("a") <0) || (before.compareTo("z") >0)) // before is not a// letter&& ((after.compareTo("a") <0) || (after.compareTo("z") >0))) {return psn; }// The last position didn't work, so let's find// the next, if there is one. psn =phrase.indexOf(goal, psn +1); }return-1;}/** * Search for one word in phrase. The search is not case * sensitive. This method will check that the given goal * is not a substring of a longer string (so, for * example, "I know" does not contain "no"). The search * begins at the beginning of the string. * * @param statement * the string to search * @param goal * the string to search for * @return the index of the first occurrence of goal in * statement or -1 if it's not found */privateintfindKeyword(String statement,String goal){returnfindKeyword(statement, goal,0);}
Now we've got to update our getResponse method to use this instead of indexOf.
Let's use this chart to walk through what's happening:
Activity 4: Slice and dice
Let's use parts of the user's message in our response. Read the details in the student guide. Before proceeding.
Replace the else in our getResponse method with the following code:
// Responses which require transformationselseif (findKeyword(statement,"I want to",0)>=0) { response =transformIWantToStatement(statement); }else {// Look for a two word (you <something> me)// patternint psn =findKeyword(statement,"you",0);if (psn >=0&&findKeyword(statement,"me", psn)>=0) { response =transformYouMeStatement(statement); }else { response =getRandomResponse(); } }
Now below this getResponse method, let's add in a few methods that are being called by the code above.
/** * Take a statement with "I want to <something>." and transform it into * "What would it mean to <something>?" * @param statement the user statement, assumed to contain "I want to" * @return the transformed statement */privateStringtransformIWantToStatement(String statement) {// Remove the final period, if there is one statement =statement.trim();String lastChar =statement.substring(statement.length() -1);if (lastChar.equals(".")) { statement =statement.substring(0, statement.length() -1); }int psn =findKeyword (statement,"I want to",0);String restOfStatement =statement.substring(psn +9).trim();return"What would it mean to "+ restOfStatement +"?"; } /** * Take a statement with "you <something> me" and transform it into * "What makes you think that I <something> you?" * @param statement the user statement, assumed to contain "you" followed by "me" * @return the transformed statement */privateStringtransformYouMeStatement(String statement) {// Remove the final period, if there is one statement =statement.trim();String lastChar =statement.substring(statement.length() -1);if (lastChar.equals(".")) { statement =statement.substring(0, statement.length() -1); }int psnOfYou =findKeyword (statement,"you",0);int psnOfMe =findKeyword (statement,"me", psnOfYou +3);String restOfStatement =statement.substring(psnOfYou +3, psnOfMe).trim();return"What makes you think that I "+ restOfStatement +" you?"; }
Okay, now you're ready to follow along the student guide with the class and ask some questions along the way.
Chatterbots
Your team will build a chatbot that will enter into a conversation with other chatbots built in class.
Step 1: Duplicate and rename example
Next, we need to rename the file and the class.
Do not name your chatbot the same as anyone else's in class.
Step 2: Instantiate bot
Now that you've created your robot, you can add it to the main method.
Step 3: Meet requirements
File
1 point: Your file has an original name that matches the name in your class definition
name
1 point: Returns a one-word name that roughly matches your class name
greet
2 points: Returns a 25 - 100 word opening statement.
Bonus +1 point: Randomly selects from at least three opening statements
respond
3 points: Checks for three different antagonistic words and does not complete the respond method but instead calls the antagonize or pacify method instead
3 points: Checks for three different pacifying words and does not complete the respond method but instead calls the antagonize or pacify method instead
5 points: Searches for at least five keywords and responds to the given subject matter.
antagonize
3 points: Searches for at least three keywords and responds in a negative tone to the given subject matter.
pacify
3 points: Searches for at least three keywords and responds in a positive, disarming tone to the given subject matter.
BONUS POINTS
Have your greet method randomly select from at least three opening statements
Successfully use substring on the given prompt/statement
Your bot successfully adapts to antagonistic or pacifying messages from other bots
Review
Let's review some basics before we move onto our next, advanced concept. Now's the time to hit codingbat, SoloLearn, Codecademy, or other training websites to practice. You want the basics down pat so you can focus on new concepts as we move forward.
publicclassDrills{publicstaticvoidmain(String[] args){// Declare 5 different data types with initial values// A standard for loop printing a message three times// A for-each loop traversing a String[array]// An infinite loop// a short-circut conditional with four tests// Break a loop if a conditional passes// Loop through each char in a String// Print only the first three letters in “word”// Print all the odd numbers from 1 - 100// Create a Scanner and take an input// Create a Scanner, take a number, and count down from that number to 0 }// Create a method that returns a comparison (include a JavaDoc comment)}