Mix.install([
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_cell"},
{:tested_cell, github: "brooklinjazz/tested_cell"},
{:utils, path: "#{__DIR__}/../utils"}
])
Ensure you type the ea
keyboard shortcut to evaluate all Elixir cells before starting. Alternatively you can evaluate the Elixir cells as you read.
You're going to create a pokemon battle game with persistence.
Each Pokemon will be a GenServer process storing the pokemon's stats in state.
classDiagram
class Pokemon {
health: 20
attack: 5
type: :fire
name: "charmander"
}
Pokemon can attack each other. An attack should update the Pokemon
's health in the GenServer's state.
Create a Pokemon
module which will be a GenServer and a struct definition. It should store the following in values in state.
:name
will be a string.:type
will be an atom of either:grass
,:water
, or:fire
:health
will be an integer with a default value of20
:attack
will be an integer with a default value of5
You should be able to create a Pokemon
struct.
%Pokemon{name: "Charmander", type: :fire, health: 20, attack: 5}
Pokemon
GenServers should be able to attack one another. This should subtract one pokemon's :attack
from the other pokemon's :health
, and update the value in the state of the GenServer. A pokemon's health should not be able to fall below 0
.
attacker = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur1", type: :grass})
defender = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur2", type: :grass})
Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur1", type: :grass, health: 15, attack: 20} = :sys.get_state(defender)
Example Solution
defmodule Pokemon do
use GenServer
@enforce_keys [:name, :type]
defstruct @enforce_keys ++ [health: 20, attack: 5]
def start_link(opts \\ []) do
{pokemon, opts} = Keyword.pop!(opts, :pokemon)
GenServer.start_link(__MODULE__, pokemon, opts)
end
@impl true
def init(state) do
{:ok, state}
end
def attack(attacker, defender) do
attacker_type = Pokemon.get(attacker, :type)
defender_type = Pokemon.get(defender, :type)
damage_amount = Pokemon.get(attacker, :attack) * multiplier(attacker_type, defender_type)
GenServer.call(defender, {:apply_damage, damage_amount})
end
def multiplier(attacker_type, defender_type) do
case {attacker_type, defender_type} do
{:fire, :grass} -> 2
{:grass, :water} -> 2
{:water, :fire} -> 2
{:fire, :water} -> 0.5
{:grass, :fire} -> 0.5
{:water, :grass} -> 0.5
_ -> 1
end
end
def get(pokemon, key) do
GenServer.call(pokemon, {:get, key})
end
def handle_call({:get, key}, _from, pokemon) do
{:reply, Map.get(pokemon, key), pokemon}
end
@impl true
def handle_call({:apply_damage, amount}, _from, pokemon) do
updated_pokemon = Map.update!(pokemon, :health, fn health -> max(health - amount, 0) end)
{:reply, updated_pokemon, updated_pokemon}
end
end
Enter your solution below.
defmodule Pokemon do
@moduledoc """
Pokemon Server
Defines a Pokemon GenServer and struct.
## Examples
iex> %Pokemon{name: "Charmander", type: :fire}
%Pokemon{name: "Charmander", type: :fire, health: 20, attack: 5}
"""
use GenServer
@doc """
Start the Pokemon GenServer.
## Examples
iex> {:ok, pokemon} = Pokemon.start_link([pokemon: %Pokemon{name: "Charmander", type: :fire}])
"""
def start_link(opts \\ []) do
end
@impl true
def init(state) do
{:ok, state}
end
@doc """
Apply the attacker's attack damage to the defender's health.
## Examples
With default values.
iex> {:ok, attacker} = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur1", type: :grass})
iex> {:ok, defender} = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur2", type: :grass})
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 15, attack: 5}
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 10, attack: 5}
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 5, attack: 5}
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 0, attack: 5}
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 0, attack: 5}
Ensure you use the attacking pokemon's attack number to apply damage to the defending pokemon's health.
iex> {:ok, attacker} = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur1", type: :grass, attack: 20})
iex> {:ok, defender} = Pokemon.start_link(pokemon: %Pokemon{name: "Bulbasaur2", type: :grass, health: 45})
iex> Pokemon.attack(attacker, defender)
%Pokemon{name: "Bulbasaur2", type: :grass, health: 25, attack: 5}
"""
def attack(attacker, defender) do
end
end
Multiply the damage dealt by an attack by 2
if the attacker has a type advantage. Multiply the damage applied by 0.5
if the attacker has a type weakness.
Type Advantages are:
- fire -> grass
- grass -> water
- water -> fire
Type weaknesses are:
- fire -> water
- grass -> fire
- water -> grass
Modify your solution above to complete this bonus section.
Run the following in your command line from the beta_curriculum folder to track and save your progress in a Git commit.
$ git add .
$ git commit -m "finish pokemon server exercise"
Previous | Next |
---|---|
Rock Paper Scissors GenServer | Supervisors |