Nén mê cung ASCII


9

Thử thách

Thiết kế một thuật toán nén chuyên dùng để nén mê cung ASCII. Bạn sẽ cần tạo cả thuật toán nén và thuật toán giải nén. Điểm của bạn sẽ được dựa trên kích thước của mê cung nén của bạn.

Mazes

Những mê cung được thực hiện chủ yếu của các nhân vật (tầng), +, -, |, và #(tường), và chính xác một mỗi ^(bắt đầu) và $(kết thúc). Chúng cũng có thể chứa các chữ cái ASCII, được tính là gạch lát sàn. Đối với mục đích của thử thách này, mê cung không cần phải giải quyết được và ý nghĩa thực sự của nội dung mê cung là không liên quan.

  • + sẽ được sử dụng cho các ô tường nơi có ít nhất một ô tường liền kề theo chiều ngang và ít nhất một ô tường liền kề theo chiều dọc.
  • | sẽ được sử dụng cho các ô tường nơi có ít nhất một ô tường liền kề theo chiều dọc, nhưng không có các ô tường liền kề theo chiều ngang.
  • - sẽ được sử dụng cho các ô tường nơi có ít nhất một ô tường liền kề theo chiều ngang, nhưng không có các ô tường liền kề theo chiều dọc
  • # sẽ chỉ được sử dụng cho các ô tường không liền kề trực tiếp với các ô tường khác.

Tất cả các mê cung đều là hình chữ nhật, nhưng không nhất thiết phải có sự liên kết lưới / tường thông thường.

Mazes để nén

Mê cung 1

+----+----
|  o |    |
| -- | o--+
|    | |  $
 --^-+-+---

Mê cung 2

+-----+---+
|  a  |   |
^ +-+-+ # |
| | |  B  |
| | | --+ |
|   c   | $
+-------+--

Mê cung 3

----------+-+-+-----+-+
^         | | |     | |
+-- --+R #  | |p| | | |
|     | |       | |   |
+---+ +-+-+-- +-+ | | |
|  m| | | |   |   | | |
| +-+ | | | | | --+ | |
| | |    h  | |   | | |
| | | | | |  #  --+-+ |
|     | | | | |  S|   $
+-----+-+-+-+-+---+----

Mê cung 4

+-----+---+-+---+-------^-----+
|     |x  | |   |     tsrq    |
+-+-- +-- | +--  #  --+---- --+
| |   |           |   |       |
| | | | | +-+-+---+ | +-- | +-+
| | | u | | | |     | |   | | |
| +-+ | | | | +---- +-+---+ | |
| |   | |   |    y  |       w |
| | --+ | --+ +-- | +---- | | |
|     | |   | |   | |     | | |
+-- --+ +-+ | | | | +-- | +-+-+
|     | | |   | | | |   |     |
$ | --+-+ | --+-+ | +-+-+-- --+
| |   |      z|   |   |    v  |
+-+---+-------+---+---+-------+

Mê cung 5

++ -----------+
++-       Beep|
$  ----+---+--+
+-+boop|   |  |
| +--- | | | ++
|      | |  +++
+------+-+--+ ^

Mê 6

+-$---------------+-+--
|                 | |j 
| |l ---- # ---+ |  |  
| | |       m  | +--+ |
| | | +-+---- #       |
| | | | |      +----+ |
|o| | | | +----+    | |
|       | |    | -- | |
| | | | | | -+ |    | |
| | | | |  | | +--- | |
| | | | +- | | |   | ++
+-+ |n| |  | ++ +--+ | 
    | |   -+- | |  | +-
+---+ +---    |  | |  ^
|    |     --+ --+ | | 
| -- | |  k  |     | ++
|    | |      +--- | ++
|    |      | |    |  |
+-- -+----  | +----+--+

Mê cung 7

+---+-+-------------+-+^+-----+-------+---+-+---+-+---+-+---+
|   |c|             | | |  c  |       |   | |   | |   |c|   |
+-- | | +-- +-- # | | | +-- --+ +---- +-- | +-+ | | +-+ | --+
|       |   |     | |           |         |   | |c| |       |
| | +-- | +-+-- +-+ +-- # +- # -+-- +-- | | --+ | | | | --+C|
|c| |   | | c   |         |         |c  |             |   | |
+-+-+---+-+-----+---------+---------+---+-------------+---+$|

