Compressione monopolistica


17

Data una stringa che rappresenta lo stato corrente di una partita di Monopoli all'inizio del turno di un giocatore, comprimi tutti i dati necessari nell'output più piccolo. Le risposte saranno valutate in base alla dimensione dell'output e alla dimensione della fonte .

Nota: ci sono molte varianti regionali, ma tutti i riferimenti in questo post a nomi di proprietà, ecc., Sono basati su questa scheda .


Ingresso:

L'input verrà dato come una singola ;stringa separata. Questa stringa viene data al programma in qualsiasi modo consueto nella lingua scelta, che sia stdin, argomenti, ecc.

L'input non formattato è simile al seguente:

numPlayers                     (1 to 8)
whose turn                     (0 to numPlayers-1)
for each player:
    bankrupt?                  (true/false)
    money                      (0 to 2^16-1)
    get-out-of-jail-free cards (0 to 2)
    position                   (0 to 39) 
    jail turns                 (-1 to 2)
for 28 properties:
    owner                      (-1 to numPlayers-1)
    mortgaged?                 (true/false)
    improvement level          (0 to 5)
for 16 chance cards in deck:
    card index                 (-1 to 15)
for 16 community chest cards in deck:
    card index                 (-1 to 15)

Un input formattato di esempio è questo:

3;1;false;1546;0;14;-1;false;7692;1;10;1;true;1;false;1;1;false;0;0;true;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;3;12;7;4;5;2;13;11;15;6;8;9;10;1;14;-1;

Preso a poco a poco:

3;1;

Ci sono 3 giocatori, ed è il turno del giocatore 1 (a zero, quindi il secondo giocatore)

Giocatori

false;1546;0;14;-1;
false;7692;1;10;1;
true;

Il primo giocatore:

  • non è fallito
  • ha $ 1546 in contanti a portata di mano
  • possiede 0 carte esenti da prigione
  • è in posizione 14 (Virginia Ave)
  • non è in prigione

Il secondo giocatore è in prigione ed è stato per un turno. Non sono sicuro del perché , dal momento che ha una carta GOoJF, ma è lì.

Il terzo giocatore è in bancarotta e non sono richieste né fornite ulteriori informazioni.

Proprietà

1;false;1;
1;false;0;
0;true;0;
-1;false;0;
-1;false;0;
-1;false;0;
...

Le proprietà sono elencate in ordine attorno al tabellone, a partire dal Mediterraneo e terminando a Boardwalk. Le proprietà che non possono essere possedute non sono incluse in questo elenco, quindi ci saranno un totale di 28. Il livello di miglioramento 0significa non migliorato. Il livello 1è una casa, fino al livello 5di un hotel. Un -1proprietario indica che non appartiene a nessun giocatore.

Secondo le regole standard, una proprietà ipotecata deve essere di proprietà e non deve essere migliorata. Una proprietà che viene migliorata deve essere di proprietà e non deve essere ipotecata.

Inoltre, per migliorare una proprietà, un giocatore deve possedere l'intero blocco di colore. Ai fini di questo gioco, non ci importa se le proprietà vengono migliorate "in modo uniforme".

Nota che queste posizioni non sono le stesse delle posizioni del giocatore indicate sopra. Ad esempio, un giocatore nella 5posizione sarebbe su Reading Railroad, che è la terza proprietà dell'elenco (poiché Go, Community Chest e Income Tax non possono essere posseduti). Le posizioni dei giocatori vanno da 0(Vai) in senso orario a 39(Passeggiata).

Carte

0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;
3;12;7;4;5;2;13;11;15;6;8;9;10;1;14;-1;

Ciascuno dei mazzi Chance e Community Chest ha un 16totale di carte. I numeri sono presentati come appaiono nel mazzo attualmente mischiato. Per questo esempio, la prima carta estratta dal mazzo Chance sarà la carta 0, seguita dalla carta 1(chi mescola quel mazzo succhia). La prima carta tirata dal petto della Comunità è la carta 3, quindi 12.

Non preoccuparti del significato di ciascuna carta (il testo della carta), ad eccezione della carta 0. Questa è la carta Esci di prigione gratuita per quel mazzo. Se un giocatore lo possiede, sarà alla fine della lista, rappresentato come -1.


Produzione:

