Skip to main content
edited body
Source Link
spicy.dll
  • 987
  • 9
  • 26
  1. Any previously placed numbers are <=>= the current number.
  2. Any numbers placed can only be constrained by previously placed numbers.
  3. If the current number is equal to any of its constraining neighbors, there is no other legal place for the current number to go and, therefore, no valid arrangement exists.
  1. Any previously placed numbers are <= the current number.
  2. Any numbers placed can only be constrained by previously placed numbers.
  3. If the current number is equal to any of its constraining neighbors, there is no other legal place for the current number to go and, therefore, no valid arrangement exists.
  1. Any previously placed numbers are >= the current number.
  2. Any numbers placed can only be constrained by previously placed numbers.
  3. If the current number is equal to any of its constraining neighbors, there is no other legal place for the current number to go and, therefore, no valid arrangement exists.
Source Link
spicy.dll
  • 987
  • 9
  • 26

After a few days of theory crafting, I worked out that all valid grids can be rearranged such that the items fall along "forward slash" diagonals in descending order starting from the top left.

36 35 33 30 26 21  OR  11 10 09 08 07 06 
34 32 29 25 20 15      10 09 08 07 06 05
31 28 24 19 14 10      09 08 07 06 05 04
27 23 18 13 09 06      08 07 06 05 04 03
22 17 12 08 05 03      07 06 05 04 03 02
16 11 07 04 02 01      06 05 04 03 02 01

This pattern ensures three things:

  1. Any previously placed numbers are <= the current number.
  2. Any numbers placed can only be constrained by previously placed numbers.
  3. If the current number is equal to any of its constraining neighbors, there is no other legal place for the current number to go and, therefore, no valid arrangement exists.

Here is my solution in Zig 0.15.2:

const std = @import("std");
const Reader = std.io.Reader;

const GRID_SIDE_LEN: comptime_int = 6;
const GRID_SIZE = GRID_SIDE_LEN * GRID_SIDE_LEN;

// Optimal input buffer size calculation
const num_brackets_in: comptime_int = 2;
const num_digits_in = GRID_SIZE * 2;
const num_space_commas_in = (GRID_SIZE - 1) * 2;
const input_buf_size = num_brackets_in + num_digits_in + num_space_commas_in + 1;
var input_buf: [input_buf_size]u8 = undefined;

// Optimal output buffer size calculation
const num_brackets_out = (GRID_SIDE_LEN * 2) + 2;
const num_digits_out = num_digits_in;
const num_space_commas_out = (((GRID_SIDE_LEN - 1) * GRID_SIDE_LEN) + (GRID_SIDE_LEN - 1)) * 2;
const output_buf_size = num_brackets_out + num_digits_out + num_space_commas_out + 1;

var stdout_buf: [output_buf_size]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
const stdout = &stdout_writer.interface;

fn readList(reader: *Reader) !?[GRID_SIZE]u8 {
    var list: [GRID_SIZE]u8 = undefined;

    const line = try reader.takeDelimiter('\n') orelse return null;
    const list_start = std.mem.indexOfScalar(u8, line, '[') orelse return error.ParseError;
    const after_brace = line[list_start + 1..];
    const list_part = std.mem.sliceTo(after_brace, ']');

    // ] not found
    if (after_brace.len == list_part.len) {
        return error.ParseError;
    }
    
    var numbers_tokenized = std.mem.tokenizeAny(u8, list_part, " ,");
    var list_idx: usize = 0;
    while (numbers_tokenized.next()) |num_part| : (list_idx += 1) {
        list[list_idx] = try std.fmt.parseInt(u8, num_part, 10);
    }
    if (list_idx != GRID_SIZE) {
        return error.ParseError;
    }
    return list;
}

fn sortGridOrImpossible(list: [GRID_SIZE]u8) ?[GRID_SIDE_LEN][GRID_SIDE_LEN]u8 {
    var sorted_list: [GRID_SIZE]u8 = undefined;
    @memcpy(&sorted_list, &list);
    std.mem.sort(u8, &sorted_list, {}, comptime std.sort.desc(u8));

    var grid: [GRID_SIDE_LEN][GRID_SIDE_LEN]u8 = undefined;
    var list_idx: usize = 0;
    
    // Iterate over all diagonals
    for (0..GRID_SIDE_LEN + GRID_SIDE_LEN - 1) |diag| {
        var col: usize = if (diag < GRID_SIDE_LEN) diag + 1 else GRID_SIDE_LEN;
        const start_row: usize = if (diag < GRID_SIDE_LEN) 0 else diag - (GRID_SIDE_LEN - 1);
        const end_row: usize = if (diag < GRID_SIDE_LEN) diag + 1 else GRID_SIDE_LEN;

        for (start_row..end_row) |row| {
            col -= 1;

            const next_val = sorted_list[list_idx];
            const row_match = row != 0 and grid[row - 1][col] == next_val;
            const col_match = col != 0 and grid[row][col - 1] == next_val;
            if (row_match or col_match) {
                return null;
            }

            grid[row][col] = next_val;
            list_idx += 1;
        }
    }
    return grid;
}

fn printGrid(grid: [GRID_SIDE_LEN][GRID_SIDE_LEN]u8, writer: *std.io.Writer) !void {
    try writer.print("[", .{});
    for (grid, 0..) |grid_row, row_idx| {
        if (row_idx > 0) {
            try writer.print(", ", .{});
        }
        try writer.print("[", .{});
        for (grid_row, 0..) |num, col_idx| {
            if (col_idx > 0) {
                try writer.print(", ", .{});
            }
            try writer.print("{d}", .{num});
        }
        try writer.print("]", .{});
    }
    try writer.print("]\n", .{});
    try writer.flush();
}

fn verifyGrid(grid: [GRID_SIDE_LEN][GRID_SIDE_LEN]u8) bool {
    for (grid, 0..) |row, row_idx| {
        for (row, 0..) |num, col_idx| {
            if (row_idx > 0 and num >= grid[row_idx - 1][col_idx]) {
                return false;
            }

            if (col_idx > 0 and num >= grid[row_idx][col_idx - 1]) {
                return false;
            }
        }
    }
    return true;
}

pub fn main() !void {
    const argv = std.os.argv;
    var input_file: std.fs.File = if (argv.len >= 2) 
        try std.fs.cwd().openFileZ(argv[1], .{ .mode = .read_only })
    else
        std.fs.File.stdin();

    var input_reader = input_file.reader(&input_buf);
    const input = &input_reader.interface;
    var impossible_count: usize = 0;
    
    while (try readList(input)) |list| {
        const grid = sortGridOrImpossible(list);
        if (grid) |real_grid| {
            std.debug.assert(verifyGrid(real_grid));
            try printGrid(real_grid, stdout);
        } else {
            try stdout.print("Impossible\n", .{});
            try stdout.flush();
            impossible_count += 1;
        }
    }
    
    try stdout.print("Total impossible: {d}\n", .{impossible_count});
    try stdout.flush();
}