The game

Given an   N×N   square array of zeroes or ones in an initial configuration,   and a target configuration of zeroes and ones.



The game is to transform one to the other in as few moves as possible by inverting whole numbered rows or whole lettered
columns at once   (as one move).

In an inversion.   any  1  becomes  0,   and any  0  becomes  1  for that whole row or column.



Task

Create a program to score for the Flipping bits game.

 The game should create an original random target configuration and a starting configuration.
 Ensure that the starting position is never the target position.
 The target position must be guaranteed as reachable from the starting position.   (One possible way to do this is to generate the start position by legal flips from a random target position.   The flips will always be reversible back to the target from the given start position).
 The number of moves taken so far should be shown.


Show an example of a short game here, on this page, for a   3×3   array of bits.




with Ada.Text_IO, Ada.Command_Line, Ada.Numerics.Discrete_Random;
 
procedure Flip_Bits is
 
   subtype Letter is Character range 'a' .. 'z';
 
   Last_Col: constant letter := Ada.Command_Line.Argument(1)(1);
   Last_Row: constant Positive := Positive'Value(Ada.Command_Line.Argument(2));
 
   package Boolean_Rand is new Ada.Numerics.Discrete_Random(Boolean);
   Gen: Boolean_Rand.Generator;
 
   type Matrix is array
     (Letter range 'a' .. Last_Col, Positive range 1 .. Last_Row) of Boolean;
 
   function Rand_Mat return Matrix is
      M: Matrix;
   begin
      for I in M'Range(1) loop
	 for J in M'Range(2) loop
	    M(I,J) := Boolean_Rand.Random(Gen);
	 end loop;
      end loop;
      return M;
   end Rand_Mat;
 
   function Rand_Mat(Start: Matrix) return Matrix is
      M: Matrix := Start;
   begin
      for I in M'Range(1) loop
	 if  Boolean_Rand.Random(Gen) then
	    for J in M'Range(2) loop
	       M(I,J) := not M(I, J);
	    end loop;
	 end if;
      end loop;
      for I in M'Range(2) loop
	 if  Boolean_Rand.Random(Gen) then
	    for J in M'Range(1) loop
	       M(J,I) := not M(J, I);
	    end loop;
	 end if;
      end loop;
      return M;
   end Rand_Mat;
 
   procedure Print(Message: String; Mat: Matrix) is
      package NIO is new Ada.Text_IO.Integer_IO(Natural);
   begin
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put_Line(Message);
      Ada.Text_IO.Put("   ");
      for Ch in Matrix'Range(1) loop
	 Ada.Text_IO.Put(" " & Ch);
      end loop;
      Ada.Text_IO.New_Line;
      for I in Matrix'Range(2) loop
	 NIO.Put(I, Width => 3);
	 for Ch in Matrix'Range(1) loop
	    Ada.Text_IO.Put(if Mat(Ch, I) then " 1" else " 0");
	 end loop;
         Ada.Text_IO.New_Line;
      end loop;
   end Print;
 
   Current, Target: Matrix;
   Moves: Natural := 0;
 
begin
   -- choose random Target and start ("Current") matrices
   Boolean_Rand.Reset(Gen);
   Target := Rand_Mat;
   loop
      Current := Rand_Mat(Target);
      exit when Current /= Target;
   end loop;
   Print("Target:", Target);
 
   -- print and modify Current matrix, until it is identical to Target
   while Current /= Target loop
     Moves := Moves + 1;
     Print("Current move #" & Natural'Image(Moves), Current);
      Ada.Text_IO.Put_Line("Flip row 1 .." & Positive'Image(Last_Row) &
			     " or column 'a' .. '" & Last_Col & "'");
      declare
	 S: String := Ada.Text_IO.Get_Line;
	 function Let(S: String) return Character is (S(S'First));
	 function Val(Str: String) return Positive is (Positive'Value(Str));
      begin
	 if Let(S) in 'a' .. Last_Col then
	    for I in Current'Range(2) loop
	       Current(Let(S), I) := not Current(Let(S), I);
	    end loop;
	 else 
	    for I in Current'Range(1) loop
	       Current(I, Val(S)) := not Current(I, Val(S));
	    end loop;
	 end if;
      end;
   end loop;
 
   -- summarize the outcome
   Ada.Text_IO.Put_Line("Done after" & Natural'Image(Moves) & " Moves.");
end Flip_Bits;