Wednesday, October 11, 2017

Isabella: Deployment and Gender

Previously on Dr. Lambda's blog:

In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we taught Isabella to play games, to improve her mood.

Now, the continuation...

Deployment

I finally got around to deploying Isabella to the cloud, so I could test her on other devices.

I have multiple computers which I use for different work tasks. As Isabella was a project related to my teaching, she was only running on my teaching-computer. This means that every time I got a new idea I had to find my other computer, wait for it to open, find the folder, open the editor, and finally write down the note, or implement the feature.

This of course meant that some times, if it was a small idea, I just wouldn't bother. I have spent a lot of words arguing for eliminating all annoyances and obstacle connected to coding, and yet this is pretty much the biggest impediment I can think of. And it is so easy to get rid of.

First we decide where to deploy her to. I am a big fan of Heroku, so that was the obvious choice for me. Then it is as easy as:

  • calling heroku create [project-name].
  • create a Procfile
web: node index.js
  • create a .gitignore
node_modules
  • make a trivial server script
import * as Express from 'express';
let app = Express();
app.set('port', (process.env.PORT || 5000));
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});
app.get('/*', function (req, res) {
  res.sendFile(__dirname + req.url);
});
app.listen(app.get('port'), function () {
  console.log('listening on *:' + app.get('port'));
});
  • and finally commit everything with git
git add .
git commit -m "Deploy to Heroku"
git push

Of course she should have been in version control from the beginning, which would have meant I could easily clone her on my other computer and run her locally there too. It would also enable me to develop on her on the other computer, eliminating the impediment discussed above.

Gender

Once deployed to Heroku I could not wait to test her out on my other computer. I flew over to it, opened the URL, hit enter, and held my breath. She loaded, and finally I heard the words I was waiting for: "I am listening", spoken in a deep male voice. I was stunned, and then I broke out in laughter. Of all the things I expected he switching gender was not one of them.

I had already implemented functionality for changing her name, so the first thing I asked was "Can I call you David". This was very originally to solve the problem of having two devices listening at once, I needed a way to distinguish them. It was impressive to me how much effect the name had, just because I am calling her Isabella, I was completely set on her being a her.

I had previously played around with different voices, to make sure I was using the one I liked best for text-to-speech. But now I actually needed it, so I added a new command for changing her voice.

In order to complete the feature, I added a list of the most common male and female names, so that when you change her name, if you choose a male name, she will also try to find a male voice, and vice versa.

Saturday, October 7, 2017

Isabella: Games

Previously on Dr. Lambda's blog:

In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we explained our reasoning for adding feelings or moods to Isabella.

Now, the continuation...

Games

Now that Isabella have feelings, it makes sense to think about it a bit. What happens if she gets in a bad mood? How can you make her happy again? How do people make each other happy?

Obviously we cant give her a gift, or a hug. Although now that I'm thinking about it, it is not a bad business idea, making an Isabella gift shop, where you can buy virtual gifts for her, to improve her mood. Especially – let's be honest – she is just a fancy, useful tamagotchi.

Another way we humans improve our moods, is by playing games. This we can do with Isabella, and then give a boost to her mood. But which games can she play? The easy answer is: pretty much every game you can play while driving. The first one I thought of was "20 questions", and the easiest I could think of was: guess a number. So let's look at both in turn.

Guess a number

Guess a number is a very simple game, where one player thinks of a number between 0-100, and then the other player has to guess it, using as few guesses as possible.

The cool thing is that this game is exactly what you would expect a computer to like, based on popular preconceptions.

This game was really easy to implement (both ways), using our follow-up system. When she is thinking of the number, you just compare the input with the number and say higher, or lower. When you thinking of the number, she just uses binary search – like any good computer.

Akinator

20 questions is quite a bit more complex. But luckily, like so many times, somebody has already made a brilliant game called Akinator which is exactly what I want. Even more lucky: it has an API. Unfortunately, the API has no documentation. The closest was a couple of projects on github which tried to use it as well.

Unfortunately their code was not quite what I was looking for, so I, instead, made my own, very thin layer on top of the API. Maybe it will be useful for someone else, so here it is:

type StepInformation = {
  question: string,
  answers: {
    answer: string
  }[],
  step: string,
  progression: string,
  questionid: string,
  infogain: string
}
type AnswerResponse = {
  identification: {
    channel: number,
    session: string,
    signature: string
  },
  step_information: StepInformation
}
type CharacterResponse = {
  elements: {
    element: {
      id: string,
      name: string,
      id_base: string,
      proba: string,
      description: string,
      valide_contrainte: string,
      ranking: string,
      minibase_addable: string,
      relative_id: string,
      pseudo: string,
      picture_path: string,
      absolute_picture_path: string
    }
  }[],
  NbObjetsPertinents: string
}
class RawApinator {
  private session: string;
  private signature: string;
  private step = 0;
  constructor() { }
  hello() {
    return new Promise<AnswerResponse>((resolve, reject) => {
      $.ajax({
        url: 'http://api-us3.akinator.com/ws/new_session?partner=1&player=maxipaxi',
        dataType: "jsonp",
        error: reject,
        success: (data: { completion: string, parameters: AnswerResponse }) => {
          this.session = data.parameters.identification.session;
          this.signature = data.parameters.identification.signature;
          this.step = 0;
          resolve(data.parameters);
        }
      });
    });
  }
  sendAnswer(answerId: number) {
    return new Promise<StepInformation>((resolve, reject) => {
      $.ajax({
        url: 'http://api-us3.akinator.com/ws/answer?session=' + this.session 
           + '&signature=' + this.signature + '&step=' + this.step 
           + '&answer=' + answerId,
        dataType: "jsonp",
        error: reject,
        success: (data: { completion: string, parameters: StepInformation }) => {
          this.step++;
          resolve(data.parameters);
        }
      });
    });
  }
  getCharacters() {
    return new Promise<CharacterResponse>((resolve, reject) => {
      $.ajax({
        url: 'http://api-us3.akinator.com/ws/list?session=' + this.session 
           + '&signature=' + this.signature + '&step=' + this.step 
           + '&size=2&max_pic_width=246&max_pic_height=294&pref_photos=OK-FR&mode_question=0',
        dataType: "jsonp",
        error: reject,
        success: (data: { completion: string, parameters: CharacterResponse }) => {
          this.step++;
          resolve(data.parameters);
        }
      });
    });
  }
}

