söndag 13 december 2009

Python and doctest

I've just recently got to learn Python, and after a bit of struggling with the new syntax, I found the language to be very appealing. It's well structured, and has been around for a long time so if you can't find the library you are looking for in pythons standard libraries (which is very unlikely), there are a bunch of other libraries to install (for example lxml, which is a great xml/xpath wrapper for libxml2). The only issue I've had so far is with some libraries not being compatible with python 3.1 (which differs a lot from 2.x), but this is probably a transition period soon being over.

And as with all languages, sooner or later you would like to know how to setup your tests. And when googling "python test framework", I stumbled upon doctest:

The doctest module searches for pieces of text that look like interactive Python sessions, and then executes those sessions to verify that they work exactly as shown.
This is something I've never seen before, but yet strikes me as very powerful. To write your tests, you simply include an example execution of the function you want to test in the comment, i.e.:

'''
This function prints the string "Hello World!".

For example:
>>> say_hello_world()
Hello World!
'''
def say_hello_world():
print("Hello World!")
To run the tests, you can either include the test execution in the main method of the script file, which to me is wrong if you would like to have another default main method. Another way to do it (if you have python 2.6 or later) is to execute like this:
python -m doctest -v helloworld.py
This will load the doctest module, and then execute the tests in helloworld.py, which will call the function say_hello_world(), and make sure it's output equals "Hello World!", as written in the comment. You then extend the tests by simply including more examples in your comment.

IMO, the real power of this is that you don't need to write separate test functions, plus you get the documentation for free. I.e., with xunit the same test would look something like this:

def test_say_hello_world():
mocked_print = create_print_mock_function()
say_hello_world()
xunit.assert_equal("Hello World!", mocked_print.get())

And I'm not even sure if create_print_mock_function is possible to do. Even if it would, the tests becomes much larger, and harder to maintain.

The main downside I see to using doctest is that it depends on the expected output being formatted exactly as python would have formatted it. For example, the following function will fail, just because python will include extra spaces in the array:

'''
>>> get_array()
[1,2]
'''
def get_array():
return [1,2]
will fail with
Failed example:
get_array()
Expected:
[1,2]
Got:
[1, 2]
In this case it's easy to fix, but how will it scale when you have hundreds of tests? What happens when you update your tostring methods? Instead of inspecting the returned data, you inspect the data formatted to a string, which can very well mean something quite different if you are writing your own tostring methods.

Anyway, at the latest coding dojo (Majorna Coding Dojo!), we decided to give python and doctest a try, and it worked very well. Even though most of us didn't know Python from before, we didn't have any problems using doctest.

I will definitely use doctest more in the future, and will even see if I can find similar testing frameworks (Document Driven Development, DDD) for other languages such as Ruby, C# and C++. So if you are into Python programming, I definitely recommend you trying this out.

torsdag 27 augusti 2009

My first piano sheet successfully created!



Just recently finished my first piano sheet based on an improvisation I made some years ago. Not that I was planning to do anything serious with it, just to learn and to some day be able to say: "I've produced my own piano sheet". Well, now I can :) There are some things I know I could make better, but it takes too much time figuring out how to do it in Cubase (which I used to produce the sheet), so I'll just let it be as it is now. It was really fun producing it, so I'll definitely continue doing so. And hopefully it won't take so much time in the future!

Update: My server isn't up and running anymore, but you may actually find my music on spotify!

tisdag 18 augusti 2009

Podcasts at 2x speed

On a morning walk to work for a few days ago, when I, as usual, listened to a podcast on my iPhone, I happened to accidentally click on an icon I've never noticed earlier - "2x". Suddenly my podcast started playing at two times the speed (at least what it felt like), but without making it difficult listening. It was actually quite easy to keep up with the podcast-dialogue, and after 15 minutes I had completed half an hour long podcast. It feels a bit like skimming through a book, you may miss the details, but get the big picture. And when they suddenly start talking about something that is very interesting you can always return to the "standard speed".

So if you have an iPhone or iPod Touch with OS 3.0, start a podcast and click the 2x-icon in the upper right corner, and you may be able to start optimizing your time!

tisdag 2 juni 2009

Objective-C... yet another programming language?

Just recently installed XCode on my mac in order to develop applications for my iPhone. The first thing I realized was that I needed to learn yet another programming language. Just another syntax, or does Objective-C actually bring something new?

It turns out Objective-C (which is a language derived from C, similar to C++, but still very different) has one feature I haven't seen before. First of, instead of calling a method on an object, your send a message to it. It's almost the same, but also requires you to pass along named parameters (which means more code to write, but easier to read). The new and cool feature appears when you want to send a message to an object, which should return a value, when the object turns out to be nil.

