MindReader

In this chapter we return to console programs.  We will learn about text and word processing, that is how to use characters and strings.  In addition, we will also deepen our understanding of classes.  We will see how classes are structured and we will create our own classes.

.

String

Strings are made up of zero or more characters.  A character can be a letters, a digits or a special character.  The apostrophes (single or double quotes) are necessary to distinguish digits from numbers:

let x = 42;
let z = "42";

The first line declares the number 42, the second line the string "42".  Special characters are characters like '.', '$', etc., but there are also invisible characters such as '\n' for a new line or '\t' for a tab.

Let's continue with a few examples:

let s1 = "Hello";
let s2 = "world";

Here we declare two strings, s1 and s2, and initialize them to the values "Hello" and "world".  Strings can contain none, one or many characters:

let s3 = "!";
let s4 = " ";
let s5 = "";

Here s3 contains an exclamation mark.  s4 also contains a character, the space.  s5 contains no character at all, but s5 is still a string.

Try it

What makes strings really interesting is that we can make new strings out of old ones by concatenating them:

s6 = s1 + s4 + s2 + s3;

and also print them

println( s6 );

And just as we can read numbers (int and double) from the console, we can also read strings from the console:

let name = readLine("Enter your name: ");

The String class has a few useful functions:

  • length:  returns the number of characters in a string,
  • charAt(pos):  returns the character at the position pos,
  • substring(p1):  returns the substring from position p1 to the end of the string,
  • substring(p1, p2):  returns the substring from position p1 to position p2, but without the character at position p2,
  • includes(s):  returns true if the string s is contained in the string,
  • indexOf(s):  determines if the string s is contained in the string, if it is it returns the position of the character, -1 otherwise,
  • trim(): removes white spaces at the beginning and end of a string, and
  • toUpperCase():  returns the uppercase version of a string, of course there is also a toLowerCase().

As we will see in a moment, there are quite a few things we can do with these functions.

.

Exercise: Print the Characters of a String

As a first example, we iterate through the characters of a string and display them one by one on the console:

let s1 = "Hello";
for (let i=0; i<s1.length; i++) {
    let c = s1.charAt( i );
    println( c );
}

.

Exercise: Reverse a String

We can use what we just learned to reverse a string:

let s1 = "stressed";
let s2 = "";
for ( let i=0; i<s1.length; i++) {
    let c = s1.charAt( i );
    s2 = c + s2;
}
println( s2 );

The word "stressed" becomes the word "desserts".  Words that remain the same when reversed, such as "racecar" or "otto", are called palindromes.

.

Exercise: Compare two Strings

Next, we want to determine whether two strings are equal.  In JavaScript we can use the "==" operator for this:

let s1 = "Hello ";
let s2 = s1 + "world";
let s3 = "Hello world";
if (s2 == s3) {
    println("Equal");
} else {
    println("Not Equal");            
}

As expected "Equal" is what gets prinited here.  Just for curiosity, what happens if we compare numbers and strings:

let x = 42;
let y = "42";
if (x == y) {
    console.log("x and y are equal");
}
if (x === y) {
    console.log("x and y are exactly equal");
}

When we use the simple equality operator "==", JavaScript says they are equal.  However, there is also the exactly equals operator "===" with three equal signs, and in that case they are not longer equal.  The difference is that the exactly equal operator also checks if the two variables are of the same type.

SEP: Always use the exactly equals operator "===" when comparing variables.

.

Exercise: Cut a String

As a last little exercise, we want to cut a string into two pieces.  For this we use the functions indexOf() and substring():

let s1 = "Hello world";
let posSpace = s1.indexOf(" ");
let s2 = s1.substring(0, posSpace);
let s3 = s1.substring(posSpace);
println(s2);
println(s3);

We find the position of the space using the indexOf() function.  Then we cut off the front part with the substring() function.  Two things are important here: the first character is at position 0, i.e. as usual we start counting at 0.  And the character at the posSpace position is not included.  Finally, we cut off the rest, and store it in s3.

.

Try it

StringTokenizer

Cutting strings into pieces is a very common task. That is why there is a special class for this, the StringTokenizer:

let sentence = "hi there what's up?";
let toki = new StringTokenizer(sentence, " .,?");
while (toki.hasMoreTokens()) {
    println(toki.nextToken());
}

