code: 9ferno

ref: 83246e296ea433b65b9d295b5e08fedd39ff1ab7
dir: /appl/spree/engines/afghan.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: import cardlib;
	dTOP, dLEFT, oLEFT, oRIGHT, EXPAND, FILLX, FILLY, aUPPERCENTRE,
	Stackspec: import Cardlib;
include "../gather.m";

CLICK, REDEAL: con iota;

clique: ref Clique;
rows: array of ref Object;		# [10]
central: array of ref Object;	# [4]
chokey, deck: ref Object;
direction := 0;
nredeals := 0;

Rowpilespec := Stackspec(
	"display",		# style
	10,			# maxcards
	0,			# conceal
	nil			# title
);

Centralpilespec := Stackspec(
	"pile",
	13,
	0,
	nil
);

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";
	}
	cardlib->init(spree, clique);
	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(rows, "rows");
	cardlib->archivearray(central, "central");
	cardlib->setarchivename(chokey, "chokey");
	cardlib->setarchivename(deck, "deck");
	archiveobj.setattr("direction", string direction, None);
	archiveobj.setattr("nredeals", string nredeals, None);
}

start(members: array of ref Member, archived: int)
{
	if (archived) {
		archiveobj := cardlib->unarchive();
		allow->unarchive(archiveobj);
		rows = cardlib->getarchivearray("rows");
		central = cardlib->getarchivearray("central");
		chokey = cardlib->getarchiveobj("chokey");
		deck = cardlib->getarchiveobj("deck");
		direction = int archiveobj.getattr("direction");
		nredeals = int archiveobj.getattr("nredeals");
	} else {
		p := members[0];
		Cmember.join(p, -1).layout.lay.setvisibility(All);
		startclique();
		allow->add(CLICK, p, "click %o %d");
	}
}

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 {
	REDEAL =>
		if (nredeals >= 3)
			return "no more redeals";
		redeal();
		nredeals++;
	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) {
			# selecting a card to move
			if (idx < 0 || idx >= len stack.children)
				return "invalid index";
			case stype {
			"row" or
			"chokey" =>
				select(cp, stack, (nc - 1, nc));
			* =>
				return "you can't move cards from there";
			}
		} else {
			# selecting a stack to move to.
			card := cardlib->getcard(sel.stack.children[sel.r.start]);
			case stype {
			"central" =>
				top := cardlib->getcard(stack.children[nc - 1]);
				if (direction == 0) {
					if (card.number != (top.number + 1) % 13 &&
							card.number != (top.number + 12) % 13)
						return "out of sequence";
					if (card.suit != top.suit)
						return "wrong suit";
					direction = card.number - top.number;
				} else {
					if (card.number != (top.number + direction + 13) % 13)
						return "out of sequence";
					if (card.suit != top.suit)
						return "wrong suit";
				}
			"row" =>
				if (nc == 0 || sel.stack.getattr("type") == "chokey")
					return "you wish!";
				top := cardlib->getcard(stack.children[nc - 1]);
				if (card.suit != top.suit)
					return "wrong suit";
				if (card.number != (top.number + 1) % 13 &&
						card.number != (top.number + 12) % 13)
					return "out of sequence";
			"chokey" =>
				if (nc != 0)
					return "only one card allowed there";
			* =>
				return "can't move there";
			}
			sel.transfer(stack, -1);
		}
	}
	return nil;
}

startclique()
{
	addlayobj, addlayframe: import cardlib;

	entry := clique.newobject(nil, All, "widget entry");
	entry.setattr("command", "say", All);

	but := clique.newobject(nil, All, "widget button");
	but.setattr("text", "Redeal", All);
	but.setattr("command", "redeal", All);
	allow->add(REDEAL, Cmember.index(0).p, "redeal");

	addlayframe("topf", nil, nil, dTOP|EXPAND|FILLX|aUPPERCENTRE, dTOP);
	addlayobj(nil, "topf", nil, dLEFT, but);
	addlayobj(nil, "topf", nil, dLEFT|EXPAND|FILLX, entry);

	addlayframe("arena", nil, nil, dTOP|EXPAND|FILLX|FILLY, dTOP);

	addlayframe("left", "arena", nil, dLEFT|EXPAND, dTOP);
	addlayframe("central", "arena", nil, dLEFT|EXPAND, dTOP);
	addlayframe("right", "arena", nil, dLEFT|EXPAND, dTOP);

	rows = array[10] of {* => newstack(nil, Rowpilespec, "row")};
	central = array[4] of {* => newstack(nil, Centralpilespec, "central")};
	chokey = newstack(nil, Centralpilespec, "chokey");

	deck = clique.newobject(nil, All, "stack");
	cardlib->makecards(deck, (0, 13), nil);
	cardlib->shuffle(deck);

	for (i := 0; i < 5; i++)
		addlayobj(nil, "left", nil, dTOP|oRIGHT, rows[i]);
	for (i = 5; i < 10; i++)
		addlayobj(nil, "right", nil, dTOP|oRIGHT, rows[i]);
	for (i = 0; i < 4; i++)
		addlayobj(nil, "central", nil, dTOP, central[i]);
	addlayobj(nil, "central", nil, dTOP, chokey);

	for (i = 0; i < 52; i++)
		cardlib->setface(deck.children[i], 1);
	# get top card from deck for central piles.
	c := deck.children[len deck.children - 1];
	v := cardlib->getcard(c);
	j := 0;
	for (i = len deck.children - 1; i >= 0; i--) {
		w := cardlib->getcard(deck.children[i]);
		if (w.number == v.number)
			deck.transfer((i, i + 1), central[j++], -1);
	}
	for (i = 0; i < 10; i += 5) {
		for (j = i; j < i + 4; j++)
			deck.transfer((0, 5), rows[j], -1);
		deck.transfer((0, 4), rows[j], -1);
	}
}

redeal()
{
	for (i := 0; i < len rows; i++)
		cardlib->discard(rows[i], deck, 0);
	cardlib->shuffle(deck);

	i = 0;
	while ((n := len deck.children) > 0) {
		l, r: int;
		if (n >= 10)
			l = r = 5;
		else {
			l = n / 2;
			r = n - l;
		}
		deck.transfer((0, l), rows[i], 0);
		deck.transfer((0, r), rows[i + 5], 0);
		i++;
	}

	n = cardlib->nmembers();
	for (i = 0; i < n; i++)
		Cmember.index(i).sel.set(nil);
}

newstack(parent: ref Object, spec: Stackspec, stype: string): ref Object
{
	stack := cardlib->newstack(parent, nil, spec);
	stack.setattr("type", stype, None);
	stack.setattr("actions", "click", 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);
	}
}

archivearray(a: array of ref Object, name: string)
{
	for (i := 0; i < len a; i++)
		cardlib->setarchivename(a[i], name + string i);
}

unarchivearray(a: array of ref Object, name: string)
{
	for (i := 0; i < len a; i++)
		a[i] = cardlib->getarchiveobj(name + string i);
}