Magic: the Gathering é um jogo de cartas colecionáveis em que, entre outras coisas, os jogadores jogam cartas representando criaturas, que podem atacar o outro jogador ou se defender contra os ataques do outro jogador, bloqueando.
Neste desafio de código-golfe, seu programa estará no lugar de um jogador de Magic que decide como bloquear em combate.
Cada criatura tem dois atributos relevantes: Poder e resistência. O poder de uma criatura é a quantidade de dano que ela pode causar em um combate, e sua resistência é a quantidade de dano necessária para destruí-la. A energia é sempre pelo menos 0 e a resistência é sempre pelo menos 1.
Durante o combate em Magic, o jogador do turno declara que algumas de suas criaturas estão atacando o oponente. Então, o outro jogador, conhecido como defensor, pode atribuir suas criaturas como bloqueadores. Uma criatura pode bloquear apenas uma criatura por combate, mas várias criaturas podem bloquear a mesma criatura.
Depois que os bloqueadores são declarados, o jogador atacante decide, para cada criatura atacante que foi bloqueada, como distribuir o dano (igual ao seu poder) que aquela criatura causa às criaturas que o bloqueiam.
Então, o dano é causado. Cada criatura causa dano igual ao seu poder. As criaturas atacantes que foram bloqueadas causam dano conforme descrito acima. Criaturas atacantes desbloqueadas causam dano ao jogador defensor. As criaturas bloqueadoras causam dano à criatura que eles bloquearam. Criaturas pertencentes ao jogador defensor que não bloquearam não causam dano. (As criaturas não precisam bloquear.)
Finalmente, qualquer criatura que cause dano igual ou superior a sua resistência é destruída e removida do campo de batalha. Qualquer quantidade de dano menor que a resistência de uma criatura não tem efeito.
Aqui está um exemplo deste processo:
Uma criatura com poder P e resistência T é representada como 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.
O jogador defensor tem 3 objetivos em combate: destruir as criaturas do oponente, preservar as próprias criaturas e receber o menor dano possível. Além disso, criaturas com mais poder e resistência são mais valiosas.
Para combiná-los em uma única medida, diremos que a pontuação do jogador defensor de um combate é igual à soma dos poderes e durezas de suas criaturas sobreviventes, menos a soma dos poderes e durezas das criaturas sobreviventes de seu oponente, menos um metade da quantidade de dano causado ao jogador defensor. O jogador defensor deseja maximizar essa pontuação, enquanto o jogador atacante deseja minimizá-la.
No combate mostrado acima, a pontuação foi:
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 o jogador defensor não tivesse bloqueado o combate descrito acima, a pontuação teria sido
8 - 10 - (5/2) = -4.5
A melhor opção para o jogador defensor seria bloquear o 2/2
com o 1/1
e 1/4
oe bloquear o 3/3
com o 0/1
. Se eles tivessem feito isso, apenas 1/4
o 3/3
sobreviveria e nenhum dano teria sido causado ao jogador defensor, fazendo a pontuação
5 - 6 - (0/2) = -1
Seu desafio é escrever um programa que produza a melhor opção de bloqueio para o jogador defensor. "Ótimo" significa a escolha que maximiza a pontuação, dado que o oponente distribui dano da maneira que minimiza a pontuação, considerando como você bloqueou.
Esta é uma pontuação máxima: a pontuação máxima sobre as distribuições de dano que minimizam a pontuação de cada combinação de bloqueio.
Entrada: A entrada consiste em duas listas de 2 tuplas, em que cada 2 tuplas é da forma (Potência, Robustez). A primeira lista conterá os poderes e as resistências de cada criatura atacante (as criaturas do seu oponente). A segunda lista conterá os poderes e as durezas de cada uma de suas criaturas.
Tuplas e listas podem ser representadas em qualquer formato conveniente, como:
[[2, 2], [3, 3]]
[[1, 4], [1, 1], [0, 1]]
Saída: A saída consistirá em uma série de duas tuplas, na forma (criatura bloqueadora, criatura bloqueada) - ou seja, uma de suas criaturas seguida por uma de suas criaturas. As criaturas serão referenciadas por seu índice nas listas de entrada. Os índices podem ser 0 ou 1 indexados. Mais uma vez, qualquer formato conveniente. Qualquer pedido é bom. Por exemplo, o cenário ideal de bloqueio acima, considerando a entrada acima, pode ser representado como:
[0, 0] # 1/4 blocks 2/2
[1, 0] # 1/1 blocks 2/2
[2, 1] # 0/1 blocks 3/3
Exemplos:
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).
A entrada e a saída podem ser via STDIN, STDOUT, CLA, entrada / retorno de função, etc. São aplicadas brechas padrão . Este é o código-golfe: o código mais curto em bytes vence.
Para esclarecer as especificações e fornecer idéias iniciais, este pastebin fornece uma solução de referência em Python. A best_block
função é uma solução de exemplo para esse desafio e a execução do programa fornecerá entradas e saídas mais detalhadas.