Skip to content

Commit

Permalink
bots take a while after attack. allow other skills. (#951)
Browse files Browse the repository at this point in the history
* bots take a while after attack. allow other skills.

* format

* Fix bots spawn in each game mode

* Replace range list of integers to list of strings representing each skill

* Pattern match boolean in function signature instead of guard

* Delete unused function

* Delete unused code

---------

Co-authored-by: Nicolas Sanchez <[email protected]>
  • Loading branch information
manucamejo and Nico-Sanchez authored Sep 12, 2024
1 parent aeb6fb7 commit 07459e1
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 55 deletions.
37 changes: 3 additions & 34 deletions apps/arena/lib/arena/matchmaking/game_launcher.ex
Original file line number Diff line number Diff line change
@@ -1,26 +1,11 @@
defmodule Arena.Matchmaking.GameLauncher do
@moduledoc false
alias Arena.Utils
alias Ecto.UUID

use GenServer

# Time to wait to start game with any amount of clients
@start_timeout_ms 4_000
# The available names for bots to enter a match, we should change this in the future
@bot_names [
"TheBlackSwordman",
"SlashJava",
"SteelBallRun",
"Jeff",
"Messi",
"Stone Ocean",
"Jeepers Creepers",
"Bob",
"El javo",
"Alberso",
"Thomas"
]

# API
def start_link(_) do
Expand Down Expand Up @@ -96,19 +81,6 @@ defmodule Arena.Matchmaking.GameLauncher do
batch_start_at
end

defp get_bot_clients(missing_clients) do
characters =
Arena.Configuration.get_game_config()
|> Map.get(:characters)
|> Enum.filter(fn character -> character.active end)

Enum.map(1..missing_clients//1, fn i ->
client_id = UUID.generate()

{client_id, Enum.random(characters).name, Enum.at(@bot_names, i), nil}
end)
end

defp spawn_bot_for_player(bot_clients, game_id) do
Enum.each(bot_clients, fn {bot_client, _, _, _} ->
send(self(), {:spawn_bot_for_player, bot_client, game_id})
Expand All @@ -118,12 +90,9 @@ defmodule Arena.Matchmaking.GameLauncher do
# Receives a list of clients.
# Fills the given list with bots clients, creates a game and tells every client to join that game.
defp create_game_for_clients(clients, game_params \\ %{}) do
bot_clients =
if Application.get_env(:arena, :spawn_bots) do
get_bot_clients(Application.get_env(:arena, :players_needed_in_match) - Enum.count(clients))
else
[]
end
# We won't spawn bots in normal matches.
# Check https://github.com/lambdaclass/mirra_backend/pull/951 to know how to restore former behavior
bot_clients = []

{:ok, game_pid} =
GenServer.start(Arena.GameUpdater, %{
Expand Down
11 changes: 4 additions & 7 deletions apps/arena/lib/arena/matchmaking/quick_game_mode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ defmodule Arena.Matchmaking.QuickGameMode do
@impl true
def handle_call({:join, client_id, character_name, player_name}, {from_pid, _}, state) do
create_game_for_clients([{client_id, character_name, player_name, from_pid}], %{
bots_enabled: false,
bots_enabled: true,
zone_enabled: false
})

Expand Down Expand Up @@ -88,12 +88,9 @@ defmodule Arena.Matchmaking.QuickGameMode do
# Receives a list of clients.
# Fills the given list with bots clients, creates a game and tells every client to join that game.
defp create_game_for_clients(clients, game_params \\ %{}) do
bot_clients =
if Application.get_env(:arena, :spawn_bots) do
get_bot_clients(Application.get_env(:arena, :players_needed_in_match) - Enum.count(clients))
else
[]
end
# We will spawn bots in quick-game matches.
# Check https://github.com/lambdaclass/mirra_backend/pull/951 to know how to restore former behavior.
bot_clients = get_bot_clients(Application.get_env(:arena, :players_needed_in_match) - Enum.count(clients))

{:ok, game_pid} =
GenServer.start(Arena.GameUpdater, %{
Expand Down
20 changes: 10 additions & 10 deletions apps/bot_manager/lib/bot_state_machine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ defmodule BotManager.BotStateMachine do
{:move, %{x: 0, y: 0}}
end

def decide_action(%{game_state: game_state, bot_player: bot_player}) do
def decide_action(%{game_state: game_state, bot_player: bot_player, attack_blocked: attack_blocked}) do
closest_player =
map_directions_to_players(game_state, bot_player)
|> Enum.min_by(fn player_info -> player_info.distance end)

random_distance = 1000

cond do
closest_player.distance > 300 ->
attack_blocked ->
:stand

closest_player.distance > random_distance ->
{:move, closest_player.direction}

closest_player.distance < 50 ->
{:move, Vector.mult(closest_player.direction, -1)}
{:move, create_random_direction()}

closest_player.distance <= 300 ->
closest_player.distance <= random_distance ->
{:attack, closest_player.direction}

true ->
Expand All @@ -33,12 +38,7 @@ defmodule BotManager.BotStateMachine do
def decide_action(_), do: :stand

defp create_random_direction() do
Enum.random([
%{x: 1, y: 0},
%{x: 0, y: -1},
%{x: -1, y: 0},
%{x: 0, y: 1}
])
%{x: Enum.random(1..200) / 100 - 1, y: Enum.random(1..200) / 100 - 1}
end

defp map_directions_to_players(game_state, bot_player) do
Expand Down
22 changes: 18 additions & 4 deletions apps/bot_manager/lib/game_socket_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule BotManager.GameSocketHandler do
use WebSockex, restart: :temporary
require Logger

@decision_delay_ms 200
@decision_delay_ms 1000
@action_delay_ms 30

def start_link(%{"bot_client" => bot_client, "game_id" => game_id} = params) do
Expand All @@ -28,7 +28,7 @@ defmodule BotManager.GameSocketHandler do
def handle_connect(_conn, state) do
send(self(), :decide_action)
send(self(), :perform_action)
{:ok, Map.put(state, :bots_enabled?, true)}
{:ok, Map.put(state, :bots_enabled?, true) |> Map.put(:attack_blocked, false)}
end

def handle_frame({:binary, frame}, state) do
Expand Down Expand Up @@ -65,11 +65,25 @@ defmodule BotManager.GameSocketHandler do
{:ok, Map.put(state, :current_action, action)}
end

def handle_info(:unblock_attack, state) do
{:ok, Map.put(state, :attack_blocked, false)}
end

def handle_info(:perform_action, state) do
Process.send_after(self(), :perform_action, @action_delay_ms)

send_current_action(state)

state =
case state.current_action do
{:attack, _} ->
Map.put(state, :attack_blocked, true)
Process.send_after(self(), :unblock_attack, Enum.random(2000..4000))

_ ->
state
end

{:ok, state}
end

Expand All @@ -96,15 +110,15 @@ defmodule BotManager.GameSocketHandler do
WebSockex.cast(self(), {:send, {:binary, game_action}})
end

defp send_current_action(%{current_action: {:attack, direction}}) do
defp send_current_action(%{current_action: {:attack, direction}, attack_blocked: false}) do
timestamp = DateTime.utc_now() |> DateTime.to_unix(:millisecond)

game_action =
BotManager.Protobuf.GameAction.encode(%BotManager.Protobuf.GameAction{
action_type:
{:attack,
%BotManager.Protobuf.Attack{
skill: "1",
skill: Enum.random(["1", "2", "3"]),
parameters: %BotManager.Protobuf.AttackParameters{
target: %BotManager.Protobuf.Direction{
x: direction.x,
Expand Down

0 comments on commit 07459e1

Please sign in to comment.