Mê cung 8

------+-+-+---+-+---+-----------+---+-----+---------------+-+
^     | | |   | |   |           |   |     |      r        | |
+-- | | | t | | +-- +----- # ---+-- +-- --+-- ----+-+ --+ | |
|   |   | | |   |   |         r |   |             | |   |   |
| | | | | +-+ --+ --+-- --------+-- | ----+ --+ | | | --+ | |
| |r| |            rotation               |   | |   |   | | $
+-+-+-+-----------------------------------+---+-+---+---+-+--

Mê cung 9

|$|^--+-+---+-----+-+---+-+-+---+---+-+---+-----+
| |   | |   |     | |   | | | f |   | |   |     |
| +-+ | | # +-+ --+ +-+ | | | # | +-+ +-- | ----+
|   |       | |    f| |           | | |   |   f |
| |F+-+ | | | | +---+ | | | ----+-+ | | --+ --+-+
| |   | | |     |     | | |   f |   |         | |
| | | | +-+-+---+-- | | | +-+-+-+ +-+ +--- # -+ |
| | | |     |   |   |   | | | |   | | |         |
+-+-+ | +---+ --+ | +---+-+ | | --+ f | | | | --+
|     | |         |                 | | | | |   |
| --+f| | | +-- --+--f--+ --+ | ----+ | +-+ +---+
|   |     | |     |     |   | |           |     |
+---+-----+-+-----+-----+---+-+-----------+-----+

Mê cung 10

+-----+-+-----------+
|  q  | |         q |
|Q+-+ | +-+-+-+---- |
$ | |     | | |  q  |
+-+ | | | | | +-- +-+
| |   | |     |   | |
| +-- +-+ |q| +-+ | |
|    q|   | |   |   |
| | | +-- | +-+ | --+
| | | |   | | |     |
+-+-+-+ +-+-+ +-- | |
|       |         | |
+--- # -+ | | +-- | |
|  q      | | |   | ^
+-+ +-- | | +-+ | +-+
| | |   | |q|   |   |
| +-+-+ | +-+-- | | |
|     | | |     | | |
| | | +-+-+-- +-+ +-+
| | |         | q   |
+-+-+---------+-----+

Quy tắc, giả định, chấm điểm

  • Sơ hở tiêu chuẩn bị cấm
    • Viết một chương trình chung, không phải một chương trình chỉ hoạt động trong mười trường hợp thử nghiệm. Nó phải có khả năng xử lý bất kỳ mê cung tùy ý.
  • Bạn có thể cho rằng sẽ có chính xác một lối vào và một lối ra. Lối vào và lối ra sẽ luôn ở trên biên giới của mê cung.
  • Bạn có thể cho rằng tất cả các đầu vào sử dụng các bức tường tuân theo các quy tắc được liệt kê ở trên. Thuật toán nén của bạn không phải làm việc đối với các mê cung chứa các bức tường vi phạm các quy tắc đó.
  • Mê cung đầu vào có thể hoặc không thể giải quyết được.
  • Bạn có thể cho rằng mê cung sẽ không lớn hơn 100 ký tự theo cả hai hướng.
  • Bạn có thể cho rằng các chữ cái sẽ không xuất hiện trên cạnh của mê cung. (vì đây là trường hợp cho các ví dụ được cung cấp)
  • Điểm của bạn là tổng kích thước, tính bằng byte (octet), của tất cả các mê cung được nén.
    • Bạn có thể sử dụng hex, base64, chuỗi nhị phân hoặc bất kỳ định dạng tương tự nào làm đại diện cho mê cung nén của bạn nếu bạn thấy thuận tiện hơn. Bạn vẫn nên đếm kết quả trong toàn bộ octet, làm tròn cho mỗi mê cung (ví dụ: 4 chữ số base64 là 3 byte, 2 chữ số hex là 1 byte, 8 chữ số nhị phân là 1 byte, v.v ...)
    • Điểm số thấp nhất chiến thắng!

Có giới hạn kích thước cho một mê cung?
Hiện thân của sự thiếu hiểu biết

@EmbodimentofIgnorance 100x100
Beefster 15/03/19