First, we initialize the StringTokenizer by telling it what it should cut apart (sentence), and how it should cut it. That means we tell it the separators it should use, like ' ', '.', ',' or '?', for instance.  The StringTokenizer then splits the string into tokens, i.e., words.  To get to these words, we ask the StringTokenizer if it has any tokens, so we call the hasMoreTokens() function.  If it has any, we ask the StringTokenizer to give us the next token using nextToken().  We do this until all the tokens are used up.

.

Try it

Immutability

Something that sometimes leads to confusion is the immutability of strings.  Immutability means that a string cannot be changed.  Let's take a look at an example:

let s1 = "Hello";
s1.toUpperCase();
println(s1);

What is displayed on the console?  "Hello" is displayed, and not, as one might hope, "HELLO".  This has to do with the fact that strings are unchangeable, i.e. immutable.

But there is a trick to changing strings anyways: we simply assign a new value to them:

let s1 = "Hello";
s1 = s1.toUpperCase();
println(s1);

.

Classes

We have already heard a lot about classes, and we have used them many times, e.g. the Karel class or the graphic classes like GRect and GOval.  Also, in this chapter we used the String class and the StringTokenizer class.  The question that arises is: can we make our own classes?

Of course, and it's not that hard.  As a first example, we look at a student from the perspective of a university.  What information would a university need to know about a student?  If we limit ourselves to the essentials, then these are name, matriculation number and credit points (ECTS).  We take these attributes of a student and combine them into a class:

class Student {
    constructor(_name, _id, _credits) {
        this.name = _name;
        this.id = _id;
        this.credits = _credits;
    }
}

In principle, a class is nothing more than a data container.  Each class should be saved in its own file, the name of the file should be the same as the name of the class with a '.js' extension.

SEP: Class names should always start with capital letters and follow the CamelCase convention.

.

Constructor

Classes are 'intelligent' data containers.  This means that they not only contain and bundle the data, but also initialize, manage and change it.  So they also do something with the data, and classes have functions for that.  Usually, we call functions that belong to classes methods, to distinguish them from the normal functions.  The most important method is the constructor, which initializes the data.

class Student {
    constructor(_name, _id, _credits) {
        this.name = _name;
        this.id = _id;
        this.credits = _credits;
    }
}

The constructor, like any function, can have parameters.  A constructor never returns a value, because it is implicitely assumed, what it returns is an instance of that class.

Now we have a class, how do we use it?  We use them like any other class.  Let's write a console program which creates an instance of our Student class:

async function setup() {
    ...
    let fritz = new Student("Fritz", 12345, 0.0);
	println(fritz.name + ", " + fritz.id + ", " + fritz.credits);
}

We create a new instance of the class Student.  This instance (also called object) we call fritz.  To create the object we have to call the constructor with "new".  We initialize the instance variables of the class: that is, name is initialized to the value "Fritz", id to the value 12345, and credits to the value 0.0.

.

Methods

Let's say we want to print this information about fritz quite often.  Then every time, we would have to write the above print statement.  However, if we create a method called toString(),

class Student {
	...
    toString() {
        return "Student [name=" + this.name + ", id=" + this.id 
               + ", credits=" + this.credits + "]";
    }
}

then printing is much easier:

async function setup() {
    ...
	println(fritz.toString());
}

 

Another example is incrementing credits.  Fritz has been studying hard, passed his Programming exam, and now he wants he credits updated:

	fritz.credits = fritz.credits + 5;

More convenient would be an incrementCredits() method:

	fritz.incrementCredits(5);

This is more for convenience, but makes our code more readable.

SEP: Every class should have a toString() method.

.

Closures

Now notice, we can read fritz's name, but we can also change it:

println(fritz.name);
fritz.name = "Hans";
println(fritz.name);

JavaScript is pretty open about accessing the instance variables of a class.  Sometimes you are fine with that, but sometimes you don't want this.

Consider the following class, where we added a method named getId():

class Student {

    constructor(_name, _id, _credits) {
        this.name = _name;
        this.credits = _credits;

        // closure: read only
        this.getId = (function () {
            let id = _id;
            return function () {
                return id
            }
        })();
    }
}

