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:
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;