From 6c91d899d29a05532b6622852d6e46b4ee155d65 Mon Sep 17 00:00:00 2001 From: Artem Horbunov Date: Thu, 21 May 2020 19:06:40 +0300 Subject: [PATCH] first --- final/.gitignore | 54 +++++++++++ final/LICENSE | 29 ++++++ final/Makefile | 12 +++ final/README.md | 66 ++++++++++++++ final/commands.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++ final/commands.h | 27 ++++++ final/game.c | 48 ++++++++++ final/game.h | 25 +++++ final/main.c | 9 ++ 9 files changed, 501 insertions(+) create mode 100644 final/.gitignore create mode 100644 final/LICENSE create mode 100644 final/Makefile create mode 100644 final/README.md create mode 100644 final/commands.c create mode 100644 final/commands.h create mode 100644 final/game.c create mode 100644 final/game.h create mode 100644 final/main.c diff --git a/final/.gitignore b/final/.gitignore new file mode 100644 index 0000000..ecf4432 --- /dev/null +++ b/final/.gitignore @@ -0,0 +1,54 @@ +game + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/final/LICENSE b/final/LICENSE new file mode 100644 index 0000000..39d1664 --- /dev/null +++ b/final/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Daniel Hládek +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/final/Makefile b/final/Makefile new file mode 100644 index 0000000..4dc91ba --- /dev/null +++ b/final/Makefile @@ -0,0 +1,12 @@ +CFLAGS=-std=c99 -Wall -g + +all: game + + + +clean: + rm game + +game: main.c game.c game.h commands.c commands.h + gcc main.c game.c game.h commands.c commands.h -lcurses -lm -o game + diff --git a/final/README.md b/final/README.md new file mode 100644 index 0000000..9c5b92c --- /dev/null +++ b/final/README.md @@ -0,0 +1,66 @@ +# World Game Library + +Learn asycnronous C programming by a game. + +## Installation and Running + +Make sure, that `ncurses` is installed. + +Clone this repository. + +Compile: + +```c +make +``` + +Run: + +```c +./game +``` + +## Make your own game + +The game happens in a rectangular world of colorful characters. +Your goal as a programmer is to modify the world according to the pressed keys and the internal game state. +The library implements the game loop and draws the world to screen. + +Your game consists of two functions: + +- `void* init_game(struct world* g)` is called in the beginning. Here you can initialize the internal state of the game. +The function should return a pointer to an instance of the initial game state `game`. +- `int world_event(struct world* world,void* game)` +is called by the game loop in periodic interval or when a key was pressed. Non-zero return value or `Ctrl+C` key interrupts the game loop. + +The world variable represents state of two-dimensional grid of characters on the screen. The screen matrix looks like this: + +``` + origin + [0,0] width + +--------------------+ +h | | +e | | +i | | +g | | +h | | +t | | + +--------------------+ +``` + +The world has the following parameters: + +- `int height`: y-dimension of the grid. +- `int width`: x-dimension of the grid. +- `int interval`: maximum time between steps in milliseconds. + +### The World Event Function + +The `int world_event(struct world* world,void* game)` + function should: + +1. Read the game state (from the `void* game`) pointer. +1. Examine the pressed key. If the `key` variable is non-zero, a key was pressed. According to the pressed key, modify the game state `game`. +1. Draw the game state. In the beginning of the step function the screen is empty. +1. Returning non-zero value ends the game loop. + diff --git a/final/commands.c b/final/commands.c new file mode 100644 index 0000000..9beae67 --- /dev/null +++ b/final/commands.c @@ -0,0 +1,231 @@ +#include "commands.h" +#include "game.h" +#include +#include +#include +#include +#include +#include + +void welcome_screen(){ + box(stdscr, ACS_VLINE, ACS_HLINE); + mvprintw(LINES / 2 - 8, COLS / 2 - 12, "Use WASD to change direction"); + mvprintw(LINES / 2 - 6, COLS / 2 - 15, "Dont touch the walls or your path"); + mvprintw(LINES / 2 - 4, COLS / 2 - 10, "To exit anytime use 'q'"); + mvprintw(LINES / 2 - 2, COLS / 2 - 11, "Press any key to continue"); + getch(); + erase(); +} + +int pick_color(){ + mvprintw(LINES / 2 - 11, COLS / 2 - 20, "Pick color. To change use 's' and 'w'"); + mvprintw(LINES / 2 - 9, COLS / 2 - 13, "To confirm use 'space'"); + int color = 2; + while(1){ + init_pair(1, COLOR_BLACK, (color == 5) ? 7 : color); + attron(COLOR_PAIR(1)); + for(int i = 0; i < 10; i++){ + for(int q = 0; q < 20; q++){ + mvaddch(LINES / 2 - 3 + i, COLS / 2 - 2 + q - 10, ' '); + } + } + int ch = getch(); + if(ch == ' ') break; + else if(ch == 's'){ + color++; + if(color > 6) color = 1; + } + else if(ch == 'w'){ + color--; + if(color < 1) color = 6; + } + else if(ch == 'q'){ + endwin(); + exit(0); + } + flushinp(); + } + return (color == 5) ? 7 : color; +} + +int pick_speed(){ + box(stdscr, ACS_VLINE, ACS_HLINE); + mvprintw(LINES / 2 - 8, COLS / 2 - 12, "Pick speed. To change use 's'"); + mvprintw(LINES / 2 - 6, COLS / 2 - 8, "To confirm use 'space'"); + + int speed = 60; + while(1){ + mvprintw(LINES / 2 - 3, COLS / 2 + 2, "%d", 70 - speed); + int ch = getch(); + if(ch == ' ') break; + else if(ch == 's'){ + speed -= 10; + if(speed == 10) speed = 60; + } + } + erase(); + box(stdscr, ACS_VLINE, ACS_HLINE); + return speed; +} + +void init_screen(struct game* game){ + initscr(); + resize_term(LINES, (COLS % 2) ? COLS : COLS - 1); + curs_set(0); + noecho(); + welcome_screen(); + game->speed = pick_speed(); + srand(time(NULL)); + if(has_colors() == true){ + start_color(); + init_pair(7, COLOR_BLACK, COLOR_MAGENTA); + int color = pick_color(); + init_pair(8, color, COLOR_BLACK); + init_pair(9, COLOR_WHITE, COLOR_BLACK); + erase(); + attron(COLOR_PAIR(9)); + box(stdscr, ACS_VLINE, ACS_HLINE); + attron(COLOR_PAIR(8)); + } + game->y = rand() % (LINES - 5) + 2; + game->x = (rand() % (COLS - 5) + 2) / 2 * 2; + mvaddch(game->y, game->x, '@'); + mvaddch(game->y + 1, game->x, ACS_HLINE); + mvaddch(game->y - 1, game->x, ACS_HLINE); + mvaddch(game->y, game->x + 2, ACS_VLINE); + mvaddch(game->y, game->x - 2, ACS_VLINE); + mvaddch(game->y + 1, game->x + 2, '+'); + mvaddch(game->y + 1, game->x - 2, '+'); + mvaddch(game->y - 1, game->x - 2, '+'); + mvaddch(game->y - 1, game->x + 2, '+'); + timeout(game->speed); +} + +void draw_path(struct game* game){ + static char save; + char line; + getyx(stdscr, game->y, game->x); + if(save != game->direction){ + if(save == 'w' && game->direction == 'd') mvaddch(game->y, game->x - 1, ACS_ULCORNER); + else if(save == 'w' && game->direction == 'a') mvaddch(game->y, game->x - 1, ACS_URCORNER); + else if(save == 's' && game->direction == 'd') mvaddch(game->y, game->x - 1, ACS_LLCORNER); + else if(save == 's' && game->direction == 'a') mvaddch(game->y, game->x - 1, ACS_LRCORNER); + else if(save == 'a' && game->direction == 's'){ + mvaddch(game->y, game->x, ACS_HLINE); + mvaddch(game->y, game->x - 1, ACS_ULCORNER); + } + else if(save == 'a' && game->direction == 'w'){ + mvaddch(game->y, game->x, ACS_HLINE); + mvaddch(game->y, game->x - 1, ACS_LLCORNER); + } + else if(save == 'd' && game->direction == 's'){ + mvaddch(game->y, game->x - 2, ACS_HLINE); + mvaddch(game->y, game->x - 1, ACS_URCORNER); + } + else if(save == 'd' && game->direction == 'w'){ + mvaddch(game->y, game->x - 2, ACS_HLINE); + mvaddch(game->y, game->x - 1, ACS_LRCORNER); + } + save = game->direction; + } + else if(game->direction == 'w' || game->direction == 's'){ + mvaddch(game->y, game->x - 1, ACS_VLINE); + } + else if(game->direction == 'd'){ + mvaddch(game->y, game->x - 1, ACS_HLINE); + mvaddch(game->y, game->x - 2, ACS_HLINE); + } + else if(game->direction == 'a'){ + mvaddch(game->y, game->x - 1, ACS_HLINE); + mvaddch(game->y, game->x, ACS_HLINE); + } +} + +int direction_check(struct game* game ){ + getyx(stdscr, game->y, game->x); + int read = -1; + switch(game->direction){ + case 'w': + read = mvinch(game->y - 1, game->x - 1) & A_CHARTEXT; + break; + case 'a': + read = mvinch(game->y, game->x - 3) & A_CHARTEXT; + break; + case 's': + read = mvinch(game->y + 1, game->x - 1) & A_CHARTEXT; + break; + case 'd': + read = mvinch(game->y, game->x + 1) & A_CHARTEXT; + break; + } + usleep(100); + move(game->y, game->x); + if(read == (ACS_HLINE & A_CHARTEXT) || read == (ACS_VLINE & A_CHARTEXT) || read == ('#' & A_CHARTEXT) ) return 1; + return 0; +} + +int movement(struct game* game){ + static char save; + if((game->direction == 'w' && save == 's') || (game->direction == 's' && save == 'w') || (game->direction == 'a' && save == 'd') || (game->direction == 'd' && save == 'a')) game->direction = save; + if(direction_check(game)) return 0; + switch(game->direction){ + case 'w': + draw_path(game); + mvaddch(game->y - 1, game->x - 1, '^'); + break; + case 'a': + draw_path(game); + mvaddch(game->y, game->x - 3, '<'); + break; + case 's': + draw_path(game); + mvaddch(game->y + 1, game->x - 1, 'v'); + break; + case 'd': + draw_path(game); + mvaddch(game->y, game->x + 1, '>'); + break; + } + save = game->direction; + return 1; +} + +void spawn_blocks(struct game* game){ + int y = (rand() % LINES - 4) + 2; + int x = (rand() % (COLS - 5) + 2) / 2 * 2; + if(abs(y - game->y) < 10 || abs(x - game->x) < 20) return; + attron(COLOR_PAIR(7)); + for(int i = 0; i < 3; i++){ + for(int q = 0; q < 6; q++){ + mvaddch(y + i, x + q, '#'); + } + } + attron(COLOR_PAIR(8)); +} + +void game_over(struct game* game){ + timeout(9); + for(int i = 0, cycle = 0; i < LINES; i++){ + flushinp(); + for(int q = 0; q < COLS; q += 4){ + attron(COLOR_PAIR(7)); + mvaddch(i, q - (i % 2), ' '); + addch(' '); + attron(COLOR_PAIR(9)); + addch(' '); + addch(' '); + refresh(); + } + if(getch() != ERR) break; + } + attron(COLOR_PAIR(9)); + timeout(-1); + flushinp(); + erase(); + mvprintw(LINES / 2 - 2, COLS / 2 - 5, "Score: %d", game->score); + mvprintw(LINES / 2, COLS / 2 - 10, "Press any key to exit"); + getch(); + endwin(); + printf("\033[33mScore: %d\n\033[32mThanks for playing!\033[35m\n@Corupt Runner \033[37mby \033[36mArtem Horbunov\033[0m\n", game->score); + free(game); +} \ No newline at end of file diff --git a/final/commands.h b/final/commands.h new file mode 100644 index 0000000..8c7adcd --- /dev/null +++ b/final/commands.h @@ -0,0 +1,27 @@ +#ifndef _COMMANDS_H_INCLUDE_ +#define _COMMANDS_H_INCLUDE_ + +#include "game.h" + +//Shows user a guide how to play +void welcome_screen(); + +//Initialize the screen with all configurations +void init_screen(); + +//Draw path after the player +void draw_path(struct game* game); + +//Check there is something on the way +int direction_check(struct game* game ); + +//Change the direction of player +int movement(struct game* game); + +//Spawn the blocking structure +void spawn_blocks(struct game* game); + +//Show the score and destroy the game +void game_over(struct game* game); + +#endif diff --git a/final/game.c b/final/game.c new file mode 100644 index 0000000..d8c4200 --- /dev/null +++ b/final/game.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include "game.h" +#include "commands.h" + +// Start is called one in the beginning +struct game* init_game(){ + // Allocate memory for the state + struct game* st = calloc(1,(sizeof(struct game))); + // Initialize state + st->score = 0; + st->x = (rand() % (COLS - 5) + 2) / 2 * 2; + st->y = rand() % (LINES - 5) + 2; + // Store pointer to the state to the world variable + return st; +} + +// It should modify the state and draw it. +int world_event(struct game* game){ + //Wait for input to start the game + do{ + game->direction = getch(); + }while(game->direction != 'w' && game->direction != 'a' && game->direction != 's' && game->direction != 'd'); + for(int i = -1; i < 2; i++){ + for(int q = -2; q < 4; q += 2){ + mvaddch(game->y + i, game->x + q, ' '); + } + } + //The loop of the game + while(1){ + if(rand() % 10 == 0){ + spawn_blocks(game); + move(game->y, game->x); + } + int save = game->direction; + flushinp(); + game->direction = getch(); + //If wrong input- ignore it and change to the old one + if(game->direction != 'w' && game->direction != 'a' && game->direction != 's' && game->direction != 'd' && game->direction != 'q') game->direction = save; + if(game->direction == 'q') break; + if(movement(game) == 0) break; + game->score += 1; + } + getch(); + game_over(game); +} \ No newline at end of file diff --git a/final/game.h b/final/game.h new file mode 100644 index 0000000..8459a0a --- /dev/null +++ b/final/game.h @@ -0,0 +1,25 @@ +#ifndef _GAME_H_INCLUDE_ +#define _GAME_H_INCLUDE_ + +// Set of variables that expresses state of the game. +// +struct game { + //X position of the player + int x; + //Y position of the player + int y; + //Score + int score; + //Direction + int direction; + //Speed + int speed; +}; + +// Returns pointer to newly allocated player +struct game* init_game(); + +// Changes world according to the game state (pressed key, screen size or other event) +int world_event(struct game* game); + +#endif diff --git a/final/main.c b/final/main.c new file mode 100644 index 0000000..166ad5d --- /dev/null +++ b/final/main.c @@ -0,0 +1,9 @@ +#include "game.h" +#include "commands.h" +#include + +int main(int argc, char** argv){ + struct game* game = init_game(); + init_screen(game); + world_event(game); +} \ No newline at end of file