Kajetan Johannes Hammerle 6 months ago
commit
97d349886d
3 changed files with 438 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 360 0
      Main.c
  3. 77 0
      Makefile

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+tetris

+ 360 - 0
Main.c

@@ -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;
+}

+ 77 - 0
Makefile

@@ -0,0 +1,77 @@
+all: tetris
+
+run: tetris
+	./tetris
+	
+tetris: *.c
+	gcc -o tetris *.c -lncursesw \
+		-Wall \
+		-Walloc-zero \
+		-Walloca \
+		-Wanalyzer-too-complex \
+		-Warith-conversion \
+		-Warray-bounds=2 \
+		-Warray-parameter \
+		-Wattribute-alias=2 \
+		-Wbad-function-cast \
+		-Wbidi-chars=any \
+		-Wcast-align=strict \
+		-Wcast-qual \
+		-Wconversion \
+		-Wdate-time \
+		-Wdisabled-optimization \
+		-Wdouble-promotion \
+		-Wduplicated-branches \
+		-Wduplicated-cond \
+		-Wenum-compare \
+		-Wenum-conversion \
+		-Werror \
+		-Wextra \
+		-Wfloat-equal \
+		-Wformat-overflow=2 \
+		-Wformat-signedness \
+		-Wformat-truncation=2 \
+		-Wformat=2 \
+		-Wframe-larger-than=8388608 \
+		-Wimplicit-fallthrough=5 \
+		-Winfinite-recursion \
+		-Winit-self \
+		-Winvalid-pch \
+		-Wjump-misses-init \
+		-Wlarger-than=1073741824 \
+		-Wlogical-op \
+		-Wmissing-braces \
+		-Wmissing-declarations \
+		-Wmissing-include-dirs \
+		-Wmissing-prototypes \
+		-Wmultichar \
+		-Wnarrowing \
+		-Wnested-externs \
+		-Wnormalized=nfkc \
+		-Wnull-dereference \
+		-Wold-style-definition \
+		-Woverlength-strings \
+		-Wredundant-decls \
+		-Wshadow \
+		-Wshift-overflow=2 \
+		-Wsign-conversion \
+		-Wstack-protector \
+		-Wstack-usage=8388608 \
+		-Wstrict-overflow=2 \
+		-Wstrict-prototypes \
+		-Wstringop-overflow=4 \
+		-Wswitch-enum \
+		-Wtrampolines \
+		-Wtrivial-auto-var-init \
+		-Wundef \
+		-Wunreachable-code \
+		-Wunused-const-variable=2 \
+		-Wuse-after-free=3 \
+		-Wvla \
+		-Wwrite-strings \
+		-pedantic \
+		-pedantic-errors \
+		-O3
+	
+clean:
+	rm -f tetris