Magic: the Gathering Combat Golf


30

Magic: the Gathering è un gioco di carte collezionabili in cui, tra le altre cose, i giocatori giocano a carte che rappresentano creature, che possono quindi attaccare l'altro giocatore o difendersi dagli attacchi dell'altro giocatore bloccando.

In questa sfida di code-golf, il tuo programma sarà al posto di un giocatore di Magic che deciderà come bloccare in combattimento.


Ogni creatura ha due attributi rilevanti: Potenza e costituzione. Il potere di una creatura è la quantità di danno che può infliggere in un combattimento, e la sua costituzione è la quantità di danno necessaria per distruggerlo. La potenza è sempre almeno 0 e la costituzione è sempre almeno 1.

Durante il combattimento in Magic, il giocatore di turno dichiara che alcune delle sue creature stanno attaccando l'avversario. Quindi, l'altro giocatore, noto come il giocatore in difesa, può assegnare le proprie creature come bloccanti. Una creatura può bloccare solo una creatura per combattimento, ma più creature possono bloccare tutte la stessa creatura.

Dopo che i bloccanti sono stati dichiarati, il giocatore attaccante decide, per ogni creatura attaccante che è stata bloccata, come distribuire il danno (pari alla sua potenza) che quella creatura infligge alle creature che la bloccano.

Quindi, il danno viene inflitto. Ogni creatura infligge danno pari alla sua potenza. Le creature attaccanti che sono state bloccate infliggono danni come descritto sopra. Le creature attaccanti non bloccate infliggono danni al giocatore in difesa. Le creature bloccanti infliggono danni alla creatura che hanno bloccato. Le creature appartenenti al giocatore in difesa che non hanno bloccato non infliggono danni. (Le creature non devono bloccare.)

Infine, qualsiasi creatura a cui è stato inflitto un danno pari o superiore alla sua costituzione viene distrutta e rimossa dal campo di battaglia. Qualsiasi quantità di danno inferiore alla costituzione di una creatura non ha alcun effetto.


Ecco un esempio di questo processo:

Una creatura con potenza P e costituzione T è rappresentata come P/T

Attacking:
2/2, 3/3
Defending player's creatures:
1/4, 1/1, 0/1
Defending player declares blockers:
1/4 and 1/1 block 2/2, 0/1 does not block.
Attacking player distributes damage:
2/2 deals 1 damage to 1/4 and 1 damage to 1/1
Damage is dealt:
2/2 takes 2 damage, destroyed.
3/3 takes 0 damage.
1/1 takes 1 damage, destroyed.
1/4 takes 1 damage.
0/1 takes 0 damage.
Defending player is dealt 3 damage.

Il giocatore in difesa ha 3 goal in combattimento: distruggi le creature dell'avversario, conserva le proprie creature e subisce il minor danno possibile. Inoltre, le creature con più forza e costituzione sono più preziose.

Per combinarli in una singola misura, diremo che il punteggio del giocatore in difesa da un combattimento è uguale alla somma dei poteri e della costituzione delle creature sopravvissute, meno la somma dei poteri e della costituzione delle creature sopravvissute dell'avversario, meno una metà dell'ammontare del danno inflitto al giocatore in difesa. Il giocatore in difesa vuole massimizzare questo punteggio, mentre il giocatore in attacco vuole minimizzarlo.

Nel combattimento mostrato sopra, il punteggio era:

Defending player's surviving creatures:
1/4, 0/1
1 + 4 + 0 + 1 = 6
Attacking player's surviving creature:
3/3
3 + 3 = 6
Damage dealt to defending player:
3
6 - 6 - 3/2 = -1.5

Se il giocatore in difesa non si fosse bloccato affatto nel combattimento sopra descritto, il punteggio sarebbe stato

8 - 10 - (5/2) = -4.5

