This commit is contained in:
Artem Horbunov 2020-05-21 19:06:40 +03:00
parent bb007f68ab
commit 6c91d899d2
9 changed files with 501 additions and 0 deletions

54
final/.gitignore vendored Normal file
View File

@ -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

29
final/LICENSE Normal file
View File

@ -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.

12
final/Makefile Normal file
View File

@ -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

66
final/README.md Normal file
View File

@ -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.

231
final/commands.c Normal file
View File

@ -0,0 +1,231 @@
#include "commands.h"
#include "game.h"
#include <curses.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
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);
}

27
final/commands.h Normal file
View File

@ -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

48
final/game.c Normal file
View File

@ -0,0 +1,48 @@
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#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);
}

25
final/game.h Normal file
View File

@ -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

9
final/main.c Normal file
View File

@ -0,0 +1,9 @@
#include "game.h"
#include "commands.h"
#include <stdlib.h>
int main(int argc, char** argv){
struct game* game = init_game();
init_screen(game);
world_event(game);
}