function lights_out ( m, n, k )

%*****************************************************************************80
%
%% LIGHTS_OUT plays a game in which the user must put out all the lights.
%
%  Discussion:
%
%    A 5x5 board of squares is displayed, and each square contains a light
%    which has randomly been set on or off.
%
%    The player's task is to put all the lights out.  
%
%    Pressing any square switches the status of the light in that square,
%    from ON to OFF, or OFF to ON.  However, the same press also switches
%    the status of the lights immediately above, below, to the left and
%    right of the selected square.
%
%    This makes the task difficult! 
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Marlow Anderson, Todd Feil,
%    Turning Lights Out With Linear Algebra,
%    Mathematics Magazine,
%    Volume 71, Number 4, October 1998, pages 300-303.
%
%    Oscar Martin-Sanchez,
%    Two Analyses of Lights Out,
%    Mathematics Magazine,
%    Volume 74, Number 4, October 2001, pages 295-304.
%
%  Parameters:
%
%    Optional input, integer M, N, the number of rows and columns.
%    Default values are 5 and 5.
%
%    Optional input, integer K, the number of times a random light should
%    be pushed.  Default value is 5.
%
  if ( nargin < 3 )
    k = 5;
  elseif ( ischar ( k ) )
    k = str2num ( k );
  end

  if ( nargin < 2 )
    n = 5;
  elseif ( ischar ( n ) )
    n = str2num ( n );
  end

  if ( nargin < 1 )
    m = 5;
  elseif ( ischar ( m ) )
    m = str2num ( m );
  end
%
%  Initialize the puzzle.
%
  L = lo_initialize ( m, n, k );
%
%  Show the board, let the user select a light.
%
  push = 0;
  while ( true )

    lo_display ( m, n, L, push );

    if ( sum ( sum ( L ) ) == 0 )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'You won with %d pushes.\n', push );
      break
    end

    [ i, j ] = lo_push ( m );

    if ( i < 1 | m < i | j < 1 | n < j )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'You gave up after %d pushes.\n', push );
      break;
    end

    push = push + 1;
    L = lo_update ( m, n, L, i, j );

  end

  return
end
function L = lo_initialize ( m, n, k )

%*****************************************************************************80
%
%% LO_INITIALIZE initializes the Lights Out status.
%
%  Discussion:
%
%    A random configuration of lights might not be solvable.
%    This function produces a solvable problem by starting with a blank
%    screen and pressing a random light K times.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, integer K, the number of times a random light should
%    be pushed.
%
%    Output, logical L(M,N), true where lights are on.
%
  L = zeros ( m, n );
  for step = 1 : k
    i = randi ( [ 1, m ] );
    j = randi ( [ 1, n ] );
    L = lo_update ( m, n, L, i, j );
  end

  return
end
function [ i, j ] = lo_push ( m );

%*****************************************************************************80
%
%% LO_PUSH records the user's push.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the number of rows.
%
%    Output, integer I, J, the row and column selected by the user.
%
  [ x, y ] = ginput ( 1 );

  j = ceil ( x );
  i = m + 1 - ceil ( y );
  
  return
end
function L = lo_update ( m, n, L, i, j );

%*****************************************************************************80
%
%% LO_UPDATE updates the Lights Out status after (I,J) is pushed.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, logical L(M,N), true where lights are on.
%
%    Input, integer I, J, the row and column of the light that was
%    pushed.
%
%    Output, logical L(M,N), the updated Lights Out status.
%
  L(i,j) = ~ L(i,j);

  if ( 1 < i )
    L(i-1,j) = ~ L(i-1,j);
  end
  if ( i < m )
    L(i+1,j) = ~ L(i+1,j);
  end
  if ( 1 < j )
    L(i,j-1) = ~ L(i,j-1);
  end
  if ( j < n )
    L(i,j+1) = ~ L(i,j+1);
  end

  return
end
function lo_print ( m, n, L, push );

%*****************************************************************************80
%
%% LO_PRINT prints the current Lights Out status.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, logical L(M,N), true where lights are on.
%
%    Input, integer PUSH, the number of times the user has pushed a light.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'Lights Out!  %d pushes\n', push );
  fprintf ( 1, '\n' );

  for i = 1 : m
    for j = 1 : n
      if ( L(i,j) )
        fprintf ( 1, ' *' );
      else
        fprintf ( 1, ' .' );
      end
    end
    fprintf ( 1, '\n' );
  end

  return
