Consider the following scenario...
There is an aquarium which houses three kinds of fish:
The Blowfish however become poisonous to eat after ingesting Jellyfish resulting in certain death for its predators.
Which type of fish is most likely to be the last survivor in the aquarium?
import random
import matplotlib.pyplot as plt
class Fish:
def __init__(self, name, is_deadly=False):
self.name = name
self.is_deadly = is_deadly
class Shark(Fish):
def __init__(self):
super().__init__("Shark")
def eat(self, prey):
if prey.is_deadly is True:
print(f"The shark ate a {prey.name} but it was poisonous!")
else:
print(f"The shark ate a {prey.name}!")
class Blowfish(Fish):
def __init__(self):
super().__init__("Blowfish")
def eat(self, prey):
print(f"The blowfish ate a {prey.name}!")
prey.is_deadly = True
class Jellyfish(Fish):
def __init__(self):
super().__init__("Jellyfish")
# Create an empty aquarium
aquarium = []
# Set initial number of each type of fish
num_sharks = 3
num_blowfish = 6
num_jellyfish = 10
# Add the specified number of each fish to the aquarium
for _ in range(num_sharks):
aquarium.append(Shark())
for _ in range(num_blowfish):
aquarium.append(Blowfish())
for _ in range(num_jellyfish):
aquarium.append(Jellyfish())
# Shuffle the aquarium to randomize fish order
random.shuffle(aquarium)
plt.scatter(["Sharks", "Blowfish", "Jellyfish"], [num_sharks, num_blowfish, num_jellyfish])
# Simulation loop
while len((set(map(type, aquarium)))) > 1:
for i in range(len(aquarium)):
try:
fish = aquarium[i]
# Find a random fish to eat
prey_index = random.randint(0, len(aquarium) - 1)
prey = aquarium[prey_index]
if fish.name == "Shark" and (prey.name == "Blowfish" or prey.name == "Jellyfish"):
fish.eat(prey)
if prey.is_deadly is True:
aquarium = [fish for idx, fish in enumerate(aquarium) if idx not in {i, prey_index}]
else:
aquarium.pop(prey_index)
elif fish.name == "Blowfish" and prey.name == "Jellyfish":
fish.eat(prey)
fish.is_deadly = True
aquarium.pop(prey_index)
num_sharks = sum(1 for fish in aquarium if fish.name == "Shark")
num_blowfish = sum(1 for fish in aquarium if fish.name == "Blowfish")
num_jellyfish = sum(1 for fish in aquarium if fish.name == "Jellyfish")
plt.scatter(["Sharks", "Blowfish", "Jellyfish"], [num_sharks, num_blowfish, num_jellyfish], marker='o', color='blue')
plt.ylabel("Population")
except:
continue
# Display the final state of the aquarium
try:
print(f"The last fish in the aquarium is a {aquarium[0].name}.")
except:
print("No fish are alive in the aquarium.")
The blowfish ate a Jellyfish! The blowfish ate a Jellyfish! The blowfish ate a Jellyfish! The blowfish ate a Jellyfish! The shark ate a Jellyfish! The blowfish ate a Jellyfish! The blowfish ate a Jellyfish! The shark ate a Blowfish but it was poisonous! The shark ate a Jellyfish! The blowfish ate a Jellyfish! The shark ate a Blowfish but it was poisonous! The shark ate a Blowfish but it was poisonous! The blowfish ate a Jellyfish! The last fish in the aquarium is a Blowfish.
In truth, I was exposed to this problem through a series of induction based puzzles and an additional piece of information was provided which is key to answering the examined question. That is namely the initial frequency of each type of fish. If we take for example the fact that there are not many Sharks, more Blowfish and and abundance of Jellyfish, the most logical outcome is that the Sharks will be split between eating Jellyfish (which are much more abundant) and Blowfish, which in comparison will default to eating Jellyfish (and become poisonous in the process). As the proportion of poisonous Blowfish increases until there are equal or more poisonous Blowfish relative to Sharks, the probability of Blowfish becoming the surviving species converges to 1. Interestingly, there is one particular scenario in which Jellyfish emerge as the final victors, in the case that all Blowfish have become poisonous and are equal in population to Sharks. The Hail Mary for our tentacled friends lies in the Sharks hunting the Blowfish down to extinction, incidentally resulting in their own demise simultaneously.
The initial proportions of each fish may be modulated in the above code - you will find that although the simulated randomness may result in occasional surprises, the expected outcomes fall in line with the provided explanation. Personally, I am very drawn to this type of puzzle which approximates very simple feedback mechanisms between agents operating in a random space. In this example, there is such a delicate interplay between these three sub-groups and the extinction event of one could prove catastrophic for the other. I can't help but think of humans playing the role of the sharks in the context of a much bigger 'aquarium'. One scenario I have not yet elucidated takes the following form: if the Sharks and the Blowfish hunt the Jellyfish down to extinction leaving an equal proportion of Sharks and poisonous Blowfish - the Sharks will now point their dominance towards the last remaining lifeform resulting in a scenario where no life subsists in our aquarium. Maybe if we modified the behaviour of our simulated agents to consume merely what they needed, a sort of harmonious compromise could ensue. But that would be too simple, now wouldn't it...