MyClass* myObject = nil;
if ([myObject getValue] == 0.0) {
// myObject getValue retuned 0, OR myObject is nil
}

So when executing this code, as opposed to how normal programming languages handles it, it doesn't crash. It simply returns 0.0 if the expected return type is a number, or nil if the expected return type is a pointer to an object. This also works for functions returning nothing (i.e. void), sending a void returning message to a nil object will simpy do nothing.

In for example C# you always have to do null checks all over the code, in order to verify your object isn't nil.

I'm not sure I like it or not, but if you learn to think "pure Objective-C" then perhaps this can be used as an advantage! I'm looking forward to learning Objective-C further, and to adapt these new concepts!

I'll let you know when I've published my first iPhone app :)

onsdag 4 mars 2009

Why you love and hate Ruby

What strikes me when programming Ruby is how simple everything is! You've got utility functions for almost everything, and writing an application doesn't take more then a few lines of code. No need to setup a project, no need to think about types. You just write code!

The downside of this, of course, is that the code easily becomes bad written, and as there are very few rules in Ruby, it isn't very easy reading others' code. This is why Ruby sometimes is classified as a "hard to lean language". There are simply too many ways of writing the same code.

I don't agree that this makes the language more hard to learn, but perhaps makes it take longer to learn the language fully. For instance, I just recently learned you can write code in the following way:

puts "Hello my friend" if userIsFriendly

I've never seen code like that before, but it really opens up for some nice syntax!

Another feature of Ruby is that Classes are open, and everything is an object, meaning you can extend/modify classes on the fly. Even classes are objects! For example:

class Fixnum
def minutes
return Timespan.new(self)
end
end
class Timespan
def ago
return Time.now - self
end
end

5.minutes.ago


The syntax is almost perfect! How would you otherwise say "5 minutes ago"? That's what people often talk about when they describe Ruby. "The only truly object oriented language".

Why Ruby isn't the perfect language, IMO, is because it's too "open". There are too many ways of writing code. Writing smaller applications, or better scripts, won't take you more than a few minutes, but when you need to work on a larger project, I would definitely go with a strictly typed language, such as C#, where you have better control over your code. Also, not surprisingly, no IDE can help you writing your code, as the code is dynamically typed, and you really can't say what functions are available on an object until you actually run the code.

So, thumb up for this language when working on smaller applications. But for larger projects, I would recommend going for another one.

fredag 6 februari 2009

Trying out ruby for the very first time

So, I've made up my mind. The language of this year is going to be Ruby! The reasons are many, but mostly I feel I can use this language more in my daily work, and on a podcast over at AltDotNet about Ruby, they mentioned something like "learning ruby will make you a better C# developer". Languages like lisp and python will have to wait for at least one year!

About two weeks ago, I was out skiing with Gustaf and some friends of him. We ended up playing a lot of four in a row, a really funny and simple game. This game made me think about how to develop a min max AI, and suddenly I had written down the pseudo code for such an AI in my notebook. Here is a picture of the game is supposed to be played (from wikipedia):
Now, back home at my computer, I started digging into the ruby documentations, and after not more than a few hours I had finished the first version of the AI! Turns out Ruby isn't that hard to learn, at least not if you just want to get some basic things done. It's like writing c#, except you skip the types, and instead of writing void foo(int x) { ... } you write def foo(x) ... end, and so on...

Here is how my final version of the game looks when you run it:

C:\Documents and Settings\Christian\Desktop>ruby 4irad.rb
Run against ai (A), another player (P) or let two AIs play against eachother (X)
A
Enter AI 1 level (1 to 5):
3
Enter name of human player (default Human 1):

---------------
|0 1 2 3 4 5 6|
---------------
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
---------------
AI 1 (3) played: 3
---------------
|0 1 2 3 4 5 6|
---------------
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | |O| | | |
---------------
Human 1, make your move:
2
Human 1 played: 2
---------------
|0 1 2 3 4 5 6|
---------------
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | |X|O| | | |
---------------
AI 1 (3) played: 3
---------------
|0 1 2 3 4 5 6|
---------------
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | |O| | | |
| | |X|O| | | |
---------------
Human 1, make your move:

and so on...

I wonder how much code can you post in a blog, without the post becoming too long? I would like to post the code as an attachment, but I'm not sure you can do that in blogger, so I'll simply put it here. To try it out, copy the code to a file, i.e. 4inarow.rb, then run it using ruby 4inarow.rb. Enjoy ;)

$debugEnabled = false
def debug(text)
if $debugEnabled then
puts ">>> " + text
end
end

