The Shield Wall

18

2

Historical Background

The shield wall is a tactical war formation that dates back to at least 2500 BC. It consisted of warriors overlapping their shields with those of their companions to form a 'wall'. The appeal of the tactic comes from the fact that even the most unskilled warrior could fight in a wall as long as they had a shield and a weapon. Because of the proximity of the walls, there was little space to move around, and the battle usually became a shoving match fought with sharp weapons.

The Challenge

Your task is to create a program or function that, given two arrays/lists/vectors of warriors as input, decides the outcome of the battle. The lists will represent a single-line wall of shields, and they will follow a specific notation:

Vikings:

The nordic warriors had a fierce drive for battle. During the late 8th through the mid 11th centuries, Danish vikings invaded the kingdoms of Britain looking for riches and farmable land. For the purpose of this challenge, these are the vikings' warriors:

  • The Jarl: Usually found leading his men from the center of the wall, jarls were the leaders of the viking hordes. Takes 15 damage to die, and deals 2 damage per round.
  • The Berserker: Although fantasy has greatly twisted the image of the berserkers, these warriors were known to fight in a trance-like fury without any kind of protection other than their shields. Takes 6 damage to die, and deals 3 damage per round.
  • The Chieftain: Chieftains were rich men who had free men at their service. They'd usually have earned great glory and riches in battle. Takes 10 damage to die, and deals 2 damage per round.
  • The Free Men: Warriors that served a chieftain. They were sworn to fight for their lords until death. Takes 8 damage to die, and deals 1 damage per round.
  • The Skald: Skalds, usually translated as bards, were free men who were hired to write poems, stories or songs about the great deeds of the nordic warriors. Takes 8 damage to die, and gives each adjacent warrior 1 bonus damage. Skalds deal no damage. Warriors cannot gain more than 1 bonus damage this way.

Saxons:

The Saxons came to settle in Britain from continental Europe following the demise of the Roman Empire in the 5th century. For the purposes of this challenge, there are the saxons' warriors:

  • The Earl: Ealdormen, commonly called Earls, were members of the higher nobility. They usually held great streches of land and had hundreds or even thousands of sworn men. Takes 20 damage to die, and deals 1 damage per round.
  • The Knight: For lack of a better term, the knights were minor noblemen who owned some land. In most cases, knights were sworn servants to an Earl. Takes 10 damage to die, and deals 2 damage per round.
  • The Warrior: Common men, usually minor noblemen without land or peasants who served a knight. When adjacent to a Knight or Earl, warriors have a +1 damage bonus. Takes 8 damage to die, and deals 2 damage per round.
  • The Fyrd: The Fyrd was a militia-like group of free men, usually poor farmers, who'd bring any weapon (or weapon-like farming implement) they had to fight in the wall. Takes 5 damage to die, and deals 1 damage per round.
  • The Priest: Priests were highly valued in early Saxon culture, being heralds of the words of God. Priests take 15 damage to die, and prevent up to 1 damage each adjacent warrior would be dealt. Priests deal no damage. Priests cannot prevent more than 1 damage to a warrior.

The Wall

Walls meet each other at their centers. Each round, each warrior assigns damage to the warrior directly in front of it or, if there's no living warrior in front of it, the diagonally adjacent living warrior with least health remaining. If there is a tie, choose the warrior closer to the edge of the wall.

Example:

Vikings
[M,M,M,B,B,C,J,C,B,B,M,M,M]
[F,F,F,W,W,K,E,K,W,W,F,F,F]
Saxons

To make matters easier, let's convert these walls into numbers:
Round 0:
 M M M B B C  J  C  B B M M M
[8,8,8,6,6,10,15,10,6,6,8,8,8]
[5,5,5,8,8,10,20,10,8,8,5,5,5]
 F F F W W K  E  K  W W F F F

Round 1: Notice that 2 of the Saxons' warriors are adjacent to Knights, so they have a +1 damage bonus.
 M M M B B C J  C B B M M M
[7,7,7,4,3,8,14,8,3,4,7,7,7]
 | | | | | | || | | | | | |
[4,4,4,5,5,8,18,8,5,5,4,4,4]
 F F F W W K E  K W W F F F

Round 2: 
 M M M B B C J  C B B M M M
[6,6,6,2,0,6,13,6,0,2,6,6,6]
 | | | | | | || | | | | | |
[3,3,3,2,2,6,16,6,2,2,3,3,3]
 F F F W W K E  K W W F F F

