Büyü: Toplama Savaş Golfü


30

Büyü: The Gathering , diğerlerinin yanı sıra, oyuncuların yaratıkları temsil eden kartları oynadığı, daha sonra diğer oyuncuya saldırabileceği veya diğer oyuncunun saldırılarına karşı bloke ederek savunabilecekleri bir takas oyunudur.

Bu kod-golf mücadelesinde, programınız savaşta nasıl engelleyeceğinize karar veren bir Sihirli oyuncu yerine olacaktır.


Her yaratığın iki alakalı özelliği vardır: Güç ve tokluk. Bir yaratığın gücü, bir savaşta verebileceği hasar miktarıdır ve sertliği, onu yok etmek için gerekli olan zarar miktarıdır. Güç her zaman en az 0'dır ve tokluk her zaman en az 1'dir.

Magic'teki dövüşler sırasında, sırasını değiştiren oyuncu bazı canlılarının rakibe saldırdığını açıklar. Daha sonra, savunma oyuncusu olarak bilinen diğer oyuncu, yaratıklarını engelleyici olarak atayabilir. Bir yaratık savaş başına yalnızca bir yaratığı engelleyebilir, ancak birden fazla yaratığın tümü aynı yaratığı engelleyebilir.

Engelleyiciler ilan edildikten sonra, saldıran oyuncu, engellenen her yaratık için, yaratığın onu engelleyen yaratıklara vereceği hasarı (gücüne eşit) nasıl dağıtacağına karar verir.

Ardından, hasar verilir. Her yaratık gücüne eşit hasar verir. Bloke edilen saldırgan yaratıklar, yukarıda açıklandığı gibi hasar verir. Engellenmemiş saldıran yaratıklar savunma oyuncularına hasar verir. Engelleyici yaratıklar engelledikleri yaratığa zarar verir. Engelleme yapmayan oyuncuya ait yaratıklar herhangi bir hasar vermediler. (Yaratıkların engellenmesi gerekmez.)

Son olarak, herhangi bir yaratık, tokluğuna eşit veya ondan daha büyük hasar vermiş, imha edilmiş ve savaş alanından uzaklaştırılmıştır. Bir yaratığın tokluğundan daha az hasarın hiçbir etkisi yoktur.


İşte bu sürecin bir örneği:

P gücü ve tokluğu olan bir yaratık T olarak temsil edilir. 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.

Savunan oyuncunun savaşta 3 hedefi var: Rakibin yaratıklarını yok et, kendi yaratıklarını koru ve mümkün olduğunca az hasar al. Ek olarak, daha fazla güç ve tokluğa sahip canlılar daha değerlidir.

Bunları tek bir ölçüde birleştirmek için savunan oyuncunun bir dövüşten aldığı puanın, hayatta kalan canlılarının güçlerinin ve tokluklarının, eksi rakibinin hayatta kalan yaratıklarının güçlerinin ve tokluklarının toplamına eşit olduğunu söyleyeceğiz. Hasar miktarının yarısı savunan oyuncuya verilir. Savunan oyuncu bu puanı en üst seviyeye çıkarmak isterken, hücum oyuncusu en aza indirmek ister.

Yukarıda gösterilen savaşta skor şuydu:

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

Eğer savunma oyuncusu yukarıda açıklanan savaşta hiç engellememiş olsaydı, skor

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

Savunan oyuncu için en uygun seçim engellemek için olurdu 2/2ile 1/1ve 1/4ve engellemek için 3/3birlikte 0/1. Öyle yapsaydılar, sadece 1/4ve 3/3hayatta kalırlardı ve savunan oyuncuya hasar verilmezdi, skoru yapardı.

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

Buradaki zorluk, savunan oyuncu için en uygun engelleme seçimini sağlayacak bir program yazmak. "Optimal", rakibinizin nasıl engellediğiniz göz önüne alındığında hasarı en aza indirecek şekilde dağıtması koşuluyla skoru en yükseğe çıkaran seçim anlamına gelir.

Bu bir maksimum: Her bloklama kombinasyonu için skoru en aza indiren hasar dağılımları üzerindeki maksimum puan.


Giriş: Giriş , her 2-grubun formda olduğu iki 2-listeden oluşur (Güç, Dayanıklılık). İlk liste her saldıran yaratığın (rakibin yaratıkları) güçlerini ve dayanıklılıklarını içerecektir. İkinci liste, yaratıklarınızın her birinin güçlerini ve dayanıklılıklarını içerecektir.

Dokular ve listeler, aşağıdakiler gibi herhangi bir uygun formatta gösterilebilir:

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

Çıktı: Çıktı , formdaki (yaratığı engelleyen, engellenen yaratık) - yani, yaratıklarınızdan biri tarafından takip edilen bir yaratıktan oluşacaktır. Yaratıklar, giriş listelerindeki dizinleri tarafından belirtilecektir. Dizinler 0 veya 1 dizinlenmiş olabilir. Yine, uygun herhangi bir format. Herhangi bir sipariş iyi. Örneğin, yukarıdan verilen en uygun engelleme senaryosu, yukarıdaki girdiyi verir:

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

Örnekler:

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

Giriş ve çıkış STDIN, STDOUT, CLA, fonksiyon giriş / dönüş, vb . Olabilir . Standart boşluklar geçerlidir. Bu kod-golf: bayt cinsinden en kısa kod kazanır.


Spesifikasyonu netleştirmek ve ilk fikirleri vermek için bu pastebin Python'da referans bir çözüm sunar. best_blockFonksiyon bu zorluğa bir örnek çözümdür ve programı çalıştıran daha ayrıntılı girişi ve çıkışını sağlayacak.


18
Tepenin bu kralı yapmalısın.
PyRulez

1
@Arnauld hayır, aynı zamanda geçerli bir cevap.
isaacg

Yanıtlar:


6

JavaScript (ES7),  354  348 bayt

Girişi olarak alır ([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)`)

Çevrimiçi deneyin!

Daha az golf oynadı ve formatlandı

Bu kod, sadece maptakma adlar ve eval()okunabilirlik için sargısız sarılmış golf versiyonuyla aynıdır .

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

Nasıl?

Başlatma ve ana döngü

Yaptığımız ilk şey, tanımsız bir güce ve tokluğa sahip sahte bir saldırgan yaratık eklemek . Yöntem, yeni bir numara verir bu da dahil yaratıklar, saldıran.0ApushA

A = a.push([, 0])

Hiç bir yaratığı engellememek yerine bu aptal yaratığı engelleyeceğiz. Bu, koddaki bazı basitleştirmelere izin verir.

ADDN

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

SMoO

MO

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

Savunmamızı inşa etmek

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

Saldırıyı optimize etmek

Saldırganların kararları birbiriyle ilişkili değildir. Saldırgan taraf için küresel optimum, her saldırgan için yerel optimanın toplamıdır.

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

Defans puanının güncellenmesi

Saldırgandaki her bir yinelemeden sonra, defans puanını şu ile güncelleriz:

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

Sol kısım, eğer hayatta kaldıysa, saldırganın skorunu çıkarır. Saldırı hiç engellenmediyse sağ taraf, saldırganın sertliğinin yarısını çıkarır veya aksi takdirde en iyi saldırgan puanını ekler (bu, savunan taraf açısından en kötü olanıdır).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.