some tweaks
This commit is contained in:
parent
31e179a74a
commit
c213507aec
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user