Round 3: Remember to collapse the arrays to account for dead warriors. Also, notice that the 2 outermost Fyrd are now attacking the diagonally adjacent viking. 
   M M M B C J  C B M M M
  [4,5,4,0,4,12,4,0,4,5,4]
  /| | | | | || | | | | |\
[2,2,2,1,0,4,14,4,0,1,2,2,2]
 F F F W W K E  K W W F F F

Round 4: Notice once again the saxon Warriors next to the Knights dealing 3 damage:
   M M M C J  C M M M
  [2,4,1,2,11,2,1,4,2]
  /| | | | || | | | |\
[2,1,1,0,2,12,2,0,1,1,2]
 F F F W K E  K W F F F
Round 5:
 M M M C J  C M M M
[1,3,0,0,10,0,0,3,1]
 | | | | || | | | |
[1,0,0,0,10,0,0,0,1]
 F F F K E  K F F F

Round 6: 
    M M J M M
   [1,2,9,2,1]
     \| | |/   
     [0,8,0]
      F E F
Rounds 7 and 8:
      M M J M M         M M J M M
     [1,2,8,2,1]       [1,2,8,2,1]
         \|/               \|/ 
         [4]               [0]
          E                 E  

Output: Viking victory.

Rules:

  • Default Loopholes apply.
  • You can use any convenient IO method.
  • This is , so shortest code (in bytes, per language) wins.
  • You may not assume the lists will have the same length, but they will always be alignable at their centers (there will always be an odd number of warriors in each list if the lists are of different sizes).
  • You may output any truthy/falsey value. Please specify in your answer the equivalents of "Viking/Saxon victory".
  • The loser is determined when all the warriors of a wall are dead.
  • If you ever end up with walls that are not alignable during the code execution, align them as centrally as possible, leaving one extra warrior on the longer wall to the right side. E.g.:

      [M,M,M,J,M,M,M]
        [K,E,K,W];
    
          [B,B,B,J]    
    [K,K,W,W,K,E,K,W,W,K,K]
    
  • Feel free to try and test your code with any setup of walls, not just the ones in the test cases.

Test Cases:

V: [M,M,B,C,B,C,J,C,B,C,B,M,M]
S: [F,F,W,K,W,K,E,K,W,K,W,F,F]
O: Viking victory.
------------------------------
V: [M,M,M,M,M,M,M,M,M,M]
S: [W,W,W,W,W,W,W,W,W,W]
O: Saxon victory.
------------------------------
V: [B,C,M,B,C,M,M,C,B,M,C,B,M]
S:   [W,F,W,F,E,E,E,F,W,F,W]
O: Viking victory.
------------------------------
V:         [B,B,B,J,B,B,B]
S: [W,W,W,W,K,K,K,E,K,K,K,W,W,W,W]
O: Saxon victory.
------------------------------
V: [J]
S: [E]
O: Viking victory.
------------------------------
V: [C,C,C,C,B,B,M,M,M,M,J,J,J,M,M,M,M,B,B,C,C,C,C]
S: [K,K,K,K,K,K,K,K,K,K,W,E,W,K,K,K,K,K,K,K,K,K,K]
O: Saxon victory.
------------------------------
V: [M,M,S,C,B,J,B,C,S,M,M]
S: [F,K,P,W,K,E,K,W,P,K,F]
O: Saxon victory.
------------------------------
V: [S,S,S,...,S]
S: [P,P,P,...,P]
O: UNDEFINED (since both priests and skalds deal no damage, you can output anything here.)
------------------------------

There are some historical inaccuracies. Feel free to point them out and I'll do my best to fix them.

J. Sallé

Posted 2017-11-21T15:54:20.670

Reputation: 3 233

Link to the Sandbox – J. Sallé – 2017-11-21T15:54:47.757

Can we define other symbols instead of the first letters of the names, for example numbers 0-9? – NieDzejkob – 2017-11-21T16:19:49.383

@NieDzejkob sure thing. Just make sure you specify in your answer which symbols were used for which warrior. – J. Sallé – 2017-11-21T16:21:10.840

3Would it be considered cheating to take input as their properties rather than letters? (example as (health, damage, damagebonus, protbonus)) – HyperNeutrino – 2017-11-21T17:02:46.663

@HyperNeutrino I'm not exactly sure, but I think it'd be okay? I don't see how that could give you a major advantage. As I told NieDzejkob, as long as you specify in your answer what's representing each warrior, go for it. – J. Sallé – 2017-11-21T17:41:43.010

