ASCII 미로 압축


9

도전

ASCII 미로 압축에 특화된 압축 알고리즘을 설계하십시오. 압축 알고리즘과 압축 해제 알고리즘을 모두 작성해야합니다. 점수는 압축 된 미로의 크기를 기준으로합니다.

미로

이 미로는 주로 문자로 만든 총, +, -, |,과 #(벽), 그리고 정확히 한 각의 ^(시작) 및 $(끝). 또한 바닥 타일로 간주되는 ASCII 문자가 포함될 수 있습니다. 이 도전의 목적을 위해, 미로를 해결할 수있을 필요는 없으며 미로 함량의 실제 의미는 관련이 없습니다.

  • + 하나 이상의 수평으로 인접한 벽 셀과 하나 이상의 수직으로 인접한 벽 셀이있는 벽 셀에 사용될 것이다.
  • | 하나 이상의 수직으로 인접한 벽 셀이 있지만 수평으로 인접한 벽 셀이없는 벽 셀에 사용됩니다.
  • - 하나 이상의 수평으로 인접한 벽 셀이 있지만 수직으로 인접한 벽 셀이없는 벽 셀에 사용됩니다.
  • # 다른 벽 셀에 직교하지 않는 벽 셀에만 사용됩니다.

모든 미로는 직사각형이지만 반드시 규칙적인 그리드 / 벽 정렬이 필요하지는 않습니다.

압축 미로

미로 1

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

미로 2

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

미로 3

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

미로 4

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

미로 5

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

미로 6

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

미로 7

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

미로 8

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

미로 9

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

미로 10

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

규칙, 가정, 득점

  • 표준 허점은 금지되어 있습니다
    • 10 가지 테스트 사례에서만 작동하는 것이 아니라 일반적인 프로그램을 작성하십시오. 임의의 미로를 처리 할 수 ​​있어야합니다.
  • 정확히 하나의 입구와 하나의 출구가 있다고 가정 할 수 있습니다. 입구와 출구는 항상 미로의 경계에 있습니다.
  • 모든 입력이 위에 열거 된 규칙을 따르는 벽을 사용한다고 가정 할 수 있습니다. 압축 알고리즘은 해당 규칙을 위반하는 벽이 포함 된 미로에 대해 작동하지 않아도됩니다.
  • 입력 미로는 해결 가능하거나 불가능할 수 있습니다.
  • 미로가 어느 방향 으로든 100 자 이하 여야한다고 가정 할 수 있습니다.
  • 미로의 가장자리에는 글자가 나타나지 않는다고 가정 할 수 있습니다. (이것은 제공된 예제의 경우이므로)
  • 귀하의 점수는 모든 압축 미로의 총 크기 (바이트 (옥텟))입니다.
    • 보다 편리한 방법으로 16 진수, base64, 바이너리 문자열 또는 이와 유사한 형식을 압축 된 미로의 표현으로 사용할 수 있습니다. 여전히 결과를 전체 옥텟으로 계산하고 각 미로로 반올림해야합니다 (예 : 4 base64 숫자는 3 바이트, 2 16 진수는 1 바이트, 8 바이너리 숫자는 1 바이트 등).
    • 최저 점수가 이깁니다!

미로에 크기 제한이 있습니까?
무지의 실시

@EmbodimentofIgnorance 100x100
Beefster

@Arnauld는 실제로 복사 붙여 넣기 문제 였지만 SE 형식은 줄 끝에 공백을 제거한다고 생각합니다. 예, 공백으로 채워져 있어야합니다.
Beefster

@ChasBrown은 표준 허점으로 간주되며 기본적으로 금지되어 있습니다.
Beefster

1
@schnaader, 예제 테스트 사례를 고려할 때 합리적인 것으로 보입니다.
Beefster

답변:


5

JavaScript (Node.js) , 점수 =  586541503 492  479 바이트

벽은 예측 함수가 정확한 추측을 반환하는지 여부를 설명하는 허프만 인코딩 된 비트 스트림으로 저장됩니다.

특수 문자는 로 저장되며 , 여기서 는 이전 특수 문자와의 거리이고 는 ASCII 코드입니다.(d,c)dc

온라인으로 사용해보십시오!

흔한

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];
}

압축

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')
  );
}

감압

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');
}

어떻게?

미로는 비트 스트림으로 인코딩되어 결국 문자열로 변환됩니다.

헤더