It looks very strange, but it does what we wanted: we can read the id, but we can no longer change it:

    let fritz = new Student("Fritz", 12345, 0.0);
	println(fritz.getId());  // 12345
    fritz.id = 42;
    println(fritz.getId());  // 12345

JavaScript just ignores the assignment.  Closures are kind of complicated, and we will not use them often.  But you know now, that they exist.

.

Try it

Exercise: Hansel und Gretel

The class Student is now ready for use and as an example we take a look at the two students Hansel and Gretel at the Brothers Grimm college:

async function setup() {
    createConsole();

    let hansel = new Student("Hänschen", 12345, 0.0);
    println(hansel.name);

    let gretel = new Student("Gretel", 54321, 11.0);
    println(gretel.name);
    gretel.name = "Gretchen";
    gretel.incrementCredits(5);
    println(gretel.toString());
}

.

Type Conversion

JavaScript has a few functions for converting between strings and numbers.  It is not uncommon that we have to convert a string into a number. 

let x = parseInt("42");
let y = parseFloat("42.0");

Sometimes we also want to convert a floating point number into an integer number:

let z = Math.trunc(42.0);

Conversely, to create a string from a number, you can use a little trick:

let x = 42;
let fourtyTwo = "" + x;

There is also other ways, but knowing one is fine.

.


Review

In this chapter we have mainly dealt with strings and classes.  Also, it is the first time we've written our own class.  We have seen that a class has variables and methods.  Sometimes we also call the variables instance variables, class properties, or class attributes.  We have seen that methods always do something, and that there is a special method, the constructor. We also learned about the String and StringTokenizer classes.

.


Projects

The projects in this chapter mainly deal with strings and writing simple classes.

.

Student

In the example for Student the question arises why are the instance variables private?  Let us look at the problem from the other side.  Suppose instance variables were public: could we then prevent a student from getting negative credits, or that her id could be changed?

.

Try it

ASCII

To store characters internally the computer uses numbers.  This means we can add and subtract these characters just like numbers, and we can even use them in conditions and loops.  For example, to determine if a character is an uppercase letter, you can use the following condition:

if (c >= 'A' && c <= 'Z') { ... }    

Or to turn a lowercase letter into an uppercase letter, we can use the following trick:

let klein = 'd';
let gross = klein.toUpperCase();

You can also make ints out of chars and vice versa via a cast, that is a type conversion

let char = String.fromCharCode(i);
print(char);

With this knowledge we want to write the following three functions:

  • isUpperCase(char c)
  • toUpperCase(char c)
  • printASCIITable()

As for the latter, it is sufficient to output the ASCII characters between 32 and 128, since the first 31 ASCII characters cannot be printed.

.

Try it

PasswordCreator

With our knowledge of strings we can now write a password generator.  A good password should be at least 8 characters long and contain at least one lowercase letter, one uppercase letter, one digit and one symbol.  So we could define four strings containing the possible characters, and then use the RandomGenerator to randomly select two of them and put them together to a password, for example:

const small = "abcdefghijklmnopqrstuvwxyz";
let password = "";
password += small.charAt(rgen.nextInt(small.length));
...

.

Try it

RandomText

For test purposes it is sometimes helpful to be able to generate random text.  That's not so hard.  We start again with the top-down approach.  Let's assume we have a function createRandomSentence() that generates a random sentence.  Then we could create a random text by simply creating several random sentences.  The createRandomSentence() function, in turn, is easy if we assume there is a function called createRandomWord(), because then we would just take somewhere between three and five words, put spaces between them and add a period at the end.  Also the createRandomWord() function is not that difficult, because words consist of lower case letters, and have a length of about 3 to 8 letters, so

function createRandomWord() {
    let word = "";
    let nrOfCharsInWord = rgen.nextInt(3, 8);
    for (let i = 0; i < nrOfCharsInWord; i++) {
        word += String.fromCharCode('a'.charCodeAt(0) + rgen.nextInt(26));
    }
    return word;
}

.

Try it

Palindrome

Words that remain the same when reversed, such as "racecar" or "pensioner", are also called palindromes.  We want to write a function called isPalindrome(String s), which determines if a string is a palindrome.  To do this, we write a function reverse() that reverses a given string, and then compare the original with the reversed string using equals().  If both are equal, it is a palindrome.

