feat: implement game logic

This commit is contained in:
2025-07-23 17:04:19 +02:00
parent 9edf9bfb24
commit b45a77f76d
2 changed files with 101 additions and 55 deletions

View File

@@ -1,78 +1,125 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing;
const Direction = enum { const Direction = enum {
up, up,
down, down,
left, left,
right, right,
fn vector(self: Direction) Vec2 {
return switch (self) {
Direction.up => Vec2{ 0, -1 },
Direction.down => Vec2{ 0, 1 },
Direction.left => Vec2{ -1, 0 },
Direction.right => Vec2{ 1, 0 },
};
}
}; };
/// x, y /// x, y
const Vec2 = @Vector(2, u32); const Vec2 = @Vector(2, i32);
const Element = struct { const State = enum {
position: Vec2, running,
lost,
prev: ?Element, won,
next: ?Element,
pub fn hasPrev(self: Element) bool {
return self.prev != null;
}
pub fn hasNext(self: Element) bool {
return self.next != null;
}
}; };
const GameState = struct { const Game = struct {
playFieldSize: Vec2, playFieldSize: Vec2,
food: Vec2, food: Vec2,
direction: Direction,
body: std.ArrayList(Vec2),
state: State,
headDirection: Direction, pub fn create(allocator: std.mem.Allocator) !Game {
var body = try std.ArrayList(Vec2).initCapacity(allocator, 1000);
try body.append(Vec2{ 10, 10 });
try body.append(Vec2{ 9, 10 });
try body.append(Vec2{ 8, 10 });
head: Element, return Game{
tail: Element,
pub fn create() GameState {
const pos1 = Element{
.position = Vec2{ 10, 10 },
.prev = null,
.next = null,
};
const pos2 = Element{
.position = Vec2{ 9, 10 },
.prev = pos1,
.next = null,
};
const pos3 = Element{
.position = Vec2{ 8, 10 },
.prev = pos2,
.next = null,
};
pos1.next = pos2;
pos2.next = pos3;
return GameState{
.playFieldSize = Vec2{ 20, 20 }, .playFieldSize = Vec2{ 20, 20 },
.food = Vec2{ 2, 5 }, .food = Vec2{ 2, 5 },
.headDirection = Direction.left, .direction = Direction.right,
.head = pos1, .body = body,
.tail = pos3, .state = State.running,
}; };
} }
pub fn tick() void {} pub fn deinit(self: *Game) void {
self.body.deinit();
fn calculateSnakePositions(self: GameState, allocator: std.Allocator) []Vec2 {
var array = std.ArrayList(u8).initCapacity(allocator, 1000);
var current = self.head;
while (current.hasNext()) {
array.add(current.position);
} }
return array; pub fn setDirection(self: *Game, newDirection: Direction) void {
if (self.direction == newDirection) {
return;
}
const headPos = self.body.items[0];
const firstBodyPart = self.body.items[1];
if (std.meta.eql(headPos + newDirection.vector(), firstBodyPart)) {
return;
}
self.direction = newDirection;
}
pub fn tick(self: *Game) !void {
if (self.state != State.running) {
return;
}
const newHead = self.body.items[0] + self.direction.vector();
try self.body.insert(0, newHead);
_ = self.body.pop();
} }
}; };
test "should initialize game" {
const allocator = testing.allocator;
var game = try Game.create(allocator);
defer game.deinit();
const actual = game.body.items;
const expected = [_]Vec2{ Vec2{ 10, 10 }, Vec2{ 9, 10 }, Vec2{ 8, 10 } };
try testing.expectEqualSlices(Vec2, &expected, actual);
try testing.expectEqual(Direction.right, game.direction);
try testing.expectEqual(Vec2{ 20, 20 }, game.playFieldSize);
try testing.expectEqual(State.running, game.state);
}
test "should move down" {
const allocator = testing.allocator;
var game = try Game.create(allocator);
defer game.deinit();
game.setDirection(Direction.down);
try testing.expectEqual(Direction.down, game.direction);
try game.tick();
const expected = [_]Vec2{ Vec2{ 10, 11 }, Vec2{ 10, 10 }, Vec2{ 9, 10 } };
try testing.expectEqualSlices(Vec2, &expected, game.body.items);
}
test "should not be able to change direction to the previous position" {
const allocator = testing.allocator;
var game = try Game.create(allocator);
defer game.deinit();
game.setDirection(Direction.left);
try testing.expectEqual(Direction.right, game.direction);
}
test "should loose if exiting playField" {}
test "should loose if eating itself" {}
test "should grow if eating food" {}
test "should win if no space is left" {}
test "should tick if running" {}
test "should fail tick if lost" {}
test "should fail tick if won" {}

View File

@@ -1,5 +1,8 @@
const std = @import("std"); const std = @import("std");
const rl = @import("raylib"); const rl = @import("raylib");
comptime {
_ = @import("game.zig");
}
pub fn main() anyerror!void { pub fn main() anyerror!void {
const alloc = std.heap.page_allocator; const alloc = std.heap.page_allocator;
@@ -25,7 +28,3 @@ pub fn main() anyerror!void {
rl.drawRectangle(10, 10, 10, 10, .black); rl.drawRectangle(10, 10, 10, 10, .black);
} }
} }
test "should initialize game" {
try std.testing.expect(42 == 42);
}