2 * Copyright (c) 2002 by Edwin de Jong <mauddib@gmx.net>.
4 * Permission to use, copy, modify, and distribute this software and its
5 * documentation for any purpose and without fee is hereby granted,
6 * provided that the above copyright notice appear in all copies and that
7 * both that copyright notice and this permission notice appear in
8 * supporting documentation.
10 * This file is provided AS IS with no warranties of any kind. The author
11 * shall have no liability with respect to the infringement of copyrights,
12 * trade secrets or any patents by this file or any part thereof. In no
13 * event will the author be liable for any lost revenue or profits or
14 * other special, indirect and consequential damages.
17 /* this file is a part of pacman.c, and included via pacman.h. It handles the
18 AI of the ghosts and the pacman. */
21 /* fills array of DIRVECS size with possible directions, returns number of
22 directions. 'posdirs' points to a possibly undefined array of four
23 integers. The vector will contain booleans wether the direction is
24 a possible direction or not. Reverse directions are deleted. */
26 ghost_get_posdirs(pacmangamestruct *pp, int *posdirs, ghoststruct *g)
28 unsigned int i, nrdirs = 0;
30 /* bit of black magic here */
31 for (i = 0; i < DIRVECS; i++) {
33 if (g->lastbox != NOWHERE && i == (g->lastbox + 2) % DIRVECS
34 && g->aistate != goingout) {
38 if (g->aistate == goingout && i == 1) {
42 /* check if possible direction */
44 check_pos(pp, g->row + dirvecs[i].dy,
45 g->col + dirvecs[i].dx,
46 g->aistate == goingout ? True : False)) ==
55 /* Directs ghost to a random direction, exluding opposite (except in the
56 impossible situation that there is only one valid direction). */
58 ghost_random(pacmangamestruct *pp, ghoststruct *g)
60 int posdirs[DIRVECS], nrdirs = 0, i, dir = 0;
62 nrdirs = ghost_get_posdirs(pp, posdirs, g);
63 for (i = 0; i < DIRVECS; i++)
64 if (posdirs[i] == True) dir = i;
66 if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
68 for (i = 0; i < DIRVECS; i++) {
69 if (posdirs[i] == True && NRAND(nrdirs) == 0) {
75 g->nextrow = g->row + dirvecs[dir].dy;
76 g->nextcol = g->col + dirvecs[dir].dx;
80 /* Determines best direction to chase the pacman and goes that direction. */
82 ghost_chasing(pacmangamestruct *pp, ghoststruct *g)
84 int posdirs[DIRVECS], nrdirs = 0, i, dir = 0, highest = -100000,
85 thisvecx, thisvecy, thisvec;
87 nrdirs = ghost_get_posdirs(pp, posdirs, g);
88 for (i = 0; i < DIRVECS; i++)
89 if (posdirs[i] == True) dir = i;
91 if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
93 for (i = 0; i < DIRVECS; i++) {
94 if (posdirs[i] == False) continue;
95 thisvecx = (pp->pacman.col - g->col) * dirvecs[i].dx;
96 thisvecy = (pp->pacman.row - g->row) * dirvecs[i].dy;
97 thisvec = thisvecx + thisvecy;
98 if (thisvec >= highest) {
104 g->nextrow = g->row + dirvecs[dir].dy;
105 g->nextcol = g->col + dirvecs[dir].dx;
109 /* Determines the best direction to go away from the pacman, and goes that
112 ghost_hiding(pacmangamestruct *pp, ghoststruct *g)
114 int posdirs[DIRVECS], nrdirs = 0, i, dir = 0, highest = -100000,
115 thisvecx, thisvecy, thisvec;
117 nrdirs = ghost_get_posdirs(pp, posdirs, g);
118 for (i = 0; i < DIRVECS; i++)
119 if (posdirs[i] == True) dir = i;
121 if (nrdirs == 0) dir = (g->lastbox + 2) % DIRVECS;
123 for (i = 0; i < DIRVECS; i++) {
124 if (posdirs[i] == False) continue;
125 thisvecx = (g->col - pp->pacman.col) * dirvecs[i].dx;
126 thisvecy = (g->row - pp->pacman.row) * dirvecs[i].dy;
127 thisvec = thisvecx + thisvecy;
128 if (thisvec >= highest)
132 g->nextrow = g->row + dirvecs[dir].dy;
133 g->nextcol = g->col + dirvecs[dir].dx;
137 /* Determines a vector from the pacman position, towards all dots. The vector
138 is inversely proportional to the square of the distance of each dot.
139 (so, close dots attract more than far away dots). */
141 pac_dot_vec(pacmangamestruct *pp, pacmanstruct *p, long *vx, long *vy)
143 int x, y, bx = 0, by = 0, ex = LEVWIDTH, ey = LEVHEIGHT;
144 long dx, dy, dist, top = 0;
148 /* determine begin and end vectors */
151 case 0: ex = LEVHEIGHT/2; break;
152 case 1: bx = LEVHEIGHT/2; break;
153 case 2: ey = LEVHEIGHT/2; break;
154 case 3: by = LEVHEIGHT/2; break;
159 for (y = by; y < ey; y++)
160 for (x = bx; x < ex; x++)
161 if (check_dot(pp, x, y) == 1) {
162 dx = (long)x - (long)(p->col);
163 dy = (long)y - (long)(p->row);
164 dist = dx * dx + dy * dy;
167 *vx += (dx * ((long)LEVWIDTH * (long)LEVHEIGHT))
169 *vy += (dy * ((long)LEVWIDTH * (long)LEVHEIGHT))
174 /* Determine a vector towards the closest ghost (in one loop, so we spare a
175 couple of cycles which we can spoil somewhere else just as fast). */
177 pac_ghost_prox_and_vector(pacmangamestruct *pp, pacmanstruct *p,
180 int dx, dy, dist, closest = 100000;
186 for (g = 0; g < pp->nghosts; g++) {
187 if (pp->ghosts[g].dead == True ||
188 pp->ghosts[g].aistate == inbox ||
189 pp->ghosts[g].aistate == goingout)
191 dx = pp->ghosts[g].col + /*dirvecs[pp->ghosts[g].lastbox].dx*/ -
193 dy = pp->ghosts[g].row + /*dirvecs[pp->ghosts[g].lastbox].dy*/ -
195 dist = dx * dx + dy * dy;
196 if (dist < closest) {
206 /* fills array of DIRVECS size with possible directions, returns number of
207 directions. posdirs should point to an array of 4 integers. */
209 pac_get_posdirs(pacmangamestruct *pp, pacmanstruct *p, int *posdirs)
214 for (i = 0; i < DIRVECS; i++) {
215 /* if we just ate, or we are in a statechange, it is allowed
216 to go the opposite direction */
217 if (p->justate == 0 &&
218 p->state_change == 0 &&
219 p->lastbox != NOWHERE &&
220 i == (p->lastbox + 2) % DIRVECS) {
222 } else if ((posdirs[i] =
223 check_pos(pp, p->row + dirvecs[i].dy,
224 p->col + dirvecs[i].dx, 0)) == 1)
232 /* Clears the trace of vectors. */
234 pac_clear_trace(pacmanstruct *p)
238 for(i = 0; i < TRACEVECS; i++) {
239 p->trace[i].vx = NOWHERE; p->trace[i].vy = NOWHERE;
244 /* Adds a new vector to the trace. */
246 pac_save_trace(pacmanstruct *p, const int vx, const int vy)
248 if (!(vx == NOWHERE && vy == NOWHERE)) {
249 p->trace[p->cur_trace].vx = vx;
250 p->trace[p->cur_trace].vy = vy;
251 p->cur_trace = (p->cur_trace + 1) % TRACEVECS;
255 /* Check if a vector can be found in the trace. */
257 pac_check_trace(const pacmanstruct *p, const int vx, const int vy)
261 for (i = 1; i < TRACEVECS; i++) {
262 curel = (p->cur_trace - i + TRACEVECS) % TRACEVECS;
263 if (p->trace[curel].vx == NOWHERE &&
264 p->trace[curel].vy == NOWHERE)
266 if (p->trace[curel].vx == vx &&
267 p->trace[curel].vy == vy)
275 /* AI mode "Eating" for pacman. Tries to eat as many dots as possible, goes
276 to "hiding" if too close to a ghost. If in state "hiding" the vectors
277 of the ghosts are also taken into account (thus not running towards them
278 is the general idea). */
280 pac_eating(pacmangamestruct *pp, pacmanstruct *p)
282 int posdirs[DIRVECS], nrdirs, i, highest = -(1 << 16),
283 score, dir = 0, dotfound = 0, prox, worst = 0;
286 if ((prox = pac_ghost_prox_and_vector(pp, p, (int *)&vx, (int *)&vy)) <
287 4 * 4 && p->aistate == ps_eating) {
288 p->aistate = ps_hiding;
293 if (prox > 6 * 6 && p->aistate == ps_hiding) {
294 p->aistate = ps_eating;
295 if (p->justate == 0) p->state_change = 1;
299 if (prox < 3 * 3) p->state_change = 1;
301 nrdirs = pac_get_posdirs(pp, p, posdirs);
303 /* remove directions which lead to ghosts */
304 if (p->aistate == ps_hiding) {
305 for (i = 0; i < DIRVECS; i++) {
306 if (posdirs[i] == 0) continue;
307 score = vx * dirvecs[i].dx + vy * dirvecs[i].dy;
308 if (score > highest) {
316 highest = -(1 << 16);
319 /* get last possible direction if all else fails */
320 for (i = 0; i < DIRVECS; i++)
321 if (posdirs[i] != 0) dir = i;
323 pac_dot_vec(pp, p, &vx, &vy);
325 if (vx != NOWHERE && vy != NOWHERE && pac_check_trace(p, vx, vy) > 0) {
327 if (p->roundscore >= 12) {
329 p->aistate = ps_random;
335 if (p->justate == 0) pac_save_trace(p, vx, vy);
337 for (i = 0; i < DIRVECS; i++) {
338 if (posdirs[i] == 0) continue;
339 score = dirvecs[i].dx * vx + dirvecs[i].dy * vy;
340 if (check_dot(pp, p->col + dirvecs[i].dx,
341 p->row + dirvecs[i].dy) == 1) {
346 } else if (score > highest) {
350 } else if (score > highest && dotfound == 0) {
356 p->nextrow = p->row + dirvecs[dir].dy;
357 p->nextcol = p->col + dirvecs[dir].dx;
362 /* Tries to catch the ghosts. */
364 pac_chasing(pacmangamestruct *pp, pacmanstruct *p)
369 /* Goes completely random, but not in the opposite direction. Used when a
372 pac_random(pacmangamestruct *pp, pacmanstruct *p)
374 int posdirs[DIRVECS], nrdirs, i, dir = -1, lastdir = 0;
376 if (pac_ghost_prox_and_vector(pp, p, NULL, NULL) < 5 * 5) {
377 p->aistate = ps_hiding;
380 if (NRAND(20) == 0) {
381 p->aistate = ps_eating;
386 nrdirs = pac_get_posdirs(pp, p, posdirs);
388 for (i = 0; i < DIRVECS; i++) {
389 if (posdirs[i] == 0) continue;
391 if (check_dot(pp, p->col + dirvecs[i].dx,
392 p->row + dirvecs[i].dy) == 1) {
394 p->aistate = ps_eating;
398 } else if (NRAND(nrdirs) == 0)
402 if (dir == -1) dir = lastdir;
404 p->nextrow = p->row + dirvecs[dir].dy;
405 p->nextcol = p->col + dirvecs[dir].dx;
410 pac_get_vector_screen(pacmangamestruct *pp, pacmanstruct *p,
411 const int x, const int y, int *vx, int *vy)
415 lx = (x - pp->xb) / pp->xs;
416 ly = (y - pp->yb) / pp->ys;
419 else if ((unsigned int) lx > LEVWIDTH) lx = LEVWIDTH - 1;
422 else if ((unsigned int) ly > LEVHEIGHT) ly = LEVHEIGHT - 1;
427 if (lx == p->oldlx && ly == p->oldly) return 0;
428 p->oldlx = lx; p->oldly = ly;
433 pac_trackmouse(ModeInfo * mi, pacmangamestruct *pp, pacmanstruct *p)
435 int dx, dy, cx, cy, vx, vy;
437 int posdirs[DIRVECS], i, dir = -1, highest = -(2 << 16), score;
440 (void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
441 &r, &c, &dx, &dy, &cx, &cy, &m);
443 if (cx <= 0 || cy <= 0 ||
444 cx >= MI_WIDTH(mi) - 1 ||
445 cy >= MI_HEIGHT(mi) - 1)
448 p->state_change = pac_get_vector_screen(pp, p, cx, cy, &vx, &vy);
450 (void) pac_get_posdirs(pp, p, posdirs);
452 for (i = 0; i < DIRVECS; i++) {
453 if (posdirs[i] == 0) continue;
454 score = dirvecs[i].dx * vx + dirvecs[i].dy * vy;
455 if (score > highest) {
461 p->nextrow = p->row + dirvecs[dir].dy;
462 p->nextcol = p->col + dirvecs[dir].dx;
467 /* Calls correct state function, and changes between states. */
469 ghost_update(pacmangamestruct *pp, ghoststruct *g)
472 if (!(g->nextrow == NOWHERE && g->nextcol == NOWHERE)) {
478 if (g->dead == True) /* dead ghosts are dead,
479 so they don't run around */
482 if ((g->aistate == randdir || g->aistate == chasing) &&
486 g->aistate = randdir; break;
488 g->aistate = chasing; break;
490 g->aistate = chasing; break;
493 } else if (g->aistate == inbox) {
495 g->aistate = goingout;
499 } else if (g->aistate == goingout) {
500 if (g->col < LEVWIDTH/2 - JAILWIDTH/2 ||
501 g->col > LEVWIDTH/2 + JAILWIDTH/2 ||
502 g->row < LEVHEIGHT/2 - JAILHEIGHT/2 ||
503 g->row > LEVHEIGHT/2 + JAILHEIGHT/2 )
504 g->aistate = randdir;
507 switch (g->aistate) {
514 ghost_chasing(pp, g);
521 g->cfactor = GETFACTOR(g->nextcol, g->col);
522 g->rfactor = GETFACTOR(g->nextrow, g->row);
525 /* Calls correct pacman state function. */
527 pac_update(ModeInfo *mi, pacmangamestruct *pp, pacmanstruct *p)
529 if (!(p->nextrow == NOWHERE && p->nextcol == NOWHERE)) {
534 if (pp->level[p->row * LEVWIDTH + p->col] == '.') {
535 pp->level[p->row * LEVWIDTH + p->col] = ' ';
539 } else if (!trackmouse) {
543 if (!(trackmouse && pac_trackmouse(mi, pp, p))) {
545 switch (p->aistate) {
563 p->cfactor = GETFACTOR(p->nextcol, p->col);
564 p->rfactor = GETFACTOR(p->nextrow, p->row);