some tweaks

This commit is contained in:
Kodi Craft 2025-11-09 14:32:59 +01:00
parent 31e179a74a
commit c213507aec
Signed by: kodi
GPG Key ID: 69D9EED60B242822
3 changed files with 47 additions and 25 deletions

View File

@ -68,14 +68,17 @@ defmodule Diamondtail.Brain do
else else
{id, s} {id, s}
end end
end) end) |> Map.new()
end) end)
|> resolve_state() |> resolve_state()
end end
def valid_actions(%GameState{type: :v, snakes: snakes, self: self}) do def valid_actions(%GameState{type: :v, snakes: snakes, self: self} = state) do
if Map.has_key?(snakes, self) do if Map.has_key?(snakes, self) do
[:up, :down, :left, :right] [:up, :down, :left, :right] |> Enum.filter(fn action ->
result = apply_actions(state, action)
!snake_dies?(result, self)
end)
else else
[] []
end end
@ -102,7 +105,9 @@ defmodule Diamondtail.Brain do
Enum.count(enemies) * genome.enemy_alive_weight + Enum.count(enemies) * genome.enemy_alive_weight +
self.health * genome.own_health_weight + self.health * genome.own_health_weight +
self.length * genome.own_length_weight +
Enum.sum_by(enemies, &(&1.health)) * genome.enemy_health_weight + Enum.sum_by(enemies, &(&1.health)) * genome.enemy_health_weight +
(Enum.sum_by(enemies, &(&1.length)) / (Enum.count(enemies) + 1)) * genome.enemy_length_weight +
(Enum.map(enemies, &(abs(&1.head.x - self.head.x) + abs(&1.head.y - self.head.y))) |> Enum.min(&<=/2, fn -> 0 end)) * genome.enemy_head_distance_weight + (Enum.map(enemies, &(abs(&1.head.x - self.head.x) + abs(&1.head.y - self.head.y))) |> Enum.min(&<=/2, fn -> 0 end)) * genome.enemy_head_distance_weight +
(Enum.map(food, &(abs(&1.x - self.head.x) + abs(&1.y - self.head.y))) |> Enum.min(&<=/2, fn -> 0 end)) * genome.food_distance_weight (Enum.map(food, &(abs(&1.x - self.head.x) + abs(&1.y - self.head.y))) |> Enum.min(&<=/2, fn -> 0 end)) * genome.food_distance_weight
else else
@ -124,10 +129,10 @@ defmodule Diamondtail.Brain do
valid_actions = valid_actions(state) valid_actions = valid_actions(state)
if valid_actions == [] do if valid_actions == [] do
if Map.has_key?(state.snakes, state.self) do # Terminal state
Float.max_finite() case state.type do
else :v -> Float.min_finite()
Float.min_finite() :q -> Float.max_finite()
end end
else else
{score, _alpha, _beta} = valid_actions(state) {score, _alpha, _beta} = valid_actions(state)
@ -174,20 +179,26 @@ defmodule Diamondtail.Brain do
end) end)
# Eliminate snakes # Eliminate snakes
|> Map.update(:snakes, nil, fn snakes -> |> Map.update(:snakes, nil, fn snakes ->
Enum.filter(snakes, fn {snake_id, snake} -> Enum.filter(snakes, fn {snake_id, _snake} ->
# Die if health is less than or equal to 0 !snake_dies?(state, snake_id)
snake.health > 0 &&
# Die if head is out of bounds
snake.head.x >= 0 && snake.head.x < state.width &&
snake.head.y >= 0 && snake.head.y < state.height &&
# Die if head is in a tail
!Enum.any?(state.snakes, fn {_, %{body: body}} -> Enum.member?(body, snake.head) end) &&
# Die if head is on head of a DIFFERENT snake of equal or greater length
!Enum.any?(state.snakes, fn {id, %{head: h, length: l}} -> snake.head == h && snake.length <= l && id != snake_id end)
end) |> Map.new() end) |> Map.new()
end) end)
end end
defp snake_dies?(state, snake_id) do
snake = state.snakes[snake_id]
# Die if health is less than or equal to 0
snake.health <= 0 ||
# Die if head is out of bounds
snake.head.x < 0 || snake.head.x >= state.width ||
snake.head.y < 0 || snake.head.y >= state.height ||
# Die if head is in a tail
Enum.any?(state.snakes, fn {_, %{body: body}} -> Enum.member?(body, snake.head) end) ||
# Die if head is on head of a DIFFERENT snake of equal or greater length
Enum.any?(state.snakes, fn {id, %{head: h, length: l}} -> snake.head == h && snake.length <= l && id != snake_id end)
end
defp apply_action_to_snake(snake, move) do defp apply_action_to_snake(snake, move) do
delta = case move do delta = case move do
:up -> %{x: 0, y: 1} :up -> %{x: 0, y: 1}
@ -211,11 +222,18 @@ defmodule Diamondtail.Brain do
{action, GameState.evaluate_state(genome, qstate, ceil(genome.search_depth))} {action, GameState.evaluate_state(genome, qstate, ceil(genome.search_depth))}
end, ordered: false) end, ordered: false)
{:ok, {action, score}} = Enum.max_by(options, fn {:ok, {_, s}} -> s end) if Enum.empty?(options) do
%{
move: :left,
shout: "I'm dead, good game!"
}
else
{:ok, {action, score}} = Enum.max_by(options, fn {:ok, {_, s}} -> s end)
%{ %{
move: action, move: action,
shout: "#{inspect(options |> Enum.map(fn {:ok, v} -> v end) |> Enum.to_list)} (#{score})" shout: "#{inspect(options |> Enum.map(fn {:ok, v} -> v end) |> Enum.to_list)} (#{score})"
} }
end
end end
end end

