program mancala; {*emulates the board game MANCALA. 1/8/95 *}

const
	NUMBINS=6;
	NUMSTONES=48; {*variation:NUMSTONES:=2*NUMBINS*(6, not 4) *}
	{*also of course lots of things need to be <32768 *}

type grid=array[1..2, 1..NUMBINS] of integer;
	pair=array[1..2] of integer;

{*variables and procedures constant across a game*}
var
	bins: grid;
	mancala, help: pair;
	searchlevel,player, opponent: integer;
	gameover: boolean;

procedure swapplayer;
begin
if player=1 then player:=2 else player:=1;
opponent:=3-opponent;
end;

procedure paws;
var i,j:integer;
begin
{*for i:=1 to 16000 do for j:=1 to 256 do begin end;*}
write('                                        [Hit RETURN when ready]');
readln;
end;

procedure makebox(i,j:integer);
{*make a box on the screen starting at column i, row j; width 4 *}
begin
gotoxy(i,j-1);write(#218);write(#196);write(#196);write(#196);write(#191);
gotoxy(i,j);write(#179);write('   ');write(#179);
gotoxy(i,j+1);write(#192);write(#196);write(#196);write(#196);write(#217);
gotoxy(i+2,j);
end;

procedure bigbox(i,j:integer);
{*make a tall box on the screen starting at column i, row j; width 4 *}
begin
gotoxy(i,j-3);write(#218);write(#196);write(#196);write(#196);write(#191);
gotoxy(i,j-2);write(#179);write('   ');write(#179);
gotoxy(i,j-1);write(#179);write('   ');write(#179);
gotoxy(i,j);write(#179);write('   ');write(#179);
gotoxy(i,j+1);write(#179);write('   ');write(#179);
gotoxy(i,j+2);write(#179);write('   ');write(#179);
gotoxy(i,j+3);write(#192);write(#196);write(#196);write(#196);write(#217);
gotoxy(i+2,j);
end;

procedure nicebin(i,c,r:integer);
begin
makebox(c-1,r);
write(i);
gotoxy(c+4,r);
end;

procedure nicebigbin(i,c,r:integer);
begin
bigbox(c-1,r);
write(i);
gotoxy(c+4,r);
end;

procedure niceint(i,c,r:integer);
begin
gotoxy(c,r);
write(i);
end;

procedure showstats;
var x,p,i: integer;
begin
ClrScr;
Gotoxy(14,1); write('Bins and mancala for player 1');
gotoxy(3,3);write('M');
nicebigbin(mancala[1],2,7);
for i:=1 to NUMBINS do begin
	nicebin(bins[1,NUMBINS+1-i],8*i+1,5);
	niceint(NUMBINS+1-i,8*i+2,3);
	end;
gotoxy(14, 13); write('Bins and mancala for player 2');
gotoxy(8*NUMBINS+10,11); write('M');
nicebigbin(mancala[2],8*NUMBINS+9,7);
for i:=1 to NUMBINS do begin
	nicebin(bins[2,i],8*i+1,9);
	niceint(i,8*i+2,11)
	end;
gotoxy(1,16);
writeln('Next play will be by player ', player,'.');writeln;
end;

procedure update(var board:grid; var manc:pair; p, c: integer; var outcome:integer);
{*updates board and manc(ala) when player p choses bin  c, assumed legit.
	outcome=0 if normal, 1 if extra turn, 2 if games ends, 4 if capture,
	6 if capture and game ends.*}

var i,o,x1,x2:integer;
begin
o:=3-p; {*opponent*}
outcome:=0;
i:=board[p,c];
board[p,c]:=0;
while i>0 do begin
	i:=i-1;
	c:=c+1; if c>2*NUMBINS+1 then c:=1;
	if c<=NUMBINS then board[p,c]:=board[p,c]+1;
	if c=NUMBINS+1 then manc[p]:=manc[p]+1;
	if c>NUMBINS+1 then board[o,c-NUMBINS-1]:=board[o,c-NUMBINS-1]+1;
	end;
if c=NUMBINS+1 then outcome:=1;
if (c<=NUMBINS) AND (board[p,c]=1) AND (board[o,NUMBINS+1-c]>0) then begin
	outcome:=4;
	manc[p]:=manc[p]+1+board[o, NUMBINS+1-c];
	board[p,c]:=0;
	board[o,NUMBINS+1-c]:=0;
	end;
x1:=0; for i:=1 to NUMBINS do x1:=x1+ board[1,i];
x2:=0; for i:=1 to NUMBINS do x2:=x2+ board[2,i];
if (x1=0) OR (x2=0) then begin
	outcome:=outcome+2;
	if outcome=3 then outcome:=2; {*no extra turns if game over!*}
	for i:=1 to NUMBINS do begin
		manc[1]:=manc[1]+board[1,i];
		manc[2]:=manc[2]+board[2,i];
		{*board[1,i]:=0; board[2,i]:=0;*}
		{*should not be necessary, except, say, to draw it*}
		end;
	end;
end;

procedure humanmoves;
var valid: boolean;
	i,choice, result: integer;
begin
valid:=FALSE;
while NOT valid do begin
	write('Which bin would you like to empty? ');
	readln(choice);
	if bins[player,choice]>0 then valid:=TRUE;
	end;
update(bins, mancala, player, choice, result);
if (result=4) OR (result=6) then begin
	writeln('You end in an otherwise empty bin of yours and get your opponents stones.');
	paws;
	end;
if (result=1) then begin
	writeln('You ended in your mancala so you get another turn.');
	paws;
	swapplayer;
	end;
if (result=2) OR (result=6) then begin
	writeln('Game ends when a player goes out.');
	gameover:=TRUE;
	end;
end;

function betterchoice(givengrid:grid;givenpair:pair;plyr,level:integer; var value:integer):integer;
var testgrid:grid; testmanc:pair; bc,c,cnext,i,r,bestsofar:integer;
begin
bestsofar:=-NUMSTONES-1;
for c:=1 to NUMBINS do if (givengrid[plyr,c]>0) AND (bestsofar<NUMSTONES) then begin
	for i:=1 to NUMBINS do testgrid[1,i]:=givengrid[1,i];
	for i:=1 to NUMBINS do testgrid[2,i]:=givengrid[2,i];
	for i:=1 to 2 do testmanc[i]:=givenpair[i];
	update(testgrid,testmanc,plyr,c,r);
	{*So that's what we'd have if we chose this move. Now assign a value to it: *}
	if (r=1) then begin
		cnext:=betterchoice(testgrid,testmanc,plyr,level, value);
		level:=-1;
		end;
	if level=0 then begin
		value:=-round(random(2*NUMSTONES+1)-NUMSTONES-0.5);
		end;
	if level=1 then begin
		value:=testmanc[plyr]-testmanc[3-plyr];
		if testmanc[plyr]>NUMSTONES/2 then value:=NUMSTONES;
		if testmanc[3-plyr]>NUMSTONES/2 then value:=-NUMSTONES;
		end;
	if level>1 then begin
		cnext:=betterchoice(testgrid,testmanc, 3-plyr,level-1, value);
		value:=-value;
		end;
	{*damage report:*}
	if (value>bestsofar) then begin
		bc:=c;
		bestsofar:=value;
		end;
	end;
{*run over all your original options. Then *}
if bestsofar<-NUMSTONES {*i.e. had no valid moves *} then begin
	bestsofar:=givenpair[plyr]-givenpair[3-plyr];
	if givenpair[plyr]>NUMSTONES/2 then bestsofar:=NUMSTONES;
	if givenpair[3-plyr]>NUMSTONES/2 then bestsofar:=-NUMSTONES;
	bc:=1; {*should be irrelevant*}
	end;
value:=bestsofar;
betterchoice:=bc;
end;

procedure computermoves;
var choice,i,result:integer;
begin
choice:=betterchoice(bins,mancala,player,searchlevel,i);
writeln('Computer player ',player,' empties bin ',choice);
update(bins, mancala, player, choice, result);
if (result=4) OR (result=6) then begin
	writeln('Computer ends in an otherwise empty bin of its own and get its opponents stones.');
	end;
if (result=1) then begin
	writeln('Computer ended in its mancala so it gets another turn.');
	swapplayer;
	end;
if (result=2) OR (result=6) then begin
	writeln('Game ends when a player goes out.');
	gameover:=TRUE;
	end;
paws;
end;

procedure taketurn;
begin
swapplayer;
showstats;
if help[player]=1 then computermoves else humanmoves;
end;

{*****************main loop routines follow*****************}

procedure setup;
var handsize, i, j:integer;
begin
ClrScr;
writeln('This is a computer modelling of the game MANCALA, distributed by');
writeln('Great American Trading Company. Computer adaptation is by Dave Rusin 1/9/95.');
writeln('The simulation is faithful to the rules (main variant).');
writeln;
writeln('Hit conrtol-C to abort.');
writeln;
;
for i:=1 to 2 do help[i]:=0;
writeln('Would you like the computer to think for (some) players? ');
write('How many? (0 thru 2): ');readln(player);writeln;
if (player<0) OR (player>2) then begin
	writeln('I see you can not read. Computer will be player 1 only.');
	player:=1;
	end;
for i:=1 to player do help[i]:=1;
searchlevel:=0;
if player>0 then begin
	searchlevel:=0;
	write('How many moves ahead should the computer think? (0 through 5): ');
	readln(searchlevel);
	end;
if searchlevel<0 then searchlevel:=0;
if searchlevel>6 then writeln('I hope you have a really fast machine.');
handsize:=round(NUMSTONES/(2*NUMBINS));
{* i.e. 4. Rules suggest using handsize:=6 for variation*}
for i:=1 to 2 do for j:=1 to NUMBINS do bins[i,j]:=handsize;
for i:=1 to 2  do mancala[i]:=0;
player:=2;
opponent:=1;
gameover:=FALSE;
if help[1]=1 then begin
	writeln('Computer player 1 will begin.');
	paws;
	end;
end;

procedure announce;
var i: integer;
begin
writeln('Player 1 has ',mancala[1],' stones;');
writeln('Player 2 has ',mancala[2],' stones;');
if mancala[1]=mancala[2] then writeln('Tie Game!') else begin
	write('Winner is player ');
	if mancala[1]>mancala[2] then writeln('1') else writeln('2');
	end;
paws;
end;

begin {*main loop*}
setup;
while NOT gameover do taketurn;
announce;
end.

{*
Rules synopsis:
Game is played with 48 stones and a board with 14 bins: 2 rows of 6 with
a large one at each end. Game is for two players taking turns moving stones.
The goal is to collect the greater number of stones by the time one player
has cleared all his stones.
Play begins with 4 stones in each small pocket. One row of 6 bins and the
large bin to their right are assigned to each player. (The large bin is the
player's _mancala_.) Players take turns moving stones until one of them has
removed all stones from his small bins; the opponent them moves all 
remaining stones fron his small bins to his mancala; the player with the
larger number of stones is the winner.
When a player is to take his turn, he selects one of non-empty his small bins,
removes all the stones in it, and places them back on the board in the 
following manner: view the collection of bins as a circle, including all 12
small bins and the player's mancala, but not the opponent's mancala; then
the "next" bin will mean the one (in the circle of 13) which is to the right
of (counterclockwise from) the current bin. With this notation, player
places one stone in the next bin after the one from which they were taken,
another stone in the next bin after that, and so on until all the stones are
placed. Usually the turn ends here. There are two exceptions:
If the last stone is placed in the player's mancala, the player gets
another turn. 
If the last stone is placed in one of the player's small bins,
and that bin is otherwise empty, that stone _and_ all stones in the bin
opposite this bin are placed in the player's mancala, and the turn ends.
(Rules unclear here: I assume if there's nothing to capture, you don't.)

*}

