ASCII Maze Compression


9

Sfida

Progetta un algoritmo di compressione specializzato per la compressione di labirinti ASCII. Sarà necessario creare sia un algoritmo di compressione sia un algoritmo di decompressione. Il tuo punteggio si baserà sulla dimensione dei tuoi labirinti compressi.

labirinti

Questi labirinti sono fatte principalmente di caratteri (piani), +, -, |, e #(pareti), e precisamente uno ciascuno di ^(start) e $(fine). Possono anche contenere lettere ASCII, che contano come piastrelle per pavimento. Ai fini di questa sfida, i labirinti non devono essere risolvibili e il significato reale dei contenuti del labirinto è irrilevante.

  • + verrà utilizzato per celle a parete in cui è presente almeno una cella a parete adiacente orizzontalmente e almeno una cella a parete adiacente verticalmente.
  • | verrà utilizzato per celle a parete in cui è presente almeno una cella a parete adiacente verticalmente, ma nessuna cella a parete adiacente orizzontalmente.
  • - verrà utilizzato per celle a parete in cui è presente almeno una cella a parete adiacente orizzontalmente, ma nessuna cella a parete adiacente verticalmente
  • # verrà utilizzato solo per le cellule della parete che non sono ortogonalmente adiacenti ad altre cellule della parete.

Tutti i labirinti sono rettangolari, ma non hanno necessariamente un regolare allineamento griglia / parete.

Labirinti da comprimere

Labirinto 1

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

Labirinto 2

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

Labirinto 3

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

Labirinto 4

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

Labirinto 5

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

Labirinto 6

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

Labirinto 7

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

Labirinto 8

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

Labirinto 9

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

Labirinto 10

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

Regole, ipotesi, punteggio

  • Le scappatoie standard sono vietate
    • Scrivi un programma generale, non uno che funziona solo per i dieci casi di test. Deve essere in grado di gestire qualsiasi labirinto arbitrario.
  • Puoi presumere che ci saranno esattamente un'entrata e un'uscita. Entrate ed uscite saranno sempre al confine del labirinto.
  • Si può presumere che tutti gli input utilizzino i muri che seguono le regole sopra elencate. L'algoritmo di compressione non deve funzionare per labirinti contenenti muri che violano tali regole.
  • I labirinti in ingresso possono o meno essere risolvibili.
  • Puoi presumere che il labirinto non sarà più grande di 100 caratteri in entrambe le direzioni.
  • Puoi presumere che le lettere non appariranno sul bordo del labirinto. (poiché questo è il caso degli esempi forniti)
  • Il tuo punteggio è la dimensione totale, in byte (ottetti), di tutti i labirinti compressi.
    • Puoi usare esadecimali, base64, stringhe binarie o qualsiasi formato simile come rappresentazione per il tuo labirinto compresso se lo ritieni più conveniente. Dovresti comunque contare il risultato in interi ottetti, arrotondato per ogni labirinto (ad es. 4 cifre base64 sono 3 byte, 2 cifre esadecimali sono 1 byte, 8 cifre binarie sono 1 byte, ecc ...)
    • Il punteggio più basso vince!

Esiste un limite di dimensioni per un labirinto?
Incarnazione dell'ignoranza

@EmbodimentofIgnorance 100x100
Beefster

@Arnauld in realtà è stato un problema di copia e incolla, ma penso che la formattazione SE rimuova gli spazi alla fine della riga comunque. Sì, dovrebbe essere imbottito nello spazio.
Beefster

@ChasBrown, che conta come una scappatoia standard, il che significa che è vietato per impostazione predefinita.
Beefster

1
@schnaader, sembra ragionevole dato l'esempio dei casi di test.
Beefster,

Risposte:


5

JavaScript (Node.js) , punteggio =  586 541 503 492  479 byte

I muri sono memorizzati come un flusso di bit con codifica Huffman che descrive se una funzione di previsione sta restituendo l'ipotesi corretta o meno.

(d,c)dc

Provalo online!

Comune

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

Compressione

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

Decompressione

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

Come?

Un labirinto è codificato come un flusso di bit che alla fine viene convertito in una stringa.

Intestazione

L'intestazione è composta da:

  • w
  • h

Dati del muro

01

01

  • 000000
  • 0100001
  • 10010010
  • 111000011
  • 0110100
  • eccetera.

WnPnCn

Wn=PnCn

Le forme del muro finale sono dedotte in modo simile alla risposta di Nick Kennedy .

Personaggi speciali

Ogni carattere speciale è codificato come:

  • 1

    • 63
    • 111111
  • Il codice del personaggio:

    • su 5 bit se è ^, $, #o[a-z]
    • 11110[A-O]
    • 11111[P-Z]

Hai provato algoritmi di compressione diversi da deflate? Ce ne sono moltissimi sullo scaffale!
dfeuer

Non esiste una regola che dice che deve funzionare in TIO!
dfeuer

O_o bello, mi chiedo se la compressione decimale sarebbe di aiuto (sostanzialmente l'opposto di huffman, lo spazio è 0 a 1, diviso in sezioni con dimensioni arbitrarie (<1 ovviamente), e la codifica è il numero binario più corto che rientra all'interno la porzione corretta dello spazio
solo ASCII il

La codifica decimale solo ASCII (nota anche come codifica aritmetica) dovrebbe sicuramente migliorare il rapporto di compressione, ma probabilmente con un piccolo margine su un flusso di dati così breve. Sono sicuro che è possibile migliorare la codifica di Huffman e / o la funzione di previsione prima di passare alla codifica aritmetica, tuttavia (entrambi sono davvero di base in questo momento).
Arnauld,

@ Solo ASCII Ad esempio, dovrei probabilmente provare codici più lunghi (l'utilizzo di stuzzichini è arbitrario). Potrei anche aggiungere un flag di 1 bit nell'intestazione che dice se i dati devono essere decompressi con i codici Huffman statici predefiniti o con i codici dinamici (se risulta migliorare la compressione di alcuni labirinti). Una cosa che ho provato è stata di ruotare il labirinto di 90 ° e vedere se la compressione era migliore. Ma quello stava solo risparmiando 1 byte in generale.
Arnauld,

4

R, punteggio 668 byte

Questo sfrutta il fatto che il personaggio del muro è determinato dai suoi dintorni. Pertanto, i caratteri di muro possono essere codificati come bit. Le informazioni rimanenti che devono essere memorizzate sono le dimensioni del labirinto, le posizioni di inizio e fine e le posizioni di qualsiasi altro personaggio non a muro. Dato che i caratteri non a parete sono ASCII, ho usato il bit più significativo di ogni byte per indicare se c'è un altro carattere che segue in modo che alcune delle parole nei labirinti non debbano avere la posizione di ciascun personaggio memorizzato separatamente. Si noti inoltre che per labirinti inferiori o uguali a 256 caratteri (ad esempio fino a 16x16 o labirinti rettangolari equivalenti), le posizioni possono essere memorizzate in un byte, mentre per labirinti più grandi le posizioni richiedono due byte.

Funzioni di utilità

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

Algoritmo di compressione

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

Algoritmo di decompressione

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

Provalo online!


Sapevo che saresti stato in grado di memorizzare i muri come bit, ma mi piace il tuo approccio per comprimere i dati di posizione del personaggio non muro. +1
Neil
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.