View File

@ -16,7 +16,9 @@ defmodule Diamondtail.Population do
field :enemy_alive_weight, float() field :enemy_alive_weight, float()
field :own_health_weight, float() field :own_health_weight, float()
field :own_length_weight, float()
field :enemy_health_weight, float() field :enemy_health_weight, float()
field :enemy_length_weight, float()
field :enemy_head_distance_weight, float() field :enemy_head_distance_weight, float()
field :food_distance_weight, float() field :food_distance_weight, float()
@ -57,10 +59,12 @@ defmodule Diamondtail.Population do
def random() do def random() do
%Genome{ %Genome{
mutation_chance: random_between(0.1, 0.2), mutation_chance: random_between(0.1, 0.2),
mutation_max_amplitude: random_between(0.1, 0.05), mutation_max_amplitude: random_between(0.2, 0.05),
enemy_alive_weight: random_between(-100.0, -200.0), enemy_alive_weight: random_between(-100.0, -200.0),
own_health_weight: random_between(0.7, 1.0), own_health_weight: random_between(0.7, 1.0),
enemy_health_weight: random_between(-0.8, -0.4), own_length_weight: random_between(5, 10),
enemy_health_weight: random_between(-0.8, -0.4), # Summed
enemy_length_weight: random_between(-10, -5), # Averaged
# both of the following are compared to the nearest object, to avoid overrewarding when there are more enemies/foods # both of the following are compared to the nearest object, to avoid overrewarding when there are more enemies/foods
enemy_head_distance_weight: random_between(1, 2), # maximize distance, reward staying away enemy_head_distance_weight: random_between(1, 2), # maximize distance, reward staying away

View File

@ -18,7 +18,7 @@ defmodule Diamondtail.Router do
|> send_resp(200, JSON.encode!(%{ |> send_resp(200, JSON.encode!(%{
apiversion: "1", apiversion: "1",
author: "kodicraft", author: "kodicraft",
color: "#8cd9ff", color: "#4684ff",
head: "trans-rights-scarf", head: "trans-rights-scarf",
tail: "hook", tail: "hook",
version: Mix.Project.config()[:version] version: Mix.Project.config()[:version]