Wishful Coding

Didn't you ever wish your
computer understood you?

5 minutes Lisp in Python

I love both Python and Clojure, so after I read this gist about a Lisp in Ruby, I decided to craft my own in Python.

The idea is basically to abuse a few Python functions, namely Generators, Decorators and grouping to have a syntax that looks like Lisp with yield at the start of every statement.

It's all still Python, so you can mix and mach however you like, I sneaked a List Comprehension in the example, which fits the Lisp syntax quite well.

At the moment of writing, the Gist below is also #3 on HackerNews.

# 5 minutes Lisp in Python
# Pepijn de Vos <http://pepijndevos.nl>
#
# Inspired by 30 minutes Lisp in Ruby
# http://gist.github.com/562017
# 
# This Lisp does not read or parse anything at all.
# A generator and a Decorator are abused to run sexps.
#
# Usage:
# 
# Define a function decorated with @lisp and start every sexp with yield.
#
# The function names should be strings.
#
# Result is stored in fn name.
#
# Example below:
#

def lisp(fn):
    code = fn()
    val = code.next()
    while True:
        try:
            try:
                newval = getattr(__builtins__, val[0])(*val[1:])
            except AttributeError:
                newval = getattr(val[1], val[0])(*val[2:])

            val = code.send(newval)
        except StopIteration:
            return getattr(val[1], val[0])(*val[2:])

@lisp
def example():
  (yield 'join',
    ", #",
    (yield '__mul__',
      [(yield 'str', i) for i in
        (yield 'range', (yield '__add__', 5, 5))],
      2))

print example

Twitter in your inbox

I have been working on this for quite a while, with initial plans to make it a service. But I have changed plans, and am releasing it to the world.

What have I been working on, you ask? Not that I like sliced bread, but it's at least as good as that: It solves some problems while it introduces some others. It's a proxy between Twitter and your email client that sits on your computer or a server translating mail-language(POP, SMTP) to Twtter-language(REST API).

This way you can read your tweets in your mail client, store them in folders, search though them, and do all the other stuff mail clients are good at. It even allows you to reply and forward(retweet in Twitter lingo) tweets.

Here is how to get it. You go to my GitHub repo, which lives at the link below, and pull/download the code.

http://github.com/pepijndevos/Twemail

The next step is to get the dependencies, which include Twisted, OAuth2 and Twitter-Text-Python. You might try to use setup.py for that, but I'm not sure it works.

Now you simply run "twistd -ny twemail.tac" in the Twemail directory. If everything went well you'll see some messages rolling by about starting services.

Now all that remains is to configure your mail client with the following information"

  • email: username@twitter.com
  • password: your Twitter password
  • POP server: localhost on port 1100
  • SMTP server: localhost on port 2500

Here is how it works.

  • You can send messages by mailing post@twitter.com
  • Replying is done by hitting reply and replacing the subject line.
  • Retweets are done by forwarding to the sender of the tweet

In the future I also want to handle attachments and IMAP, but this is currently not included.

Clojure Rock-Paper-Scissors using a Markov chain

A long time ago I saw a challenge on a Python forum about writing a Rock-Paper-Scissors bot that performs better than a random bot.

As strange as it might seem, Rock-Paper-Scissors is anything but random. As long as it's played by people, they'll always try to come up with a winning move. I've been told there are even championships with this game.
Clojure Rock-Paper-Scissors using a Markov chain
So while trying to improve my Clojure skills I came up with the idea of using a Markov chain to write a Rock-Paper-Scissors bot.

Markov chains - as I recently learnt - are a random process where every step is only based on the previous. I'm not a psychologist, but when I play Rock-Paper-Scissors, I usually go like "I just lost using paper, so lets try rock" or "I won using rock, lets do it again", so I think it suits the challenge.

I use this map to define the probability of certain actions:
{:won {:same 1, :inferior 0, :superior 0}, :lost {:same 0, :inferior 1, :superior 0}, :draw {:same 0, :inferior 0, :superior 1}}

So with this example data, If I used rock, and won. I'll choose rock again, now I lose, so I choose the item inferior to rock: scissors. Draw, so I'll choose the item superior to scissors: rock.

The roulette function takes a map like {:same 1, :inferior 2, :superior 4} and returns one of the keys with the probability of the value. Calling this 7 times should approximately return 4 times :superior, 2 times :inferior and :same only once.

The guess function uses the Markov map to predict my action, and take the item that wins from mine.

Then there is the check function, which just returns if I won or lost based on the 2 items, and the relation fn which does the same for determining which of the 2 items is better.

Then there is the play function which focuses on getting your input, comparing results, and updating the Markov data to represent your actual actions.

You should update the map with the map that is printed at the end of the game, to improve the learning process of my bot. If you collect a lot of data, it could be interesting to send it in, so I can improve my bot to have a proper data set to start with.

Have fun!

(ns markov-stone)

;{:won {:same 0, :inferior 0, :superior 0}, :lost {:same 0, :inferior 0, :superior 0}, :draw {:same 0, :inferior 0, :superior 0}}

(defn roulette [slices]
  (let [slices (sort-by val slices)
        total (reduce + (vals slices))
        r (rand total)
        cumulative (map vector (keys slices) (reductions + (vals slices)))]
    (first (first (drop-while #(< (last %) r) cumulative)))))

(defn guess [markov prev state]
  (let [inferior {:s :p, :p :r, :r :s}
        superior (clojure.set/map-invert inferior)
        pred (->> markov
                  state
                  roulette)]
    (case pred
          :same (superior prev)
          :superior (inferior prev)
          :inferior prev)))

(defn check [user cpu]
  (if (= user cpu)
    :draw
    (if (= user (cpu {:s :p, :p :r, :r :s}))
      :lost
      :won)))

(defn relation [prev cur]
  (if (= prev cur)
    :same
    (if (= prev (cur {:s :p, :p :r, :r :s}))
      :superior
      :inferior)))

(defn play [markov prev state score]
  (println "Choose your weapon[r/p/s]!")
  (if-let [user (#{:r :p :s} (keyword (read-line)))]
    (let [names {:r "ROCK" :p "PAPER" :s "SCISSORS"}
          cpu (guess markov prev state)
          result (check user cpu)
          score (case result
                      :won (inc score)
                      :lost (dec score)
                      :draw score)]
      (println (names cpu))
      (println "Game is" (name result) "- Score:" score)
      (recur (update-in markov [result (relation prev user)] inc) user result score))
    (do
      (println "Invalid input; Final score:" score)
      markov)))

(println (play {:won {:same 20, :inferior 17, :superior 16}, :lost {:same 16, :inferior 13, :superior 38}, :draw {:same 23, :inferior 18, :superior 21}} :r :draw 0))
Published on