From ok@atlas.otago.ac.nz  Wed Aug 30 03:31:10 2000
Received: from atlas.otago.ac.nz (atlas.otago.ac.nz [139.80.32.250])
	by swi.psy.uva.nl (8.9.3/8.9.3) with ESMTP id DAA27947
	for <prolog@swi.psy.uva.nl>; Wed, 30 Aug 2000 03:31:08 +0200 (MET DST)
Received: (from ok@localhost)
	by atlas.otago.ac.nz (8.9.3/8.9.3) id NAA11474;
	Wed, 30 Aug 2000 13:30:04 +1200 (NZST)
Date: Wed, 30 Aug 2000 13:30:04 +1200 (NZST)
From: "Richard A. O'Keefe" <ok@atlas.otago.ac.nz>
Message-Id: <200008300130.NAA11474@atlas.otago.ac.nz>
To: Randy.Justice@cnet.navy.mil, prolog@swi.psy.uva.nl
Subject: Re:  Dynamic Declaration of Atoms

	I have a structure called deck ( a simple order deck of card)
	
	/* Structure of Deck. 
	   Face Card.
	   Suit.
	   Numeric value.
	*/
	
	deck([[a,s,1],[2,s,2],[3,s,3],[4,s,4],....]).
	
I should warn you that this is a poor data structure design:
 - it is space inefficient
 - it is time inefficient
 - it is hard for people to read
 - it is very easy to get wrong.

If you want to represent the Face, Suit, and Rank of a card,
use a single term
	card(Face,Suit,Rank)

A list of cards is indeed a good representation for a deck:

	[card(a,s,1), card(2,s,2), card(3,s,3), card(4,s,4), ...]

If you have occasion to work with lots and lots of decks, you might
care to exploit the fact that cards don't change their faces, suits,
or ranks, and have a set of facts

    % card(Name, Face, Suit, Rank): Name <-> Face Suit Rank
    card(sa, a, s,  1).
    card(s2, 2, s,  2).
    ...
    card(st, t, s, 13).
    ...
    card(ct, t, c, 52).

and then you can represent a deck state as a list like

	[sa, s2, s3, s4, ... ]


	I want to create a rule to randomly pull a card from the deck
	and place it in a stack/pile (there are 7 stacks/pile).
	
	It's not a problem to get prolog to get a random card.  I
	created a rule called "get_card".  My problem is how to create
	dynamic "stack/pile" where is visible outside the rule.  I want
	anothor rule to see the "stack/pile".
	
It sounds as though you are trying to do things by changing the
data base.  There are times when that is appropriate, but there are
*vastly* fewer such times than beginners imagine.

By the way "get_card" is a rather poor name in any programming
language.  The word "get" conveys very little information.
In this case, the public Prolog library has for 20 years contained

    select(Item, ListWithItem, ListSansItem) :-
	/* Specification, NOT implementation */
	append(A, [Item|Z], ListWithItem),
	append(A,       Z , ListSansItem).

It is ******EXTREMELY****** annoying that SWI Prolog has for who
knows what reason incompatibly permuted the arguments of this predicate.
If it is important enough to build in, it is important enough to build
in *right*.


You want a random version of this.  If you're willing to put up with
having the random number generator state mutating away in the data base,
you might like to have a predicate like

    select_random(Item, ListWithItem, ListSansItem) :-
	random_list_index(ListWithItem, I),
	select_nth0(I, Item, ListWithItem, ListSansItem).
	
    random_list_index(List, I) :-
	length(List, N),
	random_integer(N, I).

    select_nth0(Index, Item, ListWithItem, ListSansItem) :-
	(   integer(Index) ->
	    Index >= 0,
	    select_nth0_i_(Index, Item, ListWithItem, ListSansItem)
	;   var(Index) ->
	    select_nth0_v_(Index, Item, ListWithItem, ListSansItem, 0)
	;   abort % should report domain failure
	).

    select_nth0_i_(N, Item, [Head|With], Sans) :-
	(   N =:= 0 ->
	    Item = Head, Sans = With
	;   M is N - 1,
	    Sans = [Head|Sans1],
	    select_nth0_i_(M, Item, With, Sans1)
	).

    select_nth0_v_(N, Item, [Item|Sans], Sans, N).
    select_nth0_v_(N, Item, [Head|With], [Head|Sans], N0) :-
	N1 is N0 + 1,
	select_nth0_v_(N, Item, With, Sans, N1).

Now you define the interface you really want:

    select_random_card(Card, DeckWithCard, DeckSansCard) :-
	select_random(Card, DeckWithCard, DeckSansCard).

I think you'll agree that "select_random_card" says much more clearly
than "get_card" what it's really all about.

Now, as for placing it on one of seven piles.

First, a version which assumes that the magic number seven is fixed
for all time.

Use the data structure

    piles(P1,P2,P3,P4,P5,P6,P7)

to represent the piles.

    add_to_random_pile(Card, Piles0, Piles) :-
	random_integer(7, I),
	add_to_random_pile_indexed(I, Piles0, Piles, Card).

    add_to_random_pile_indexed(0, piles(P1,P2,P3,P4,P5,P6,P7),
                           piles([Card|P1],P2,P3,P4,P5,P6,P7), Card).
    ...
    add_to_random_pile_indexed(6, piles(P1,P2,P3,P4,P5,P6,P7),
                           piles(P1,P2,P3,P4,P5,P6,[Card|P7]), Card).


Now a version which assumes that the magic number seven is not all
that magic, and that you might want to do this for varying numbers
of piles.

Use the data structure

    [P1, ..., Pn]

to represent the piles.  (Lists *are* the right thing to use when there
is a variable number of things.)

Again, the public Prolog library has for about 19 years contained

    select(This, ListWithThis, That, ListWithThat) :-
	/* Specification, NOT implementation *.
	append(A, [This|Z], ListWithThis),
	append(A, [That|Z], ListWithThat).

We'd like a random version of that.

    select_random(This, ListWithThis, That, ListWithThat) :-
	random_list_index(ListWithThis, I),
	select_nth0(I, This, ListWithThis, That, ListWithThat).

    select_nth0(Index, This, ListWithThis, That, ListWithThat) :-
	(   integer(Index) ->
	    Index >= 0,
	    select_nth0_i_(Index, This, ListWithThis, That, ListWithThat)
	;   var(Index) ->
	    select_nth0_v_(Index, This, ListWithThis, That, ListWithThat, 0)
	;   abort % should report domain failure
	).

    select_nth0_i_(N, This, [Head|WithThis], That, WithThat) :-
	(   N =:= 0 ->
	    This = Head, WithThat = [That|WithThis]
	;   M is N - 1,
	    WithThat = [Head|WithThat1],
	    select_nth0_i_(M, This, WithThis, That, WithThat1)
	).

    select_nth0_v_(N, This, [This|Tail], That, [That|Tail], N).
    select_nth0_v_(N, This, [Head|WithThis], That, [Head|WithThat], N0) :-
	N1 is N0 + 1,
	select_nth0_v_(N, This, WithThis, That, WithThat, N1).


    add_to_random_pile(Card, Piles0, Piles) :-
	random_list_index(Piles0, I),
	select_nth0(I, Pile, Piles0, [Card|Pile], Piles).


But the crucial question was this one:

	I created a rule called "get_card".  My problem is how to create
	dynamic "stack/pile" where is visible outside the rule.  I want
	another rule to see the "stack/pile".

The way to get another predicate to see a data structure is

    Pass it as a parameter.

It's just exactly what you would do in SML or Haskell or Clean or
Mercury or any "declarative" language, and while it may seem strange,
it is orders of magnitude faster than changing the data base.

