ASCII Labirent Sıkıştırma


9

Meydan okuma

ASCII labirentlerini sıkıştırmak için özel bir sıkıştırma algoritması tasarlayın. Hem bir sıkıştırma algoritması hem de bir dekompresyon algoritması oluşturmanız gerekir. Puanınız sıkıştırılmış labirentine göre belirlenir.

Labirentler

Bu labirentler öncelikle karakter yapılır (kat), +, -, |, ve #(duvarlar) ve tam olarak bir her ^(başlangıç) ve $(son). Ayrıca yer karosu olarak sayılan ASCII harfleri içerebilir. Bu zorluğun amaçları için labirentlerin çözülmesi gerekmemektedir ve labirent içeriğinin gerçek anlamı önemsizdir.

  • + en az bir yatay olarak bitişik duvar hücresi ve en az bir dikey olarak bitişik duvar hücresi bulunan duvar hücreleri için kullanılacaktır.
  • | en az bir dikey olarak bitişik duvar hücresinin olduğu, ancak yatay olarak bitişik duvar hücrelerinin olmadığı duvar hücreleri için kullanılacaktır.
  • - yatay olarak bitişik en az bir duvar hücresinin olduğu, ancak dikey olarak bitişik duvar hücrelerinin bulunmadığı duvar hücreleri için kullanılacaktır.
  • # yalnızca diğer duvar hücrelerine ortogonal olarak bitişik olmayan duvar hücreleri için kullanılır.

Tüm labirentler dikdörtgen şeklindedir, ancak düzenli bir ızgara / duvar hizalaması olması gerekmez.

Sıkıştırılacak Labirentler

Labirent 1

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

Labirent 2

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

Labirent 3

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

Labirent 4

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

Labirent 5

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

Labirent 6

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

Labirent 7

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

Labirent 8

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

Labirent 9

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

Labirent 10

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

Kurallar, Varsayımlar, Puanlama

  • Standart boşluklar yasaklandı
    • Sadece on test vakası için çalışan bir program değil, genel bir program yazın. Herhangi bir keyfi labirent işleyebilmelidir.
  • Tam olarak bir giriş ve bir çıkış olacağını varsayabilirsiniz. Girişler ve çıkışlar her zaman labirentin sınırında olacaktır.
  • Tüm girdilerin yukarıda sıralanan kurallara uyan duvarlar kullandığını varsayabilirsiniz. Sıkıştırma algoritmanızın, bu kuralları ihlal eden duvarlar içeren labirentler için çalışması gerekmez.
  • Giriş labirentleri çözülebilir veya çözülmeyebilir.
  • Labirentin her iki yönde de 100 karakterden fazla olmayacağını varsayabilirsiniz.
  • Harflerin labirentin kenarında görünmeyeceğini varsayabilirsiniz. (verilen örnekler için durum böyle olduğu için)
  • Skorunuz, sıkıştırılmış labirentlerin toplam bayt (sekizli) cinsinden boyutudur.
    • Daha uygun bulursanız, sıkıştırılmış labirentinizin temsili olarak hex, base64, ikili dizeler veya benzer bir biçim kullanabilirsiniz. Yine de her labirent için yuvarlanmış olan sonucu tüm sekizli olarak saymalısınız (örneğin, 4 temel64 basamak 3 bayt, 2 onaltılık basamak 1 bayt, 8 ikili basamak 1 bayt vb.)
    • En düşük puan kazanır!

Bir labirent için boyut sınırı var mı?
Somutlaşmışı

Teşhis 100x100
Beefster

@Arnauld aslında bu bir kopya yapıştırma sorunu, ama SE biçimlendirme yine de satır sonunda boşluk şeritler düşünüyorum. Evet, boşluk dolması gerekiyordu.
Beefster

@ChasBrown, standart bir boşluk olarak sayılır, yani varsayılan olarak yasaklanmıştır.
Beefster

1
@schnaader, örnek test durumları göz önüne alındığında makul görünüyor.
19'de Beefster

Yanıtlar:


5

JavaScript (Node.js) , skor =  586641503492479  bayt

Duvarlar, bir tahmin fonksiyonunun doğru tahmini döndürüp döndürmediğini açıklayan Huffman tarafından kodlanmış bir bit akışı olarak saklanır.

Özel karakterler şu şekilde depolanır: (d,c), nerede d önceki özel karakterden uzaklık ve c ASCII kodudur.

Çevrimiçi deneyin!

Yaygın

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

Sıkıştırma

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

Baskıyı azaltma

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

Nasıl?

Bir labirent, sonunda bir dizeye dönüştürülen bir bit akışı olarak kodlanır.

Başlık

