code: 9ferno

ref: fcccaf74c54ecd44b7e8bf45a7b0c99572f6a949
dir: /appl/spree/engines/spider.b/

View raw version
implement Gatherengine;

include "sys.m";
	sys: Sys;
include "draw.m";
include "sets.m";
	All, None: import Sets;
include "../spree.m";
	spree: Spree;
	Attributes, Range, Object, Clique, Member, rand: import spree;
include "allow.m";
	allow: Allow;
include "cardlib.m";
	cardlib: Cardlib;
	Selection, Cmember, Card: import cardlib;
	getcard: import cardlib;
	dTOP, dRIGHT, dLEFT, oRIGHT, oDOWN,
	aCENTRERIGHT, aCENTRELEFT, aUPPERRIGHT, aUPPERCENTRE,
	EXPAND, FILLX, FILLY, Stackspec: import Cardlib;
include "../gather.m";

clique: ref Clique;

open: array of ref Object;		# [10]
deck: ref Object;
discard: ref Object;
dealbutton: ref Object;

CLICK, MORECARDS: con iota;

Openspec := Stackspec(
	"display",		# style
	19,			# maxcards
	0,			# conceal
	""			# title
);

clienttype(): string
{
	return "cards";
}

maxmembers(): int
{
	return 1;
}

readfile(nil: int, nil: big, nil: int): array of byte
{
	return nil;
}

init(srvmod: Spree, g: ref Clique, nil: list of string, nil: int): string
{
	sys = load Sys Sys->PATH;
	clique = g;
	spree = srvmod;
	allow = load Allow Allow->PATH;
	if (allow == nil) {
		sys->print("whist: cannot load %s: %r\n", Allow->PATH);
		return "bad module";
	}
	allow->init(spree, clique);
	cardlib = load Cardlib Cardlib->PATH;
	if (cardlib == nil) {
		sys->print("whist: cannot load %s: %r\n", Cardlib->PATH);
		return "bad module";
	}
	return nil;
}

propose(members: array of string): string
{
	if (len members != 1)
		return "one member only";
	return nil;
}

archive()
{
	archiveobj := cardlib->archive();
	allow->archive(archiveobj);
	cardlib->archivearray(open, "open");
	cardlib->setarchivename(deck, "deck");
	cardlib->setarchivename(discard, "discard");
	cardlib->setarchivename(dealbutton, "dealbutton");
}

start(members: array of ref Member, archived: int)
{
	cardlib->init(spree, clique);
	if (archived) {
		archiveobj := cardlib->unarchive();
		allow->unarchive(archiveobj);
		open = cardlib->getarchivearray("open");
		discard = cardlib->getarchiveobj("discard");
		deck = cardlib->getarchiveobj("deck");
		dealbutton = cardlib->getarchiveobj("dealbutton");
	} else {
		p := members[0];
		Cmember.join(p, -1).layout.lay.setvisibility(All);
		startclique();
		allow->add(CLICK, p, "click %o %d");
		allow->add(MORECARDS, p, "morecards");
	}
}

command(p: ref Member, cmd: string): string
{
	(err, tag, toks) := allow->action(p, cmd);
	if (err != nil)
		return err;
	cp := Cmember.find(p);
	if (cp == nil)
		return "you are not playing";
	case tag {
	CLICK =>
		# click stack index
		stack := clique.objects[int hd tl toks];
		nc := len stack.children;
		idx := int hd tl tl toks;
		sel := cp.sel;
		stype := stack.getattr("type");
		if (sel.isempty() || sel.stack == stack) {
			if (idx < 0 || idx >= len stack.children)
				return "invalid index";
			case stype {
			"open" =>
				select(cp, stack, (idx, nc));
			* =>
				return "you can't move cards from there";
			}
		} else {
			from := sel.stack;
			case stype {
			"open" =>
				c := getcard(sel.stack.children[sel.r.start]);
				n := c.number + 1;
				for (i := sel.r.start; i < sel.r.end; i++) {
					c2 := getcard(sel.stack.children[i]);
					if (c2.face == 0)
						return "cannot move face down cards";
					if (c2.number != n - 1)
						return "bad number sequence";
					n = c2.number;
				}
				if (nc != 0) {
					c2 := getcard(stack.children[nc - 1]);
					if (c2.number != c.number + 1)
						return "descending, only";
				}
				srcstack := sel.stack;
				sel.transfer(stack, -1);
				turntop(srcstack);

				nc = len stack.children;
				if (nc >= 13) {
					c = getcard(stack.children[nc - 1]);
					suit := c.suit;
					for (i = 0; i < 13; i++) {
						c = getcard(stack.children[nc - i - 1]);
						if (c.suit != suit || c.number != i)
							break;
					}
					if (i == 13) {
						stack.transfer((nc - 13, nc), discard, -1);
						turntop(stack);
					}
				}
			* =>
				return "can't move there";
			}
		}
	MORECARDS =>
		for (i := 0; i < 10; i++)
			if (len open[i].children == 0)
				return "spaces must be filled before redeal";
		for (i = 0; i < 10; i++) {
			if (len deck.children == 0)
				break;
			cp.sel.set(nil);
			cardlib->setface(deck.children[0], 1);
			deck.transfer((0, 1), open[i], -1);
		}
		setdealbuttontext();
	}
	return nil;
}

setdealbuttontext()
{
	dealbutton.setattr("text", sys->sprint("deal more (%d left)", len deck.children), All);
}

turntop(stack: ref Object)
{
	if (len stack.children > 0)
		cardlib->setface(stack.children[len stack.children - 1], 1);
}

startclique()
{
	addlayobj, addlayframe: import cardlib;
	open = array[10] of {* => newstack(nil, Openspec, "open", nil)};
	deck = clique.newobject(nil, All, "stack");
	discard = clique.newobject(nil, All, "stack");
	cardlib->makecards(deck, (0, 13), "0");
	cardlib->makecards(deck, (0, 13), "1");
	addlayframe("arena", nil, nil, dTOP|EXPAND|FILLX|FILLY, dTOP);
	addlayframe("top", "arena", nil, dTOP|EXPAND|FILLX|FILLY, dTOP);

	for (i := 0; i < 10; i++)
		addlayobj(nil, "top", nil, dLEFT|oDOWN|EXPAND|aUPPERCENTRE, open[i]);
	addlayframe("bot", "arena", nil, dTOP, dTOP);
	dealbutton = newbutton("morecards", "deal more");
	addlayobj(nil, "bot", nil, dLEFT, dealbutton);
	deal();
	setdealbuttontext();
}

deal()
{
	cardlib->shuffle(deck);
	for (i := 0; i < 10; i++) {
		deck.transfer((0, 4), open[i], 0);
		turntop(open[i]);
	}
}

newstack(parent: ref Object, spec: Stackspec, stype, title: string): ref Object
{
	stack := cardlib->newstack(parent, nil, spec);
	stack.setattr("type", stype, None);
	stack.setattr("actions", "click", All);
	stack.setattr("title", title, All);
	return stack;
}

select(cp: ref Cmember, stack: ref Object, r: Range)
{
	if (cp.sel.isempty()) {
		cp.sel.set(stack);
		cp.sel.setrange(r);
	} else {
		if (cp.sel.r.start == r.start && cp.sel.r.end == r.end)
			cp.sel.set(nil);
		else
			cp.sel.setrange(r);
	}
}

newbutton(cmd, text: string): ref Object
{
	but := clique.newobject(nil, All, "widget button");
	but.setattr("command", cmd, All);
	but.setattr("text", text, All);
	return but;
}