strangelights.com

Main F# Site

Edit Edit
Print Print
Recent Changes Recent Changes
Subscriptions Subscriptions
Lost and Found Lost and Found
Find References Find References
Rename Rename
Search
List all versions List all versions
Master Mind
.

This game sample is based on the Mastermind board game . Your opponent, the computer, creates a code with colored pegs which you have 12 attempts to break. To set pegs for the current guess row, select the peg color using the right hand side radio buttons, then simply click the hole you want to set. Once you have composed your guess you can press the "I feel lucky!" button to get a score. A black peg is given for each colored peg which matched both position and color. A white peg is given for each color match out of position. 4 black pegs means you have won.

The sample use Windows Presentation Foundation (WPF) controls, which in turn requires .Net 3.0. To compile you may create a new F# application project in Visual Studio 2008 and add references to dlls: PresentationCore, PresentationFramework & WindowsBase; then just hit F5.

Online Diploma Programs | arts degree | Architecture degree | Online Interior Design degree | Online Business management degree | Harley Davidson Bedding | Mikimoto Pearl Necklace

Good luck!

    // Mastermind board game implementation using WPF by Phillip Trelford 2008
    // See: http://en.wikipedia.org/wiki/Mastermind_(board_game)
    // Created against Microsft F# CTP 1.9.6.2 on VS2008 Pro
    // Required references: PresentationCore, PresentationFramework & WindowsBase


    #light


    open System
    open System.Windows
    open System.Windows.Media
    open System.Windows.Controls
    open System.Windows.Shapes
    open System.Windows.Controls.Primitives   


    /// Peg colors    
    let colors = 
        [Colors.Yellow; Colors.Red; Colors.Green; Colors.Cyan; Colors.Blue; Colors.Purple]


    /// Creates code to break    
    let CreateCode () =
        let r = new Random()       
        List.init 4 (fun _ ->  
            r.Next(List.length colors) 
            |> List.nth colors
        )              


    /// Scores guess for code    
    let Score code guess =        
        let exactMatches, misses =
            List.zip code guess
            |> List.partition (fun (a,b) -> a = b) 
        let xs, ys = misses |> List.unzip
        let Count x xs = xs |> List.filter (fun k -> k = x) |> List.length  
        /// Colors matched count
        let colorMatchCount = colors |> List.sum_by (fun c -> 
            min (xs |> Count c) (ys |> Count c)
        )    
        List.length exactMatches, colorMatchCount                     


    /// Table layout panel type    
    type TableLayout (columnCount:int,rowCount:int) =
        inherit Grid ()
        // Create column and row defintions
        do  for i=1 to columnCount do 
                ColumnDefinition() |> base.ColumnDefinitions.Add
            for i=1 to rowCount do
                RowDefinition () |> base.RowDefinitions.Add
        /// Default constructor
        new () = new TableLayout(0,0)
        /// Sets column widths    
        member this.SetColumnWidths (lengths:GridLength seq) =
            lengths 
            |> Seq.map (fun length -> ColumnDefinition(Width=length))
            |> Seq.iter this.ColumnDefinitions.Add       
        /// Sets row heights   
        member this.SetRowHeights (lengths:GridLength seq) =
            lengths 
            |> Seq.map (fun length -> RowDefinition(Height=length))
            |> Seq.iter this.RowDefinitions.Add        
        /// Adds item              
        member this.AddItem (item:#UIElement) =
            let span = 
                match this.ColumnDefinitions.Count with
                | 0 -> 1
                | n -> n
            let index = this.Children.Count
            item |> this.Children.Add |> ignore        
            Grid.SetColumn(item, index % span)
            Grid.SetRow(item, index / span)  
        /// Adds range of items
        member this.AddRange (items:#UIElement seq) =
            items |> Seq.iter this.AddItem


    /// Peg hole type
    type PegHole () = 
        inherit ButtonBase ()
        /// Creates peg content
        let CreatePegContent (color) =        
            let background = new Ellipse(Fill=SolidColorBrush(color),
                                         Stroke=SolidColorBrush(Colors.Black),
                                         StrokeThickness=1.0)        
            let radial = RadialGradientBrush(Colors.White, Colors.Transparent,
                                             RadiusX=1.0/3.0, RadiusY=1.0/3.0) 
            let grid = new Grid()                                         
            [background; new Ellipse(Fill=radial)]                              
            |> Seq.iter (fun child -> grid.Children.Add child |> ignore)          
            grid     
        /// Creates hole content
        let CreateHoleContent() =                 
            let circle = new Ellipse(Stroke=SolidColorBrush(Colors.White, Opacity=0.0666),
                                     StrokeThickness=1.5)
            let background = new Ellipse(Fill=SolidColorBrush(Colors.Black),                   
                                         RenderTransformOrigin=Point(X=0.5, Y=0.5),
                                         RenderTransform=ScaleTransform(0.5,0.5))     
            let grid = new Grid () 
            [circle; background] 
            |> Seq.iter (fun child -> grid.Children.Add child |> ignore)                                
            grid    
        let hole = CreateHoleContent()       
        do  base.Content <- hole    
        let mutable pegColor = None
        let fire, event = Event.create ()    
        /// Fires when color property changes
        member this.ColorChanged = event     
        /// Peg color property
        member this.Color
            with get = pegColor                        
            and set (value:Color option) =
                pegColor <- value                            
                base.Content <- 
                    match value with
                    | Some color -> CreatePegContent(color)
                    | None -> hole                                                                           
                fire ()


    /// Peg hole panel type
    type PegHolePanel (columnCount:int,rowCount:int,Create:_ -> PegHole) =   
        let layout = new TableLayout(columnCount, rowCount)    
        let pegs = Array.init (columnCount*rowCount) Create   
        do    layout.AddRange pegs               
        member this.Panel = layout
        member this.Pegs = pegs    
        member this.Colors = pegs |> Array.map (fun peg -> peg.Color)                  
        member this.Clear () = pegs |> Seq.iter (fun peg -> peg.Color <- None)    


    /// Decoding row type            
    type DecodingRow (GetSelectedColor) =        
        let CreateSmallPeg _ = new PegHole(Width=9.0, Height=9.0, Margin=Thickness(2.0))
        let CreateGuessPeg _ = 
            let peg = new PegHole(Width=18.0, Height=18.0, Margin=Thickness(2.0))
            peg.Click.Add (fun _ -> peg.Color <- Some(GetSelectedColor()) )                                        
            peg
        // Peg panels                        
        let scorePegs = PegHolePanel(2, 2, CreateSmallPeg)
        let guessPegs = PegHolePanel(4, 1, CreateGuessPeg)
        // Layout of peg panels
        let layout = new TableLayout()    
        do  [3.0;1.0] 
            |> List.map (fun length -> GridLength(length, GridUnitType.Star))
            |> layout.SetColumnWidths    
            [guessPegs.Panel; scorePegs.Panel]
            |> layout.AddRange
        /// Row panel container        
        let box = new GroupBox(Content=layout, IsEnabled=false, Margin=Thickness(2.0))                
        member this.Panel = box        
        member this.ScorePegs = scorePegs
        member this.GuessPegs = guessPegs
        member this.Clear() = scorePegs.Clear(); guessPegs.Clear()   


    /// Decoding board type        
    type DecodingBoard(rowCount,GetSelectedColor) =
        /// Row layout       
        let layout = new TableLayout(1, rowCount)
        /// Decoding board rows
        let rows = Array.init rowCount (fun x -> DecodingRow GetSelectedColor)
        do  rows 
            |> Array.map (fun row -> row.Panel) 
            |> layout.AddRange        
        /// Decoding board panel container
        let box = new GroupBox(Header="Decoding Board", Content=layout, Margin=Thickness(2.0))                                                                                                
        member this.Panel = box
        member this.Rows = rows
        member this.Clear() =
            rows |> Seq.iter (fun row -> 
                row.Clear(); 
                row.Panel.IsEnabled <- false
                row.Panel.IsHitTestVisible <- false
            )


    /// Peg color selector type                                
    type PegSelector() =
        /// Currently selected color        
        let mutable selectedColor = List.hd colors
        /// Selector layout
        let layout = new TableLayout()   
        do  // Set a row height for each available color
            colors 
            |> Seq.map (fun _ -> GridLength(24.0, GridUnitType.Pixel))
            |> layout.SetRowHeights  
            // Create peg color radios and add to layout
            colors |> Seq.map (fun color ->        
                /// Color peg
                let peg = new PegHole(Color=Some(color), Width=18.0, Height=18.0)
                /// Color selection radio
                let radio = new RadioButton(Foreground=SolidColorBrush(color), Content=peg) 
                // Handle button click event
                peg.Click.Add (fun _ -> radio.IsChecked <- new Nullable<bool>(true))      
                // Handle radio checked event
                radio.Checked.Add (fun _ -> selectedColor <- color)
                // Initialise radio checked status
                let isChecked = color = selectedColor
                radio.IsChecked <- new Nullable<bool>(isChecked)
                // Return radio button instance
                radio
            )
            |> layout.AddRange
        member this.Panel = layout        
        member this.Selected = selectedColor                                    


    [<STAThread>]
    do  /// Current code to break
        let code = ref []
        /// Current row index
        let rowIndex = ref 0
        /// Row count
        let rowCount = 12
        /// Peg selector
        let pegSelector = PegSelector()
        /// Decoding board
        let decodingBoard = DecodingBoard(rowCount, (fun _ -> pegSelector.Selected))       
        /// Guess button 
        let guessButton = new Button(Content="I feel lucky!", Height=24.0)    
        /// Begins guessing for current row
        let BeginRow () =    
            /// Current row
            let row = decodingBoard.Rows.[!rowIndex]       
            row.Panel.IsEnabled <- true
            row.Panel.IsHitTestVisible <- true
            row.Panel.BorderBrush <- SolidColorBrush(Colors.Black)     
            row.Panel.ToolTip <- "Click on hole to add currently selected peg color"   
            /// Set can guess
            let SetCanGuess () = 
                guessButton.IsEnabled <- 
                    row.GuessPegs.Colors 
                    |> Seq.exists (fun color -> color = None)
                    |> not       
            SetCanGuess ()
            row.GuessPegs.Pegs |> Seq.iter (fun peg ->peg.ColorChanged.Add SetCanGuess )                            
        /// Ends guessing for current row, returning true if guess matched code
        let EndRow () =
            let row = decodingBoard.Rows.[!rowIndex]
            row.Panel.IsHitTestVisible <- false
            row.Panel.BorderBrush <- SolidColorBrush(Colors.LightGray)
            // Score row
            let guess = row.GuessPegs.Colors |> Seq.map Option.get |> Seq.to_list
            let black, white = Score !code guess        
            Seq.concat [Array.create black Colors.Black;Array.create white Colors.White]
            |> Seq.iteri (fun index color -> row.ScorePegs.Pegs.[index].Color <- Some(color))
            black = (!code).Length
        /// Begins game
        let BeginGame () =
            code := CreateCode()
            decodingBoard.Clear()
            rowIndex := 0 
            BeginRow() 
        /// Ends game with specified message    
        let EndGame message =
            MessageBox.Show(message) |> ignore        
            BeginGame()                      
        // Add guess button click event handler                
        guessButton.Click.Add (fun _ ->
            match EndRow() with
            | true -> EndGame "You Win"
            | false ->
                incr rowIndex
                if !rowIndex = rowCount then EndGame "You Lose"
                else BeginRow()
        )                          
        /// Cheat button
        let cheatButton = new Button(Content="Cheat!", Height=24.0)    
        // Add cheat button click event handler
        cheatButton.Click.Add (fun _ ->
            let row = decodingBoard.Rows.[!rowIndex]
            !code |> Seq.iteri (fun index color -> 
                row.GuessPegs.Pegs.[index].Color <- Some(color))
            )        
        /// Creates color selection box                                       
        let CreateSelectionPanel colors controls =         
            /// Stack panel
            let stack = new StackPanel(VerticalAlignment=VerticalAlignment.Top)
            controls |> Seq.iter (fun control -> stack.Children.Add(control) |> ignore)         
            // Return select box   
            new GroupBox(Header="Select", Content=stack, Margin=Thickness(2.0))              
        /// Dock panel                                                                                                              
        let dock = new DockPanel ()   
        decodingBoard.Panel |> dock.Children.Add |> ignore     
        [(guessButton:>UIElement); (pegSelector.Panel:>UIElement); (cheatButton:>UIElement)]    
        |> CreateSelectionPanel colors
        |> dock.Children.Add |> ignore
        /// Main window
        let window = new Window(Title="Mastermind", Width=240.0, Height=480.0)
        window.ResizeMode <- ResizeMode.CanMinimize
        window.Background <- SolidColorBrush(Colors.Peru)    
        // Set main window content
        window.Content <- dock
        // Begin game
        BeginGame ()    
        /// Application        
        let app = new System.Windows.Application()
        app.Run(window) |> ignore
Welcome to F Sharp Wiki, view the HomePage

This site supports the new NoFollow anti-spam initiative.

Recent Topics

Copyright 2005, Robert Pickering (Others where credited) | Terms of Use