![]() |
Edit |
![]() |
|
![]() |
Recent Changes |
![]() |
Subscriptions |
![]() |
Lost and Found |
![]() |
Find References |
![]() |
Rename |
| Search |
![]() |
List all versions |
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.
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
![]() |
| This site supports the new NoFollow anti-spam initiative. |
Recent Topics