|
@@ -0,0 +1,360 @@
|
|
|
|
+#define _XOPEN_SOURCE_EXTENDED
|
|
|
|
+
|
|
|
|
+#include <curses.h>
|
|
|
|
+#include <locale.h>
|
|
|
|
+#include <ncursesw/curses.h>
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+#include <sys/time.h>
|
|
|
|
+#include <time.h>
|
|
|
|
+#include <unistd.h>
|
|
|
|
+#include <wchar.h>
|
|
|
|
+#include <wctype.h>
|
|
|
|
+
|
|
|
|
+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;
|
|
|
|
+}
|