.

.

Try it

CountUpperCase

Here we should write a function that counts the uppercase letters in a string.  This can be very useful, for example, if you want to determine whether a certain text is German or English: English text has on average less capital letters per sentence.  We can either use the character class or the function we wrote above.

.

.

.

Try it

ReadOneChar

We want the user to be able to enter only one letter. The user should not be able to enter zero or more than one letter, but only exactly one letter.  Therefore we want to write a function readOneChar(), which returns one char as return value.

There is the readString() function for reading strings.  Hence, we use the loop-and-a-half, and use as abort criterion that the string read should be of length one.

while (true) {
    s = await readLine(msg);
    if (s.length == 1)
        break;
    println("Please, only enter one character:");
}

.

Encrypt

Another interesting application that we can now easily implement is a small encryption program.  We use the so-called Caesar cipher, named after Julius Caesar [2].  To encrypt a text using Caesar's cipher, the letters in a given text are simply shifted by a fixed number, the key.  For example, if the key is four, then an 'a' becomes an 'e', a 'b' becomes an 'f', etc.  To decrypt, the process is simply reversed.

To implement this in code, we write two functions, encrypt() and decrypt(), one for encrypting, the other for decrypting.  Both take two parameters, a string and an int, the key.  One returns the encrypted text as a string, the other returns the decrypted text.

.

.

Try it

.

Note: in order not to make it too complicated, it makes sense to convert the text to lower case before starting the encryption.  And the remainder operator '%' is very practical here:

function encryptChar(c, key) {
    let d = c.charCodeAt(0) - 'a'.charCodeAt(0);
    let e = d + key;
    let f = e % 26;
    let g = String.fromCharCode(f + 'a'.charCodeAt(0));
    return g;
}

.

Try it

Abjad

Around 1500 B.C. the Phoenicians developed the first alphabet script, a left-hand consonant script. Consonant scripts are writing systems in which only  consonants are being used [2].

To show that such a script actually can be readable, archaeologists have commissioned us to write a console program that removes all vowels from a given text.  We can proceed as follows:

  • with readLine() we ask the user to enter a normal text,
  • we then look for vowels and remove them, and
  • we then output the result using println().

Ideally we should remember the top-down approach, i.e. we should perhaps assume that the functions removeVowels() and isVowel() exist, and then implement them later.  You could also use the replace() function of the String class, but more about this in the next project.

.

Try it

Franconian

The language of the Franks, also called 'lingua franca', has been wrongly forgotten.  The 'lingua franca' (Italian for 'Franconian language') is a Romance-based pidgin language. Pidgin language or pidgin refers to a reduced form of language that is used by people of different languages to communicate [3].

In order to contribute to the understanding of peoples, we should write a German-Franconian translation program. In Franconian the following phonetic simplifications take place (also known as "inner-German weakening of consonants"):

  • t -> d
  • p -> b
  • k -> g

For example, the word 'Politiker' (politician) becomes 'Bolidiger' in Franconian.

So let us write a function translateGermanToFranconian(), which has a string as parameter with the German text, and returns a string, which is the translated Franconian version.  For this you could use the replaceAll() function of the String class:

String german = "politiker";
String franconian = german.replaceAll('t','d');
...

But replaceAll() and also replace() use "regular expressions".  So unless you know what those are, you should waiting using them until your second semester.  However, a function like the following is all you need:

function translateChar(c) {
    switch (c) {
        case 't':
            return 'd';
        case 'k':
            return 'g';
        case 'p':
            return 'b';
        default:
            return c;
    }
}

.

Try it

PigLatin

Pig Latin [5] is a secret language for children that has very simple rules:

  • if a word begins with a consonant, then the consonant is moved to the end of the word and the syllable "ay" is appended.  So "loser" becomes "oserlay" or "button" becomes "uttonbay",
  • if a word begins with a vowel, then only the syllable "ay" is appended. So "eagle" becomes "eagleay" and "america" becomes "amercaay".

Hence, we should write another console program that asks the user for an English sentence and then translates it into Pig Latin.  Here, too, we should use of the top-down approach.

.

Try it

YodaTalk

