Posizionamento pigro della corazzata


39

Immagina il seguente scenario: stai giocando a navi da guerra con un amico ma decidi di imbrogliare. Invece di muovere una nave dopo che spara dove era la tua nave, decidi di non posizionare alcuna nave. Gli dici che tutti i suoi colpi sono mancati, finché non è impossibile posizionare le navi in ​​questo modo.

Devi scrivere una funzione, o un programma completo, che in qualche modo accetta 3 argomenti: la dimensione del campo, un elenco di quantità di dimensioni della nave e un elenco di scatti.

Campo di battaglia

Uno dei parametri indicati è la dimensione della scheda. Il campo di battaglia è un quadrato di celle e il parametro dato è semplicemente un lato del quadrato.
Ad esempio, la seguente è una tavola di dimensioni 5.

Le coordinate sul campo sono specificate come una stringa a 2 componenti: una lettera seguita da un numero. Puoi fare affidamento sul fatto che le lettere siano in alcuni casi particolari.
La lettera specifica la colonna, il numero specifica la riga della cella (1 indicizzata). Ad esempio nella figura sopra, la cella evidenziata è indicata da "D2".
Poiché ci sono solo 26 lettere, il campo non può essere più grande di 26x26.

Navi

Le navi sono linee rette di 1 o più blocchi. La quantità di navi è specificata in un elenco, in cui il primo elemento è la quantità di navi a 1 cella, la seconda - di navi a 2 celle e così via.
Ad esempio, l'elenco [4,1,2,0,1]creerebbe il seguente set di navi:

Quando vengono posizionate sul campo di battaglia, le navi non possono intersecarsi o toccarsi. Nemmeno con gli angoli. Possono comunque toccare i bordi del campo.
Di seguito puoi vedere un esempio di posizionamento valido della nave:

Si può presumere che per un determinato set di navi, esista sempre un posizionamento su una tavola vuota di una determinata dimensione.

Produzione

Se tali posizionamenti di navi esistono, devi emetterne uno qualsiasi.
Il programma deve generare una matrice separata da nuova riga di caratteri ascii di uno dei 3 tipi - uno per indicare una cella vuota, uno - un pezzo di nave e uno - una cella contrassegnata come "mancata". Non devono essere emessi altri caratteri.
Per esempio,

ZZ@Z
\@@Z
@\\Z
\Z\\

(In questo esempio, ho definito @cella vuota, cella \"mancata" e Zpezzo di nave)

Se non esiste tale posizionamento, il programma / funzione dovrebbe tornare senza emettere nulla.

Ingresso

Se decidi di creare un programma completo, spetta a te specificare come vengono inseriti gli elenchi, alcuni potrebbero passare tramite argomenti, altri tramite stdin.

Questo è , vince il minor numero di personaggi.

Un esempio di soluzione non golfizzata può essere trovato qui
Compilare con -std=c99, il primo argomento è la dimensione della tavola, gli altri argomenti sono le dimensioni della nave. Un elenco di colpi separati da una nuova riga viene fornito su stdin. Esempio:
./a 4 1 1 1 <<< $'A2\nA4\nB3\nC3\nC4\D4'


26x26? Ho disegnato una soluzione basata su regexps e ricorsione, e diventa estremamente lenta = inutilizzabile per i campi più di 6x6. O faccio qualcosa di molto stupido o la mancanza di risposte significa che anche altri non hanno successo.
user2846289

Ho appena scritto un'implementazione in C, calcola istantaneamente almeno 10x10con un 4,3,2,1set di navi
mniip

@mniip: hai un modo specifico di trattare casi difficili (big board, molte navi, guasti a causa della posizione dei colpi)? O è solo una forza bruta (un po 'intelligente)?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Ha alcune ottimizzazioni, cerca di posizionare prima le piccole navi ed esclude le navi di uguale dimensione dalla forza bruta. È un po 'lento quando ci sono molte navi su una tavola piccola e quasi vuota.
mniip

@ n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Ho aggiunto un link di soluzione di esempio.
mniip

Risposte:


2

GolfScript, 236 caratteri

n%([~](:N):M;[.,,]zip{~[)]*~}%-1%:S;{(65-\~(M*+}%:H;{{M*+}+N,/}N,%H-S[]]]{(~\.{(:L;{{M*+{+}+L,%}+N)L-,/}N,%{.{.M/\M%M*+}%}%{3$&,L=},{:K[{..M+\M-}%{..)\(}%3$\- 1$3$K+]}%\;\;\;\+.}{;:R;;;0}if}do{{{M*+.H?)'!'*\R?)'#'*'.'++1<}+N,/n}N,%}R!!*

L'ingresso è dato su STDIN. La prima riga contiene le dimensioni e il numero di navi, ciascuna delle quali segue le coordinate della linea di un singolo colpo.

Esempio:

4 1 1 1
A2
A4
B3
C3
C4
D4

##.#
!..#
#!!#
!.!!

Ho pensato che anche questa sfida dovrebbe avere almeno una risposta GolfScript. Alla fine è diventato molto ungolfscriptish a causa dell'algoritmo utilizzato che privilegia le prestazioni rispetto alla brevità.

Codice annotato:

n%               # Split the input into lines
([~]             # The first line is evaluated to an array [N S1 S2 S3 ...]
(:N              # This happy smiley removes the first item and assigns it to variable N
):M;             # While this sad smiley increases N by one and assigns it to M

[.,,]zip         # For the rest of numbers in the first line create the array [[0 S1] [1 S2] [2 S3] ...]
{~[)]*~}%        # Each element [i Si] is converted into the list (i+1 i+1 ... i+1) with Si items. 
-1%:S;           # Reverse (i.e. largest ship first) and assign the list to variable S.
                 # The result is a list of ship lengths, e.g. for input 3 0 2 we have S = [3 3 1 1 1].

