-
Notifications
You must be signed in to change notification settings - Fork 0
/
autoplay.c
166 lines (139 loc) · 4.23 KB
/
autoplay.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#ifdef AUTOPLAY
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include "autoplay.h"
#include "item.h"
#include "player.h"
#include "mob.h"
#include "level.h"
const void ** autoplay_list_choice(const char * choices[],
const void * results[]){
const void ** selected = xcalloc(2,void *);
(void) choices;
// choose the first list item
selected[0] = results[0];
selected[1] = NULL;
return selected;
}
// wield lantern and starting weapon, then wear starting armour
static char opening_moves[] = {'w','x','w','x','W'};
static unsigned int move_count = 0;
// get the cell at an offset from the player
static Cell * cell_at(Mob * player, int dx, int dy) {
return player->level->cells[player->xpos + dx][player->ypos + dy];
}
// move into an enemy if there is one
static bool find_enemy(Mob * player, Direction * out) {
for (int dx = -1; dx <= 1; dx ++) {
for (int dy = -1; dy <= 1; dy ++) {
Cell * cell = cell_at(player, dx, dy);
if(cell->occupant != NULL && cell->occupant != player) {
out->dx = dx;
out->dy = dy;
return true;
}
}
}
return false;
}
static int path_dx[32] = {0};
static int path_dy[32] = {0};
static unsigned int path_pos = 0;
static unsigned int path_maxpos = 32;
// distance between two points
static float distance(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) {
float dx = (float)x1 - (float)x2;
float dy = (float)y1 - (float)y2;
return sqrt(dx * dx + dy * dy);
}
// simple greedy parthfinding algorithm
static void greedy_pathfind(Mob * player, unsigned int tx, unsigned int ty, unsigned int cx, unsigned int cy) {
// check for termination
if ((cx == tx && cy == ty) || path_pos == path_maxpos) {
path_dx[path_pos] = 0;
path_dy[path_pos] = 0;
path_pos = 0;
return;
}
// pick the next move: don't undo the last move, if there are no
// better (known empty) places than the current one, terminate.
int dx = 0;
int dy = 0;
float dist = distance(tx, ty, cx, cy);
for (int xoff = -1; xoff <= 1; xoff ++) {
if (cx == 0 && xoff == -1) continue;
if (cx == LEVELWIDTH && xoff == 1) continue;
for (int yoff = -1; yoff <= 1; yoff ++) {
if (cy == 0 && yoff == -1) continue;
if (cy == LEVELHEIGHT && yoff == 1) continue;
if(player->level->cells[cx + xoff][cy + yoff]->solid)
continue;
float new_dist = distance(tx, ty, cx + xoff, cy + yoff);
if(new_dist < dist) {
dx = xoff;
dy = yoff;
dist = new_dist;
}
}
}
if (dx == 0 && dy == 0) {
// no move found, terminate
path_dx[path_pos] = 0;
path_dy[path_pos] = 0;
path_pos = 0;
} else {
// record the move
path_dx[path_pos] = dx;
path_dy[path_pos] = dy;
path_pos ++;
greedy_pathfind(player, tx, ty, cx + dx, cy + dy);
}
}
// find a path from the current point.
static void pathfind(Mob * player, unsigned int tx, unsigned int ty) {
path_pos = 0;
greedy_pathfind(player, tx, ty, player->xpos, player->ypos);
}
Direction autoplay_select_direction(Mob * player){
Direction out = {.dx = 0, .dy = 0, .ch = 0};
// perform a standard set of moves to begin with
if(move_count < sizeof(opening_moves)){
out.ch = opening_moves[move_count];
move_count++;
return out;
}
// if standing on the stairs, go down
if(cell_at(player, 0, 0)->baseSymbol == '>') {
out.ch = '>';
return out;
}
// if adjacent to an enemy, attack
if(find_enemy(player, &out)) {
return out;
}
// if no path to follow, or following would hit a wall (why does
// this happen?) move randomly now and then pathfind for later.
if((path_dx[path_pos] == 0 && path_dy[path_pos] == 0) || cell_at(player, path_dx[path_pos], path_dy[path_pos])->solid) {
// move randomly
do {
out.dx = (rand() % 3) - 1;
out.dy = (rand() % 3) - 1;
}
while(cell_at(player, out.dx, out.dy)->solid);
// pathfind
for(unsigned int x = 1; x < LEVELWIDTH; x++) {
for(unsigned int y = 1; y < LEVELHEIGHT; y++) {
if(player->level->cells[x][y]->baseSymbol == '>') {
pathfind(player, x, y);
}
}
}
} else {
out.dx = path_dx[path_pos];
out.dy = path_dy[path_pos];
path_pos ++;
}
return out;
}
#endif // AUTOPLAY