La scelta ottimale per il giocatore in difesa sarebbe stata quella di bloccare il 2/2con il 1/1e il 1/4, e di bloccare il 3/3con il 0/1. Se lo avessero fatto, solo 1/4e 3/3sarebbe sopravvissuto, e nessun danno sarebbe stato inflitto al giocatore in difesa, facendo il punteggio

5 - 6 - (0/2) = -1

La tua sfida è scrivere un programma che produrrà la scelta di blocco ottimale per il giocatore in difesa. "Ottimale" indica la scelta che massimizza il punteggio, dato che l'avversario distribuisce il danno nel modo che minimizza il punteggio, dato il modo in cui è stato bloccato.

Questa è una massima: il punteggio massimo rispetto alle distribuzioni di danno che minimizzano il punteggio per ogni combinazione di blocco.


Input: l'input sarà costituito da due elenchi di 2 tuple, in cui ogni 2 tuple ha la forma (Potenza, Forza). Il primo elenco conterrà i poteri e la costituzione di ogni creatura attaccante (le creature del tuo avversario). Il secondo elenco conterrà i poteri e la costituzione di ciascuna delle tue creature.

Tuple ed elenchi possono essere rappresentati in qualsiasi formato conveniente, come ad esempio:

[[2, 2], [3, 3]]
[[1, 4], [1, 1], [0, 1]]

Output: L'output consisterà in una serie di 2 tuple, nella forma (creatura bloccante, creatura bloccata), ovvero una delle tue creature seguita da una delle loro creature. Alle creature verrà fatto riferimento il loro indice negli elenchi di input. Gli indici possono essere 0 o 1 indicizzati. Ancora una volta, qualsiasi formato conveniente. Qualsiasi ordine va bene. Ad esempio, lo scenario di blocco ottimale dall'alto, dato l'input sopra, potrebbe essere rappresentato come:

[0, 0]    # 1/4 blocks 2/2
[1, 0]    # 1/1 blocks 2/2
[2, 1]    # 0/1 blocks 3/3

Esempi:

Input:
[[2, 2], [3, 3]]
[[1, 4], [1, 1], [0, 1]]
Output:
[0, 0]
[1, 0]
[2, 1]

Input:
[[3, 3], [3, 3]]
[[2, 3], [2, 2], [2, 2]]
Output:
[1, 0]
[2, 0]
or
[1, 1]
[2, 1]

Input:
[[3, 1], [7, 2]]
[[0, 4], [1, 1]]
Output:
[1, 0]
or
[0, 0]
[1, 0]

Input:
[[2, 2]]
[[1, 1]]
Output:

(No output tuples).

Ingresso e uscita possono avvenire tramite STDIN, STDOUT, CLA, ingresso / ritorno funzione, ecc . Si applicano scappatoie standard . Questo è code-golf: vince il codice più corto in byte.


Per chiarire le specifiche e fornire idee iniziali, questo pastebin fornisce una soluzione di riferimento in Python. La best_blockfunzione è una soluzione di esempio a questa sfida e l'esecuzione del programma fornirà input e output più dettagliati.


18
Dovresti fare questo re della collina.
PyRulez,

1
@Arnauld no, questa è anche una risposta valida.
isaacg

Risposte:


6

JavaScript (ES7),  354  348 byte

Accetta input come ([attackers], [defenders]).

(a,d,O,M)=>eval(`for(N=(A=a.push([,0]))**d.length;N--;)O=a[X='map'](([P,T],i)=>S-=((g=(n,l)=>n?l[X](([t,S],i)=>g(n-1,b=[...l],b[i]=[t-1,S])):m=l[X](([t,S])=>s+=t>0&&S,s=0)&&s>m?m:s)(P,l[n=0,i][X](m=([p,t])=>[t,p+t,n+=p])),n<T&&P+T)+(l[i]<1?T/2:-m),S=0,d[X]((x,i)=>l[(j=N/A**i%A|0)<A-1&&o.push([i,j]),j].push(x),o=[],l=a[X](_=>[])))&&S<M?O:(M=S,o)`)