Devi produrre (per console, stdout o file) una rappresentazione dello stato del gioco. Ciò deve includere tutte le informazioni richieste per rappresentare il gioco. Ad esempio, è possibile omettere (o abbreviare) proprietà non possedute, poiché non possono né essere migliorate né ipotecate. L'input non può ometterli perché è un elenco non indicizzato.

La compressione deve essere eseguita in modo da poter calcolare la dimensione di output nel caso peggiore. Ciò può squalificare alcuni algoritmi di compressione (a meno che non sia possibile dimostrare il caso peggiore e fornire un esempio di input nel caso peggiore).

A meno che il codice sorgente non sia irragionevolmente dettagliato, spiega come viene rappresentato il gioco. Le risposte consistenti in nient'altro che un programma golfizzato e output compresso sono scoraggiate. Ad esempio, se si omettono determinati valori, spiegare come è possibile derivarli dall'output.


Punteggio / Regole:

Il punteggio si basa sia sulla dimensione di compressione nel caso peggiore in bit , sia sulla dimensione del codice sorgente in byte :

score = (outputBits * 2) + encoderSourceBytes

Una risposta completa deve includere:

  • Esempio di output
  • Sorgente encoder
  • Sorgente del decodificatore (non conteggiato rispetto al punteggio)

Tutti gli encoder devono essere programmi completi e sono vietate le scappatoie standard . È inoltre vietato l'uso di librerie di compressione integrate o esterne.

Il vincitore è la risposta con il punteggio più basso , come definito sopra.


Hm, perché non richiedere un decodificatore e una prova che la codifica funziona davvero (ed è riverificabile)? E che dire di includere cose come la compressione gzip integrata? Ciò renderebbe davvero difficile capire la dimensione di compressione nel caso peggiore.
Martin Ender,

@ m.buettner Modificato. Ho aggiunto un po 'di librerie di compressione e un po' di prove del caso peggiore. Non voglio davvero imporre un decodificatore. I poster possono includerli se desiderano provare la loro soluzione, ma non verranno conteggiati rispetto al punteggio.
Geobits

Oh sì, non stavo suggerendo di aggiungerli al punteggio. Potresti comunque richiedere un decodificatore (non salvato) come prova. Ciò semplifica il test se gli invii possono gestire casi speciali.
Martin Ender,

@ m.buettner Hai fatto un punto eccellente. Decodificatori lo è.
Geobits

2
The second player is in jail, and has been for one turn. I'm not sure why, since he has a GOoJF card, but he's there.Essere in prigione è un buon lategame perché non stai pagando l'affitto. :)
undergroundmonorail

Risposte:


4

(Una risposta è stata recentemente modificata, il che ha portato questa domanda alla mia attenzione, e ho deciso di provarlo anche se è una vecchia domanda.)

Swift 1.2 - 1016 punti (2 * 81 + 854)

L'output è nel peggiore di 81 byte e cambia con la quantità di giocatori.

Entrambi i due metodi funzionano. La versione Playground è leggermente più lunga.

Comprimi Playground

(Presuppone Input.txtè nel Playground Documents Directory)

// Compressor e(inputFileName, outputFileName)
import Cocoa;typealias S=String;typealias U=UInt8;func e(a:S,b:S){var d=NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)as![S],g=d[0],r=S(contentsOfFile:"\(g)/\(a)",encoding:4,error:nil)!.componentsSeparatedByString(";"),z=[U](count:4,repeatedValue:0),c=[U](),p:()->Int={r.removeAtIndex(0).toInt()!},f:()->Bool={r.removeAtIndex(0)=="true" ?true:false},j=U(p());c+=[(j<<4)|(U(p()))];for _ in 0..<j{if f(){c+=[255]}else{let(t,g,v)=(UInt16(p()),U(p()),U(p()));c+=[(U(p()))|(g<<2),v,U(t>>8),U(t&255)]}};for _ in 0..<28{c+=[(U(bitPattern:Int8(p()))<<4)|((f() ?1:0)<<3)|(U(p()))]};for h in 0..<16{let y=h>7 ?1:0,x=Int8(p()),w=Int8(p());c+=[(U(bitPattern:x)<<4)|(U(bitPattern:w)&15)];z[y]=z[y]<<1;if x == -1{z[y]|=1};z[y+1]=z[y+1]<<1;if w == -1{z[y+1]|=1}};NSData(bytes:c+z,length:c.count+4).writeToFile("\(g)/\(b)",atomically:true)}