@Arnauld thực sự đó là một vấn đề sao chép, nhưng tôi nghĩ định dạng SE dải không gian ở cuối dòng dù thế nào đi nữa. Vâng, nó được cho là đệm không gian.
Beefster

@ChasBrown, được coi là một lỗ hổng tiêu chuẩn, có nghĩa là nó bị cấm theo mặc định.
Beefster 16/03/19

1
@schnaader, có vẻ hợp lý khi đưa ra các trường hợp thử nghiệm ví dụ.
Beefster 26/03/19

Câu trả lời:


5

JavaScript (Node.js) , điểm =  586 541 503 492  479 byte

Các bức tường được lưu trữ dưới dạng luồng bit được mã hóa Huffman mô tả liệu một chức năng dự đoán có trả lại dự đoán chính xác hay không.

(d,c)dc

Hãy thử trực tuyến!

Chung

const HUFFMAN = [
  '00',       // 0000
  '010',      // 0001
  '1001',     // 0010
  '11100',    // 0011
  '011',      // 0100
  '101',      // 0101
  '11110',    // 0110
  '100010',   // 0111
  '110',      // 1000
  '11101',    // 1001
  '1111100',  // 1010
  '1111101',  // 1011
  '10000',    // 1100
  '1111110',  // 1101
  '100011',   // 1110
  '1111111'   // 1111
];

let bin = (n, w) => n.toString(2).padStart(w, '0');

let wallShape = (row, x, y) => {
  let vWall = (row[y - 1] || [])[x] | (row[y + 1] || [])[x],
      hWall = row[y][x - 1] | row[y][x + 1];

  return ' -|+'[row[y][x] ? vWall * 2 | hWall : 0];
}

let predictWall = (row, x, y, w, h) => {
  let prvRow = row[y - 1] || [];
  return !x | !y | x == w - 1 | y == h - 1 | (prvRow[x] | row[y][x - 1]) & !prvRow[x - 1];
}

Nén

let pack = str => {
  let row = str.split('\n').map(r => [...r]),
      w = row[0].length,
      h = row.length;

  let wall = row.map((r, y) => r.map((c, x) => +/[-+|]/.test(c)));

  if(row.some((r, y) => r.some((c, x) => wall[y][x] && wallShape(wall, x, y) != c))) {
    throw "invalid maze";
  }

  row = wall.map((r, y) => r.map((v, x) => predictWall(wall, x, y, w, h) ^ v));
  row = row.map(r => r.join('')).join('');
  row = row.replace(/.{1,4}/g, s => HUFFMAN[parseInt(s.padEnd(4, '0'), 2)]);

  str =
    str.replace(/[\n|+-]/g, '').replace(/ *(\S)/g, (s, c) => {
      let n = c.charCodeAt(),
          i = '^$#'.indexOf(c);

      return (
        bin(s.length > 63 ? 0xFC000 | s.length - 1 : s.length - 1, 6) +
        bin(~i ? i : n < 91 ? (n > 80 ? 0x1F0 : 0x1E0) | ~-n & 15 : n - 94, 5)
      );
    }).trim();

  return (
    Buffer.from(
      (bin(w, 7) + bin(h, 7) + row + str)
      .match(/.{1,8}/g).map(s => parseInt(s.padEnd(8, '0'), 2))
    ).toString('binary')
  );
}

Giảm bớt sức ép

let unpack = str => {
  str = [...str].map(c => bin(c.charCodeAt(), 8)).join('');

  let x, y, n, i, s,
      ptr = 0,
      read = n => parseInt(str.slice(ptr, ptr += n), 2),
      w = read(7),
      h = read(7),
      row = [];

  for(x = s = ''; s.length < w * h;) {
    ~(i = HUFFMAN.indexOf(x += read(1))) && (s += bin(i, 4), x = '');
  }
  for(i = y = 0; y < h; y++) {
    for(row[y] = [], x = 0; x < w; x++) {
      row[y][x] = predictWall(row, x, y, w, h) ^ s[i++];
    }
  }

  row = row.map((r, y) => r.map((c, x) => wallShape(row, x, y)));

  for(i = 0; str[ptr + 10];) {
    for(
      n = (n = read(6)) == 0x3F ? read(14) + 1 : n + 1;
      n -= row[i / w | 0][i % w] == ' ';
      i++
    ) {}

    row[i / w | 0][i % w] = String.fromCharCode(
      (n = read(5)) >= 0x1E ? read(4) + (n == 0x1F ? 81 : 65) : [94, 36, 35][n] || n + 94
    );
  }
  return row.map(r => r.join('')).join('\n');
}