class State

attr_reader :cols, :rows, :player, :winner, :lastPlayed, :isFull

Player1 = "Player 1"
Player2 = "Player 2"

def initialize
@cols = []
(0..6).each { @cols << [] }
@rows = []
(0..5).each { @rows << Array.new(7, nil) }
@player = Player1
@winner = nil
@isFull = false
end

def deep_copy
return Marshal.load( Marshal.dump( self ) )
end

def to_s
return "---------------\n" +
"|" + (0..6).to_a.join(" ") + "|\n" +
"---------------\n" +
@rows.reverse.collect{|row| "|" + row.collect{|r| if !r then " " elsif r == Player2 then "X" else "O" end}.join("|") + "|"}.join("\n") + "\n" +
"---------------"
end

def canPlay( i )
return @winner == nil && @cols[i].length < 6
end

def play( i )
if canPlay i then
rowIndex = @cols[i].length
@cols[i] << @player
@rows[rowIndex][i] = @player
@lastPlayed = i

switchPlayer
updateWinner
updateIsFull
end
end

def updateIsFull
for p in @rows[5]
if !p then return end
end
@isFull = true
end

def findWinner( row )
for i in 0..(row.length-4)
p = nil
n = 0
for j in 0..3
rp = getPlayed(row[i+j])
if rp.class != Fixnum then
if !p then
p = rp
elsif p != rp
p = 0
break
end
if rp != 0 then
n += 1
end
end
end
if p != 0 and n == 4 then
return p
end
end
return nil
end

def updateWinner
for row in getAllRanges
p = findWinner row
if p then
@winner = p
end
end
end

def switchPlayer
if @player == Player1 then
@player = Player2
else
@player = Player1
end
end

def getPlayed(cell)
c = cell[0]
r = cell[1]
col = @cols[c]
if r >= col.length then
return r - col.length + 1
end
return @cols[c][r]
end

def posValid( c, r )
return c >= 0 && c <= 6 && r >= 0 && r <= 5
end

def getDiagonal( c, r, dc )
d = []
while posValid( c, r )
d << [c, r]
c += dc
r += 1
end
return d
end

def getAllDiagonalsRanges
diags = []
for r in 0..5
diags << getDiagonal( 0, r, 1 )
end
for c in 1..6
diags << getDiagonal( c, 0, 1 )
end
for r in 0..5
diags << getDiagonal( 6, r, -1 )
end
for c in 0..5
diags << getDiagonal( c, 0, -1 )
end
return diags
end

def getAllColsRanges
return (0..6).collect{|c| (0..5).collect{|r| [c, r]}}
end
def getAllRowsRanges
return (0..5).collect{|r| (0..6).collect{|c| [c, r]}}
end

def getAllRanges
return getAllColsRanges +
getAllRowsRanges +
getAllDiagonalsRanges
end
end

class Node

attr_reader :state, :children, :isLeaf

def initialize( state, player )
if !player then
raise "player is nil"
end

@state = state
@isLeaf = true
@children = {}
@player = player
end

def getPoints( row )
points = 0.0
for i in 0..(row.length-4)
p = nil
n = 0
divider = 1
for j in 0..3
rp = @state.getPlayed(row[i+j])
if rp.class == Fixnum then
divider *= rp
else
if !p then
p = rp
elsif !rp then

elsif p != rp
p = nil
break
end
if rp then
n += 1
end
end
end
if p then
blockPoints = calculatePoints n / divider
if p == @player then
points += blockPoints
else
points = points - blockPoints
end
end
end
if points.infinite? then
debug "Found winner: " + points.to_s + ": " + row.join("|")
end
return points
end

def calculatePoints( n )
return n.to_f**3 / (4 - [n,4].min)
end

def nodePoints
points = 0.0
for row in state.getAllRanges
p = getPoints(row)
if p != 0 then
#debug row.collect{|r| if r then r else " " end}.join("|") + " " + p.to_s
end

newPoints = points + p
if newPoints.nan? then
raise "points + p == NaN: points=" + points.to_s + " p=" + p.to_s
end
points = newPoints
end
#puts state
#puts "Points: " + points.to_s
return points
end

def allChildPoints
return children.values.collect {|child| child.totalPoints}
end

def isMyTurn
return state.player == @player
end

def totalPoints
if @totalPoints then
return @totalPoints
end
p = nil
if @isLeaf then
p = nodePoints
else
if isMyTurn then
p = allChildPoints.max
else
p = allChildPoints.min
end
end
if !p then
raise "nil points returned"
end
@totalPoints = p
return p
end

