Skip to content

Commit c3e18eb

Browse files
author
theprimeagen
committed
creeps kill towers
1 parent 506561f commit c3e18eb

File tree

12 files changed

+174
-31
lines changed

12 files changed

+174
-31
lines changed

foo.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"rows": 24,
33
"cols": 80,
4-
"creepRate": 2000000,
54
"fps": 33333,
65
"runCount": 50000,
7-
"realtime": true,
6+
"roundTimeUS": 5000000,
7+
"realtime": false,
88
"viz": true,
99
"seed": 1234
10+
1011
}

relay.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ primary_region = 'ord'
3030
timeout = 2000
3131

3232
[[vm]]
33-
memory = '1gb'
34-
cpu_kind = 'shared'
35-
cpus = 1
33+
memory = '16gb'
34+
cpu_kind = 'performance'
35+
cpus = 32

src/engine/creep.zig

+14-2
Original file line numberDiff line numberDiff line change
@@ -109,25 +109,37 @@ pub fn contains(self: *Creep, pos: math.Vec2) bool {
109109
}
110110

111111
pub fn calculatePath(self: *Creep, board: []const bool) void {
112+
if (!self.alive) {
113+
return;
114+
}
115+
112116
const cols = self.values.cols;
113117
assert(board.len % cols == 0, "the length is not a rectangle");
114118
assert(board.len == self.path.len, "the length of the board is different than what the creep was initialized with.");
119+
assert(self.alive, "cannot calculate a path for a dead creep");
115120

116121
for (0..self.scratch.len) |idx| {
117122
self.scratch[idx] = -1;
118123
}
119124

120-
const pos = self.pos.position().toIdx(cols);
125+
const pos = self.aabb.min.position().toIdx(cols);
121126
const last = walk(pos, pos, cols, board, self.scratch);
122127

123128
if (last == 0) {
124-
unreachable;
129+
a.never("unable to move creep forward");
125130
} else {
126131
self.pathLen = path(self.scratch, last, self.path);
127132
self.pathIdx = 0;
128133
}
129134
}
130135

136+
pub fn kill(self: *Creep, gs: *GS) void {
137+
assert(gs.fns != null, "must have functions defined");
138+
139+
self.alive = false;
140+
gs.fns.?.creepKilled(gs, self);
141+
}
142+
131143
pub fn completed(self: *Creep) bool {
132144
return self.pos.position().col == self.values.cols - 1;
133145
}

src/engine/game-state.zig

+91-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const towers = @import("tower.zig");
88
const creeps = @import("creep.zig");
99
const projectiles = @import("projectile.zig");
1010

11+
const never = a.never;
12+
const Values = objects.Values;
1113
const AABB = math.AABB;
1214
const GS = objects.gamestate.GameState;
1315
const Message = objects.message.Message;
@@ -39,6 +41,13 @@ pub fn update(state: *GS, delta: i64) !void {
3941

4042
for (state.creeps.items) |*c| {
4143
creeps.update(c, state);
44+
45+
if (creeps.completed(c) and c.alive) {
46+
creeps.kill(c, state);
47+
if (getRandomTower(state, c.team)) |tid| {
48+
towers.killById(tid, state);
49+
}
50+
}
4251
}
4352

4453
for (state.projectile.items) |*p| {
@@ -70,6 +79,36 @@ pub fn update(state: *GS, delta: i64) !void {
7079
});
7180
}
7281

