Magic: the Gathering to gra karciana, w której gracze grają między innymi w karty przedstawiające stworzenia, które mogą następnie zaatakować drugiego gracza lub obronić się przed atakami drugiego gracza, blokując.
W tym wyzwaniu polegającym na grze w golfa Twój program zastąpi magiczny gracz decydujący o sposobie blokowania w walce.
Każde stworzenie ma dwa istotne atrybuty: Moc i wytrzymałość. Moc stworzenia to ilość obrażeń, jakie może zadać w walce, a jego wytrzymałość to ilość obrażeń niezbędnych do jego zniszczenia. Moc zawsze wynosi co najmniej 0, a wytrzymałość zawsze wynosi co najmniej 1.
Podczas walki w Magii gracz, którego tura jest zadeklarowana, atakuje przeciwnika niektóre ze swoich stworzeń. Następnie drugi gracz, znany jako obrońca, może przypisać swoje stworzenia jako blokerów. Stworzenie może zablokować tylko jedno stworzenie na walkę, ale wszystkie stworzenia mogą zablokować to samo stworzenie.
Po ogłoszeniu blokady, atakujący gracz decyduje, dla każdego atakującego stwora, który został zablokowany, jak rozłożyć obrażenia (równe jego mocy), które stwór zadaje stworom blokującym je.
Następnie zadawane są obrażenia. Każde stworzenie zadaje obrażenia równe swojej mocy. Atakowane stworzenia, które zostały zablokowane, zadają obrażenia, jak opisano powyżej. Odblokowane atakujące stworzenia zadają obrażenia broniącemu się graczowi. Blokujące stworzenia zadają obrażenia stworzeniu, które zablokowały. Stworzenia należące do broniącego się gracza, które nie blokowały, nie zadają żadnych obrażeń. (Nie trzeba blokować stworzeń).
Wreszcie każde stworzenie zadające obrażenia równe lub większe niż jego wytrzymałość jest niszczone i usuwane z pola bitwy. Żadna ilość obrażeń mniejsza niż wytrzymałość stworzenia nie ma wpływu.
Oto przykład tego procesu:
Istota o mocy P i wytrzymałości T jest reprezentowana jako 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.
Broniący się gracz ma 3 cele w walce: Zniszcz stwory przeciwnika, zachowaj własne stwory i zadaj jak najmniej obrażeń. Ponadto, stworzenia o większej mocy i wytrzymałości są cenniejsze.
Aby połączyć je w jedną miarę, powiemy, że wynik broniącego się gracza podczas walki jest równy sumie mocy i twardości ocalałych stworzeń, minus suma mocy i twardości ocalałych przeciwników, minus jeden połowa obrażeń zadawanych obrońcy. Broniący gracz chce zmaksymalizować ten wynik, podczas gdy atakujący gracz chce go zminimalizować.
W pokazanej powyżej walce wynik był następujący:
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
Gdyby obrońca nie blokował się wcale w opisanej powyżej walce, wynik byłby taki
8 - 10 - (5/2) = -4.5
Optymalnym wyborem dla broniącego się gracza byłoby zablokowanie za 2/2
pomocą 1/1
i 1/4
, i zablokowanie za 3/3
pomocą 0/1
. Gdyby to zrobili, tylko 1/4
i oni 3/3
przeżyliby, a obrońca nie zadałby żadnych obrażeń, dzięki czemu uzyskałby wynik
5 - 6 - (0/2) = -1
Twoim wyzwaniem jest napisanie programu, który wygeneruje optymalny wybór blokowania dla broniącego się gracza. „Optymalny” oznacza wybór, który maksymalizuje wynik, biorąc pod uwagę, że przeciwnik rozdziela obrażenia w sposób, który minimalizuje wynik, biorąc pod uwagę sposób zablokowania.
Jest to maksymalny: maksymalny wynik w rozkładzie obrażeń, który minimalizuje wynik dla każdej kombinacji blokującej.
Wkład: Dane wejściowe będą się składały z dwóch list 2-krotek, przy czym każda 2-krotka ma postać (Moc, Wytrzymałość). Pierwsza lista będzie zawierać moce i wytrzymałość każdego atakującego stwora (stworów przeciwnika). Druga lista będzie zawierać moce i wytrzymałość każdego z twoich stworzeń.
Krotki i listy mogą być reprezentowane w dowolnym dogodnym formacie, takim jak:
[[2, 2], [3, 3]]
[[1, 4], [1, 1], [0, 1]]
Wyjście: Wyjście będzie składało się z serii 2 krotek w formie (blokujące stworzenie, zablokowane stworzenie) - to znaczy jedno z twoich stworów, po którym następuje jedno z ich stworzeń. Do stworzeń będzie odwoływał się ich indeks na listach wejściowych. Indeksy mogą być indeksowane 0 lub 1. Ponownie, każdy wygodny format. Każde zamówienie jest w porządku. Na przykład optymalny scenariusz blokowania z góry, biorąc pod uwagę powyższe dane wejściowe, można przedstawić jako:
[0, 0] # 1/4 blocks 2/2
[1, 0] # 1/1 blocks 2/2
[2, 1] # 0/1 blocks 3/3
Przykłady:
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).
Wejścia i wyjścia mogą odbywać się za pośrednictwem STDIN, STDOUT, CLA, funkcji wejścia / powrotu itp. Obowiązują standardowe luki . To jest code-golf: wygrywa najkrótszy kod w bajtach.
Aby wyjaśnić specyfikację i przedstawić początkowe pomysły, ta pasta stanowi rozwiązanie referencyjne w języku Python. Ta best_block
funkcja stanowi przykładowe rozwiązanie tego problemu, a uruchomienie programu zapewni bardziej szczegółowe dane wejściowe i wyjściowe.
źródło
Odpowiedzi:
JavaScript (ES7),
354348 bajtówPobiera dane wejściowe jako
([attackers], [defenders])
.Wypróbuj online!
Mniej golfa i sformatowane
Ten kod jest identyczny z wersją golfową, tylko bez
map
aliasów ieval()
opakowania dla czytelności.W jaki sposób?
Inicjalizacja i główna pętla
push
Zamierzamy zablokować to obojętne stworzenie zamiast nie blokować żadnego stworzenia. Pozwala to na pewne uproszczenia w kodzie.
Budowanie naszej obrony
Optymalizacja ataku
Decyzje atakujących są ze sobą niezwiązane. Globalne optimum dla strony atakującej jest sumą lokalnych optymów dla każdego atakującego.
Aktualizacja wyniku obrońcy
Po każdej iteracji atakującego aktualizujemy wynik obrońcy o:
Lewa część odejmuje wynik atakującego, jeśli przeżył. Prawa część odejmuje połowę wytrzymałości atakującego, jeśli atak w ogóle nie został zablokowany, lub dodaje najlepszy wynik atakującego w przeciwnym razie (który jest najgorszy z punktu widzenia strony broniącej się).
źródło