매직 : 개더링 전투 골프


30

Magic : The Gathering 은 무엇보다도 플레이어가 생물을 나타내는 카드를 재생하여 다른 플레이어를 공격하거나 차단하여 다른 플레이어의 공격을 방어 할 수있는 거래 카드 게임입니다.

이 코드 골프 도전에서, 당신의 프로그램은 전투에서 차단하는 방법을 결정하는 매직 플레이어 대신에있을 것입니다.


각 생물에는 힘과 강인함의 두 가지 관련 속성이 있습니다. 생물의 힘은 전투에서 다룰 수있는 피해량이며, 그 인성은 파괴하는 데 필요한 피해량입니다. 힘은 항상 0 이상이고 인성은 항상 1 이상입니다.

매직 전투 중에 자신의 차례가 된 플레이어는 자신의 생물 중 일부가 상대를 공격한다고 선언합니다. 그런 다음 방어 플레이어라고 알려진 다른 플레이어는 자신의 생물을 차단 자로 지정할 수 있습니다. 생물은 전투 당 하나의 생물 만 차단할 수 있지만, 다수의 생물은 모두 동일한 생물을 차단할 수 있습니다.

차단자가 선언 된 후, 공격 플레이어는 차단 된 각 공격 생물에 대해 해당 생물이 자신을 방어하는 생물에게주는 피해 (힘과 동일)를 분배하는 방법을 결정합니다.

그런 다음 피해가 처리됩니다. 각 생물은 그 힘과 동일한 피해를 입 힙니다. 차단 된 공격 생물은 위와 같이 피해를 입 힙니다. 차단되지 않은 공격 생물은 방어 플레이어에게 피해를 입 힙니다. 생물을 막 으면 자신이 막은 생물에게 피해를 입 힙니다. 막지 않은 방어 플레이어의 생물은 피해를 입히지 않습니다. (생물을 막을 필요는 없습니다.)

마지막으로, 인성보다 크거나 같은 피해를 입은 생물은 모두 파괴되어 전장에서 제거됩니다. 생물의 인성보다 적은 피해량은 효과가 없습니다.


이 프로세스의 예는 다음과 같습니다.

힘 P와 강인도 T를 가진 생물은 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.

방어 플레이어는 전투에서 3 가지 목표를 가지고 있습니다. 상대방의 생물을 파괴하고 자신의 생물을 보존하며 가능한 적은 피해를 입 힙니다. 또한 힘과 강인성이 더 높은 생물이 더 가치가 있습니다.

이것들을 단일 척도로 결합하기 위해, 전투에서 방어하는 플레이어의 점수는 살아남은 생물의 힘과 강약의 합에서, 상대방의 살아남은 생물의 힘과 강약의 합에서 1을 뺀 것과 같습니다. 방어 플레이어에게 입힌 피해량의 절반. 방어 플레이어는이 점수를 최대화하려고하지만 공격 플레이어는이 점수를 최소화하려고합니다.

위에 표시된 전투에서 점수는 다음과 같습니다.

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

방어 플레이어가 위에서 설명한 전투에서 전혀 차단하지 않았다면 점수는

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

방어 플레이어를위한 최적의 선택은 차단했을 2/21/1과를 1/4, 그리고 차단 3/3와 함께 0/1. 그들이 그렇게했던 경우 만 1/4과는 3/3살아남을 것이고, 손상은 점수를 만들고, 방어 플레이어에 처리되지 않은 것

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

당신의 도전은 방어 플레이어를위한 최적의 차단 선택을 출력하는 프로그램을 작성하는 것입니다. "최적"은 상대가 당신이 어떻게 막았는지에 따라 점수를 최소화하는 방식으로 피해를 분배한다는 점에서 점수를 최대화하는 선택을 의미합니다.

이는 최대치입니다 : 각 블로킹 조합에 대한 점수를 최소화하는 데미지 분포에 대한 최대 점수입니다.