// Decompressor d(inputFileName, outputFileName)
func d(a:S,b:S){var d = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)as![String],e=d[0],i=NSData(contentsOfFile:"\(e)/\(a)")!,n=[UInt8](count:i.length,repeatedValue:0),o="";i.getBytes(&n,length:i.length);let k=n.removeAtIndex(0),j=k>>4;o+="\(j);\(k&15);";for _ in 0..<j{let h=n.removeAtIndex(0);if h>>7 == 1{o+="true;";continue};let p=n.removeAtIndex(0),b=n.removeAtIndex(0),c=n.removeAtIndex(0);o+="false;\(UInt16(b)<<8|UInt16(c));\(h>>2);\(p);\(h&0b11);"};for _ in 0..<28{let p=Int8(bitPattern:n.removeAtIndex(0)),mortgage=((p>>3)&1)==1 ?"true":"false";o+="\(p>>4);\(mortgage);\(p&7);"};var m=[U](count:4,repeatedValue:0);for i in reverse(0..<4){m[i]=n.removeLast()};for h in 0..<16{var i=h>7 ?1:0,z=n.removeAtIndex(0),x=Int8(z>>4),y=Int8(z&15),isUnowned1=m[i]&128;m[i]=m[i]<<1;let isUnowned2=m[i+1]&128;m[i+1]=m[i+1]<<1;if isUnowned1 != 0 {x=(-1)};if isUnowned2 != 0 {y=(-1)};o+="\(x);\(y);"};o.writeToFile("\(e)/\(b)",atomically:true,encoding:4,error:nil)}

// Test function to compare the files
func t(a:S,b:S)->Bool{let d=NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)as![String],c=d[0],i=S(contentsOfFile:"\(c)/\(a)",encoding:4,error:nil)!,j=S(contentsOfFile:"\(c)/\(b)",encoding:4,error:nil)!;return i==j}
// Usage
e("Input.txt", "Output.bin")  // Encode
d("Output.bin", "Output.txt") // Decode
t("Input.txt", "Output.txt")  // Test -> Should output 'true'

Compress.swift - esegui in Terminal usandoswift Compress.swift

(Presuppone Input.txtè sul Desktop)

// Compressor - 854 Bytes
import Cocoa;typealias S=String;typealias U=UInt8;func e(a:S,b:S){var d=NSSearchPathForDirectoriesInDomains(.DesktopDirectory,.UserDomainMask, true)as![S],g=d[0],r=S(contentsOfFile:"\(g)/\(a)",encoding:4,error:nil)!.componentsSeparatedByString(";"),z=[U](count:4,repeatedValue:0),c=[U](),p:()->Int={r.removeAtIndex(0).toInt()!},f:()->Bool={r.removeAtIndex(0)=="true" ?true:false},j=U(p());c+=[(j<<4)|(U(p()))];for _ in 0..<j{if f(){c+=[255]}else{let(t,g,v)=(UInt16(p()),U(p()),U(p()));c+=[(U(p()))|(g<<2),v,U(t>>8),U(t&255)]}};for _ in 0..<28{c+=[(U(bitPattern:Int8(p()))<<4)|((f() ?1:0)<<3)|(U(p()))]};for h in 0..<16{let y=h>7 ?1:0,x=Int8(p()),w=Int8(p());c+=[(U(bitPattern:x)<<4)|(U(bitPattern:w)&15)];z[y]=z[y]<<1;if x == -1{z[y]|=1};z[y+1]=z[y+1]<<1;if w == -1{z[y+1]|=1}};NSData(bytes:c+z,length:c.count+4).writeToFile("\(g)/\(b)",atomically:true)}
// Decompressor
func d(a:S,b:S){var d = NSSearchPathForDirectoriesInDomains(.DesktopDirectory,.UserDomainMask,true)as![String],e=d[0],i=NSData(contentsOfFile:"\(e)/\(a)")!,n=[UInt8](count:i.length,repeatedValue:0),o="";i.getBytes(&n,length:i.length);let k=n.removeAtIndex(0),j=k>>4;o+="\(j);\(k&15);";for _ in 0..<j{let h=n.removeAtIndex(0);if h>>7 == 1{o+="true;";continue};let p=n.removeAtIndex(0),b=n.removeAtIndex(0),c=n.removeAtIndex(0);o+="false;\(UInt16(b)<<8|UInt16(c));\(h>>2);\(p);\(h&0b11);"};for _ in 0..<28{let p=Int8(bitPattern:n.removeAtIndex(0)),mortgage=((p>>3)&1)==1 ?"true":"false";o+="\(p>>4);\(mortgage);\(p&7);"};var m=[U](count:4,repeatedValue:0);for i in reverse(0..<4){m[i]=n.removeLast()};for h in 0..<16{var i=h>7 ?1:0,z=n.removeAtIndex(0),x=Int8(z>>4),y=Int8(z&15),isUnowned1=m[i]&128;m[i]=m[i]<<1;let isUnowned2=m[i+1]&128;m[i+1]=m[i+1]<<1;if isUnowned1 != 0 {x=(-1)};if isUnowned2 != 0 {y=(-1)};o+="\(x);\(y);"};o.writeToFile("\(e)/\(b)",atomically:true,encoding:4,error:nil)}
func t(a:S,b:S)->Bool{let d=NSSearchPathForDirectoriesInDomains(.DesktopDirectory,.UserDomainMask,true)as![String],c=d[0],i=S(contentsOfFile:"\(c)/\(a)",encoding:4,error:nil)!,j=S(contentsOfFile:"\(c)/\(b)",encoding:4,error:nil)!;return i==j}
e("Input.txt", "Output.bin")
d("Output.bin", "Output.txt")
println(t("Input.txt", "Output.txt"))

