Posiziona una tessera Carcassonne


23

Il gioco da tavolo

Nel gioco da tavolo " Carcassonne " i giocatori piazzano le tessere abbinando i loro bordi e guadagnano i punteggi più alti creando grandi aree contigue di terreno. Di seguito sono (approssimativamente) i tipi e le quantità di tessere incluse nel gioco:

#01 x4 inserisci qui la descrizione dell'immagine #02 x5 inserisci qui la descrizione dell'immagine #03 x8 inserisci qui la descrizione dell'immagine #04 x2 inserisci qui la descrizione dell'immagine

#05 x9 inserisci qui la descrizione dell'immagine #06 x4 inserisci qui la descrizione dell'immagine #07 x1 inserisci qui la descrizione dell'immagine #08 x3 inserisci qui la descrizione dell'immagine

#09 x3 inserisci qui la descrizione dell'immagine #10 x3 inserisci qui la descrizione dell'immagine #11 x4 inserisci qui la descrizione dell'immagine #12 x5 inserisci qui la descrizione dell'immagine

#13 x3 inserisci qui la descrizione dell'immagine #14 x3 inserisci qui la descrizione dell'immagine #15 x2 inserisci qui la descrizione dell'immagine #16 x5 inserisci qui la descrizione dell'immagine

#17 x5 inserisci qui la descrizione dell'immagine #18 x2 inserisci qui la descrizione dell'immagine #19 x3 inserisci qui la descrizione dell'immagine #20 x1 inserisci qui la descrizione dell'immagine

#21 x5 inserisci qui la descrizione dell'immagine #22 x2 inserisci qui la descrizione dell'immagine #23 x1 inserisci qui la descrizione dell'immagine #24 x1 inserisci qui la descrizione dell'immagine

#25 x1 inserisci qui la descrizione dell'immagine

L'obiettivo

È necessario posizionare una tessera abbinando i bordi, mentre si cerca di mantenere le aree contigue più ampie possibili del terreno.

Posizionamento

  • Le tessere possono essere posizionate solo in uno degli (fino a 4) spazi vuoti adiacenti a qualsiasi tessera (o tessere) esistente nell'area di gioco.
  • Le piastrelle possono essere ruotate di 90, 180 o 270 gradi.

Bordo-matching

  • I bordi di una tessera posizionata devono corrispondere ai bordi a contatto delle (fino a 4) tessere vicine, ovvero i pixel a contatto sono dello stesso colore.

Terreno contiguo

  • "Chiusura di un'area del terreno" si riferisce al posizionamento di una tessera in modo tale che qualsiasi area contigua di colore non possa quindi essere proseguita con ulteriori posizionamenti delle tessere.
  • Se è possibile un posizionamento alternativo, deve essere scelto su qualsiasi posizionamento di piastrelle che chiuderebbe un'area di terreno.
  • Se devi scegliere tra un numero di posizionamenti di chiusura, scegli uno qualsiasi. Se devi scegliere tra un numero di posizionamenti non chiusi, scegli uno qualsiasi.
  • Ignora # ff00ff (i pixel degli angoli) durante il calcolo di aree contigue. Ignorare anche gli edifici, ovvero le aree di colore già completamente racchiuse in una piastrella.

Ingresso

  • L'input è di due immagini:

    1. L'area giochi.

      • L'area di gioco iniziale è composta da una tessera #11(una singola tessera).
      • Anche l'area di gioco aumentata creata come output deve essere supportata come input.
    2. La tessera da posizionare.

      • Tutti i riquadri di esempio devono essere supportati come input.
  • Determinare i bordi corrispondenti / terreno contiguo usando questi dati immagine da solo. Nessun hardcoding.

Produzione

  • L'output è un'immagine che mostra l'area di gioco risultante dopo aver posizionato la tessera.
  • L'immagine deve essere compatibile con il proprio programma, ovvero può essere utilizzata come input dell'area di gioco.
  • Se è impossibile posizionare una tessera, restituire un errore.

Puoi presumere che

  • Le piastrelle sono sempre 55 px per 55 px
  • Le tessere presenteranno sempre e solo i colori attualmente utilizzati nelle tessere di esempio.

Gli appunti

  • La tua risposta deve presentare un esempio di output dopo almeno 2 passaggi (ne viene incoraggiato un numero maggiore).
  • Questo è un rendering parziale e impreciso del gioco da tavolo originale, non è necessario applicare nessuna delle regole o tattiche non menzionate qui.

Punto

  • Il tuo punteggio è il conteggio dei byte del tuo invio.
  • I dati delle immagini non sono inclusi nel tuo punteggio.
  • Il punteggio più basso vince.