Başlık şunlardan oluşur:

  • genişlik w 7 bitte
  • yükseklik h 7 bitte

Duvar verileri

Tüm labirentte yürüyoruz ve daha önce karşılaşılan hücrelere dayanarak bir sonraki hücrenin bir duvar olup olmadığını tahmin etmeye çalışıyoruz. Biz yayarız0 eğer doğruysak ya da 1 eğer yanılıyorsak.

Bu, (umarım) önemli ölçüde daha fazla olan bir dizi düzeltme biti ile sonuçlanır. 0daha 1'S. Bu sıra nibblelara bölünür ve sabit kodlu Huffman kodları kullanılarak saklanır:

  • 000000
  • 0100001
  • 10010010
  • 111000011
  • 0110100
  • vb.

Duvarın kodunu çözmek için Wn, dekompresyon rutini aynı tahmini hesaplar Pn ve gerekirse düzeltme bitini kullanarak sonucu değiştirir Cn:

Wn=PnCn

Son duvar şekilleri Nick Kennedy'nin cevabına benzer bir şekilde çıkarılır .

Özel karakterler

Her özel karakter şu şekilde kodlanır:

  • Mesafe eksi 1 son özel karakterden (duvarları göz ardı ederek):

    • 6 bitten küçükse 63
    • veya gibi 111111 Aksi takdirde + 14 bit (test vakalarında asla kullanılmaz, ancak teoride gereklidir)
  • Karakterin kodu:

    • 5 bit üzerinde o eğer ^, $, #veya[a-z]
    • veya 11110 İçin + 4 bit [A-O]
    • veya 11111 İçin + 4 bit [P-Z]

Başka sıkıştırma algoritmaları denediniz deflatemi? Rafta korkunç bir sürü var!
dfeuer

TIO'da çalışması gerektiğini söyleyen bir kural yok!
dfeuer

O_o güzel, ondalık sıkıştırmanın hiç yardımcı olup olmayacağını merak ediyorum (temel olarak huffman'ın tam tersi, boşluk 0 ila 1, keyfi boyutta bölümlere ayrılmıştır (elbette <1) ve kodlama içeri giren en kısa ikili sayıdır doğru dilim
sadece ASCII sadece

@ Yalnızca ASCII yalnızca Ondalık kodlama (aritmetik kodlama olarak da bilinir), sıkıştırma oranını kesinlikle geliştirmelidir, ancak muhtemelen bu kadar kısa bir veri akışı üzerinde küçük bir farkla. Aritmetik kodlamaya geçmeden önce Huffman kodlamasını ve / veya tahmin fonksiyonunu iyileştirmenin mümkün olduğuna eminim (her ikisi de şu anda gerçekten basittir).
Arnauld

@ Yalnızca ASCII Örneğin, muhtemelen daha uzun kodları denemek gerekir (nibbles kullanma isteğe bağlıdır). Ayrıca, verilerin varsayılan statik Huffman kodlarıyla veya dinamik kodlarla (bazı labirentlerin sıkıştırmasını iyileştirmek için ortaya çıkıyorsa) paketinin açılması gerekip gerekmediğini söyleyen 1 bitlik bir bayrak ekleyebilirim. Denediğim bir şey labirenti 90 ° döndürmek ve daha iyi sıkışıp sıkışmadığını görmekti. Ama bu sadece 1 baytlık tasarruf sağlıyordu.
Arnauld

4

R, skor 668 byte

Bu, duvar karakterinin çevresi tarafından belirlendiği gerçeğinden yararlanır. Bu şekilde, duvar karakterleri bit olarak kodlanabilir. Saklanması gereken geri kalan bilgi, labirentin boyutları, başlangıç ​​ve bitiş konumları ve diğer duvar dışı karakterlerin konumlarıdır. Duvar dışı karakterler ASCII olduğundan, labirentteki bazı sözcüklerin depolanan her karakterin konumuna sahip olması gerekmeyen, başka bir karakter olup olmadığını belirtmek için her baytın en önemli bitini kullandım. ayrı ayrı. Ayrıca 256 karakterden küçük veya buna eşit olan labirentler için (örneğin 16x16 veya eşdeğer dikdörtgen labirentlere kadar) konumların bir baytta saklanabileceğini, daha büyük labirentlerde konumların iki bayta ihtiyaç duyduğunu unutmayın.

Yardımcı fonksiyonlar

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

Sıkıştırma algoritması

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

Dekompresyon algoritması

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

Çevrimiçi deneyin!


Duvarları bit olarak saklayabileceğinizi biliyordum, ancak duvar dışı karakter konum verilerini sıkıştırma yaklaşımınızı seviyorum. +1
Neil
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.