입력 : 입력은 2 개의 튜플 목록으로 구성되며, 각 2 개의 튜플은 (파워, 인성) 형식입니다. 첫 번째 목록에는 각 공격 생물 (상대방 생물)의 힘과 강인함이 포함됩니다. 두 번째 목록에는 각 생물의 힘과 강인함이 포함됩니다.

튜플과리스트는 다음과 같은 편리한 형식으로 표시 될 수 있습니다.

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

출력 : 출력은 (차단 생물, 막힌 생물) 형태, 즉 생물 중 하나와 그 생물 중 하나가 뒤 따르는 일련의 2- 튜플로 구성됩니다. 생물은 입력 목록에서 색인으로 참조됩니다. 인덱스는 0 또는 1 인덱스 일 수 있습니다. 다시 말하지만 편리한 형식입니다. 모든 주문은 괜찮습니다. 예를 들어, 위의 입력을 고려하여 위에서 최적의 차단 시나리오는 다음과 같이 표시 될 수 있습니다.

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

예 :

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

입력 및 출력은 STDIN, STDOUT, CLA, 기능 입력 / 반환 등을 통해 이루어질 수 있습니다. 표준 허점이 적용됩니다. 이것은 코드 골프입니다 : 바이트 단위의 최단 코드가 이깁니다.


사양을 명확하게하고 초기 아이디어를 제공하기 위해이 pastebin 은 Python에서 참조 솔루션을 제공합니다. 이 best_block기능은이 문제에 대한 샘플 솔루션이며 프로그램을 실행하면보다 자세한 입력 및 출력을 제공 할 수 있습니다.


18
이 언덕의 왕을 만들어야합니다.
PyRulez

1
@Arnauld nope, 그것은 또한 유효한 답변입니다.
isaacg

답변:


6

자바 스크립트 (ES7)  354  348 바이트

로 입력을 ([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)`)

온라인으로 사용해보십시오!

덜 골프와 형식

이 코드는 map별명과 eval()가독성을 위해 래핑 없이 골프 버전과 동일합니다 .

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

방법?

초기화 및 메인 루프

우리가 가장 먼저하는 일이 정의되지 않은 권력과의 인성 더미 공격 생물을 추가 할 것입니다 . 이 방법을 포함하여 새로운 공격 생물 를 반환합니다 .0ApushA

A = a.push([, 0])

우리는 생물을 전혀 차단하지 않고이 더미 생물을 차단할 것입니다. 이를 통해 코드를 단순화 할 수 있습니다.

방어 플레이어의 조합 수는 로 표현할 수 있습니다 . 여기서 는 방어자의 수입니다. 카운터 과 함께 for 루프를 사용하여 이러한 모든 가능성을 반복합니다.ADDNN

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

현재 반복의 점수와 최대 점수는 각각 와 저장됩니다 . 마찬가지로 현재 솔루션과 최상의 솔루션은 및 저장됩니다 .SMoO

각 반복이 끝날 때마다 과 를 업데이트 해야하는지 테스트합니다 .MO

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

방어 구축

아래는 각 반복의 시작 부분에서 실행되는 코드 블록입니다. 새로운 중요한 변수 인 은 각 공격자에 대한 차단 목록을 보유하고 있습니다.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()

공격 최적화

공격자의 결정은 서로 관련이 없습니다. 공격 측에 대한 전역 최적은 각 공격자에 대한 로컬 최적의 합계입니다.

인덱스 에서 파워 와 인성 의 각 공격자를 반복합니다 .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)
)                         //

수비수 점수 업데이트

공격자가 반복 할 때마다 다음과 같이 방어자 점수를 업데이트합니다.

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

왼쪽 부분은 공격자가 생존 한 경우 점수를 뺍니다. 오른쪽 부분은 공격이 전혀 차단되지 않은 경우 공격자의 강인성을 절반을 빼거나 그렇지 않으면 가장 높은 공격자 점수를 추가합니다 (수비측 관점에서 최악).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.