Showing posts with label project euler. Show all posts
Showing posts with label project euler. Show all posts

Thursday, May 28, 2009

100 problems solved on Project Euler

Today, I just finished my 100th problem on Project Euleur, using Scala - of course.

Project Euler is a really good way to learn a language on small problems, to see its different sides and idiomatic constructs, and be confronted on performance/algorithm optimization choices - that teaches you to feel when something migth make you gain an order or two of execution time / memory consumption, and when you are working for peanuts.

OK, you won't see how the language solves big architectural design and maintenance problems, but you will learn if the language fits your mind, and if you are able to get things done with it. And definitly, Scala shines on such problems, and completly fits my mind.

So, it was a really good learning experience. Now, I need to test it on bigger problem, to see how it goes on the long run, and I have some ideas for that...

Saturday, January 3, 2009

ProjectEuler#84 : monopoly game in Scala

In the last weeks, I slowed down my completion rate of Project Euler's problems a lot. But yesterday, looking for something to do for the last days of Christmas's holidays, I found the #84 problem which seemed quite fun: the goal is to find what are the 3 most visited squares in the game if we played with 4 sided dices in place of the common 6 sided ones.

It's interesting, because it's a probabilistic problem, and I'm lame in proba. So, as the problem does not require too much precision (at contrary to problem #213...), I could solve it in a monte-carlo way. This is the kind of proba solving method that suits me the best, because no proba are used...

As always, it was really simple to express the solution I imagine in Scala, and that's the result.

Be careful ! If you play at Project Euler, the following contains a solution that you may prefer to find by yourself !