end
function lo_display ( m, n, L, push );

%*****************************************************************************80
%
%% LO_DISPLAY displays the current Lights Out status.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, logical L(M,N), true where lights are on.
%
%    Input, integer PUSH, the number of times the user has pushed a light.
%

%
%  Draw the board of MxN gray squares, each of "radius" 0.45,
%  on a white background.
%
  radius = 0.45;
  white = 'w';
  gray = [ 0.9, 0.9, 0.9 ];
  board_ij_init ( m, n, radius, white, gray );
%
%  Draw a yellow circle in each square that is lit.
%
  yellow = [ 1.0, 1.0, 0.0 ];

  hold on

  for i = 1 : m
    for j = 1 : n
      if ( L(i,j) )
        circle_ij_fill ( m, i, j, radius, yellow )
      end
    end
  end

  hold off
  title_string = sprintf ( 'Lights Out! %d pushes', push );
  title ( title_string, 'Fontsize', 24 );

  return
end
function board_ij_init ( m, n, r, color_back, color_fore )

%*****************************************************************************80
%
%% BOARD_IJ_INIT initializes the board.
%
%  Discussion:
%
%    An MxN array of squares is drawn, each filled with the same color,
%    and each of "radius" r.  As long as r <- 0.5, the board will fit
%    within a square of dimensions [0,M]x[1,N].
%
%    Note that the unit cell (i,j), with radius 1/2, is assumed to be centered 
%    at (i-0.5,j-0.5), with vertices (i-1,j-1), (i,j-1), (i,j), (i-1,j).
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the height and width of the board, measured
%    in unit cells.
%
%    Input, real R, the radius of each cell.  A cell of radius 0.5 will
%    fill its unit square.  A cell of radius 0.45 will leave a margin
%    in which the board background color is visible.  R should not
%    exceed 0.5!
%
%    Input, MATLAB colors COLOR_BACK, COLOR_FORE.  These can be
%    any of the 8 abbreviated color terms 'r', 'g', 'b', 'c',
%    'm', 'y', 'w', 'k', or an RGB triple such as [1.0,0.4,0.0].
%    The board background is drawn with COLOR_BACK, and the board
%    cells are then drawn with COLOR_FORE.
%
  d = max ( 0.5 - r,  0.0 );

  fill ( [ -d, n+d, n+d, -d ], [ -d, -d, m+d, m+d ], color_back );

  hold on

  for i = 1 : m

    for j = 1 : n

      square_ij_fill ( m, i, j, r, color_fore );

    end
    
  end

  hold off

  axis equal
  axis off

  return
end
function circle_ij_fill ( m, i, j, r, color )

%*****************************************************************************80
%
%% CIRCLE_IJ_FILL plots a filled circle in cell (I,J).
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 October 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the number of rows in the matrix.
%
%    Input, real I, J, the indices of the cell.
%
%    Input, real R, the radius of the circle.
%
%    Input, MATLAB color COLOR, can be any of the 8 abbreviated color terms
%    'r', 'g', 'b', 'c', 'm', 'y', 'w', 'k', or an RGB triple such as 
%    [1.0,0.4,0.0].  The circle is filled with this color.
%
  a = linspace ( 0.0, 2.0 * pi, 25 );
  x =     j - 0.5 + r * cos ( a );
  y = m - i + 0.5 + r * sin ( a );

  fill ( x, y, color );

  return
end
function square_ij_fill ( m, i, j, r, color )

%*****************************************************************************80
%
%% SQUARE_IJ_FILL plots a filled square centered at Cell(I,J)
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 November 2017
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the number of rows in the matrix.
%
%    Input, integer I, J, the indices of the cell.
%
%    Input, real R, the radius of the square.
%
%    Input, MATLAB color COLOR, can be any of the 8 abbreviated color terms
%    'r', 'g', 'b', 'c', 'm', 'y', 'w', 'k', or an RGB triple such as 
%    [1.0,0.4,0.0].  The circle is filled with this color.
%
  xc = j - 0.5;
  yc = m - i + 0.5;

  a = xc - r;
  b = xc + r;
  c = yc - r;
  d = yc + r;

  fill ( [ a, b, b, a ], [ c, c, d, d ], color );

  return
end
