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
{id, s}
end
end)
end) |> Map.new()
end)
|> resolve_state()
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
[:up, :down, :left, :right]
[:up, :down, :left, :right] |> Enum.filter(fn action ->
result = apply_actions(state, action)
!snake_dies?(result, self)
end)
else
[]
end
@ -102,7 +105,9 @@ defmodule Diamondtail.Brain do
Enum.count(enemies) * genome.enemy_alive_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.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(food, &(abs(&1.x - self.head.x) + abs(&1.y - self.head.y))) |> Enum.min(&<=/2, fn -> 0 end)) * genome.food_distance_weight
else
@ -124,10 +129,10 @@ defmodule Diamondtail.Brain do
valid_actions = valid_actions(state)
if valid_actions == [] do
if Map.has_key?(state.snakes, state.self) do
Float.max_finite()
else
Float.min_finite()
# Terminal state
case state.type do
:v -> Float.min_finite()
:q -> Float.max_finite()
end
else
{score, _alpha, _beta} = valid_actions(state)
@ -174,20 +179,26 @@ defmodule Diamondtail.Brain do
end)
# Eliminate snakes
|> Map.update(:snakes, nil, fn snakes ->
Enum.filter(snakes, fn {snake_id, snake} ->
# 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)
Enum.filter(snakes, fn {snake_id, _snake} ->
!snake_dies?(state, snake_id)
end) |> Map.new()
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
delta = case move do
:up -> %{x: 0, y: 1}
@ -211,6 +222,12 @@ defmodule Diamondtail.Brain do
{action, GameState.evaluate_state(genome, qstate, ceil(genome.search_depth))}
end, ordered: false)
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)
%{
@ -219,3 +236,4 @@ defmodule Diamondtail.Brain do
}
end
end
end

View File

@ -16,7 +16,9 @@ defmodule Diamondtail.Population do
field :enemy_alive_weight, float()
field :own_health_weight, float()
field :own_length_weight, float()
field :enemy_health_weight, float()
field :enemy_length_weight, float()
field :enemy_head_distance_weight, float()
field :food_distance_weight, float()
@ -57,10 +59,12 @@ defmodule Diamondtail.Population do
def random() do
%Genome{
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),
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
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!(%{
apiversion: "1",
author: "kodicraft",
color: "#8cd9ff",
color: "#4684ff",
head: "trans-rights-scarf",
tail: "hook",
version: Mix.Project.config()[:version]