💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › games › patience.c captured on 2021-12-17 at 13:26:06.

View Raw

More Information

-=-=-=-=-=-=-

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
typedef struct Card Card;
struct Card{int v; Card *next;}deck[52], *stock, *waste, *found[4], *tab[7], **from;
enum{GAP = 12};
int
foundation(Card **to){
	Card *tmp;
	if((*from)->v % 13 == 0 && *to != nil
	|| (*from)->v % 13 && (*to == nil || (*from)->v != (*to)->v + 1))
		return 0;
	tmp = *to;
	*to = *from;
	*from = (*from)->next;
	if(*from != nil && (*from)->v < 0)
		(*from)->v += 52;
	(*to)->next = tmp;
	return 1;
}
int
tableau(Card **to){
	Card *c, *tmp;
	int rank;
	rank = *to == nil ? 12 : (*to)->v % 13 - 1;
	if(rank < 0 || (*from)->v % 13 != rank &&
	(from == &waste || from >= found && from <= found + 3))
		return 0;
	for(c = *from; c->v % 13 != rank && c->next != nil && c->next->v > 0;)
		c = c->next;
	if(c->v % 13 != rank || rank != 12 &&
	(c->v >= 13 & c->v < 39) == ((*to)->v >= 13 & (*to)->v < 39))
		return 0;
	tmp = c->next;
	if(tmp != nil && tmp->v < 0)
		tmp->v += 52;
	c->next = *to;
	*to = *from;
	*from = tmp;
	return 1;
}
void
automove(void){
	int i;
	for(i = 0; i < 4; i++)
		if(foundation(found + i))
			return;
	for(i = 0; i < 7 && !tableau(tab + i); i++)
		;
}
void
threadmain(int, char**){
	Mousectl *m;
	Keyboardctl *kbd;
	Card *c;
	Rune k;
	Rectangle r;
	Image *bg, *fg, *tmp, *back, *cards;
	int w, h, i, j, b;
	char *s = "restocks: 0";
	enum{RESIZE, MOUSE, KEYBOARD};
	Alt a[] = {
		[RESIZE]{nil, nil, CHANRCV},
		[MOUSE]{nil, nil, CHANRCV},
		[KEYBOARD]{nil, &k, CHANRCV},
		{nil, nil, CHANEND}
	};
	if(initdraw(nil, nil, "patience") < 0)
		sysfatal("initdraw: %r");
	if((i = open("/sys/games/lib/cards/default.bit", OREAD)) < 0)
		sysfatal("couldn't find cards: %r");
	if((cards = readimage(display, i, 0)) == nil)
		sysfatal("readimage: /sys/games/lib/cards/default.bit bad format");
	close(i);
	w = Dx(cards->r) / 13;
	h = Dy(cards->r) / 4;
	bg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DDarkgreen);
	fg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DPaleyellow);
	tmp = allocimage(display, Rect(0, 0, 4, 4), RGBA32, 1, DBlue);
	back = allocimage(display, Rect(0, 0, w, h), RGBA32, 0, DNofill);
	if(bg == nil || fg == nil || tmp == nil || back == nil)
		sysfatal("allocimage: %r");
	draw(tmp, Rect(2, 2, 3, 3), fg, nil, ZP);
	draw(back, back->r, tmp, nil, ZP);
	freeimage(tmp);
	border(back, back->r, 1, fg, ZP);
	if((m = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	a[RESIZE].c = m->resizec;
	a[MOUSE].c = m->c;
	a[MOUSE].v = &m->Mouse;
	if((kbd = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");
	a[KEYBOARD].c = kbd->c;
	srand(truerand());
	for(j = 0; j < 52; j++)
		deck[j].v = j;
	while(j > 1){
		i = nrand(j--);
		b = deck[i].v;
		deck[i].v = deck[j].v;
		deck[j].v = b;
	}
	stock = deck;
	for(i = 0; i < 22; i++)
		deck[i].next = deck + i + 1;
	waste = deck + ++i;
	for(j = 0; j < 7; j++){
		tab[j] = deck + ++i;
		for(b = 0; b < j; b++){
			deck[i].next = deck + i + 1;
			deck[++i].v -= 52;
		}
	}
	sendul(m->resizec, 1);
	for(;;)
		switch(alt(a)){
		case MOUSE:
			if(!m->buttons)
				break;
			b = m->buttons;
			do readmouse(m); while(m->buttons);
			i = m->xy.x - (screen->r.min.x + screen->r.max.x - 7 * w - 6 * GAP >> 1);
			j = m->xy.y - screen->r.min.y - font->height - 2 * GAP;
			if(i < 0 || j < 0 || j < h + GAP && j >= h || i % (w + GAP) >= w)
				from = nil;
			else if(j < h)
				switch(i /= w + GAP){
				case 0:
					from = nil;
					if(stock == nil){
						if(waste == nil)
							break;
						s[10] = s[10] == '9' ? 'A' : s[10] + 1;
						while(waste != nil){
							c = waste->next;
							waste->next = stock;
							stock = waste;
							waste = c;
						}
					}
					c = stock->next;
					stock->next = waste;
					waste = stock;
					stock = c;
					break;
				case 1:
					if(from == &waste || b & 6){
						from = &waste;
						automove();
						from = nil;
					}
					else
						from = waste == nil ? nil : &waste;
					break;
				case 3: case 4: case 5: case 6:
					i -= 3;
					if(b & 6)
						from = found[i] == nil ? nil : found + i;
					if(from == found + i)
						automove();
					else if(from != nil)
						foundation(found + i);
					else if(found[i] != nil){
						from = found + i;
						break;
					}
				default:
					from = nil;
					break;
				}
			else{
				i /= w + GAP;
				if(i >= 7 || tab[i] == nil && (from == nil || j >= 2 * h + GAP))
					from = nil;
				else if(tab[i] == nil){
					tableau(tab + i);
					from = nil;
				}
				else{
					for(c = tab[i]; c->next != nil; c = c->next)
						j -= c->next->v < 0 ? h / 8 : h / 4;
					if(j >= 2 * h + GAP)
						from = nil;
					else if(from == tab + i || b & 6){
						from = tab + i;
						automove();
						from = nil;
					}
					else if(from == nil)
						from = tab + i;
					else{
						tableau(tab + i);
						from = nil;
					}
				}
			}
			if(0)
		case RESIZE:
			if(getwindow(display, Refnone) < 0)
				sysfatal("getwindow: %r");
			draw(screen, screen->r, bg, nil, ZP);
			r.min.x = screen->r.min.x + screen->r.max.x - 7 * w - 6 * GAP >> 1;
			r.min.y = screen->r.min.y + GAP;
			string(screen, r.min, fg, ZP, font, s);
			r.min.y += font->height + GAP;
			r.max.x = r.min.x + w;
			r.max.y = r.min.y + h;
			if(stock != nil)
				draw(screen, r, back, nil, ZP);
			else
				border(screen, r, 1, fg, ZP);
			r.min.x = r.max.x + GAP;
			r.max.x = r.min.x + w;
			if(waste == nil)
				border(screen, r, 1, fg, ZP);
			else{
				draw(screen, r, cards, nil,
					Pt(waste->v % 13 * w, waste->v / 13 * h));
				if(from == &waste)
					border(screen, r, -GAP / 2, fg, ZP);
			}
			r.max.x += w + GAP;
			for(i = 0; i < 4; i++){
				r.min.x = r.max.x + GAP;
				r.max.x = r.min.x + w;
				if(found[i] == nil)
					border(screen, r, 1, fg, ZP);
				else
					draw(screen, r, cards, nil,
						Pt(found[i]->v % 13 * w, found[i]->v / 13 * h));
				if(from == found + i)
					border(screen, r, -GAP / 2, fg, ZP);
			}
			for(i = 6; i >= 0; i--){
				r.min.y = screen->r.min.y + font->height + h + 3 * GAP;
				if(tab[i] == nil){
					r.max.y = r.min.y + h;
					border(screen, r, 1, fg, ZP);
				}
				else{
					for(c = tab[i]; c->next != nil; c = c->next)
						r.min.y += c->next->v < 0 ? h / 8 : h / 4;
					r.max.y = r.min.y + h;
					draw(screen, r, cards, nil, Pt(tab[i]->v % 13 * w, tab[i]->v / 13 * h));
					if(from == tab + i){
						r.min.y = r.max.y;
						r.max.y = r.min.y + GAP / 2;
						draw(screen, r, fg, nil, ZP);
						r.min.y -= h;
					}
					for(c = tab[i]->next; c != nil; c = c->next){
						r.max.y = r.min.y;
						if(c->v < 0){
							r.min.y = r.max.y - h / 8;
							draw(screen, r, back, nil, ZP);
						}
						else{
							r.min.y = r.max.y - h / 4;
							draw(screen, r, cards, nil, Pt(c->v % 13 * w, c->v / 13 * h));
						}
					}
				}
				r.max.x = r.min.x - GAP;
				r.min.x = r.max.x - w;
			}
			flushimage(display, 1);
			break;
		case KEYBOARD:
			switch(k){
			case Kdel: case 'q':
				threadexitsall(nil);
			case Kesc:
				if((i = open("/dev/wctl", OWRITE)) >= 0){
					write(i, "hide", 4);
					close(i);
				}
				break;
			}
			break;
		default:
			sysfatal("Apollinaris medicus Titi Impe hic cacavit bene");
		}
}