เวทมนตร์: กอล์ฟการต่อสู้ที่รวบรวม


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 เป้าหมายในการต่อสู้: ทำลายสิ่งมีชีวิตของฝ่ายตรงข้ามรักษาสิ่งมีชีวิตของตัวเองและได้รับความเสียหายน้อยที่สุดเท่าที่จะทำได้ นอกจากนี้สิ่งมีชีวิตที่มีพละกำลังและความเหนียวก็มีค่ามากกว่า

ในการรวมสิ่งเหล่านี้เข้าด้วยกันเราจะบอกว่าคะแนนของผู้เล่นที่ได้รับการปกป้องจากการต่อสู้นั้นเท่ากับผลรวมของพลังและความทนทานของสิ่งมีชีวิตที่ยังมีชีวิตอยู่ ครึ่งหนึ่งของจำนวนความเสียหายที่เกิดขึ้นกับผู้เล่นที่ป้องกัน ผู้เล่นที่ป้องกันต้องการเพิ่มคะแนนนี้ในขณะที่ผู้เล่นที่โจมตีต้องการลดให้น้อยที่สุด

ในการต่อสู้ที่แสดงด้านบนคะแนนคือ:

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/2ด้วย1/1และ1/4และปิดกั้นด้วย3/3 0/1ถ้าพวกเขาทำเช่นนั้นเท่านั้น1/4และ3/3จะมีชีวิตรอดและไม่มีความเสียหายจะได้รับการแจกให้ผู้เล่นทีมป้องกันทำให้คะแนน

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

ความท้าทายของคุณคือการเขียนโปรแกรมที่จะส่งออกตัวเลือกการปิดกั้นที่ดีที่สุดสำหรับผู้เล่นที่ปกป้อง "ดีที่สุด" หมายถึงตัวเลือกที่เพิ่มคะแนนให้มากที่สุดเนื่องจากคู่ต่อสู้กระจายความเสียหายในลักษณะที่ทำให้คะแนนน้อยที่สุดตามที่คุณบล็อก

นี่คือ maximin: คะแนนสูงสุดเหนือการแจกแจงความเสียหายซึ่งลดคะแนนสำหรับการปิดกั้นแต่ละชุด


อินพุต:อินพุตประกอบด้วยสองรายการของ 2-tuples โดยที่ 2-tuple แต่ละรายการมีรูปแบบ (Power, Toughness) รายการแรกจะมีพลังและความทนทานของสิ่งมีชีวิตที่โจมตีแต่ละชนิด (สิ่งมีชีวิตของคู่ต่อสู้ของคุณ) รายการที่สองจะมีพลังและความทนทานของสิ่งมีชีวิตแต่ละตัวของคุณ

สิ่งอันดับและรายการอาจแสดงในรูปแบบที่สะดวกเช่น:

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

เอาท์พุท:เอาท์พุทจะประกอบด้วยชุดของ 2-tuples ในรูปแบบ (บล็อกสิ่งมีชีวิตสัตว์ที่ถูกบล็อก) - นั่นคือหนึ่งในสิ่งมีชีวิตของคุณตามด้วยหนึ่งในสิ่งมีชีวิตของพวกเขา สิ่งมีชีวิตจะถูกอ้างอิงโดยดัชนีของพวกเขาในรายการอินพุต ดัชนีอาจเป็นดัชนี 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, ฟังก์ชั่นอินพุต / คืน ฯลฯใช้ช่องโหว่มาตรฐาน นี่คือ code-golf: รหัสที่สั้นที่สุดในหน่วยไบต์ชนะ


เพื่อชี้แจงข้อมูลจำเพาะและให้แนวคิดเริ่มต้นpastebin นี้จะให้บริการโซลูชั่นอ้างอิงใน Python best_blockฟังก์ชั่นเป็นโซลูชั่นตัวอย่างเพื่อความท้าทายนี้และทำงานโปรแกรมจะให้ข้อมูลอย่างละเอียดมากขึ้นและการส่งออก


18
คุณควรสร้างราชาแห่งขุนเขานี้
PyRulez

1
@Arnauld ไม่นั่นเป็นคำตอบที่ถูกต้องเช่นกัน
isaacg

คำตอบ:


6

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

ลองออนไลน์!

golfed น้อยและจัดรูปแบบ

รหัสนี้เหมือนกับรุ่น golfed โดยไม่มี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
}

อย่างไร?

การเริ่มต้นและลูปหลัก

สิ่งแรกที่เราทำคือการเพิ่มสิ่งมีชีวิตหุ่นโจมตีด้วยอำนาจที่ไม่ได้กำหนดและความทนทานของ0วิธีการส่งกลับหมายเลขใหม่ของสิ่งมีชีวิตโจมตีรวมถึงนี้0ApushA

A = a.push([, 0])

เราจะปิดกั้นสิ่งมีชีวิตจำลองนี้แทนที่จะปิดกั้นสิ่งมีชีวิตใด ๆ เลย วิธีนี้ช่วยให้เข้าใจง่ายขึ้นในรหัส

จำนวนชุดค่าผสมสำหรับผู้เล่นป้องกันสามารถแสดงเป็นโดยที่คือจำนวนผู้ปกป้อง เราใช้สำหรับวงกับเคาน์เตอร์ย้ำกว่าเป็นไปได้ทั้งหมดเหล่านี้ADDNN

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

คะแนนของการวนซ้ำในปัจจุบันและคะแนนสูงสุดจะถูกเก็บไว้ในและตามลำดับ ในทำนองเดียวกันการแก้ปัญหาในปัจจุบันและทางออกที่ดีที่สุดจะถูกเก็บไว้ในและOSMoO

ในตอนท้ายของการทำซ้ำแต่ละครั้งเราจะทดสอบว่าต้องปรับปรุงและหรือไม่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()

การเพิ่มประสิทธิภาพการโจมตี

การตัดสินใจของผู้โจมตีไม่เกี่ยวข้องกัน ความเหมาะสมระดับโลกสำหรับด้านการโจมตีคือผลรวมของ optima ท้องถิ่นสำหรับผู้โจมตีแต่ละคน

เราย้ำในแต่ละโจมตีของอำนาจและความทนทานที่ดัชนีฉันPTi

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

ในการค้นหาการตัดสินใจที่ดีที่สุดสำหรับผู้โจมตีเราจะสร้างรายการใหม่ที่ได้มาจาก :l[i]

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

แต่ละรายการในรายการนี้มีความเหนียวของตัวบล็อกตามด้วยคะแนน (กำลัง + ความทนทาน) ในขณะที่สร้างรายการนี้เรายังคำนวณซึ่งเป็นผลรวมของพลังของตัวบล็อกทั้งหมด เราจะใช้ในภายหลังเพื่อทดสอบว่าผู้โจมตีถูกทำลายหรือไม่ (ความจริงที่ว่าผลรวมกลางจะถูกเก็บไว้เป็นรายการที่ 3 สำหรับแต่ละตัวบล็อกเป็นเพียงเคล็ดลับการเล่นกอล์ฟเพื่อบันทึกไม่กี่ไบต์รายการที่สามนี้ไม่ได้ใช้)n

ตอนนี้เราเรียกใช้ฟังก์ชันเรียกซ้ำด้วยพารามิเตอร์ 2 ตัวคือกำลังของผู้โจมตีปัจจุบันและรายการที่กำหนดไว้ด้านบน ฟังก์ชั่นนี้จะพยายามทุกวิถีทางที่เป็นไปได้ในการส่งคะแนนความเสียหายในบล็อค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.