to select :prompt [:options]
  foreach :options [(print # ?)]
  forever [
    type :prompt type "| |
    make "n readword
    if (and [number? :n] [:n >= 1] [:n <= count :options]) [output item :n :options]
    print sentence [Must enter a number between 1 and] count :options
  ]
end

print equal? [huff and puff] (select
  [Which is from the three pigs?]
  [fee fie] [huff and puff] [mirror mirror] [tick tock])


function select (list)
   if not list or #list == 0 then
      return ""
   end
   local last, sel = #list
   repeat
      for i,option in ipairs(list) do
         io.write(i, ". ", option, "\n")
      end
      io.write("Choose an item (1-", tostring(last), "): ")
      sel = tonumber(string.match(io.read("*l"), "^%d+$"))
   until type(sel) == "number" and sel >= 1 and sel <= last
   return list[math.floor(sel)]
end

print("Nothing:", select {})
print()
print("You chose:", select {"fee fie", "huff and puff", "mirror mirror", "tick tock"})


textMenu[data_List] := Module[{choice},
  If[[email protected] == 0, [email protected]""];
  While[!([email protected] && [email protected] >= choice > 0),
   MapIndexed[Print[#2[[1]], ") ", #1]&, data];
   choice = Input["Enter selection..."]
   ];
  data[[choice]]
  ]


function sucess = menu(list)

    if numel(list) == 0
        sucess = '';
        return
    end

    while(true)

        disp('Please select one of these options:');

        for i = (1:numel(list))

            disp([num2str(i) ') ' list{i}]);

        end

        disp([num2str(numel(list)+1) ') exit']);

        try
            key = input(':: ');
            if key == numel(list)+1
                break
            elseif (key > numel(list)) || (key < 0)
                continue
            else
                disp(['-> ' list{key}]);
            end
        catch
            continue
        end

    end

    sucess = true;

end


(
  :prompt =list
  (list bool)
  (list (' dup append) map prompt choose)
  ("") if
) :menu

("fee fie" "huff and puff" "mirror mirror" "tick tock")
"Enter an option" menu
"You chose: " print! puts!


MODULE  Menu;

FROM    InOut IMPORT WriteString, WriteCard, WriteLn, ReadCard;

CONST   StringLength     = 100;
        MenuSize         = 4;

TYPE    String           = ARRAY[0..StringLength-1] OF CHAR;

VAR     menu             : ARRAY[0..MenuSize] OF String;
        selection, index : CARDINAL;

BEGIN
    menu[1] := "fee fie";
    menu[2] := "huff and puff";
    menu[3] := "mirror mirror";
    menu[4] := "tick tock";

    FOR index := 1 TO HIGH(menu) DO
        WriteString("[");
        WriteCard(    index,1);
        WriteString(        "] ");
        WriteString(            menu[index]);
        WriteLn;
    END;(*of FOR*)

    WriteString("Choose what you want : ");
    ReadCard(selection);

    IF (selection <= HIGH(menu)) AND (selection > 0) THEN
        WriteString("You have chosen: ");
        WriteString(                  menu[selection]);
        WriteLn;
    ELSE
        WriteString("Selection is out of range!");
        WriteLn;
    END (*of IF*)
END Menu.


MENU(STRINGS,SEP)
 ;http://rosettacode.org/wiki/Menu
 NEW I,A,MAX
 ;I is a loop variable
 ;A is the string read in from the user
 ;MAX is the number of substrings in the STRINGS list
 ;SET STRINGS="fee fie^huff and puff^mirror mirror^tick tock"
 SET MAX=$LENGTH(STRINGS,SEP)
 QUIT:MAX=0 ""
WRITEMENU
 FOR I=1:1:MAX WRITE I,": ",$PIECE(STRINGS,SEP,I),!
 READ:30 !,"Choose a string by its index: ",A,!
 IF (A<1)!(A>MAX)!(A\1'=A) GOTO WRITEMENU
 KILL I,MAX
 QUIT $PIECE(STRINGS,SEP,A)


def _menu($items)
	for ($i = 0) ($i < len($items)) ($i = $i + 1)
		println "  " + $i + ") " + $items[$i]
	end
end

def _ok($reply, $itemcount)
	try
		$n = int($reply)
		return (($n >= 0) && ($n < $itemcount))
	catch
		return $false
	end
end

def selector($items, $pmt)
	// Prompt to select an item from the items
	if (len($items) = 0)
		return ""
	end
	$reply = -1
	$itemcount = len($items)
	while !_ok($reply, $itemcount)
		_menu($items)
		println $pmt
		$reply = int(input())
	end
	return $items[$reply]
end

$items = list()
append $items "fee fie" "huff and puff" "mirror mirror" "tick tock"
$item = selector($items, "Which is from the three pigs: ")
println "You chose: " + $item


import strutils, rdstdin

proc menu(xs: openArray[string]) =
  for i, x in xs: echo "  ", i, ") ", x

proc ok(reply: string; count: Positive): bool =
  try:
    let n = parseInt(reply)
    return 0 <= n and n < count
  except: return false

proc selector(xs: openArray[string]; prompt: string): string =
  if xs.len == 0: return ""
  var reply = "-1"
  while not ok(reply, xs.len):
    menu(xs)
    reply = readLineFromStdin(prompt).strip()
  return xs[parseInt(reply)]

const xs = ["fee fie", "huff and puff", "mirror mirror", "tick tock"]
let item = selector(xs, "Which is from the three pigs: ")
echo "You chose: ", item


let select ?(prompt="Choice? ") = function
  | [] -> ""
  | choices ->
      let rec menu () =
        List.iteri (Printf.printf "%d: %s\n") choices;
        print_string prompt;
        try List.nth choices (read_int ())
        with _ -> menu ()
      in menu ()


FUNCTION bashMenu RETURNS CHAR(
   i_c AS CHAR
):

   DEF VAR ii        AS INT.
   DEF VAR hfr       AS HANDLE.
   DEF VAR hmenu     AS HANDLE EXTENT.
   DEF VAR ikey      AS INT.
   DEF VAR ireturn   AS INT INITIAL ?.

   EXTENT( hmenu ) = NUM-ENTRIES( i_c ).

   CREATE FRAME hfr ASSIGN
      WIDTH    =  80
      HEIGHT   =  NUM-ENTRIES( i_c )
      PARENT   =  CURRENT-WINDOW
      VISIBLE  =  TRUE
      .

   DO ii = 1 TO NUM-ENTRIES( i_c ):

      CREATE TEXT hmenu ASSIGN
         FRAME          =  hfr
         FORMAT         =  "x(79)"
         SCREEN-VALUE   =  SUBSTITUTE( "&1. &2", ii, ENTRY( ii, i_c ) )
         ROW            =  ii
         VISIBLE        =  TRUE
         .

   END.

   IF i_c = "" THEN
      ireturn = 1.

   DO WHILE ireturn = ?:

      READKEY.
      ikey = INTEGER( CHR( LASTKEY ) ) NO-ERROR.
      IF ikey >= 1 AND ikey <= NUM-ENTRIES( i_c ) THEN
         ireturn = ikey.

   END.

   RETURN ENTRY( ireturn, i_c ).

END FUNCTION.

MESSAGE
   bashMenu( "fee fie,huff and puff,mirror mirror,tick tock" )
VIEW-AS ALERT-BOX.


declare
  fun {Select Prompt Items}
     case Items of nil then ""
     else
	for
	   Item in Items
	   Index in 1..{Length Items}
	do
	   {System.showInfo Index#") "#Item}
	end
	{System.printInfo Prompt}
	try
	   {Nth Items {ReadInt}}
	catch _ then
	   {Select Prompt Items}
	end
     end
  end

  fun {ReadInt}
     class TextFile from Open.file Open.text end
     StdIo = {New TextFile init(name:stdin)}
  in
     {String.toInt {StdIo getS($)}}
  end

  Item = {Select "Which is from the three pigs: "
	  ["fee fie" "huff and puff" "mirror mirror" "tick tock"]}

in
  {System.showInfo "You chose: "#Item}


choose(v)=my(n);for(i=1,#v,print(i". "v[i]));while(type(n=input())!="t_INT"|n>#v|n<1,);v[n]
choose(["fee fie","huff and puff","mirror mirror","tick tock"])


program Menu;
{$ASSERTIONS ON}
uses
  objects;
var
  MenuItems :PUnSortedStrCollection;
  selected  :string;

Function SelectMenuItem(MenuItems :PUnSortedStrCollection):string;
var
  i, idx :integer;
  code   :word;
  choice :string;
begin
  // Return empty string if the collection is empty.
  if MenuItems^.Count = 0 then
  begin
    SelectMenuItem := '';
    Exit;
  end;

  repeat
    for i:=0 to MenuItems^.Count-1 do
    begin
      writeln(i+1:2, ') ', PString(MenuItems^.At(i))^);
    end;
    write('Make your choice: ');
    readln(choice);
    // Try to convert choice to an integer.
    // Code contains 0 if this was successful.
    val(choice, idx, code)
  until (code=0) and (idx>0) and (idx<=MenuItems^.Count);
  // Return the selected element.
  SelectMenuItem := PString(MenuItems^.At(idx-1))^;
end;

begin
  // Create an unsorted string collection for the menu items.
  MenuItems := new(PUnSortedStrCollection, Init(10, 10));

  // Add some menu items to the collection.
  MenuItems^.Insert(NewStr('fee fie'));
  MenuItems^.Insert(NewStr('huff and puff'));
  MenuItems^.Insert(NewStr('mirror mirror'));
  MenuItems^.Insert(NewStr('tick tock'));

  // Display the menu and get user input.
  selected := SelectMenuItem(MenuItems);
  writeln('You chose: ', selected);

  dispose(MenuItems, Done);

  // Test function with an empty collection.
  MenuItems := new(PUnSortedStrCollection, Init(10, 10));

  selected := SelectMenuItem(MenuItems);
  // Assert that the function returns an empty string.
  assert(selected = '', 'Assertion failed: the function did not return an empty string.');

  dispose(MenuItems, Done);
end.


sub menu
{
        my ($prompt,@array) = @_;
        return '' unless @array;

        print "  $_: $array[$_]\n" for(0..$#array);
        print $prompt;
        $n = <>;
        return $array[$n] if $n =~ /^\d+$/ and defined $array[$n];
        return &menu($prompt,@array);
}

@a = ('fee fie', 'huff and puff', 'mirror mirror', 'tick tock');
$prompt = 'Which is from the three pigs: ';

$a = &menu($prompt,@a);

print "You chose: $a\n";


function menu_select(sequence items, object prompt)
sequence res = ""
    items = remove_all("",items)
    if length(items)!=0 then
        while 1 do
            for i=1 to length(items) do
                printf(1,"%d) %s\n",{i,items[i]})
            end for
            puts(1,iff(atom(prompt)?"Choice?":prompt))
            res = scanf(trim(gets(0)),"%d")
            puts(1,"\n")
            if length(res)=1 then
                integer nres = res[1][1]
                if nres>0 and nres<=length(items) then
                    res = items[nres]
                    exit
                end if
            end if
        end while
    end if
    return res
end function

constant items = {"fee fie", "huff and puff", "mirror mirror", "tick tock"}
constant prompt = "Which is from the three pigs? "
string res = menu_select(items,prompt)
printf(1,"You chose %s.\n",{res})


<?php
$stdin = fopen("php://stdin", "r");
$allowed = array(1 => 'fee fie', 'huff and puff', 'mirror mirror', 'tick tock');

for(;;) {
    foreach ($allowed as $id => $name) {
        echo "  $id: $name\n";
    }
    echo "Which is from the four pigs: ";
    $stdin_string = fgets($stdin, 4096);
    if (isset($allowed[(int) $stdin_string])) {
        echo "You chose: {$allowed[(int) $stdin_string]}\n";
        break;
    }
}


(de choose (Prompt Items)
   (use N
      (loop
         (for (I . Item) Items
            (prinl I ": " Item) )
         (prin Prompt " ")
         (flush)
         (NIL (setq N (in NIL (read))))
         (T (>= (length Items) N 1) (prinl (get Items N))) ) ) )
(choose "Which is from the three pigs?"
   '("fee fie" "huff and puff" "mirror mirror" "tick tock") )


test: proc options (main);

declare menu(4) character(100) varying static initial (
   'fee fie', 'huff and puff', 'mirror mirror', 'tick tock');
declare (i, k) fixed binary;

do i = lbound(menu,1) to hbound(menu,1);
   put skip edit (trim(i), ': ', menu(i) ) (a);
end;
put skip list ('please choose an item number');
get list (k);
if k >= lbound(menu,1) & k <= hbound(menu,1) then
   put skip edit ('you chose ', menu(k)) (a);
else
   put skip list ('Could not find your phrase');

end test;


function Select-TextItem
{
  <#
    .SYNOPSIS
        Prints a textual menu formatted as an index value followed by its corresponding string for each object in the list.
    .DESCRIPTION
        Prints a textual menu formatted as an index value followed by its corresponding string for each object in the list;
        Prompts the user to enter a number;
        Returns an object corresponding to the selected index number.
    .PARAMETER InputObject
        An array of objects.
    .PARAMETER Prompt
        The menu prompt string.
    .EXAMPLE
        “fee fie”, “huff and puff”, “mirror mirror”, “tick tock” | Select-TextItem
    .EXAMPLE
        “huff and puff”, “fee fie”, “tick tock”, “mirror mirror” | Sort-Object | Select-TextItem -Prompt "Select a string"
    .EXAMPLE
        Select-TextItem -InputObject (Get-Process)
    .EXAMPLE
        (Get-Process | Where-Object {$_.Name -match "notepad"}) | Select-TextItem -Prompt "Select a Process" | Stop-Process -ErrorAction SilentlyContinue
  #>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true)]
        $InputObject,

        [Parameter(Mandatory=$false)]
        [string]
        $Prompt = "Enter Selection"
    )

    Begin
    {
        $menuOptions = @()
    }
    Process
    {
        $menuOptions += $InputObject
    }
    End
    {
        if(!$inputObject){
            return ""
        }
        do
        {
            [int]$optionNumber = 1

            foreach ($option in $menuOptions)
            {
                Write-Host ("{0,3}: {1}" -f $optionNumber,$option)

                $optionNumber++
            }

            Write-Host ("{0,3}: {1}" -f 0,"To cancel")

            $choice = Read-Host $Prompt

            $selectedValue = ""

            if ($choice -gt 0 -and $choice -le $menuOptions.Count)
            {
                $selectedValue = $menuOptions[$choice - 1]
            }

        }
        until ($choice -match "^[0-9]+$" -and ($choice -eq 0 -or $choice -le $menuOptions.Count))

        return $selectedValue
    }
}

“fee fie”, “huff and puff”, “mirror mirror”, “tick tock” | Select-TextItem -Prompt "Select a string"


:a
printline ==========MENU==========
printline 1. Fee Fie
printline 2. Huff Puff
printline 3. Mirror, Mirror
printline 4. Tick, Tock
editvar /newvar /value=a /userinput=1 /title=What page do you want to go to?
if -a- /hasvalue 1 printline You chose a line from the book Jack and the Beanstalk. & exitcurrentprogram 1
if -a- /hasvalue 2 printline You chose a line from the book The Three Little Pigs. & exitcurrentprogram 1
if -a- /hasvalue 3 printline You chose a line from the book Snow White. & exitcurrentprogram 1
if -a- /hasvalue 4 printline You chose a line from the book Beauty and the Beast. & exitcurrentprogram 1
printline You either chose an invalid choice or didn't chose.
editvar /newvar /value=goback /userinput=1 /title=Do you want to chose something else?
if -goback- /hasvalue y goto :a else exitcurrentprogram 1


rosetta_menu([], "") :- !.              %% Incase of an empty list.
rosetta_menu(Items, SelectedItem) :-
    repeat,                             %% Repeat until everything that follows is true.
        display_menu(Items),            %% IO
        get_choice(Choice),             %% IO
    number(Choice),                     %% True if Choice is a number.
    nth1(Choice, Items, SelectedItem),  %% True if SelectedItem is the 1-based nth member of Items, (fails if Choice is out of range)
    !.

display_menu(Items) :-
    nl,
    foreach( nth1(Index, Items, Item),
             format('~w) ~s~n', [Index, Item]) ).

get_choice(Choice) :-
    prompt1('Select a menu item by number:'),
    read(Choice).


If OpenConsole()
  Define i, txt$, choice
  Dim txts.s(4)
  EnableGraphicalConsole(1)  ;- Enable graphical mode in the console
  Repeat
    ClearConsole()
    Restore TheStrings       ; Set reads address
    For i=1 To 4
      Read.s  txt$
      txts(i)=txt$
      ConsoleLocate(3,i): Print(Str(i)+": "+txt$)
    Next
    ConsoleLocate(3,6): Print("Your choice? ")
    choice=Val(Input())
  Until choice>=1 And choice<=4
  ClearConsole()
  ConsoleLocate(3,2): Print("You chose: "+txts(choice))
  ;
  ;-Now, wait for the user before ending to allow a nice presentation
  ConsoleLocate(3,5): Print("Press ENTER to quit"): Input()
EndIf
End

DataSection
  TheStrings:
  Data.s  "fee fie", "huff And puff", "mirror mirror", "tick tock"
EndDataSection


def _menu(items):
    for indexitem in enumerate(items):
        print ("  %2i) %s" % indexitem)

def _ok(reply, itemcount):
    try:
        n = int(reply)
        return 0 <= n < itemcount
    except:
        return False

def selector(items, prompt):
    'Prompt to select an item from the items'
    if not items: return ''
    reply = -1
    itemcount = len(items)
    while not _ok(reply, itemcount):
        _menu(items)
        # Use input instead of raw_input for Python 3.x
        reply = raw_input(prompt).strip()
    return items[int(reply)]

if __name__ == '__main__':
    items = ['fee fie', 'huff and puff', 'mirror mirror', 'tick tock']
    item = selector(items, 'Which is from the three pigs: ')
    print ("You chose: " + item)


showmenu <- function(choices = NULL)
{
   if (is.null(choices)) return("")
   ans <- menu(choices)
   if(ans==0) "" else choices[ans]

}
str <- showmenu(c("fee fie", "huff and puff", "mirror mirror", "tick tock"))
str <- showmenu()


#lang racket

(define (menu choices)
  (cond [(null? choices) ""]
        [else (for ([c choices] [i (in-naturals 1)]) (printf "~a. ~a\n" i c))
              (printf "Enter a number: ")
              (define n (string->number (read-line)))
              (or (and (exact-integer? n)
                       (<= 1 n (length choices))
                       (list-ref choices (sub1 n)))
                  (menu choices))]))

(menu '("fee fie" "huff and puff" "mirror mirror" "tick tock"))


sub menu ( $prompt, @items ) {
    return '' unless @items.elems;
    repeat until my $selection ~~ /^ \d+ $/ && @items[--$selection] {
        my $i = 1;
        say "  {$i++}) $_" for @items;
        $selection = prompt $prompt;
    }
    return @items[$selection];
}

my @choices = 'fee fie', 'huff and puff', 'mirror mirror', 'tick tock';
my $prompt = 'Enter the number corresponding to your selection: ';

my $answer = menu( $prompt, [] );
say "You chose: $answer" if $answer.chars;

$answer = menu( $prompt, @choices );
say "You chose: $answer" if $answer.chars;


rebol [
	Title: "Text Menu"
	URL: http://rosettacode.org/wiki/Select
]

choices: ["fee fie" "huff and puff" "mirror mirror" "tick tock"]
choice: ""

valid?: func [
	choices [block! list! series!]
	choice
][
	if error? try [choice: to-integer choice] [return false]
	all [0 < choice  choice <= length? choices]
]

while [not valid? choices choice][
	repeat i length? choices [print ["  " i ":" choices/:i]]
	choice: ask "Which is from the three pigs? "
]
print ["You chose:" pick choices to-integer choice]


Red ["text menu"]

menu: function [items][
	print either empty? items [""] [until [
		repeat n length? items [print [n ":" items/:n]]
		attempt [pick items to-integer ask "Your choice: "]
	]]
]


/*REXX program displays a list,  then prompts the user for a selection number (integer).*/
        do forever                               /*keep prompting until response is OK. */
        call list_create                         /*create the list from scratch.        */
        call list_show                           /*display (show)  the list to the user.*/
        if #==0   then return ''                 /*if list is empty,  then return  null.*/
        say right(' choose an item by entering a number from 1 ───►' #, 70, '═')
        parse pull x                             /*get the user's choice  (if any).     */

              select
              when x=''              then call sayErr  "a choice wasn't entered"
              when words(x)\==1      then call sayErr  'too many choices entered:'
              when \datatype(x,'N')  then call sayErr  "the choice isn't numeric:"
              when \datatype(x,'W')  then call sayErr  "the choice isn't an integer:"
              when x<1 | x>#         then call sayErr  "the choice isn't within range:"
              otherwise              leave       /*this leaves the    DO FOREVER   loop.*/
              end   /*select*/
        end         /*forever*/
                                                 /*user might've entered   2.  or  003  */
x=x/1                                            /*normalize the number (maybe).        */
say;  say 'you chose item' x": " #.x
return #.x                                       /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
list_create:   #.1= 'fee fie'                    /*this is one method for list-building.*/
               #.2= 'huff and puff'
               #.3= 'mirror mirror'
               #.4= 'tick tock'
               #=4                               /*store the number of choices in   #   */
               return                            /*(above)  is just one convention.     */
/*──────────────────────────────────────────────────────────────────────────────────────*/
list_show:     say                               /*display a blank line.                */
                      do j=1  for #              /*display the list of choices.         */
                      say '[item'   j"]   " #.j  /*display item number with its choice. */
                      end   /*j*/
               say                               /*display another blank line.          */
               return
/*──────────────────────────────────────────────────────────────────────────────────────*/
sayErr:        say;         say  '***error***'  arg(1)  x;          say;            return


aList = ["fee fie", "huff and puff", "mirror mirror", "tick tock"]
selected = menu(aList, "please make a selection: ")
see "" + selected + nl

func menu aList, prompt
     ndex = 1
     while index>0 and index<=len(aList)
           for index = 1 to len(aList)
               if aList[index]!="" see "" + index + " : " + aList[index] + " " ok
           next
           see nl
           see prompt
           give select
           index = number(select)
           see "" + aList[index] + nl
           if select!=string(index) index = -1 ok
           if index>=0 if index<=len(aList) if aList[index]="" index = -1 ok ok ok
     end
     return aList[index]


def select(prompt, items = [])
  if items.empty?
    ''
  else
    answer = -1
    until (0...items.length).cover?(answer)
      items.each_with_index {|i,j| puts "#{j}. #{i}"}
      print "#{prompt}: "
      begin
        answer = Integer(gets)
      rescue ArgumentError
        redo
      end
    end
    items[answer]
  end
end

# test empty list
response = select('Which is empty')
puts "empty list returns: >#{response}<\n"

# "real" test
items = ['fee fie', 'huff and puff', 'mirror mirror', 'tick tock']
response = select('Which is from the three pigs', items)
puts "you chose: >#{response}<"


dim choose$(5)
choose$(1) = "1 Fee Fie"
choose$(2) = "2 Huff Puff"
choose$(3) = "3 Mirror, Mirror"
choose$(4) = "4 Tick, Tock"
choose$(5) = "Exit"

[start]
print "Menu Selection"
listbox #lb,choose$(),5
button #sel, "Accept",[select]
wait

[select]
selected$=#lb selection$()
print " "
if selected$<>"" then
  print "You selected ";selected$
 else
  print "No selection made"
end if
 button #con, "Continue",[go2]
wait
[go2]
if selected$<>"Exit" then
  cls
  goto [start]
 else
  cls
  end
end if


fn menu_select<'a>(items: &'a [&'a str]) -> &'a str {
    if items.len() == 0 {
        return "";
    }

    let stdin = std::io::stdin();
    let mut buffer = String::new();

    loop {
        for (i, item) in items.iter().enumerate() {
            println!("{}) {}", i + 1, item);
        }
        print!("Pick a number from 1 to {}: ", items.len());

        // Read the user input:
        stdin.read_line(&mut buffer).unwrap();
        println!();

        if let Ok(selected_index) = buffer.trim().parse::<usize>() {
            if 0 < selected_index {
                if let Some(selected_item) = items.get(selected_index - 1) {
                    return selected_item;
                }
            }
        }

        // The buffer will contain the old input, so we need to clear it before we can reuse it.
        buffer.clear();
    }
}

fn main() {
    // Empty list:
    let selection = menu_select(&[]);
    println!("No choice: {:?}", selection);

    // List with items:
    let items = [
        "fee fie",
        "huff and puff",
        "mirror mirror",
        "tick tock",
    ];

    let selection = menu_select(&items);
    println!("You chose: {}", selection);
}


import scala.util.Try

object Menu extends App {
  val choice = menu(list)

  def menu(menuList: Seq[String]): String = {
    if (menuList.isEmpty) "" else {
      val n = menuList.size

      def getChoice: Try[Int] = {
        println("\n   M E N U\n")
        menuList.zipWithIndex.map { case (text, index) => s"${index + 1}: $text" }.foreach(println(_))
        print(s"\nEnter your choice 1 - $n : ")
        Try {
          io.StdIn.readInt()
        }
      }

      menuList(Iterator.continually(getChoice)
        .dropWhile(p => p.isFailure || !(1 to n).contains(p.get))
        .next.get - 1)
    }
  }

  def list = Seq("fee fie", "huff and puff", "mirror mirror", "tick tock")

  println(s"\nYou chose : $choice")
}


$ include "seed7_05.s7i";

const func string: menuSelect (in array string: items, in string: prompt) is func
  result
    var string: selection is "";
  local
    var string: item is "";
    var integer: index is 0;
    var integer: num is 0;
  begin
    if length(items) <> 0 then
      repeat
        for item key index range items do
          writeln(index <& ". " <& item);
        end for;
        write(prompt);
        readln(num);
      until num >= 1 and num <= length(items);
      selection := items[num];
    end if
  end func;

const array string: items is [] ("fee fie", "huff and puff", "mirror mirror", "tick tock");
const string: prompt is "Which is from the three pigs? ";

const proc: main is func
  begin
    writeln("You chose " <& menuSelect(items, prompt));
  end func;


func menu (prompt, arr) {
    arr.len > 0 || return ''
    loop {
        for i in ^arr {
            say "  #{i}: #{arr[i]}"
        }
        var n = Sys.scanln(prompt) \\ return()
        n ~~ /^[0-9]+\z/ ? Num(n) : next
        arr.exists(n) && return arr[n]
    }
}

var list = ['fee fie', 'huff and puff', 'mirror mirror', 'tick tock']
var prompt = 'Please choose an item number: '

var answer = menu(prompt, list)
say "You choose: #{answer}"


func getMenuInput(selections: [String]) -> String {
  guard !selections.isEmpty else {
    return ""
  }

  func printMenu() {
    for (i, str) in selections.enumerated() {
      print("\(i + 1)) \(str)")
    }

    print("Selection: ", terminator: "")
  }

  while true {
    printMenu()

    guard let input = readLine(strippingNewline: true), !input.isEmpty else {
      return ""
    }

    guard let n = Int(input), n > 0, n <= selections.count else {
      continue
    }

    return selections[n - 1]
  }
}

let selected = getMenuInput(selections: [
  "fee fie",
  "huff and puff",
  "mirror mirror",
  "tick tock"
])

print("You chose: \(selected)")


proc select {prompt choices} {
    set nc [llength $choices]
    if {!$nc} {
	return ""
    }
    set numWidth [string length $nc]
    while true {
	set i 0
	foreach s $choices {
	    puts [format "  %-*d: %s" $numWidth [incr i] $s]
	}
	puts -nonewline "$prompt: "
	flush stdout
	gets stdin num
	if {[string is int -strict $num] && $num >= 1 && $num <= $nc} {
	    incr num -1
	    return [lindex $choices $num]
	}
    }
}


Input "",Str1 //input as ITEM 1:ITEM 2:ITEM 3...
":"+Str1+":→Str1
Σ(sub(Str1,X,1)=":",X,1,length(Str1→X
0→dim(L₁
For(Z,2,length(Str1
inString(Str1,":",Z-1
1+Ans+.01inString(Str1,":",Ans+1→L₁(1+dim(L₁
ᴇ2fPart(Ans→Z
End
seq(iPart(L₁(X))+.01(ᴇ2fPart(L₁(X))-iPart(L₁(X))),X,1,dim(L₁→L₁
Repeat A>0 and A<X
ClrHome
For(Z,1,dim(L₁
Disp "  :"+sub(Str1,iPart(L₁(Z)),ᴇ2fPart(L₁(Z
Output(min(7,Z),1+(Z≤7),Z
End
Input A
End
Disp sub(Str1,iPart(L₁(A)),ᴇ2fPart(L₁(A


DIM menu$(4)
MAT READ menu$
DATA "fee fie", "huff and puff", "mirror mirror", "tick tock"

FUNCTION sel$(choices$())
    IF UBOUND(choices$) - LBOUND(choices$) = 0 THEN LET sel$ = ""
    LET ret$ = ""
    DO
       FOR i = LBOUND(choices$) TO UBOUND(choices$)
           PRINT i; ": "; choices$(i)
       NEXT i
       PRINT prompt$;
       INPUT index
       IF index <= UBOUND(choices$) AND index >= LBOUND(choices$) THEN LET ret$ = choices$(index)
    LOOP WHILE ret$ = ""
    LET sel$ = ret$
END FUNCTION

PRINT sel$(menu$())
END


# choose 'choice 1' 'choice 2' ...
#   Prints menu to standard error. Prompts with PS3.
#   Reads REPLY from standard input. Sets CHOICE.
choose() {
    CHOICE=                     # Default CHOICE is empty string.
    [[ $# -gt 0 ]] || return    # Return if "[email protected]" is empty.
    select CHOICE; do           # Select from "[email protected]".
        if [[ -n $CHOICE ]]; then
            break
        else
            echo Invalid choice.
        fi
    done
}

PS3='Which is from the three pigs: '
choose 'fee fie' 'huff and puff' 'mirror mirror' 'tick tock'
[[ -n $CHOICE ]] && echo You chose: $CHOICE
[[ -z $CHOICE ]] && echo No input.


def _menu (string<> items)
	for (decl int i) (< i (size items)) (inc i)
		out "  " i ") " items<i> endl console
	end for
end _menu

def _ok (string reply, int itemcount)
	try
		decl int n
		set n (int reply)
		return (and (or (> n 0) (= n 0)) (< n itemcount))
	catch
		return false
	end try
end _ok

def selector (string<> items, string prompt)
	# Prompt to select an item from the items
	if (= (size items) 0)
		return ""
	end if
	decl int itemcount reply
	set reply -1
	set itemcount (size items)
	while (not (_ok reply itemcount))
		_menu items
		out prompt console
		set reply (in int console)
	end while
	return items<(int reply)>
end selector

decl string<> items
append "fee fie" "huff and puff" "mirror mirror" "tick tock" items
decl string item
set item (selector items "Which is from the three pigs: ")
out "You chose: " item endl console


'The Function
Function Menu(ArrList, Prompt)
    Select Case False   'Non-standard usage hahaha
        Case IsArray(ArrList)
           Menu = ""   'Empty output string for non-arrays
           Exit Function
        Case UBound(ArrList) >= LBound(ArrList)
           Menu = ""   'Empty output string for empty arrays
           Exit Function
    End Select
    'Display menu and prompt
    Do While True
        For i = LBound(ArrList) To UBound(ArrList)
            WScript.StdOut.WriteLine((i + 1) & ". " & ArrList(i))
        Next
        WScript.StdOut.Write(Prompt)
        Choice = WScript.StdIn.ReadLine
        'Check if input is valid
        If IsNumeric(Choice) Then   'Check for numeric-ness
            If CStr(CLng(Choice)) = Choice Then   'Check for integer-ness (no decimal part)
                Index = Choice - 1   'Arrays are 0-based
                'Check if Index is in array
                If LBound(ArrList) <= Index And Index <= UBound(ArrList) Then
                    Exit Do
                End If
            End If
        End If
        WScript.StdOut.WriteLine("Invalid choice.")
    Loop
    Menu = ArrList(Index)
End Function

'The Main Thing
Sample = Array("fee fie", "huff and puff", "mirror mirror", "tick tock")
InputText = "Which is from the three pigs: "
WScript.StdOut.WriteLine("Output: " & Menu(Sample, InputText))


import "/ioutil" for Input

var menu = Fn.new { |list|
    var n = list.count
    if (n == 0) return ""
    var prompt = "\n   M E N U\n\n"
    for (i in 0...n) prompt = prompt + "%(i+1). %(list[i])\n"
    prompt = prompt + "\nEnter your choice (1 - %(n)): "
    var index = Input.integer(prompt, 1, n)
    return list[index-1]
}

var list = ["fee fie", "huff and puff", "mirror mirror", "tick tock"]
var choice = menu.call(list)
System.print("\nYou chose : %(choice)")


string 0;

func Menu(List);
int  List;
int  Size, I, C;
[Size:= List(0);
if Size < 1 then return List(0); \if empty list, return pointer to 0
for I:= 1 to Size-1 do
        [IntOut(0, I);  Text(0, ": ");
        Text(0, List(I));  CrLf(0);
        ];
CrLf(0);
Text(0, List(Size));            \display prompt
loop    [C:= ChIn(0);           \buffered keyboard requires Enter key
        if C>=^1 & C<=Size-1+^0 then return List(C-^0);
        Text(0, "Please enter 1 thru ");  IntOut(0, Size-1);
        Text(0, ": ");
        ];
];

Text(0, Menu([5, "fee fie", "huff and puff", "mirror mirror", "tick tock",
                "Which phrase is from the Three Little Pigs? "]))


// Rosetta Code problem: https://www.rosettacode.org/wiki/Menu
// by Jjuanhdez, 06/2022

dim choose$(5)
restore menudata
for a = 0 to 5 : read choose$(a) : next a

print menu$(choose$())
end

sub menu$(m$())
    clear screen
    repeat
        print color("green","black") at(0,0) "Menu selection"
        vc = 0
        b = arraysize(m$(),1)
        while vc < 1 or vc > b
            for i = 1 to b-1
                print i, "  ", choose$(i)
            next i
            print choose$(b)
            print

            input "Your choice: " c
            print at(0,7) "Your choice:      "
            if c > 0 and c < 6 then
                vc = c
                print color("yellow","black") at(0,8) choose$(vc)
            else
                print color("red","black") at(0,8) choose$(0)
                break
            fi
        wend
    until vc = 5
end sub

label menudata
data "Ack, not good", "fee fie      ", "huff and puff"
data "mirror mirror", "tick tock    ", "exit         "


fcn teleprompter(options){
   os:=T("exit").extend(vm.arglist); N:=os.len();
   if(N==1) return("");
   while(1){
      Utils.zipWith(fcn(n,o){"%d) %s".fmt(n,o).println()},[0..],os);
      a:=ask("Your choice: ");
      try{ n:=a.toInt(); if(0<=n<N) return(os[n]); } catch{}
      println("Ack, not good");
   }
}

teleprompter("fee fie" , "huff and puff" , "mirror mirror" , "tick tock")
.println();


with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;

procedure Minesweeper is
   package IO renames Ada.Text_IO;
   package Nat_IO is new IO.Integer_IO (Natural);
   package Nat_RNG is new Ada.Numerics.Discrete_Random (Natural);

   type Stuff is (Empty, Mine);
   type Field is record
      Contents : Stuff   := Empty;
      Opened   : Boolean := False;
      Marked   : Boolean := False;
   end record;
   type Grid is array (Positive range <>, Positive range <>) of Field;

   -- counts how many mines are in the surrounding fields
   function Mines_Nearby (Item : Grid; X, Y : Positive) return Natural is
      Result : Natural := 0;
   begin
      -- left of X:Y
      if X > Item'First (1) then
         -- above X-1:Y
         if Y > Item'First (2) then
            if Item (X - 1, Y - 1).Contents = Mine then
               Result := Result + 1;
            end if;
         end if;
         -- X-1:Y
         if Item (X - 1, Y).Contents = Mine then
            Result := Result + 1;
         end if;
         -- below X-1:Y
         if Y < Item'Last (2) then
            if Item (X - 1, Y + 1).Contents = Mine then
               Result := Result + 1;
            end if;
         end if;
      end if;
      -- above of X:Y
      if Y > Item'First (2) then
         if Item (X, Y - 1).Contents = Mine then
            Result := Result + 1;
         end if;
      end if;
      -- below of X:Y
      if Y < Item'Last (2) then
         if Item (X, Y + 1).Contents = Mine then
            Result := Result + 1;
         end if;
      end if;
      -- right of X:Y
      if X < Item'Last (1) then
         -- above X+1:Y
         if Y > Item'First (2) then
            if Item (X + 1, Y - 1).Contents = Mine then
               Result := Result + 1;
            end if;
         end if;
         -- X+1:Y
         if Item (X + 1, Y).Contents = Mine then
            Result := Result + 1;
         end if;
         -- below X+1:Y
         if Y < Item'Last (2) then
            if Item (X + 1, Y + 1).Contents = Mine then
               Result := Result + 1;
            end if;
         end if;
      end if;
      return Result;
   end Mines_Nearby;

   -- outputs the grid
   procedure Put (Item : Grid) is
      Mines : Natural := 0;
   begin
      IO.Put ("   ");
      for X in Item'Range (1) loop
         Nat_IO.Put (Item => X, Width => 3);
      end loop;
      IO.New_Line;
      IO.Put ("   +");
      for X in Item'Range (1) loop
         IO.Put ("---");
      end loop;
      IO.Put ('+');
      IO.New_Line;
      for Y in Item'Range (2) loop
         Nat_IO.Put (Item => Y, Width => 3);
         IO.Put ('|');
         for X in Item'Range (1) loop
            if Item (X, Y).Opened then
               if Item (X, Y).Contents = Empty then
                  if Item (X, Y).Marked then
                     IO.Put (" - ");
                  else
                     Mines := Mines_Nearby (Item, X, Y);
                     if Mines > 0 then
                        Nat_IO.Put (Item => Mines, Width => 2);
                        IO.Put (' ');
                     else
                        IO.Put ("   ");
                     end if;
                  end if;
               else
                  if Item (X, Y).Marked then
                     IO.Put (" + ");
                  else
                     IO.Put (" X ");
                  end if;
               end if;
            elsif Item (X, Y).Marked then
               IO.Put (" ? ");
            else
               IO.Put (" . ");
            end if;
         end loop;
         IO.Put ('|');
         IO.New_Line;
      end loop;
      IO.Put ("   +");
      for X in Item'Range (1) loop
         IO.Put ("---");
      end loop;
      IO.Put ('+');
      IO.New_Line;
   end Put;

   -- marks a field as possible bomb
   procedure Mark (Item : in out Grid; X, Y : in Positive) is
   begin
      if Item (X, Y).Opened then
         IO.Put_Line ("Field already open!");
      else
         Item (X, Y).Marked := not Item (X, Y).Marked;
      end if;
   end Mark;

   -- clears a field and it's neighbours, if they don't have mines
   procedure Clear
     (Item   : in out Grid;
      X, Y   : in Positive;
      Killed : out Boolean)
   is
      -- clears the neighbours, if they don't have mines
      procedure Clear_Neighbours (The_X, The_Y : Positive) is
      begin
         -- mark current field opened
         Item (The_X, The_Y).Opened := True;
         -- only proceed if neighbours don't have mines
         if Mines_Nearby (Item, The_X, The_Y) = 0 then
            -- left of X:Y
            if The_X > Item'First (1) then
               -- above X-1:Y
               if The_Y > Item'First (2) then
                  if not Item (The_X - 1, The_Y - 1).Opened and
                     not Item (The_X - 1, The_Y - 1).Marked
                  then
                     Clear_Neighbours (The_X - 1, The_Y - 1);
                  end if;
               end if;
               -- X-1:Y
               if not Item (The_X - 1, The_Y).Opened and
                  not Item (The_X - 1, The_Y).Marked
               then
                  Clear_Neighbours (The_X - 1, The_Y);
               end if;
               -- below X-1:Y
               if The_Y < Item'Last (2) then
                  if not Item (The_X - 1, The_Y + 1).Opened and
                     not Item (The_X - 1, The_Y + 1).Marked
                  then
                     Clear_Neighbours (The_X - 1, The_Y + 1);
                  end if;
               end if;
            end if;
            -- above X:Y
            if The_Y > Item'First (2) then
               if not Item (The_X, The_Y - 1).Opened and
                  not Item (The_X, The_Y - 1).Marked
               then
                  Clear_Neighbours (The_X, The_Y - 1);
               end if;
            end if;
            -- below X:Y
            if The_Y < Item'Last (2) then
               if not Item (The_X, The_Y + 1).Opened and
                  not Item (The_X, The_Y + 1).Marked
               then
                  Clear_Neighbours (The_X, The_Y + 1);
               end if;
            end if;
            -- right of X:Y
            if The_X < Item'Last (1) then
               -- above X+1:Y
               if The_Y > Item'First (2) then
                  if not Item (The_X + 1, The_Y - 1).Opened and
                     not Item (The_X + 1, The_Y - 1).Marked
                  then
                     Clear_Neighbours (The_X + 1, The_Y - 1);
                  end if;
               end if;
               -- X+1:Y
               if not Item (The_X + 1, The_Y).Opened and
                  not Item (The_X + 1, The_Y).Marked
               then
                  Clear_Neighbours (The_X + 1, The_Y);
               end if;
               -- below X+1:Y
               if The_Y < Item'Last (2) then
                  if not Item (The_X + 1, The_Y + 1).Opened and
                     not Item (The_X + 1, The_Y + 1).Marked
                  then
                     Clear_Neighbours (The_X + 1, The_Y + 1);
                  end if;
               end if;
            end if;
         end if;
      end Clear_Neighbours;
   begin
      Killed := False;
      -- only clear closed and unmarked fields
      if Item (X, Y).Opened then
         IO.Put_Line ("Field already open!");
      elsif Item (X, Y).Marked then
         IO.Put_Line ("Field already marked!");
      else
         Killed := Item (X, Y).Contents = Mine;
         -- game over if killed, no need to clear
         if not Killed then
            Clear_Neighbours (X, Y);
         end if;
      end if;
   end Clear;

   -- marks all fields as open
   procedure Open_All (Item : in out Grid) is
   begin
      for X in Item'Range (1) loop
         for Y in Item'Range (2) loop
            Item (X, Y).Opened := True;
         end loop;
      end loop;
   end Open_All;

   -- counts the number of marks
   function Count_Marks (Item : Grid) return Natural is
      Result : Natural := 0;
   begin
      for X in Item'Range (1) loop
         for Y in Item'Range (2) loop
            if Item (X, Y).Marked then
               Result := Result + 1;
            end if;
         end loop;
      end loop;
      return Result;
   end Count_Marks;

   -- read and validate user input
   procedure Get_Coordinates
     (Max_X, Max_Y : Positive;
      X, Y         : out Positive;
      Valid        : out Boolean)
   is
   begin
      Valid := False;
      IO.Put ("X: ");
      Nat_IO.Get (X);
      IO.Put ("Y: ");
      Nat_IO.Get (Y);
      Valid := X > 0 and X <= Max_X and Y > 0 and Y <= Max_Y;
   exception
      when Constraint_Error =>
         Valid := False;
   end Get_Coordinates;

   -- randomly place bombs
   procedure Set_Bombs (Item : in out Grid; Max_X, Max_Y, Count : Positive) is
      Generator : Nat_RNG.Generator;
      X, Y      : Positive;
   begin
      Nat_RNG.Reset (Generator);
      for I in 1 .. Count loop
         Placement : loop
            X := Nat_RNG.Random (Generator) mod Max_X + 1;
            Y := Nat_RNG.Random (Generator) mod Max_Y + 1;
            -- redo placement if X:Y already full
            if Item (X, Y).Contents = Empty then
               Item (X, Y).Contents := Mine;
               exit Placement;
            end if;
         end loop Placement;
      end loop;
   end Set_Bombs;

   Width, Height : Positive;

begin
   -- can be dynamically set
   Width  := 6;
   Height := 4;
   declare
      The_Grid : Grid (1 .. Width, 1 .. Height);
      -- 20% bombs
      Bomb_Count         : Positive := Width * Height * 20 / 100;
      Finished           : Boolean  := False;
      Action             : Character;
      Chosen_X, Chosen_Y : Positive;
      Valid_Entry        : Boolean;
   begin
      IO.Put ("Nr. Bombs: ");
      Nat_IO.Put (Item => Bomb_Count, Width => 0);
      IO.New_Line;
      Set_Bombs
        (Item  => The_Grid,
         Max_X => Width,
         Max_Y => Height,
         Count => Bomb_Count);
      while not Finished and Count_Marks (The_Grid) /= Bomb_Count loop
         Put (The_Grid);
         IO.Put ("Input (c/m/r): ");
         IO.Get (Action);
         case Action is
            when 'c' | 'C' =>
               Get_Coordinates
                 (Max_X => Width,
                  Max_Y => Height,
                  X     => Chosen_X,
                  Y     => Chosen_Y,
                  Valid => Valid_Entry);
               if Valid_Entry then
                  Clear
                    (Item   => The_Grid,
                     X      => Chosen_X,
                     Y      => Chosen_Y,
                     Killed => Finished);
                  if Finished then
                     IO.Put_Line ("You stepped on a mine!");
                  end if;
               else
                  IO.Put_Line ("Invalid input, retry!");
               end if;
            when 'm' | 'M' =>
               Get_Coordinates
                 (Max_X => Width,
                  Max_Y => Height,
                  X     => Chosen_X,
                  Y     => Chosen_Y,
                  Valid => Valid_Entry);
               if Valid_Entry then
                  Mark (Item => The_Grid, X => Chosen_X, Y => Chosen_Y);
               else
                  IO.Put_Line ("Invalid input, retry!");
               end if;
            when 'r' | 'R' =>
               Finished := True;
            when others =>
               IO.Put_Line ("Invalid input, retry!");
         end case;
      end loop;
      Open_All (The_Grid);
      IO.Put_Line
        ("Solution: (+ = correctly marked, - = incorrectly marked)");
      Put (The_Grid);
   end;
end Minesweeper;


; Minesweeper.ahk - v1.0.6
; (c) Dec 28, 2008 by derRaphael
; Licensed under the Terms of EUPL 1.0
; Modded by Sobriquet and re-licenced as CeCILL v2
; Modded (again) by Sobriquet and re-licenced as GPL v1.3

#NoEnv
#NoTrayIcon
SetBatchLines,-1
#SingleInstance,Off
/*
[InGameSettings]
*/
Level	=Beginner
Width	=9
Height	=9
MineMax	=10
Marks	=1
Color	=1
Sound	=0
BestTimeBeginner	=999 seconds	Anonymous
BestTimeIntermediate=999 seconds	Anonymous
BestTimeExpert		=999 seconds	Anonymous
; above settings are accessed as variables AND modified by IniWrite - be careful
BlockSize	=16
Title		=Minesweeper
MineCount	=0
mColor		=Blue,Green,Red,Purple,Navy,Olive,Maroon,Teal
GameOver	=0
TimePassed	=0

; Add mines randomly
While (MineCount<MineMax)									; loop as long as neccessary
{
	Random,x,1,%Width%										; get random horizontal position
	Random,y,1,%Height%										; get random vertical position
	If (T_x%x%y%y%!="M")									; only if not already a mine
		T_x%x%y%y%:="M"									; assign as mine
		,MineCount++										; keep count
}

Gui +OwnDialogs
;Menu,Tray,Icon,C:\WINDOWS\system32\winmine.exe,1
Menu,GameMenu,Add,&New	F2,NewGame
Menu,GameMenu,Add
Menu,GameMenu,Add,&Beginner,LevelMenu
Menu,GameMenu,Add,&Intermediate,LevelMenu
Menu,GameMenu,Add,&Expert,LevelMenu
Menu,GameMenu,Add,&Custom...,CustomMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,&Marks (?),ToggleMenu
Menu,GameMenu,Add,Co&lor,ToggleMenu
Menu,GameMenu,Add,&Sound,ToggleMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,Best &Times...,BestTimesMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,E&xit,GuiClose
Menu,GameMenu,Check,% "&" level . (level="Custom" ? "..." : "")
If (Marks)
	Menu,GameMenu,Check,&Marks (?)
If (Color)
	Menu,GameMenu,Check,Co&lor
If (Sound)
	Menu,GameMenu,Check,&Sound

Menu,HelpMenu,Add,&Contents	F1,HelpMenu
Menu,HelpMenu,Add,&Search for Help on...,HelpMenu
Menu,HelpMenu,Add,Using &Help,HelpMenu
Menu,HelpMenu,Add
Menu,HelpMenu,Add,&About Minesweeper...,AboutMenu

Menu,MainMenu,Add,&Game,:GameMenu
Menu,MainMenu,Add,&Help,:HelpMenu
Gui,Menu,MainMenu

Gui,Font,s22,WingDings
Gui,Add,Button,% "h31 w31 y13 gNewGame vNewGame x" Width*BlockSize/2-4,K	; J=Smiley / K=Line / L=Frowny / m=Circle
Gui,Font,s16 Bold,Arial
Gui,Add,Text,% "x13 y14 w45 Border Center vMineCount c" (color ? "Red" : "White"),% SubStr("00" MineCount,-2)	; Remaining mine count
Gui,Add,Text,% "x" Width*BlockSize-34 " y14 w45 Border Center vTimer c" (color ? "Red" : "White"),000			; Timer

; Buttons
Gui,Font,s11,WingDings
Loop,% Height													; iterate vertically
{
	y:=A_Index												; remember line
	Loop,% Width												; set covers
	{
		x:=A_Index
		Gui,Add,Button,% "vB_x" x "y" y	" w" BlockSize 			; mark variable / width
						. " h" BlockSize " " (x = 1				; height / if 1st block
							? "x" 12 " y" y*BlockSize+39		; fix vertical offset
							: "yp x+0")							; otherwise stay inline /w prev. box
	}
}

; Show Gui
Gui,Show,% "w" ((Width>9 ? Width : 9))*BlockSize+24 " h" Height*BlockSize+68,%Title%
OnMessage(0x53,"WM_HELP")										; invoke Help handling for tooltips
Return ;--------------------------------------------------------; End of auto-execute section
#IfWinActive Minesweeper ahk_class AutoHotkeyGUI 				; variable %title% not supported

StartGame(x,y) {
	Global
	If (TimePassed)
		Return
	If (T_x%x%y%y%="M")											; we'll save you this time
	{
		T_x%x%y%y%:=""										; remove this mine
		Loop,% Height
		{
			y:=A_Index
			Loop,% Width
			{
				x:=A_Index
				If ( T_x%x%y%y%!="M")							; add it to first available non-mine square
					T_x%x%y%y%:="M"
					,break:=1
			IfEqual,break,1,Break
			}
		IfEqual,break,1,Break
		}
	}
	Loop,% Height												; iterate over height
	{
		y:=A_Index
		Loop,% Width											; iterate over width
		{
			x:=A_Index
			Gui,Font
			If (T_x%x%y%y%="M") {								; check for mine
				Gui,Font,s11,WingDings
				hColor:="Black"								; set color for text
			} Else {
				Loop,3 {										; loop over neighbors: three columns vertically
					ty:=y-2+A_Index							; calculate top offset
					Loop,3 {									; and three horizontally
						tx:=x-2+A_Index						; calculate left offset
						If (T_x%tx%y%ty%="M")					; no mine and inbound
							T_x%x%y%y%++						; update hint txt
					}
				}
				Gui,Font,s11 Bold,Courier New Bold
				If (Color)
					Loop,Parse,mColor,`,						; find color
						If (T_x%x%y%y% = A_Index) {				; hinttxt to color
							hColor:=A_LoopField				; set color for text
							Break
						}
				Else hColor:="Black"
			}
			Gui,Add,Text,% "c" hColor " Border +Center vT_x" x "y" y	; set color / variable
							. " w" BlockSize " h" BlockSize " "	(x = 1	; width / height / if 1st block
								? "x" 12 " y" y*BlockSize+39			; fix vertical position
								: "yp x+0")								; otherwise align to previous box
							,% T_x%x%y%y%								; M is WingDings for mine
			GuiControl,Hide,T_x%x%y%y%
		}
	}
	GuiControl,,Timer,% "00" . TimePassed:=1
	SetTimer,UpdateTimer,1000										; start timer
	If (Sound)
		SoundPlay,C:\WINDOWS\media\chord.wav
}

GuiSize:
	If (TimePassed)
		SetTimer,UpdateTimer,On 									; for cheat below
Return

UpdateTimer:														; timer
	GuiControl,,Timer,% (++TimePassed < 999) ? SubStr("00" TimePassed,-2) : 999
	If (Sound)
		SoundPlay,C:\WINDOWS\media\chord.wav
Return

Check(x,y){
	Global
	If (GameOver || B_x%x%y%y% != ""						; the game is over / prevent click on flagged squares and already-opened squares
		|| x<1 || x>Width || y<1 || y>Height)				; do not check neighbor on illegal squares
		Return
	If (T_x%x%y%y%="") {									; empty field?
		CheckNeighbor(x,y)									; uncover it and any neighbours
	} Else If (T_x%x%y%y% != "M") {							; no Mine, ok?
		GuiControl,Hide,B_x%x%y%y%							; hide covering button
		GuiControl,Show,T_x%x%y%y%
		B_x%x%y%y%:=1										; remember we been here
		UpdateChecks()
	} Else {												; ewww ... it's a mine!
		SetTimer,UpdateTimer,Off							; kill timer
		GuiControl,,T_x%x%y%y%,N							; set to Jolly Roger=N to mark death location
		GuiControl,,NewGame,L								; set Smiley Btn to Frown=L
		RevealAll()
		If (Sound)											; do we sound?
			If (FileExist("C:\Program Files\Microsoft Office\Office12\MEDIA\Explode.wav"))
				SoundPlay,C:\Program Files\Microsoft Office\Office12\MEDIA\Explode.wav
			Else
				SoundPlay,C:\WINDOWS\Media\notify.wav
	}
}

CheckNeighbor(x,y) {								; This function checks neighbours of the clicked
	Global											; field and uncovers empty fields plus adjacent hint fields
	If (GameOver || B_x%x%y%y%!="")
		Return
	GuiControl,Hide,B_x%x%y%y%						; uncover it
	GuiControl,Show,T_x%x%y%y%
	B_x%x%y%y%:=1									; remember it
	UpdateChecks()
	If (T_x%x%y%y%!="")								; is neighbour an nonchecked 0 value field?
		Return
	If (y-1>=1)										; check upper neighbour
		CheckNeighbor(x,y-1)
	If (y+1<=Height)								; check lower neighbour
		CheckNeighbor(x,y+1)
	If (x-1>=1)										; check left neighbour
		CheckNeighbor(x-1,y)
	If (x+1<=Width)									; check right neighbour
		CheckNeighbor(x+1,y)
	If (x-1>=1 && y-1>=1)							; check corner neighbour
		CheckNeighbor(x-1,y-1)
	If (x-1>=1 && y+1<=Height)						; check corner neighbour
		CheckNeighbor(x-1,y+1)
	If (x+1<=Width && y-1>=1)						; check corner neighbour
		CheckNeighbor(x+1,y-1)
	If (x+1<=Width && y+1<=Height)					; check corner neighbour
		CheckNeighbor(x+1,y+1)
}

UpdateChecks() {
	Global
	MineCount:=MineMax, Die1:=Die2:=0
	Loop,% Height
	{
		y:=A_Index
		Loop,% Width
		{
			x:=A_Index
			If (B_x%x%y%y%="O")
				MineCount--
			If (T_x%x%y%y%="M" && B_x%x%y%y%!="O") || (T_x%x%y%y%!="M" && B_x%x%y%y%="O")
				Die1++
			If (B_x%x%y%y%="" && T_x%x%y%y%!="M")
				Die2++
		}
	}
	GuiControl,,MineCount,% SubStr("00" MineCount,-2)
	If (Die1 && Die2)
		Return	; only get past here if flags+untouched squares match mines perfectly
	SetTimer,UpdateTimer,Off							; game won - kill timer
	GuiControl,,NewGame,J								; set Smiley Btn to Smile=J
	RevealAll()
	If (Sound)											; play sound
		SoundPlay,C:\WINDOWS\Media\tada.wav
	If (level!="Custom" && TimePassed < SubStr(BestTime%level%,1,InStr(BestTime%level%," ")-1) )
	{
		InputBox,name,Congratulations!,You have the fastest time`nfor level %level%.`nPlease enter your name.`n,,,,,,,,%A_UserName%
		If (name && !ErrorLevel)
		{
			BestTime%level%:=%TimePassed% seconds	%name%
			IniWrite,BestTime%level%,%A_ScriptFullPath%,InGameSettings,BestTime%level%
		}
	}
}

RevealAll(){
	Global
	Loop,% Height										; uncover all
	{
		y:=A_Index
		Loop,% Width
		{
			x:=A_Index
			If(T_x%x%y%y%="M")
			{
				GuiControl,Hide,B_x%x%y%y%
				GuiControl,Show,T_x%x%y%y%
			}
		}
	}
	GameOver:=True									; remember the game's over to block clicks
}

~F2::NewGame()
NewGame(){
NewGame:
	Reload
	ExitApp
}

LevelMenu:
	If (A_ThisMenuItem="&Beginner")
		level:="Beginner", Width:=9, Height:=9, MineMax:=10
	Else If (A_ThisMenuItem="&Intermediate")
		level:="Intermediate", Width:=16, Height:=16, MineMax:=40
	Else If (A_ThisMenuItem="&Expert")
		level:="Expert", Width:=30, Height:=16, MineMax:=99
	IniWrite,%level%,%A_ScriptFullPath%,InGameSettings,level
	IniWrite,%Width%,%A_ScriptFullPath%,InGameSettings,Width
	IniWrite,%Height%,%A_ScriptFullPath%,InGameSettings,Height
	IniWrite,%MineMax%,%A_ScriptFullPath%,InGameSettings,MineMax
	IniWrite,%Marks%,%A_ScriptFullPath%,InGameSettings,Marks
	IniWrite,%Sound%,%A_ScriptFullPath%,InGameSettings,Sound
	IniWrite,%Color%,%A_ScriptFullPath%,InGameSettings,Color
	NewGame()
Return

CustomMenu:												; label for setting window
	Gui,2:+Owner1										; make Gui#2 owned by gameWindow
	Gui,2:Default										; set to default
	Gui,1:+Disabled										; disable gameWindow
	Gui,-MinimizeBox +E0x00000400
	Gui,Add,Text,x15 y36 w38 h16,&Height:
	Gui,Add,Edit,Number x60 y33 w38 h20 vHeight HwndHwndHeight,%Height%	; use inGame settings
	Gui,Add,Text,x15 y60 w38 h16,&Width:
	Gui,Add,Edit,Number x60 y57 w38 h20 vWidth HwndHwndWidth,%Width%
	Gui,Add,Text,x15 y85 w38 h16,&Mines:
	Gui,Add,Edit,Number x60 y81 w38 h20 vMineMax HwndHwndMines,%MineMax%
	Gui,Add,Button,x120 y33 w60 h26 Default HwndHwndOK,OK
	Gui,Add,Button,x120 y75 w60 h26 g2GuiClose HwndHwndCancel,Cancel		; jump directly to 2GuiClose label
	Gui,Show,w195 h138,Custom Field
Return

2ButtonOK:													; label for OK button in settings window
	Gui,2:Submit,NoHide
	level:="Custom"
	MineMax:=MineMax<10 ? 10 : MineMax>((Width-1)*(Height-1)) ? ((Width-1)*(Height-1)) : MineMax
	Width:=Width<9 ? 9 : Width>30 ? 30 : Width
	Height:=Height<9 ? 9 : Height>24 ? 24 : Height
	GoSub,LevelMenu
Return

2GuiClose:
2GuiEscape:
	Gui,1:-Disabled								; reset GUI status and destroy setting window
	Gui,1:Default
	Gui,2:Destroy
Return

ToggleMenu:
	Toggle:=RegExReplace(RegExReplace(A_ThisMenuItem,"\s.*"),"&")
	temp:=%Toggle%
	%Toggle%:=!%Toggle%
	Menu,GameMenu,ToggleCheck,%A_ThisMenuItem%
	IniWrite,% %Toggle%,%A_ScriptFullPath%,InGameSettings,%Toggle%
Return

BestTimesMenu:
	Gui,3:+Owner1
	Gui,3:Default
	Gui,1:+Disabled
	Gui,-MinimizeBox +E0x00000400
	Gui,Add,Text,x15 y22 w250 vBestTimeBeginner HwndHwndBTB,Beginner:	%BestTimeBeginner%
	Gui,Add,Text,x15 y38 w250 vBestTimeIntermediate HwndHwndBTI,Intermediate:	%BestTimeIntermediate%
	Gui,Add,Text,x15 y54 w250 vBestTimeExpert HwndHwndBTE,Expert:		%BestTimeExpert%
	Gui,Add,Button,x38 y89 w75 h20 HwndHwndRS,&Reset Scores
	Gui,Add,Button,x173 y89 w45 h20 Default HwndHwndOK,OK
	Gui,Show,w255 h122,Fastest Mine Sweepers
Return

3ButtonResetScores:
	BestTimeBeginner	=999 seconds	Anonymous
	BestTimeIntermediate=999 seconds	Anonymous
	BestTimeExpert		=999 seconds	Anonymous
	GuiControl,,BestTimeBeginner,Beginner:	%BestTimeBeginner%`nIntermediate:	%BestTimeIntermediate%`nExpert:		%BestTimeExpert%
	GuiControl,,BestTimeIntermediate,Intermediate:	%BestTimeIntermediate%`nExpert:		%BestTimeExpert%
	GuiControl,,BestTimeExpert,Expert:		%BestTimeExpert%
	IniWrite,%BestTimeBeginner%,%A_ScriptFullPath%,InGameSettings,BestTimeBeginner
	IniWrite,%BestTimeIntermediate%,%A_ScriptFullPath%,InGameSettings,BestTimeIntermediate
	IniWrite,%BestTimeExpert%,%A_ScriptFullPath%,InGameSettings,BestTimeExpert
3GuiEscape:	; fall through:
3GuiClose:
3ButtonOK:
	Gui,1:-Disabled									; reset GUI status and destroy setting window
	Gui,1:Default
	Gui,3:Destroy
Return

^q::
GuiClose:
	ExitApp

HelpMenu:
	;Run C:\WINDOWS\Help\winmine.chm
Return

AboutMenu:
	Msgbox,64,About Minesweeper,AutoHotkey (r) Minesweeper`nVersion 1.0.6`nCopyright (c) 2014`nby derRaphael and Sobriquet
Return

~LButton::
~!LButton::
~^LButton::
~#LButton::
	Tooltip
	If (GetKeyState("RButton","P"))
		Return
	If A_OSVersion not in WIN_2003,WIN_XP,WIN_2000,WIN_NT4,WIN_95,WIN_98,WIN_ME
		If(A_PriorHotkey="~*LButton Up" && A_TimeSincePriorHotkey<100)
			GoTo,*MButton Up	; invoke MButton handling for autofill on left double-click in vista+
	If (GameOver)
		Return
	MouseGetPos,,y
	If (y>47)
		GuiControl,,NewGame,m
Return

~*LButton Up::
	If (GetKeyState("RButton","P"))
		GoTo,*MButton Up
	If (GameOver)
		Return
	GuiControl,,NewGame,K
	control:=GetControlFromClassNN(), NumX:=NumY:=0
	RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<Y>\d+)",Num)		; get Position
	StartGame(NumX,NumY)										; start game if neccessary
	Check(NumX,NumY)
Return

+LButton::
*MButton::
	Tooltip
	If (GameOver)
		Return
	MouseGetPos,,y
	If (y>47)
		GuiControl,,NewGame,m
Return

+LButton Up::
*MButton Up::
	If (GameOver)
		Return
	GuiControl,,NewGame,K
	StartGame(NumX,NumY)											; start game if neccessary
	If (GetKeyState("Esc","P"))
	{
		SetTimer,UpdateTimer,Off
		Return
	}
	control:=GetControlFromClassNN(), NumX:=NumY:=0
	RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<y>\d+)",Num)
	If ( !NumX || !NumY || B_x%NumX%y%NumY%!=1)
		Return
	temp:=0
	Loop,3 {														; loop over neighbors: three columns vertically
		ty:=NumY-2+A_Index										; calculate top offset
		Loop,3 {													; and three horizontally
			tx:=NumX-2+A_Index									; calculate left offset
			If (B_x%tx%y%ty% = "O")									; count number of marked mines around position
				temp++
		}
	}
	If (temp=T_x%NumX%y%NumY%)
		Loop,3 {													; loop over neighbors: three columns vertically
			ty:=NumY-2+A_Index									; calculate top offset
			Loop,3 {												; and three horizontally
				tx:=NumX-2+A_Index								; calculate left offset
				Check(tx,ty)										; simulate user clicking on each surrounding square
			}
		}
Return

~*RButton::
	If (GameOver || GetKeyState("LButton","P"))
		Return
	control:=GetControlFromClassNN(), NumX:=NumY:=0
	RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<y>\d+)",Num)			; grab 'em
	If ( !NumX || !NumY || B_x%NumX%y%NumY%=1)
		Return
	StartGame(NumX,NumY)											; start counter if neccessary
	B_x%NumX%y%NumY%:=(B_x%NumX%y%NumY%="" ? "O" : (B_x%NumX%y%NumY%="O" && Marks=1 ? "I" : ""))
	GuiControl,,B_x%NumX%y%NumY%,% B_x%NumX%y%NumY%
	UpdateChecks()
Return

~*RButton Up::
	If (GetKeyState("LButton","P"))
		GoTo,*MButton Up
Return

GetControlFromClassNN(){
	Global width
	MouseGetPos,,,,control
	If (SubStr(control,1,6)="Button")
		NumX:=mod(SubStr(control,7)-2,width)+1,NumY:=(SubStr(control,7)-2)//width+1
	Else If (SubStr(control,1,6)="Static")
		NumX:=mod(SubStr(control,7)-3,width)+1,NumY:=(SubStr(control,7)-3)//width+1
	Return "T_x" NumX "y" NumY
}

WM_HELP(_,_lphi)
{
	Global
	hItemHandle:=NumGet(_lphi+0,12)
	If (hItemHandle=HwndBTB || hItemHandle=HwndBTI || hItemHandle=HwndBTE)
		ToolTip Displays a player's best game time`, and the player's name`, for`neach level of play: Beginner`, Intermediate`, and Expert.
	Else If (hItemHandle=HwndRS)
		ToolTip Click to clear the current high scores.
	Else If (hItemHandle=HwndOK)
		ToolTip Closes the dialog box and saves any changes you`nhave made.
	Else If (hItemHandle=HwndCancel)
		ToolTip Closes the dialog box without saving any changes you have`nmade.
	Else If (hItemHandle=HwndHeight)
		ToolTip Specifies the number of vertical squares on the`nplaying field.
	Else If (hItemHandle=HwndWidth)
		ToolTip Specifies the number of horizontal squares on the`nplaying field.
	Else If (hItemHandle=HwndMines)
		ToolTip Specifies the number of mines to be placed on the`nplaying field.
}

:*?B0:xyzzy:: ; cheat
	KeyWait,Shift,D T1									; 1 sec grace period to press shift
	If (ErrorLevel)
		Return
	While,GetKeyState("Shift","P")
	{
		control:=GetControlFromClassNN()
		If (%control% = "M")
			SplashImage,,B X0 Y0 W1 H1 CW000000			; black pixel
		Else
			SplashImage,,B X0 Y0 W1 H1 CWFFFFFF			; white pixel
		Sleep 100
	}
	SplashImage,Off
Return

/*
 Version History
=================
Dec 29, 2008
1.0.0	Initial Release
1.0.1	BugFix
		- Game Restart Issues mentioned by Frankie
		- Guess count fixup I
		- Startbehaviour of game (time didnt start when 1st click was RMB Guess)
Dec 30, 2008
1.0.2	BugFix
		- Field Size Control vs Max MineCount / mentioned by °digit° / IsNull
1.0.3	BugFix
		- Guess count fixup II mentioned by °digit°
		- Corrected count when 'guessed' field uncovered
Dec 31, 2008
1.0.4	BugFix
		- Fix of Min Field & MineSettings
Mar 7, 2014
1.0.5	AddFeatures
		- Make appearance more like original
		- Make first click always safe
		- Re-license as CeCILL v2
Mar 8, 2014
1.0.6	AddFeatures
		- Add cheat code
		- Add middle-button shortcut
		- Re-license as GPL 1.3
*/


N = 6 : M = 5 : H = 25 : P = 0.2

fastgraphics
graphsize N*H,(M+1)*H
font "Arial",H/2+1,75
dim f(N,M) # 1 open, 2 mine, 4 expected mine
dim s(N,M) # count of mines in a neighborhood

trian1 = {1,1,H-1,1,H-1,H-1} : trian2 = {1,1,1,H-1,H-1,H-1}
mine = {2,2, H/2,H/2-2, H-2,2, H/2+2,H/2, H-2,H-2, H/2,H/2+2, 2,H-2, H/2-2,H/2}
flag = {H/2-1,3, H/2+1,3, H-4,H/5, H/2+1,H*2/5, H/2+1,H*0.9-2, H*0.8,H-2, H*0.2,H-2, H/2-1,H*0.9-2}

mines = int(N*M*P) : k = mines : act = 0
while k>0
	i = int(rand*N) : j = int(rand*M)
	if not f[i,j] then
		f[i,j] = 2 : k = k - 1		# set mine
		s[i,j] = s[i,j] + 1 : gosub adj	# count it

	end if
end while
togo =  M*N-mines : over = 0 : act = 1

gosub redraw
while not over
	clickclear
	while not clickb
		pause 0.01
	end while
	i = int(clickx/H) : j = int(clicky/H)
	if i<N and j<M then
		if clickb=1 then
			if not (f[i,j]&4) then ai = i : aj = j : gosub opencell
			if not s[i,j] then gosub adj
		else
			if not (f[i,j]&1) then
				if f[i,j]&4 then mines = mines+1
				if not (f[i,j]&4) then mines = mines-1
				f[i,j] = (f[i,j]&~4)|(~f[i,j]&4)
			end if
		end if
		if not (togo or mines) then over = 1
		gosub redraw
	end if
end while
imgsave "Minesweeper_game_BASIC-256.png", "PNG"
end

redraw:
	for i = 0 to N-1
		for j = 0 to M-1
			if over=-1 and f[i,j]&2 then f[i,j] = f[i,j]|1
			gosub drawcell
		next j
	next i
	# Counter
	color (32,32,32) : rect 0,M*H,N*H,H
	color white : x = 5 : y = M*H+H*0.05
	if not over then text x,y,"Mines: " + mines
	if over=1 then text x,y,"You won!"
	if over=-1 then text x,y,"You lost"
	refresh
	return

drawcell:
	color darkgrey
	rect i*H,j*H,H,H
	if f[i,j]&1=0 then	# closed
		color black : stamp i*H,j*H,trian1
		color white : stamp i*H,j*H,trian2
		color grey : rect i*H+2,j*H+2,H-4,H-4
		if f[i,j]&4 then color blue : stamp i*H,j*H,flag
	else
		color 192,192,192 : rect i*H+1,j*H+1,H-2,H-2
		# Draw
		if f[i,j]&2 then	# mine
			if not (f[i,j]&4) then color red
			if f[i,j]&4 then color darkgreen
			circle i*H+H/2,j*H+H/2,H/5 : stamp i*H,j*H,mine
		else
			if s[i,j] then color (32,32,32) : text i*H+H/3,j*H+1,s[i,j]
		end if
	end if
	return

adj:
	aj = j-1
	if j and i then ai = i-1 : gosub adjact
	if j then ai = i : gosub adjact
	if j and i<N-1 then ai = i+1 : gosub adjact
	aj = j
	if i then ai = i-1 : gosub adjact
	if i<N-1 then ai = i+1 : gosub adjact
	aj = j+1
	if j<M-1 and i then ai = i-1 : gosub adjact
	if j<M-1 then ai = i : gosub adjact
	if j<M-1 and i<N-1 then ai = i+1 : gosub adjact
	return

adjact:
	if not act then s[ai,aj] = s[ai,aj]+1 : return
	if act then gosub opencell : return

opencell:
	if not (f[ai,aj]&1) then
		f[ai,aj] = f[ai,aj]|1
		togo = togo-1
	end if
	if f[ai,aj]&2 then over = -1
	return


get "libhdr"

static $( randstate = 0 $)

manifest $(
    nummask  = #XF
    flagmask = #X10
    bombmask = #X20
    revealed = #X40
    MAXINT = (~0)>>1
    MININT = ~MAXINT
$)

let min(x,y) = x<y -> x, y
and max(x,y) = x>y -> x, y

let rand() = valof
$(  randstate := random(randstate)
    resultis randstate >> 7
$)

let randto(x) = valof
$(  let r, mask = ?, 1
    while mask<x do mask := (mask << 1) | 1
    r := rand() & mask repeatuntil r < x
    resultis r
$)

// Place a bomb on the field (if not already a bomb)
let placebomb(field, xsize, ysize, x, y) =
    (field!(y*xsize+x) & bombmask) ~= 0 -> false,
    valof
$(  for xa = max(x-1, 0) to min(x+1, xsize-1)
        for ya = max(y-1, 0) to min(y+1, ysize-1)
        $(  let loc = ya*xsize+xa
            let n = field!loc & nummask
            field!loc := (field!loc & ~nummask) | (n + 1)
        $)
    field!(y*xsize+x) := field!(y*xsize+x) | bombmask
    resultis true
$)

// Populate the field with N bombs
let populate(field, xsize, ysize, nbombs) be
$(  for i=0 to xsize*ysize-1 do field!i := 0
    while nbombs > 0
    $(  let x, y = randto(xsize), randto(ysize)
        if placebomb(field, xsize, ysize, x, y) then
            nbombs := nbombs - 1
    $)
$)

// Reveal field (X,Y) - returns true if stepped on a bomb
let reveal(field, xsize, ysize, x, y) =
    (field!(y*xsize+x) & bombmask) ~= 0 -> true,
    valof
$(  let loc = y*xsize+x
    field!loc := field!loc | revealed
    if (field!loc & nummask) = 0 then
        for xa = max(x-1, 0) to min(x+1, xsize-1)
            for ya = max(y-1, 0) to min(y+1, ysize-1)
                if (field!(ya*xsize+xa) &
                        (bombmask | flagmask | revealed)) = 0 do
                    reveal(field, xsize, ysize, xa, ya)
    resultis false
$)

// Toggle flag
let toggleflag(field, xsize, ysize, x, y) be
$(  let loc = y*xsize+x
    field!loc := field!loc neqv flagmask
$)

// Show the field. Returns true if won.
let showfield(field, xsize, ysize, kaboom) = valof
$(  let bombs, flags, hidden, found = 0, 0, 0, 0
    for i=0 to xsize*ysize-1
    $(  if (field!i & revealed) = 0 do hidden := hidden + 1
        unless (field!i & bombmask) = 0 do bombs := bombs + 1
        unless (field!i & flagmask) = 0 do flags := flags + 1
        if (field!i & bombmask) ~= 0 & (field!i & flagmask) ~= 0
            do found := found + 1
    $)
    writef("Bombs: %N - Flagged: %N - Hidden: %N*N", bombs, flags, hidden)
    wrch('+')
    for x=0 to xsize-1 do wrch('-')
    writes("+*N")
    for y=0 to ysize-1
    $(  wrch('|')
        for x=0 to xsize-1
        $(  let loc = y*xsize+x
            test kaboom & (field!loc & bombmask) ~= 0 do
                wrch('**')
            or test (field!loc & (flagmask | revealed)) = flagmask do
                wrch('?')
            or test (field!loc & revealed) = 0 do
                wrch('.')
            or test (field!loc & nummask) = 0 do
                wrch(' ')
            or
                wrch('0' + (field!loc & nummask))
        $)
        writes("|*N")
    $)
    wrch('+')
    for x=0 to xsize-1 do wrch('-')
    writes("+*N")
    resultis found = bombs
$)

// Ask a question, get number
let ask(q, min, max) = valof
$(  let n = ?
    $(  writes(q)
        n := readn()
    $) repeatuntil min <= n <= max
    resultis n
$)

// Read string
let reads(v) = valof
$(  let ch = ?
    v%0 := 0
    $(  ch := rdch()
        if ch = endstreamch then resultis false
        v%0 := v%0 + 1
        v%(v%0) := ch
    $) repeatuntil ch = '*N'
    resultis true
$)

// Play game given field
let play(field, xsize, ysize) be
$(  let x = ?
    let y = ?
    let ans = vec 80
    if showfield(field, xsize, ysize, false)
    $(  writes("*NYou win!*N")
        finish
    $)
    $(  writes("*NR)eveal, F)lag, Q)uit? ")
        unless reads(ans) finish
        unless ans%0 = 2 & ans%2='*N' loop
        ans%1 := ans%1 | 32
        if ans%1 = 'q' then finish
    $) repeatuntil ans%1='r' | ans%1='f'

    y := ask("Row? ", 1, ysize)-1
    x := ask("Column? ", 1, xsize)-1

    switchon ans%1 into
    $(  case 'r':
            unless (field!(y*xsize+x) & flagmask) = 0
            $(  writes("*NError: that field is flagged, unflag it first.*N")
                endcase
            $)
            unless (field!(y*xsize+x) & revealed) = 0
            $(  writes("*NError: that field is already revealed.*N")
                endcase
            $)
            if reveal(field, xsize, ysize, x, y)
            $(  writes("*N K A B O O M *N*N")
                showfield(field, xsize, ysize, true)
                finish
            $)
            endcase
        case 'f':
            test (field!(y*xsize+x) & revealed) = 0
               do toggleflag(field, xsize, ysize, x, y)
               or writes("*NError: that field is already revealed.*N")
            endcase
    $)
    wrch('*N')
$) repeat

let start() be
$(  let field, xsize, ysize, bombs = ?, ?, ?, ?

    writes("Minesweeper*N-----------*N*N")
    randstate := ask("Random seed? ", MININT, MAXINT)
    xsize := ask("Width (4-64)? ", 4, 64)
    ysize := ask("Height (4-22)? ", 4, 22)

    // 10 to 20% bombs
    bombs := muldiv(xsize,ysize,10) + randto(muldiv(xsize,ysize,10)+1)

    field := getvec(xsize*ysize)
    populate(field, xsize, ysize, bombs)
    play(field, xsize, ysize)
$)


#if 0

  Unix build:

  make CPPFLAGS=-DNDEBUG LDLIBS=-lcurses mines

  dwlmines, by David Lambert;  sometime in the twentieth Century.  The
  program is meant to run  in a terminal window compatible with curses
  if unix  is defined  to cpp,  or to an  ANSI terminal  when compiled
  without  unix macro  defined.   I suppose  I  have built  this on  a
  windows 98 computer using gcc running in a cmd window.  The original
  probably came from a VAX running  VMS with a vt100 sort of terminal.
  Today I  have xterm and gcc available  so I will claim  only that it
  works with this combination.

  As  this program can  automatically play  all the  trivially counted
  safe  squares.  Action  is quick  leaving the  player with  only the
  thoughtful action.  Whereas  's' steps on the spot  with the cursor,
  capital 'S' (Stomp) invokes autoplay.

  The cursor motion keys are as in the vi editor; hjkl move the cursor.

  'd' displays the number of unclaimed bombs and cells.

  'f' flags a cell.

  The  numbers  on the  field  indicate the  number  of  bombs in  the
  unclaimed neighboring  cells.  This is more useful  than showing the
  values you  expect.  You  may find unflagging  a cell adjacent  to a
  number will help you understand this.

  There  is extra  code  here.  The  multidimensional array  allocator
  allocarray is much better than  those of Numerical Recipes in C.  If
  you subtracted  the offset  1 to make  the arrays FORTRAN  like then
  allocarray could substitute for those of NR in C.
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifndef NDEBUG
# define DEBUG_CODE(A) A
#else
# define DEBUG_CODE(A)
#endif

#include <time.h>

#define DIM(A) (sizeof((A))/sizeof(*(A)))
#define MAX(A,B) ((A)<(B)?(B):(A))
#define BIND(A,L,H) ((L)<(A)?(A)<(H)?(A):(H):(L))

#define SET_BIT(A,B) ((A)|=1<<(B))
#define CLR_BIT(A,B) ((A)&=~(1<<(B)))
#define TGL_BIT(A,B) ((A)^=1<<(B))
#define INQ_BIT(A,B) ((A)&(1<<(B)))
#define FOREVER for(;;)
#define MODINC(i,mod) ((i)+1<(mod)?(i)+1:0)
#define MODDEC(i,mod) (((i)<=0?(mod):(i))-1)

void error(int status,const char *message) {
  fprintf(stderr, "%s\n", message);
  exit(status);
}

void*dwlcalloc(int n,size_t bytes) {
  void*rv = (void*)calloc(n,bytes);
  if (NULL == rv)
    error(1,"memory allocation failure");
  DEBUG_CODE(fprintf(stderr,"allocated address %p\n",rv);)
  return rv;
}

void*allocarray(int rank,size_t*shape,size_t itemSize) {
  /*
     Allocates arbitrary dimensional arrays (and inits all pointers)
     with only 1 call to malloc.  David W. Lambert, written before 1990.
     This is wonderful because one only need call free once to deallocate
     the space.  Special routines for each size array are not need for
     allocation of for deallocation.  Also calls to malloc might be expensive
     because they might have to place operating system requests.  One call
     seems optimal.
  */
  size_t size,i,j,dataSpace,pointerSpace,pointers,nextLevelIncrement;
  char*memory,*pc,*nextpc;
  if (rank < 2) {
    if (rank < 0)
      error(1,"invalid negative rank argument passed to allocarray");
    size = rank < 1 ? 1 : *shape;
    return dwlcalloc(size,itemSize);
  }
  pointerSpace = 0, dataSpace = 1;
  for (i = 0; i < rank-1; ++i)
    pointerSpace += (dataSpace *= shape[i]);
  pointerSpace *= sizeof(char*);
  dataSpace *= shape[i]*itemSize;
  memory = pc = dwlcalloc(1,pointerSpace+dataSpace);
  pointers = 1;
  for (i = 0; i < rank-2; ) {
    nextpc = pc + (pointers *= shape[i])*sizeof(char*);
    nextLevelIncrement = shape[++i]*sizeof(char*);
    for (j = 0; j < pointers; ++j)
      *((char**)pc) = nextpc, pc+=sizeof(char*), nextpc += nextLevelIncrement;
  }
  nextpc = pc + (pointers *= shape[i])*sizeof(char*);
  nextLevelIncrement = shape[++i]*itemSize;
  for (j = 0; j < pointers; ++j)
    *((char**)pc) = nextpc, pc+=sizeof(char*), nextpc += nextLevelIncrement;
  return memory;
}

#define PRINT(element) \
  if (NULL == print_elt) \
    printf("%10.3e",*(double*)(element)); \
  else \
    (*print_elt)(element)

/* matprint prints an array in APL\360 style */
/* with a NULL element printing function matprint assumes an array of double */
void matprint(void*a,int rank,size_t*shape,size_t size,void(*print_elt)()) {
  union {
    unsigned **ppu;
    unsigned *pu;
    unsigned u;
  } b;
  int i;
  if (rank <= 0 || NULL == shape)
    PRINT(a);
  else if (1 < rank) {
    for (i = 0; i < shape[0]; ++i)
      matprint(((void**)a)[i], rank-1,shape+1,size,print_elt);
    putchar('\n');
    for (i = 0, b.pu = a; i < shape[0]; ++i, b.u += size) {
      PRINT(b.pu);
      putchar(' ');
    }
  }
}

#ifdef __unix__
# include <curses.h>
# include <unistd.h>
# define SRANDOM srandom
# define RANDOM random
#else
# include <windows.h>
  void addch(int c) { putchar(c); }
  void addstr(const char*s) { fputs(s,stdout); }
# define ANSI putchar(27),putchar('[')
  void initscr(void) { printf("%d\n",AllocConsole()); }
  void cbreak(void) { ; }
  void noecho(void) { ; }
  void nonl(void) { ; }
  int move(int r,int c) { ANSI; return printf("%d;%dH",r+1,c+1); }
  int mvaddch(int r,int c,int ch) { move(r,c); addch(ch); }
  void refresh(void) { ; }
# define WA_STANDOUT 32
  int attr_on(int a,void*p) { ANSI; return printf("%dm",a); }
  int attr_off(int a,void*p) { attr_on(0,NULL); }
# include <stdarg.h>
  void printw(const char*fmt,...) {
    va_list args;
    va_start(args,fmt);
    vprintf(fmt,args);
    va_end(args);
  }
  void clrtoeol(void) {
    ANSI;addstr("0J");
  }
# define SRANDOM srand
# define RANDOM rand
#endif

#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 1		/* just a guess */
#endif

#if 0
  cell status
    UNKN --- contains virgin earth (initial state)
    MINE --- has a mine
    FLAG --- was flagged
#endif

enum {UNKN,MINE,FLAG};	/* bit numbers */

#define DETECT(CELL,PROPERTY) (!!INQ_BIT(CELL,PROPERTY))

DEBUG_CODE(                                                                \
  void pchr(void*a) {		/* odd comment removed */                  \
    putchar('A'+*(char*a));	/* should print the nth char of alphabet */\
  }                             /* where 'A' is 0 */                       \
)

char**bd;			/* the board */
size_t shape[2];
#define RWS (shape[0])
#define CLS (shape[1])

void populate(int x,int y,int pct) { /* finished size in col, row, % mines */
  int i,j,c;
  x = BIND(x,4,200), y = BIND(y,4,400); /* confine input as piecewise linear */
  shape[0] = x+2, shape[1] = y+2; bd = (char**)allocarray(2,shape,sizeof(char));
  memset(*bd,1<<UNKN,shape[0]*shape[1]*sizeof(char)); /* all unknown */
  for (i = 0; i < shape[0]; ++i)		      /* border is safe */
    bd[i][0] = bd[i][shape[1]-1] = 0;
  for (i = 0; i < shape[1]; ++i)
    bd[0][i] = bd[shape[0]-1][i] = 0;
  {
    time_t seed; /* now I would choose /dev/random */
    printf("seed is %u\n",(unsigned)seed);
    time(&seed), SRANDOM((unsigned)seed);
  }
  c = BIND(pct,1,99)*x*y/100;	/* number of mines to set */
  while(c) {
    i = RANDOM(), j = 1+i%y, i = 1+(i>>16)%x;
    if (! DETECT(bd[i][j],MINE)) /* 1 mine per site */
      --c, SET_BIT(bd[i][j],MINE);
  }
  DEBUG_CODE(matprint(bd,2,shape,sizeof(int),pchr);)
    RWS = x+1, CLS = y+1;	/* shape now stores the upper bounds */
}

struct {
  int i,j;
} neighbor[] = {
  {-1,-1}, {-1, 0}, {-1, 1},
  { 0,-1}, /*home*/ { 0, 1},
  { 1,-1}, { 1, 0}, { 1, 1}
};
/* NEIGHBOR seems to map 0..8 to local 2D positions */
#define NEIGHBOR(I,J,K) (bd[(I)+neighbor[K].i][(J)+neighbor[K].j])

int cnx(int i,int j,char w) {	/* count neighbors with property w */
  int k,c = 0;
  for (k = 0; k < DIM(neighbor); ++k)
    c += DETECT(NEIGHBOR(i,j,k),w);
  return c;
}

int row,col;
#define ME bd[row+1][col+1]

int step(void) {
  if (DETECT(ME,FLAG)) return 1; /* flags offer protection */
  if (DETECT(ME,MINE)) return 0; /* lose */
  CLR_BIT(ME,UNKN);
  return 1;
}

int autoplay(void) {
  int i,j,k,change,m;
  if (!step()) return 0;
  do				/* while changing */
    for (change = 0, i = 1; i < RWS; ++i)
      for (j = 1; j < CLS; ++j)
	if (!DETECT(bd[i][j],UNKN)) { /* consider nghbrs of safe cells */
	  m = cnx(i,j,MINE);
	  if (cnx(i,j,FLAG) == m) { /* mines appear flagged */
	    for (k = 0; k < DIM(neighbor); ++k)
	      if (DETECT(NEIGHBOR(i,j,k),UNKN)&&!DETECT(NEIGHBOR(i,j,k),FLAG)) {
		if (DETECT(NEIGHBOR(i,j,k),MINE)) { /* OOPS! */
		  row = i+neighbor[k].i-1, col = j+neighbor[k].j-1;
		  return 0;
		}
		change = 1, CLR_BIT(NEIGHBOR(i,j,k),UNKN);
	      }
	  } else if (cnx(i,j,UNKN) == m)
	    for (k = 0; k < DIM(neighbor); ++k)
	      if (DETECT(NEIGHBOR(i,j,k),UNKN))
		change = 1, SET_BIT(NEIGHBOR(i,j,k),FLAG);
	}
  while (change);
  return 1;
}

void takedisplay(void) { initscr(), cbreak(), noecho(), nonl(); }

void help(void) {
  move(RWS,1),clrtoeol(), printw("move:hjkl flag:Ff step:Ss other:qd?");
}

void draw(void) {
  int i,j,w;
  const char*s1 = " 12345678";
  move(1,1);
  for (i = 1; i < RWS; ++i, addstr("\n "))
    for (j = 1; j < CLS; ++j, addch(' ')) {
      w = bd[i][j];
      if (!DETECT(w,UNKN)) {
	w = cnx(i,j,MINE)-cnx(i,j,FLAG);
	if (w < 0) attr_on(WA_STANDOUT,NULL), w = -w;
	addch(s1[w]);
	attr_off(WA_STANDOUT,NULL);
      }
      else if (DETECT(w,FLAG)) addch('F');
      else addch('*');
    }
  move(row+1,2*col+1);
  refresh();
}

void show(int win) {
  int i,j,w;
  const char*s1 = " 12345678";
  move(1,1);
  for (i = 1; i < RWS; ++i, addstr("\n "))
    for (j = 1; j < CLS; ++j, addch(' ')) {
      w = bd[i][j];
      if (!DETECT(w,UNKN)) {
	w = cnx(i,j,MINE)-cnx(i,j,FLAG);
	if (w < 0) attr_on(WA_STANDOUT,NULL), w = -w;
	addch(s1[w]);
	attr_off(WA_STANDOUT,NULL);
      }
      else if (DETECT(w,FLAG))
	if (DETECT(w,MINE)) addch('F');
	else attr_on(WA_STANDOUT,NULL), addch('F'),attr_off(WA_STANDOUT,NULL);
      else if (DETECT(w,MINE)) addch('M');
      else addch('*');
    }
  mvaddch(row+1,2*col,'('), mvaddch(row+1,2*(col+1),')');
  move(RWS,0);
  refresh();
}

#define HINTBIT(W) s3[DETECT(bd[r][c],(W))]
#define NEIGCNT(W) s4[cnx(r,c,(W))]

const char*s3="01", *s4="012345678";

void dbg(int r, int c) {
  int i,j,unkns=0,mines=0,flags=0,pct;
  char o[6];
  static int hint;
  for (i = 1; i < RWS; ++i)
    for (j = 1; j < CLS; ++j)
      unkns += DETECT(bd[i][j],UNKN),
	mines += DETECT(bd[i][j],MINE),
	flags += DETECT(bd[i][j],FLAG);
  move(RWS,1), clrtoeol();
  pct = 0.5+100.0*(mines-flags)/MAX(1,unkns-flags);
  if (++hint<4)
    o[0] = HINTBIT(UNKN), o[1] = HINTBIT(MINE), o[2] = HINTBIT(FLAG),
      o[3] = HINTBIT(UNKN), o[4] = NEIGCNT(MINE), o[5] = NEIGCNT(FLAG);
  else
    memset(o,'?',sizeof(o));
  printw("(%c%c%c) u=%c, m=%c, f=%c,  %d/%d (%d%%) remain.",
	 o[0],o[1],o[2],o[3],o[4],o[5],mines-flags,unkns-flags,pct);
}
#undef NEIGCNT
#undef HINTBIT

void toggleflag(void) {
  if (DETECT(ME,UNKN))
    TGL_BIT(ME,FLAG);
}
int sureflag(void) {
  toggleflag();
  return autoplay();
}
int play(int*win) {
  int c = getch(), d = tolower(c);
  if ('q' == d) return 0;
  else if ('?' == c) help();
  else if ('h' == d) col = MODDEC(col,CLS-1);
  else if ('l' == d) col = MODINC(col,CLS-1);
  else if ('k' == d) row = MODDEC(row,RWS-1);
  else if ('j' == d) row = MODINC(row,RWS-1);
  else if ('f' == c) toggleflag();
  else if ('s' == c) return *win = step();
  else if ('S' == c) return *win = autoplay();
  else if ('F' == c) return *win = sureflag();
  else if ('d' == d) dbg(row+1,col+1);
  return 1;
}

int convert(const char*name,const char*s) {
  if (strlen(s) == strspn(s,"0123456789"))
    return atoi(s);
  fprintf(stderr,"    use:  %s [rows [columns [percentBombs]]]\n",name);
  fprintf(stderr,"default:  %s 20 30 25\n",name);
  exit(EXIT_SUCCESS);
}

void parse_command_line(int ac,char*av[],int*a,int*b,int*c) {
  switch (ac) {
  default:
  case 4: *c = convert(*av,av[3]);
  case 3: *b = convert(*av,av[2]);
  case 2: *a = convert(*av,av[1]);
  case 1: ;
  }
}

int main(int ac,char*av[],char*env[]) {
  int win = 1, rows = 20, cols = 30, prct = 25;
  parse_command_line(ac,av,&rows,&cols,&prct);
  populate(rows,cols,prct);
  takedisplay();
  while(draw(), play(&win));
  show(win);
  free(bd);
# ifdef __unix__
  {
    const char*s = "/bin/stty";
    execl(s,s,"sane",(const char*)NULL);
  }
# endif
  return 0;
}


using System;
using System.Drawing;
using System.Windows.Forms;

class MineFieldModel
{
    public int RemainingMinesCount{
        get{
            var count = 0;
            ForEachCell((i,j)=>{
                if (Mines[i,j] && !Marked[i,j])
                    count++;
            });
            return count;
        }
    }

    public bool[,] Mines{get; private set;}
    public bool[,] Opened{get;private set;}
    public bool[,] Marked{get; private set;}
    public int[,] Values{get;private set; }
    public int Width{ get{return Mines.GetLength(1);} }
    public int Height{ get{return Mines.GetLength(0);} }

    public MineFieldModel(bool[,] mines)
    {
        this.Mines = mines;
        this.Opened = new bool[Height, Width]; // filled with 'false' by default
        this.Marked = new bool[Height, Width];
        this.Values = CalculateValues();
    }

    private int[,] CalculateValues()
    {
        int[,] values = new int[Height, Width];
        ForEachCell((i,j) =>{
            var value = 0;
            ForEachNeighbor(i,j, (i1,j1)=>{
                if (Mines[i1,j1])
                    value++;
            });
            values[i,j] = value;
        });
        return values;
    }

    // Helper method for iterating over cells
    public void ForEachCell(Action<int,int> action)
    {
        for (var i = 0; i < Height; i++)
        for (var j = 0; j < Width; j++)
            action(i,j);
    }

    // Helper method for iterating over cells' neighbors
    public void ForEachNeighbor(int i, int j, Action<int,int> action)
    {
        for (var i1 = i-1; i1 <= i+1; i1++)
        for (var j1 = j-1; j1 <= j+1; j1++)
            if (InBounds(j1, i1) && !(i1==i && j1 ==j))
                action(i1, j1);
    }

    private bool InBounds(int x, int y)
    {
        return y >= 0 && y < Height && x >=0 && x < Width;
    }

    public event Action Exploded = delegate{};
    public event Action Win = delegate{};
    public event Action Updated = delegate{};

    public void OpenCell(int i, int j){
        if(!Opened[i,j]){
            if (Mines[i,j])
                Exploded();
            else{
                OpenCellsStartingFrom(i,j);
                Updated();
                CheckForVictory();
            }
        }
    }

    void OpenCellsStartingFrom(int i, int j)
    {
            Opened[i,j] = true;
            ForEachNeighbor(i,j, (i1,j1)=>{
                if (!Mines[i1,j1] && !Opened[i1,j1] && !Marked[i1,j1])
                    OpenCellsStartingFrom(i1, j1);
            });
    }

    void CheckForVictory(){
        int notMarked = 0;
        int wrongMarked = 0;
        ForEachCell((i,j)=>{
            if (Mines[i,j] && !Marked[i,j])
                notMarked++;
            if (!Mines[i,j] && Marked[i,j])
                wrongMarked++;
        });
        if (notMarked == 0 && wrongMarked == 0)
            Win();
    }

    public void Mark(int i, int j){
        if (!Opened[i,j])
            Marked[i,j] = true;
            Updated();
            CheckForVictory();
    }
}

class MineFieldView: UserControl{
    public const int CellSize = 40;

    MineFieldModel _model;
    public MineFieldModel Model{
        get{ return _model; }
        set
        {
            _model = value;
            this.Size = new Size(_model.Width * CellSize+1, _model.Height * CellSize+2);
        }
    }

    public MineFieldView(){
        //Enable double-buffering to eliminate flicker
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);
        this.Font = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold);

        this.MouseUp += (o,e)=>{
            Point cellCoords = GetCell(e.Location);
            if (Model != null)
            {
                if (e.Button == MouseButtons.Left)
                    Model.OpenCell(cellCoords.Y, cellCoords.X);
                else if (e.Button == MouseButtons.Right)
                    Model.Mark(cellCoords.Y, cellCoords.X);
            }
        };
    }

    Point GetCell(Point coords)
    {
        var rgn = ClientRectangle;
        var x = (coords.X - rgn.X)/CellSize;
        var y = (coords.Y - rgn.Y)/CellSize;
        return new Point(x,y);
    }

    static readonly Brush MarkBrush = new SolidBrush(Color.Blue);
    static readonly Brush ValueBrush = new SolidBrush(Color.Black);
    static readonly Brush UnexploredBrush = new SolidBrush(SystemColors.Control);
    static readonly Brush OpenBrush = new SolidBrush(SystemColors.ControlDark);

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        var g = e.Graphics;
        if (Model != null)
        {
            Model.ForEachCell((i,j)=>
            {
                var bounds = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
                if (Model.Opened[i,j])
                {
                    g.FillRectangle(OpenBrush, bounds);
                    if (Model.Values[i,j] > 0)
                    {
                        DrawStringInCenter(g, Model.Values[i,j].ToString(), ValueBrush, bounds);
                    }
                }
                else
                {
                    g.FillRectangle(UnexploredBrush, bounds);
                    if (Model.Marked[i,j])
                    {
                        DrawStringInCenter(g, "?", MarkBrush, bounds);
                    }
                    var outlineOffset = 1;
                    var outline = new Rectangle(bounds.X+outlineOffset, bounds.Y+outlineOffset, bounds.Width-2*outlineOffset, bounds.Height-2*outlineOffset);
                    g.DrawRectangle(Pens.Gray, outline);
                }
                g.DrawRectangle(Pens.Black, bounds);
            });
        }

    }

    static readonly StringFormat FormatCenter = new StringFormat
                            {
                                LineAlignment = StringAlignment.Center,
                                Alignment=StringAlignment.Center
                            };

    void DrawStringInCenter(Graphics g, string s, Brush brush, Rectangle bounds)
    {
        PointF center = new PointF(bounds.X + bounds.Width/2, bounds.Y + bounds.Height/2);
        g.DrawString(s, this.Font, brush, center, FormatCenter);
    }

}

class MineSweepForm: Form
{

    MineFieldModel CreateField(int width, int height)
{
        var field = new bool[height, width];
        int mineCount = (int)(0.2 * height * width);
        var rnd = new Random();
        while(mineCount > 0)
        {
            var x = rnd.Next(width);
            var y = rnd.Next(height);
            if (!field[y,x])
            {
                field[y,x] = true;
                mineCount--;
            }
        }
        return new MineFieldModel(field);
    }

    public MineSweepForm()
    {
        var model = CreateField(6, 4);
        var counter = new Label{ };
        counter.Text = model.RemainingMinesCount.ToString();
        var view = new MineFieldView
                        {
                            Model = model, BorderStyle = BorderStyle.FixedSingle,
                        };
        var stackPanel = new FlowLayoutPanel
                        {
                            Dock = DockStyle.Fill,
                            FlowDirection = FlowDirection.TopDown,
                            Controls = {counter, view}
                        };
        this.Controls.Add(stackPanel);
        model.Updated += delegate{
            view.Invalidate();
            counter.Text = model.RemainingMinesCount.ToString();
        };
        model.Exploded += delegate {
            MessageBox.Show("FAIL!");
            Close();
        };
        model.Win += delegate {
            MessageBox.Show("WIN!");
            view.Enabled = false;
        };

    }
}

class Program
{
    static void Main()
    {
        Application.Run(new MineSweepForm());
    }
}


#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
typedef unsigned char byte;

enum fieldValues : byte { OPEN, CLOSED = 10, MINE, UNKNOWN, FLAG, ERR };

class fieldData
{
public:
    fieldData() : value( CLOSED ), open( false ) {}
    byte value;
    bool open, mine;
};

class game
{
public:
    ~game()
    { if( field ) delete [] field; }

    game( int x, int y )
    {
        go = false; wid = x; hei = y;
	field = new fieldData[x * y];
	memset( field, 0, x * y * sizeof( fieldData ) );
	oMines = ( ( 22 - rand() % 11 ) * x * y ) / 100;
	mMines = 0;
	int mx, my, m = 0;
	for( ; m < oMines; m++ )
	{
	    do
	    { mx = rand() % wid; my = rand() % hei; }
	    while( field[mx + wid * my].mine );
	    field[mx + wid * my].mine = true;
	}
	graphs[0] = ' '; graphs[1] = '.'; graphs[2] = '*';
	graphs[3] = '?'; graphs[4] = '!'; graphs[5] = 'X';
    }

    void gameLoop()
    {
	string c, r, a;
	int col, row;
	while( !go )
	{
	    drawBoard();
	    cout << "Enter column, row and an action( c r a ):\nActions: o => open, f => flag, ? => unknown\n";
	    cin >> c >> r >> a;
	    if( c[0] > 'Z' ) c[0] -= 32; if( a[0] > 'Z' ) a[0] -= 32;
	    col = c[0] - 65; row = r[0] - 49;
	    makeMove( col, row, a );
	}
    }

private:
    void makeMove( int x, int y, string a )
    {
	fieldData* fd = &field[wid * y + x];
	if( fd->open && fd->value < CLOSED )
	{
	    cout << "This cell is already open!";
	    Sleep( 3000 ); return;
	}
	if( a[0] == 'O' ) openCell( x, y );
	else if( a[0] == 'F' )
	{
	    fd->open = true;
	    fd->value = FLAG;
	    mMines++;
	    checkWin();
	}
	else
	{
	    fd->open = true;
	    fd->value = UNKNOWN;
	}
    }

    bool openCell( int x, int y )
    {
	if( !isInside( x, y ) ) return false;
	if( field[x + y * wid].mine ) boom();
	else
	{
	    if( field[x + y * wid].value == FLAG )
	    {
		field[x + y * wid].value = CLOSED;
		field[x + y * wid].open = false;
		mMines--;
	    }
	    recOpen( x, y );
	    checkWin();
	}
	return true;
    }

    void drawBoard()
    {
	system( "cls" );
	cout << "Marked mines: " << mMines << " from " << oMines << "\n\n";
	for( int x = 0; x < wid; x++ )
	    cout << "  " << ( char )( 65 + x ) << " ";
	cout << "\n"; int yy;
	for( int y = 0; y < hei; y++ )
	{
	    yy = y * wid;
	    for( int x = 0; x < wid; x++ )
		cout << "+---";

	    cout << "+\n"; fieldData* fd;
	    for( int x = 0; x < wid; x++ )
	    {
		fd = &field[x + yy]; cout<< "| ";
		if( !fd->open ) cout << ( char )graphs[1] << " ";
		else
		{
		    if( fd->value > 9 )
			cout << ( char )graphs[fd->value - 9] << " ";
		    else
		    {
			if( fd->value < 1 ) cout << "  ";
			    else cout << ( char )(fd->value + 48 ) << " ";
		    }
		}
	    }
	    cout << "| " << y + 1 << "\n";
	}
	for( int x = 0; x < wid; x++ )
	    cout << "+---";

	cout << "+\n\n";
    }

    void checkWin()
    {
	int z = wid * hei - oMines, yy;
	fieldData* fd;
	for( int y = 0; y < hei; y++ )
	{
	    yy = wid * y;
	    for( int x = 0; x < wid; x++ )
	    {
		fd = &field[x + yy];
		if( fd->open && fd->value != FLAG ) z--;
	    }
	}
	if( !z ) lastMsg( "Congratulations, you won the game!");
    }

    void boom()
    {
	int yy; fieldData* fd;
	for( int y = 0; y < hei; y++ )
	{
	    yy = wid * y;
	    for( int x = 0; x < wid; x++ )
	    {
		fd = &field[x + yy];
		if( fd->value == FLAG )
		{
		    fd->open = true;
		    fd->value = fd->mine ? MINE : ERR;
		}
		else if( fd->mine )
		{
		    fd->open = true;
		    fd->value = MINE;
		}
	    }
	}
	lastMsg( "B O O O M M M M M !" );
    }

    void lastMsg( string s )
    {
	go = true; drawBoard();
	cout << s << "\n\n";
    }

    bool isInside( int x, int y ) { return ( x > -1 && y > -1 && x < wid && y < hei ); }

    void recOpen( int x, int y )
    {
	if( !isInside( x, y ) || field[x + y * wid].open ) return;
	int bc = getMineCount( x, y );
	field[x + y * wid].open = true;
	field[x + y * wid].value = bc;
	if( bc ) return;

	for( int yy = -1; yy < 2; yy++ )
	    for( int xx = -1; xx < 2; xx++ )
	    {
		if( xx == 0 && yy == 0 ) continue;
		recOpen( x + xx, y + yy );
	    }
    }

    int getMineCount( int x, int y )
    {
	int m = 0;
	for( int yy = -1; yy < 2; yy++ )
	    for( int xx = -1; xx < 2; xx++ )
	    {
		if( xx == 0 && yy == 0 ) continue;
		if( isInside( x + xx, y + yy ) && field[x + xx + ( y + yy ) * wid].mine ) m++;
	    }

	return m;
    }

    int wid, hei, mMines, oMines;
    fieldData* field; bool go;
    int graphs[6];
};

int main( int argc, char* argv[] )
{
    srand( GetTickCount() );
    game g( 4, 6 ); g.gameLoop();
    return system( "pause" );
}


import ceylon.random {

	DefaultRandom
}

class Cell() {

	shared variable Boolean covered = true;
	shared variable Boolean flagged = false;
	shared variable Boolean mined = false;
	shared variable Integer adjacentMines = 0;

	string =>
			if (covered && !flagged)
			then "."
			else if (covered && flagged)
			then "?"
			else if (!covered && mined)
			then "X"
			else if (!covered && adjacentMines > 0)
			then adjacentMines.string
			else " ";
}

"The main function of the module. Run this one."
shared void run() {

	value random = DefaultRandom();
	value chanceOfBomb = 0.2;

	value width = 6;
	value height = 4;

	value grid = Array { for (j in 1..height) Array { for (i in 1..width) Cell() } };

	function getCell(Integer x, Integer y) => grid[y]?.get(x);

	void initializeGrid() {

		for (row in grid) {
			for (cell in row) {
				cell.covered = true;
				cell.flagged = false;
				cell.mined = random.nextFloat() < chanceOfBomb;
			}
		}

		function countAdjacentMines(Integer x, Integer y) => count {
			for (j in y - 1 .. y + 1)
			for (i in x - 1 .. x + 1)
			if (exists cell = getCell(i, j))
			cell.mined
		};

		for (j->row in grid.indexed) {
			for (i->cell in row.indexed) {
				cell.adjacentMines = countAdjacentMines(i, j);
			}
		}
	}

	void displayGrid() {
		print("  " + "".join(1..width));
		print("  " + "-".repeat(width));
		for (j->row in grid.indexed) {
			print("``j + 1``|``"".join(row)``|``j + 1``");
		}
		print("  " + "-".repeat(width));
		print("  " + "".join(1..width));
	}

	Boolean won() =>
		expand(grid).every((cell) => (cell.flagged && cell.mined) || (!cell.flagged && !cell.mined));

	void uncoverNeighbours(Integer x, Integer y) {
		for (j in y - 1 .. y + 1) {
			for (i in x - 1 .. x + 1) {
				if (exists cell = getCell(i, j), cell.covered, !cell.flagged, !cell.mined) {
					cell.covered = false;
					if (cell.adjacentMines == 0) {
						uncoverNeighbours(i, j);
					}
				}
			}
		}
	}

	while (true) {
		print("Welcome to minesweeper!
		       -----------------------");
		initializeGrid();
		while (true) {
			displayGrid();
			print("
			       The number of mines to find is ``count(expand(grid)*.mined)``.
			       What would you like to do?
			       [1] reveal a free space (or blow yourself up)
			       [2] mark (or unmark) a mine");
			assert (exists instruction = process.readLine());
			print("Please enter the coordinates. eg 2 4");
			assert (exists line2 = process.readLine());
			value coords = line2.split().map(Integer.parse).narrow<Integer>().sequence();
			if (exists x = coords[0], exists y = coords[1], exists cell = getCell(x - 1, y - 1)) {
				switch (instruction)
				case ("1") {
					if (cell.mined) {
						print("=================
						       === You lose! ===
						       =================");
						expand(grid).each((cell) => cell.covered = false);
						displayGrid();
						break;
					}
					else if (cell.covered) {
						cell.covered = false;
						uncoverNeighbours(x - 1, y - 1);
					}
				}
				case ("2") {
					if (cell.covered) {
						cell.flagged = !cell.flagged;
					}
				}
				else { print("bad choice"); }
				if (won()) {
					print("****************
					       *** You win! ***
					       ****************");
					break;
				}
			}
		}
	}
}