Here is a short video of us playing a game.

Wednesday, October 4, 2017

Javascript Drinking Game and the Scientific Method

Spoiler warning: this post contains a minor spoiler of the Goal.

Introduction

I am a big fan of Java Puzzlers. I love quizzes and games like that. At university I would regularly host a Java quiz. In the quiz I would present a few lines of Java-code, and then the audience would have to choose from 5 options what the output would be.

Following Atwoods law: "Everything that can be written in Javascript, will eventually be". For this reason I think it is important to learn about some of the pitfalls and corner cases in Javascript. Therefore I have invented a game for learning some of them in Javascript.

The Scientific Method

The game consists of a lot of tiny code snippets, grouped into small sections. Many of these sections are structured to encourage the player to discover an underlying rule. The way you play this game is much like the Scientific Method:

  • Make an observation
  • Form/refine a hypothesis
  • Test the hypothesis
  • Repeat

Learning to work like this is very useful, in many different areas. It gives us the ability to uncover the underlying structure of something, without being able to observe it directly. This is also a point in the Goal.

This is exactly the same with these games, you have a language like Javascript, with some fixed rules (the interpreter), of course in this case we could lookup the source code of the interpreter, however we don't want to do that. Even if we did the behavior might stem from a complex interaction in the code. Thus we cannot observe the rules directly.

The way we play the game is:

  • You see a code snippet (make an observation)
  • Try to guess what it does (form a hypothesis)
  • Run the code (test the hypothesis)

If your prediction was inaccurate you refine your hypothesis to include the new data, and at this point move onto the next snippet. As mentioned the snippets are grouped into sections which encourages the formation of good hypotheses.

The Game

The rules are simple:

  • Open the developer console (f12).
  • Type in the expression
  • Try to predict what the result will be
  • Evaluate it
  • If you were wrong you drink (an appropriate amount)

So without further ado here are the snippets.

  • > "1" + 1
  • > "1" - 1
  • > "1" + - 1

  • > let x = 3
    > "5" + x - x
  • > "5" - x + x

  • > parseInt("")
  • > isNaN("")
  • > typeof NaN

  • > throw "ball"

  • > []+{}
  • > {}+[]
  • > []+{} === {}+[]

  • > null > 0
  • > null < 0
  • > null == 0
  • > null >= 0
  • > null <= 0
  • > NaN < NaN
  • > NaN >= NaN

  • > function dis() { return this }
    > five = dis.call(5)
  • > five.wtf = "potato"
  • > five.wtf
  • > five * 5
  • > five.wtf
  • > five++
  • > five.wtf
  • > five.wtf = "potato?"
  • > five.wtf
  • > five

  • > "1000" == "1e3"

  • > new Date("12/08/2016")
  • > new Date("2016-12-08")

  • > 0.1 + 0.2

  • > +[]
  • > {} + []
  • > {}{}{}{}{} + []
  • > [] + []
  • > {}{}{}{}[] + []
  • > [] + {}
  • > {}{}{}{}[] + {}
  • > +{}
  • > {} + {}
  • > {}{}{}{}{} + {}

  • > []?true:false
  • > {}?true:false
  • > ({}?true:false)

  • > [1001, 101, 1, 2].sort()
  • > [1, 2, 101, 1001].sort()
  • > [6, -2, 2, -7].sort()

  • > [1,2,3] == [1,2,3]

  • > let t = [0]
    > t == t
  • > t == !t

  • > Math.max() > Math.min()
  • > Math.max() < Math.min()

  • > true + true === 2
  • > true - true === 0
  • > true === 1

  • > [] == false
  • > [] ? true : false

  • > let numbers = [1, 2, 3]
    > numbers.map(n => { value: n })

  • > Ninja = function (name) {
      this.name = name;
    }
    > Ninja.prototype.jump = function () {
      console.log(this.name + " jumped");
    }
    > let john = new Ninja("John");
  • > john.jump()
  • > setTimeout(john.jump, 1000)
  • > setTimeout(() => john.jump(), 1000)
  • > let callback = john.jump
  • > setTimeout(() => callback(), 1000)
  • > callback()

  • > "" == false
  • > false == "0"
  • > "" == "0"

  • > [[][[]]+[]][+[]][++[+[]][+[]]]

  • > ('b' + 'a' + + 'a' + 'a').toLowerCase()

  • > console.log(b)
  • > console.log(b); var b;

Have fun with Javascript, and always drink responsibly.