Ingresso / uscita campione

3;1;false;1534;0;14;0;false;34;1;10;1;true;1;false;1;1;false;0;0;true;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;3;12;6;9;4;-1;4;8;4;2;9;5;11;10;14;7;

.

31 00 0E 05 FE 05 0A 00 22 FF 11 10 08 F0 F0 F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 
F0 F0 F0 F0 F0 F0 01 23 45 67 89 AB CD EF 3C 69 
4F 48 42 95 BA E7 00 00 20 00

4

Pure C (3592 punti)

L'output è di 182 byte. La dimensione è O (1), quindi questo è il caso peggiore.

Questo utilizza ampiamente sscanf per leggere i file e scarica semplicemente le strutture su disco.

Ho dovuto modificare leggermente l'input, poiché il tuo esempio non includeva 28 proprietà.

Per le variabili, le ho nominate dalla prima lettera di ciò che è, o se ciò sarebbe in conflitto, la seconda (o successiva) lettera. Ad esempio, Game, pLayer, pRoperty, ecc.

compress.c (680 byte):

#import <stdio.h>
#import <stdint.h>
#define T(X) for(int i=0;i<(X);i++)
typedef uint8_t U;typedef struct{U p;U t;U a[16];U e[16];}G;typedef struct{U b;uint16_t m;U c;U p;U t;}L;typedef struct{int8_t o;U m;U i;}R;G g;L l[8];R r[28];main(){FILE *f=fopen("input.txt","r");fscanf(f,"%d;%d;",&g.p,&g.t);T(g.p){l[i].b=(fgetc(f)=='t');while(fgetc(f)!=';');if(!l[i].b){fscanf(f,"%d;%d;%d;%d;",&l[i].m,&l[i].c,&l[i].p,&l[i].t);}}T(28){fscanf(f,"%d;",&r[i].o);r[i].m=(fgetc(f)=='t');while(fgetc(f)!=';');fscanf(f,"%d;",&r[i].i);}T(16){fscanf(f,"%d;",&g.a[i]);}T(16){fscanf(f,"%d;",&g.e[i]);}f=fopen("c.dat","w");fwrite(&g,sizeof(G),1,f);fwrite(&l,sizeof(L),8,f);fwrite(&r,sizeof(R),28,f);}

compress.c (pre-golf)

#include "m.h"

#define NEXT_FIELD b=strchr(b,';')+1;

G g;
L l[8];
R r[28];

char a[1024];
char *b = a;

