
--
-- Copyright (C) 2023  <fastrgv@gmail.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You may read the full text of the GNU General Public License
-- at <http://www.gnu.org/licenses/>.
--


-- Brute-Force Sudoku-X solver




with text_io;
with ada.integer_text_io;
with ada.command_line;



procedure susolvex is
	use text_io;
	use ada.integer_text_io;
	use ada.command_line;

	package myint_io is new text_io.integer_io(integer);

	subtype i9 is integer range 1..9;
	subtype s9 is integer range 0..9;
	-- zero indicates indeterminate digit

	type flagtype is array(i9) of boolean;
	flags: flagtype;

	type m9type is array(i9,i9) of s9;

	m: m9type;

	row, col: s9 := 0;


procedure output is
	mm: s9;
begin

if row>0 and col>0 then --show only m(row,col)

	put_line("-----------------------------------------------");
	for r in i9 loop
		for c in i9 loop
			if r=row and c=col then
				mm:=m(r,c);
			else
				mm:=0;
			end if;
			if c=4 or c=7 then put(" "); end if;
			put(integer'image(mm));
		end loop;
		new_line;
		if r=3 or r=6 then new_line; end if;
	end loop;
	put_line("-----------------------------------------------");


else --show whole solution

	put_line("-----------------------------------------------");
	for r in i9 loop
		for c in i9 loop
			mm:=m(r,c);
			if c=4 or c=7 then put(" "); end if;
			put(integer'image(mm));
		end loop;
		new_line;
		if r=3 or r=6 then new_line; end if;
	end loop;
	put_line("-----------------------------------------------");

end if;

end output;



procedure input(m: out m9type) is
	ifil: file_type;
begin


	if Ada.Command_Line.Argument_Count = 3 then
		-- filename + row + col to reveal

		declare
		lst: natural;
      fstr : string := Ada.Command_Line.Argument(1);--File
		rstr : string := Ada.Command_Line.Argument(2); --row
		cstr : string := Ada.Command_Line.Argument(3); --col
		begin

			text_io.open(ifil, in_file, fstr);

			for r in i9 loop
			for c in i9 loop
				get(ifil,m(r,c)); --integer_text_io
			end loop;
			end loop;

			text_io.close(ifil);

			output;

			myint_io.get(rstr,row,lst);
			myint_io.get(cstr,col,lst);

		end; --declare



	elsif Ada.Command_Line.Argument_Count = 1 then

		declare
      fstr : string := Ada.Command_Line.Argument(1);--File
		begin

			text_io.open(ifil, in_file, fstr);

			for r in i9 loop
			for c in i9 loop
				get(ifil,m(r,c)); --integer_text_io
			end loop;
			end loop;

			text_io.close(ifil);

		end; --declare

		output;

	else
		put_line("FileName expected!");
		raise program_error;
	end if;

end input;




function badBlock(r,c:i9) return boolean is
	mm,r0,c0: s9;
begin

	if r<=3 then r0:=0;
	elsif r<=6 then r0:=3;
	else r0:=6; end if;
	if c<=3 then c0:=0;
	elsif c<=6 then c0:=3;
	else c0:=6; end if;

	flags := (others=>false);

	for r in r0+1..r0+3 loop
	for c in c0+1..c0+3 loop

		mm := m(r,c);
		if mm /= 0 then
			if flags(mm) = true then
				return true; --already used
			else
				flags(mm) := true; 
			end if;
		end if;

	end loop; --c
	end loop; --r

	return false;

end badBlock;


function badRow(r:i9) return boolean is
	mm: s9;
begin

	flags := (others=>false);
	for c in i9 loop
		mm := m(r,c);
		if mm /= 0 then
			if flags(mm) = true then --already used
				return true;
			else
				flags(mm) := true; 
			end if;
		end if;
	end loop;

	return false;

end badRow;


function badCol(c:i9) return boolean is
	mm: s9;
begin

	flags := (others=>false);
	for r in i9 loop
		mm := m(r,c);
		if mm /= 0 then
			if flags(mm) = true then --already used
				return true;
			else
				flags(mm) := true; 
			end if;
		end if;
	end loop;

	return false;

end badCol;


function badDiags return boolean is
	mm: s9;
begin

	--first: main diag:
	flags := (others=>false);
	for i in i9 loop
		mm := m(i,i);
		if mm /= 0 then
			if flags(mm) = true then --already used
				return true;
			else
				flags(mm) := true; 
			end if;
		end if;
	end loop;

	--second: back diag:
	flags := (others=>false);
	for i in i9 loop
		mm := m(i,10-i);
		if mm /= 0 then
			if flags(mm) = true then --already used
				return true;
			else
				flags(mm) := true; 
			end if;
		end if;
	end loop;

	return false;

end badDiags;


function done return boolean is
	mm: s9;
begin

	for r in i9 loop
	for c in i9 loop
		mm:=m(r,c);
		if mm=0 then
			return false;
		end if;
	end loop;
	end loop;

	return true;

end done;


procedure recurse(ifound: in out boolean) is
	skip,diag,quit: boolean := false;
begin
	--simple brute force...
	--for each zero [undetermined entry] 
	--in lexicographic order...
	--try 1..9 until it is ruled out.

	quit:=false;

if not ifound then

	for r in i9 loop
		for c in i9 loop

			if r=c then diag:=true;
			elsif r+c=10 then diag:=true;
			else diag:=false; end if;

			if m(r,c)=0 then
			-- this must be the next indeterminate
			-- in lexicographic order

				for x in i9 loop

					m(r,c):=x;

					skip:=badBlock(r,c) or badRow(r) or badCol(c);

					if diag and not skip then
						skip:=badDiags;
					end if;


					if not skip then

						if done then
							put("first solution");
							if row>0 and col>0 then
								put(" @("&integer'image(row)&","&integer'image(col)&" ):");
							else
								put(":");
							end if;
							new_line;
							output;
							quit:=true;
							ifound:=true;
						else
							recurse(ifound);
						end if;

					end if;

					exit when quit;

				end loop; -- tried 1..9 for m(r,c)

				m(r,c):=0; --this setup failed...i.e.
				--there was no value for m(r,c) that was valid
				quit:=true;

			end if;

			exit when quit;
		end loop; --c

		exit when quit;
	end loop; --r

end if; --not ifound

end recurse;

	found: boolean := false;
begin --susolvex

	new_line;
	put_line("input from file:");
	input(m);

	recurse(found);

	if not found then
		put_line("Not Solvable");
	end if;

end susolvex;
