Posts tagged ‘fsharp’

Poker : Programming Problem

Although it has been a while since Brian posted the poker problem in his blog, I haven’t got the chance to look at it until I came across in ProjectEuler Problem 54. It is not the elegant or best solution at all, but just wanted to join the crew and can confirm it works with the problem’s 1000 games’ dataset.

Hope this helps.

#light
(* Problem 54 *)
type suit = |Spades |Hearts |Clubs |Diamonds 
type card=  |Ace = 14 |Two = 2  |Three =3  |Four = 4 |Five = 5 |Six = 6 
            |Seven =7  |Eight = 8 |Nine = 9 |Ten = 10 |Jack = 11 |Queen =12 |King  =13
type acard = (card * suit)  
 
let carder x:card= enum x
 
let card_value = function
    | 'A' -> card.Ace
    | 'K' -> card.King
    | 'Q' -> card.Queen
    | 'J' -> card.Jack  
    | 'T' -> card.Ten
    | c ->  carder(System.Int32.Parse(c.ToString()))
 
let suit_value = function
    | 'S' -> Spades
    | 'H' -> Hearts    
    | 'C' -> Clubs
    | 'D' -> Diamonds    
    | a -> invalid_arg (a.ToString())
 
let Create (str: string) :acard = (card_value str.[0],suit_value str.[1])
 
let isstraigh (mycards:acard list)  = 
    let mycards = List.sort_by (fun (a,b) -> a,b) mycards
    let rec isstr previouscard mycards   (straightlist : acard list) =
        if straightlist.Length >= 5 then straightlist
        else match mycards with  
                | cur :: rest -> if int (fst cur) = int ((fst previouscard)) + 1 then isstr cur rest  (cur::straightlist)
                                 else isstr cur rest  []                           
                | _ -> []
    let head = List.hd mycards
    isstr head (List.tl mycards) [head]
 
let pairl (mycards : acard list) groupfunction minelementCount =
                 mycards |> Seq.group_by groupfunction
                         |> Seq.filter (fun a -> Seq.length (snd a) >= minelementCount)
                         |> Seq.to_list
                         |> List.unzip
 
 
type Ranks =
    | Highest of card
    | Pair of  card
    | TwoPair of card*card
    | Three of card
    | Straight of card
    | Flush of card 
    | FullHouse of  card*card
    | Four of  card
    | StraightFlush of card
 
type Player = |One |Two  |Noone
 
