uni

University stuff
git clone git://git.christosmarg.xyz/uni-assignments.git
Log | Files | Refs | README | LICENSE

commit 8a2470e7311119b1388df4c115811357f4f781c0
parent b981ccfa281e40c47ac344de6c05cf273e135fd9
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sun, 23 May 2021 00:34:41 +0300

cpp-game: implemented basic "AI" for hunting the player

Diffstat:
Mc-data-structures/bintree.c | 2+-
Mc-os2/ex1/ex2.c | 8++++----
Mc-os2/ex2/ex1_1.c | 10++++------
Mc-os2/ex2/ex3_client.c | 12++++++------
Mc-os2/ex2/ex3_server.c | 32++++++++++++++++----------------
Mcpp-oop/game/Engine.cc | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mcpp-oop/game/Engine.hpp | 11++++++++---
Mcpp-oop/game/Makefile | 5+----
Mcpp-oop/game/Movable.cc | 7+++++++
Mcpp-oop/game/Movable.hpp | 1+
Mcpp-oop/game/Score.cc | 2+-
Mcpp-oop/game/Score.hpp | 4+++-
Mcpp-oop/game/main.cc | 6++++--
13 files changed, 190 insertions(+), 89 deletions(-)

diff --git a/c-data-structures/bintree.c b/c-data-structures/bintree.c @@ -110,7 +110,7 @@ main(int argc, char *argv[]) printinorder(root); break; case 3: - if ((max = maxgrade(root)) == -1) + if ((max = maxgrade(root)) < 0) puts("Empty tree."); else printf("Max grade: %.2f\n", max); diff --git a/c-os2/ex1/ex2.c b/c-os2/ex1/ex2.c @@ -69,7 +69,7 @@ main(int argc, char *argv[]) /* Print the message to stdout */ if (write(STDOUT_FILENO, buf, n) != n) die("write"); - if (wait(NULL) == -1) + if (wait(NULL) < 0) die("wait"); /* Create P2 */ switch (fork()) { @@ -87,21 +87,21 @@ main(int argc, char *argv[]) exit(EXIT_SUCCESS); default: /* Wait for all children to exit first */ - if (wait(NULL) == -1) + if (wait(NULL) < 0) die("wait"); } } exit(EXIT_SUCCESS); default: /* wait for P2 to exit */ - if (wait(NULL) == -1) + if (wait(NULL) < 0) die("wait"); } /* * Finally, the parent process executes ps(1) after * everything else has exited */ - if (execl("/bin/ps", "ps", NULL) == -1) + if (execl("/bin/ps", "ps", NULL) < 0) die("execl"); } diff --git a/c-os2/ex2/ex1_1.c b/c-os2/ex2/ex1_1.c @@ -37,12 +37,12 @@ tdprint(void *foo) struct foo *f; f = (struct foo *)foo; - if (sem_wait(&f->mutex) == -1) + if (sem_wait(&f->mutex) < 0) die("sem_wait"); printf("%s", f->str); /* Prevent memory leak from strdup(2). */ free(f->str); - if (sem_post(&f->mutex) == -1) + if (sem_post(&f->mutex) < 0) die("sem_post"); return NULL; @@ -72,9 +72,7 @@ main(int argc, char *argv[]) { struct foo *f; pthread_t *tds; - int len; - int n = 5; - int i; + int i, len, n = 5; argv0 = *argv; len = LEN(nums); @@ -87,7 +85,7 @@ main(int argc, char *argv[]) */ tds = emalloc(len * sizeof(pthread_t)); - if (sem_init(&f->mutex, 0, 1) == -1) + if (sem_init(&f->mutex, 0, 1) < 0) die("sem_init"); while (n--) { for (i = 0; i < len; i++) { diff --git a/c-os2/ex2/ex3_client.c b/c-os2/ex2/ex3_client.c @@ -70,12 +70,12 @@ main(int argc, char *argv[]) argv += optind; - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) die("socket"); (void)memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1); - if (connect(fd, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)) == -1) + if (connect(fd, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)) < 0) die("connect"); res = emalloc(sizeof(struct pack_res)); @@ -95,17 +95,17 @@ main(int argc, char *argv[]) scanf("%d", &arr[i]); } (void)getchar(); - if (send(fd, &n, sizeof(int), 0) == -1) + if (send(fd, &n, sizeof(int), 0) < 0) die("send"); - if (send(fd, arr, n * sizeof(int), 0) == -1) + if (send(fd, arr, n * sizeof(int), 0) < 0) die("send"); - if (recv(fd, res, sizeof(struct pack_res), 0) == -1) + if (recv(fd, res, sizeof(struct pack_res), 0) < 0) die("recv"); printf("response: %s\tavg: %.2f\n", res->str, res->avg); printf("%s> continue (y/n)? ", argv0); ch = getchar(); - if (send(fd, &ch, 1, 0) == -1) + if (send(fd, &ch, 1, 0) < 0) die("send"); if (ch == 'n') break; diff --git a/c-os2/ex2/ex3_server.c b/c-os2/ex2/ex3_server.c @@ -55,10 +55,10 @@ srv(struct foo *foo) rc = -1; f = (struct foo *)foo; while (cont != 'n') { - if (recv(f->cfd, &n, sizeof(int), 0) == -1) + if (recv(f->cfd, &n, sizeof(int), 0) < 0) goto fail; arr = emalloc(n * sizeof(int)); - if (recv(f->cfd, arr, n * sizeof(int), 0) == -1) + if (recv(f->cfd, arr, n * sizeof(int), 0) < 0) goto fail; res = emalloc(sizeof(struct pack_res)); printf("cfd: %d\tn: %d\n", f->cfd, n); @@ -74,13 +74,13 @@ srv(struct foo *foo) } else (void)strncpy(res->str, "sequence: failed", sizeof(res->str) - 1); - if (send(f->cfd, res, sizeof(struct pack_res), 0) == -1) + if (send(f->cfd, res, sizeof(struct pack_res), 0) < 0) goto fail; f->ntotal++; printf("[%s] success: %d: total: %d\n", argv0, f->nsucc, f->ntotal); - if (recv(f->cfd, &cont, 1, 0) == -1) + if (recv(f->cfd, &cont, 1, 0) < 0) goto fail; free(arr); } @@ -175,22 +175,22 @@ main(int argc, char *argv[]) (void)sigemptyset(&sa.sa_mask); sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; - if (sigaction(SIGHUP, &sa, NULL) == -1) - die("sigaction: SIGHUP"); - if (sigaction(SIGINT, &sa, NULL) == -1) - die("sigaction: SIGINT"); - if (sigaction(SIGTERM, &sa, NULL) == -1) - die("sigaction: SIGTERM"); - - if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + /*if (sigaction(SIGHUP, &sa, NULL) < 0)*/ + /*die("sigaction: SIGHUP");*/ + /*if (sigaction(SIGINT, &sa, NULL) < 0)*/ + /*die("sigaction: SIGINT");*/ + /*if (sigaction(SIGTERM, &sa, NULL) < 0)*/ + /*die("sigaction: SIGTERM");*/ + + if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) die("socket"); (void)memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1); - if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) + if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) < 0) die("bind"); - if (listen(sfd, backlog) == -1) + if (listen(sfd, backlog) < 0) die("listen"); f = emalloc(sizeof(struct foo)); @@ -205,14 +205,14 @@ main(int argc, char *argv[]) * accept(2)'s `addr` and `addrlen` arguments can be NULL if * we don't care about the address information of the client. */ - if ((f->cfd = accept(sfd, NULL, NULL)) == -1) + if ((f->cfd = accept(sfd, NULL, NULL)) < 0) continue; printf("[%s] accepted client: %d\n", argv0, f->cfd); switch (fork()) { case -1: die("fork"); case 0: - if (srv(f) == -1) + if (srv(f) < 0) perror("srv"); _exit(0); default: diff --git a/cpp-oop/game/Engine.cc b/cpp-oop/game/Engine.cc @@ -19,22 +19,24 @@ Engine::Engine(const char *mapfile, const char *scorefile) throw "init_entities failed"; if (!init_score(scorefile)) throw "init_score failed: " + std::string(scorefile); - f_running = 1; } Engine::~Engine() { + player = nullptr; for (auto&& e : entities) - if (e != NULL) - delete e; - colors.clear(); + delete e; + delete score; map.clear(); + colors.clear(); (void)delwin(gw); (void)endwin(); } +/* Private methods */ + bool Engine::load_map(const char *mapfile) { @@ -52,13 +54,13 @@ Engine::load_map(const char *mapfile) row.push_back(c); if (c == '\n') { /* XXX: h != hprev */ - h = row.size(); + w = row.size(); map.push_back(row); row.clear(); } } f.close(); - w = map.size(); + h = map.size(); return true; } @@ -74,14 +76,18 @@ Engine::init_curses() noecho(); cbreak(); curs_set(false); + /* Allow arrow key presses. */ keypad(stdscr, true); + /* ESC has a small delay after it's pressed, so remove it. */ set_escdelay(0); + /* Don't wait for a keypress, just continue if there's nothing. */ + timeout(1000); xmax = getmaxx(stdscr); ymax = getmaxy(stdscr); - wr = w; - wc = h; + wr = h; + wc = w; wy = CENTER(ymax, wr); wx = CENTER(xmax, wc); if ((gw = newwin(wr, wc, wy, wx)) == NULL) @@ -104,6 +110,14 @@ Engine::init_curses() return true; } +bool +Engine::collides_with_wall(int x, int y) +{ + if (x < w && y < h) + return map[y][x] == '*'; + return true; +} + void Engine::calc_pos(int *x, int *y) { @@ -117,13 +131,13 @@ Engine::calc_pos(int *x, int *y) for (const auto& e : entities) if (*x == e->get_x() && *y == e->get_y()) continue; - } while (map[*x][*y] != ' '); + } while (collides_with_wall(*x, *y)); } bool Engine::init_entities() { - int i, type, x, y; + int i, x, y, type; srand(time(nullptr)); @@ -133,7 +147,7 @@ Engine::init_entities() * `x, y`) so that mvwaddch(3) doesn't print the entity on the * wrong coordinates. */ - entities.push_back(new Potter(y, x, Movable::Direction::DOWN, 'P')); + entities.push_back(new Potter(x, y, Movable::Direction::DOWN, 'P')); for (i = 0; i < nenemies; i++) { calc_pos(&x, &y); /* @@ -142,11 +156,11 @@ Engine::init_entities() */ switch (type = rand() % 2) { case 0: - entities.push_back(new Gnome(y, x, + entities.push_back(new Gnome(x, y, Movable::Direction::DOWN, 'G')); break; case 1: - entities.push_back(new Traal(y, x, + entities.push_back(new Traal(x, y, Movable::Direction::DOWN, 'T')); break; } @@ -159,29 +173,82 @@ Engine::init_entities() bool Engine::init_score(const char *scorefile) { + score = new Score(scorefile); + return true; } void +Engine::ctrl_menu() +{ + WINDOW *opts; + int wr, wc, wy, wx; + + wc = 32; + wr = 9; + wx = CENTER(xmax, wc); + wy = CENTER(ymax, wr); + if ((opts = newwin(wr, wc, wy, wx)) == NULL) + return; + werase(opts); + box(opts, 0, 0); + + mvwprintw(opts, 1, 1, "Up Move up"); + mvwprintw(opts, 2, 1, "Down Move down"); + mvwprintw(opts, 3, 1, "Left Move left"); + mvwprintw(opts, 4, 1, "Right Move right"); + mvwprintw(opts, 5, 1, "ESC Quit"); + mvwprintw(opts, 7, 1, "Press any key to quit the menu"); + + wrefresh(opts); + (void)wgetch(opts); + werase(opts); + wrefresh(opts); + (void)delwin(opts); +} + +void +Engine::calc_dist(std::map<int, int>& dists, int ex, int ey, int dir) +{ + int px, py, dx, dy, d; + + px = player->get_x(); + py = player->get_y(); + dx = ex - px; + dy = ey - py; + d = floor(sqrt(dx * dx + dy * dy)); + dists.insert(std::pair<int, int>(d, dir)); +} + +/* Public methods */ + +void Engine::kbd_input() { - int key, dir; + int key, dir, newx, newy; + newx = player->get_x(); + newy = player->get_y(); + switch (key = getch()) { case KEY_LEFT: + newx--; dir = Movable::Direction::LEFT; break; case KEY_RIGHT: + newx++; dir = Movable::Direction::RIGHT; break; case KEY_UP: + newy--; dir = Movable::Direction::UP; break; case KEY_DOWN: + newy++; dir = Movable::Direction::DOWN; break; case 'c': - menuopts(); + ctrl_menu(); return; case ESC: /* FALLTHROUGH */ /* FIXME: what? */ @@ -190,12 +257,62 @@ Engine::kbd_input() return; } - player->set_newpos(dir, wxmax, wymax); + if (!collides_with_wall(newx, newy)) + player->set_newpos(dir, wxmax, wymax); +} + +/* FIXME: move asynchronously */ +void +Engine::enemies_move() +{ + std::map<int, int> dists; + int ex, ey; + auto mindist = [](const std::pair<int, int>& a, const std::pair<int, int>& b) { + return a.first < b.second; + }; + + for (const auto& e : entities) { + if (e == player) + continue; + ex = e->get_x(); + ey = e->get_y(); + dists.clear(); + /* West */ + if (!collides_with_wall(ex - 1, ey)) + calc_dist(dists, ex - 1, ey, Movable::Direction::LEFT); + /* East */ + if (!collides_with_wall(ex + 1, ey)) + calc_dist(dists, ex + 1, ey, Movable::Direction::RIGHT); + /* North */ + if (!collides_with_wall(ex, ey - 1)) + calc_dist(dists, ex, ey - 1, Movable::Direction::UP); + /* South */ + if (!collides_with_wall(ex, ey + 1)) + calc_dist(dists, ex, ey + 1, Movable::Direction::DOWN); + + if (!dists.empty()) { + auto min = std::min_element(dists.begin(), + dists.end(), mindist); + e->set_newpos(min->second, wxmax, wymax); + } + } } void Engine::collisions() { + int px, py, ex, ey; + + px = player->get_x(); + py = player->get_y(); + + for (const auto& e : entities) { + ex = e->get_x(); + ey = e->get_y(); + if (e != player && px == ex && py == ey) { + /* TODO: increase score */ + } + } } void @@ -210,6 +327,7 @@ Engine::redraw() char msg_opts[] = "c Controls"; int color; + /* TODO: print more info */ werase(gw); erase(); mvprintw(0, 0, "Potter: (%d, %d)", player->get_x(), player->get_y()); @@ -249,32 +367,3 @@ Engine::is_running() { return f_running; } - -void -Engine::menuopts() -{ - WINDOW *opts; - int wr, wc, wy, wx; - - wc = 32; - wr = 9; - wx = CENTER(xmax, wc); - wy = CENTER(ymax, wr); - if ((opts = newwin(wr, wc, wy, wx)) == NULL) - return; - werase(opts); - box(opts, 0, 0); - - mvwprintw(opts, 1, 1, "Up Move up"); - mvwprintw(opts, 2, 1, "Down Move down"); - mvwprintw(opts, 3, 1, "Left Move left"); - mvwprintw(opts, 4, 1, "Right Move right"); - mvwprintw(opts, 5, 1, "ESC Quit"); - mvwprintw(opts, 7, 1, "Press any key to quit the menu"); - - wrefresh(opts); - (void)wgetch(opts); - werase(opts); - wrefresh(opts); - (void)delwin(opts); -} diff --git a/cpp-oop/game/Engine.hpp b/cpp-oop/game/Engine.hpp @@ -1,11 +1,13 @@ #ifndef _ENGINE_HPP_ #define _ENGINE_HPP_ +#include <cmath> #include <csignal> #include <cstdlib> #include <ctime> #include <iostream> #include <fstream> +#include <map> #include <string> #include <vector> @@ -29,7 +31,7 @@ private: std::vector<std::vector<char>> map; std::vector<int> colors; Potter *player; - Score score; + Score *score; WINDOW *gw; int xmax; int ymax; @@ -45,6 +47,7 @@ public: ~Engine(); void kbd_input(); + void enemies_move(); void collisions(); void upd_score(); void redraw(); @@ -53,10 +56,12 @@ public: private: bool load_map(const char *mapfile); bool init_curses(); + void calc_pos(int *x, int *y); bool init_entities(); bool init_score(const char *scorefile); - void calc_pos(int *x, int *y); - void menuopts(); + bool collides_with_wall(int x, int y); + void ctrl_menu(); + void calc_dist(std::map<int, int>& dists, int ex, int ey, int dir); }; #endif /* _ENGINE_HPP_ */ diff --git a/cpp-oop/game/Makefile b/cpp-oop/game/Makefile @@ -1,12 +1,9 @@ -# See LICENSE file for copyright and license details. -# game - # compiler CC = c++ # includes and libs INCS = -Iinclude -LIBS = -Llib -lncursesw +LIBS = -Llib -lncursesw -lm # flags CFLAGS = -std=c++14 -pedantic -Wall -Os ${INCS} diff --git a/cpp-oop/game/Movable.cc b/cpp-oop/game/Movable.cc @@ -30,6 +30,7 @@ Movable::set_newpos(int dir, int xmax, int ymax) y = ymax - 1; break; } + this->dir = dir; } int @@ -44,6 +45,12 @@ Movable::get_y() return y; } +int +Movable::get_dir() +{ + return dir; +} + char Movable::get_sym() { diff --git a/cpp-oop/game/Movable.hpp b/cpp-oop/game/Movable.hpp @@ -18,6 +18,7 @@ public: void set_newpos(int dir, int xmax, int ymax); int get_x(); int get_y(); + int get_dir(); char get_sym(); }; diff --git a/cpp-oop/game/Score.cc b/cpp-oop/game/Score.cc @@ -1,7 +1,7 @@ #include <cstring> #include "Score.hpp" -Score::Score() +Score::Score(const char *scorefile) { (void)memset(&hiscores, 0, sizeof(hiscores)); } diff --git a/cpp-oop/game/Score.hpp b/cpp-oop/game/Score.hpp @@ -1,6 +1,8 @@ #ifndef _SCORE_HPP_ #define _SCORE_HPP_ +#include <fstream> + class Score { private: struct HighScore { @@ -9,7 +11,7 @@ private: } hiscores[5]; public: - Score(); + Score(const char *scorefile); ~Score(); }; diff --git a/cpp-oop/game/main.cc b/cpp-oop/game/main.cc @@ -38,7 +38,8 @@ main(int argc, char *argv[]) die("input files cannot be the same"); /* * We're linking with `lncursesw` so we need to have UTF-8 enabled, - * otherwise colors and certain characters might not show up properly. + * otherwise colors and certain characters might not show up properly + * in some terminals. */ if (!setlocale(LC_ALL, "")) die("setlocale"); @@ -49,10 +50,11 @@ main(int argc, char *argv[]) die(e); } while (eng->is_running()) { + eng->redraw(); eng->kbd_input(); + eng->enemies_move(); eng->collisions(); eng->upd_score(); - eng->redraw(); } delete eng;