anchor.diff:  -module(minesweeperl).
   -author('Tom Szilagyi <tomszilagyi@gmail.com>').
  --export([new_game/3, new_game/4]).
  +-export([ new_game/3
  +        , new_game/4
  +        , step/2
  +        , flag/2
  +        , question/2]).
   
   -include_lib("eunit/include/eunit.hrl").
   
   -record(field,
  -        { is_mine    = false
  -        , is_covered = true
  -        , is_flagged = false
  -        , neighbours
  -        , neighbour_mines
  +        { has_mine = false  :: true | false
  +        , status = covered
  +                  :: covered    % field is in its initial, hidden state
  +                  | flagged     % player or solver flagged field
  +                  | questioned  % player marked field with '?'
  +                  | uncovered   % stepped on or uncovered indirectly
  +                  | exploded    % this is a mine, and player has stepped on it
  +        , neighbours :: [pos_integer()] % list of field seq numbers
           }).
   
   -record(board,
  -        { dims
  -        , n_mines
  -        , fields
  +        { dims       :: {pos_integer(), pos_integer()}
  +        , n_mines    :: pos_integer()
  +        , n_hidden   :: pos_integer()         % countdown to 0 -> end of game
  +        , fields     :: array:array(#field{}) % access fields via seq number
           }).
  +
  +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  +%% Exported functions
  +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   
   new_game(Rows, Cols, _Mines) when Rows < 2; Cols < 2 ->
       throw(badarg);
  
positive.code:                {Px+1, Py-1}, {Px+1, Py}, {Px+1, Py+1}], Dim).
  
  
  neighbour_mines(#field{neighbours = Nbs}, Fields) ->
      NbFs = [array:get(Seq, Fields) || Seq <- Nbs],
      lists:foldl(fun(#field{is_mine = true}, Cnt) -> Cnt+1;
                     (#field{}, Cnt) -> Cnt
                  end, 0, NbFs).
  
  
  
negative.code:     #board{dims = {3, 4}, n_mines = 5, fields = Fields} = Board,
      F5 = array:get(5, Fields),
      F6 = array:get(6, Fields),
      ?assertEqual(true, F5#field.has_mine),
      ?assertEqual(false, F6#field.has_mine),
      ?assertEqual(2, nbr(mines, F5, Fields)),
      ?assertEqual(5, nbr(mines, F6, Fields)).
  
  step_on_empty_test() ->
      Board = new_game(3, 4, 5, [2,3,5,7,9]),
      {Events, #board{n_hidden = NHidden, fields=NewFields}} = step({1,2}, Board),
      ?assertEqual(11, NHidden),
      ?assertMatch([{uncover, 6, {empty, 5}}], Events),
      ?assertMatch(#field{status=uncovered}, array:get(6, NewFields)).
  
  step_on_mine_test() ->
      Board = new_game(3, 4, 5, [2,3,5,7,9]),
      {Events, #board{n_hidden = NHidden}} = step({0,2}, Board),
      ?assertEqual(0, NHidden),
      ?assertMatch([game_over,
                    {uncover, 11, {empty, 1}},
                    {uncover, 10, {empty, 3}},
                    {uncover,  9, mine},
                    {uncover,  8, {empty, 2}},
                    {uncover,  7, mine},
                    {uncover,  6, {empty, 5}},
                    {uncover,  5, mine},
                    {uncover,  4, {empty, 2}},
                    {uncover,  3, mine},
                    {uncover,  1, {empty, 2}},
                    {uncover,  0, {empty, 1}},
                    {exploded, 2}], Events).
  
  step_on_hole_test() ->
      %%  *0, 1,*2,*3
      %%   4, 5, 6, 7
      %%  *8, 9,10,11
      %%  12,13,14,15
      Board = new_game(4, 4, 4, [0,2,3,8]),
      {Events, #board{n_hidden = NHidden}} = step({2,2}, Board),
      ?assertEqual(7, NHidden),
      ?assertMatch([{uncover, 15, {empty, 0}},
                    {uncover, 13, {empty, 1}},
                    {uncover, 14, {empty, 0}},
                    {uncover, 11, {empty, 0}},
                    {uncover,  9, {empty, 1}},
                    {uncover,  7, {empty, 2}},
                    {uncover,  6, {empty, 2}},
                    {uncover,  5, {empty, 3}},
                    {uncover, 10, {empty, 0}}], Events).
  
  step_on_hole2_test() ->
      %%  *0, 1,*2,*3
      %%   4, 5, 6, 7
      %%  *8, 9,10,11
      %%  12,13,14,15
      %%  16,17,18,19
      Board = new_game(5, 4, 4, [0,2,3,8]),
      {Events, #board{n_hidden = NHidden}} = step({2,2}, Board),
      ?assertEqual(6, NHidden),
      ?assertMatch([{uncover, 19, {empty, 0}},
                    {uncover, 16, {empty, 0}},
                    {uncover, 12, {empty, 1}},
                    {uncover, 17, {empty, 0}},
                    {uncover, 18, {empty, 0}},
                    {uncover, 15, {empty, 0}},
                    {uncover, 13, {empty, 1}},
                    {uncover, 14, {empty, 0}},
                    {uncover, 11, {empty, 0}},
                    {uncover,  9, {empty, 1}},
                    {uncover,  7, {empty, 2}},
                    {uncover,  6, {empty, 2}},
                    {uncover,  5, {empty, 3}},
                    {uncover, 10, {empty, 0}}], Events).
  
  generate_mines_test() ->
      ?assertMatch([_,_,_], generate_mines(2*2, 3)),
      ?assertMatch([_,_,_,_,_], generate_mines(3*4, 5)),
      ?assertMatch(badarg, catch generate_mines(3*4, 12)).
  
  seq2pos_test() ->
      ?assertEqual({0,0}, seq2pos(0, {3,4})),
      ?assertEqual({0,1}, seq2pos(1, {3,4})),
      ?assertEqual({0,2}, seq2pos(2, {3,4})),
      ?assertEqual({0,3}, seq2pos(3, {3,4})),
      ?assertEqual({1,0}, seq2pos(4, {3,4})),
      ?assertEqual({1,1}, seq2pos(5, {3,4})),
      ?assertEqual({1,2}, seq2pos(6, {3,4})),
      ?assertEqual({1,3}, seq2pos(7, {3,4})),
      ?assertEqual({2,0}, seq2pos(8, {3,4})),
      ?assertEqual({2,1}, seq2pos(9, {3,4})),
      ?assertEqual({2,2}, seq2pos(10, {3,4})),
      ?assertEqual({2,3}, seq2pos(11, {3,4})).
  
  pos2seq_test() ->
      ?assertEqual(0, pos2seq({0,0}, {3,4})),
      ?assertEqual(1, pos2seq({0,1}, {3,4})),
      ?assertEqual(2, pos2seq({0,2}, {3,4})),
      ?assertEqual(3, pos2seq({0,3}, {3,4})),
      ?assertEqual(4, pos2seq({1,0}, {3,4})),
      ?assertEqual(5, pos2seq({1,1}, {3,4})),
      ?assertEqual(6, pos2seq({1,2}, {3,4})),
      ?assertEqual(7, pos2seq({1,3}, {3,4})),
      ?assertEqual(8, pos2seq({2,0}, {3,4})),
      ?assertEqual(9, pos2seq({2,1}, {3,4})),
      ?assertEqual(10, pos2seq({2,2}, {3,4})),
      ?assertEqual(11, pos2seq({2,3}, {3,4})).
  
  neighbours_test() ->
      %% Field seq numbers:
  