In the typology of languages [6], the sentence structure subject-verb-object (SVO), i.e. the subject is in the first place, the verb in the second and the object in the third, occurs very frequently. About 75% of all languages in the world follow this or a very similar pattern.  We know that Yoda comes from the swamp planet Dagobah and there the preferred sentence construction is object, subject verb (O,SV).

For example, the English sentence,

You are lucky.

translates into "Yodish":

Lucky, you are.

To simplify interplanetary communication, it is now our task to write a console program that translates a given text from English into Yodish.  We can assume that each sentence entered always consists of three words, always in the order SVO.

Here's what we could do:

  • we ask the user to enter an English text,
  • then we identify the three elements subject, verb and object using a StringTokenizer,
  • and using println(), we then output the translated Yodish in the form object, subject verb.

Of course we use the top-down approach again and implement a function called translateFromEnglishToYodish().

.

Try it

RandomGenerator

We want to write our own RandomGenerator class.  This is not so difficult when we know that there is a function called Math.random() in standard JavaScript.  This function returns a floating point number between 0 and 1, including 0, but excluding 1. If we want to use it to generate a number between 1 and 6, this is how it works:

let diceRoll = 1 + (int)(Math.random() * 6);

With this knowledge we want to write a class called RandomGenerator, that has the methods nextInt(int a, int b), nextInt(int b), nextBoolean() and nextColor().  In the future we can always use our own class instead of the ACM class, if we want to.

.

Try it

Counter

We want to create a class Counter that can act as a counter.  The class should have a private instance variable called count.  In the constructor, this variable is to be set to the value 0.  Then there should be a method called getValue(), which simply returns the current value of the variable count.  And there should be a method called incrementCounter(), which increases the value of the variable count by one.  Can you prevent somebody else from messing with your counter? (Closure)

.

.

Try it

Point

Next we want to write a class Point.  This should correspond to a point in two-dimensional space, i.e. have an x and a y coordinate.  The class should have a constructor of the form Point(int x, int y), and the methods: getX(), getY(), move(int dx, int dy), equals(Point p), add(Point p), and toString().

.

.

.

Try it

ConnectTwoMovingPoints

Our class Point seems relatively boring on its own.  But if we use it for the motion of two points which are connected by a colored line, then this actually looks quite nice.

We start with the Point class, which, however, has slightly different requirements than the one of the last project.  It should be as simple as possible.  It should have four instance variables, namely x, y, vx and vy.  The constructor should have no parameters, and it should initialize the instance variables with some small random values.  This Point class should have only one method:

    move() {
        this.x += this.vx;
        this.y += this.vy;
    }

We want to use this class in our graphics program ConnectTwoMovingPoints:

let p1;
let p2;

function setup() {
    ...
    p1 = new Point();
    p2 = new Point();
}

function draw() {
    p1.move();
    p2.move();
    checkForCollisionWithWall(p1);
    checkForCollisionWithWall(p2);
    drawConnection(p1, p2);
    update();
}

In our game loop, we move the two points, check if the points are still in the field, and then connect the two points with a line.  That's actually quite simple.  However, if you only want to display ten lines at a time and want to delete the older lines again, you need arrays (next chapter).

.

Try it

Blackjack

According to Wikipedia, "Blackjack is the most widely played card game offered in casinos" [9].  We want to implement a slightly simpler version of this game.

Instead of cards, we simply use numbers, numbers between 1 and 11.  The computer plays the dealer and starts by generating a random number between 17 and 25. Then it's the player's turn.  She starts with a card, i.e. a random number between 1 and 11, and can then decide whether she wants another card.  If yes, another random number between 1 and 11 is generated  and added to the current "hand".  When the player no longer wishes to have a new card, the "hand" of the player is compared with that of the computer.

The winner is whoever has 21 or less points and has more than the other player.  Otherwise, it's a draw.

.

Try it

SimpleCraps

"Craps or Seven Eleven is a dice game that enjoys great popularity, especially in the USA." [10]

We will implement a simpler version of Craps:  We have just a simple dice.  The player starts with credits of 100 Euro.  In each round, 10 Euro are bet and the player bets on one of the following outcomes:

  • odd
  • even
  • high (4,5,6)
  • low (1,2,3)

The game is over when the player has no more credits.

.

Try it

Factorial