{                # On the stack remains a list of coordinates
  (65-           # Convert the letter from A,B,... into numeric value 0,1,...
  \~(            # The number value is decreased by one
  M*+            # Both are combined to a single index (M*row+col)
}%:H;            # The list of shots is then assigned to variable H

                 # The algorithm is recursive backtracking implemented using a stack of tuples [A S R] where
                 #   - A is the list of open squares
                 #   - S is a list of ships to be placed
                 #   - R is the list of positions where ships were placed                     

    {{           # initial A is the full space of possible coordinates
      M*+        #   combine row and column values into a single index
    }+N,/}N,%    # do the N*N loop
    H-           # minus all places where a shot was done already
    S            # initial S is the original list
    []           # initial R is the empty list (no ships placed yet)
  ]
]                # The starting point is pushed on the stack 

{                # Start of the loop running on the stack
  (~\            # Pop from the stack and extract items in order A R S

  .{             #   If S is non-empty

    (:L;         #     Take first item in S (longest ship) and asign its length to L

    {{M*+{+}+L,%}+N)L-,/}N,%{.{.M/\M%M*+}%}%
                 #     This lengthy expression just calculates all possible ship placements on a single board
                 #     (could be shortened by 3 chars if we don't respect board size but I find it clearer this way)

    {3$&,L=},    #     This step is just a filter on those placements. The overlap (&) with the list of open squares (3$) 
                 #     must be of size L, i.e. all coordinates must be free

                 #     Now we have possible placements. For each one generate the appropriate tuple (A* S* R*) for recursion
    {
      :K         #     Assign the possible new ship placement to temporary variable K
      [
        {..M+\M-}%
        {..)\(}% 
                 #       For each coordinate add the square one row above and below (first loop)
                 #       and afterwards for the resulting list also all squares left and right (second loop)
        3$\-     #       Remove all these squares from the list of available squares A in order to get the new A*
        1$       #       Reduced list of ships S* (note that the first item of S was already remove above)
        3$K+     #       Last item in tuple is R* = R + K, i.e. the ship's placements are added to the result
      ]
    }%           

    \;\;\;       #     Discard the original values A R S
    \+           #     Push the newly generated tuples on the stack
    .            #     Loop until the stack is empty

  }{             #   else

    ;:R;;;       #     Assign the solution to the variable R and remove all the rest from the stack. 
    0            #     Push a zero in order to break the loop

  }if            #   End if

}do              # End of the loop


{                # The output block starts here
  {{             
    M*+
    .H?)         #   Is the current square in the list of shots?
    '!'*         #     then take a number of '!' otherwise an empty string
    \R?)         #   Is the current square in the list of ships?
    '#'*         #     then take a number of '#' otherwise an empty string
    '.'++        #   Join both strings and append a '.'
    1<           #   Take the first item of the resulting string, i.e. altogether this is a simple if-else-structure
  }+N,/n}N,%     # Do a N*N loop
}
R!!*             # Run this block only if R was assigned something during the backtracking. 
                 # (!! is double-negation which converts any non-zero R into a one)
                 # Note: since the empty list from the algorithm is still on the stack if R wasn't assigned
                 # anything the operator !! works on the code block (which yields 1) which is then multiplied
                 # with the empty list.

6

SWI-Prolog, 536 441 1 byte

1 fine riga UNIX, nuova riga finale non conteggiata

La versione con tutte le ottimizzazioni rimosse ( 441 byte):

:-[library(clpfd)].
m(G,L):-maplist(G,L).
l(L,A):-length(A,L).
y(A,E,(X,Y)):-nth1(X,A,R),nth1(Y,R,F),var(F),F=E.
a(A,S):-l(L,A),X#>0,X#=<L,Y#>0,Y#=<L,h(S,(X,Y),A).
h(0,_,_).
h(L,(X,Y),A):-(B=A;transpose(A,T),B=T),y(B,s,(X,Y)),M#=L-1,Z#=Y+1,h(M,(X,Z),B).
e([],_,[]).
e([H|R],I,O):-J#=I+1,e(R,J,P),l(H,Q),Q ins I,append(P,Q,O).
r(R):-m(c,R),nl.
c(E):-var(E)->put(@);put(E).
g(L,H,S):-l(L,A),m(l(L),A),m(y(A,\),S),e(H,1,G),!,m(a(A),G),!,m(r,A).

Poiché il codice viene modificato per ridurre al minimo il numero di byte, non accetterà più un elenco di scatti con duplicati.


La versione con ottimizzazione di base, completamente giocata a golf ( 536 → 506 byte)

:-[library(clpfd)].
m(G,L):-maplist(G,L).
l(L,A):-length(A,L).
x(A,I,E):-X=..[a|A],arg(I,X,E).
y(A,E,(X,Y)):-x(A,X,R),x(R,Y,E).
a(A,S):-l(L,A),X#>0,X#=<L,Y#>0,Y#=<L,(B=A;transpose(A,T),B=T),h(S,(X,Y),B).
h(0,_,_).
h(L,(X,Y),A):-y(A,E,(X,Y)),var(E),E=s,M#=L-1,Z#=Y+1,h(M,(X,Z),A).
e([],_,[]).
e([H|R],I,O):-J#=I+1,e(R,J,P),l(H,Q),Q ins I,append(P,Q,O).
r(R):-m(c,R),nl.
c(E):-var(E)->put(@);put(E).
g(L,H,S):-l(L,A),m(l(L),A),sort(S,T),m(y(A,\),T),e(H,1,G),!,l(E,T),sumlist(G,D),L*L-E>=D,m(a(A),G),!,m(r,A).

Realizzo alcuni controlli di base ( conta il numero di blocchi necessari per la nave ) per rendere il codice più veloce per casi chiaramente impossibili. Il codice è anche immune ai duplicati nell'elenco di scatti finora.


Di seguito è la versione (in qualche modo) leggibile, con commenti dettagliati:

:-[library(clpfd)].

% Shorthand for maplist, which works like map in high order function
m(G,L):-maplist(G,L).

