[comp.lang.forth] How do I do this in FORTH?

schmidtg@iccgcc.decnet.ab.com (10/16/90)

I'm new to this news group and I'm also relatively new at writing
non-trivial Forth programs.  I have written a 3-D Tic-Tac-Toe playing
program in 'c' which I am now trying to convert to Forth.  All has
been going smoothly so far until attempting to write the minimax
routine in Forth.  The minimax routine is responsible for performing
a depth first search and is therefore recursive.  The current 'c'
implementation requires three arguments along with four automatic
variables which constitute a new stack frame for each recursive
invocation.  The problem I am encountering in Forth is the difficulty
of accessing seven variables on the stack while still using the
stack for intermediate results.  Forth seems to do well when the
number of items on the stack is no greater than four.  It seems
like my attempts so far have resulted in very awkward code.  I'm
looking for ideas/solutions to this problem.  I don't see how this
routine could be easily factored to resolve this problem, especially
since it is recursive.  Also, using variables is out of the question
as context must be preserved during recursion.  I have considered
defining some scheme for maintaining a "frame pointer" and allowing me
to reference variables off the stack symbolically.  This seems like
overkill, and would probably be very inefficient.  I seem to be at a
roadblock here and I wonder if I have just managed to find an application
that Forth is not very good at.  All ideas are welcome.

The "minimax" routine as written in 'c'...


int minimax(level,alpha,beta,pbest_move)
int level;
int alpha, beta;
int *pbest_move;
{
	int temp_move, the_move;
	int temp_score;
	int node_type;

	temp_move = -1;
	if ( ((temp_move = next_move(temp_move)) < 0)
		|| level == MAX_LEVEL
		|| win_check() )
			node_type = TERM;
	else
	{
		if (level & 1) /* odd ply */
			node_type = MIN;
		else /* even ply */
			node_type = MAX;
	}

	if (node_type == TERM) /* terminal node */
		return(static_eval());
	else
	{
		do {
			make_move(level,temp_move);
			temp_score = minimax(level+1,alpha,beta,&the_move);
			unmake_move(temp_move);
			if (node_type == MAX)
			{
				if (temp_score > alpha)
				{
					alpha = temp_score;
					*pbest_move = temp_move;
				}
			}
			else
			{
				if (temp_score < beta)
				{
					beta = temp_score;
					*pbest_move = temp_move;
				}
			}

		} while ( ((temp_move = next_move(temp_move)) > 0)
			&& alpha < beta);
		if (alpha >= beta)
		{
			if (node_type == MAX)
				a_cut++;
			else
				b_cut++;
		}
		if (node_type == MAX)
			return(alpha);
		else
			return(beta);
	}
}

-------------------------------------------------------------------------
| Greg Schmidt				| Not much of a .sig right now! |
| schmidtg@iccgcc.decnet.ab.com		|                               |
-------------------------------------------------------------------------

-- 


=============================================================================
Disclaimer: If I ever had a truly original idea I wouldn't share it with you!
-----------------------------------------------------------------------------
"People with nothing to hide have nothing to fear from O.B.I.T"
	-- Peter Lomax
=============================================================================
Greg Schmidt -> schmidtg@iccgcc.decnet.ab.com
=============================================================================

mwnewman@watmsg.waterloo.edu (mike newman) (10/19/90)

In article <1450.2719bf62@iccgcc.decnet.ab.com> you write:
>
>I'm new to this news group and I'm also relatively new at writing
>non-trivial Forth programs.  I have written a 3-D Tic-Tac-Toe playing
>program in 'c' which I am now trying to convert to Forth.  All has
>been going smoothly so far until attempting to write the minimax
>routine in Forth.

Intereseting you should mention this.  I'm doing exactly the same
thing with an othello playing program.  I haven't gotten very far, as
I only last week found a un*x forth to run.  The way I think I'll
handle it is to define a seperate stack for alpha, beta, depth.
Set it up analogously to the return stack, ie: define words like
alpha-pop, alpha-push, alpha@  <==>  r>, >r, r@

something like:

variable alpha-stack 100 allot
variable alpha-pointer alpha-stack alpha-pointer !

( stack growing upwards - why not ? )
: alpha-pop alpha-stack @ 4 ( or 2 for 16-bit forths ) alpha-pointer -! ;
: alpha-push 4 alpha-pointer +! alpha-stack ! ;

: alpha@ alpha-stack @ ;
: alpha-drop 4 alpha-pointer -! ;

Now this looks like it might involve a lot of overhead, BUT: code
these in assembly language.  The only overhead is fetching and storing
the stack pointers.  And you could even keep these stack
pointers in registers and kill that little bit of overhead too.

code alpha-pop
	move	alpha-pointer,r0
	move	(r0)+,-(sp)
	move	r0,alpha-pointer
	c;

better yet: (better for speed...)

code alpha-pop
	move	(r57)+,-(sp)	; r57 is reserved for alpha stack-pointer
	c;

Note that this last is likely faster (depending on the machine) then a
normal variable "@", or a frame pointer approach, since you don't have
any literal offset to deal with.  Of course this assumes that there
are free registers, that are not being used by anything else.  Then
again, depending on the machine, the first may reduce to a single
instruction too.


I can't think of any other efficient way to do this, but then I often can't
think of what day it is today :-)

Hope this helps.  I'd be interested in hearing about any other
suggestions you get.

mike newman
mwnewman@msg.waterloo.edu