/*
* This is the companion object of our Monopoly class, it's the
* place where we put all the common and side stuff,
* the board definition and our Random number generator
* definition, for example.
*/
object Monopoly {
/*
* The monopoly board
* (yes, we could have just use the square numbers,
* but I found that clearer at the begining, and the overhead is
* really small)
*/
val board = Array(
"GO" , "A1", "CC1", "A2" , "T1", "R1", "B1" , "CH1", "B2", "B3",
"JAIL", "C1", "U1" , "C2" , "C3", "R2", "D1" , "CC2", "D2", "D3",
"FP" , "E1", "CH2", "E2" , "E3", "R3", "F1" , "F2" , "U2", "F3",
"G2J" , "G1", "G2" , "CC3", "G3", "R4", "CH3", "H1" , "T2", "H2"
)

/*
* Match a case name to it's number
*/
val reverseBoard = {
var m = Map[String,Int]()
for(i <- 0 until board.size) m = m.+((board(i) , i))
m
}

abstract class Random {
def next : Int
}

/*
* A simple homothetie from int's range to a new one range,
* used to transform Random to dice roll
*/
def randomInRange(start:Int,end:Int, random: Random) : Int = {
//that was funny to set up, there was some intersting
//overflow and int division to take care of ;)
val i = start +
((random.next.toFloat - Int.MinValue) * (end+1 - start) /
(Int.MaxValue.toFloat - Int.MinValue) ).toInt
if(i > end) end else if(i }

/*
* Utility methods that sends three square back
*/
def goBack3(square:String) : String = {
val i = reverseBoard(square)
if(i>2)board(i-3) else board(board.size+i-4)
}

/*
* Utility method that given a square,
* return the next Railway Company square
*/
def gotoNextR(square:String) : String = {
val i = reverseBoard(square)
if(i>5 && i <= 15) "R2"
else if(i>15 && i <= 25) "R3"
else if(i>25 && i <= 35) "R4"
else "R1"
}

/*
* Utility method that given a square,
* return the next Utility company square
*/
def gotoNextU(square:String) : String = {
val i = reverseBoard(square)
if(i > 12 && i <= 28) "U2"
else "U1"
}

/*
* Represent a stack of cards, used for Communty cards and
* Chance.
* We cycle throught an array to convey the idea that cards
* are put back on the stack after use.
*
* Each "card" is a function that is awaiting for the real
* square name to return the resulting square move. Most of
* the card does nothing for us, so the function is identity
* (return the square itself)
*/
class CardStack(random: Random, private val size:Int) {
protected val cards = new Array[String=>String](size)
for(i <- 0 until size) cards(i) = (square:String) => square
private var index = 0
private def rand = random

def next(square:String) : String = {
index = (index+1)%size
cards(index)(square)
}
}

/*
* The class that represents the chance card heap.
* Fairly random card distribution,
* see http://xkcd.com/221/ for more details
*/
class CH(random: Random) extends CardStack(random,16) {
cards(1) = gotoNextR _
cards(2) = goBack3 _
cards(4) = _ => "GO"
cards(5) = _ => "R1"
cards(7) = _ => "C1"
cards(9) = _ => "H2"
cards(10) = gotoNextR _
cards(12) = _ => "JAIL"
cards(14) = gotoNextU _
cards(15) = _ => "E3"
}

/*
* The class that represent the community chest card heap.
* Fairly random card distribution,
* see http://xkcd.com/221/ for more details (again)
*/
class CC(random: Random) extends CardStack(random,16) {
cards(4) = _ => "GO"
cards(10) = _ => "JAIL"
}

/*
* A class that represents a pair of dices
* with a given number of faces
*
* The only intersting thing to do with
* is of course to roll them
* (with cards, dices and money involved, I can't
* understand where The Monopoly failed to be attractive).
*/
class Dices(faces:Int,random: Random) {
def roll = (
randomInRange(1,faces,random),
randomInRange(1,faces,random)
)
}
}

/*
* That's the actuall Monopoly representation.
* In our simplification of the Game, a
* Monopoly has a pair of dices, a Community Chest
* and a Chance heap of cards, a log of the number
* of consecutive doubles, and a marker for the
* current position of the player (notice that as
* there is only one player, the game should be
* even less attractive than the real Mo,opoly...)
*
* Nonetheless, we want to play to our game, so
* we have a "turn" method that roll the dices and
* move the player thanks to a "next square" method
* processor.
*
* And that's all, our monopoly really looks like
* the real !
*/
class Monopoly(faces:Int,random: Monopoly.Random) {
import Monopoly.{CC,CH,Dices}
import Monopoly.{board=>B,reverseBoard=>RB}

val dices = new Dices(faces,random)
val cc = new CC(random)
val ch = new CH(random)
var doubles = 0
var currentPosition = 0

private def next(square:String) : String = {
val s = square match {
case "CC1" => cc.next(square)
case "CC2" => cc.next(square)
case "CC3" => cc.next(square)
case "CH1" => ch.next(square)
case "CH2" => ch.next(square)
case "CH3" => ch.next(square)
case "G2J" => "JAIL"
case _ => square
}

if(s == square) s
else next(s)
}

/*
* A turn starts at one square and end on another,
* perhaps passing on several other squares
*/
def turn() : String = {
val d = dices.roll

var newSquare = next(
B((currentPosition+d._1+d._2)%B.size ))

//3 consecutive double send to Jail
if(d._1 == d._2) {
doubles = doubles+1
if (doubles >= 3) {
doubles = 0
newSquare = "JAIL"
}
} else doubles = 0

currentPosition = RB(newSquare)
newSquare
}
}

/*
* Now that we can have a Monopoly,
* we game play a party !
* In Scala, party are called "application",
* but it's because software developper are
* known to be dull people.
*/
object Problem85 extends Application {
import Monopoly.{reverseBoard=>RB}

/*
* We want to observe the evolution of
* the game, so we define a Logger
* (I'm sooooo unsurprising)
*/
abstract class Logger {
def register(square:String)
}

/*
* A looger that just output to console,
* used at the begining, to see the player
* evolves...
*/
trait ConsoleLogger extends Logger {
override def register(s:String) = println(s)
}

/*
* ... and was quickly replaced by a logger that
* just keep everything in a map, not that
* it was boring... Well, actually, it was.
*/
trait MapLogger extends Logger {
var map = new HashMap[String,Int]()
override def register(s:String) {
/*
* ** Note to Java developpers **
* look how it's easy to say
* "I want to retrieve a key value
* from my map, but if the key does
* not exists, defined it with the
* given default value" in Scala.
* Just think to the number of if, and
* brackets and the like in Java...
*/
map(s) = map.getOrElse(s,0) + 1
}
}

/*
* Aaaaaahhhhhhh ! At last, there is our Game !
* It's defined with a random generator, the number
* of face we want our dices to have (remember,
* that's actually the point of the problem...), and
* a limit number.
*
* The limit number is the number of turn that our
* game will play before stoping (and we, look to
* the results). As we are in a Monte Carlo similution,
* this number will have to be HUGE. Say ONE MILLION !
* Ok, that's ridiculously small, but thanks to the
* kindness of project Euler team, the number converge
* quickly, and my PC is not as fast as clusters used
* for real Monte Carlo simulation,
* so it will have to do ;)
*/
abstract class Game(
faces:Int,
random: Monopoly.Random,
limit:Int) extends Logger {

val monopoly = new Monopoly(faces,random)
var square = ""
var turn = 0

def start = {
while(turn < limit) {
this.register(monopoly.turn())
turn = turn + 1
}
}
}

/*
* Oups, don't forget to actually get a random
* generator. As explained for the Game, we don't
* need something too sophisticated for this
* simple example. We may have taken
* http://www.honeylocust.com/RngPack/ for a
* better pseudo-random generator (for example).
*/
class JavaSimpleRandom extends Monopoly.Random {
val r = new scala.util.Random()
override def next = r.nextInt
}

/* ============================= */
/* Actual test */
/* ============================= */

val r = new JavaSimpleRandom()
val rounds = 1000000
val test = new Game(4,r,rounds) with MapLogger

print("begins... ")
test.start
println("results:")
val a = test.map.toList.sort(
( x:(String,Int), y:(String,Int) ) => x._2 > y._2)
for(i <- 0 until 5) {
println("%5s (%02d): %s".
format(
a(i)._1,
RB(a(i)._1),
a(i)._2.toFloat/rounds)
)
}
}



Voilà !

Tuesday, November 4, 2008

ProjectEuler#42, aka Scala shines as a scripting language

I'm still playing with project Euler, and I'm going to love Scala more and more.
Yesterday, I solved the #42 problem, on which we need to parse a text file, and make some simple transformations and filtering on the results (it's why I choose this title, because it's generally the kind of things you expect to do in a scripting language, and well, I'm really
impressive, and I read this morning the post "Scala as a scripting language?", and I say "ho, it's exactly what I felt when I did pb#42 yesterday, let's blog on it, it's fashion :)

So, this problem is a simple Project Euler one (at least, there is no math on it), but just stop and think to what may look the Java's solution. Yes, you will have to deal with Files, input stream, and the like, filtering data, sorting things, and even if it's quite simple, it's always a little too heavy compared to what it may be.

So, there is the full solution in Scala, in a mixed imperative and functional style, as produced in the first sketch version (OK, I'm lying, in the first sketch, I didn't include comments and variables names looked more to 'lsv' and the like, these transformations are blog add-ons).

I'm sure there is a lot of optimizations available, but the important think is that Scala let you program as you think, whitout to much noise and boilerplate...

So let's go for the code:


package ex26_50
import scala.io.Source
/*
Problem#42
The nth term of the sequence of triangle numbers
is given by, tn = n(n+1);
so the first ten triangle numbers are:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...

By converting each letter in a word to a number corresponding to its
alphabetical position and adding these values we form a word value.
For example, the word value for SKY is 19 + 11 + 25 = 55 = t10.

If the word value is a triangle number then we shall
call the word a triangle word.

Using words.txt (right click and 'Save Link/Target As...'),
a 16K text file containing nearly two-thousand common English
words, how many are triangle words?

*/

object Problem42 extends Application {

/*
* iterate over all the lines of a file and
* split each one on ',', remove surronding ",
* and put the result in a list
*/
def parseWords(file:String): List[String] = {
for {
line <- Source.fromFile(file).getLines.toList
s <- line.split(",")
if(!s.isEmpty)
} yield s.replaceAll("\"","")
}

/*
* Simple mapping function that associate
* each char to it's value (position in the alphabet, begin to 1)
* and a word to the sum of its char value
*/
def wordValue(s:String) : Int = {
var sum = 0
for(c <- s) {
// iterate over the chars of the string
val cval = c.toInt
if(cval >= 64 && cval <= 90) {
// 64 = ascii val for 'A', 90 = ascii val for 'Z'
sum = sum + cval - 64
} else {
error("Unauthorized char: " + c.toString)
}
}
sum
}

/*
* That's the definition of the list number on witch we will work:
* - parse the file to a list of word
* - transform each word to it's word value
* - sort the resulting, so that we can know the
* upper bound of our triangle value to caluculate
*/

val list_wordValues =
parseWords("src/words.txt").map(wordValue(_)).sort(_ > _)

//we also see that in Scala, Format syntax is actually usable
println("Max value in %s words: %s (min: %s)".format(
list_wordValues.size,
list_wordValues.head,
list_wordValues.reverse.head))

/*
* Given the upper bound,
* generate the set of triangle number
*/
val set_triangles = {
var set = Set[Int]()
var n = 1
var i = 1
while (n < max ) {
n = (i * (i+1))/2
i = i + 1
set = set.+(n)
}
set
}

/*
* That's all, we just have to filter the list of words to
* only keep whose which are in the set of triangles value,
* and take the size of the resulting list.
*/
val nb = list_wordValues.filter( (i:Int) =>
set_triangles.contains(i)).size

println("Found %s triangles words !".format(nb))
}


I love scala :)

PS: It's the full working solution, appart from the leading "_", sorry for the formatting, it seems that blogger is not the most friendly for code formatting. If somebody has some hint on that subject, I will be glad to heard them (ok, perhaps I should have a look on the plugins)

EDIT: I found a way to highlight code thanks to SHJS. Thanks to the author for this plugin that wasn't to hard to make it work with blogger, and which the only Javascript highlighter that supports Scala :)

  © Blogger template 'Minimalist G' by Ourblogtemplates.com 2008

Back to TOP