헤더는 다음으로 구성됩니다.

  • 7 비트 의 폭w
  • 7 비트 의 높이h

벽 데이터

우리는 전체 미로를 살펴보고 이전에 발생한 셀을 기반으로 다음 셀이 벽인지 여부를 예측하려고 시도합니다. 정확 하면 , 잘못하면 방출합니다 .01

(희망)와 훨씬 더 보정 비트의 순서로 결과를 '초 이상 S'. 이 시퀀스는 니블로 분할되며 하드 코드 된 허프만 코드를 사용하여 저장됩니다.01

  • 000000
  • 0100001
  • 10010010
  • 111000011
  • 0110100
  • 기타

벽 을 디코딩하기 , 압축 해제 루틴은 동일한 예측 계산하고 필요한 경우 보정 비트 사용하여 결과를 토글합니다 .WnPnCn

Wn=PnCn

최종 벽 모양은 Nick Kennedy의 답변 과 비슷한 방식으로 추론됩니다 .

특수 문자

각 특수 문자는 다음과 같이 인코딩됩니다.

  • 벽을 무시하고 마지막 특수 문자 에서 을 뺀 거리 :1

    • 보다 작 으면 6 비트63
    • 또는 + 14 비트 그렇지 않으면 (테스트 케이스에서 사용하지 않지만 이론적으로 필요한 않음)111111
  • 캐릭터의 코드 :

    • 5 비트에 그것의 경우 ^, $, #또는[a-z]
    • 또는 + 4 비트11110[A-O]
    • 또는 + 4 비트11111[P-Z]

이외의 압축 알고리즘을 사용해 보셨습니까 deflate? 선반에 끔찍한 많은 것이 있습니다!
dfeuer

TIO에서 작동해야한다는 규칙은 없습니다!
dfeuer

O_o nice, 십진 압축이 전혀 도움이되는지 궁금합니다 (기본적으로 허프만의 반대, 공간은 0에서 1까지이며 임의의 크기 (<1)로 섹션으로 나뉘며 인코딩은 그 안에 속하는 가장 짧은 이진수입니다) 올바른 공간 조각
ASCII 전용

@ASCII 전용 10 진수 코딩 (일명 산술 코딩)은 압축률을 확실히 향상시켜야하지만 이러한 짧은 데이터 스트림에서 약간의 마진을 보일 수 있습니다. 나는 산술 코딩으로 전환하기 전에 허프만 코딩 및 / 또는 예측 기능을 향상시킬 수 있다고 확신합니다 (두 가지 모두 현재 기본적 임).
Arnauld

@ASCII 전용 예를 들어, 아마도 더 긴 코드를 시도해야합니다 (니블 사용은 임의적입니다). 헤더에 1 비트 플래그를 추가하여 기본 정적 허프만 코드 또는 동적 코드로 데이터를 압축 해제 해야하는지 여부를 알 수 있습니다 (일부 미로의 압축을 향상시키는 것으로 밝혀진 경우). 내가 시도한 한 가지 방법은 미로를 90 ° 회전시키고 압축이 더 잘되는지 확인하는 것이 었습니다. 그러나 그것은 전체적으로 1 바이트를 절약했습니다.
Arnauld

4

R, 668 바이트 점수

이것은 벽 특성이 주변 환경에 의해 결정된다는 사실을 이용합니다. 따라서 월 문자는 비트로 인코딩 될 수 있습니다. 저장해야 할 나머지 정보는 미로의 크기, 시작과 끝의 위치 및 벽이 아닌 다른 문자의 위치입니다. 벽이 아닌 문자는 ASCII이므로 각 바이트의 최상위 비트를 사용하여 미로의 일부 단어가 각 문자의 위치를 ​​저장할 필요가 없도록 다음에 오는 다른 문자가 있는지 여부를 나타 냈습니다. 갈라져. 또한 256 자 이하의 미로 (예 : 최대 16x16 또는 이에 상응하는 직사각형 미로)의 경우 위치는 1 바이트로 저장 될 수 있지만, 더 큰 미로의 경우 위치는 2 바이트가 필요합니다.

유틸리티 기능

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))
    )
  }
}

압축 알고리즘

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)
}

감압 알고리즘

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 = "")
}

온라인으로 사용해보십시오!


벽을 비트로 저장할 수 있다는 것을 알고 있지만 벽이 아닌 문자 위치 데이터를 압축하는 방법이 마음에 듭니다. +1
Neil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.