shithub: cflood

ref: 8d4482800b46dbcdb67d49d9ba35bf0a03aa205c
dir: /games/cflood.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

enum {
	Ssmall,
	Snormal,
	Slarge,

	Flood = 1<<7,
	ColorMask = 0x0f,

	NumColors = 6,

	ButtonSize = 32
};

const int sizes[] = {14, 21, 28};
const int turns[] = {25, 35, 50};
const ulong srccolors[NumColors] = {
	0x6060a8ff,
	0xf6f61dff,
	0x46b0e0ff,
	0x7ea020ff,
	0xf070a0ff,
	0xdc4a20ff
};
char *mstr[] = {
	"14x14 / 25",
	"25x25 / 35",
	"28x28 / 50",
	"exit",
	0
};

static int size;
static int wait4click;
static int turnsleft;
static uchar cells[28*28]; // enough for maximal size
static Image *colors[NumColors];
static Rectangle buttons[NumColors];

static void
floodneighbours(uchar color, int x, int y);

static void
redraw(Image *screen) {
	Rectangle r = screen->r;
	draw(screen, r, display->white, nil, ZP);

	const uchar *cell = &cells[0];
	int w = Dx(r), h = Dy(r) - ButtonSize - 2;
	int c = (w < h ? w : h) / size;
	w = c*size;

	// cells
	for(int x = r.min.x; x < r.min.x+size*c; x+=c) {
		for(int y = r.min.y; y < r.min.y+size*c; y+=c) {
			Rectangle r = Rect(x, y, x+c, y+c);
			draw(screen, r, colors[*cell & ColorMask], nil, ZP);
			cell++;
		}
	}

	// buttons
	int startx = r.min.x + (w/2) - NumColors*ButtonSize/2;
	int y = r.min.y + h;
	for(int i = 0, x = startx; i < NumColors; i++, x += ButtonSize) {
		buttons[i] = Rect(x, y, x+ButtonSize, y+ButtonSize);
		draw(screen, buttons[i], colors[i], nil, ZP);
	}

	flushimage(display, 1);
}

static void
floodrecurse(uchar color, int x, int y) {
	uchar *c = &cells[x + y*size];
	if((*c & Flood) == 0 && (*c & ColorMask) == color) {
		*c = color | Flood;
		floodneighbours(color, x, y);
	}
}

static void
floodneighbours(uchar color, int x, int y) {
	cells[x + y*size] = color | Flood;

	if(x > 0)
		floodrecurse(color, x-1, y);
	if(x < size-1)
		floodrecurse(color, x+1, y);
	if(y > 0)
		floodrecurse(color, x, y-1);
	if(y < size-1)
		floodrecurse(color, x, y+1);
}

static int
reflood(uchar color) {
	color &= ColorMask;

	int n = 0;
	for(int x = 0; x < size; x++)
		for(int y = 0; y < size; y++)
			if(cells[x + y*size] & Flood) {
				floodneighbours(color, x, y);
				n++;
			}
	return n;
}

static void
flood(uchar color) {
	if((cells[0] & Flood) != 0 && (cells[0] & ColorMask) == color)
		return;

	if(!turnsleft)
		return;

	int n = reflood(color);
	redraw(screen);

	if(n == size*size) {
		// win
		wait4click = 1;
	} else if(--turnsleft == 0) {
		// fail
		wait4click = 1;
	}
}

static void
newgame(int sid) {
	size = sizes[sid];
	turnsleft = turns[sid];

	// randomize
	uchar *c = &cells[0];
	for(int i = 0; i < size*size; i++) {
		*c++ = nrand(NumColors);
	}

	cells[0] |= Flood;
	reflood(cells[0]);
	redraw(screen);
}

void
eresized(int new)
{
	if(new && getwindow(display, Refnone) < 0)
		fprint(2, "can't reattach to window");
	redraw(screen);
}

void main(int, char**) {
	Menu menu;

	if(initdraw(0, 0, "cflood") < 0)
		sysfatal("initdraw failed");

	Rectangle r = Rect(0, 0, 1, 1);
	for(int i = 0; i < NumColors; i++) {
		colors[i] = allocimage(display, r, CMAP8, 1, srccolors[i]);
	}

	einit(Emouse);
	menu.item = mstr;
	menu.lasthit = 0;
	srand(time(0));

	int sid = Snormal;
	newgame(sid);

	for(int mold = 0;;) {
		Event e;
		int key = event(&e);
		Mouse m = e.mouse;

		if(key != Emouse)
			continue;

		if(m.buttons & 4) {
			int p = emenuhit(3, &m, &menu);
			if(p >= Ssmall && p <= Slarge)
				newgame(sid = p);
			else if(p > 0)
				break;
		} else if(wait4click && !mold && m.buttons != mold) {
			wait4click = 0;
			newgame(sid);
		} else if(m.buttons & 1) {
			for(int i = 0; i < NumColors; i++) {
				if(ptinrect(m.xy, buttons[i])) {
					flood(i);
					break;
				}
			}
		}

		mold = m.buttons;
	}
}