diff --git a/src/main.zig b/src/main.zig index 13326bc..d45e61c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,6 +15,79 @@ const Card = packed struct(u6) { rank: u4, }; +const GameState = struct { + allocator: std.mem.Allocator, + prng: std.rand.DefaultPrng, + draw_pile: std.ArrayListUnmanaged(Card), + discard_pile: std.ArrayListUnmanaged(Card), + hands: []std.ArrayListUnmanaged(Card), + + pub fn init(allocator: std.mem.Allocator, seed: u64, num_players: usize) !@This() { + var draw_pile = std.ArrayListUnmanaged(Card).fromOwnedSlice(try makeStandardDeck(allocator)); + errdefer draw_pile.deinit(allocator); + + var prng = std.rand.DefaultPrng.init(seed); + + prng.random().shuffle(Card, draw_pile.items); + + const cards_per_player: usize = switch (num_players) { + 1, 2 => 7, + 3, 4 => 6, + else => unreachable, + }; + + const hands = try allocator.alloc(std.ArrayListUnmanaged(Card), num_players); + errdefer allocator.free(hands); + for (hands) |*hand| { + hand.* = try std.ArrayListUnmanaged(Card).initCapacity(allocator, cards_per_player); + for (0..cards_per_player) |_| { + hand.appendAssumeCapacity(draw_pile.pop()); + } + } + + var discard_pile = try std.ArrayListUnmanaged(Card).initCapacity(allocator, 52); + errdefer discard_pile.deinit(allocator); + + // start game with one card in the discard pile + discard_pile.appendAssumeCapacity(draw_pile.pop()); + + return @This(){ + .allocator = allocator, + .prng = prng, + .draw_pile = draw_pile, + .discard_pile = discard_pile, + .hands = hands, + }; + } + + pub fn deinit(this: *@This()) void { + this.draw_pile.deinit(this.allocator); + this.discard_pile.deinit(this.allocator); + for (this.hands) |*hand| { + hand.deinit(this.allocator); + } + this.allocator.free(this.hands); + } +}; + +pub fn makeStandardDeck(allocator: std.mem.Allocator) ![]Card { + var deck = try allocator.alloc(Card, 52); + errdefer allocator.free(deck); + + var next_index: usize = 0; + for (0..4) |suit| { + for (1..14) |rank| { + deck[next_index] = Card{ + .suit = @enumFromInt(suit), + .rank = @intCast(rank), + }; + next_index += 1; + } + } + + return deck; +} + /// A texture with a regular grid of sprites const TileSheet = struct { texture: seizer.Texture, @@ -167,6 +240,10 @@ pub fn main() !void { var canvas = try seizer.Canvas.init(gpa.allocator(), .{}); defer canvas.deinit(gpa.allocator()); + // game state + var game_state = try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1); + defer game_state.deinit(); + while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) { seizer.backend.glfw.c.glfwPollEvents(); @@ -209,31 +286,40 @@ pub fn main() !void { 1001...std.math.maxInt(c_int) => card_tilemap_large.?, else => unreachable, }; - for (0..4) |suit| { - for (1..14) |rank| { - const card = Card{ .suit = @enumFromInt(suit), .rank = @intCast(rank) }; - const tile_index = deck_sprites.getTileForCard(card); - deck_sprites.tilesheet.renderTile(&canvas, @intCast(tile_index), .{ - @floatFromInt(rank * deck_sprites.tilesheet.tile_size[0]), - @floatFromInt(suit * deck_sprites.tilesheet.tile_size[1]), - }, .{}); - } - } - deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.blank, .{ - @floatFromInt(1 * deck_sprites.tilesheet.tile_size[0]), - @floatFromInt(5 * deck_sprites.tilesheet.tile_size[1]), - }, .{}); - deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.back, .{ - @floatFromInt(2 * deck_sprites.tilesheet.tile_size[0]), - @floatFromInt(5 * deck_sprites.tilesheet.tile_size[1]), - }, .{}); + const draw_pile_pos = [2]f32{ + canvas.window_size[0] / 2 - @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])), + canvas.window_size[1] / 2, + }; + const discard_pile_pos = [2]f32{ + canvas.window_size[0] / 2 + @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])), + canvas.window_size[1] / 2, + }; + const hand_pos = [2]f32{ + (canvas.window_size[0] - @as(f32, @floatFromInt(game_state.hands[0].items.len * deck_sprites.tilesheet.tile_size[0]))) / 2, + canvas.window_size[1] - @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])), + }; - for (0..52) |i| { + for (game_state.draw_pile.items, 0..) |_, i| { const oy = -@as(f32, @floatFromInt(i)) * (@as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])) / (52.0 * 4)); deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.back, .{ - @floatFromInt(4 * deck_sprites.tilesheet.tile_size[0]), - @as(f32, @floatFromInt(5 * deck_sprites.tilesheet.tile_size[1])) + oy, + draw_pile_pos[0], + draw_pile_pos[1] + oy, + }, .{}); + } + + for (game_state.discard_pile.items, 0..) |card, i| { + const oy = -@as(f32, @floatFromInt(i)) * (@as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])) / (52.0 * 4)); + deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.getTileForCard(card), .{ + discard_pile_pos[0], + discard_pile_pos[1] + oy, + }, .{}); + } + + for (game_state.hands[0].items, 0..) |card, i| { + deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.getTileForCard(card), .{ + hand_pos[0] + @as(f32, @floatFromInt(i)) * @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])), + hand_pos[1], }, .{}); }