add stuff, try to fix bad behaviour
This commit is contained in:
parent
c213507aec
commit
c1fbe9cd80
@ -55,7 +55,6 @@ defmodule Diamondtail.Brain do
|
|||||||
|> Map.update(:snakes, nil, fn snakes ->
|
|> Map.update(:snakes, nil, fn snakes ->
|
||||||
Map.update(snakes, state.self, nil, &apply_action_to_snake(&1, move))
|
Map.update(snakes, state.self, nil, &apply_action_to_snake(&1, move))
|
||||||
end)
|
end)
|
||||||
# |> resolve_state()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_actions(%GameState{type: :q} = state, moves) do
|
def apply_actions(%GameState{type: :q} = state, moves) do
|
||||||
@ -84,17 +83,22 @@ defmodule Diamondtail.Brain do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_actions(%GameState{type: :q, snakes: snakes, self: self}) do
|
def valid_actions(%GameState{type: :q, snakes: snakes, self: self} = state) do
|
||||||
non_self_snakes = Map.delete(snakes, self)
|
non_self_snakes = Map.delete(snakes, self)
|
||||||
for i <- 0..(4 ** map_size(non_self_snakes)) - 1 do
|
for i <- 0..(4 ** map_size(non_self_snakes)) - 1 do
|
||||||
Enum.with_index(non_self_snakes)
|
Enum.with_index(non_self_snakes)
|
||||||
|> Enum.map(fn {{id, _}, j} ->
|
|> Enum.map(fn {{id, _}, j} ->
|
||||||
{id, Enum.at([:up, :down, :left, :right], rem(floor(i / (4 ** j)), 4))}
|
{id, Enum.at([:up, :down, :left, :right], rem(floor(i / (4 ** j)), 4))}
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(fn {id, action} ->
|
||||||
|
result = apply_actions(state, %{id => action})
|
||||||
|
!snake_dies?(result, id)
|
||||||
|
end)
|
||||||
|> Map.new()
|
|> Map.new()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec evaluate_state(Genome.t(), GameState.t(), non_neg_integer(), float() | nil, float() | nil) :: float()
|
||||||
def evaluate_state(genome, state, depth, alpha \\ Float.min_finite, beta \\ Float.max_finite)
|
def evaluate_state(genome, state, depth, alpha \\ Float.min_finite, beta \\ Float.max_finite)
|
||||||
def evaluate_state(genome, %GameState{type: :v} = state, 0, _, _) do
|
def evaluate_state(genome, %GameState{type: :v} = state, 0, _, _) do
|
||||||
# If we're dead, the result is always super duper negative
|
# If we're dead, the result is always super duper negative
|
||||||
@ -104,11 +108,14 @@ defmodule Diamondtail.Brain do
|
|||||||
self = state.snakes[state.self]
|
self = state.snakes[state.self]
|
||||||
|
|
||||||
Enum.count(enemies) * genome.enemy_alive_weight +
|
Enum.count(enemies) * genome.enemy_alive_weight +
|
||||||
|
Enum.count(enemies, &(&1.length >= self.length)) * genome.enemy_longer_weight +
|
||||||
self.health * genome.own_health_weight +
|
self.health * genome.own_health_weight +
|
||||||
self.length * genome.own_length_weight +
|
self.length * genome.own_length_weight +
|
||||||
|
length(available_tiles_from(state, self.head)) * genome.own_accessible_tiles_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.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.flat_map(enemies, &(if &1.length >= self.length, do: [abs(&1.head.x - self.head.x) + abs(&1.head.y - self.head.y)], else: [])) |> Enum.min(&<=/2, fn -> 0 end)) * genome.longer_enemy_head_distance_weight +
|
||||||
|
(Enum.flat_map(enemies, &(if &1.length < self.length, do: [abs(&1.head.x - self.head.x) + abs(&1.head.y - self.head.y)], else: [])) |> Enum.min(&<=/2, fn -> 0 end)) * genome.shorter_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
|
||||||
Float.min_finite()
|
Float.min_finite()
|
||||||
@ -130,9 +137,10 @@ defmodule Diamondtail.Brain do
|
|||||||
valid_actions = valid_actions(state)
|
valid_actions = valid_actions(state)
|
||||||
if valid_actions == [] do
|
if valid_actions == [] do
|
||||||
# Terminal state
|
# Terminal state
|
||||||
|
# We only win if this is a Q state AND we're still alive, this might still be a draw
|
||||||
case state.type do
|
case state.type do
|
||||||
:v -> Float.min_finite()
|
:v -> Float.min_finite()
|
||||||
:q -> Float.max_finite()
|
:q -> if snake_dies?(state, state.self), do: IO.inspect(Float.min_finite()), else: Float.max_finite()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
{score, _alpha, _beta} = valid_actions(state)
|
{score, _alpha, _beta} = valid_actions(state)
|
||||||
@ -188,15 +196,19 @@ defmodule Diamondtail.Brain do
|
|||||||
defp snake_dies?(state, snake_id) do
|
defp snake_dies?(state, snake_id) do
|
||||||
snake = state.snakes[snake_id]
|
snake = state.snakes[snake_id]
|
||||||
|
|
||||||
# Die if health is less than or equal to 0
|
if snake == nil do
|
||||||
snake.health <= 0 ||
|
true
|
||||||
# Die if head is out of bounds
|
else
|
||||||
snake.head.x < 0 || snake.head.x >= state.width ||
|
# Die if health is less than or equal to 0
|
||||||
snake.head.y < 0 || snake.head.y >= state.height ||
|
snake.health <= 0 ||
|
||||||
# Die if head is in a tail
|
# Die if head is out of bounds
|
||||||
Enum.any?(state.snakes, fn {_, %{body: body}} -> Enum.member?(body, snake.head) end) ||
|
snake.head.x < 0 || snake.head.x >= state.width ||
|
||||||
# Die if head is on head of a DIFFERENT snake of equal or greater length
|
snake.head.y < 0 || snake.head.y >= state.height ||
|
||||||
Enum.any?(state.snakes, fn {id, %{head: h, length: l}} -> snake.head == h && snake.length <= l && id != snake_id end)
|
# 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 && l >= snake.length && id != snake_id end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action_to_snake(snake, move) do
|
defp apply_action_to_snake(snake, move) do
|
||||||
@ -211,6 +223,22 @@ defmodule Diamondtail.Brain do
|
|||||||
|> Map.update(:body, nil, fn body -> List.delete_at(body, 0) ++ [snake.head] end)
|
|> Map.update(:body, nil, fn body -> List.delete_at(body, 0) ++ [snake.head] end)
|
||||||
|> Map.update(:head, nil, fn %{x: x, y: y} -> %{x: x + delta.x, y: y + delta.y} end)
|
|> Map.update(:head, nil, fn %{x: x, y: y} -> %{x: x + delta.x, y: y + delta.y} end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def available_tiles_from(state, %{x: x, y: y} = base_pos, explored \\ []) do
|
||||||
|
Enum.reduce([%{x: x + 1, y: y}, %{x: x - 1, y: y}, %{x: x, y: y + 1}, %{x: x, y: y - 1}], [base_pos | explored], fn pos, acc ->
|
||||||
|
if Enum.member?(acc, pos) || in_solid_or_out_of_bounds?(state, pos) do
|
||||||
|
acc
|
||||||
|
else
|
||||||
|
available_tiles_from(state, pos, acc)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp in_solid_or_out_of_bounds?(state, %{x: x, y: y} = pos) do
|
||||||
|
x >= state.width || y >= state.height ||
|
||||||
|
x < 0 || y < 0 ||
|
||||||
|
Enum.any?(state.snakes, fn {_, %{body: b, head: h}} -> h == pos || Enum.member?(b, pos) end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec determine_move(Genome.t(), map(), map()) :: %{move: :up | :down | :left | :right, shout: String.t()}
|
@spec determine_move(Genome.t(), map(), map()) :: %{move: :up | :down | :left | :right, shout: String.t()}
|
||||||
|
|||||||
@ -15,12 +15,15 @@ defmodule Diamondtail.Population do
|
|||||||
field :mutation_max_amplitude, float()
|
field :mutation_max_amplitude, float()
|
||||||
|
|
||||||
field :enemy_alive_weight, float()
|
field :enemy_alive_weight, float()
|
||||||
|
field :enemy_longer_weight, float()
|
||||||
field :own_health_weight, float()
|
field :own_health_weight, float()
|
||||||
field :own_length_weight, float()
|
field :own_length_weight, float()
|
||||||
field :enemy_health_weight, float()
|
field :enemy_health_weight, float()
|
||||||
field :enemy_length_weight, float()
|
field :enemy_length_weight, float()
|
||||||
field :enemy_head_distance_weight, float()
|
field :longer_enemy_head_distance_weight, float()
|
||||||
|
field :shorter_enemy_head_distance_weight, float()
|
||||||
field :food_distance_weight, float()
|
field :food_distance_weight, float()
|
||||||
|
field :own_accessible_tiles_weight, float()
|
||||||
|
|
||||||
field :search_depth, float()
|
field :search_depth, float()
|
||||||
field :value_discount, float()
|
field :value_discount, float()
|
||||||
@ -61,17 +64,21 @@ defmodule Diamondtail.Population do
|
|||||||
mutation_chance: random_between(0.1, 0.2),
|
mutation_chance: random_between(0.1, 0.2),
|
||||||
mutation_max_amplitude: random_between(0.2, 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),
|
||||||
|
enemy_longer_weight: random_between(-50.0, -30.0), # Try to stay longer than our enemies to avoid dying to head-to-head collisions
|
||||||
own_health_weight: random_between(0.7, 1.0),
|
own_health_weight: random_between(0.7, 1.0),
|
||||||
own_length_weight: random_between(5, 10),
|
own_length_weight: random_between(5, 10),
|
||||||
enemy_health_weight: random_between(-0.8, -0.4), # Summed
|
enemy_health_weight: random_between(-0.8, -0.4), # Summed
|
||||||
enemy_length_weight: random_between(-10, -5), # Averaged
|
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
|
longer_enemy_head_distance_weight: random_between(0.3, 1), # maximize distance, reward staying away
|
||||||
food_distance_weight: random_between(-2, -1), # minimize distance, reward staying near
|
shorter_enemy_head_distance_weight: random_between(-1, -0.3), # minimize distance, reward staying near to try to go in for kills
|
||||||
|
food_distance_weight: random_between(-0.5, -0.1), # minimize distance, reward staying near, don't just stick next to food though
|
||||||
|
|
||||||
search_depth: random_between(3.0, 7.0),
|
own_accessible_tiles_weight: random_between(0.7, 1),
|
||||||
value_discount: random_between(1.0, 10.0) # as percentage
|
|
||||||
|
search_depth: random_between(3.0, 5.0),
|
||||||
|
value_discount: random_between(1.0, 20.0) # as percentage
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user