def grow
if @totalPoints then
#puts "Gah, totalPoints is set"
@totalPoints = nil
end
if @isLeaf && !@state.winner
#debug "Growing leaf: " + self.to_s
for i in 0..6
if @state.canPlay i then
childState = @state.deep_copy
childState.play i
child = Node.new(childState, @player)
children[i] = child
end
end
@isLeaf = false
else
for child in @children.values
child.grow
end
end
end

def findNodeWithState( state )
if state.lastPlayed then
return @children[state.lastPlayed]
else
return nil
end
end

def to_s
return "Played: " + @state.lastPlayed.to_s + " Points: " + totalPoints.to_s
end
end

class AIPlayer

def initialize( level, name )
@level = level
@name = name
end

def to_s
return @name + " (" + @level.to_s + ")"
end

def selectBestNode
# Add some randomness
bestChild = nil
bestChildCount = nil
for child in @node.children.values
#puts child.state
#puts child.totalPoints
if !bestChild then
bestChild = child
bestChildCount = 1
else
if child.totalPoints > bestChild.totalPoints then
bestChild = child
bestChildCount = 1
elsif child.totalPoints == bestChild.totalPoints then
bestChildCount = bestChildCount + 1
if rand(bestChildCount) == 0 then
bestChild = child
end
end
end
end

#Just to make sure
if !bestChild then
raise "bestChild is nil"
end

debug "Best points: " + bestChild.totalPoints.to_s


@node = bestChild
end

def play(state)
if state.lastPlayed then
debug "Finding last played node"
@node = @node.findNodeWithState(state)
@node.grow
end
debug "Finding the best node"
selectBestNode
@node.grow
state.play @node.state.lastPlayed
end

def setupGame(state, player)
@node = Node.new(state, player)
debug "Creating min max tree (" + (7**@level).to_s + " nodes)..."
(1..@level).each{ @node.grow }
end

end

class HumanPlayer

def initialize(name)
@name = name
end

def to_s
return @name
end

def play(state)
puts @name + ", make your move:"

begin
toPlay = gets
if toPlay == "q\n" then
exit
end
toPlay = toPlay.to_i
if !state.canPlay toPlay
puts "Can't play " + toPlay.to_s + ". Select another:"
selectOther = true
else
state.play toPlay
end
end while selectOther
end

def setupGame(state, player)

end
end

def runGame

puts "Run against ai (A), another player (P) or let two AIs play against eachother (X)?"
gameType = readline.strip
case gameType
when "A", "a"
player1 = createAIPlayer
player2 = createHumanPlayer

when "P", "p"
player1 = createHumanPlayer
player2 = createHumanPlayer

when "X", "x"
player1 = createAIPlayer
player2 = createAIPlayer

else
puts "Invalid input: #{gameType}"
exit
end

players = [player1, player2]
state = State.new

player1.setupGame(state, State::Player1)
player2.setupGame(state, State::Player2)

while (!state.winner && !state.isFull)
for player in players
puts state
player.play(state)
puts player.to_s + " played: " + state.lastPlayed.to_s
if state.winner then break end
end
end

if state.winner
puts state
puts "Winner: " + state.winner.to_s
elsif state.isFull
puts state
puts "Draw!"
end
end

$aiCounter = 0
def createAIPlayer
$aiCounter = $aiCounter + 1
name = "AI " + $aiCounter.to_s
puts "Enter " + name + " level (1 to 5):"
level = readline.to_i
return AIPlayer.new(level, name)
end

$humanCounter = 0
def createHumanPlayer
$humanCounter = $humanCounter + 1
defaultName = "Human " + $humanCounter.to_s
puts "Enter name of human player (default " + defaultName + "):"
name = readline.strip
if !name || name == "" then
name = defaultName
end
return HumanPlayer.new(name)
end

def runTests

state = State.new

state.play(3)
state.play(3)
state.play(4)
state.play(4)
state.play(5)
state.play(5)
state.play(6)

#test getPlayed
if state.getPlayed([3, 0]) != State::Player1 then raise "getPlayed doesn't work" end
if state.getPlayed([3, 1]) != State::Player2 then raise "getPlayed doesn't work" end
if state.getPlayed([3, 2]) != 1 then raise "getPlayed doesn't work" end
if state.getPlayed([3, 2]).class != Fixnum then raise "getPlayed doesn't work" end

#test findWinner
winningRow = (3..6).collect{|c| [c, 0]}
if !state.findWinner(winningRow) then raise "findWinner doesn't work. winningRow=" + winningRow.to_s end

#test state.winner
if state.winner == nil then raise "winner should be set" end

puts "All tests ok"

end

runGame
#runTests