#include #include #include #include #include #include typedef enum { MOVING, STILL, EMPTY, WALL } FieldState; static FieldState** fields; static int width; static int height; static int objectX; static int objectY; static int movingObject[8]; static void (*rotateFunction)(void); static uint32_t seed = 0; static void initRandom(void) { while(seed == 0) { struct timespec time; timespec_get(&time, TIME_UTC); seed = (uint32_t)time.tv_nsec; } } static int nextRandom(int max) { seed = seed * 0x915f77f5 + 1; int32_t random = ((int32_t)(seed >> 16)) % max; return random; } static void initGamefield(int w, int h) { width = w; height = h; fields = (FieldState**)malloc(sizeof(FieldState*) * (size_t)height); for(int y = 0; y < height; y++) { fields[y] = (FieldState*)malloc(sizeof(FieldState) * (size_t)width); for(int x = 0; x < width; x++) { fields[y][x] = EMPTY; } } } static void printGamefield(void) { clear(); static const char* MAP[] = {"\u2592", "\u2593", " "}; static const char* wall = "\u2588"; int maxX = 0; int maxY = 0; getmaxyx(stdscr, maxY, maxX); int factorX = maxX / (width + 2); int factorY = maxY / (height + 2); while(factorY > 1 && factorY * 2 > factorX) { factorY--; } factorX = factorY * 2; for(int x = 0; x < (width + 2) * factorX; x++) { for(int y = 0; y < factorY; y++) { mvaddstr(y, x, wall); mvaddstr(y + (height + 1) * factorY, x, wall); } } for(int y = 0; y < height; y++) { for(int wy = y * factorY; wy < (y + 1) * factorY; wy++) { mvaddstr(wy + factorY, 0, wall); for(int x = 1; x < factorX; x++) { addstr(wall); } for(int x = 0; x < width; x++) { for(int w = 0; w < factorX; w++) { addstr(MAP[fields[y][x]]); } } for(int x = 0; x < factorX; x++) { addstr(wall); } } } } static void deleteGamefield(void) { for(int i = 0; i < height; i++) { free(fields[i]); } free(fields); } static void setField(int x, int y, FieldState state) { if(x >= 0 && y >= 0 && x < width && y < height) { fields[y][x] = state; } } static FieldState getField(int x, int y) { if(x >= 0 && y >= 0 && x < width && y < height) { return fields[y][x]; } return WALL; } static int canMove(int x, int y) { FieldState t = getField(x, y); return t == EMPTY || t == MOVING; } static void rotate33(void) { int data[8]; for(int i = 0; i < 8; i += 2) { data[i] = movingObject[i + 1] - objectY + objectX; data[i + 1] = 2 - (movingObject[i] - objectX) + objectY; if(!canMove(data[i], data[i + 1])) { return; } } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], EMPTY); movingObject[i] = data[i]; movingObject[i + 1] = data[i + 1]; } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], MOVING); } } static void rotate41(void) { int data[8]; for(int i = 0; i < 8; i += 2) { data[i] = movingObject[i + 1] - objectY + objectX; data[i + 1] = movingObject[i] - objectX + objectY; if(!canMove(data[i], data[i + 1])) { return; } } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], EMPTY); movingObject[i] = data[i]; movingObject[i + 1] = data[i + 1]; } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], MOVING); } } static void rotate22(void) { // nothing to do } static void rotate(void) { rotateFunction(); } static void spawnObject(int x, int y) { objectX = x; objectY = y; int u = nextRandom(8); objects[u]++; switch(u) { // .X. // XXX // ... case 0: movingObject[0] = x + 1; movingObject[1] = y + 0; movingObject[2] = x + 0; movingObject[3] = y + 1; movingObject[4] = x + 1; movingObject[5] = y + 1; movingObject[6] = x + 2; movingObject[7] = y + 1; rotateFunction = rotate33; break; // .X. // .X. // .XX case 1: movingObject[0] = x + 1; movingObject[1] = y + 0; movingObject[2] = x + 1; movingObject[3] = y + 1; movingObject[4] = x + 1; movingObject[5] = y + 2; movingObject[6] = x + 2; movingObject[7] = y + 2; rotateFunction = rotate33; break; case 2: // .X. // .X. // XX. movingObject[0] = x + 1; movingObject[1] = y + 0; movingObject[2] = x + 1; movingObject[3] = y + 1; movingObject[4] = x + 1; movingObject[5] = y + 2; movingObject[6] = x + 0; movingObject[7] = y + 2; rotateFunction = rotate33; break; // X.. // XX. // .X. case 3: movingObject[0] = x + 0; movingObject[1] = y + 0; movingObject[2] = x + 0; movingObject[3] = y + 1; movingObject[4] = x + 1; movingObject[5] = y + 1; movingObject[6] = x + 1; movingObject[7] = y + 2; rotateFunction = rotate33; break; // .X. // XX. // X.. case 4: movingObject[0] = x + 1; movingObject[1] = y + 0; movingObject[2] = x + 0; movingObject[3] = y + 1; movingObject[4] = x + 1; movingObject[5] = y + 1; movingObject[6] = x + 0; movingObject[7] = y + 2; rotateFunction = rotate33; break; // XX // XX case 5: movingObject[0] = x + 0; movingObject[1] = y + 0; movingObject[2] = x + 1; movingObject[3] = y + 0; movingObject[4] = x + 0; movingObject[5] = y + 1; movingObject[6] = x + 1; movingObject[7] = y + 1; rotateFunction = rotate22; break; // .X.. // .X.. // .X.. // .X.. case 6: case 7: movingObject[0] = x + 0; movingObject[1] = y + 0; movingObject[2] = x + 0; movingObject[3] = y + 1; movingObject[4] = x + 0; movingObject[5] = y + 2; movingObject[6] = x + 0; movingObject[7] = y + 3; rotateFunction = rotate41; break; } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], MOVING); } } static int moveObject(int x, int y) { for(int i = 0; i < 8; i += 2) { if(!canMove(movingObject[i] + x, movingObject[i + 1] + y)) { return 0; } } objectX += x; objectY += y; for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], EMPTY); movingObject[i] += x; movingObject[i + 1] += y; } for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], MOVING); } return 1; } static void removeRows(void) { for(int y = 0; y < height; y++) { int remove = 1; for(int x = 0; x < width; x++) { remove = remove && getField(x, y) == STILL; } if(remove) { for(int ry = y; ry > 0; ry--) { for(int rx = 0; rx < width; rx++) { setField(rx, ry, getField(rx, ry - 1)); } } } } } static void onObjectFall(void) { if(!moveObject(0, 1)) { for(int i = 0; i < 8; i += 2) { setField(movingObject[i], movingObject[i + 1], STILL); } removeRows(); spawnObject(0, 0); } } int main(void) { initRandom(); setlocale(LC_ALL, "en_US.UTF-8"); initscr(); cbreak(); keypad(stdscr, TRUE); noecho(); nodelay(stdscr, TRUE); curs_set(0); initGamefield(10, 18); spawnObject(0, 0); long lag = 0; clock_t lastTime = clock(); long ticks = 1000000; int running = 1; while(running) { int key = getch(); if(key == KEY_LEFT) { moveObject(-1, 0); } else if(key == KEY_RIGHT) { moveObject(1, 0); } else if(key == KEY_DOWN) { onObjectFall(); } else if(key == KEY_UP) { rotate(); } else if(key == 'q') { running = 0; } clock_t time = clock(); lag += (time - lastTime); lastTime = time; if(lag > ticks) { lag -= ticks; onObjectFall(); } printGamefield(); printw("%ld", lag); refresh(); // keep the game from taking a bit too much cpu usleep(1000); lag += 1000; } endwin(); deleteGamefield(); return 0; }