82+
fn getRandomTower(self: *GS, team: u8) ?usize {
83+
switch (team) {
84+
Values.TEAM_ONE => {
85+
if (self.oneTowerCount == 0) {
86+
return null;
87+
}
88+
},
89+
Values.TEAM_TWO => {
90+
if (self.twoTowerCount == 0) {
91+
return null;
92+
}
93+
},
94+
else => never("invalid team"),
95+
}
96+
97+
// TODO: Such a bad way to do this
98+
// i am sure there is a better way...
99+
while (true) {
100+
for (self.towers.items, 0..) |*t, idx| {
101+
// 50% chance is not really random especially given the order...
102+
if (t.alive and t.team == team and self.values.randBool()) {
103+
return idx;
104+
}
105+
}
106+
}
107+
108+
never("i should select a tower");
109+
return null;
110+
}
111+
73112
pub fn init(self: *GS) void {
74113
self.fns = &.{
75114
.placeProjectile = placeProjectile,
@@ -96,9 +135,12 @@ pub fn init(self: *GS) void {
96135
pub fn towerDied(self: *GS, t: *Tower) void {
97136
if (t.team == objects.Values.TEAM_ONE) {
98137
self.oneStats.towersLost += 1;
138+
self.oneTowerCount -= 1;
99139
} else {
100140
self.twoStats.towersLost += 1;
141+
self.twoTowerCount -= 1;
101142
}
143+
102144
self.boardChanged += 1;
103145
}
104146

@@ -125,6 +167,11 @@ pub fn strike(self: *GS, p: *Projectile) void {
125167
}
126168
}
127169

170+
pub fn completed(self: *GS) bool {
171+
return !self.noBuildZone and
172+
(self.oneTowerCount == 0 or self.twoTowerCount == 0);
173+
}
174+
128175
pub fn roundPlayed(state: *GS) bool {
129176
return !state.playing and state.round == state.one and state.round == state.two;
130177
}
@@ -173,9 +220,17 @@ pub fn message(state: *GS, msg: Message) !void {
173220
a.never("haven't programmed this yet");
174221
}
175222

176-
if (try placeTower(state, aabb, c.team)) |id| {
177-
std.debug.print("placed tower: {}\n", .{id});
223+
if (try placeTower(state, aabb, c.team)) |_| {
178224
} else {
225+
var count: usize = 0;
226+
while (creepByAABB(state, aabb)) |id| : (count += 1) {
227+
creeps.kill(&state.creeps.items[id], state);
228+
}
229+
230+
if (count > 0) {
231+
return;
232+
}
233+
179234
std.debug.print("could not place tower: {s} {s}\n", .{try aabb.string(), try c.string()});
180235
a.never("haven't programmed this yet also");
181236
}
@@ -218,7 +273,7 @@ pub fn clone(self: *GS) !GS {
218273

219274
pub fn towerByAABB(self: *GS, aabb: AABB) ?usize {
220275
for (self.towers.items, 0..) |*t, i| {
221-
if (t.aabb.overlaps(aabb)) {
276+
if (t.alive and t.aabb.overlaps(aabb)) {
222277
return i;
223278
}
224279
}
@@ -227,7 +282,7 @@ pub fn towerByAABB(self: *GS, aabb: AABB) ?usize {
227282

228283
pub fn creepByAABB(self: *GS, aabb: AABB) ?usize {
229284
for (self.creeps.items, 0..) |*c, i| {
230-
if (c.aabb.overlaps(aabb)) {
285+
if (c.alive and c.aabb.overlaps(aabb)) {
231286
return i;
232287
}
233288
}
@@ -335,14 +390,15 @@ fn canPlaceTower(self: *GS, aabb: math.AABB, team: u8) bool {
335390
}
336391

337392
if (creepByAABB(self, aabb)) |_| {
338-
std.debug.print("on creep", .{});
339393
return false;
340394
}
341395

342396
return true;
343397
}
344398

345399
pub fn placeTower(self: *GS, aabb: math.AABB, team: u8) !?usize {
400+
Values.assertTeam(team);
401+
346402
const pos = aabb.min;
347403
assert(aabb.min.closeEnough(pos, 0.0001), "you must place towers on natural numbers");
348404

@@ -358,6 +414,11 @@ pub fn placeTower(self: *GS, aabb: math.AABB, team: u8) !?usize {
358414
.tower(self.values);
359415

360416
try self.towers.append(t);
417+
if (team == Values.TEAM_ONE) {
418+
self.oneTowerCount += 1;
419+
} else {
420+
self.twoTowerCount += 1;
421+
}
361422

362423
updateBoard(self);
363424

@@ -395,11 +456,35 @@ pub fn towerById(self: *GS, id: usize) *Tower {
395456

396457
pub fn validateState(self: *GS) void {
397458
for (self.creeps.items) |*c| {
398-
if (tower(self, c.pos)) |t| {
459+
if (!c.alive) {
460+
continue;
461+
}
462+
463+
if (towerByAABB(self, c.aabb)) |t| {
399464
std.debug.print("tower: {s} collided with creep {s}\n", .{a.u(self.towers.items[t].pos.string()), a.u(c.string())});
400465
assert(false, "a creep is within a tower");
401466
}
402467
}
468+
469+
var one: usize = 0;
470+
var tuwu: usize = 0;
471+
472+
for (self.towers.items) |*t| {
473+
if (!t.alive) {
474+
continue;
475+
}
476+
477+
switch (t.team) {
478+
Values.TEAM_ONE => one += 1,
479+
Values.TEAM_TWO => tuwu += 1,
480+
else => never("how tf did i get here?"),
481+
}
482+
483+
}
484+
485+
std.debug.print("one = {} / {} two = {} / {}\n", .{one, self.oneTowerCount, tuwu, self.twoTowerCount});
486+
assert(one == self.oneTowerCount, "one's tower count does not equal the alive towers");
487+
assert(tuwu == self.twoTowerCount, "two's tower count does not equal the alive towers");
403488
}
404489

405490
const testing = std.testing;

src/engine/tower.zig

+14-3
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,7 @@ pub fn update(self: *Tower, gs: *GS) !void {
127127
}
128128

129129
if (self.ammo == 0) {
130-
self.alive = false;
131-
self.deadTimeUS = gs.time;
132-
gs.fns.?.towerDied(gs, self);
130+
kill(self, gs);
133131
return;
134132
}
135133

@@ -220,6 +218,19 @@ pub fn creepWithinRange(self: *Tower, gs: *GS) ?*Creep {
220218
return out;
221219
}
222220

221+
pub fn killById(tid: usize, gs: *GS) void {
222+
kill(&gs.towers.items[tid], gs);
223+
}
224+
225+
pub fn kill(t: *Tower, gs: *GS) void {
226+
assert(gs.fns != null, "gs must have functions on it");
227+
228+
t.alive = false;
229+
t.deadTimeUS = gs.time;
230+
231+
gs.fns.?.towerDied(gs, t);
232+
}
233+
223234
fn createTestTower() Tower {
224235
return TowerBuilder.
225236
start().

src/engine/utils.zig

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11

2+
3+

src/math/math.zig

+9
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,12 @@ test "aabb overlap" {
322322
try t.expect(!b_false_y.overlaps(a));
323323
try t.expect(b_true.overlaps(a));
324324
}
325+
326+
test "aabb collision failure creep tower issue" {
327+
const tower = AABB{.min = .{.x = 2.8e1, .y = 0e0}, .max = .{.x = 3.1e1, .y = 1e0}};
328+
const creep = AABB{.min = .{.x = 3e1, .y = 1e0}, .max = .{.x = 3.1e1, .y = 2e0}};
329+
330+
try t.expect(!tower.overlaps(creep));
331+
try t.expect(!creep.overlaps(tower));
332+
}
333+

src/objects/creep.zig

+13-2
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,26 @@ pub const Creep = struct {
4242
pathLen: usize = 0,
4343
alloc: Allocator,
4444

45-
pub fn string(self: *Creep) ![]u8 {
46-
const buf = scratchBuf(150);
45+
pub fn string(self: *const Creep) ![]u8 {
46+
const buf = scratchBuf(250);
4747
return std.fmt.bufPrint(buf, "creep({}, {}, {})\r\n pos = {s} aabb = {s}\r\n path = {}/{}, life = {}, speed = {}\r\n", .{
4848
self.alive, self.id, self.team,
4949
try self.pos.string(), try self.aabb.string(),
5050
self.pathIdx, self.pathLen, self.life, self.speed,
5151
});
5252
}
5353

54+
pub fn dumpScratch(self: *Creep) void {
55+
std.debug.print("Creep({} {}): Scratch\n", .{self.alive, self.id});
56+
for (0..self.scratch.len) |idx| {
57+
if (idx > 0 and idx % self.values.cols == 0) {
58+
std.debug.print("\n", .{});
59+
}
60+
std.debug.print("{} ", .{self.scratch[idx]});
61+
}
62+
std.debug.print("\n", .{});
63+
}
64+
5465
pub fn init(alloc: Allocator, values: *const Values) !Creep {
5566
return .{
5667
.values = values,

src/objects/game-state.zig

+10-2
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ pub const GameState = struct {
5151
boardChanged: usize = 0,
5252

5353
one: usize = 0,
54+
oneTowerCount: usize = 0,
5455
oneCoords: [3]?Coord,
5556
oneStats: Stats = Stats{},
5657
oneCreepRange: Range = Range{},
5758
oneNoBuildTowerRange: Range = Range{},
5859

5960
two: usize = 0,
61+
twoTowerCount: usize = 0,
6062
twoCoords: [3]?Coord,
6163
twoStats: Stats = Stats{},
6264
twoCreepRange: Range = Range{},
@@ -158,6 +160,12 @@ pub const GameState = struct {
158160

159161
pub fn debugBoard(self: *GameState) void {
160162
std.debug.print("\nBoard:\n", .{});
163+
164+
for (0..self.values.cols) |c| {
165+
std.debug.print("{:0>3}", .{c});
166+
}
167+
std.debug.print("\n", .{});
168+
161169
outer:
162170
for (self.board, 0..) |b, idx| {
163171
if (idx > 0 and idx % self.values.cols == 0) {
@@ -166,13 +174,13 @@ pub const GameState = struct {
166174

167175
for (self.creeps.items) |*c| {
168176
if (idx == c.pos.position().toIdx(self.values.cols)) {
169-
std.debug.print("c ", .{});
177+
std.debug.print("c ", .{});
170178
continue :outer;
171179
}
172180
}
173181

174182
const v: usize = if (b) 1 else 0;
175-
std.debug.print("{} ", .{v});
183+
std.debug.print("{} ", .{v});
176184
}
177185
std.debug.print("\n", .{});
178186
}

0 commit comments

Comments
 (0)