4: Strings

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.

What's a magpie?

A bid that can mimic speech.

Check out the chatbot in Activity 1:

169KB
Magpie Lab Student Guide_updated_Sept_2014.pdf
pdf

Strings

Check out the official documentation.

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:

import java.util.Scanner;

public class StringExplorer
{
	public static void main(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 after
      System.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

	}
}

Project Configuration

We might be doing our project on Replit.com. If not, set up a project in VS Code:

Name the project Magpie.

GitHub Setup

Open the GitHub Desktop app and Add a Local Repository. Don't create a new repo because the app forces the creation of a new folder. If you use the "create a repository" link it will use the existing folder made by VS Code.

Use the Java git ignore settings. Once it's up, push the repo. Then select the Repository menu and View on GitHub. Send me the link in Slack.

Activity 2 Starter Code

Now we're going to add a few new classes to the project.

Magpie.java

import java.util.Scanner;


/**
 * A simple class to run the Magpie class.
 * @author Laurie White
 * @version April 2012
 */
public class MagpieRunner
{
	/**
	 * Create a Magpie, give it user input, and print its replies.
	 */
	public static void main(String[] args)
	{
		Magpie maggie = new Magpie();
		
		System.out.println (maggie.getGreeting());
		Scanner in = new Scanner (System.in);
		String statement = in.nextLine();
		
		while (!statement.equals("Bye"))
		{
			System.out.println (maggie.getResponse(statement));
			statement = in.nextLine();
		}
	}
	
}

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
 */
private int findKeyword(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 below
	int psn = phrase.indexOf(goal, startPos);

	// Refinement--make sure the goal isn't part of a
	// word
	while (psn >= 0)
	{
		// Find the string of length 1 before and after
		// the word
		String 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 word
		if (((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
 */
private int findKeyword(String statement, String goal)
{
	return findKeyword(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 transformations
		else if (findKeyword(statement, "I want to", 0) >= 0)
		{
			response = transformIWantToStatement(statement);
		}

		else
		{
			// Look for a two word (you <something> me)
			// pattern
			int 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
	 */
	private String transformIWantToStatement(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
	 */
	private String transformYouMeStatement(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.

public class Drills{
	
	public static void main(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)
	
}

Last updated