added hunger and graph
This commit is contained in:
179
chatgpt.exs
179
chatgpt.exs
@@ -1,88 +1,163 @@
|
||||
# Installer Jason pour JSON si besoin
|
||||
Mix.install([
|
||||
{:jason, "~> 1.4"}
|
||||
{:jason, "~> 1.4"},
|
||||
{:matplotex, "~> 0.4.71"}
|
||||
])
|
||||
|
||||
defmodule ZombieApocalypse do
|
||||
@moduledoc """
|
||||
Simulation zombie complète.
|
||||
|
||||
- Chaque zombie a un niveau de faim.
|
||||
- Faim augmente de 5 chaque heure.
|
||||
- Si un zombie mange un humain, sa faim revient à 0 et l'humain devient un zombie.
|
||||
- Certains zombies peuvent mourir.
|
||||
- Sauvegarde JSON à chaque heure.
|
||||
- Affichage dans le terminal avec pourcentages.
|
||||
- Génération graphique final avec Matplotex.
|
||||
"""
|
||||
|
||||
@state_file "states.json"
|
||||
@final_file "states_final.json"
|
||||
|
||||
# =====================================
|
||||
# Démarrage de la simulation
|
||||
def start(humans \\ 10, zombies \\ 3, max_hours \\ 200) do
|
||||
# Vider le fichier temporaire
|
||||
File.write!(@state_file, "")
|
||||
# =====================================
|
||||
def start(humans \\ 10, zombies \\ 3, max_hours \\ 100) do
|
||||
File.write!(@state_file, "[]")
|
||||
IO.puts("🧟 Lancement de la simulation...")
|
||||
|
||||
IO.puts("🧟 Lancement de la simulation Zombie Apocalypse !")
|
||||
zombie_list = for _ <- 1..max(zombies, 0), do: 0
|
||||
|
||||
# Ouvrir le fichier en continu pour append rapide
|
||||
{:ok, file} = File.open(@state_file, [:write, :utf8])
|
||||
loop(0, humans, zombie_list, max_hours, humans, zombies)
|
||||
|
||||
# Boucle principale
|
||||
loop(0, humans, zombies, max_hours, file)
|
||||
|
||||
# Fermer le fichier
|
||||
File.close(file)
|
||||
|
||||
# Générer JSON final
|
||||
finalize_json()
|
||||
# Génération graphique
|
||||
plot_final()
|
||||
IO.puts("✅ Simulation terminée. JSON et graphique générés (zombie_final.png).")
|
||||
end
|
||||
|
||||
# =====================================
|
||||
# Boucle principale
|
||||
defp loop(hour, humans, zombies, max_hours, file) when hour < max_hours do
|
||||
# =====================================
|
||||
defp loop(hour, humans, zombie_list, max_hours, initial_humans, initial_zombies)
|
||||
when hour < max_hours do
|
||||
state = %{
|
||||
hour: hour,
|
||||
humans: humans,
|
||||
zombies: zombies,
|
||||
zombies: length(zombie_list),
|
||||
timestamp: DateTime.utc_now()
|
||||
}
|
||||
|
||||
# Append rapide
|
||||
IO.write(file, Jason.encode!(state) <> "\n")
|
||||
append_state(state)
|
||||
|
||||
# Afficher seulement toutes les 10 heures
|
||||
if rem(hour, 10) == 0 do
|
||||
IO.puts("Heure #{hour} | Humains: #{humans} | Zombies: #{zombies}")
|
||||
end
|
||||
percent_humans =
|
||||
if initial_humans > 0, do: humans / initial_humans * 100, else: 0
|
||||
|
||||
percent_zombies =
|
||||
if initial_zombies > 0, do: length(zombie_list) / initial_zombies * 100, else: 0
|
||||
|
||||
IO.puts(
|
||||
"Heure #{hour} : Humains = #{humans} (#{Float.round(percent_humans, 1)}%), " <>
|
||||
"Zombies = #{length(zombie_list)} (#{Float.round(percent_zombies, 1)}%)"
|
||||
)
|
||||
|
||||
cond do
|
||||
humans <= 0 ->
|
||||
IO.puts("💀 Tous les humains ont été infectés.")
|
||||
|
||||
zombies <= 0 ->
|
||||
IO.puts("🎉 Tous les zombies ont été éliminés.")
|
||||
humans <= 0 or length(zombie_list) == 0 ->
|
||||
{humans, zombie_list}
|
||||
|
||||
true ->
|
||||
{new_humans, new_zombies} = simulate_hour(humans, zombies)
|
||||
loop(hour + 1, new_humans, new_zombies, max_hours, file)
|
||||
{new_humans, new_zombies} = simulate_hour(humans, zombie_list)
|
||||
loop(hour + 1, new_humans, new_zombies, max_hours, initial_humans, initial_zombies)
|
||||
end
|
||||
end
|
||||
|
||||
defp loop(_hour, _humans, _zombies, _max_hours, _file), do: :ok
|
||||
defp loop(_, humans, zombie_list, _, _, _), do: {humans, zombie_list}
|
||||
|
||||
# Simulation d'une heure (calcul probabiliste)
|
||||
defp simulate_hour(humans, zombies) do
|
||||
attacks = zombies
|
||||
# Humains infectés : chaque attaque a 40% de chance
|
||||
humans_lost = Enum.count(1..attacks, fn _ -> :rand.uniform(100) <= 40 end)
|
||||
# Zombies tués : chaque attaque a 15% de chance
|
||||
zombies_lost = Enum.count(1..attacks, fn _ -> :rand.uniform(100) <= 15 end)
|
||||
# =====================================
|
||||
# Simulation d'une heure
|
||||
# =====================================
|
||||
defp simulate_hour(humans, zombie_list) do
|
||||
new_zombie_list = Enum.map(zombie_list, &(&1 + 5))
|
||||
|
||||
humans = max(humans - humans_lost, 0)
|
||||
zombies = max(zombies - zombies_lost + humans_lost, 0) # infections
|
||||
{humans, zombies}
|
||||
{remaining_humans, zombies_after_hour} =
|
||||
Enum.reduce(new_zombie_list, {humans, []}, fn hunger, {hum_left, acc} ->
|
||||
if hum_left > 0 and :rand.uniform() <= 0.4 + hunger / 100 do
|
||||
# zombie mange humain → nouveau zombie
|
||||
{hum_left - 1, [0, 0 | acc]}
|
||||
else
|
||||
{hum_left, [hunger | acc]}
|
||||
end
|
||||
end)
|
||||
|
||||
# Certains zombies peuvent mourir
|
||||
killed = Enum.count(zombies_after_hour, fn _ -> :rand.uniform() <= 0.15 end)
|
||||
final_zombies = Enum.drop(zombies_after_hour, killed)
|
||||
|
||||
{remaining_humans, final_zombies}
|
||||
end
|
||||
|
||||
# Générer le JSON final complet
|
||||
defp finalize_json do
|
||||
# =====================================
|
||||
# Sauvegarde JSON
|
||||
# =====================================
|
||||
defp append_state(state) do
|
||||
states =
|
||||
File.stream!(@state_file)
|
||||
|> Stream.map(&Jason.decode!/1)
|
||||
|> Enum.to_list()
|
||||
File.read!(@state_file)
|
||||
|> Jason.decode!()
|
||||
|
||||
File.write!(@final_file, Jason.encode!(states, pretty: true))
|
||||
IO.puts("✅ JSON final créé : #{@final_file}")
|
||||
File.write!(@state_file, Jason.encode!(states ++ [state], pretty: false))
|
||||
end
|
||||
|
||||
# =====================================
|
||||
# Graphique final
|
||||
# =====================================
|
||||
defp plot_final do
|
||||
states =
|
||||
File.read!(@state_file)
|
||||
|> Jason.decode!()
|
||||
|
||||
humans = Enum.map(states, & &1["humans"]) |> Enum.filter(&is_number/1)
|
||||
zombies = Enum.map(states, & &1["zombies"]) |> Enum.filter(&is_number/1)
|
||||
hours = Enum.map(states, & &1["hour"]) |> Enum.filter(&is_number/1)
|
||||
|
||||
if length(hours) > 0 and length(humans) == length(hours) and length(zombies) == length(hours) do
|
||||
infection_rate =
|
||||
if length(humans) >= 2 do
|
||||
humans
|
||||
|> Enum.chunk_every(2, 1, :discard)
|
||||
|> Enum.map(fn [prev, next] -> prev - next end)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
inf_hours = Enum.drop(hours, 1)
|
||||
|
||||
fig = Matplotex.plot(hours, humans, color: "blue", label: "Humains")
|
||||
fig = Matplotex.plot(fig, hours, zombies, color: "red", label: "Zombies")
|
||||
|
||||
if infection_rate != [] and length(inf_hours) == length(infection_rate) do
|
||||
fig =
|
||||
Matplotex.plot(fig, inf_hours, infection_rate,
|
||||
color: "green",
|
||||
label: "Vélocité Infection"
|
||||
)
|
||||
end
|
||||
|
||||
fig_opts = %{figsize: {8, 5}}
|
||||
fig = Matplotex.figure(fig, fig_opts)
|
||||
fig = Matplotex.set_title(fig, "Simulation Zombie Apocalypse")
|
||||
fig = Matplotex.set_xlabel(fig, "Heure")
|
||||
fig = Matplotex.set_ylabel(fig, "Population / Infection rate")
|
||||
|
||||
svg = Matplotex.show(fig)
|
||||
File.write!("zombie_final.svg", svg)
|
||||
System.cmd("rsvg-convert", ["zombie_final.svg", "-o", "zombie_final.png"])
|
||||
|
||||
IO.puts("🖼️ Graphique généré : zombie_final.png")
|
||||
else
|
||||
IO.puts("⚠️ Données insuffisantes pour tracer le graphique.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Lancer la simulation
|
||||
ZombieApocalypse.start(7000000000, 3, 200)
|
||||
# =====================================
|
||||
# Lancement
|
||||
# =====================================
|
||||
ZombieApocalypse.start(10_000_000, 3, 100)
|
||||
|
||||
Reference in New Issue
Block a user