SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

     NAME
          Cardlib - support for card games in Spree engines.

     SYNOPSIS
          include "sys.m";
          include "draw.m";
          include "sets.m";
          include "spree.m";
          include "spree/cardlib.m";

          Object: import Spree;
          cardlib := load Cardlib Cardlib->PATH;

          init:               fn(spree: Spree, clique: ref Clique, archived: int);
          selection:     fn(stack: ref Object): ref Selection;

          makecard: fn(deck: ref Object, c: Card, rear: string): ref Object;
          makecards:     fn(stack: ref Object, r: Range, rear: string);
          getcard:       fn(card: ref Object): Card;
          getcards:      fn(stack: ref Object): array of Card;
          setface:       fn(card: ref Object, face: int);
          flip:               fn(stack: ref Object);
          shuffle:       fn(stack: ref Object);
          discard:       fn(stk, pile: ref Object, facedown: int);
          deal:               fn(stack: ref Object, n: int, stacks: array of ref Object, first: int);
          sort:               fn(stack: ref Object, rank, suitrank: array of int);

          addlayframe:   fn(name: string, parent: string, layout: ref Layout, packopts: int, facing: int);
          addlayobj:     fn(name: string, parent: string, layout: ref Layout, packopts: int, obj: ref Object);
          dellay:        fn(name: string, layout: ref Layout);
          maketable:     fn(parent: string);

          newstack:      fn(parent: ref Object, p: ref Member, spec: Stackspec): ref Object;

          archive:       fn(): ref Object;
          unarchive:     fn(): ref Object;
          setarchivename: fn(o: ref Object, name: string);
          archivearray:  fn(a: array of ref Object, name: string);
          getarchiveobj: fn(name: string): ref Object;
          getarchivearray: fn(name: string): array of ref Object;

          nmembers: fn(): int;

          Layout: adt {
               lay: ref Object;
          };

          Stackspec: adt {
               style:    string;
               maxcards: int;

     Page 1                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

               title:         string;
               conceal:  int;
          };

          Card: adt {
               suit:          int;
               number:   int;
               face:          int;
          };

          # a member currently playing
          Cmember: adt {
               ord:      int;
               id:       int;
               p:        ref Member;
               obj:      ref Object;
               layout:   ref Layout;
               sel:      ref Selection;

               join:          fn(p: ref Member, ord: int): ref Cmember;
               index:    fn(ord: int): ref Cmember;
               find:          fn(p: ref Member): ref Cmember;
               findid:   fn(id: int): ref Cmember;
               leave:    fn(cp: self ref Cmember);
               next:          fn(cp: self ref Cmember, fwd: int): ref Cmember;
               prev:          fn(cp: self ref Cmember, fwd: int): ref Cmember;
          };

          Selection: adt {
               stack:    ref Object;
               ownerid:  int;
               isrange:  int;
               r:        Range;
               idxl:          list of int;

               set:      fn(sel: self ref Selection, stack: ref Object);
               setexcl:  fn(sel: self ref Selection, stack: ref Object): int;
               setrange: fn(sel: self ref Selection, r: Range);
               addindex: fn(sel: self ref Selection, i: int);
               delindex: fn(sel: self ref Selection, i: int);
               isempty:  fn(sel: self ref Selection): int;
               isset:         fn(sel: self ref Selection, index: int): int;
               transfer: fn(sel: self ref Selection, dst: ref Object, index: int);
               owner:    fn(sel: self ref Selection): ref Cmember;
          };

     DESCRIPTION
          Cardlib provides facilities to help in the implementation of
          spree(2) engines that implement the spree-cards(4) inter-
          face.  Facilities include the layout of clients' cards, sup-
          port for card selections, and card manipulation.

     Page 2                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

          Init must be called first to initialise the Cardlib module,
          giving it the spree module and the current clique.  Archived
          should be non-zero if the card game is being restored from
          an archive.

        Cards
          The value of a playing card is represented by the Card adt,
          having attributes suit, number, and face. Suit ranges from 0
          to 3 inclusive, representing clubs, diamonds, hearts and
          spades respectively; number ranges from 0 to 12 inclusive
          for the standard cards, with ace low and king high - a joker
          is represented by a number greater than 12; face represents
          whether the card is face up or face down (0 is face down).

          A actual card is represented by an object in the object
          hierarchy of type card, with attributes number, face, and
          rear.  Number is the suit/number of the card (held as n,
          where n%4 gives the suit, and n/4 the rank).  Face is as
          held in the Card adt, and rear is a number that represents
          the pattern on the back of the card (numbered from 0
          upwards).  Conventionally the number attribute is made
          invisible to all players when the face attribute is set to
          zero.

          Makecard creates a new card of value c, placing the new card
          object at the end of deck, and setting the rear attribute to
          rear if it is non-nil.  Makecards makes a set of cards, all
          face down, in all four suits, having numbers within the
          range r.

          Getcard gets the value representation of a card from object
          card; getcards gets the values of all the card objects
          within stack. Setface sets of card to face; the visibility
          of the card's number is changed appropriately.

          The following few routines operate on stacks of cards:
          objects which contain only card objects: flip reverses a
          stack of cards, reversing their faces as it does so; shuffle
          shuffles a stack of cards, and sort sorts a stack of cards
          by suit and then number, according to rank and suitrank.
          Rank and suitrank are permutations mapping number/suit to
          sort precedence (0 low).  If either of these are nil, then a
          default ranking scheme is chosen (two low, ace high for num-
          ber).  Discard moves all the cards in stk onto pile, turning
          them face down if facedown is non-zero.  Deal deals out all
          the cards in stack as evenly as possible amongst stacks,
          dealing to stacks[first] first.

        Members and card selection
          Cardlib keeps a record of the current players of the game; a
          player is represented by a Cmember adt; the players are
          assumed to sit in a circle, numbered from 0 updwards;

     Page 3                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

          nmembers gives the number of current players.  Each player
          has a unique integer id, and an associated selection and
          card layout.

          m.join(m, ord)
                    Join a new player to the game; m is the clique
                    member that's joining, and ord is where to slot
                    the player in the circle of existing players.  If
                    ord is -1, the player will be added at the end.

          m.leave() Remove m from the list of current players.

          m.index(ord)
                    Index returns the ordth player around the table.

          m.find(m) Find the Cmember corresponding to member m.

          m.findid(id)
                    Find the Cmember with identifier id, and return
                    it.  m.next(fwd) Next returns the next player
                    around the table from m. If fwd is non-zero, it
                    counts upwards, otherwise it counts downwards.
                    m.prev(fwd) Prev is the opposite of next.  If fwd
                    is non-zero, it counts downwards, otherwise it
                    counts upwards.

        Selection
          Each Cmember m has an associated selection, m.sel, which
          consists of a selection of some cards from a single stack of
          cards.  A selection can consist of either a range of cards
          within a stack, or an arbitrary set of cards within a stack.
          A stack can only be the subject of one selection; the member
          that has that selection is known as its owner.

          sel.set(stack)
                    Set makes stack (an object containing only card
                    objects) the subject of sel's selection. If stack
                    is nil, the selection is cleared.

          sel.setexcl(stack)
                    Setexcl is the same as set except that it will
                    fail if the stack is owned by a different player.
                    It returns 0 if it fails, otherwise non-zero.

          sel.setrange(r)
                    Setrange sets the selection sel to be a range of
                    cards within its stack.  If the selection had been
                    of distinct cards (set using addindex), it is
                    first cleared.

          sel.addindex(i)
                    Addindex adds the card at index i to the selection

     Page 4                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

                    sel. If a range had previously been selected, it
                    is first cleared.

          sel.delindex(i)
                    Delindex deletes the card at index i from the
                    selection.  If the selection was previously a
                    range, this is a no-op.

          sel.isempty()
                    Isempty returns non-zero if sel holds an empty
                    selection.

          sel.isset(index)
                    Isset returns non-zero if the card at index index
                    is contained within the selection sel.

          sel.transfer(dst, index)
                    Transfer moves all the cards in the selection sel
                    to just before index within the stack dst.
                    sel.owner() Owner returns the Cmember that owns
                    the selection sel.

        Layout
          Creating a stack of cards does not specify how it is to be
          displayed to members of the game. Each member has a layout
          object which defines which objects are to be displayed to
          that member, and how they are to be laid out.  Any member
          must see at most one layout object (it is conventional to
          make a layout object visible only to its owner).  Objects
          are laid out using tk-like pack(9) semantics: frames pack
          together display objects or other frames.  A display object
          can lay out anything the card client knows how to display
          (see ``Display Objects'', below).

          Addlayframe adds a new frame named name within a layout
          frame named parent, specific to layout. If parent is nil,
          the frame is added to the root of the hierarchy.  If layout
          is nil, a frame is added to parent for each member that has
          a layout frame of that name.  Packopts specifies how the
          frame is to be packed within its parent: it is a bitmask,
          specifying the side of the cavity against which it is to be
          packed, the place it is to be anchored should the cavity be
          bigger than its requested size, how to fill its cavity,
          whether to expand its requested size to fill extra available
          space.  See pack(9) for details of the packing algorithm.
          The packing direction is specified with one of dTOP, dLEFT,
          dBOTTOM or dRIGHT.  The anchor direction is specified with
          one of aCENTRE, aUPPERCENTRE, aUPPERLEFT, aCENTRELEFT,
          aLOWERLEFT, aLOWERCENTRE, aLOWERRIGHT, aCENTRERIGHT, or
          aUPPERRIGHT.  FILLX and FILLY specify how to fill unused
          space in its cavity (not mutually exclusive), and EXPAND
          requests unused space.  Facing influences direction that

     Page 5                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

          objects are packed in underneath the frame. It should be one
          of the pack direction constants specified above (e.g.
          dTOP).  For instance, if dRIGHT is specified, then all
          objects packed underneath have their attributes modified 90°
          clockwise, as if the player in question was sitting on the
          left of the table, looking right.  This feature means that
          it is possible to set up a ``table'' in which layout objects
          can be added to all players at the same time, but which
          nonetheless looks different to each player around the table.

          Maketable creates such a      ``table'' for between 0 and 4
          players.  It creates a frame for each player, named pn,
          where n is the ordinal number of the player around the
          table; and an inner space, named public.  The parent argu-
          ment to maketable gives the frame within which the table is
          to be created.

          Addlayobj adds a new display object obj to the layout
          hierarchy. Name, parent, layout, and packopts are the same
          as for addlayframe except that if it is a stack object, then
          packopts also specifies the orientation of the stack, with
          one of the constants oRIGHT, oUP, oLEFT, or oDOWN, giving
          the direction in which cards are laid out within the stack.

          Dellay deletes the object named name from the layout hierar-
          chy. If layout is nil, it is deleted from all layouts, oth-
          erwise from layout only.

        Display Objects
          Currently, two kinds of objects can be displayed: stacks and
          widgets. A stack has object type stack, and contains only
          cards objects.  Attributes on the stack object define its
          appearance: maxcards gives the default size of the stack;
          style gives the style of stack layout, currently one of pile
          (all the cards piled directly on top of one another), or
          display (cards are spread out in the direction specified by
          the orientation given in the packing options, see Layout,
          above); title gives a title to display with the stack.

          Newstack creates a new stack according to the specifications
          in spec, where spec is an adt that holds style, maxcards,
          and title, as described above.  If spec.conceal is non-zero,
          the contents of the new stack will be made invisible to all
          (except owner, if owner is non-nil).

          Widgets are created by making an object of type ``widget
          type'', where type is one of button, entry,or menu.  The
          text attribute controls the text that is displayed in the
          widget; command gives the text that will be sent to the
          engine when the widget is activated, and width specifies the
          widget of the widget, in multiples of the width of the ``0''
          character.

     Page 6                       Plan 9            (printed 12/22/24)

     SPREE-CARDLIB(2)                                 SPREE-CARDLIB(2)

          Entries can be made in a menu widget by creating new objects
          of type menuentry inside a menu object. The text and command
          attributes have the usual meaning here.

        Archival
          Engines that use cardlib should not use spree-objstore(2) to
          archive their objects: cardlib provides an interface to do
          this, and also knows how to archive and unarchive its own
          internal state.

          Archive commits all the internal state of cardlib to the
          object hierarchy, prior to archival.  It returns an
          ``archive'' object that can be used as a convenient place to
          put attributes that need archiving but are not associated
          with any particular object.  Setarchivename associates name
          with the object o such that it can be retrieved when unar-
          chiving by calling getarchiveobj with the same name.  Simi-
          larly Archivearray associates a name with each object in the
          array a such that the array can be retrieved when unarchiv-
          ing by calling getarchivearray with the same name.  Name
          should not end in a decimal digit.  Unarchive unarchives
          cardlib's internal state. It returns the same archive object
          that was returned by archive.

     SOURCE
          /appl/spree/lib/cardlib.b

     SEE ALSO
          spree(2), spree-allow(2), spree-objstore(2)

     BUGS
          This interface is not complete and is liable to change.

     Page 7                       Plan 9            (printed 12/22/24)