226 lines
8.5 KiB
Zig
226 lines
8.5 KiB
Zig
const std = @import("std");
|
|
const print = std.debug.print;
|
|
const input = @embedFile("input");
|
|
const MAP_SIZE = 140;
|
|
|
|
const Direction = enum { UP, DOWN, LEFT, RIGHT };
|
|
|
|
const Side = struct {
|
|
start: [2]usize,
|
|
end: [2]usize,
|
|
direction: Direction,
|
|
};
|
|
|
|
const Area = struct {
|
|
positions: *std.AutoHashMap([2]usize, void),
|
|
sides: *std.ArrayList(Side), // start and end point
|
|
char: u8,
|
|
area: usize = 0,
|
|
|
|
fn isInSide(self: Area, x: usize, y: usize, direction: Direction) bool {
|
|
for (self.sides.items) |side| {
|
|
if (direction != side.direction) continue;
|
|
|
|
if (side.start[0] == side.end[0] and side.start[0] == x and side.start[1] <= y and y <= side.end[1]) return true;
|
|
if (side.start[1] == side.end[1] and side.start[1] == y and side.start[0] <= x and x <= side.end[0]) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn init(allocator: std.mem.Allocator, char: u8) !Area {
|
|
const positions = try allocator.create(std.AutoHashMap([2]usize, void));
|
|
positions.* = std.AutoHashMap([2]usize, void).init(allocator);
|
|
const sides = try allocator.create(std.ArrayList(Side));
|
|
sides.* = std.ArrayList(Side).init(allocator);
|
|
return Area{
|
|
.positions = positions,
|
|
.sides = sides,
|
|
.char = char,
|
|
};
|
|
}
|
|
};
|
|
|
|
const Map = struct {
|
|
allocator: std.mem.Allocator,
|
|
map: [MAP_SIZE + 2][MAP_SIZE + 2]u8 = undefined,
|
|
|
|
areas: *std.ArrayList(Area),
|
|
area_search_x: usize = 0,
|
|
area_search_y: usize = 0,
|
|
|
|
fn init(allocator: std.mem.Allocator) !Map {
|
|
const areas = try allocator.create(std.ArrayList(Area));
|
|
areas.* = std.ArrayList(Area).init(allocator);
|
|
return Map{
|
|
.allocator = allocator,
|
|
.areas = areas,
|
|
};
|
|
}
|
|
|
|
fn setInput(self: *Map) void {
|
|
for (input, 0..) |c, i| {
|
|
if (c == '\n') continue;
|
|
self.map[@divFloor(i, MAP_SIZE + 1) + 1][i % (MAP_SIZE + 1) + 1] = c;
|
|
}
|
|
}
|
|
|
|
fn set(self: *Map, char: u8) void {
|
|
for (0..MAP_SIZE + 2) |x| for (0..MAP_SIZE + 2) |y| {
|
|
self.map[x][y] = char;
|
|
};
|
|
}
|
|
|
|
fn posInArea(self: Map, x: usize, y: usize) bool {
|
|
for (self.areas.items) |area| {
|
|
if (area.positions.contains([2]usize{ x, y })) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn populateAreas(self: *Map) !void {
|
|
for (1..MAP_SIZE + 1) |x| for (1..MAP_SIZE + 1) |y| {
|
|
if (self.posInArea(x, y)) continue;
|
|
try self.areas.append(try Area.init(self.allocator, self.map[x][y]));
|
|
try self.lookAround(x, y);
|
|
};
|
|
}
|
|
|
|
fn lookAround(self: *Map, x: usize, y: usize) !void {
|
|
try self.checkSide(x, y);
|
|
self.areas.items[self.areas.items.len - 1].area += 1;
|
|
try self.areas.items[self.areas.items.len - 1].positions.put([2]usize{ x, y }, {});
|
|
if (self.map[x - 1][y] != '.' and self.map[x - 1][y] == self.map[x][y] and !self.areas.getLast().positions.contains([2]usize{ x - 1, y })) try self.lookAround(x - 1, y);
|
|
if (self.map[x + 1][y] != '.' and self.map[x + 1][y] == self.map[x][y] and !self.areas.getLast().positions.contains([2]usize{ x + 1, y })) try self.lookAround(x + 1, y);
|
|
if (self.map[x][y - 1] != '.' and self.map[x][y - 1] == self.map[x][y] and !self.areas.getLast().positions.contains([2]usize{ x, y - 1 })) try self.lookAround(x, y - 1);
|
|
if (self.map[x][y + 1] != '.' and self.map[x][y + 1] == self.map[x][y] and !self.areas.getLast().positions.contains([2]usize{ x, y + 1 })) try self.lookAround(x, y + 1);
|
|
}
|
|
|
|
fn checkSide(self: *Map, x: usize, y: usize) !void {
|
|
if (self.map[x + 1][y] != self.map[x][y] and !self.areas.getLast().isInSide(x, y, .DOWN)) try self.addSide(x, y, .DOWN);
|
|
if (self.map[x - 1][y] != self.map[x][y] and !self.areas.getLast().isInSide(x, y, .UP)) try self.addSide(x, y, .UP);
|
|
if (self.map[x][y - 1] != self.map[x][y] and !self.areas.getLast().isInSide(x, y, .LEFT)) try self.addSide(x, y, .LEFT);
|
|
if (self.map[x][y + 1] != self.map[x][y] and !self.areas.getLast().isInSide(x, y, .RIGHT)) try self.addSide(x, y, .RIGHT);
|
|
}
|
|
|
|
fn addSide(self: *Map, x: usize, y: usize, direction: Direction) !void {
|
|
var start_x: usize = x;
|
|
var start_y: usize = y;
|
|
|
|
var end_x: usize = x;
|
|
var end_y: usize = y;
|
|
|
|
switch (direction) {
|
|
.UP => {
|
|
while (self.map[x][start_y - 1] == self.map[x][y] and self.map[x - 1][start_y - 1] != self.map[x][y]) start_y -= 1;
|
|
while (self.map[x][end_y + 1] == self.map[x][y] and self.map[x - 1][end_y + 1] != self.map[x][y]) end_y += 1;
|
|
},
|
|
.DOWN => {
|
|
while (self.map[x][start_y - 1] == self.map[x][y] and self.map[x + 1][start_y - 1] != self.map[x][y]) start_y -= 1;
|
|
while (self.map[x][end_y + 1] == self.map[x][y] and self.map[x + 1][end_y + 1] != self.map[x][y]) end_y += 1;
|
|
},
|
|
.LEFT => {
|
|
while (self.map[start_x - 1][y] == self.map[x][y] and self.map[start_x - 1][y - 1] != self.map[x][y]) start_x -= 1;
|
|
while (self.map[end_x + 1][y] == self.map[x][y] and self.map[end_x + 1][y - 1] != self.map[x][y]) end_x += 1;
|
|
},
|
|
.RIGHT => {
|
|
while (self.map[start_x - 1][y] == self.map[x][y] and self.map[start_x - 1][y + 1] != self.map[x][y]) start_x -= 1;
|
|
while (self.map[end_x + 1][y] == self.map[x][y] and self.map[end_x + 1][y + 1] != self.map[x][y]) end_x += 1;
|
|
},
|
|
}
|
|
|
|
try self.areas.items[self.areas.items.len - 1].sides.append(Side{
|
|
.start = [2]usize{ start_x, start_y },
|
|
.end = [2]usize{ end_x, end_y },
|
|
.direction = direction,
|
|
});
|
|
}
|
|
|
|
fn calculPrice(self: Map) usize {
|
|
var total: usize = 0;
|
|
for (self.areas.items) |area| {
|
|
total += area.sides.items.len * area.area;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
fn printPos(self: Map, x: usize, y: usize) void {
|
|
for (0..MAP_SIZE + 2) |x2| {
|
|
print("\n", .{});
|
|
for (0..MAP_SIZE + 2) |y2| {
|
|
if (x == x2 and y == y2) {
|
|
print("{c}", .{self.map[x][y]});
|
|
} else {
|
|
print(".", .{});
|
|
}
|
|
}
|
|
}
|
|
print("\n", .{});
|
|
}
|
|
|
|
fn printAreasOneByOne(self: Map) !void {
|
|
for (self.areas.items) |area| {
|
|
clearScreen();
|
|
print("Region {c} with Side: {d}; and Area: {d}\n", .{ area.char, area.sides.items.len, area.area });
|
|
for (area.sides.items) |side| {
|
|
print("Side {any}\n", .{side});
|
|
}
|
|
for (0..MAP_SIZE + 2) |x| {
|
|
print("\n", .{});
|
|
for (0..MAP_SIZE + 2) |y| {
|
|
if (area.positions.contains([2]usize{ x, y })) {
|
|
print("{c}", .{self.map[x][y]});
|
|
} else {
|
|
print(".", .{});
|
|
}
|
|
}
|
|
}
|
|
print("\n", .{});
|
|
try waitForInput();
|
|
}
|
|
}
|
|
|
|
fn printAreasSideOneByOne(self: Map) !void {
|
|
for (self.areas.items) |area| {
|
|
for ([4]Direction{ .UP, .DOWN, .LEFT, .RIGHT }) |direction| {
|
|
clearScreen();
|
|
print("Region {c} with Side: {d}; and Area: {d}, Direction {any}\n", .{ area.char, area.sides.items.len, area.area, direction });
|
|
for (0..MAP_SIZE + 2) |x| {
|
|
print("\n", .{});
|
|
for (0..MAP_SIZE + 2) |y| {
|
|
if (area.isInSide(x, y, direction)) {
|
|
print("{c}", .{self.map[x][y]});
|
|
} else {
|
|
print(".", .{});
|
|
}
|
|
}
|
|
}
|
|
print("\n", .{});
|
|
try waitForInput();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
defer arena.deinit();
|
|
const allocator = arena.allocator();
|
|
|
|
var map = try Map.init(allocator);
|
|
map.set('.');
|
|
map.setInput();
|
|
try map.populateAreas();
|
|
//try map.printAreasSideOneByOne();
|
|
try std.testing.expectEqual(821372, map.calculPrice());
|
|
}
|
|
|
|
fn clearScreen() void {
|
|
print("\x1B[2J\x1B[H", .{});
|
|
}
|
|
|
|
fn waitForInput() !void {
|
|
var buf: [5]u8 = undefined;
|
|
const stdin = std.io.getStdIn().reader();
|
|
_ = try stdin.readUntilDelimiter(&buf, '\n');
|
|
}
|