Làm sao?

Một mê cung được mã hóa dưới dạng một luồng bit cuối cùng được chuyển đổi thành một chuỗi.

Tiêu đề

Tiêu đề bao gồm:

  • w
  • h

Dữ liệu tường

01

01

  • 000000
  • 0100001
  • 10010010
  • 111000011
  • 0110100
  • Vân vân.

WnPnCn

Wn= =PnCn

Các hình dạng tường cuối cùng được suy luận theo cách tương tự như câu trả lời của Nick Kennedy .

Nhân vật đặc biệt

Mỗi ký tự đặc biệt được mã hóa là:

  • 1

    • 63
    • 111111
  • Mã của nhân vật:

    • trên 5 bit nếu nó ^, $, #hoặc[a-z]
    • 11110[A-O]
    • 111111[P-Z]

Bạn đã thử các thuật toán nén khác hơn deflate? Có rất nhiều khủng khiếp trên kệ!
dfeuer

Không có quy tắc nào nói rằng nó phải hoạt động trong TIO!
dfeuer 16/03/19

Thật tuyệt, tự hỏi liệu nén thập phân có giúp ích gì không (về cơ bản ngược lại với huffman, khoảng trắng là 0 đến 1, được chia thành các phần có kích thước tùy ý (tất nhiên là <1) và mã hóa là số nhị phân ngắn nhất nằm trong lát cắt chính xác của không gian
chỉ có ASCII

@ ASCII - chỉ mã hóa thập phân (hay còn gọi là mã hóa số học) chắc chắn sẽ cải thiện tỷ lệ nén, nhưng có lẽ bằng một lề nhỏ trên luồng dữ liệu ngắn như vậy. Tôi chắc chắn rằng có thể cải thiện mã Huffman và / hoặc chức năng dự đoán trước khi chuyển sang mã hóa số học (cả hai đều thực sự cơ bản ngay bây giờ).
Arnauld

Ví dụ @ ASCII, có lẽ tôi nên thử các mã dài hơn (sử dụng nibble là tùy ý). Tôi cũng có thể thêm cờ 1 bit trong tiêu đề cho biết liệu dữ liệu sẽ được giải nén bằng mã Huffman tĩnh mặc định hoặc bằng mã động (nếu nó biến ra để cải thiện việc nén một số mê cung). Một điều mà tôi đã thử là xoay mê cung 90 ° và xem liệu có nén tốt hơn không. Nhưng đó chỉ là tiết kiệm 1 byte tổng thể.
Arnauld

4

R, điểm 668 byte

Điều này lợi dụng thực tế là nhân vật tường được xác định bởi môi trường xung quanh. Như vậy, các ký tự trên tường có thể được mã hóa dưới dạng bit. Thông tin còn lại cần được lưu trữ là kích thước của mê cung, vị trí bắt đầu và kết thúc và vị trí của bất kỳ nhân vật không tường nào khác. Vì các ký tự không phải là tường là ASCII, tôi đã sử dụng bit đáng kể nhất của mỗi byte để cho biết liệu có một ký tự khác theo sau để một số từ trong mê cung không phải lưu trữ vị trí của từng ký tự không riêng biệt. Cũng lưu ý rằng đối với mê cung nhỏ hơn hoặc bằng 256 ký tự (ví dụ: tối đa 16x16 hoặc mê cung hình chữ nhật tương đương), các vị trí có thể được lưu trữ trong một byte, trong khi đối với mê cung lớn hơn, các vị trí cần hai byte.

Các chức năng tiện ích

r <- as.raw

int_as_raw <- function(int, bytes = 2) {
  if (bytes == 1) {
    r(int)
  } else {
    do.call(c, lapply(int, function(.x) r(c(.x %/% 256, .x %% 256))))
  }
}

raw_as_int <- function(raw, bytes = 2) {
  if (bytes == 1) {
    as.integer(raw)
  } else {
    sapply(
      seq(1, length(raw) - 1, 2),
      function(.x) as.integer(as.integer(raw[.x + 0:1]) %*% c(256, 1))
    )
  }
}

Thuật toán nén

compress_maze <- function(maze) {
  maze_array <- do.call(rbind, strsplit(maze, ""))
  simple_maze <- r(maze_array %in% c("+", "#", "-", "|"))
  simple_maze <- packBits(c(simple_maze, rep(r(0), (8 - length(simple_maze)) %% 8)))
  maze_dim <- int_as_raw(dim(maze_array), 1)
  bytes_needed <- 1 + (length(maze_array) > 256)
  start_finish <- int_as_raw(sapply(c("^", "$"), function(.x) which(maze_array == .x)) - 1, bytes = bytes_needed)
  other_ascii_locs_rle <- rle(!(maze_array %in% c(" ", "+", "#", "-", "|", "$", "^")))
  other_ascii_locs <- cumsum(
    c(1, other_ascii_locs_rle$lengths[-length(other_ascii_locs_rle$lengths)])
  )[other_ascii_locs_rle$values]
  other_ascii_locs_length <- other_ascii_locs_rle$lengths[other_ascii_locs_rle$values]

  encode_ascii <- function(loc, len) {
    text <- charToRaw(paste(maze_array[loc:(loc + len - 1)], collapse = ""))
    if (len > 1) {
      text[1:(len - 1)] <- text[1:(len - 1)] | r(128)
    }
    c(int_as_raw(loc - 1, bytes = bytes_needed), text)
  }

  other_ascii_encoded <- Map(encode_ascii,
    other_ascii_locs,
    other_ascii_locs_length
    )
  other_ascii_encoded <- do.call(c, other_ascii_encoded)
  c(maze_dim, simple_maze, start_finish, other_ascii_encoded)
}

Thuật toán giải nén

decompress_maze <- function(c_maze) {
  dim_maze <- as.integer(c_maze[1:2])
  len_maze <- prod(dim_maze)
  len_maze_b <- ceiling(len_maze / 8)
  bit_maze <- rawToBits(c_maze[-(1:2)])[1:len_maze]
  dim(bit_maze) <- dim_maze
  bit_maze[-1, ] <- bit_maze[-1, ] | rawShift(bit_maze[-nrow(bit_maze), ] & r(1), 1)
  bit_maze[-nrow(bit_maze), ] <- bit_maze[-nrow(bit_maze), ] | rawShift(bit_maze[-1, ] & r(1), 1)
  bit_maze[, -1] <- bit_maze[, -1] | rawShift(bit_maze[, -ncol(bit_maze)] & r(1), 2)
  bit_maze[, -ncol(bit_maze)] <- bit_maze[, -ncol(bit_maze)] | rawShift(bit_maze[, -1] & r(1), 2)
  bit_maze[(bit_maze & r(1)) == r(0)] <- r(0)
  array_maze <- c(" ", "#", "|", "-", "+")[(as.integer(bit_maze) + 1) %/% 2 + 1]
  dim(array_maze) <- dim_maze
  bytes_needed <- 1 + (len_maze > 256)
  start_finish <- raw_as_int(c_maze[2 + len_maze_b + 1:(bytes_needed * 2)], bytes_needed) + 1
  array_maze[start_finish] <- c("^", "$")
  i <- 3 + len_maze_b + 2 * bytes_needed
  while (i < length(c_maze)) {
    loc <- raw_as_int(c_maze[i + 1:bytes_needed - 1], bytes_needed) + 1
    i <- i + bytes_needed
    text <- character(0)
    while (c_maze[i] & r(128)) {
      text <- c(text, rawToChar(c_maze[i] & r(127)))
      i <- i + 1
    }
    text <- c(text, rawToChar(c_maze[i]))
    array_maze[loc:(loc + length(text) - 1)] <- text
    i <- i + 1
  }
  apply(array_maze, 1, paste, collapse = "")
}

Hãy thử trực tuyến!


Tôi biết bạn có thể lưu trữ các bức tường dưới dạng bit nhưng tôi thích cách tiếp cận của bạn để nén dữ liệu vị trí ký tự không phải tường. +1
Neil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.