Fare un gioco completo

Potresti scrivere una sceneggiatura che utilizza la tua sottomissione per giocare a un gioco completo, che potrebbe consistere in:

  • Posizionamento di una tessera scelta in modo pseudocasuale dall'insieme completo di 85.
  • Restituisce la tessera sul set se non può essere posizionata.
  • Ripetizione fino a quando non è stata posizionata ogni tessera o fino a quando non è possibile posizionare due tessere di fila.

Non sarà incluso nel conteggio dei byte, né migliorerà il tuo punteggio, ma probabilmente offrirò una generosità a questo tipo di risposta.


1
qual è la differenza tra 12, 15 e 17?
Kaine,

grazie per averlo scoperto, 17 era un duplicato. tuttavia 15 differisce in quanto può potenzialmente chiudere un'area di terreno. (a proposito, le aree di colore non sono contigue se toccano solo gli angoli dei pixel)
jsh

Quindi un 15 e due 2 potrebbero creare 2 sezioni nere separate di dimensione 2. Mentre un 12 e due 2 potrebbero invece creare una sezione nera di 3 dimensioni. Ok.
Kaine,

2
1. se è possibile utilizzare lo strumento secchio riempimento vernice ms per modificare il colore di un'area, si tratta di un'area contigua. nel tuo esempio ci sarebbero 7 aree contigue. 2. sembra ragionevole. purché si utilizzino due immagini come specificato, è possibile farlo come desiderato. 3. puoi rappresentare uno spazio vuoto nel modo che preferisci. la trasparenza è una buona opzione. puoi anche usare qualsiasi colore non presente nelle piastrelle di esempio.
Jsh

1
@ hosch250 l'area di gioco è infinita (si estende se necessario). Con solo la prima tessera in gioco, la prima tessera è l'intera area di gioco.
jlahd,

Risposte:


8

Perl 5 con PerlMagick: 875 789 763

Non ho contato la linea a partire da sub w, che viene utilizzata per ordinare le posizioni sulla distanza dal centro per preferire soluzioni compatte (ora funziona correttamente). In questa versione la chiusura è evitata come richiesto ma trovo il contrario più interessante e fedele al gioco. Per ottenere ciò, cambia la linea $s=$t if!grep...in $s=$t if grep....

