DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #43 - Boardgame Fight Resolver

Today's challenge is a smaller part of a larger idea.

You are creating a board game, similar to a mix between Fire Emblem and Chess. The game features four unique pieces: Swordsman, Cavalry, Archer, and Pikeman. Each piece has its own advantages and weaknesses in combat against other pieces.

You must write a function fightResolve that takes the attacking and defending pieces as input parameters and returns the winning piece.

The outcome of the fight between two pieces depends on which piece attacks, the type of the attacking piece and the type of the defending piece.

Archers > Swordsmen > Pikemen > Cavalry > Archers

Archers always win against swordsmen, swordsmen always win against pikemen, pikemen always win against cavalry and cavalry always win against archers.

If a matchup occurs that was not previously mentioned (for example Archers vs Pikemen) the attacker will always win.


This challenge comes from user Brysen on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

Top comments (22)

Collapse
 
alvaromontoro profile image
Alvaro Montoro • Edited

CSS

Add the attributes data-piece1 and data-piece2 to any element, and it will announce the winner:

[data-piece1][data-piece2]::before {
  content: attr(data-piece1) ' vs ' attr(data-piece2) ': ';
}

/* By default, piece 1 will win */
[data-piece1][data-piece2]::after {
  content: attr(data-piece1) ' wins!';
}

/* Only in some particular cases, piece 2 will win */
[data-piece2="archers"i][data-piece1="swordsmen"i]::after,
[data-piece2="swordsmen"i][data-piece1="pikemen"i]::after,
[data-piece2="pikemen"i][data-piece1="cavalry"i]::after,
[data-piece2="cavalry"i][data-piece1="archers"i]::after { 
  content: attr(data-piece2) ' wins!'
}

In CSS Selectors Level 4, there is an option to make some selectors case-insensitive (adding an i at the end as displayed above). And it is fairly well supported (haven't tried on IE). So it doesn't matter if the user writes "archers" or "Archers" or "aRcHeRs", all of them will be matched.

Collapse
 
somedood profile image
Basti Ortiz

This is by far the most clever CSS I have ever seen. I applaud you.

Collapse
 
reko91 profile image
reko91

Mad

Collapse
 
giorgiobertolotti profile image
Giorgio Bertolotti

Okay close it all, we have a winner!

Collapse
 
avalander profile image
Avalander

Look ma, pattern matching!

fightResolve :: String -> String -> String
fightResolve "Swordsman" "Archer"  = "Archer"
fightResolve "Pikeman" "Swordsman" = "Swordsman"
fightResolve "Cavalry" "Pikeman"   = "Pikeman"
fightResolve "Archer" "Cavalry"    = "Cavalry"
fightResolve attacker _            = attacker
Collapse
 
thepeoplesbourgeois profile image
Josh • Edited

Ahh, pattern matches. So elegance, much match ( *-*)

Collapse
 
konaarctic profile image
Kona Arctic • Edited


#!/bin/bash

# ( attack , defend )
# archer: 1; swordsmen: 2; pikemen: 3; cavalry: 4;
function fightResolve {
    echo "$1$2" | grep -qEe '(21|32|43|14)' - &&
        return $2
    return $1
}
Collapse
 
willsmart profile image
willsmart • Edited

I'm surprised no-one has used the age old xor-the-second-bit-of-the-first-characters-and-xor-the-fifth-bit-of-first-and-second-characters trick 😉

In C:

const char *defenderWins(const char *attacker, const char *defender) {
    return (((attacker[0] ^ defender[0]) & 2) | ((attacker[1] ^ defender[0]) & 0x10)) == 0x12;
}

Clearly, this doesn't really need any testing, but here's an exhaustive dump, err, full set of trials, just to be pedantic

#include <stdio.h>

const char *winner(const char *attacker, const char *defender) {
    return (((attacker[0] ^ defender[0]) & 2) | ((attacker[1] ^ defender[0]) & 0x10)) == 0x12 ? defender : attacker;
}

int main()
{
    const char *teams[] = { "archers","swordsmen", "pikemen", "cavalry" };
    const size_t teamCount=sizeof(teams)/sizeof(*teams);

    for (int attackeri = 0; attackeri<teamCount; ++attackeri) {
        for (int defenderi = 0; defenderi<teamCount; ++defenderi) {
            const char *attacker = teams[attackeri];
            const char *defender = teams[defenderi];
            printf("%s -> %s : %s wins\n", attacker, defender, winner(attacker,defender));
        }
    }
    return 0;
}

prints:

archers -> archers : archers wins
archers -> swordsmen : archers wins
archers -> pikemen : archers wins
archers -> cavalry : cavalry wins
swordsmen -> archers : archers wins
swordsmen -> swordsmen : swordsmen wins
swordsmen -> pikemen : swordsmen wins
swordsmen -> cavalry : swordsmen wins
pikemen -> archers : pikemen wins
pikemen -> swordsmen : swordsmen wins
pikemen -> pikemen : pikemen wins
pikemen -> cavalry : pikemen wins
cavalry -> archers : cavalry wins
cavalry -> swordsmen : cavalry wins
cavalry -> pikemen : pikemen wins
cavalry -> cavalry : cavalry wins
Collapse
 
avalander profile image
Avalander

Care to explain how it works? I don't get it 😅

Collapse
 
willsmart profile image
willsmart

Sure thing. It's a "don't look behind the curtain" kind of thing 🧙‍♂️
here's the rest of the code...

const a = [
  'archers',
  'swordsmen', 'pikemen', 'cavalry'
]
let i1 = 2,
  i2 = 1,
  i3 = 3,
  i4 = 3,
  i5 = 2,
  i6 = 2,
  i7 = 2,
  i8 = 2

winner_mank = (s1, s2) => ((s1.charCodeAt(i1) >> i3) ^ ((s2.charCodeAt(i2)) >> i4)) & ((s1.charCodeAt(i5) >> i7) ^ ((s2.charCodeAt(i6)) >> i8)) & 1 ? s2 : s1

winner_good = (s1, s2) => a.indexOf(s1) == (a.indexOf(s2) + 1) % 4 ? s2 : s1

verify = () => {
  for (const s1 of a) {
    for (const s2 of a) {
      if (winner_good(s1, s2) != winner_mank(s1, s2)) return
    }
  }
  console.log({
    i1,
    i2,
    i3,
    i4,
    i5,
    i6,
    i7,
    i8
  })
  debugger
  return true
}

for (i1 = 0; i1 < 7; i1++) {
  for (i2 = 0; i2 < 7; i2++) {
    for (i3 = 0; i3 < 8; i3++) {
      for (i4 = 0; i4 < 8; i4++) {
        for (i5 = 0; i5 < 7; i5++) {
          for (i6 = 0; i6 < 7; i6++) {
            for (i7 = 0; i7 < 8; i7++) {
              for (i8 = 0; i8 < 8; i8++) {
                verify()
              }
            }
          }
        }
      }
    }
  }
}

winner_mank is reducible to the one posted when i1=i3=0, i2=i4=1, i5=1,i7=0, i6=i8=4. And I felt it was may as well be in C.
I was lying about the age old trick thing. Upside is it's really really quick, downsides are all the other things 😂

Collapse
 
jay profile image
Jay

Rust Solution: Playground

fn fight_resolver(defender: Class, attacker: Class) -> Class {
    match (defender, attacker) {
        (Class::Swordsmen, Class::Archer)
        | (Class::Pikemen, Class::Swordsmen)
        | (Class::Cavalry, Class::Pikemen)
        | (Class::Archer, Class::Cavalry) => defender,
        (_, att) => att,
    }
}
Collapse
 
choroba profile image
E. Choroba

Perl solution, using a hash of known fight results.

#!/usr/bin/perl
use warnings;
use strict;

{   my %stronger = (
        archers   => 'swordsmen',
        swordsmen => 'pikemen',
        pikemen   => 'cavalry',
        cavalry   => 'archers');
    sub fight_resolve {
        my ($attacker, $defender) = @_;
        ($stronger{$defender} // "") eq $attacker ? $defender : $attacker
    }
}

use Test::More tests => 16;

is fight_resolve('archers', 'archers'    ), 'archers';
is fight_resolve('archers', 'swordsmen'  ), 'archers';
is fight_resolve('archers', 'pikemen'    ), 'archers';
is fight_resolve('archers', 'cavalry'    ), 'cavalry';
is fight_resolve('swordsmen', 'archers'  ), 'archers';
is fight_resolve('swordsmen', 'swordsmen'), 'swordsmen';
is fight_resolve('swordsmen', 'pikemen'  ), 'swordsmen';
is fight_resolve('swordsmen', 'cavalry'  ), 'swordsmen';
is fight_resolve('pikemen', 'archers'    ), 'pikemen';
is fight_resolve('pikemen', 'swordsmen'  ), 'swordsmen';
is fight_resolve('pikemen', 'pikemen'    ), 'pikemen';
is fight_resolve('pikemen', 'cavalry'    ), 'pikemen';
is fight_resolve('cavalry', 'archers'    ), 'cavalry';
is fight_resolve('cavalry', 'swordsmen'  ), 'cavalry';
is fight_resolve('cavalry', 'pikemen'    ), 'pikemen';
is fight_resolve('cavalry', 'cavalry'    ), 'cavalry';
Collapse
 
brightone profile image
Oleksii Filonenko

Rust:

enum Piece {
    Archer,
    Swordsman,
    Pikeman,
    Cavalry,
}

fn fight_resolve<'a>(attacker: &'a Piece, defender: &'a Piece) -> &'a Piece {
    use Piece::*;

    match (attacker, defender) {
        (Swordsman, Archer) | (Pikeman, Swordsman) | (Cavalry, Pikeman) | (Archer, Cavalry) => {
            defender
        }
        (attacker, _) => attacker,
    }
}
Collapse
 
ynndvn profile image
La blatte

How bout some G O L F

f=(a,d)=>(t='ASPC',(t.indexOf(d[0])-t.indexOf(a[0])+1)%4?a:d)

If I understood the problem correctly, the defender only wins if it is the "previous" element on the list. Hence, I just store a string with the initials in the correct order, check the initial index. If the defender is just before the attacker, it wins.

r=['Archers', 'Swordsmen', 'Pikemen', 'Cavalry'];
s='',r.forEach(e=>r.forEach(k=>s+=`${e} VS ${k}: ${f(e,k)}\n`)),s 


"Archers VS Archers: Archers
Archers VS Swordsmen: Archers
Archers VS Pikemen: Archers
Archers VS Cavalry: Cavalry
Swordsmen VS Archers: Archers
Swordsmen VS Swordsmen: Swordsmen
Swordsmen VS Pikemen: Swordsmen
Swordsmen VS Cavalry: Swordsmen
Pikemen VS Archers: Pikemen
Pikemen VS Swordsmen: Swordsmen
Pikemen VS Pikemen: Pikemen
Pikemen VS Cavalry: Pikemen
Cavalry VS Archers: Cavalry
Cavalry VS Swordsmen: Cavalry
Cavalry VS Pikemen: Pikemen
Cavalry VS Cavalry: Cavalry
"
Collapse
 
chrisachard profile image
Chris Achard

I put a front end on it with React:

I kept the actual method super simple though with a bunch of if statements! Though there are a whole bunch of more clever ways to do it :)

Collapse
 
hanachin profile image
Seiei Miyagi

ruby <3

Swordsman, Cavalry, Archer, Pikeman = :swardsman, :cavalry, :archer, :pikeman

def fightResolve(attacking_piece, defending_piece)
  case [attacking_piece, defending_piece]
  when [Swordsman, Archer],
       [Pikeman, Swordsman],
       [Cavalry, Pikeman],
       [Archer, Cavalry]
    defending_piece
  else
    attacking_piece
  end
end
Collapse
 
vivek97 profile image
Vivek97

private static Map pair = new HashMap<>();

void challange_43()
{
    //Archers > Swordsmen > Pikemen > Cavalry > Archers
    pair.put("Archers","Swordsmen");
    pair.put("Swordsmen","Pikemen");
    pair.put("Pikemen","Cavalry");
    pair.put("Cavalry","Archers");
}

public static void main(String[] args) {

    Challange_43 object = new Challange_43();
    System.out.println("Winner between Pikemen, Cavalry is "+object.getWinner("Pikemen","Cavalry"));
    System.out.println("Winner between Archers Swordsmen is "+object.getWinner("Archers","Swordsmen"));
    System.out.println("Winner between Archers, Cavalry is "+object.getWinner("Archers","Cavalry"));


}

public String getWinner(String attacker, String defender)
{

    if(pair.containsKey(defender)&& pair.get(defender).equals(attacker))
        return defender;    
    else
        return attacker;    
}
Collapse
 
thepeoplesbourgeois profile image
Josh • Edited

Hmm...

There's never any reason to have certain pieces attack certain other pieces, then. What's the catch? Is it like Stratego where it's unclear what kind of piece you're attacking until after you make the attempt? Is there possibly a hit-point calculation, like in Civilization? Or do certain conditions take effect after certain outcomes, e.g., if you sacrifice a piece to a situation where it will lose, the other piece must advance to the free square as your opponent's next move?

just curious... I mean, there's gotta be some reason for the attacker to either be unable to anticipate that the defender will win, or some reason that attacking certain pieces in an otherwise known-losing situation would wind up not being a total bum deal for the player initiating the move 😅

Collapse
 
vivek97 profile image
Vivek97 • Edited

private static Map pair = new HashMap<>();

void challange_43()
{
    //Archers > Swordsmen > Pikemen > Cavalry > Archers
    pair.put("Archers","Swordsmen");
    pair.put("Swordsmen","Pikemen");
    pair.put("Pikemen","Cavalry");
    pair.put("Cavalry","Archers");
}

public static void main(String[] args) {

    Challange_43 object = new Challange_43();
    System.out.println("Winner between Pikemen, Cavalry is "+object.getWinner("Pikemen","Cavalry"));
    System.out.println("Winner between Archers Swordsmen is "+object.getWinner("Archers","Swordsmen"));
    System.out.println("Winner between Archers, Cavalry is "+object.getWinner("Archers","Cavalry"));


}

public String getWinner(String attacker, String defender)
{

    if(pair.containsKey(defender)&& pair.get(defender).equals(attacker))
        return defender;    
    else
        return attacker;    
}