main() {
    FILE *i = fopen("input.txt", "r");
    fgets(a, 1024, i);
    fclose(i);

    sscanf(b, "%d;%d;", &g.p, &g.t);
    NEXT_FIELD NEXT_FIELD

    TIMES(g.p) {
        l[i].b = (*b == 't'); NEXT_FIELD
        if(!l[i].b) {
            sscanf(b, "%d;%d;%d;%d;", &l[i].m, &l[i].c, &l[i].p, &l[i].t);
            NEXT_FIELD NEXT_FIELD NEXT_FIELD NEXT_FIELD
        }
    }

    TIMES(28) {
        sscanf(b, "%d;", &r[i].o); NEXT_FIELD
        r[i].m = (*b == 't'); NEXT_FIELD
        sscanf(b, "%d;", &r[i].i); NEXT_FIELD
    }

    TIMES(16) {
        sscanf(b, "%d", &g.a[i]);
        NEXT_FIELD
    }

    TIMES(16) {
        sscanf(b, "%d", &g.e[i]);
        NEXT_FIELD
    }

    FILE *c = fopen("c.dat", "w");
    fwrite(&g, sizeof(G), 1, c);
    fwrite(&l, sizeof(L), 8, c);
    fwrite(&r, sizeof(R), 28, c);
    fclose(c);
}

decompress.c :

#import <stdio.h>
#import <stdint.h>

#define T(X) for(int i = 0; i < (X); i++)
typedef uint8_t U;

typedef struct {
    U p;
    U t;
    U a[16];
    U e[16];
} G;
typedef struct {
    U b;
    uint16_t m;
    U c;
    U p;
    U t;
} L;
typedef struct {
    int8_t o;
    U m;
    U i;
} R;

G g;
L l[8];
R r[28];

main() {
    FILE *c = fopen("c.dat", "r");
    fread(&g, sizeof(G), 1, c);
    fread(&l, sizeof(L), 8, c);
    fread(&r, sizeof(R), 28, c);
    fclose(c);

    FILE *d = fopen("output.txt", "w");

    fprintf(d, "%d;%d;", g.p, g.t);
    T(g.p) {
        fprintf(d, "%s;", l[i].b ? "true" : "false");
        if(!l[i].b){
            fprintf(d, "%d;%d;%d;%d;", l[i].m, l[i].c, l[i].p, l[i].t);
        }
    }
    T(28) {
        fprintf(d, "%d;%s;%d;", r[i].o, r[i].m ? "true" : "false", r[i].i);
    }
    T(16) { fprintf(d, "%d;", g.a[i] != 255 ? g.a[i] : -1); }
    T(16) { fprintf(d, "%d;", g.e[i] != 255 ? g.e[i] : -1); }

    fclose(d);
}

Ingresso / uscita :

3;1;false;1546;0;14;0;false;7692;1;10;1;true;1;false;1;1;false;0;0;true;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;-1;false;0;0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;3;12;7;4;5;2;13;11;15;6;8;9;10;1;14;-1;

Compresso (182 byte):

0301 0001 0203 0405 0607 0809 0a0b 0c0d
0e0f 030c 0704 0502 0d0b 0f06 0809 0a01
0eff 0000 0a06 000e 0000 0000 0c1e 010a
0100 0100 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0100 0101 0000 0001 00ff 0000 ff00
00ff 0000 ff00 00ff 0000 ff00 00ff 0000
ff00 00ff 0000 ff00 00ff 0000 ff00 00ff
0000 ff00 00ff 0000 ff00 00ff 0000 ff00
00ff 0000 ff00 00ff 0000 ff00 00ff 0000
ff00 00ff 0000 

Eseguirlo!

$ make compress decompress && ./compress && ./decompress && md5 input.txt output.txt
MD5 (input.txt) = fa655a5a17d67b188424ab0dcfdfb825
MD5 (output.txt) = fa655a5a17d67b188424ab0dcfdfb825

Grazie, ho inserito l'intestazione per salvare un po 'e ho corretto il mio punteggio per contare i byte.
wjl

Sembra che potresti salvare alcuni byte con la codifica run-length successiva. Non sono sicuro di quanto sia fattibile, ma se lo fai tramite un byte di escape, dovrebbe anche funzionare bene con dati non ripetitivi. Eh.
cjfaure,
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.