use Image::Magick;
sub p{/x/;@o=$r->GetPixel(y=>$'+pop,x,$`+pop);"@o"}
sub o{$w=&p;"0 0 0"eq$w?3:&p eq$w}
sub f{$r->FloodfillPaint(y=>$J+$',x,$I+$&,channel,All,fill,@_)}
($i=Image::Magick->new)->Read(@ARGV);$r=$b=$i->[0];
$h=$b->Get(rows)+112;$:=$b->Get(width)+112;
$b->Extent(geometry,"$:x$h-56-56",background,none);
@v=grep p()eq"0 0 0",map{map-54+55*$_.x.($'*55-54),//..$:/55}1..$h/55;
sub w{$_=pop;/x/;abs($:-2*$`)+abs($h-2*$')}@v=sort{w($b)<=>w($a)}@v;
map{map{/x/;$I=$`;$J=$';$r=$b->Clone();
($t=$r)->Composite(image,$i->[1],x,$I,y=>$J);
if((o(27,0,27,-1)&o(0,27,-1,27)&o(27,54,27,55)&o(54,27,55,27))==1){
$s=$t if!grep{/../;$r=$t->Clone();f(none);f(red);
!grep{p()eq"1 0 0"}@v}
map{/..$/;($_,$&.$`)}map{($_.-1,$_.55)}10,27,45;
$o=$r=$t;}$i->[1]->Rotate(degrees,90)}($_)x4}@v;
$s||=$o or exit 1;$s->Trim();$s->Write("car.png")

Uso: perl car.pl board.png tile.png. Risultato memorizzato in car.png. Lo stato di uscita è 1 se non è stato possibile posizionare la tessera.

Script per eseguire un gioco completo. Assume il codice di cui sopra è nel file car.ple le piastrelle sono memorizzati nella tilesdirectory denominata 01.pnga 25.png.

use List::Util shuffle;$x='00';
@t=shuffle map{($x++)x$_}split'',a4582941333353325523152111;
`cp tiles/11.png car.png`;
$i++,`perl car.pl car.png tiles/$_.png`,print"placed $i\n"for@t

Ora funziona abbastanza lentamente. 8-12 minuti sulla mia macchina. Con chiusura preferita: Preferisci l'esempio di chiusura Con chiusura evitata (notare che nulla è chiuso).


Il test di chiusura dell'area sembra non funzionare correttamente . La tessera City-with-road-corner a (0,1) è stata l'ultima posizionata.
jlahd,

@jlahd Hai ragione. Per i test ho invertito la condizione in quanto è molto più facile non chiudere una regione (inoltre è una strategia migliore nel gioco reale per chiuderle). Ma ora non sono sicuro che questa condizione inversa funzioni anche correttamente. Lo riparerò oggi.
Nutki,

@jlahd Risolto, grazie per averlo notato. La condizione opposta era OK dopo tutto BTW.
Nutki,

15

Lisp comune, 2650 2221 1992 1186 1111 byte

Aggiornamento: il golf "facile" ora è stato fatto, ulteriori guadagni richiederanno maggiori cambiamenti.

Aggiornamento 2: con la concorrenza sempre più accesa, la nuova versione non favorisce più le posizioni all'interno dell'attuale rettangolo del campo di gioco (che sarebbe 57 byte in più). Questa opzione, oltre a una semplice ottimizzazione della velocità, è abilitata per impostazione predefinita nella versione scaricabile con il simulatore, ma non nella risposta ufficiale di seguito.

Aggiornamento 3: modifiche minori all'interfaccia per guadagni di conteggio di byte importanti.

Ho anche creato una semplice interfaccia utente Web. Il pacchetto completo (un singolo file LISP e le immagini del riquadro) può essere scaricato qui . Per provarlo, installare hunchentoot, zpnge png-readcon quiclisp, carico carcassonne.lisp, e collegarsi a localhost:8080. Il codice è stato testato su CCL / Windows e SBCL / Linux. Le librerie sopra menzionate sono necessarie solo per la parte UI / simulatore; la soluzione stessa è semplice ANSI Common Lisp.

(defun c(f p &aux b a s z(c 55))
  (macrolet((d(v l &body b)`(dotimes(,v,l),@b))
            (b(b c)`(d i c(d j c(setf,b,c))))
            (r(&rest p)`(aref,@p))
            (n(u v i j)`(and(setf l(*(f,u,v)l))
                            (find(r f(+,u,i)(+,v,j))`(0,(r f,u,v))))))
    (labels((p(p w)(d y(ceiling w 2)(d x(- w y y)(rotatef(r p y #6=(+ x y))(r p #6##7=(- w y))(r p #7##8=(- w x y))(r p #8#y)))))
            (a(y x)(or(if(= 0(r f y x))1 #4=(and(= 1(incf(r s y x)))(=(r f y x)z)(push`(,y,x)a)0))0))
            (f(y x)(setf z(r f y x))(if #4#(loop for((y x))= a while(pop a)maximize(+(a(1- y)x)(a y(1- x))(a(1+ y)x)(a y(1+ x))))1)))
      (d i 8(when(d x #1=(array-dimension f 0)(or(= 0(r f(- #1#52 i)x))(return t)))(setf f(adjust-array f`(#2=,(+ #1#c)#2#))))(p f(1- #1#)))
      (d i 4(d u #9=(/ #1#c)(d v #9#
        (let((y(* u c))(x(* v c))(l 9e9))
          (when(= 0(r f y x))
            (b #10=(r f(+ y i)(+ x j))(r p i j))
            (setf s(make-array`(,#1#,#1#))a())
            (ignore-errors(if(> #11=(*(loop for d from 1 to 53
                                            sum(+(n y #3=(+ x d)-1 0)(n #5=(+ y d)(+ 54 x)0 1)(n(+ 54 y)#3#1 0)(n #5#x 0 -1)))
                                      (1+ l))
                                (or(car b)0))
                             (setf b`(,#11#,i,y,x))))
            (b #10#0)))))
         (p p 54))
      (when b(d j(cadr b)(p p 54))(b(r f(+(third b)i)(+(nth 3 b)j))(r p i j)))
      `(,f,b))))

Tutti i feed di linea e la spaziatura di inizio riga sono solo a scopo cosmetico, per garantire la leggibilità e non conteggiati nella somma totale.

Dovresti chiamare la funzione ccon due argomenti: il campo di gioco corrente e il riquadro da posizionare. Entrambi dovrebbero essere array 2D; la tessera 55x55 e il campo ne sono multipli. Inoltre, l'array di campo deve essere regolabile. La funzione restituisce un elenco di due elementi con il nuovo campo come primo argomento. Il secondo elemento è NILse la tessera non può essere posizionata, o altrimenti un elenco contenente le coordinate in alto a sinistra e la rotazione della tessera più recente su quella matrice e il punteggio per quella tessera. Queste informazioni possono essere utilizzate per scopi di visualizzazione.

Si noti che in ulteriori chiamate, è necessario utilizzare il nuovo campo restituito canche se il secondo elemento dell'elenco è NIL(l'array originale potrebbe essere stato modificato adjust-arraye quindi invalidato).

Il codice ora è un po 'lento, l'ottimizzazione del conteggio dei byte si traduce in calcoli ridondanti. L'esempio seguente è stato completato in circa tre minuti sul mio sistema.

Esempio di esecuzione per tutte e 85 le tessere:

inserisci qui la descrizione dell'immagine

Schermata dell'interfaccia utente Web:

inserisci qui la descrizione dell'immagine


Preferire il posizionamento all'interno del rettangolo corrente è una buona idea. Stavo notando che tende ad essere serpente se prendi la via facile.
BMac,

non il punteggio vincente, ma ottieni la generosità per alcune belle innovazioni.
giovedì

9

DarkBASIC Pro: 2078 1932 1744 byte

AGGIORNAMENTO: solo più sforzo per giocare a golf

AGGIORNAMENTO: ora soddisfa pienamente le specifiche, inclusa la preferenza per le scelte non di chiusura.

Ho scelto DarkBASIC perché, sebbene sia piuttosto dettagliato, fornisce un set di comandi estremamente diretto e semplice per la manipolazione delle immagini.

Ho caricato un EXE per le persone che non hanno il compilatore DarkBASIC ( Windows ).

Uscita campione

#constant m memblock
#constant f function
#constant k endfunction
#constant z exitfunction
#constant i image
#constant e endif
#constant t then
#constant o or
#constant s paste image
#constant n next
#constant r for
set i colorkey 0,20,0:load i "map.png",1:f$="next.png"
if file exist(f$)=0 t f$=str$(rnd(24)+1)+".png"
load i f$,2:make m from i 1,1:make m from i 2,2
global ts,h,j,u,v,td
ts=i width(2):h=i width(1):j=i height(1):u=h/ts:v=j/ts:td=ts*2
create bitmap 2,h+td+1,j+td+1:r b=1 to 4:r xx=0 to u+1:r yy=0 to v+1:x=xx*ts-1:y=yy*ts-1
cls 5120:s 1,ts,ts,1:if (a(x+1,y) o a(x,y+1) o a(x-ts,y) o a(x,y-ts)) and a(x,y)=0
x1=ts*xx:y1=ts*yy:make i from m 2,2:s 2,x1,y1,1
cl=0:r fd=0 to 1:r x2=1 to ts-2:r yt=0 to 1:y2=yt*ts-yt:y3=yt*ts+yt-1
aa=x2:ab=x2:ba=y2:bb=y3:t2=y1:r t3=0 to 1:p=point(x1+aa,y1+ba):q=point(x1+ab,y1+bb)
if p<>q and rgbg(q)<>20 and t2+b>0 t goto fa
if fd and p<>0xFF0000
if l(x1+aa,y1+ba,p)=0 t cl=1
e
aa=y2:ba=x2:bb=x2:ab=y3:t2=x1:n t3:n yt:n x2:n fd:dn=1:c=xx-1:g=yy-1:make i from m 3,2:if cl=0 t goto dm
e
fa:
n y:n x
d=ts/2:r x=0 to d:r y=0 to d-1:vx=ts-1-x:vy=ts-1-y:t1=rd(x,y):t2=rd(vy,x):wr(vy,x,t1):t1=rd(vx,vy):wr(vx,vy,t2):t2=rd(y,vx):wr(y,vx,t1):wr(x,y,t2):n x:n y:n b
dm:
if dn=0 t report error "Not placed"
p=c<0:q=g<0:t1=h+ts*(p o c>=u):t2=j+ts*(q o g>=v):cls 5120:p=ts*p:q=ts*q:s 1,p,q,1:s 3,c*ts+p,g*ts+q,1:get i 1,0,0,t1,t2,1:save i "map.png",1
end
f l(x,y,w)
if x<0 o y<0 o x>=h+td o y>=j+td t z 1
p=point(x,y)
if rgbg(p)=20 t z 1
if p<>w t z 0
dot x,y,0xFF0000:rt=l(x+1,y,p) o l(x-1,y,p) o l(x,y+1,p) o l(x,y-1,p)
k rt
f rd(x,y)
w=m dword(2,0):b=m dword(2,12+(y*w+x)*4)
k b
f wr(x,y,d)
w=m dword(2,0):write m dword 2,12+(y*w+x)*4,d
k
f a(x,y)
if x<0 o y<0 o x>=h o y>=j t z 0
b=m byte(1,15+(y*h+x)*4)
k b
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.