1BSD/tests/insan.p

program insane(input, output);
label
	1;
type
	alfa = packed array[1..10] of char;
	face = (front, back, top, bottom, left, right);
	pair = (one2, three4, five6);
	color = (red, blue, green, white);
	blockno = 1..4;
var
	nosolutions: Boolean;
	index, halfindex: integer;
	pointr: integer;
	data: array[blockno, face] of alfa;
	sum: array[blockno, pair, color] of integer;
	halfsolution: array[blockno, 1..30] of pair;

function word(alf: alfa): color;
begin
	if alf = 'red' then
		word := red else
	if alf = 'blue' then
		word := blue else
	if alf = 'green' then
		word := green else
		word := white;
end;

procedure readin;
var
	hue: alfa;
	ch: char;
	cube: blockno;
	position: face;

procedure tone;
begin
	case ch of
	'r': hue := 'red';
	'w': hue := 'white';
	'g': hue := 'green';
	'b': hue := 'blue';
	end;
end;

begin
	for cube := 1 to 4 do
	begin
		for position := front to right do
		begin
			read(ch);
			tone;
			data[cube, position] := hue;
		end;
		readln;
	end;
end;

procedure sumcolors;
var
	cube: blockno;
	side: face;
function facepair(aface: face): pair;
begin
	case aface of
	front, back: facepair := one2;
	top, bottom: facepair := three4;
	left, right: facepair := five6
	end;
end;

procedure initializesum;
var
	cube: blockno;
	side: face;
	technicolor: color;
begin
	for cube := 1 to 4 do
		for side :=  front to right do
			for technicolor := red to white do
				sum[cube, facepair(side), technicolor] := 0;
end;

begin
	initializesum;
	for cube := 1 to 4 do
		for side := front to right do
			sum[cube, facepair(side), word(data[cube,side])] :=
			sum[cube, facepair(side), word(data[cube,side])] + 1;
end;

procedure find2222;
var
	subtotals: array[red..white] of integer;
	pair1, pair2, pair3, pair4: pair;

function two222(pair1, pair2, pair3, pair4: pair): Boolean;
var
	hue: color;
begin
	for hue := red to white do
		subtotals[hue] :=
			sum[1, pair1, hue]+
			sum[2, pair2, hue]+
			sum[3, pair3, hue]+
			sum[4, pair4, hue];
	if (subtotals[red]=2) and
	   (subtotals[blue]=2) and
	   (subtotals[green]=2) and
	   (subtotals[white]=2) then
		two222 := true else
		two222 := false;
end;

procedure listsolution;
begin
	halfsolution[1, halfindex] := pair1;
	halfsolution[2, halfindex] := pair2;
	halfsolution[3, halfindex] := pair3;
	halfsolution[4, halfindex] := pair4;
	halfindex := halfindex + 1;
end;

begin
	halfindex := 1;
	for pair1 := one2 to five6 do
	for pair2 := one2 to five6 do
	for pair3 := one2 to five6 do
	for pair4 := one2 to five6 do
		if two222(pair1, pair2, pair3, pair4) then
			listsolution;
	if halfindex <= 2 then
	begin
		nosolutions := true;
		goto 1;
	end;
end;

procedure simultaneous;
var
	done: Boolean;
begin
	nosolutions := false;
	pointr := 0;
	done := false;
	repeat
		pointr := pointr + 1;
		repeat
			index := succ(pointr);
			if (halfsolution[1, pointr]<>halfsolution[1,index]) and
			   (halfsolution[2, pointr]<>halfsolution[2,index]) and
			   (halfsolution[3, pointr]<>halfsolution[3,index]) and
			   (halfsolution[4, pointr]<>halfsolution[4,index]) then
				done := true else
				index := index + 1;
		until done or (index = pred(halfindex));
	until done or (pointr = halfindex);
	if pointr = halfindex then
	begin
		nosolutions := true;
		goto 1;
	end;
end;

procedure rearrange;
var
	box: blockno;
	a, b: pair;

procedure put(a, b: pair);
var
	old1, new1, old2, new2: face;
	save1, save2: alfa;

procedure oldpair(c: pair);
begin
	case c of
	one2:
		begin
			old1 := front;
			old2 := back;
		end;
	three4:
		begin
			old1 := top;
			old2 := bottom;
		end;
	five6:
		begin
			old1 := left;
			old2 := right;
		end
	end;
end;
procedure newpair(d: pair);
begin
	oldpair(b);
	new1 := old1;
	new2 := old2;
end;

begin
	newpair(b);
	oldpair(a);
	save1 := data[box, new1];
	data[box, new1] := data[box, old1];
	data[box, old1] := save1;
	save2 := data[box, new2];
	data[box, new2] := data[box, old2];
	data[box, old2] := save2;
end;

begin
	for box := 1 to 4 do
	begin
		a := halfsolution[box, pointr];
		b := halfsolution[box, index];
		if (a=one2) and (b=five6) then
			put(five6, three4) else
		begin
			if a = three4 then
			begin
				if b = one2 then
				begin
					put(one2, five6);
					put(three4, one2);
					put(five6, three4);
				end else
				begin
					put(three4, one2);
					put(five6, three4);
				end
			end else
			if b = one2 then
			begin
				put(one2, three4);
				put(five6, one2);
			end else
				put(five6, one2);
		end;
	end;
end;

procedure correct;
var
	list: array[1..8] of integer;
	done: Boolean;
	side: face;
	counter: integer;

procedure check;
var
	delux: array[red..white] of integer;
	kolor: color;
	counter: integer;
begin
	done := true;
	for kolor := red to white do
		for counter := 1 to 4 do
			delux[kolor] := 0;
	for counter := 1 to 4 do
	begin
		delux[word(data[counter,side])] :=
		delux[word(data[counter,side])] + 1;
		if delux[word(data[counter,side])] >= 2 then
			done := false;
	end;
end;

procedure rotate;
var
	save: alfa;
	opposite: face;
begin
	if side = back then
		opposite := front else
	if side = front then
		opposite := back else
	if side = top then
		opposite := bottom else
	if side = bottom then
		opposite := top;
	save := data[list[counter], side];
	data[list[counter], side] := data[list[counter], opposite];
	data[list[counter], opposite] := save;
end;

begin
	list[1] := 4;
	list[2] := 3;
	list[3] := 4;
	list[4] := 2;
	list[5] := 4;
	list[6] := 3;
	list[7] := 4;
	list[8] := 3;
	for side := back to top do
	begin
		counter := 0;
		check;
		while not done do
		begin
			counter := counter + 1;
			rotate;
			check;
		end;
	end
end;

procedure printout;
var
	space: integer;
	cube: integer;
	side: face;
begin
	if nosolutions then
		writeln('no solutions') else
	begin
		writeln('solution to instant insanity');
		for cube := 1 to 4 do
		begin
			write(cube, '   ');
			for side := front to bottom do
				write(data[cube, side]);
			writeln;
		end;
	end;
end;

begin
	reset(input, 'insan.d');
	readin;
	sumcolors;
	find2222;
	simultaneous;
	rearrange;
	correct;
1:
	printout;
end.
{
wbggrb
wbrgrr
wbgwrg
wrgwbr
}