Does S give bonus to M or all nordic people? – HyperNeutrino – 2017-11-21T17:49:36.140

@HyperNeutrino Skalds give their bonuses to any warrior next to them (up to +1 damage), not just to the Free Men. – J. Sallé – 2017-11-21T17:53:04.880

When exactly is the array of dead warriors collapsed? – TFeld – 2017-11-22T13:43:57.573

@TFeld If an array has dead warriors, you should remove them before the next round is computed. – J. Sallé – 2017-11-22T14:09:23.763

@DLosc Thanks for the information, I'll update the question accordingly :) – J. Sallé – 2017-11-22T14:09:30.713

@J.Sallé That's what i thought, but I don't get the same result as you in the test case (round 2->3): V:[6,6,6,2,0,6,13,6,0,2,6,6,6], S:[3,3,3,2,2,6,16,6,2,2,3,3,3], gets collapsed to: V: [6,6,6,2,6,13,6,2,6,6,6], S:[3,3,3,2,2,6,16,6,2,2,3,3,3], which in the next round is: V: [4,5,4,0,4,12,4,0,4,5,4], S:[3,2,2,1,0,4,14,4,0,1,2,2,3]. But you get V: [4,5,5,0,4,12,4,0,5,5,4], S:[2,2,2,1,0,4,14,4,0,1,2,2,2] – TFeld – 2017-11-22T14:52:59.913

@TFeld you're right, I missed 1 damage there. Luckily the result won't change. Thanks for catching that, I'll edit it. – J. Sallé – 2017-11-22T14:57:32.650

@J.Sallé What about testcase 3 (V: [B,C,M,B,C,M,M,C,B,M,C,B,M] S: [W,F,W,F,E,E,E,F,W,F,W]) In my program the saxon line has an even number of warriors at step 3. How do I align it then? – TFeld – 2017-11-22T15:27:18.897

@TFeld I actually forgot to add that case to the rules because I lost my earlier draft of the question which already had it in there hahahah. I edited that in, please check the rules section again :) – J. Sallé – 2017-11-22T15:50:17.540

@J.Sallé "assigns damage to the warrior directly in front of it or, if there's no living warrior in front of it, the diagonally adjacent living warrior with least health remaining" - can there ever be more than 1 diagonally adjacent, given arrays are collapsed before each round? – ngn – 2017-11-23T11:29:29.593

@ngn I don't think so. I added that line "for safety" you might say, because I couldn't know for sure there wouldn't be a case where that didn't happen. – J. Sallé – 2017-11-23T12:36:35.460

@J.Sallé "[A skald] gives each adjacent warrior 1 bonus damage. Skalds deal no damage." - what if two skalds are adjacent? – ngn – 2017-11-25T13:18:28.520

@ngn as you can see from the last test case, they’ll deal no damage even if there’s two skalds adjacent to one another. – J. Sallé – 2017-11-25T13:55:04.687

@J.Sallé Thanks. I noticed that but I thought the priests on the opposite side might be protecting each other - "warrior" is ambiguous in the Saxon camp and I'm still not sure how to interpret priests. I'll have a closer look at the Python solution. – ngn – 2017-11-25T14:49:18.187

@J.Sallé In the example the transition from round 4 to round 5 seems wrong - if (as you said) dead warriors are removed before the round is computed, then the Free Men at the two ends should survive, not the Fyrds. Or am I missing something? – ngn – 2017-11-25T21:05:05.793

@ngn sorry for the late answer. The dead are removed before the next round is computed, yes, and you're right. Doing those by hand is quite weird, hahahah. I've fixed it now, fortunately it didn't affect the outcome. – J. Sallé – 2017-11-27T12:17:51.500

Answers

3

Python 2, 576 573 565 554 540 549 bytes

O=[(0,0)]
g=lambda D,W,i:D[i-1]*(W[i-1]<1)+D[i]+D[i+1]*(W[i+1]<1)
h=lambda*V:[v for v in zip(*V)if v[1]>0]
def f(v,s):
 l,L=len(v),len(s);m=max(l,L);a,b=(L-l)/2,(l-L)/2;V,U=zip(*O+O*a+v+O*a+O+O);S,T=zip(*O+O*b+s+O*b+O+O);z=[0]*(m+2);w=z[:];r=range(1,m+1);U=list(U);T=list(T)
 for i in r:w[i]=[0,2,3,2,1,0][V[i]]+(5in V[i-1:i+2:2])*(V[i]<5);z[i]=[0,1,2,2+({1,2}&set(S[i-1:i+2:2])>set()),1,0][S[i]]
 for i in r:U[i]-=g(z,V,i);d=g(w,S,i);T[i]-=d-(d>0)*(5in S[i-1:i+2:2])
 V=h(V,U);S=h(S,T)
 if([],[])<(V,S)!=(v,s):return(f(V,S)if S else'V')if V else'S'

