diff options
author | David Phillips <dbphillipsnz@gmail.com> | 2016-01-12 20:41:56 +1300 |
---|---|---|
committer | David Phillips <dbphillipsnz@gmail.com> | 2016-01-12 20:41:56 +1300 |
commit | 58bef85206c7cce153400e51b247e2476e9e65a4 (patch) | |
tree | 9e9856c8c554545c44834c89fe0086f9a36abf22 | |
download | tetris-58bef85206c7cce153400e51b247e2476e9e65a4.tar.xz |
Initial commit of rough-arse Tetris
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | colour.h | 5 | ||||
-rw-r--r-- | config.mk | 2 | ||||
-rw-r--r-- | plot.c | 103 | ||||
-rw-r--r-- | plot.h | 14 | ||||
-rw-r--r-- | tetris.c | 265 | ||||
-rw-r--r-- | tetromino.h | 207 | ||||
-rw-r--r-- | window.c | 44 | ||||
-rw-r--r-- | window.h | 20 |
11 files changed, 680 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..213c075 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tetris +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..545347b --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +include config.mk + +all: tetris + +tetris: tetris.o plot.o + $(CC) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) + +clean: + rm -f tetris + find -name "*.o" -delete + +.PHONY: all clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ec5c1f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +What? +===== +Tetris. diff --git a/colour.h b/colour.h new file mode 100644 index 0000000..fc25ac9 --- /dev/null +++ b/colour.h @@ -0,0 +1,5 @@ +struct colour { + uint8_t r; + uint8_t g; + uint8_t b; +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..d3875ba --- /dev/null +++ b/config.mk @@ -0,0 +1,2 @@ +CFLAGS += -I/usr/include/SDL2 -Wall -Wextra -Werror +LDFLAGS += -lm -lsqlite3 -lSDL2 @@ -0,0 +1,103 @@ +#include <SDL.h> + +#include "colour.h" +#include "plot.h" + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; + +void plot_update() +{ + SDL_RenderPresent(renderer); +} + +void plot_rect(unsigned int x, unsigned int y, unsigned int width, unsigned int height, struct colour *c) +{ + SDL_Rect rect; + + SDL_SetRenderDrawColor(renderer, c->r, c->g, c->b, 255); + + rect.x = x; + rect.y = y; + rect.w = width; + rect.h = height; + + SDL_RenderFillRect(renderer, &rect); +} + +void plot_cell(unsigned int x, unsigned int y, struct colour *c) +{ + plot_rect( + x*CELL_SIZE+(x-1)*BORDER_THICKNESS, + y*CELL_SIZE+(y-1)*BORDER_THICKNESS, + CELL_SIZE, + CELL_SIZE, + c + ); +} + +void plot_cell_borders() +{ + int i, j; + + struct colour col = {.r = 0, .g = 0, .b = 0}; + + for (i = 1; i < WIDTH_CELLS; i++) + { + j = i*CELL_SIZE + (i-1)*BORDER_THICKNESS; + plot_rect( + j, 0, + BORDER_THICKNESS, HEIGHT_PIXELS, + &col); + } + + for (i = 1; i < HEIGHT_CELLS; i++) + { + j = i*CELL_SIZE + (i-1)*BORDER_THICKNESS; + plot_rect( + 0, j, + WIDTH_PIXELS, BORDER_THICKNESS, + &col); + } + +} + + +int plot_init() +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + fprintf(stderr, "Failed to init SDL video: %s\n", SDL_GetError()); + return 1; + } + + window = SDL_CreateWindow( + "Some title", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + WIDTH_PIXELS, + HEIGHT_PIXELS, + SDL_WINDOW_SHOWN + ); + + if (window == NULL) + { + fprintf(stderr, "Failed to create window: %s\n", SDL_GetError()); + return 1; + } + + renderer = SDL_CreateRenderer(window, 0, 0); + if (renderer == NULL) + { + fprintf(stderr, "Failed to get window renderer: %s\n", SDL_GetError()); + return 1; + } + + /* blank out the background with gray */ + SDL_SetRenderDrawColor(renderer, 0x33, 0x33, 0x33, 0xFF); + SDL_RenderFillRect(renderer, NULL); + + plot_cell_borders(); + + return 0; +} @@ -0,0 +1,14 @@ +void plot_rect(unsigned int x, unsigned int y, unsigned int width, unsigned int height, struct colour *c); +void plot_cell(unsigned int x, unsigned int y, struct colour *c); +void plot_cell_borders(); +int plot_init(); +void plot_update(); + +#define CELL_SIZE 25 +#define BORDER_THICKNESS 2 +#define WIDTH_CELLS 10 +#define HEIGHT_CELLS 20 + +#define WIDTH_PIXELS (WIDTH_CELLS * CELL_SIZE + BORDER_THICKNESS * (WIDTH_CELLS-1)) +#define HEIGHT_PIXELS (HEIGHT_CELLS * CELL_SIZE + BORDER_THICKNESS * (HEIGHT_CELLS-1)) + diff --git a/tetris.c b/tetris.c new file mode 100644 index 0000000..3b6364a --- /dev/null +++ b/tetris.c @@ -0,0 +1,265 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <SDL.h> +#include <time.h> + +#include "colour.h" +#include "tetromino.h" +#include "plot.h" + +#define TETROMINO_WIDTH 4 +#define TETROMINO_HEIGHT 4 +#define TETROMINO_AREA (TETROMINO_HEIGHT*TETROMINO_WIDTH) + + +#define MIN(a,b) (a<b? a : b) + + +void main_loop(); + + + + +struct colour palette[] = { + {.r=0x00,.g=0x00,.b=0x00}, + {.r=0x00,.g=0x00,.b=0xFF}, + {.r=0x00,.g=0xFF,.b=0x00}, + {.r=0x00,.g=0xFF,.b=0xFF}, + {.r=0xFF,.g=0x00,.b=0x00}, + {.r=0xFF,.g=0x00,.b=0xFF}, + {.r=0xFF,.g=0xFF,.b=0x00}, + {.r=0xFF,.g=0xFF,.b=0xFF} +}; + + +void draw_board(struct colour* (*board)[WIDTH_CELLS][HEIGHT_CELLS]) +{ + unsigned int x,y; + for (y = 0; y < HEIGHT_CELLS; y++) + { + for (x = 0; x < WIDTH_CELLS; x++) + { + plot_cell(x,y, (*board)[x][y]); + } + } +} + +void draw_piece(int x, int y, struct colour *c, char (*piece)[4][4]) +{ + int px, py, ipx, ipy, end_x, end_y, wx, wy; + ipx = (x < 0)? -x : 0; + ipy = (y < 0)? -y : 0; + end_y = MIN(HEIGHT_CELLS, y + 4); + end_x = MIN(WIDTH_CELLS, x + 4); + + for (wy = (y < 0)? 0 : y, py = ipy; wy < end_y; wy++, py++) + for (wx = (x < 0)? 0 : x, px = ipx; wx < end_x; wx++, px++) + if ((*piece)[px][py]) + plot_cell(wx, wy, c); +} + +void drop_piece(int x, int y, struct piece *piece, struct colour* (*board)[WIDTH_CELLS][HEIGHT_CELLS]) +{ + int px, py, ipx, ipy, end_x, end_y, wx, wy; + ipx = (x < 0)? -x : 0; + ipy = (y < 0)? -y : 0; + end_y = MIN(HEIGHT_CELLS, y + 4); + end_x = MIN(WIDTH_CELLS, x + 4); + + for (wy = (y < 0)? 0 : y, py = ipy; wy < end_y; wy++, py++) + for (wx = (x < 0)? 0 : x, px = ipx; wx < end_x; wx++, px++) + if ((*piece->bitmap)[px][py]) + (*board)[wx][wy] = piece->colour; +} + +Uint32 gravity_callback(Uint32 interval, void *param) +{ + (void)param; /* solves unused parameter warn+error */ + SDL_Event e; + SDL_UserEvent ue; + ue.type = SDL_USEREVENT; + ue.code = 0; + ue.data1 = 0; + ue.data2 = 0; + + e.type = SDL_USEREVENT; + e.user = ue; + + SDL_PushEvent(&e); + return interval; +} + +int main() +{ + srand(time(NULL)); + plot_init(); + main_loop(); +} + + +int hit_floor(int x, int y, struct piece *held, struct colour* (*board)[WIDTH_CELLS][HEIGHT_CELLS]) +{ + int px,py; + for (px = 0; px < 4; px++) + { + /* seek to first cell of column */ + py = 0; + while (py < 4 && (*held->bitmap)[px][py] == 0) + py++; + + /* column has no cells? no collision possible. NEXT! */ + if (py == 4) + continue; + + while (py < 4 && (*held->bitmap)[px][py]) + py++; + + if ( y + py >= HEIGHT_CELLS + || (*board)[x + px][y + py] != &(palette[0])) + return 1; + } + return 0; +} + +int hit_side(int x, int y, struct piece *held, struct colour* (*board)[WIDTH_CELLS][HEIGHT_CELLS]) +{ + int px,py; + for (py = 0; py < 4; py++) + { + for (px = 0; px < 4; px++) + { + if ((*held->bitmap)[px][py] + && + ( x + px >= WIDTH_CELLS + || x + px < 0 + || (*board)[x + px][y + py] != &(palette[0]) + )) + return 1; + } + } + return 0; +} + +void update_bitmap(struct piece *held) +{ + held->bitmap = &(tetrominoes[0][held->type][held->rotation]); +} + +void new_piece(struct piece *held) +{ + held->colour = &palette[rand() % 7 + 1]; + held->type = rand()%7; + held->rotation= rand()%4; + update_bitmap(held); +} + +void rotate(struct piece *held) +{ + held->rotation++; + if (held->rotation >= 4) + held->rotation -= 4; + + update_bitmap(held); +} + + +void clear_rows(struct colour* (*board)[WIDTH_CELLS][HEIGHT_CELLS]) +{ + char row; + int x, y, x1, y1; + for (y = 0; y < HEIGHT_CELLS; y++) + { + row = 1; + for (x = 0; x < WIDTH_CELLS; x++) + { + if ((*board)[x][y] == &(palette[0])) + { + row = 0; + break; + } + } + if (!row) + continue; + + for (y1 = y; y1 > 0; y1--) + for (x1 = 0; x1 < WIDTH_CELLS; x1++) + (*board)[x1][y1] = (*board)[x1][y1-1]; + + for (x1 = 0; x1 < WIDTH_CELLS; x1++) + (*board)[x1][y1] = &(palette[0]); + } +} + +void main_loop() +{ + struct colour *board[WIDTH_CELLS][HEIGHT_CELLS]; + SDL_Event e = {0}; + bool running = false; + int x = 0; + int y = 0; + int last_x = 0; + int last_y = 1; + struct piece held; + new_piece(&held); + + for (y = 0; y < HEIGHT_CELLS; y++) + for (x = 0; x < WIDTH_CELLS; x++) + board[x][y] = &(palette[0]); + + running = true; + last_x = last_y = x = y = 0; + SDL_AddTimer(500, &gravity_callback, NULL); + char lockout; + while (running) + { + lockout = 0; + clear_rows(&board); + if (hit_side(x, y, &held, &board)) + { + x = last_x; + } + if (hit_floor(x, y, &held, &board)) + { + lockout = 1; + } + draw_board(&board); + draw_piece(x, y, held.colour, held.bitmap); + plot_update(); + while(SDL_PollEvent(&e)) + { + switch (e.type) + { + case SDL_USEREVENT: + if (lockout) { + drop_piece(x, y, &held, &board); + last_x = last_y = x = y = 0; + new_piece(&held); + lockout = 0; + } else { + last_y = y++; /* gravity */ + last_x = x; + } + break; + case SDL_QUIT: + fprintf(stderr, "quit\n"); + running = false; + break; + + case SDL_KEYDOWN: + switch (e.key.keysym.sym) + { + case SDLK_a: last_x = x--; break; + case SDLK_d: last_x = x++; break; + case SDLK_w: rotate(&held); break; + case SDLK_q: + running = false; + break; + } + break; + default: + break; + } + } + } +} diff --git a/tetromino.h b/tetromino.h new file mode 100644 index 0000000..c0a33f0 --- /dev/null +++ b/tetromino.h @@ -0,0 +1,207 @@ +#define TETROMINO_WIDTH 4 +#define TETROMINO_HEIGHT 4 +#define TETROMINO_AREA (TETROMINO_HEIGHT*TETROMINO_WIDTH) +/* +struct tetromino { + char name; + uint8_t r; + uint8_t g; + uint8_t b; + unsigned int width; + unsigned int height; + char data[4][16]; +};*/ + +struct piece +{ + struct colour *colour; + char (*bitmap)[4][4]; + int type; + int rotation; +}; + +char tetromino_I[4][4][4] = { + { + {0,0,0,0}, + {1,1,1,1}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,0,1,0}, + {0,0,1,0}, + {0,0,1,0}, + {0,0,1,0} + },{ + {0,0,0,0}, + {0,0,0,0}, + {1,1,1,1}, + {0,0,0,0} + },{ + {0,1,0,0}, + {0,1,0,0}, + {0,1,0,0}, + {0,1,0,0} + } +}; + + +char tetromino_J[4][4][4] = { + { + {1,0,0,0}, + {1,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,1,0}, + {0,1,0,0}, + {0,1,0,0}, + {0,0,0,0} + },{ + {0,0,0,0}, + {1,1,1,0}, + {0,0,1,0}, + {0,0,0,0} + },{ + {0,1,0,0}, + {0,1,0,0}, + {1,1,0,0}, + {0,0,0,0} + } +}; + + +char tetromino_L[4][4][4] = { + { + {0,0,1,0}, + {1,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,0,0}, + {0,1,0,0}, + {0,1,1,0}, + {0,0,0,0} + },{ + {0,0,0,0}, + {1,1,1,0}, + {1,0,0,0}, + {0,0,0,0} + },{ + {1,1,0,0}, + {0,1,0,0}, + {0,1,0,0}, + {0,0,0,0} + } +}; + + +char tetromino_O[4][4][4]= { + { + {0,1,1,0}, + {0,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,1,0}, + {0,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,1,0}, + {0,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,1,0}, + {0,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + } +}; + + +char tetromino_S[4][4][4] = { + { + {0,1,1,0}, + {1,1,0,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,1,0,0}, + {0,1,1,0}, + {0,0,1,0}, + {0,0,0,0} + },{ + {0,0,0,0}, + {0,1,1,0}, + {1,1,0,0}, + {0,0,0,0} + },{ + {1,0,0,0}, + {1,1,0,0}, + {0,1,0,0}, + {0,0,0,0} + } +}; + + +char tetromino_T[4][4][4] = { + { + {1,1,1,0}, + {0,1,0,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,0,1,0}, + {0,1,1,0}, + {0,0,1,0}, + {0,0,0,0} + },{ + {0,0,0,0}, + {0,1,0,0}, + {1,1,1,0}, + {0,0,0,0} + },{ + {1,0,0,0}, + {1,1,0,0}, + {1,0,0,0}, + {0,0,0,0} + } +}; + +char tetromino_Z[4][4][4] = { + { + {1,1,0,0}, + {0,1,1,0}, + {0,0,0,0}, + {0,0,0,0} + },{ + {0,0,1,0}, + {0,1,1,0}, + {0,1,0,0}, + {0,0,0,0} + },{ + {0,0,0,0}, + {1,1,0,0}, + {0,1,1,0}, + {0,0,0,0} + },{ + {0,1,0,0}, + {1,1,0,0}, + {1,0,0,0}, + {0,0,0,0} + } +}; + + + + +char (*tetrominoes[])[4][4][4] = { + &tetromino_I, + &tetromino_J, + &tetromino_L, + &tetromino_O, + &tetromino_S, + &tetromino_T, + &tetromino_Z +}; diff --git a/window.c b/window.c new file mode 100644 index 0000000..2128f1c --- /dev/null +++ b/window.c @@ -0,0 +1,44 @@ +#include "window.h" + +#include <SDL.h> + +int display_init(struct window *wobj) +{ + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) + { + fprintf(stderr,"Couldn't init SDL: %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + /* Create run-of-the-mill window at specified size */ + wobj->window = SDL_CreateWindow( + wobj->title, + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + wobj->width, wobj->height, + SDL_WINDOW_SHOWN + ); + if (wobj->window == NULL) + { + fprintf(stderr, "Couldn't create window: %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + /* Grab window surface */ + wobj->surface = SDL_GetWindowSurface(wobj->window); + if (wobj->surface == NULL) + { + fprintf(stderr, "Couldn't grab window surface: %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + /* Clear/blank surface with grey, and update it */ + SDL_FillRect(wobj->surface, NULL, SDL_MapRGB(wobj->surface->format, 0x33, 0x33, 0x33)); + SDL_UpdateWindowSurface(wobj->window); + return EXIT_SUCCESS; +} + +void display_stop(struct window *wobj) +{ + SDL_DestroyWindow(wobj->window); + SDL_Quit(); +} diff --git a/window.h b/window.h new file mode 100644 index 0000000..0c97b86 --- /dev/null +++ b/window.h @@ -0,0 +1,20 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include <SDL.h> +#include <stdbool.h> + +struct window { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Surface *surface; + unsigned int height; + unsigned int width; + char* title; +}; + +int display_init(struct window *wobj); +void display_stop(struct window *wobj); + + +#endif |