implement Cardlib;include "sys.m"; sys: Sys;include "draw.m";include "sets.m"; sets: Sets; Set, set, A, B, All, None: import sets;include "../spree.m"; spree: Spree; Attributes, Range, Object, Clique, Member, rand: import spree;include "objstore.m"; objstore: Objstore;include "cardlib.m"; MAXPLAYERS: con 4; Layobject: adt { lay: ref Object; name: string; packopts: int; pick { Obj => obj: ref Object; # nil if it's a frame Frame => facing: int; # only valid if for frames }}; clique: ref Clique;cmembers: array of ref Cmember;cpids := array[8] of list of ref Cmember; # XXX first string is unnecessary as it's held in the Layobject anyway?layouts := array[17] of list of (string, ref Layout, ref Layobject);maxlayid := 1;cmemberid := 1; archiveobjs: array of list of (string, ref Object); defaultrank := array[13] of {12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};defaultsuitrank := array[] of {CLUBS => 0, DIAMONDS => 1, HEARTS => 2, SPADES => 3}; table := array[] of { 0 => array[] of { (-1, dTOP|EXPAND, dBOTTOM, dTOP), }, 1 => array [] of { (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (-1, dTOP|EXPAND, dBOTTOM, dTOP), }, 2 => array[] of { (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (1, dTOP|FILLX, dTOP, dBOTTOM), (-1, dTOP|EXPAND, dBOTTOM, dTOP) }, 3 => array[] of { (2, dRIGHT|FILLY, dRIGHT, dLEFT), (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (1, dTOP|FILLX, dTOP, dBOTTOM), (-1, dRIGHT|EXPAND, dBOTTOM, dTOP) }, 4 => array[] of { (1, dLEFT|FILLY, dLEFT, dRIGHT), (3, dRIGHT|FILLY, dRIGHT, dLEFT), (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (2, dTOP|FILLX, dTOP, dBOTTOM), (-1, dRIGHT|EXPAND, dBOTTOM, dTOP) },}; init(mod: Spree, g: ref Clique){ sys = load Sys Sys->PATH; sets = load Sets Sets->PATH; if (sets == nil) panic(sys->sprint("cannot load %s: %r", Sets->PATH)); objstore = load Objstore Objstore->PATH; if (objstore == nil) panic(sys->sprint("cannot load %s: %r", Objstore->PATH)); objstore->init(mod, g); clique = g; spree = mod;} archive(): ref Object{ for (i := 0; i < len cmembers; i++) { cp := cmembers[i]; setarchivename(cp.obj, "member" + string i); setarchivename(cp.layout.lay, "layout" + string i); sel := cp.sel; if (sel.stack != nil) setarchivename(sel.stack, "sel" + string i); } for (i = 0; i < len layouts; i++) { for (ll := layouts[i]; ll != nil; ll = tl ll) { (name, lay, layobj) := hd ll; if (name != nil) layobj.lay.setattr("layname", name, None); pick l := layobj { Frame => l.lay.setattr("facing", sides[l.facing], None); Obj => setarchivename(l.obj, "layid" + l.obj.getattr("layid")); } } } # XXX should archive layouts that aren't particular to a member. archiveobj := clique.newobject(nil, None, "archive"); setarchivename(archiveobj, "archive"); archiveobj.setattr("maxlayid", string maxlayid, None); archiveobj.setattr("cmemberid", string cmemberid, None); return archiveobj;} setarchivename(o: ref Object, name: string){ objstore->setname(o, name);} getarchiveobj(name: string): ref Object{ return objstore->get(name);} archivearray(a: array of ref Object, name: string){ for (i := 0; i < len a; i++) objstore->setname(a[i], name + string i);} getarchivearray(name: string): array of ref Object{ l: list of ref Object; for (i := 0; ; i++) { o := objstore->get(name + string i); if (o == nil) break; l = o :: l; } a := array[i] of ref Object; for (; l != nil; l = tl l) a[--i] = hd l; return a;} unarchive(): ref Object{ objstore->unarchive(); archiveobj := getarchiveobj("archive"); cpl: list of ref Cmember; for (i := 0; (o := getarchiveobj("member" + string i)) != nil; i++) { cp := ref Cmember( i, int o.getattr("id"), clique.membernamed(o.getattr("name")), o, ref Layout(getarchiveobj("layout" + string i)), ref Selection(getarchiveobj("sel" + string i), -1, 1, (0, 0), nil) ); cp.sel.ownerid = cp.id; sel := cp.sel; if (sel.stack != nil && (selstr := sel.stack.getattr("sel")) != nil) { (n, val) := sys->tokenize(selstr, " "); if (tl val != nil && hd tl val == "-") (sel.r.start, sel.r.end) = (int hd val, int hd tl tl val); else { idxl: list of int; sel.isrange = 0; for (; val != nil; val = tl val) idxl = int hd val :: idxl; sel.idxl = idxl; } } lay := cp.layout.lay; # there should be exactly one child, of type "layframe" if (len lay.children != 1 || lay.children[0].objtype != "layframe") panic("invalid layout"); x := strhash(nil, len layouts); layouts[x] = (nil, cp.layout, obj2layobj(lay.children[0])) :: layouts[x]; unarchivelayoutobj(cp.layout, lay.children[0]); cpl = cp :: cpl; } cmembers = array[len cpl] of ref Cmember; for (; cpl != nil; cpl = tl cpl) { cp := hd cpl; cmembers[cp.ord] = cp; idx := cp.id % len cpids; cpids[idx] = cp :: cpids[idx]; } maxlayid = int archiveobj.getattr("maxlayid"); cmemberid = int archiveobj.getattr("cmemberid"); return archiveobj;} unarchivelayoutobj(layout: ref Layout, o: ref Object){ for (i := 0; i < len o.children; i++) { child := o.children[i]; layobj := obj2layobj(child); if (layobj.name != nil) { x := strhash(layobj.name, len layouts); layouts[x] = (layobj.name, layout, layobj) :: layouts[x]; } if (tagof(layobj) == tagof(Layobject.Frame)) unarchivelayoutobj(layout, child); }} obj2layobj(o: ref Object): ref Layobject{ case o.objtype { "layframe" => return ref Layobject.Frame( o, o.getattr("layname"), s2packopts(o.getattr("opts")), searchopt(sides, o.getattr("facing")) ); "layobj" => return ref Layobject.Obj( o, o.getattr("layname"), s2packopts(o.getattr("opts")), getarchiveobj("layid" + o.getattr("layid")) ); * => panic("invalid layobject found, of type '" + o.objtype + "'"); return nil; }} Cmember.join(member: ref Member, ord: int): ref Cmember{ cmembers = (array[len cmembers + 1] of ref Cmember)[0:] = cmembers; if (ord == -1) ord = len cmembers - 1; else { cmembers[ord + 1:] = cmembers[ord:len cmembers - 1]; for (i := ord + 1; i < len cmembers; i++) cmembers[i].ord = i; } cp := cmembers[ord] = ref Cmember(ord, cmemberid++, member, nil, nil, nil); cp.obj = clique.newobject(nil, All, "member"); cp.obj.setattr("id", string cp.id, All); cp.obj.setattr("name", member.name, All); cp.obj.setattr("you", string cp.id, None.add(member.id)); cp.obj.setattr("cliquetitle", clique.fname, All); cp.layout = newlayout(cp.obj, None.add(member.id)); cp.sel = ref Selection(nil, cp.id, 1, (0, 0), nil); idx := cp.id % len cpids; cpids[idx] = cp :: cpids[idx]; return cp;} Cmember.find(p: ref Member): ref Cmember{ id := p.id; for (i := 0; i < len cmembers; i++) if (cmembers[i].p.id == id) return cmembers[i]; return nil;} Cmember.index(ord: int): ref Cmember{ if (ord < 0 || ord >= len cmembers) return nil; return cmembers[ord];} Cmember.next(cp: self ref Cmember, fwd: int): ref Cmember{ if (!fwd) return cp.prev(1); x := cp.ord + 1; if (x >= len cmembers) x = 0; return cmembers[x];} Cmember.prev(cp: self ref Cmember, fwd: int): ref Cmember{ if (!fwd) return cp.next(1); x := cp.ord - 1; if (x < 0) x = len cmembers - 1; return cmembers[x];} Cmember.leave(cp: self ref Cmember){ ord := cp.ord; cmembers[ord] = nil; cmembers[ord:] = cmembers[ord + 1:]; cmembers[len cmembers - 1] = nil; cmembers = cmembers[0:len cmembers - 1]; for (i := ord; i < len cmembers; i++) cmembers[i].ord = i; cp.obj.delete(); dellayout(cp.layout); cp.layout = nil; idx := cp.id % len cpids; l: list of ref Cmember; ll := cpids[idx]; for (; ll != nil; ll = tl ll) if (hd ll != cp) l = hd ll :: l; cpids[idx] = l; cp.ord = -1;} Cmember.findid(id: int): ref Cmember{ for (l := cpids[id % len cpids]; l != nil; l = tl l) if ((hd l).id == id) return hd l; return nil;} newstack(parent: ref Object, owner: ref Member, spec: Stackspec): ref Object{ vis := All; if (spec.conceal) { vis = None; if (owner != nil) vis = vis.add(owner.id); } o := clique.newobject(parent, vis, "stack"); o.setattr("maxcards", string spec.maxcards, All); o.setattr("style", spec.style, All); # XXX provide some means for this to contain the member's name? o.setattr("title", spec.title, All); return o;} makecard(deck: ref Object, c: Card, rear: string): ref Object{ card := clique.newobject(deck, None, "card"); card.setattr("face", string c.face, All); vis := None; if(c.face) vis = All; card.setattr("number", string (c.number * 4 + c.suit), vis); if (rear != nil) card.setattr("rear", rear, All); return card;} makecards(deck: ref Object, r: Range, rear: string){ for (i := r.start; i < r.end; i++) for(suit := 0; suit < 4; suit++) makecard(deck, (suit, i, 0), rear);} # deal n cards to each member, if possible.# deal in chunks for efficiency.# if accuracy is required (e.g. dealing from an unshuffled# deck containing known cards) then this'll have to change.deal(deck: ref Object, n: int, stacks: array of ref Object, first: int){ ncards := len deck.children; ord := 0; permember := n; leftover := 0; if (n * len stacks > ncards) { # if trying to deal more cards than we've got, # deal all that we've got, distributing the remainder fairly. permember = ncards / len stacks; leftover = ncards % len stacks; } for (i := 0; i < len stacks; i++) { n = permember; if (leftover > 0) { n++; leftover--; } priv := stacks[(first + i) % len stacks]; deck.transfer((ncards - n, ncards), priv, len priv.children); priv.setattr("n", string (int priv.getattr("n") + n), All); # make cards visible to member for (j := len priv.children - n; j < len priv.children; j++) setface(priv.children[j], 1); ncards -= n; }} setface(card: ref Object, face: int){ # XXX check parent stack style and if it's a pile, # only expose a face up card at the top. card.setattr("face", string face, All); if (face) card.setattrvisibility("number", All); else card.setattrvisibility("number", None);} nmembers(): int{ return len cmembers;} getcard(card: ref Object): Card{ n := int card.getattr("number"); (suit, num) := (n % 4, n / 4); return Card(suit, num, int card.getattr("face"));} getcards(stack: ref Object): array of Card{ a := array[len stack.children] of Card; for (i := 0; i < len a; i++) a[i] = getcard(stack.children[i]); return a;} discard(stk, pile: ref Object, facedown: int){ n := len stk.children; if (facedown) for (i := 0; i < n; i++) setface(stk.children[i], 0); stk.transfer((0, n), pile, len pile.children);} # shuffle children into a random order. first we make all the children# invisible (which will cause them to be deleted in the clients) then# shuffle to our heart's content, and make visible again...shuffle(o: ref Object){ ovis := o.visibility; o.setvisibility(None); a := o.children; n := len a; for (i := 0; i < n; i++) { j := i + rand(n - i); (a[i], a[j]) = (a[j], a[i]); } o.setvisibility(ovis);} sort(o: ref Object, rank, suitrank: array of int){ if (rank == nil) rank = defaultrank; if (suitrank == nil) suitrank = defaultsuitrank; ovis := o.visibility; o.setvisibility(None); cardmergesort(o.children, array[len o.children] of ref Object, rank, suitrank); o.setvisibility(ovis);} cardcmp(a, b: ref Object, rank, suitrank: array of int): int{ c1 := getcard(a); c2 := getcard(b); if (suitrank[c1.suit] != suitrank[c2.suit]) return suitrank[c1.suit] - suitrank[c2.suit]; return rank[c1.number] - rank[c2.number];} cardmergesort(a, b: array of ref Object, rank, suitrank: array of int){ r := len a; if (r > 1) { m := (r-1)/2 + 1; cardmergesort(a[0:m], b[0:m], rank, suitrank); cardmergesort(a[m:], b[m:], rank, suitrank); b[0:] = a; for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { if (cardcmp(b[i], b[j], rank, suitrank) > 0) a[k] = b[j++]; else a[k] = b[i++]; } if (i < m) a[k:] = b[i:m]; else if (j < r) a[k:] = b[j:r]; }} # reverse and flip all cards in stack.flip(stack: ref Object){ ovis := stack.visibility; stack.setvisibility(None); a := stack.children; (n, m) := (len a, len a / 2);Lines 1–500 of 917. Load more
implement Cardlib; include "sys.m"; sys: Sys; include "draw.m"; include "sets.m"; sets: Sets; Set, set, A, B, All, None: import sets; include "../spree.m"; spree: Spree; Attributes, Range, Object, Clique, Member, rand: import spree; include "objstore.m"; objstore: Objstore; include "cardlib.m"; MAXPLAYERS: con 4; Layobject: adt { lay: ref Object; name: string; packopts: int; pick { Obj => obj: ref Object; # nil if it's a frame Frame => facing: int; # only valid if for frames } }; clique: ref Clique; cmembers: array of ref Cmember; cpids := array[8] of list of ref Cmember; # XXX first string is unnecessary as it's held in the Layobject anyway? layouts := array[17] of list of (string, ref Layout, ref Layobject); maxlayid := 1; cmemberid := 1; archiveobjs: array of list of (string, ref Object); defaultrank := array[13] of {12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; defaultsuitrank := array[] of {CLUBS => 0, DIAMONDS => 1, HEARTS => 2, SPADES => 3}; table := array[] of { 0 => array[] of { (-1, dTOP|EXPAND, dBOTTOM, dTOP), }, 1 => array [] of { (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (-1, dTOP|EXPAND, dBOTTOM, dTOP), }, 2 => array[] of { (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (1, dTOP|FILLX, dTOP, dBOTTOM), (-1, dTOP|EXPAND, dBOTTOM, dTOP) }, 3 => array[] of { (2, dRIGHT|FILLY, dRIGHT, dLEFT), (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (1, dTOP|FILLX, dTOP, dBOTTOM), (-1, dRIGHT|EXPAND, dBOTTOM, dTOP) }, 4 => array[] of { (1, dLEFT|FILLY, dLEFT, dRIGHT), (3, dRIGHT|FILLY, dRIGHT, dLEFT), (0, dBOTTOM|FILLX, dBOTTOM, dTOP), (2, dTOP|FILLX, dTOP, dBOTTOM), (-1, dRIGHT|EXPAND, dBOTTOM, dTOP) }, }; init(mod: Spree, g: ref Clique) { sys = load Sys Sys->PATH; sets = load Sets Sets->PATH; if (sets == nil) panic(sys->sprint("cannot load %s: %r", Sets->PATH)); objstore = load Objstore Objstore->PATH; if (objstore == nil) panic(sys->sprint("cannot load %s: %r", Objstore->PATH)); objstore->init(mod, g); clique = g; spree = mod; } archive(): ref Object { for (i := 0; i < len cmembers; i++) { cp := cmembers[i]; setarchivename(cp.obj, "member" + string i); setarchivename(cp.layout.lay, "layout" + string i); sel := cp.sel; if (sel.stack != nil) setarchivename(sel.stack, "sel" + string i); } for (i = 0; i < len layouts; i++) { for (ll := layouts[i]; ll != nil; ll = tl ll) { (name, lay, layobj) := hd ll; if (name != nil) layobj.lay.setattr("layname", name, None); pick l := layobj { Frame => l.lay.setattr("facing", sides[l.facing], None); Obj => setarchivename(l.obj, "layid" + l.obj.getattr("layid")); } } } # XXX should archive layouts that aren't particular to a member. archiveobj := clique.newobject(nil, None, "archive"); setarchivename(archiveobj, "archive"); archiveobj.setattr("maxlayid", string maxlayid, None); archiveobj.setattr("cmemberid", string cmemberid, None); return archiveobj; } setarchivename(o: ref Object, name: string) { objstore->setname(o, name); } getarchiveobj(name: string): ref Object { return objstore->get(name); } archivearray(a: array of ref Object, name: string) { for (i := 0; i < len a; i++) objstore->setname(a[i], name + string i); } getarchivearray(name: string): array of ref Object { l: list of ref Object; for (i := 0; ; i++) { o := objstore->get(name + string i); if (o == nil) break; l = o :: l; } a := array[i] of ref Object; for (; l != nil; l = tl l) a[--i] = hd l; return a; } unarchive(): ref Object { objstore->unarchive(); archiveobj := getarchiveobj("archive"); cpl: list of ref Cmember; for (i := 0; (o := getarchiveobj("member" + string i)) != nil; i++) { cp := ref Cmember( i, int o.getattr("id"), clique.membernamed(o.getattr("name")), o, ref Layout(getarchiveobj("layout" + string i)), ref Selection(getarchiveobj("sel" + string i), -1, 1, (0, 0), nil) ); cp.sel.ownerid = cp.id; sel := cp.sel; if (sel.stack != nil && (selstr := sel.stack.getattr("sel")) != nil) { (n, val) := sys->tokenize(selstr, " "); if (tl val != nil && hd tl val == "-") (sel.r.start, sel.r.end) = (int hd val, int hd tl tl val); else { idxl: list of int; sel.isrange = 0; for (; val != nil; val = tl val) idxl = int hd val :: idxl; sel.idxl = idxl; } } lay := cp.layout.lay; # there should be exactly one child, of type "layframe" if (len lay.children != 1 || lay.children[0].objtype != "layframe") panic("invalid layout"); x := strhash(nil, len layouts); layouts[x] = (nil, cp.layout, obj2layobj(lay.children[0])) :: layouts[x]; unarchivelayoutobj(cp.layout, lay.children[0]); cpl = cp :: cpl; } cmembers = array[len cpl] of ref Cmember; for (; cpl != nil; cpl = tl cpl) { cp := hd cpl; cmembers[cp.ord] = cp; idx := cp.id % len cpids; cpids[idx] = cp :: cpids[idx]; } maxlayid = int archiveobj.getattr("maxlayid"); cmemberid = int archiveobj.getattr("cmemberid"); return archiveobj; } unarchivelayoutobj(layout: ref Layout, o: ref Object) { for (i := 0; i < len o.children; i++) { child := o.children[i]; layobj := obj2layobj(child); if (layobj.name != nil) { x := strhash(layobj.name, len layouts); layouts[x] = (layobj.name, layout, layobj) :: layouts[x]; } if (tagof(layobj) == tagof(Layobject.Frame)) unarchivelayoutobj(layout, child); } } obj2layobj(o: ref Object): ref Layobject { case o.objtype { "layframe" => return ref Layobject.Frame( o, o.getattr("layname"), s2packopts(o.getattr("opts")), searchopt(sides, o.getattr("facing")) ); "layobj" => return ref Layobject.Obj( o, o.getattr("layname"), s2packopts(o.getattr("opts")), getarchiveobj("layid" + o.getattr("layid")) ); * => panic("invalid layobject found, of type '" + o.objtype + "'"); return nil; } } Cmember.join(member: ref Member, ord: int): ref Cmember { cmembers = (array[len cmembers + 1] of ref Cmember)[0:] = cmembers; if (ord == -1) ord = len cmembers - 1; else { cmembers[ord + 1:] = cmembers[ord:len cmembers - 1]; for (i := ord + 1; i < len cmembers; i++) cmembers[i].ord = i; } cp := cmembers[ord] = ref Cmember(ord, cmemberid++, member, nil, nil, nil); cp.obj = clique.newobject(nil, All, "member"); cp.obj.setattr("id", string cp.id, All); cp.obj.setattr("name", member.name, All); cp.obj.setattr("you", string cp.id, None.add(member.id)); cp.obj.setattr("cliquetitle", clique.fname, All); cp.layout = newlayout(cp.obj, None.add(member.id)); cp.sel = ref Selection(nil, cp.id, 1, (0, 0), nil); idx := cp.id % len cpids; cpids[idx] = cp :: cpids[idx]; return cp; } Cmember.find(p: ref Member): ref Cmember { id := p.id; for (i := 0; i < len cmembers; i++) if (cmembers[i].p.id == id) return cmembers[i]; return nil; } Cmember.index(ord: int): ref Cmember { if (ord < 0 || ord >= len cmembers) return nil; return cmembers[ord]; } Cmember.next(cp: self ref Cmember, fwd: int): ref Cmember { if (!fwd) return cp.prev(1); x := cp.ord + 1; if (x >= len cmembers) x = 0; return cmembers[x]; } Cmember.prev(cp: self ref Cmember, fwd: int): ref Cmember { if (!fwd) return cp.next(1); x := cp.ord - 1; if (x < 0) x = len cmembers - 1; return cmembers[x]; } Cmember.leave(cp: self ref Cmember) { ord := cp.ord; cmembers[ord] = nil; cmembers[ord:] = cmembers[ord + 1:]; cmembers[len cmembers - 1] = nil; cmembers = cmembers[0:len cmembers - 1]; for (i := ord; i < len cmembers; i++) cmembers[i].ord = i; cp.obj.delete(); dellayout(cp.layout); cp.layout = nil; idx := cp.id % len cpids; l: list of ref Cmember; ll := cpids[idx]; for (; ll != nil; ll = tl ll) if (hd ll != cp) l = hd ll :: l; cpids[idx] = l; cp.ord = -1; } Cmember.findid(id: int): ref Cmember { for (l := cpids[id % len cpids]; l != nil; l = tl l) if ((hd l).id == id) return hd l; return nil; } newstack(parent: ref Object, owner: ref Member, spec: Stackspec): ref Object { vis := All; if (spec.conceal) { vis = None; if (owner != nil) vis = vis.add(owner.id); } o := clique.newobject(parent, vis, "stack"); o.setattr("maxcards", string spec.maxcards, All); o.setattr("style", spec.style, All); # XXX provide some means for this to contain the member's name? o.setattr("title", spec.title, All); return o; } makecard(deck: ref Object, c: Card, rear: string): ref Object { card := clique.newobject(deck, None, "card"); card.setattr("face", string c.face, All); vis := None; if(c.face) vis = All; card.setattr("number", string (c.number * 4 + c.suit), vis); if (rear != nil) card.setattr("rear", rear, All); return card; } makecards(deck: ref Object, r: Range, rear: string) { for (i := r.start; i < r.end; i++) for(suit := 0; suit < 4; suit++) makecard(deck, (suit, i, 0), rear); } # deal n cards to each member, if possible. # deal in chunks for efficiency. # if accuracy is required (e.g. dealing from an unshuffled # deck containing known cards) then this'll have to change. deal(deck: ref Object, n: int, stacks: array of ref Object, first: int) { ncards := len deck.children; ord := 0; permember := n; leftover := 0; if (n * len stacks > ncards) { # if trying to deal more cards than we've got, # deal all that we've got, distributing the remainder fairly. permember = ncards / len stacks; leftover = ncards % len stacks; } for (i := 0; i < len stacks; i++) { n = permember; if (leftover > 0) { n++; leftover--; } priv := stacks[(first + i) % len stacks]; deck.transfer((ncards - n, ncards), priv, len priv.children); priv.setattr("n", string (int priv.getattr("n") + n), All); # make cards visible to member for (j := len priv.children - n; j < len priv.children; j++) setface(priv.children[j], 1); ncards -= n; } } setface(card: ref Object, face: int) { # XXX check parent stack style and if it's a pile, # only expose a face up card at the top. card.setattr("face", string face, All); if (face) card.setattrvisibility("number", All); else card.setattrvisibility("number", None); } nmembers(): int { return len cmembers; } getcard(card: ref Object): Card { n := int card.getattr("number"); (suit, num) := (n % 4, n / 4); return Card(suit, num, int card.getattr("face")); } getcards(stack: ref Object): array of Card { a := array[len stack.children] of Card; for (i := 0; i < len a; i++) a[i] = getcard(stack.children[i]); return a; } discard(stk, pile: ref Object, facedown: int) { n := len stk.children; if (facedown) for (i := 0; i < n; i++) setface(stk.children[i], 0); stk.transfer((0, n), pile, len pile.children); } # shuffle children into a random order. first we make all the children # invisible (which will cause them to be deleted in the clients) then # shuffle to our heart's content, and make visible again... shuffle(o: ref Object) { ovis := o.visibility; o.setvisibility(None); a := o.children; n := len a; for (i := 0; i < n; i++) { j := i + rand(n - i); (a[i], a[j]) = (a[j], a[i]); } o.setvisibility(ovis); } sort(o: ref Object, rank, suitrank: array of int) { if (rank == nil) rank = defaultrank; if (suitrank == nil) suitrank = defaultsuitrank; ovis := o.visibility; o.setvisibility(None); cardmergesort(o.children, array[len o.children] of ref Object, rank, suitrank); o.setvisibility(ovis); } cardcmp(a, b: ref Object, rank, suitrank: array of int): int { c1 := getcard(a); c2 := getcard(b); if (suitrank[c1.suit] != suitrank[c2.suit]) return suitrank[c1.suit] - suitrank[c2.suit]; return rank[c1.number] - rank[c2.number]; } cardmergesort(a, b: array of ref Object, rank, suitrank: array of int) { r := len a; if (r > 1) { m := (r-1)/2 + 1; cardmergesort(a[0:m], b[0:m], rank, suitrank); cardmergesort(a[m:], b[m:], rank, suitrank); b[0:] = a; for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { if (cardcmp(b[i], b[j], rank, suitrank) > 0) a[k] = b[j++]; else a[k] = b[i++]; } if (i < m) a[k:] = b[i:m]; else if (j < r) a[k:] = b[j:r]; } } # reverse and flip all cards in stack. flip(stack: ref Object) { ovis := stack.visibility; stack.setvisibility(None); a := stack.children; (n, m) := (len a, len a / 2);