Try it online!

TFeld

Posted 2017-11-21T15:54:20.670

Reputation: 19 246

If I understand correctly, this bit: (5in V[i-1:i+2:2]) implies that adjacent skalds can inflict damage. You may need a ...*(V[i]!=5) there. Test: print f([S,S],[P]) # says V but should be a Draw – ngn – 2017-11-25T15:19:40.343

@ngn Thanks for that :) – TFeld – 2017-11-26T12:54:09.923

2

APL (Dyalog Classic), 128 bytes

{t l d b p←(-⌊2÷⍨+/0=⊃⍵)∘⌽¨⍵⋄l+←e+3+/0,0,⍨(0=t)×e←(×≢¨p∩¨h)-⊖d+×≢¨b∩¨h←3,/0,t,0⋄⍵≡a←↑¨(⊂↓l>0)/¨¨↓¨t l d b p:0⋄0∊s←+/l>0:×-/s⋄∇a}

Try it online!

There are two functions in the TIO link: g is the golfed function above and f is an ungolfed function that accepts a pair of strings, converts them to a suitable representation and calls the golfed function.

The input is five matrices: t warrior types as ints; l life; d damage; b what warrior types give bonus when adjacent; p same for protection. The matrices consist of two rows - Vikings and Saxons. If their warriors are not the same number, the matrices must be 0-padded, though not necessarily centred. The result is 1/¯1 for Viking/Saxon victory or 0 for a draw.

{
  t l d b p←(-⌊2÷⍨+/0=⊃⍵)∘⌽¨⍵ ⍝ centre the matrices
  ⍝ (-⌊2÷⍨+/0=⊃⍵) is a pair of numbers - by how much we should rotate (⌽) the rows
  ⍝       +/0=⊃⍵  how many dead? (⊃⍵ is the types, dead warriors have type 0)
  ⍝  -⌊2÷⍨        negated floor of half

  l+←e+3+/0,0,⍨(0=t)×e←(×≢¨p∩¨h)-⊖d+×≢¨b∩¨h←3,/0,t,0 ⍝ compute and apply effective damage
  ⍝ h←3,/0,t,0  are triples of types - self and the two neighbours
  ⍝ b∩¨h        for each warrior intersect (∩) h with his bonus-giving set b
  ⍝ ×≢¨         non-empty? 0 or 1
  ⍝ d+          add to the damage normally inflicted
  ⍝ ⊖           reverse vertically (harm the enemy, not self)
  ⍝ (×≢¨p∩¨h)   same technique for protections (neighbouring priests)
  ⍝ e←          remember as "e" for "effective damage"; we still need to do the diagonal attacks
  ⍝ (0=t)×      zero out the attacks on living warriors
  ⍝ 3+/0,0,⍨    sum triples - each warrior suffers the damage intended for his dead neigbours
  ⍝ e+          add that to the effective damage
  ⍝ l+←         decrease life ("e" is actually negative)

  ⍵≡a←↑¨(⊂↓l>0)/¨¨↓¨t l d b p:0 ⍝ remove dead; if no data changed, it's a draw
  ⍝ ↓¨          split each matrix into two row-vectors
  ⍝ (⊂↓l>0)     boolean mask of warrios with any life left, split in two and enclosed
  ⍝ /¨¨         keep only the survivors
  ⍝ ↑¨          mix the pairs of rows into matrices again, implicitly padding with 0-s
  ⍝ a←          call that "a" - our new arguments
  ⍝ ⍵≡a ... :0  is "a" the same as our original arguments? - nothing's changed, it's a draw

  0∊s←+/l>0:×-/s ⍝ if one team has no members left, they lost
  ⍝ l>0         bitmask of survivors
  ⍝ s←+/l>0     how many in each camp
  ⍝ 0∊          has any of the two armies been annihilated?
  ⍝ :×-/s       if yes, which one? return sign of the difference: ¯1 or 1, or maybe 0

  ∇a ⍝ repeat
}

ngn

Posted 2017-11-21T15:54:20.670

Reputation: 11 449