Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <keyboard.h>
#include <event.h>
// 2048.
// y increases down, x increases right.
// square board of this size
#define BOARDSIZE 4
// square boarder
#define BORDER 4
typedef struct Tile Tile;
struct Tile {
int value;
Image *color;
};
Tile grid[BOARDSIZE][BOARDSIZE];
Tile *grid1 = &grid[0][0];
int score = 0;
int gameover = 0;
Font *bigfont;
Image *colors[13];
Image *
val2col(int val)
{
int i;
if(val == 0)
return colors[0];
for(i = 32; (val & (1<<i)) == 0; i--)
;
if(i < nelem(colors))
return colors[i];
return colors[nelem(colors)-1];
}
void
initcolors(void)
{
int i;
int cols[] = {
0xcdc0b4ff, 0xeee4daff, 0xede0c8ff, 0xf2b179ff, 0xf59563ff,
0xf67c5fff, 0xf65e36ff, 0xedcf72ff, 0xedcc61ff,
0xedc850ff, 0xedc53fff, 0xedc22eff, 0x3c3a32ff };
for(i = 0; i < nelem(cols); i++) {
colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cols[i]);
}
}
void
initboard(void)
{
int x, y, x2, y2;
for(y = 0; y < BOARDSIZE; y++) {
for(x = 0; x < BOARDSIZE; x++) {
grid[y][x].value = 0;
grid[y][x].color = val2col(0);
}
}
y = nrand(BOARDSIZE);
x = nrand(BOARDSIZE);
grid[y][x].value = 2;
grid[y][x].color = val2col(2);;
y2 = nrand(BOARDSIZE);
x2 = nrand(BOARDSIZE);
while(y == y2 || x == x2) {
y2 = nrand(BOARDSIZE);
x2 = nrand(BOARDSIZE);
}
grid[y2][x2].value = 2;
grid[y2][x2].color = val2col(2);;
score = 0;
}
void
redraw(void)
{
Rectangle r;
Point p, sp;
int x, y; // grid indexes
int width, height; // of each tile
char buf[64];
Point strp;
int strwid;
r = insetrect(screen->r, BORDER);
sp = r.min;
r.min.y += font->height;
p = r.min;
width = Dx(r) / BOARDSIZE;
height = Dy(r) / BOARDSIZE;
draw(screen, screen->r, display->black, nil, ZP);
snprint(buf, sizeof buf, "Score: %d", score);
string(screen, sp, display->white, ZP, font, buf);
for(y = 0; y < BOARDSIZE; y++) {
for(x = 0; x < BOARDSIZE; x++) {
r = Rect(p.x + (x * width), p.y + (y * height), p.x + (x * width) + width, p.y + (y * height) + height);
r = insetrect(r, BORDER);
draw(screen, r, grid[y][x].color, nil, ZP);
if(grid[y][x].value == 0)
continue;
snprint(buf, sizeof buf, "%d", grid[y][x].value);
strwid = stringwidth(bigfont, buf);
strp = Pt(Dx(r)/2, Dy(r)/2);
strp.x -= strwid / 2;
string(screen, addpt(r.min, strp), display->black, ZP, bigfont, buf);
}
}
}
void
addrandom(void)
{
int x, y, nfree;
Tile **tfree;
nfree = 0;
for(x = 0; x < BOARDSIZE; x++) {
for(y = 0; y < BOARDSIZE; y++) {
if(grid[x][y].value == 0)
nfree++;
}
}
if(nfree > 0) {
tfree = mallocz(sizeof(Tile*) * nfree, 1);
nfree = 0;
for(x = 0; x < BOARDSIZE; x++) {
for(y = 0; y < BOARDSIZE; y++) {
if(grid[x][y].value == 0)
tfree[nfree++] = &grid[x][y];
}
}
// 10% chance of 4.
if(nrand(10) == 0)
x = 4;
else
x = 2;
nfree = nrand(nfree);
tfree[nfree]->value = x;
tfree[nfree]->color = val2col(x);
free(tfree);
}
}
void
buildrow(Tile *out[BOARDSIZE], int n, int dir)
{
int i, start, add;
add = start = 0;
switch(dir) {
case Kright:
add = 1;
start = n*BOARDSIZE;
break;
case Kleft:
add = -1;
start = n*BOARDSIZE + (BOARDSIZE-1);
break;
case Kdown:
add = BOARDSIZE;
start = n;
break;
case Kup:
add = -BOARDSIZE;
start = (BOARDSIZE-1)*BOARDSIZE + n;
break;
}
for(i = 0; i < BOARDSIZE; i++) {
out[i] = &grid1[add*i + start];
}
}
int
merge(Tile *row[BOARDSIZE])
{
int i, j, moved;
Tile nrow[BOARDSIZE];
for(i = 0; i < BOARDSIZE; i++) {
nrow[i].value = 0;
nrow[i].color = val2col(0);
}
j = BOARDSIZE-1;
for(i = j; i >= 0; i--) {
if(row[i]->value == 0) {
;
} else if(nrow[j].value == 0) {
nrow[j] = *row[i];
} else if(nrow[j].value == row[i]->value) {
score += nrow[j].value *= 2;
nrow[j].color = val2col(nrow[j].value);
j--;
} else {
nrow[--j] = *row[i];
}
}
moved = 0;
for(i = 0; i < BOARDSIZE; i++) {
if(nrow[i].value != row[i]->value)
moved = 1;
*row[i] = nrow[i];
}
return moved;
}
void
shift(int dir)
{
Tile *cur[BOARDSIZE];
int i;
int moved = 0;
for(i = 0; i < BOARDSIZE; i++) {
//print("%d\n", i);
buildrow(cur, i, dir);
moved = merge(cur) || moved;
}
if(moved) {
addrandom();
// check gameover here
}
}
void
main(int argc, char **argv)
{
char *fontname;
USED(argc);
USED(argv);
if(initdraw(0, 0, "2048") < 0)
sysfatal("initdraw: %r");
fontname = "/lib/font/bit/fixed/unicode.10x20.font";
if((bigfont = openfont(display, fontname)) == nil)
sysfatal("can't open font %s: %r", fontname);
einit(Ekeyboard|Emouse);
srand(time(0));
initcolors();
initboard();
redraw();
while(1) {
int key;
redraw();
flushimage(display, 1);
switch(key = ekbd()) {
case 0x7f: // p9p lacks Kdel
case 'q':
// quit
exits(0);
break;
case ' ':
// reset board
initboard();
case Kup:
case Kdown:
case Kleft:
case Kright:
shift(key);
break;
}
}
}
void
eresized(int new)
{
if(new && getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
redraw();
}