let rank (mycards :  acard list) =     
    let traverseL  (l :'a list)  = if not l.IsEmpty then l  |> List.hd |> Seq.to_list
                                   else []                            
    let isflush mycards = snd (pairl mycards snd 5)  |> traverseL
 
    let FofF l = traverseL l |> List.hd |> fst
 
    let flush = isflush mycards
    let straight = isstraigh mycards
 
    let ispair count= pairl mycards fst count                                                             
 
    let four,fours = let f,s = ispair 4 
                     f,s|> traverseL
    let three,threes = ispair 3 
    let two,twos = ispair 2
 
    let maxcard c = fst (List.max c)
 
    if not flush.IsEmpty && not straight.IsEmpty then StraightFlush(maxcard flush),flush
    elif  not fours.IsEmpty then Four(four.Head),fours
    elif not threes.IsEmpty && not twos.IsEmpty && FofF twos <> FofF threes then FullHouse(two.Head, three.Head), List.append (threes |> traverseL) (traverseL twos)
    elif not flush.IsEmpty then Flush(maxcard flush),flush
    elif not straight.IsEmpty then Straight(maxcard straight),straight
    elif not three.IsEmpty then Three( three.Head), threes |> traverseL
    elif List.length two = 2 then TwoPair(two.Head,two.Tail.Head),Seq.append (twos.Head) (twos.Tail.Head) |> Seq.to_list
    elif not (twos |> traverseL).IsEmpty then Pair(two.Head), twos |> traverseL
    else Highest(maxcard mycards), [List.max mycards]
 
let play input =
    let convert (line: string) = let l = line.Split([|' '|])                                  
                                 [|[for j in 0 .. 4 do yield Create( l.[j])]; [for j in 5 .. 9 do yield Create( l.[j])]|]                                                                  
    let playercrds = convert input              
    let rec iswinner pcards=               
                let ranks,rankcards = pcards |> Array.map (rank)  |> Array.unzip
 
                let removecards (mainlist) (toberemoved)  =                         
                        mainlist |> Array.map2 (fun rem main-> main |> List.filter (fun c->
                                List.fold_left(fun ac x-> if x = c then ac && false else ac && true) true rem)) toberemoved
 
                if ranks.[0]>ranks.[1] then One
                elif ranks.[0]<ranks.[1] then Two
                else iswinner  (removecards  pcards rankcards) 
 
    iswinner playercrds
 
 
play  "5H 5C 6S 7S KD 2C 3S 8S 8D TD" 
play  "5D 8C 9S JS AC 2C 5C 7D 8S QH"
play  "2D 9C AS AH AC 3D 6D 7D TD QD"
play  "4D 6S 9H QH QC 3D 6D 7H QD QS"  // prob pair queens look at the highes
play  "2H 2D 4C 4D 4S 3C 3D 3S 9S 9D" 
 
play  "2H 2D 4C 4D 4S 2H 2D 4C 4D 4S" 
 
play  "TH 8D 6C 4D 3S TH 8D 6C 4D 4S" 
 
 
 
let rdinput =   use file = System.IO.File.OpenText("poker.txt")
                let p1count = ref 0  
 
                while not file.EndOfStream do
                 if play (file.ReadLine()) = One then p1count := !p1count+1
 
                file.Close()
                !p1count

Automatic iTunes Folder Playlist in Javascript and F#

The other day, I was struggling with my playlist on itunes. As you may know iTunes doesn’t have the notion of the folder structure of your mp3s. So if you do organise your music in folders you won’t have them in iTunes. I decided to write a script using the iTunes COM SDK for doing that.

Here is the scenario all the music is stored in d:\Music. So the script starts exploring everything inside in the folder and create an entry in iTunes, and it happens only in one level. First I did it with JavaScript with a lot of pain. I was pain because of the ugly interface and also unclear documentation. Luckily there were the samples in the SDK to play with it.

var	iTunesApp = WScript.CreateObject("iTunes.Application");
var	mainLibrary = iTunesApp.LibraryPlaylist;
var	mainLibrarySource = iTunesApp.LibrarySource;
var	tracks = mainLibrary.Tracks;
var	numTracks = tracks.Count;
var numPlaylistsCreated = 0;
var ITTrackKindFile	= 1;
var	albumArray = new Array();
var	playlists = mainLibrarySource.Playlists;
 
for (var i = 1; i <= numTracks; i++)
{
	var	currTrack = tracks.Item(i);
	if (currTrack.Kind == ITTrackKindFile)
	{
		if (currTrack.Location != "")
		{
            var p =currTrack.Location
 
            var location = p  
	        if ((location != undefined) && (location != ""))
	        {
		        if (albumArray[location] == undefined)
		        {		    
			        albumArray[location] = new Array();
		        }
 
		        albumArray[location].push(currTrack);
	        }
         }
	}
}
WScript.Echo("   Tracks Read " + numTracks);
for (var albumNameKey in albumArray)
{
    var trackArray = albumArray[albumNameKey];
    var p = albumNameKey;
    var ignoreFirst =   p.indexOf("music",0); //3     
    var firstChar = 0;             
    var secondChar = 0;         
    var firstChar = ignoreFirst +5 ;
    var myArr = new Array();
    while (true)
    {
        var firstChar = p.indexOf("\\", firstChar);
        var secondChar = p.indexOf("\\", firstChar+1);
 
        if (secondChar == -1 )
            break;
        var newStr = p.substring(firstChar+1, secondChar)
        myArr.push(newStr);
        firstChar = secondChar;
    }
    var plist = null;
    if (myArr.length == 0)
    {
        if (playlists.ItemByName("_") != null)
        {
          plist = playlists.ItemByName("_")   ;
        }
        else
        { 
            plist = iTunesApp.CreatePlaylist("_");
            numPlaylistsCreated++;
        }
    }
   else if ( playlists.ItemByName(myArr[0]) != null)
    {
        plist = playlists.ItemByName(myArr[0])
    }
    else 
    {
        plist = iTunesApp.CreatePlaylist( myArr[0]);
        numPlaylistsCreated++;
    }
    for (var trackIndex in trackArray)
    {
        var		currTrack = trackArray[trackIndex];
 
        plist.AddTrack(currTrack);
    }
}
	    //create playlist
if (numPlaylistsCreated > 0)
{
		WScript.Echo( numPlaylistsCreated + " (s) created.");
}
else
{
	WScript.Echo("No playlist createds");
}

.

(It is written in a quick way without caring about the beauty :) )

