Last active
December 14, 2023 15:31
-
-
Save perky/a5870219658672353be7166f8515d1e9 to your computer and use it in GitHub Desktop.
Advent of Code 2023
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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