The factorial of a number is the product of all integer numbers less than or equal to that number.  For example, the factorial of 3 is:

	3! = 1 * 2 * 3 = 6

We want to write a function called calculateFactorial(int n) which calculates the factorial of the number n.  With that function we then want to list the factorials of the numbers from 1 to 20.  Probably using a for-loop is a good idea.

The factorials have the property that they become very large very quickly.  And that leads to a problem, because computers do have some problems, when it comes to large numbers!  What does JavaScript say when the numbers get to large?

.

.

.

Try it

Rabbits

Anyone who has ever had rabbits knows that they have an interesting characteristic: they reproduce rapidly (that's why they are called rabbits).  To see how rapidly, let us write a program that calculates how our rabbit population develops over the months.  We follow the model of Fibonacci [12]:

  • "Each pair of rabbits throws another pair of rabbits every month.
  • A newborn couple only has offspring in the second month of its life (the gestation period of rabbits is about one month).
  • All rabbits are in an enclosed space so that no animal can leave the population and none can come in from the outside."

If we write the simulation correctly, then the Fibonacci sequence, i.e. the numbers 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... should come out.

.

Try it

WordTwist

The WordTwist game is about recognizing a word in which some letters have been swapped.  So, for example, we should realize that the word "nhickce" actually originated from the word "chicken".

We begin with the starting word, e.g. "chicken", and swap some letters.  We show this word to the user, and he should then guess which was the original word.  If we use the top-down approach again (as we always should).  Then it makes sense to write a function scrambleWord(String word) that takes the starting word as parameter and returns the scrambled word as return value.  If we continue the top-down approach, it also makes sense to write a function called randomSwap(String word) that simply swaps two random letters in the word.  If we call this function several times, the result is a well-shuffled word. What we still need is a function that picks a random word to start with:

function pickGuessWord() {
    let rgen = new RandomGenerator();
    switch (rgen.nextInt(0, 3)) {
    case 0:
        return "dog";
    case 1:
        return "cat";
    default:
        return "chicken";
    }
}

.


Challenges

.

Try it

Hangman

Hangman is a simple letter game [12].  Although it is a graphical game originally, we will implement a text-based version here.  It starts with the computer picking a random word.  The player may then guess one letter at a time.  The program then shows whether this letter occurs in the word and if it does, at which position it occurs.  This is then repeated until the word has been guessed.  At the end the user should see how many attempts were necessary to guess the word.  However, only failed attempts are counted.

We need two instance variables,

let guessWord;
let hintWord;

where guessWord contains the word to be guessed (e.g. "mother") and hintWord contains the letters that were guessed correctly so far, in the beginning it contains only dashes (e.g. "------").  We need to initialize both guessWord and hintWord:

guessWord = pickGuessWord();
hintWord = createHintWord();

where the function pickGuessWord() already exists (see WordTwist above) and we still have to write the function createHintWord().

After that the game loop starts: In the first step we show the player the hintWord so that she has an idea how many letters the word contains.  Then we ask the player to enter a letter.  Here we can use the function readOneChar() from the project ReadOneChar.  By means of includes(),

let c = await readChar();
if (guessWord.includes("" + c)) {
    buildNewHintWord(c);
}

we can determine if the new letter appears in the guessWord.  If yes, we must construct a new hintWord using the buildNewHintWord(c) function.  This function should insert the correct letter in the correct place in hintWord, and the new hintWord should then be displayed to the player.

The abort criterion is relatively simple, if there are no more dashes "-" in the hintWord, then the player has guessed the word.  Alternatively we could also compare guessWord and hintWord, if they are equal the game is over.

.

Try it

MindReader

This game is about mind reading.  It is inspired by a coin toss where we have heads and tails.  But instead of tossing a coin, the player simply picks either heads or tails.  The computer now tries to predict what the player guessed.  If the prediction of the computer was correct, the computer receives one point, otherwise the player gets one.  Whoever gets 25 points first, wins.  The idea of the game is based on a handout by Professor Raja Sooriamurthi, who in turn was inspired by Professor Gregory Rawlins [13].

If we use the top-down approach, we get the following rough structure for the game:

  • the computer makes a prediction, either head ('h') or tail ('t') (computerMakePrediction()),
  • the player makes his choice, either head or tail (humanMakePick()),
  • the prediction of the computer is displayed (revealPrediction()),
  • the computers prediction is compared with the selection of the player and depending on whether the computer has guessed correctly or not, either the computer gets a point or the player.

The whole thing is then repeated until either one has reached 25 points.

For the game we need four instance variables:

let computerGuess;
let humanGuess;
let computerScore = 0;
let humanScore = 0;
let predictor;

The character entered by the player, the character predicted by the computer, as well as the scores, i.e. the player's score and the computer's score.

We could write our own predictor (see extensions), or we can use the one from Professor Sooriamurthi:

predictor = new MindReaderPredictor();

For this to work we must include the MindReaderPredictor.js file at the beginning of our code with the line:

include("Pr5_MindReader/mindReaderPredictor.js");

The class MindReaderPredictor has two functions, makePrediction() and addNewGuess(char c).  The first function tries to make a prediction, so it returns either 'h' or 't'.  The second adds a new player guess to the Predictor database, allowing the Predictor to make better predictions.  So the Predictor tries to learn from the player.

Extensions: One can come up with many extensions for this game. e.g.:

  • you could play several games in a row, but MindReaderPredictor should not be reinitialized each time, or
  • you could write your own MindReaderPredictor class, a very simple version would just randomly chooses between 'h' and 't'.

.


Questions

  1. Give an example of a class and an example of an object.
     
  2. "arnold" is an "actor". Is "arnold" an object or a class?
     
  3. What is usually the job of a constructor?
     
  4. It is good style to give each class a toString() method.  What should the toString() method do?
     
  5. How do you convert a string containing a number, e.g. "42", into an integer number?
     
  6. Strings are immutable, what does that mean?
     
  7. If you want to compare strings and numbers you have to be a little careful.  What is the difference between the "==" and the "===" operators?
     
  8. The class String has many methods.  We used the following:
    substring()
    length()
    charAt()
    toLowerCase()
    indexOf()
    Briefly describe what each of these methods does.
     
  9. Write example code that reverses a string, e.g. converts the word "STRESSED" into the word "DESSERT". (The pure JavaScript code is sufficient, a class or method declaration is not needed).
     
  10. What is the StringTokenizer class used for?
     
  11. The class StringTokenizer takes a string as parameter and has two methods called hasMoreTokens() and nextToken().  Write code that asks the user for a sentence, and then outputs the individual words of the sentence, line by line.

.


References

The references from Chapter 2 are still important here. The Wikipedia also contains a lot of information.

[1] Caesar cipher, https://en.wikipedia.org/w/index.php?title=Caesar_cipher&oldid=702242426 (last visited Feb. 17, 2016).

[2] Abjad, https://en.wikipedia.org/w/index.php?title=Abjad&oldid=704692935 (last visited Feb. 17, 2016).

[3] Pidgin-Sprachen, https://de.wikipedia.org/w/index.php?title=Pidgin-Sprachen&oldid=147087583 (last visited Feb. 17, 2016).

[4] String (Java Platform SE 7 ) - Oracle Documentation, https://docs.oracle.com/javase/7/docs/api/java/lang/String.html

[5] Pig Latin, https://de.wikipedia.org/w/index.php?title=Pig_Latin&oldid=149209295 (Abgerufen: 17. Februar 2016, 20:54 UTC).

[6] Sprachtypologie, https://de.wikipedia.org/w/index.php?title=Sprachtypologie&oldid=149705787 (Abgerufen: 17. Februar 2016, 21:10 UTC).

[7] ELIZA, https://en.wikipedia.org/w/index.php?title=ELIZA&oldid=704986757 (last visited Feb. 17, 2016).

[8] Telemarketing-Software Samantha, http://de.engadget.com/2013/12/11/audio-telemarketing-software-samatha-streitet-kategorisch-ab-e/

[9] Blackjack, https://en.wikipedia.org/wiki/Blackjack

[10] Craps, https://de.wikipedia.org/wiki/Craps

[11] Fibonacci-Folge, https://de.wikipedia.org/wiki/Fibonacci-Folge

[12] Galgenmännchen, https://de.wikipedia.org/wiki/Galgenmännchen

[13] Mind Reader: a program that predicts choices, http://nifty.stanford.edu/2007/raja-mindreader/

.