It was doing its job that I wanted but I also wanted to have my folder structure in itunes rather than a single entry for a folder. This time I implemented in F#. I have chosen F# mainly because of the type inference and the intellisense. In JavaScript without the documentation, you couldn’t do anything. F# is also chosen because of interoperability with COM and friends. So I have to admit that I didn’t like the COM interface of iTunes. All the best F# implementation with more functionality gave %40 less code

#light
#r "Interop.iTunesLib.dll";;
open iTunesLib;
open System.IO;
open System.Text.RegularExpressions
 
let itunes = new iTunesLib.iTunesAppClass()
let playLists = itunes.LibrarySource.Playlists  
let tracks = itunes.LibraryPlaylist.Tracks
 
let track = tracks.Item(2)
for track in tracks do
    if track.Kind = ITTrackKind.ITTrackKindFile &&
 (track:?> IITFileOrCDTrack).Location <> null then
        let track = track :?> IITFileOrCDTrack        
        let regex = new System.Text.RegularExpressions.Regex( @"^( ?<Drive>([a-zA-Z])):\\_music\\
((?< directory>[\w\W]+)\\)*((?< 
playlist>[\w\W]+)\\)(?< 
filename>([\w\W]+.mp3))",
            RegexOptions.IgnoreCase)
 
        printf "%d\n" track.Index
 
        let resMatch = regex.Match(track.Location)       
        if resMatch.Length = 0 
            ()
        else                       
            let lastFolder = 
             resMatch.Groups.Item("directory").Captures |> Seq.untyped_fold
                (fun (acc:IITPlaylist) (a: (System.Text.RegularExpressions.Capture)) ->                 
                    match (playLists.ItemByName(a.Value))  with
                    | null -> 
                        if acc.Index = 1 then
                            itunes.CreateFolder(a.Value)     
                        else
                            (acc :?> IITUserPlaylist).CreateFolder(a.Value)
 
                    | a1 -> 
                        if (a1 :?> IITUserPlaylist).SpecialKind = ITUserPlaylistSpecialKind.ITUserPlaylistSpecialKindFolder then
                            a1
                        else 
                          itunes.CreateFolder(a.Value)                          
                )
                (playLists.Item(1) )
 
            let pListName = resMatch.Groups.Item("playlist").Captures.Item(0).Value
 
            let lastPlayList= 
                match (playLists.ItemByName(pListName))  with
                | null ->
                    if lastFolder.Index = 1 then
                        itunes.CreatePlaylist(pListName) :?>    IITUserPlaylist    
                    else 
                        let fol = lastFolder :?> IITUserPlaylist                
                        fol.CreatePlaylist(pListName) :?>  IITUserPlaylist
                | a2 ->
                     if (a2 :?> IITUserPlaylist).SpecialKind = ITUserPlaylistSpecialKind.ITUserPlaylistSpecialKindFolder then                    
                        match (playLists.ItemByName("_" + pListName))      with
                        | null ->   (a2:?> IITUserPlaylist).CreatePlaylist("_" + pListName) :?> IITUserPlaylist
                        | a3 ->a3    :?> IITUserPlaylist
                     else
                        a2     :?> IITUserPlaylist
 
 
            lastPlayList.AddTrack(ref (track :> obj)) |> ignore
done

.

The folder playlist is not implemented on iPod. Although it make sense to use in iTunes, there is no point doing it for iPod. I was a bit frustrated, it was my sole purpose. Anyway it was nice to play with it…

So I hope it will be helpful in a way, the folder playlist didn’t help me, maybe in the next firmware update… On the other hand, I still use the first implementation for my automatic one-level folder playlist.