% Creating a square matrix A which is L x L
board(L,A):-l(L,A),m(l(L),A).

% Shorthand for length, with order of parameters reversed
l(L,A):-length(A,L).

% Unification A[I] = E
x(A,I,E):-X=..[a|A],arg(I,X,E).

% Unification A[X][Y]=E
idx2(A,E,(X,Y)):-x(A,X,R),x(R,Y,E).

% Mark positions that have been shot
markshot(A,S):-m(idx2(A,\),S).

% Place all ships on the board
placeships(H,A):-m(placeship(A),H).

% Place a length S ship horizontal/vertical forward on the board
placeship(A,S):-
    l(L,A), % Get length
    X#>0,X#=<L,Y#>0,Y#=<L, % Constraint X and Y coordinates
    transpose(A,T), % Transpose to work on columns
    (placeship_h(S,(X,Y),A) ; placeship_h(S,(Y,X),T)).

% Place ship horizontal, forward at (X,Y)
placeship_h(0,_,_).
placeship_h(L,(X,Y),A):-
    idx2(A,E,(X,Y)),var(E),E=s, % Make sure position is unassigned, then mark
    L2#=L-1,Y2#=Y+1, % Do this for all blocks of the ship
    placeship_h(L2,(X,Y2),A).

% Expand the list of ships
% e.g. [2,3,1] --> [3,2,2,2,1,1]
shipexpand(S,O):-shipexpand(S,1,O).

shipexpand([],_,[]).
shipexpand([H|R],I,O):-
    I2#=I+1,shipexpand(R,I2,O2), % process the rest
    l(H,O1),O1 ins I, % duplicate current ship size H times
    append(O2,O1,O). % larger ship size goes in front

% Print the result
pboard(A):-m(prow,A).
prow(R):-m(pcell,R),print('\n').
pcell(E):-var(E)->print(@);print(E).

game(L,H,S):-
    board(L,A), % create board
    sort(S,SS), % remove duplicates
    markshot(A,SS), % mark positions that have been shot
    shipexpand(H,HH),!, % make a list of ships
    l(SC,SS),sumlist(HH,BC),L*L-SC>=BC, % check to speed-up, can be removed
    placeships(HH,A),!, % place ships
    pboard(A). % print result

Formato della query:

game(Board_Size, Ships_List, Shots_List).

Query di esempio (utilizzando l'esempio nella domanda):

?- game(4,[1,1,1],[(2,1),(3,2),(3,3),(4,1),(4,3),(4,4)]).
ssss
\ss@
@\\@
\@\\
true.

?- game(4,[2,2,0,1],[(2,1),(3,2),(3,3),(4,1),(4,3),(4,4)]).
ssss
\sss
s\\s
\s\\
true.

Ah dannatamente mi ha battuto con poche decine di personaggi! Non sono sicuro di poter comprimere più il mio, ma continuerò a provare ... buon uso di Prolog!
Claudiu,

@Claudiu: la mia soluzione ha ancora un "buffer" di circa 20 caratteri. Ho lasciato quei codici di controllo lì dentro per motivi di prestazioni, ma possono essere rimossi senza influire sulla correttezza;) Non mi preoccupo se altre risposte scendono sotto 500, comunque.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

5

Matlab - 536 caratteri

Aggiornato: formattazione dell'output molto più piccola, alcuni spazi bianchi sono stati rimossi.

Aggiornato: aggiunta la versione golf

Aggiornato: aggiunta la versione commentata, la versione ridotta del golf di ~ 100 caratteri

% Battleship puzzle solver.
%
% n: size of the map (ex. 4 -> 4x4)
% sh: list of shots (ex. ['A2';'C4'])
% sp: ships of each size (ex. [2,0,1] -> 2x1 and 1x3)
%
function bs(n,sh,sp)

  % sp holds a vector of ship counts, where the index of each element is
  % the size of the ship. s will hold a vector of ship sizes, with order
  % not mattering. This is easier to work with using recursion, because
  % we can remove elements with Matlab's array subselection syntax, rather
  % than decrement elements and check if they're zero.
  %
  % Tricks:
  %   Since sp never contains a -1, find(1+sp) is the same as 1:length(sp)
  %   but is three characters shorter.
  %
  s=[];
  for i=find(1+sp)
    s(end+1:end+sp(i))=i;
  end


  % m will hold the map. It will be +2 in each direction, so that later we
  % can find neighbors of edge-spaces without checking bounds. For now,
  % each element is either '0' or '1' for a space or missed shot,
  % respectively. We convert the shots as provided by the user (ex. 'A2')
  % to marks on the map.
  %
  % Tricks:
  %   It takes one shorter character to subtract 47 than 'A' to determine
  %   the indices into the map.
  %
  m=zeros(n+2);
  for h=sh'
    m(h(2)-47,h(1)-63)=1;
  end


  % Solve the puzzle. q will either be the empty array or a solution map.
  %
  q=pp(m,s);


  % If a solution was found, output it, showing the original shots and the
  % ship placement. If no solution was found, print a sad face.
  %
  % Tricks:
  %   q is 0 on failure, which is treated like 'false'. q is a matrix on
  %   success which is NOT treated like 'true', so we have to check for
  %   failure first, then handle success in the 'else' block.
  %
  %   q contains the "fake shots" that surround each placed ship (see the
  %   pl function). We don't want to output those, so we just copy the ship
  %   markings into the original map.
  %
  if ~q ':('
  else
  m(find(q==2))=2;
  num2str(m(2:n+1,2:n+1),'%d')
  end



