Skip to content

Instantly share code, notes, and snippets.

@perky
Last active December 14, 2023 15:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save perky/a5870219658672353be7166f8515d1e9 to your computer and use it in GitHub Desktop.
Save perky/a5870219658672353be7166f8515d1e9 to your computer and use it in GitHub Desktop.
Advent of Code 2023
const std = @import("std");
const LineParser = @import("LineParser.zig");
const fs = @import("fs.zig");
const raylib = @import("raylib.zig");
const c = raylib.c;
const Map = struct {
cells: CellArray,
symbol_indices: IndicesArray,
numbers: NumbersArray,
width: usize = 0,
height: usize = 0,
pub const Cell = union(enum) {
empty: void,
digit: DigitCell,
symbol: u8,
};
pub const DigitCell = struct {
number_index: usize,
digit: u8,
};
pub const Number = struct {
value: i32,
is_part_number: bool = false
};
const CellArray = std.BoundedArray(Cell, 100_000);
const IndicesArray = std.BoundedArray(usize, 100_000);
const NumbersArray = std.BoundedArray(Number, 5_000);
pub fn getCell(self: *const Map, x: usize, y: usize) ?Cell {
const index = (y * self.width) + x;
if (index < self.cells.len) {
return self.cells.get(index);
}
return null;
}
};
pub fn process(allocator: std.mem.Allocator) !Solution {
// Allocate memory for the map of numbers and symbols.
var map = try allocator.create(Map);
map.* = Map{
.cells = try Map.CellArray.init(0),
.symbol_indices = try Map.IndicesArray.init(0),
.numbers = try Map.NumbersArray.init(0)
};
// Read puzzle input text and parse into map.
var line_iter = try fs.LineIterator.init(allocator, "day03");
while (line_iter.next()) |line| {
var parser = LineParser.init(line);
map.width = line.len;
map.height += 1;
while (!parser.isEnd()) {
const cursor_at_start = parser.cursor;
const char_at_start = parser.peekChar();
if (std.ascii.isDigit(char_at_start)) {
const number = try parser.readNumber(i32);
const cursor_now = parser.cursor;
try map.numbers.append(Map.Number{ .value = number });
const number_i = map.numbers.len - 1;
for (cursor_at_start..cursor_now) |digit_i| {
const digit_char = parser.line[digit_i];
const digit_cell = Map.DigitCell{
.number_index = number_i,
.digit = digit_char
};
try map.cells.append(.{ .digit = digit_cell });
}
} else if (char_at_start == '.') {
try map.cells.append(.{ .empty = {} });
try parser.advanceCursor();
} else if (!std.ascii.isAlphabetic(char_at_start)){
const symbol_index = map.cells.len;
try map.symbol_indices.append(symbol_index);
try map.cells.append(.{ .symbol = char_at_start });
try parser.advanceCursor();
} else {
return error.InvalidCharacterInPuzzleInput;
}
}
}
// Compute sum of gear parts.
var sum: i32 = 0;
for (map.symbol_indices.constSlice()) |symbol_index| {
const symbol_cell = map.cells.get(symbol_index);
if (symbol_cell.symbol != '*') {
continue;
}
const symbol_x: i32 = @intCast(symbol_index % map.width);
const symbol_y: i32 = @intCast(symbol_index / map.width);
var gear_number_indices: [2]usize = undefined;
var gear_number_count: usize = 0;
for (0..3) |k| adj_loop: {
for (0..3) |j| {
const x_offset: i32 = @intCast(k);
const y_offset: i32 = @intCast(j);
const adj_x: i32 = symbol_x + (x_offset - 1);
const adj_y: i32 = symbol_y + (y_offset - 1);
const b_same_pos = (adj_x == symbol_x and adj_y == symbol_y);
const b_off_map = (adj_x < 0 or adj_x >= map.width or adj_y < 0 or adj_y >= map.height);
if (b_same_pos or b_off_map) {
continue;
}
const cell_x: usize = @intCast(adj_x);
const cell_y: usize = @intCast(adj_y);
if (map.getCell(cell_x, cell_y)) |cell| {
switch (cell) {
.digit => |digit_cell| {
const existing_index = std.mem.indexOfScalar(
usize,
gear_number_indices[0..gear_number_count],
digit_cell.number_index
);
if (existing_index != null) {
continue;
}
if (gear_number_count == 2) {
break :adj_loop;
}
gear_number_indices[gear_number_count] = digit_cell.number_index;
gear_number_count += 1;
},
else => continue
}
}
}
}
if (gear_number_count == 2) {
const number0 = map.numbers.get(gear_number_indices[0]);
const number1 = map.numbers.get(gear_number_indices[1]);
const gear_product = number0.value * number1.value;
sum += gear_product;
map.numbers.set(gear_number_indices[0], .{ .value = number0.value, .is_part_number = true });
map.numbers.set(gear_number_indices[1], .{ .value = number1.value, .is_part_number = true });
}
}
return Solution{ .answer = sum, .map = map };
}
var map_view_x: usize = 0;
var map_view_y: usize = 0;
pub const Solution = struct {
answer: i32,
map: *Map,
pub fn draw(self: *const Solution, allocator: std.mem.Allocator, dt: f32) void {
_ = dt;
const font_size = 20;
const font_color = c.GREEN;
const map_window_size = 25;
const cell_spacing = 8;
const x_scroll_size = 3;
const y_scroll_size = 5;
const map_txt_size = 10;
const map_numbers = self.map.numbers.constSlice();
const view_width = self.map.width * cell_spacing - (self.map.width - map_window_size) * x_scroll_size;
const view_height = self.map.height * cell_spacing - (self.map.height - map_window_size) * y_scroll_size;
const cam = c.Camera2D{
.offset = c.Vector2{ .x = @floatFromInt((1080-view_width)/2), .y = @floatFromInt((720-view_height)/2) },
.target = c.Vector2{ .x = 0, .y = 0 },
.rotation = 0.0,
.zoom = 1.0,
};
c.BeginMode2D(cam);
defer c.EndMode2D();
if (c.IsKeyDown(c.KEY_RIGHT) and (map_view_x + map_window_size) < self.map.width) {
map_view_x += 1;
}
if (c.IsKeyDown(c.KEY_LEFT) and map_view_x > 0) {
map_view_x -= 1;
}
if (c.IsKeyDown(c.KEY_DOWN) and (map_view_y + map_window_size) < self.map.height) {
map_view_y += 1;
}
if (c.IsKeyDown(c.KEY_UP) and map_view_y > 0) {
map_view_y -= 1;
}
raylib.drawRectangle(
0, 0,
view_width,
view_height,
c.GRAY,
.fill
);
raylib.drawRectangle(
map_view_x * cell_spacing - (map_view_x * x_scroll_size),
map_view_y * cell_spacing - (map_view_y * y_scroll_size),
map_window_size * cell_spacing,
map_window_size * cell_spacing,
c.BLACK,
.fill
);
const map_view_x_end = @min(self.map.width, map_view_x+map_window_size);
const map_view_y_end = @min(self.map.height, map_view_y+map_window_size);
for (map_view_y..map_view_y_end) |y| {
for (map_view_x..map_view_x_end) |x| {
if (self.map.getCell(x, y)) |cell| {
const txt_x = (x * cell_spacing) - (map_view_x * x_scroll_size);
const txt_y = (y * cell_spacing) - (map_view_y * y_scroll_size);
switch (cell) {
.digit => |digit_cell| {
const number = map_numbers[digit_cell.number_index];
const col = if(number.is_part_number) c.GREEN else c.RED;
raylib.drawCodepoint(txt_x, txt_y, digit_cell.digit, map_txt_size, col);
},
.symbol => |symbol| {
raylib.drawCodepoint(txt_x, txt_y, symbol, map_txt_size, c.BLUE);
},
else => {}
}
}
}
}
raylib.drawAnswer(allocator, 0, -font_size - 10, font_size, font_color, self.answer);
}
};
const std = @import("std");
const LineParser = @import("LineParser.zig");
const fs = @import("fs.zig");
const raylib = @import("raylib.zig");
const c = raylib.c;
// example input: 5/8
// puzzle input: 10/25
const Card = struct {
id: usize = 0,
copies: usize = 1,
winning_numbers: [10]usize = undefined,
game_numbers: [25]usize = undefined
};
pub fn process(allocator: std.mem.Allocator) !Solution {
var cards = try std.BoundedArray(Card, 1_000).init(0);
// Read puzzle input text and parse into map.
var line_iter = try fs.LineIterator.init(allocator, "day04");
defer line_iter.deinit(allocator);
while (line_iter.next()) |line| {
var card = Card{};
var parser = LineParser.init(line);
try parser.skipWord("Card");
try parser.skipWhitespace();
card.id = try parser.readNumber(usize);
try parser.skipChar(':');
// Parse list of winning numbers.
for (0..card.winning_numbers.len) |i| {
try parser.skipWhitespace();
card.winning_numbers[i] = try parser.readNumber(usize);
}
// Skip over seperator.
try parser.skipWhitespace();
try parser.skipChar('|');
// Parse list of game numbers.
for (0..card.game_numbers.len) |i| {
try parser.skipWhitespace();
card.game_numbers[i] = try parser.readNumber(usize);
}
// Add to list of cards.
try cards.append(card);
}
var total_cards: usize = 0;
for (cards.constSlice(), 0..) |card, card_i| {
total_cards += card.copies;
var num_matches: usize = 0;
// Count matching winning numbers.
for (card.winning_numbers) |winning_number| {
if (std.mem.indexOfScalar(usize, card.game_numbers[0..], winning_number)) |_| {
num_matches += 1;
}
}
// Add new copies of cards.
for (0..num_matches) |i| {
var other_card = &cards.buffer[card_i + i + 1];
other_card.copies += card.copies;
}
}
return Solution{ .answer = total_cards };
}
pub const Solution = struct {
answer: usize,
pub fn draw(self: *const Solution, allocator: std.mem.Allocator, dt: f32) void {
_ = dt;
const font_size: usize = 40;
const background_color = c.Color{ .r = 100, .g = 0, .b = 0, .a = 255 };
const font_color = c.WHITE;
c.ClearBackground(background_color);
raylib.drawAnswer(allocator, 10, 10, font_size, font_color, @intCast(self.answer));
}
};
const std = @import("std");
const LineParser = @import("LineParser.zig");
const fs = @import("fs.zig");
const raylib = @import("raylib.zig");
const c = raylib.c;
const MAX_SEEDS = 20; // example: 4, puzzle: 20
const MAX_SEED_RANGES = MAX_SEEDS/2;
const Almanac = struct {
seeds: [MAX_SEEDS]usize = undefined,
soil_map: Map,
fertilizer_map: Map,
water_map: Map,
light_map: Map,
temp_map: Map,
humid_map: Map,
location_map: Map,
pub const MapRange = struct {
dst: usize,
src: usize,
len: usize
};
pub const Range = struct {
start: usize,
len: usize
};
pub const Map = std.BoundedArray(MapRange, 1_000);
const RangeArray = std.BoundedArray(Range, 1_000);
pub fn init() !Almanac {
return .{
.soil_map = try Map.init(0),
.fertilizer_map = try Map.init(0),
.water_map = try Map.init(0),
.light_map = try Map.init(0),
.temp_map = try Map.init(0),
.humid_map = try Map.init(0),
.location_map = try Map.init(0),
};
}
pub fn getMapDstRange(map: *const Map, in_ranges: *const RangeArray) !RangeArray {
var result = try RangeArray.init(0);
var pending_ranges = in_ranges.*;
while (pending_ranges.len > 0) {
const in_range = pending_ranges.pop();
const in_start = in_range.start;
const in_end = in_start + in_range.len;
var out_range: ?Range = null;
for (map.constSlice()) |entry| {
const src_start = entry.src;
const src_end = entry.src + entry.len;
if (in_end <= src_start or in_start >= src_end) {
// in_range is outside entry range.
continue;
}
var b_chop_start = false;
var b_chop_end = false;
if (in_start >= src_start and in_end < src_end) {
// in_range is fully inside src range.
out_range = .{
.start = entry.dst + (in_start - src_start),
.len = in_range.len
};
} else if (in_start < src_start and in_end < src_end) {
// in_range overlaps src start.
out_range = .{
.start = entry.dst,
.len = in_end - src_start
};
b_chop_start = true;
} else if (in_start >= src_start and in_end >= src_end) {
// in_range overlaps src end.
out_range = .{
.start = entry.dst + (in_start - src_start),
.len = src_end - in_start
};
b_chop_end = true;
} else if (in_start < src_start and in_end >= src_end) {
// src range fully inside in_range.
out_range = .{
.start = entry.dst,
.len = entry.len
};
b_chop_start = true;
b_chop_end = true;
}
if (b_chop_start) {
const chop_len = src_start - in_start;
try pending_ranges.append(.{ .start = in_start, .len = chop_len });
}
if (b_chop_end) {
const chop_len = in_end - src_end + 1;
try pending_ranges.append(.{ .start = src_end, .len = chop_len });
}
if (out_range != null) {
break;
}
}
try result.append(out_range orelse in_range);
}
return result;
}
pub fn getLowestLocationFromSeedRange(self: *const Almanac, seed_ranges: RangeArray) !usize {
const soil = try getMapDstRange(&self.soil_map, &seed_ranges);
const fertilizer = try getMapDstRange(&self.fertilizer_map, &soil);
const water = try getMapDstRange(&self.water_map, &fertilizer);
const light = try getMapDstRange(&self.light_map, &water);
const temp = try getMapDstRange(&self.temp_map, &light);
const humid = try getMapDstRange(&self.humid_map, &temp);
const location = try getMapDstRange(&self.location_map, &humid);
var lowest_location: usize = std.math.maxInt(usize);
for (location.constSlice()) |location_range| {
if (location_range.start < lowest_location) {
lowest_location = location_range.start;
}
}
return lowest_location;
}
};
pub fn process(allocator: std.mem.Allocator) !Solution {
var almanac = try Almanac.init();
// Read puzzle input text and parse into almanac.
var line_iter = try fs.LineIterator.init(allocator, "day05", true);
defer line_iter.deinit(allocator);
var parser = LineParser.init(line_iter.next().?);
try parser.skipWord("seeds:");
for (0..MAX_SEEDS) |seed_i| {
try parser.skipWhitespace();
almanac.seeds[seed_i] = try parser.readNumber(usize);
}
_ = line_iter.next().?;
parser.setLine(line_iter.next().?);
try processMap(&almanac.soil_map, &line_iter, &parser, "seed-to-soil map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.fertilizer_map, &line_iter, &parser, "soil-to-fertilizer map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.water_map, &line_iter, &parser, "fertilizer-to-water map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.light_map, &line_iter, &parser, "water-to-light map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.temp_map, &line_iter, &parser, "light-to-temperature map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.humid_map, &line_iter, &parser, "temperature-to-humidity map");
parser.setLine(line_iter.next().?);
try processMap(&almanac.location_map, &line_iter, &parser, "humidity-to-location map");
var seed_ranges = try Almanac.RangeArray.init(0);
for (0..MAX_SEED_RANGES) |seed_i| {
const seed = almanac.seeds[seed_i * 2];
const len = almanac.seeds[seed_i * 2 + 1];
try seed_ranges.append(.{
.start = seed,
.len = len
});
}
var lowest_location: usize = try almanac.getLowestLocationFromSeedRange(seed_ranges);
return Solution{ .answer = lowest_location };
}
fn processMap(map: *Almanac.Map, line_iter: *fs.LineIterator, parser: *LineParser, comptime name: []const u8) !void {
try parser.skipWord(name ++ ":");
while (line_iter.next()) |line| {
if (line.len == 0) {
break;
}
parser.setLine(line);
const dest_start = try parser.readNumber(usize);
try parser.skipWhitespace();
const source_start = try parser.readNumber(usize);
try parser.skipWhitespace();
const range = try parser.readNumber(usize);
try map.append(Almanac.MapRange{
.dst = dest_start,
.src = source_start,
.len = range
});
}
}
pub const Solution = struct {
answer: usize,
pub fn draw(self: *const Solution, allocator: std.mem.Allocator, dt: f32) void {
_ = dt;
const font_size: usize = 40;
const background_color = c.Color{ .r = 0, .g = 200, .b = 0, .a = 255 };
const font_color = c.WHITE;
c.ClearBackground(background_color);
raylib.drawAnswer(allocator, 10, 10, font_size, font_color, @intCast(self.answer));
}
};
const std = @import("std");
const raylib = @import("raylib.zig");
const c = raylib.c;
const day01 = @import("day01.zig");
const day02 = @import("day02.zig");
const day03 = @import("day03.zig");
const day04 = @import("day04.zig");
const day05 = @import("day05.zig");
pub fn main() !void {
const screen_w = 1080;
const screen_h = 720;
c.InitWindow(screen_w, screen_h, "Advent of Code 2023");
c.SetTargetFPS(60);
const allocator = std.heap.c_allocator;
var days = try std.ArrayList(IDay).initCapacity(allocator, 3);
const start_process_time = c.GetTime();
var day01_solution = try day01.process(allocator);
try days.append(IDay.init(&day01_solution));
var day02_solution = try day02.process(allocator);
try days.append(IDay.init(&day02_solution));
var day03_solution = try day03.process(allocator);
try days.append(IDay.init(&day03_solution));
var day04_solution = try day04.process(allocator);
try days.append(IDay.init(&day04_solution));
var day05_solution = try day05.process(allocator);
try days.append(IDay.init(&day05_solution));
const end_process_time = c.GetTime();
std.debug.print("process duration: {d:.3}s\n", .{ end_process_time - start_process_time });
var active_day: usize = 0;
const num_days = days.items.len;
var snow_particles: [700]raylib.Particle = undefined;
for (&snow_particles, 0..) |*p, i| {
const radius = 1.0 + (raylib.randomWholeFloat(0, 100) * 0.01);
p.* = raylib.Particle{
.id = i,
.lifetime = raylib.randomWholeFloat(0, 100) * 0.01,
.pos = .{ .x = raylib.randomWholeFloat(0, screen_w), .y = raylib.randomWholeFloat(0, screen_h) },
.vel = .{ .x = 0, .y = 50.0 + (radius * 10.0) },
.radius = radius
};
p.init_pos = p.pos;
p.init_vel = p.vel;
}
const background_img = c.GenImageGradientLinear(screen_w, screen_h, 1, c.BLACK, c.GetColor(0x144096));
const background_tex = c.LoadTextureFromImage(background_img);
while (!c.WindowShouldClose())
{
c.BeginDrawing();
defer c.EndDrawing();
c.ClearBackground(c.BLACK);
c.DrawTexture(background_tex, 0, 0, c.WHITE);
const dt: f32 = c.GetFrameTime();
if (c.IsKeyPressed(c.KEY_BACKSPACE)) {
active_day = 0;
}
if (active_day != 0 and active_day <= days.items.len) {
days.items[active_day - 1].draw(allocator, dt);
} else {
const contents_w = (100 * 5);
const contents_h = (50 * 5);
const buttons_x0: f32 = @floatFromInt((screen_w - contents_w)/2);
const buttons_y0: f32 = @floatFromInt((screen_h - contents_h)/2);
raylib.beginOffset2D(buttons_x0, buttons_y0);
for (0..5) |column| {
for ((column*5 + 1)..(column*5 + 6)) |day| {
var buf: [8]u8 = undefined;
const label = try std.fmt.bufPrint(&buf, "Day {d}", .{ day });
const button_x = (column * 100);
const button_y = (day - 1 - (column * 5)) * 50;
const text_col = if (day <= num_days) c.WHITE else c.BLACK;
if (raylib.button(allocator, label, @intCast(button_x), @intCast(button_y), 90, text_col)) {
active_day = day;
}
}
}
raylib.endOffset2D();
// Draw snow.
var shelfs: [5]raylib.Rect = undefined;
for (&shelfs, 0..) |*shelf, i| {
shelf.* = raylib.Rect{
.pos = .{ .x = buttons_x0 + (@as(f32, @floatFromInt(i)) * 100), .y = buttons_y0 },
.size = .{ .x = 90, .y = 1 }
};
}
raylib.simulateSnow(dt, snow_particles[0..], shelfs[0..]);
for (snow_particles) |p| {
c.DrawCircle(@intFromFloat(p.pos.x), @intFromFloat(p.pos.y), p.radius, c.RAYWHITE);
}
}
}
c.CloseWindow();
}
const IDay = struct {
ptr: *anyopaque,
draw_fn: *const fn (ptr: *anyopaque, allocator: std.mem.Allocator, dt: f32) void,
pub fn init(ptr: anytype) IDay {
const T = @TypeOf(ptr);
const ptr_info = @typeInfo(T);
const gen = struct {
pub fn draw(pointer: *anyopaque, allocator: std.mem.Allocator, dt: f32) void {
const self: T = @ptrCast(@alignCast(pointer));
return ptr_info.Pointer.child.draw(self, allocator, dt);
}
};
return .{
.ptr = ptr,
.draw_fn = gen.draw,
};
}
pub fn draw(self: IDay, allocator: std.mem.Allocator, dt: f32) void {
return self.draw_fn(self.ptr, allocator, dt);
}
};
const std = @import("std");
pub const c = @cImport({
@cInclude("raylib.h");
});
const null_cam = c.Camera2D{
.offset = c.Vector2{ .x = 0, .y = 0 },
.target = c.Vector2{ .x = 0, .y = 0 },
.rotation = 0,
.zoom = 1
};
var current_cam = null_cam;
pub fn beginOffset2D(x: f32, y: f32) void {
var cam = c.Camera2D{
.offset = c.Vector2{ .x = x, .y = y },
.target = c.Vector2{ .x = 0, .y = 0 },
.rotation = 0,
.zoom = 1
};
c.BeginMode2D(cam);
current_cam = cam;
}
pub fn endOffset2D() void {
c.EndMode2D();
current_cam = null_cam;
}
pub fn drawText(allocator: std.mem.Allocator, text: []const u8, x: c_int, y: c_int, size: c_int, color: c.Color) void {
const c_text = allocator.dupeZ(u8, text) catch {
c.DrawText("MEMORY ALLOC ERROR", x, y, size, color);
return;
};
defer allocator.free(c_text);
c.DrawText(c_text, x, y, size, color);
}
pub fn getTextWidth(allocator: std.mem.Allocator, text: []const u8, font_size: c_int) c_int {
const c_text = allocator.dupeZ(u8, text) catch {
return 0;
};
defer allocator.free(c_text);
return c.MeasureText(c_text, font_size);
}
pub fn drawTextAllocPrint(allocator: std.mem.Allocator, comptime fmt: []const u8, args: anytype, x: c_int, y: c_int, size: c_int, color: c.Color) void {
const text = std.fmt.allocPrintZ(allocator, fmt, args) catch {
c.DrawText("MEMORY ALLOC ERROR", x, y, size, color);
return;
};
defer allocator.free(text);
c.DrawText(text, x, y, size, color);
}
pub const RectDrawMode = enum {
fill, stroke
};
pub fn drawRectangle(x: usize, y: usize, w: usize, h: usize, col: c.Color, draw_mode: RectDrawMode) void {
switch (draw_mode) {
.fill => c.DrawRectangle(@intCast(x), @intCast(y), @intCast(w), @intCast(h), col),
.stroke => c.DrawRectangleLines(@intCast(x), @intCast(y), @intCast(w), @intCast(h), col)
}
}
pub fn drawCodepoint(x: usize, y: usize, codepoint: u8, size: usize, col: c.Color) void {
const font = c.GetFontDefault();
const pos = c.Vector2{ .x = @floatFromInt(x), .y = @floatFromInt(y) };
c.DrawTextCodepoint(font, codepoint, pos, @floatFromInt(size), col);
}
pub fn button(allocator: std.mem.Allocator, label: []const u8, x: c_int, y: c_int, w: c_int, text_col: c.Color) bool {
const font_size = 20;
const padding = 10;
const text_width = getTextWidth(allocator, label, font_size);
const button_width = @max(text_width + (padding*2), w);
const button_height = font_size + (padding*2);
const mouse_x = c.GetMouseX() - @as(c_int, @intFromFloat(current_cam.offset.x));
const mouse_y = c.GetMouseY() - @as(c_int, @intFromFloat(current_cam.offset.y));
const b_hover = (mouse_x >= x and mouse_x <= x + button_width and mouse_y >= y and mouse_y <= y + button_height);
const b_click = c.IsMouseButtonPressed(c.MOUSE_BUTTON_LEFT);
if (b_hover) {
c.DrawRectangle(x, y, button_width, button_height, c.GREEN);
} else {
c.DrawRectangleLines(x, y, button_width, button_height, c.GREEN);
}
drawText(allocator, label, x + padding, y + padding, font_size, text_col);
return (b_hover and b_click);
}
pub fn drawAnswer(allocator: std.mem.Allocator, x: c_int, y: c_int, font_size: c_int, font_color: c.Color, answer: c_int) void {
if (c.IsKeyDown(c.KEY_SPACE)) {
drawTextAllocPrint(
allocator,
"The answer is {d}.", .{ answer },
x, y, font_size, font_color
);
}
if (c.IsKeyReleased(c.KEY_SPACE)) {
const sum_text = std.fmt.allocPrintZ(allocator, "{d}", .{ answer }) catch "";
defer allocator.free(sum_text);
c.SetClipboardText(sum_text);
}
}
pub fn randomWholeFloat(min: c_int, max: c_int) f32 {
return @floatFromInt(c.GetRandomValue(min, max));
}
pub const Vec2 = struct{ x: f32 = 0, y: f32 = 0 };
pub const Particle = struct {
id: usize = 0,
lifetime: f32 = 0,
vel: Vec2 = Vec2{},
init_vel: Vec2 = Vec2{},
pos: Vec2 = Vec2{},
init_pos: Vec2 = Vec2{},
radius: f32 = 1,
};
pub const Rect = struct {
pos: Vec2 = Vec2{},
size: Vec2 = Vec2{},
pub fn isPointInside(self: *const Rect, p: Vec2) bool {
return (p.x > self.pos.x and p.x < (self.pos.x + self.size.x)
and p.y > self.pos.y and p.y < (self.pos.y + self.size.y));
}
};
pub fn simulateSnow(dt: f32, particles: []Particle, shelfs: []Rect) void {
for (particles) |*p| {
p.lifetime += dt;
p.pos.x += p.vel.x * dt;
p.pos.y += p.vel.y * dt;
var b_on_shelf = false;
for (shelfs) |shelf| {
if (shelf.isPointInside(p.pos)) {
b_on_shelf = true;
p.vel.x = 0;
p.vel.y = 0;
break;
}
}
if (!b_on_shelf) {
const phase: f32 = @as(f32, @floatFromInt(p.id)) * 0.3;
p.vel.x = std.math.sin(p.lifetime + phase) * 30.0;
}
if (p.pos.y > 730 or p.lifetime > 35) {
p.pos.x = p.init_pos.x;
p.pos.y = -10;
p.vel.y = p.init_vel.y;
p.lifetime = 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment