loading...

Daily Challenge #192 - Can you Survive the Zombies?

thepracticaldev profile image dev.to staff ・2 min read

Setup

Unfortunately, you have found yourself in a difficult situation. You have injured your leg and are unable to walk. A number n of zombies are shuffling towards you, intent on eating your brains. Luckily, you've got your trusty rifle.

The zombies will start at a range of r meters. They will move at 0.5m per second. Each second, you shoot one zombie, which depletes your ammo reserve a by 1 each shot. The remaining zombies shamble forward. Since you're a good shot but also debilitated, there's a 5% chance you might miss.

If any of the zombies manage to reach 0 meters, you get eaten and lose. If you run out of ammo, you'll also get eaten. Ignore any time that would be spent reloading.

Write a function that accepts the total number of zombies n, a range in meters r, and the number of bullets you have a.

If you shoot all the zombies, return "You shot all X zombies." If you get eaten before killing all the zombies, and before running out of ammo, return "You shot X zombies before being eaten: overwhelmed." If you run out of ammo before shooting all the zombies, return "You shot X zombies before being eaten: ran out of ammo." (If you run out of ammo at the same time as the remaining zombies reach you, return "You shot X zombies before being eaten: overwhelmed.".)

Example

zombie_shootout(3, 10, 10) => "You shot all 3 zombies."

Tests

zombie_shootout(100, 8, 200)
zombie_shootout(50, 10, 8)

Good luck! (I think you're going to need it.)


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

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

Discussion

pic
Editor guide
Collapse
savagepixie profile image
SavagePixie

Something like this should do the trick in Elixir. I'm just doubling the range and subtracting one on each iteration to avoid issues with floats and because working with integers is faster.

defmodule ZombieWars do
  def zombie_shootout(n, r, a), do: _zombie_shootout(n, r * 2, a, n)

  defp _zombie_shootout(0, _, _, s) do
    "You shot all #{s} zombies."
  end
  defp _zombie_shootout(n, 0, _a, s) do
    "You shot #{s - n} zombies before being eaten: overwhelmed."
  end
  defp _zombie_shootout(n, _r, 0, s) do
    "You shot #{s - n} zombies before being eaten: ran out of ammo."
  end
  defp _zombie_shootout(n, r, a, s) do
    if Enum.random(1..20) == 1 do
      _zombie_shootout(n, r - 1, a - 1, s)
    else
      _zombie_shootout(n - 1, r - 1, a - 1, s)
    end
  end
end
Collapse
craigmc08 profile image
Craig McIlwrath

The 5% is throwing me off. There's no way to compute a certain live/die verdict when a probability. A singular zombie could be 2000 away and you get really unlucky and miss all shots.

Maybe you mean every 20th shot you miss? Or create an impure function that returns a different value depending on random chance from the probability? Or something else?

Collapse
aminnairi profile image
Amin

I believe it would mean something like that in Haskell (I'm not really confortable enough with Monad so there is room to improvements).

import System.Random (getStdGen, randomR)

hasHit :: IO Bool
hasHit = do
    g <- getStdGen
    return $ (>5) $ fst $ randomR (1 :: Int, 100) g

main :: IO ()
main = do
    h <- hasHit
    print $ h -- True/False

And I would use this on each shot. Meaning I have 5/100 chance of missing my target. That is what I understood but I may be wrong on this one.

Collapse
kerldev profile image
Kyle Jones

This was the problem that I couldn't work out either - the tests become flaky and unreliable. Seems at odds with the rest of the scenario

Collapse
candidateplanet profile image
lusen / they / them 🏳️‍🌈🥑

Python, where of course the expected number of zombies killed is a maximum and sometimes the function says less were killed.

import random

# n: number of zombies
# r: range in meters
# a: number of bullets
def zombie_shootout(n, r, a):
  zombie_speed = 0.5 # m/s
  bullets_per_second = 1 # bullet/s
  miss_rate = .05 # %

  zombie_distance = r
  number_zombies = n
  number_bullets = a

  while True:
    #print(zombie_distance, number_zombies, number_bullets)
    # if shoot all zombies:
    if number_zombies <= 0:
      return "You shot all %s zombies." % n

    # if get eaten:
    if zombie_distance <= 0.0:
      return "You shot %s zombies before being eaten: overwhelmed." % (n - number_zombies)

    # if run out of ammo:
    if number_bullets <=  0:
      return "You shot %s zombies before being eaten: ran out of ammo." % (n - number_zombies)

    zombie_distance -= zombie_speed
    number_bullets -= bullets_per_second

    zombie_killed = 1
    if random.randint(1,100) > 95:
      zombie_killed = 0
    number_zombies -= zombie_killed

print(zombie_shootout(3, 10, 10), "\nYou shot all 3 zombies.\n")
print(zombie_shootout(3, 3, 3), "\nYou shot all 3 zombies.\n")
print(zombie_shootout(3, 4, 4), "\nYou shot all 3 zombies.\n")
print(zombie_shootout(10, 2, 30), "\nYou shot 4 zombies before being eaten: overwhelmed.\n")
print(zombie_shootout(10, 30, 4), "\nYou shot 4 zombies before being eaten: ran out of ammo.\n")
Collapse
kerldev profile image
Kyle Jones

In Python

import sys
import random


def zombie_shootout(num_of_zombies, distance, num_of_bullets):
    '''
    Work out if you survive the zombie's assault
    '''
    zombie_speed = 0.5
    num_of_zombies_shot = 0
    probability_of_missing = 0.05
    num_of_zombies_remaining = num_of_zombies

    for bullet in range(num_of_bullets):
        if distance == 0:
            return "You shot {} zombie(s) before being eaten: overwhelmed.".format(
                num_of_zombies_shot)
        if num_of_zombies_remaining == 0:
            return "You shot all {} zombies.".format(
                num_of_zombies)

        distance -= zombie_speed
        shot_chance = random.randint(0, 100)

        if shot_chance >= probability_of_missing:
            num_of_zombies_shot += 1
            num_of_zombies_remaining -= 1
    return "You shot {} zombie(s) before being eaten: ran out of ammo.".format(
        num_of_zombies_shot)


if len(sys.argv) > 3:
    print(zombie_shootout(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])))

Collapse
allgunblazin profile image
R I P 3 F R A M E S

here's my solution in Python

from random import choices

def zombie_shootout(zombies, distance, ammo):
    population = [0,1]
    distribution = [0.05, 0.95]
    zombies_killed = 0

    for i in range(distance*2):
        if ammo == 0 and zombies_killed < zombies:
            print("You killed %d zombies before being eaten: out of ammo" % zombies_killed)
            return

        if zombies_killed == zombies:
            print("You killed %d zombies and survived" % zombies_killed)
            return
        hit = choices(population, distribution)
        if not hit:
            ammo -= 1
            print(hit)
        else:
            ammo -= 1
            zombies_killed += 1

    if zombies_killed < zombies:
        print("You killed %d zombies before being eaten: overwhelmed" % zombies_killed)
Collapse
vidit1999 profile image
Vidit Sarkar

C++

// takes number of bullets and percentage of missing as input
// i.e. 5 for 5% chance of missing
// this functions guarantees that the given percentage of total bullets will miss zombies
vector<bool> hitOrMiss(int numBullets,int percentage){
    vector<bool> hits(numBullets,true);
    for(int i=0;i<numBullets*percentage/100;i++){
        hits[i] = false;
    }
    shuffle(hits.begin(),hits.end(),default_random_engine(time(0)));
    return hits;
}

void zombie_shootout(int totalZombie, float range, int numBullets){
    int tempTotalZombie = totalZombie; // holds the total number of zombies
    int tempNumBullets = numBullets; // holds the total number of bullets
    vector<bool> hits = hitOrMiss(numBullets,5);

    while(numBullets>0 && totalZombie > 0 && range > 0){
        if(hits[numBullets-1]){
            totalZombie--;
        }
        numBullets--;
        if(totalZombie==0)
            break;
        range -= 0.5;
    }

    // If you shoot all the zombies
    if(totalZombie == 0 && range > 0 && numBullets >= 0){
        cout << "You shot all " << tempTotalZombie << " zombies.\n";
        return;
    }

    // If you get eaten before killing all the zombies, and before running out of ammo
    // or If you run out of ammo at the same time as the remaining zombies reach you
    if(range == 0 && totalZombie > 0 && numBullets >= 0){
        cout << "You shot " << tempTotalZombie-totalZombie << " zombies before being eaten: overwhelmed.\n";
        return;
    }

    // If you run out of ammo before shooting all the zombies,
    if(numBullets == 0 && totalZombie > 0){
        cout << "You shot "<< tempTotalZombie-totalZombie <<" zombies before being eaten: ran out of ammo.\n";
        return;
    }
}
Collapse
nijeesh4all profile image
Nijeesh Joshy
MISS_PROBABILITY = 5


def hit?
  rand(1..100) > MISS_PROBABILITY
end

def all_zombies_dead_message(killed)
  "You shot all #{killed} zombies."
end

def got_eaten_message(killed)
  "You shot #{killed} zombies before being eaten: overwhelmed."
end

def ran_out_of_ammo_message(killed)
  "You shot #{killed} zombies before being eaten: ran out of ammo."
end

def zombie_shootout(n, r, a)
  ran_out_of_ammo_message(0) if a < n
  zombies_killed = 0
  (r * 2).times do
    return all_zombies_dead_message(zombies_killed) if n == zombies_killed
    return ran_out_of_ammo_message(zombies_killed) if a == 0
    a -= 1
    if hit?
      zombies_killed += 1
    end
  end
  return got_eaten_message(zombies_killed) if zombies_killed < n
end


puts zombie_shootout(3, 10, 10)
puts zombie_shootout(100, 8, 200)
puts zombie_shootout(50, 10, 8)
Collapse
maskedman99 profile image
Rohit Prasad

Python

import random

n = int(input('Enter the number of Zombies: '))
r = float(input('Enter the range: '))
a = int(input('Enter the number of ammo: '))

def zombie_shootout(n,r,a):
        n1 = n
        for i in range(a):
                if n == 0:
                        return "You shot all "+str(n1)+" zombies."
                if r == 0:
                        return "You shot "+str(n1-n)+" zombies before being eaten: overwhelmed."
                if a == 0:
                        return "You shot "+str(n1-n)+" zombies before being eaten: ran out of ammo."

                x = random.randint(1,100)
                if x <= 5:                      # Missed the shot
                        a -= 1
                        r -= 0.5
                else:                           # Zombie Killed
                        a -= 1
                        n -= 1
                        r -= 0.5

        return "You shot "+str(n1-n)+" zombies before being eaten: overwhelmed."

print(zombie_shootout(n,r,a))