% Depth-first search for a solution.
%
% m: map (see main code above)
% s: vector of ship sizes to place in the map
%
% Returns q: square matrix of integers, updated with all requested ship
% sizes, or 0 if no solution is possible.
%
function q = pp(m,s)

  % If we have no ships to process, we're all done recursing and the
  % current map is a valid solution. Rather than handle this in an 'else'
  % block, we can assume it's the case and overwrite q if not, saving 4
  % characters.
  %
  q=m;


  % If we have any ships to place...
  %
  % Tricks:
  %   Since we are only interested in positive values in s, we can use
  %   sum(s) in place of isempty(s), saving 4 characters.
  %
  if sum(s)

    % Try to place the first ship in the list into the map, both vertically
    % (first call to p) and vertically (second call to p). We can process
    % any ship in the list, but the first can be removed from the list
    % with the fewest characters. r will hold a cell-array of options for
    % this ship.
    %
    r=[p(m,s(1),0),p(m',s(1),1)];


    % Recurse for each option until we find a solution.
    %
    % Tricks:
    %   Start with q, our return variable, set to 0 (indicating no solution
    %   was found. On each loop we'll only bother to recurse if q is still
    %   0. This relieves the need for if/else to check whether to continue
    %   the loop, or any final q=0 if the loop exits without success.
    %
    %   Sadly, there's no way around the length(r) call. Matlab doesn't
    %   provide syntax for iterating over cell-arrays.
    %
    q=0;
    for i=1:length(r)
      if ~q q=pp(r{i},s(2:end));end
    end
  end



% Place a single ship into a map.
%
% m: map (see main code above)
% s: ship size to place
% t: if the map has been transposed prior to this call
%
% Returns r: cell-array of possible maps including this ship
%
function r=p(m,s,t)
  % Start with an empty cell-array and pre-compute the size of the map,
  % which we'll need to use a few times.
  %
  r={};
  z=size(m);


  % For each row (omitting the first and last 'buffer' rows)...
  %
  for i=2:z(2)-1

  % For each starting column in this row where enough consecutive 0s
  % appear to fit this ship...
  %
  for j=strfind(m(i,2:end-1),(1:s)*0)

    % Copy the map so we can modify it without overwriting the variable
    % for the next loop.
    %
    n=m;


    % For each location on the map that is part of this optional
    % placement...
    for l=0:s-1
      % Let's leave this is an exercise for the reader ;)
      %
      v=-1:1;
      n([(l+j)*z(1)+i,z(1),1]*[ones(1,9);kron(v,[1 1 1]);[v v v]])=1;
    end


    % Mark each location that is part of this optional placement with
    % a '2'.
    %
    n(i,1+j:j+s)=2;


    % Since our results are going into a cell-array it won't be
    % convenient for the caller to undo any transpositions they might
    % have done. If the t flag is set, transpose this map back before
    % adding it to the cell-array.
    %
    if t n=n';end
    r{end+1}=n;
  end
  end

Ecco la versione golf.

function bs(n,sh,sp)
s=[];for i=find(1+sp)
s(end+1:end+sp(i))=i;end
m=zeros(n+2);for h=sh'
m(h(2)-47,h(1)-63)=1;end
q=pp(m,s);if ~q ':('
else
m(find(q==2))=2;num2str(m(2:n+1,2:n+1),'%d')
end
function q = pp(m,s)
q=m;if sum(s)
r=[p(m,s(1),0),p(m',s(1),1)];q=0;for i=1:length(r)if ~q q=pp(r{i},s(2:end));end
end
end
function r=p(m,s,t)
r={};z=size(m);for i=2:z(2)-1
for j=strfind(m(i,2:end-1),(1:s)*0)n=m;for l=0:s-1
v=-1:1;n([(l+j)*z(1)+i,z(1),1]*[ones(1,9);kron(v,[1 1 1]);[v v v]])=1;end
n(i,1+j:j+s)=2;if t n=n';end
r{end+1}=n;end
end

Ecco alcuni esempi.

>>bs(4,['A1';'B3'],[2,1])
1220
0000
2120
0000

>>bs(4,['A1';'B4'],[2,2])
1022
2000
0022
2100

>> bs(4,['A1';'B4';'C2'],[3,1])
1022
2010
0020
2100

>> bs(4,['A1';'B4';'C2'],[3,2])
:(

La grande linea con 'kron' (vicino al fondo del codice non golfato) è la mia parte preferita di questo. Scrive un "1" in tutte le posizioni nella mappa vicino a una determinata posizione. Riesci a vedere come funziona? Utilizza il prodotto tensore di kronecker, la moltiplicazione di matrici e indicizza la mappa come matrice lineare ...


4

Python, 464 caratteri

B,L,M=input()
R=range(B)
C=B+1
M=sum(1<<int(m[1:])*C-C+ord(m[0])-65for m in M)
def P(L,H,S):
 if len(L)==0:
  for y in R:print''.join('.#!'[(S>>y*C+x&1)+2*(M>>y*C+x&1)]for x in R)
  return 1
 for s in[2**L[0]-1,sum(1<<C*j for j in range(L[0]))]:
  for i in range(C*C):
   if H&s==s and P(L[1:],H&~(s|s<<1|s>>1|s<<B|s>>B|s<<C|s>>C|s<<C+1|s>>C+1),S|s):return 1
   s*=2
 return 0
P(sum(([l+1]*k for l,k in enumerate(L)),[])[::-1],sum((2**B-1)<<B*j+j for j in R)&~M,0)

Input (stdin):

7, [4,1,2,0,1], ['A1','B2','C3']

Produzione:

!#####.
.!.....
##!###.
.......
###.#.#
.......
#.#....

Funziona con numeri interi che contengono bitmap di varie funzionalità:

M = bitmap of misses
H = bitmap of holes where ships can still go
S = bitmap of ships already placed
L = list of ship sizes not yet placed
B = dimension of board
C = bitmap row length

Se non ti dispiace, fai qualche ottimizzazione o è solo una forza bruta?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

C'è un'ottimizzazione: quella [::-1]che lo fa provare prima la nave più lunga. Ritorna indietro non appena trova una nave che non può posizionare.
Keith Randall,

È possibile utilizzare una singola scheda anziché 2 o 3 spazi sulle righe 7, 8, 11 e 12, riducendo il numero di byte a 458. Vedere qui .
mbomb007,

3

Python, 562 caratteri, -8 con output brutto, +4? per invocazione bash

I=int;R=range
import sys;a=sys.argv
S=I(a[1]);f=[[0]*(S+2)for _ in R(S+2)]
C=a[2].split()
X=[]
for i,n in enumerate(C):X=[i+1]*I(n)+X
Q=a[3].split()
for q in Q:f[I(q[1:])][ord(q[0])-64]=1
D=R(-1,2)
V=lambda r,c:(all(f[r+Q][c+W]<2for Q in D for W in D),0,0)[f[r][c]]
def F(X):
 if not X:print"\n".join(" ".join(" .@"[c]for c in r[1:-1])for r in f[1:-1])
 x=X[0];A=R(1,S+1)
 for r in A:
    for c in A:
     for h in(0,1):
        def P(m):
         for i in R(x):f[(r+i,r)[h]][(c,c+i)[h]]=m
        if(r+x,c+x)[h]<S+2and all(V((r+i,r)[h],(c,c+i)[h])for i in R(x)):P(2);F(X[1:]);P(0)
F(X)

Nota: i livelli di rientro sono spazio, scheda, scheda + spazio, scheda + scheda e scheda + scheda + spazio. Ciò consente di risparmiare alcuni caratteri utilizzando solo spazi.

Uso ed esempio :

Accetta input dagli argomenti della riga di comando. Emette uno spazio vuoto come spazio, uno scatto come a. e @come parte di una nave:

$ python bships_golf.py "7" "4 0 2 0 1" \
         "A1 C3 B5 E4 G6 G7 A3 A4 A5 A6 C1 C3 C5 C7 B6 B7 D1 D2 G3" 2>X
. @ . . @ @ @
  @   .
. @ . @   @ .
.     @ .
. . . @   @
. .   @     .
@ . . @   @ .

Se irrisolvibile, non stampa nulla:

$ python bships_golf.py "3" "2" "A1 A3 B1 C1 C3" 2>X
. . .
@   @
.   .
$ python bships_golf.py "3" "2" "A1 A2 A3 B1 C1 C3" 2>X
$

Il 2>Xè di sopprimere un messaggio di errore poiché il programma viene chiuso da un'eccezione. Sentiti libero di aggiungere una penalità di +4 se ritenuto giusto. Altrimenti dovrei fare untry: ... except:0 per sopprimerlo, che richiederebbe comunque più personaggi.

Posso anche stampare l'output come numeri ( 0, 1, e2 ) per radersi 8 caratteri, ma l'estetica valore i.

Spiegazione :

Rappresento la scheda come un elenco di elenchi di numeri interi con dimensione 2 maggiore dell'input, per evitare di dover effettuare il controllo dei limiti. 0rappresenta uno spazio vuoto, 1uno sparo e 2una nave. Corro l'elenco degli scatti Qe segnare tutti gli scatti. Converto l'elenco delle navi in ​​un elenco esplicito Xdi navi, ad esempio [4, 0, 2, 0, 1]diventa [5, 3, 3, 1, 1, 1, 1]. Quindi è un semplice algoritmo di backtracking: in ordine di dimensione decrescente, prova a posizionare una nave, quindi prova a posizionare il resto delle navi. Se non funziona, prova lo slot successivo. Non appena ha esito positivo, l'elenco delle navi Xè nullo e l'accesso X[0]genera un'eccezione che chiude il programma. Il resto è solo golf pesante (inizialmente era 1615 caratteri).


2

Perl, 455 447 437 436 422 418

$n=($N=<>+0)+1;@S=map{(++$-)x$_}<>=~/\d+/g;$_=<>;$f=('~'x$N.$/)x$N;substr($f,$n*$1-$n-65+ord$&,1)=x while/\w(\d+)/g;sub f{for my$i(0..$N*$n-1){for(0..@_-2){my($f,@b)=@_;$m=($s=splice@b,$_,1)-1;pos=pos($u=$_=$f)=$i;for(s/\G(~.{$N}){$m}~/$&&("\0"."\377"x$N)x$s|(o."\0"x$N)x$m.o/se?$_:(),$u=~s/\G~{$s}/o x$s/se?$u:()){for$k(0,$N-1..$n){s/(?<=o.{$k})~|~(?=.{$k}o)/!/sg}return$:if$:=@b?f($_,@b):$_}}}}$_=f$f,@S;y/!/~/;print

rientrato:

$n=($N=<>+0)+1;
@S=map{(++$-)x$_}<>=~/\d+/g;
$_=<>;
$f=('~'x$N.$/)x$N;
substr($f,$n*$1-$n-65+ord$&,1)=x while/\w(\d+)/g;
sub f{
    for my$i(0..$N*$n-1){
        for(0..@_-2){
            my($f,@b)=@_;
            $m=($s=splice@b,$_,1)-1;
            pos=pos($u=$_=$f)=$i;
            for(s/\G(~.{$N}){$m}~/
              $&&("\0"."\377"x$N)x$s|(o."\0"x$N)x$m.o/se?$_:(),
              $u=~s/\G~{$s}/o x$s/se?$u:()){
                for$k(0,$N-1..$n){
                    s/(?<=o.{$k})~|~(?=.{$k}o)/!/sg
                }
                return$:if$:=@b?f($_,@b):$_
            }
        }
    }
}
$_=f$f,@S;
y/!/~/;
print

Mi aspetto che possa essere ulteriormente giocato a golf (ad es. Con eval<> input pre-formattato, poiché vedo che è OK (?)), E anche alcune altre cose (50$ sigilli? No, rimarranno).

La velocità, come ho detto prima, può essere un problema (da istantaneo (vedi esempio sotto) a molto molto lungo), a seconda di dove si trova la soluzione sull'albero di ricorsione, ma lascia che sia la questione dell'obsolescenza dell'hardware utilizzato. Farò la versione ottimizzata in seguito, con la ricorsione sparita e 2-3 altri trucchi ovvi.

Viene eseguito in questo modo, 3 linee vengono alimentate tramite STDIN:

$ perl bf.pl
7
4 1 2 0 1
A1 B2 C3
xo~o~o~
~x~~~~~
o~xo~o~
~~~o~o~
o~~~~o~
o~~~~~~
o~ooooo

~è l'oceano (soluzione artistica, non è vero), os e xs sono navi e scatti. Le prime 5 righe ottengono l'input e preparano il nostro "campo di battaglia". $Nè dimensione, @Sè schiera di navi srotolata (ad esempio 1 1 1 1 2 3 3 5come sopra) e si avvia alla successiva iterazione. E così via.$f è una stringa che rappresenta il campo di battaglia (file con linee dirette concatenate). Il prossimo è il sottoprogramma ricorsivo, che prevede lo stato attuale del campo di battaglia e l'elenco delle navi rimanenti. Controlla da sinistra a destra, dall'alto verso il basso e cerca di posizionare ogni nave in ogni posizione, sia in orizzontale che in verticale (vedi? Maturo per ottimizzare, ma sarà dopo). La nave orizzontale è ovvia sostituzione regexp, verticale un po 'più complicata - manipolazione di stringhe bit a bit da sostituire in "colonna". In caso di successo (H, V o entrambi), le nuove posizioni inaccessibili sono contrassegnate con!

Modifica: OK, ecco la versione 594 byte (quando non rientrata) che in realtà cerca di essere utile (cioè veloce) - ottimizzata al meglio delle mie capacità pur implementando le stesse tecniche - ricorsione (anche se eseguita 'manualmente') e regexps. Mantiene uno "stack" -@A - matrice di stati che vale la pena indagare. Uno "stato" è composto da 4 variabili: attuale stringa del campo di battaglia, indice da cui iniziare a cercare di posizionare le navi, riferimento alla matrice delle navi rimanenti e indice della nave successiva da provare. Inizialmente c'è un singolo 'stato' - inizio della stringa vuota, tutte le navi. In corrispondenza (H o V, vedi sopra), lo stato corrente viene spinto per indagare in seguito, lo stato aggiornato (con una nave posizionata e le posizioni inaccessibili contrassegnate) viene spinto e il blocco viene riavviato. Quando la fine della stringa del campo di battaglia viene raggiunta senza successo, @Aviene esaminato il successivo stato disponibile da (se presente).

Altre ottimizzazioni sono: non ricominciare dall'inizio della stringa, provare a posizionare prima le navi di grandi dimensioni, non controllare le navi della stessa dimensione se una precedente non è riuscita nella stessa posizione, + forse qualcos'altro (come nessuna $&variabile ecc.).

$N=<>+0;
$S=[reverse map{(++$-)x$_}<>=~/\d+/g];
$_=<>;
$f=('~'x$N.$/)x$N;
substr($f,$N*$2-$N+$2-66+ord$1,1)=x while/(\w)(\d+)/g;
push@A,$f,0,$S,0;
O:{
    ($f,$i,$S,$m)=splice@A,-4;
    last if!@$S;
    F:{ 
        for$j($m..$#$S){
            next if$j and$$S[$j]==$$S[$j-1];
            $s=$$S[$j];
            my@m;
            $_=$f;
            $n=$s-1;
            pos=$i;
            push@m,$_ if s/\G(?:~.{$N}){$n}~/
                ${^MATCH}&("\0"."\377"x$N)x$s|(o."\0"x$N)x$n.o/pse;
            $_=$f;
            pos=$i;
            push@m,$_ if s/\G~{$s}/o x$s/se;
            if(@m){
                push@A,$f,$i,$S,$j+1;
                my@b=@$S;
                splice@b,$j,1;
                for(@m){
                    for$k(0,$N-1..$N+1){
                        s/(?<=o.{$k})~|~(?=.{$k}o)/!/gs
                    }
                    push@A,$_,$i+1,\@b,0
                }
                redo O
            }
        }
        redo if++$i-length$f
    }
    redo if@A
}
print@$S?'':$f=~y/!/~/r

.

perl bf+.pl
10
5 4 3 2 1
A1 B2 C3 A10 B9 C10 J1 I2 H3 I9 J10 A5 C5 E5 F6 G7
xooooo~oox
~x~~~~~~x~
ooxooooxo~
~~~~~~~~o~
xoxoxoo~o~
~o~o~x~~o~
~o~o~ox~~~
~~~~~o~ooo
oxo~~~~~x~
x~x~o~o~ox

OTOH, ci vuole ancora un'eternità per un caso impossibile 6 5 4 3 2 1nell'esempio sopra. Forse la versione pratica dovrebbe uscire immediatamente se la stazza totale della nave supera la capacità del campo di battaglia.


2

Soluzione C #

  public static class ShipSolution {
    private static int[][] cloneField(int[][] field) {

      int[][] place = new int[field.Length][];

      for (int i = 0; i < field.Length; ++i) {
        place[i] = new int[field.Length];

        for (int j = 0; j < field.Length; ++j)
          place[i][j] = field[i][j];
      }

      return place;

    }

    private static void copyField(int[][] source, int[][] target) {
      for (int i = 0; i < source.Length; ++i)
        for (int j = 0; j < source.Length; ++j)
          target[i][j] = source[i][j];
    }

    // Check if placement a legal one
    private static Boolean isPlacement(int[][] field, int x, int y, int length, Boolean isHorizontal) {
      if (x < 0)
        return false;
      else if (y < 0)
        return false;
      else if (x >= field.Length)
        return false;
      else if (y >= field.Length)
        return false;

      if (isHorizontal) {
        if ((x + length - 1) >= field.Length)
          return false;

        for (int i = 0; i < length; ++i)
          if (field[x + i][y] != 0)
            return false;
      }
      else {
        if ((y + length - 1) >= field.Length)
          return false;

        for (int i = 0; i < length; ++i)
          if (field[x][y + i] != 0)
            return false;
      }

      return true;
    }

    //  When ship (legally) placed it should be marked at the field
    //  2 - ship itself
    //  3 - blocked area where no other ship could be placed
    private static void markPlacement(int[][] field, int x, int y, int length, Boolean isHorizontal) {
      if (isHorizontal) {
        for (int i = 0; i < length; ++i)
          field[x + i][y] = 2;

        for (int i = x - 1; i < x + length + 1; ++i) {
          if ((i < 0) || (i >= field.Length))
            continue;

          for (int j = y - 1; j <= y + 1; ++j)
            if ((j >= 0) && (j < field.Length))
              if (field[i][j] == 0)
                field[i][j] = 3;
        }
      }
      else {
        for (int i = 0; i < length; ++i)
          field[x][y + i] = 2;

        for (int i = x - 1; i <= x + 1; ++i) {
          if ((i < 0) || (i >= field.Length))
            continue;

          for (int j = y - 1; j < y + length + 1; ++j)
            if ((j >= 0) && (j < field.Length))
              if (field[i][j] == 0)
                field[i][j] = 3;
        }
      }
    }

    // Ship placement itteration
    private static Boolean placeShips(int[][] field, int[] ships) {
      int[] vessels = new int[ships.Length];

      for (int i = 0; i < ships.Length; ++i)
        vessels[i] = ships[i];

      for (int i = ships.Length - 1; i >= 0; --i) {
        if (ships[i] <= 0)
          continue;

        int length = i + 1;

        vessels[i] = vessels[i] - 1;

        // Possible placements
        for (int x = 0; x < field.Length; ++x)
          for (int y = 0; y < field.Length; ++y) {
            if (isPlacement(field, x, y, length, true)) {
              int[][] newField = cloneField(field);

              // Mark
              markPlacement(newField, x, y, length, true);

              if (placeShips(newField, vessels)) {
                copyField(newField, field);

                return true;
              }
            }

            if (length > 1)
              if (isPlacement(field, x, y, length, false)) {
                int[][] newField = cloneField(field);

                // Mark
                markPlacement(newField, x, y, length, false);

                if (placeShips(newField, vessels)) {
                  copyField(newField, field);

                  return true;
                }
              }
          }

        return false; // <- the ship can't be placed legally
      }

      return true; //    <- there're no more ships to be placed
    }

    /// <summary>
    /// Solve ship puzzle
    /// </summary>
    /// <param name="size">size of the board</param>
    /// <param name="ships">ships to be placed</param>
    /// <param name="misses">misses in (line, column) format; both line and column are zero-based</param>
    /// <returns>Empty string if there is no solution; otherwise possible ship placement where
    ///   . - Blank place
    ///   * - "miss"
    ///   X - Ship
    /// </returns>
    public static String Solve(int size, int[] ships, String[] misses) {
      int[][] field = new int[size][];

      for (int i = 0; i < size; ++i)
        field[i] = new int[size];

      if (!Object.ReferenceEquals(null, misses))
        foreach (String item in misses) {
          String miss = item.Trim().ToUpperInvariant();

          int x = int.Parse(miss.Substring(1)) - 1;
          int y = miss[0] - 'A';

          field[x][y] = 1;
        }

      if (!placeShips(field, ships))
        return "";

      StringBuilder Sb = new StringBuilder();

      foreach (int[] line in field) {
        if (Sb.Length > 0)
          Sb.AppendLine();

        foreach (int item in line) {
          if (item == 1)
            Sb.Append('*');
          else if (item == 2)
            Sb.Append('X');
          else
            Sb.Append('.');
        }
      }

      return Sb.ToString();
    }
  }

  ...

  int size = 4;
  int[] ships = new int[] { 1, 1, 1 };
  String[] misses = new String[] {"C1", "C2", "B2", "A3", "A1", "B3", "D1"};

  // *X**
  // .**X
  // **.X
  // XX.X
  Console.Out.Write(ShipSolution.Solve(size, ships, misses));

Sebbene questa sia una soluzione eccellente e veloce, non sembra gestire correttamente le attività irrisolvibili. Ad esempio size=1 ships={1} moves={"A1"},.
mniip,

Mi dispiace, ho perso la condizione in cui la prossima nave non può essere posizionata legalmente. Ho modificato la soluzione.
Dmitry Bychenko il

6
Poiché la domanda è un code-golf , prova a mantenere il conteggio dei personaggi il più basso possibile (rimuovendo, ad esempio, gli spazi bianchi) e includi il conteggio dei caratteri nel tuo codice.
Programma FOX

Il conteggio dei personaggi così com'è ora è 5399.
intx13

1

Java, 1178

Sì, troppo a lungo ._.

import java.util.*;class S{public static void main(String[]a){new S();}int a;int[][]f;List<L>l;Stack<Integer>b;S(){Scanner s=new Scanner(System.in);a=s.nextInt();f=new int[a][a];for(int[]x:f)Arrays.fill(x,95);s.next(";");b=new Stack();for(int i=1;s.hasNextInt();i++){b.addAll(Collections.nCopies(s.nextInt(),i));}s.next(";");while(s.findInLine("([A-Z])")!=null)f[s.match().group(1).charAt(0)-65][s.nextInt()-1]=79;l=new ArrayList();for(int i=0;i<a;i++){l.add(new L(i){int g(int c){return g(c,i);}void s(int c,int v){f[c][i]=v;}});l.add(new L(i){int g(int r){return g(i,r);}void s(int r,int v){f[i][r]=v;}});}if(s()){String o="";for(int r=0;r<a;r++){for(int c=0;c<a;c++)o+=(char)f[c][r];o+='\n';}System.out.print(o);}}boolean s(){if(b.empty())return true;int s=b.pop();Integer n=95;for(L c:l){int f=0;for(int x=0;x<a;x++){if(n.equals(c.g(x)))f++;else f=0;if(f>=s){for(int i=0;i<s;i++)c.s(x-i,35);if(s())return true;for(int i=0;i<s;i++)c.s(x-i,n);}}}b.push(s);return false;}class L{int i;L(int v){i=v;}void s(int i,int v){}int g(int i){return 0;}int g(int c,int r){int v=0;for(int x=-1;x<2;x++)for(int y=-1;y<2;y++)try{v|=f[c+x][r+y];}catch(Exception e){}return v&(f[c][r]|32);}}}

Ungolfed:

import java.util.*;

class S {
    public static void main(String[] a) {
        new S();
    }

    /**
     * Number of rows/columns
     */
    int a;

    /**
     * A reference to the full field
     */
    int[][] f;

    /**
     * List of Ls representing all columns/rows
     */
    List<L> l;

    /**
     * Stack of all unplaced ships, represented by their length as Integer
     */
    Stack<Integer> b;

    S() {
        // Read input from STDIN:
        Scanner s = new Scanner(System.in);
        a = s.nextInt();
        f = new int[a][a];
        // initialize field to all '_'
        for(int[] x: f)
            Arrays.fill(x, 95);
        // ; as separator
        s.next(";");
        b = new Stack();
        // Several int to represent ships
        for (int i = 1; s.hasNextInt(); i++) {
            // nCopies for easier handling (empty Stack => everything placed)
            b.addAll(Collections.nCopies(s.nextInt(), i));
        }
        // ; as separator
        s.next(";");
        // find an uppercase letter on this line
        while (s.findInLine("([A-Z])") != null) {
            // s.match.group(1) contains the matched letter
            // s.nextInt() to get the number following the letter
            f[s.match().group(1).charAt(0) - 65][s.nextInt() - 1] = 79;
        }
        // Loop is left when line ends or no uppercase letter is following the current position

        // Now create a List of Lists representing single columns and rows of our field
        l = new ArrayList();
        for (int i = 0; i < a; i++) {
            l.add(new L(i) {
                int g(int c) {
                    return g(c, i);
                }

                void s(int c, int v) {
                    f[c][i] = v;
                }
            });
            l.add(new L(i) {
                int g(int r) {
                    return g(i, r);
                }

                void s(int r, int v) {
                    f[i][r] = v;
                }
            });
        }
        // try to place all ships
        if (s()) {
            // print solution
            String o = "";
            for (int r = 0; r < a; r++) {
                for (int c = 0; c < a; c++) {
                    o += (char) f[c][r];
                }
                o += '\n';
            }
            System.out.print(o);
        }
    }

    /**
     * Try to place all ships
     *
     * @return {@code true}, if a solution is found
     */
    boolean s() {
        if (b.empty()) {
            // no more ships
            return true;
        }
        // take a ship from the stack
        int s = b.pop();
        // 95 is the Ascii-code of _
        Integer n = 95;
        // go over all columns and rows
        for (L c : l) {
            // f counts the number of consecutive free fields
            int f = 0;
            // move through this column/row
            for (int x = 0; x < a; x++) {
                // Is current field free?
                if (n.equals(c.g(x))) {
                    f++;
                } else {
                    f = 0;
                }
                // Did we encounter enough free fields to place our ship?
                if (f >= s) {
                    // enter ship
                    for (int i = 0; i < s; i++) {
                        c.s(x - i, 35);
                    }
                    // try to place remaining ships
                    if (s()) {
                        return true;
                    }
                    // placing remaining ships has failed ; remove ship
                    for (int i = 0; i < s; i++) {
                        c.s(x - i, n);
                    }
                }
            }
        }
        // we have found no place for our ship so lets push it back
        b.push(s);
        return false;
    }

    /**
     * List representing a single row or column of the field.
     * "Abstract"
     */
    class L {
        /**
         * Index of row/column. Stored here as loop-variables can not be final. Used only {@link #g(int)} and {@link #s(int, int)}
         */
        int i;

        L(int v) {
            i = v;
        }

        /**
         * Set char representing the state at the i-th position in this row/column.
         * "Abstract"
         */
        void s(int i, int v) {
        }

        /**
         * Get char representing the state at the i-th position in this row/column.
         * "Abstract"
         *
         * @return {@code '_'} if this and all adjacent field contain no {@code '#'}
         */
        int g(int i) {
            return 0;
        }

        /**
         * Get char representing the state at the position in c-th column and r-th row
         *
         * @return {@code '_'} if this and all adjacent field contain no {@code '#'}
         */
        int g(int c, int r) {
            // v stores the result
            int v = 0;
            // or all adjacent fields
            for (int x = -1; x < 2; x++) {
                for (int y = -1; y < 2; y++) {
                    // ungolfed we should use something like
                    // v |= 0 > c + x || 0 > r + y || c + x >= a || r + y >= a ? 0 : f[c + x][r + y];
                    // but his will do (and is shorter)
                    try {
                        v |= f[c + x][r + y];
                    } catch (Exception e) {
                    }
                }
            }
            // we use '_' (95), 'O' (79), '#' (35). Only 35 contains the 32-bit
            // So we only need the 32-bit of the or-ed-value + the bits of the value directly in this field
            return v & (f[c][r] | 32);
        }

    }
}

Campione-Input

6 ; 3 2 1 ; A1 A3 B2 C3 D4 E5 F6 B6 E3 D3 F4

Campione-Output

O###_#
_O____
O_OOO#
#_#O_O
#_#_O#
_O___O

Con

  • O colpo mancato
  • # nave-parte
  • _ sparare qui dopo ;-)

Vedi su ideone.com

La gestione dell'input prevede gli spazi attorno ai numeri / ;e non funzionerà diversamente.

Sto posizionando prima le grandi navi perché hanno meno posti dove andare. Se si desidera passare alle piccole prima è possibile sostituire pop()da remove(0)e push(s)per add(0,s)sostituendo anche solo uno dei due dovrebbe essere ancora tradursi in un programma valido.

Se si consente alle navi di toccarsi a vicenda, la g(int,int)funzione può essere notevolmente semplificata o rimossa.

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.