Provalo online!

Meno golfizzato e formattato

Questo codice è identico alla versione golf, solo senza gli mapalias e il eval()wrapping per la leggibilità.

(a, d, O, M) => {
  for(N = (A = a.push([, 0])) ** d.length; N--;)
    O =
      a.map(([P, T], i) =>
        S -=
          (
            (g = (n, l) =>
              n ?
                l.map(([t, S], i) => g(n - 1, b = [...l], b[i] = [t - 1, S]))
              :
                m = l.map(([t, S]) => s += t > 0 && S, s = 0) && s > m ? m : s
            )(
              P,
              l[n = 0, i].map(m = ([p, t]) => [t, p + t, n += p])
            ),
            n < T && P + T
          ) + (
            l[i] < 1 ? T / 2 : -m
          ),
        S = 0,
        d.map((x, i) =>
          l[
            (j = N / A ** i % A | 0) < A - 1 && o.push([i, j]),
            j
          ].push(x),
          o = [],
          l = a.map(_ => [])
        )
      ) && S < M ? O : (M = S, o)
  return O
}

Come?

Inizializzazione e loop principale

0pushA

A = a.push([, 0])

Bloccheremo questa creatura fittizia invece di bloccare nessuna creatura. Ciò consente alcune semplificazioni nel codice.

ADDN

for(N = (A = a.push([, 0])) ** d.length; N--;)

SMoO

MO

O = (...) && S < M ? O : (M = S, o)

Costruire la nostra difesa

l

d.map((x, i) =>              // for each defender x at position i:
  l[                         //   update l[]:
    (j = N / A ** i % A | 0) //     j = index of the attacker that we're going to block
    < A - 1 &&               //     if this is not the 'dummy' creature:
    o.push([i, j]),          //       add the pair [i, j] to the current solution
    j                        //     use j as the actual index to update l[]
  ].push(x),                 //   push x in the list of blockers for this attacker
  o = [],                    //   initialize o to an empty list
  l = a.map(_ => [])         //   initialize l to an array containing as many empty lists
                             //   that there are attackers
)                            // end of map()

Ottimizzare l'attacco

Le decisioni degli attaccanti non sono correlate tra loro. L'ottimale globale per la parte attaccante è la somma dell'optima locale per ciascun attaccante.

PTi

a.map(([P, T], i) => ...)

l[i]

l[n = 0, i].map(m = ([p, t]) => [t, p + t, n += p])

n

gP

(g = (n, l) =>            // n = remaining damage points; l = list of blockers
  n ?                     // if we still have damage points:
    l.map(([t, S], i) =>  //   for each blocker of toughness t and score S at index i:
      g(                  //     do a recursive call:
        n - 1,            //       decrement the number of damage points
        b = [...l],       //       create a new instance b of l
        b[i] = [t - 1, S] //       decrement the toughness of blocker i
      )                   //     end of recursive call
    )                     //   end of map()
  :                       // else:
    m =                   //   update the best score m (the lower, the better):
      l.map(([t, S]) =>   //     for each blocker of toughness t and score S:
        s += t > 0 && S,  //       add S to s if this blocker has survived
        s = 0             //       start with s = 0
      ) &&                //     end of map()
      s > m ? m : s       //     set m = min(m, s)
)                         //

Aggiornamento del punteggio del difensore

Dopo ogni iterazione su un attaccante, aggiorniamo il punteggio del difensore con:

S -= (n < T && P + T) + (l[i] < 1 ? T / 2 : -m)

La parte sinistra sottrae il punteggio dell'attaccante se è sopravvissuto. La parte destra sottrae metà della costituzione dell'attaccante se l'attacco non è stato affatto bloccato, oppure aggiunge il miglior punteggio dell'attaccante (che è il peggiore dal punto di vista del lato difensore).

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.