#define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include static const wchar_t moving = L'\u2592'; static const wchar_t still = L'\u2593'; static const wchar_t wall = L'\u2588'; static const wchar_t empty = L' '; static wchar_t** 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) { struct timeval time; struct timezone zone = {.tz_dsttime = 0, .tz_minuteswest = 0}; gettimeofday(&time, &zone); seed = (uint32_t)time.tv_usec; } static int nextRandom(int max) { seed = seed * 27697u + 13711u; int32_t random = ((int32_t)(seed >> 1)) % max; return random; } static void initGamefield(int w, int h) { width = w + 3; height = h + 2; fields = (wchar_t**)malloc(sizeof(wchar_t*) * (size_t)height); for(int y = 0; y < height; y++) { fields[y] = (wchar_t*)malloc(sizeof(wchar_t) * (size_t)width); for(int x = 0; x < width; x++) { fields[y][x] = empty; } fields[y][width - 1] = '\0'; } for(int y = 0; y < height; y++) { fields[y][0] = wall; fields[y][width - 2] = wall; } for(int x = 0; x < width - 1; x++) { fields[0][x] = wall; fields[height - 1][x] = wall; } } static void printGamefield(void) { wchar_t* line = malloc(sizeof(wchar_t) * (size_t)width * 2); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { line[x * 2] = fields[y][x]; line[x * 2 + 1] = fields[y][x]; } mvaddwstr(y, 0, line); } free(line); } static void deleteGamefield(void) { for(int i = 0; i < height; i++) { free(fields[i]); } free(fields); } static void setField(int x, int y, wchar_t c) { if(x >= 0 && y >= 0 && x < width - 3 && y < height - 2) { fields[y + 1][x + 1] = c; } } static wchar_t getField(int x, int y) { if(x >= 0 && y >= 0 && x < width - 3 && y < height - 2) { return fields[y + 1][x + 1]; } return 0; } static int canMove(int x, int y) { wchar_t 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; switch(nextRandom(7)) { // .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.. // .X.. // .X.. // .X.. case 3: 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; // X.. // XX. // .X. case 4: 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 5: 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 6: 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; } 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 - 2; y++) { int remove = 1; for(int x = 0; x < width - 3; x++) { remove = remove && getField(x, y) == still; } if(remove) { for(int ry = y; ry > 0; ry--) { for(int rx = 0; rx < width - 3; 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; }