From b45a77f76d318e09ec973638085ac2f2fbc58c9b Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Wed, 23 Jul 2025 17:04:19 +0200 Subject: [PATCH] feat: implement game logic --- src/game.zig | 149 +++++++++++++++++++++++++++++++++------------------ src/main.zig | 7 ++- 2 files changed, 101 insertions(+), 55 deletions(-) diff --git a/src/game.zig b/src/game.zig index c474ab8..32a5a74 100644 --- a/src/game.zig +++ b/src/game.zig @@ -1,78 +1,125 @@ const std = @import("std"); +const testing = std.testing; const Direction = enum { up, down, left, 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 -const Vec2 = @Vector(2, u32); +const Vec2 = @Vector(2, i32); -const Element = struct { - position: Vec2, - - prev: ?Element, - next: ?Element, - - pub fn hasPrev(self: Element) bool { - return self.prev != null; - } - pub fn hasNext(self: Element) bool { - return self.next != null; - } +const State = enum { + running, + lost, + won, }; -const GameState = struct { +const Game = struct { playFieldSize: 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, - 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{ + return Game{ .playFieldSize = Vec2{ 20, 20 }, .food = Vec2{ 2, 5 }, - .headDirection = Direction.left, - .head = pos1, - .tail = pos3, + .direction = Direction.right, + .body = body, + .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); + pub fn setDirection(self: *Game, newDirection: Direction) void { + if (self.direction == newDirection) { + return; } - return array; + 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" {} diff --git a/src/main.zig b/src/main.zig index 9facc36..f6c7684 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,8 @@ const std = @import("std"); const rl = @import("raylib"); +comptime { + _ = @import("game.zig"); +} pub fn main() anyerror!void { const alloc = std.heap.page_allocator; @@ -25,7 +28,3 @@ pub fn main() anyerror!void { rl.drawRectangle(10, 10, 10, 10, .black); } } - -test "should initialize game" { - try std.testing.expect(42 == 42); -}