Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Pokemon Battle

pokemon_battle.livemd

Pokemon Battle

Mix.install([
  {:kino, github: "livebook-dev/kino", override: true},
  {:kino_lab, "~> 0.1.0-dev", github: "jonatanklosko/kino_lab"},
  {:vega_lite, "~> 0.1.4"},
  {:kino_vega_lite, "~> 0.1.1"},
  {:benchee, "~> 0.1"},
  {:ecto, "~> 3.7"},
  {:math, "~> 0.7.0"},
  {:faker, "~> 0.17.0"},
  {:utils, path: "#{__DIR__}/../utils"},
  {:tested_cell, git: "https://github.com/BrooklinJazz/tested_cell"}
])

Navigation

Return Home Report An Issue

Overview

You’re going to create a pokemon battle game. If you’re not familiar with pokemon, it’s a game where elemental creatures battle.

In this game, you’re going to create a few pokemon, charmander, squirtle, and bulbasaur then have them battle.

Some Elixir cells in this exercise will rely on previous solutions, so ensure that you use the ea command to evaluate all cells. Otherwise, your prior code may be stale and result in bugs.

Define A Pokemon Struct

In the Elixir cell below, define a Pokemon struct with the keys :name, :type, health, :attack, and :speed.

  • :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 of 20
  • :attack will be an integer with a default value of 5
  • :speed will be an integer
ExUnit.start(auto_run: false)

defmodule Assertion do
  use ExUnit.Case

  test "" do
    defmodule Pokemon do
    end

    assert Keyword.get(Pokemon.__info__(:functions), :__struct__),
           "Ensure you use `defstruct`."

    assert match?(%{name: nil, type: nil, speed: nil}, struct(Pokemon)),
           "Ensure you use `defstruct` with :name, :type, :health, :attack, and :speed."

    assert match?(%{health: 20, attack: 5}, struct(Pokemon)),
           "Ensure :health has a default value of 20 and :attack has a default value of 5."
  end
end

ExUnit.run()

# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
defmodule Pokemon do
end

Create 3 Pokemon

In the Elixir cell below, create 3 variables charmander, bulbasaur, and squirtle. Bind each variable to a Pokemon struct with the following stats.

classDiagram
    class Charmander {
        type: :fire
        speed: 20
    }
    class Bulbasaur {
        type: :grass
        speed: 15
    }
    class Squirtle {
        type: :water
        speed: 10
    }

Replace nil with your solution.

ExUnit.start(auto_run: false)

defmodule Assertion do
  use ExUnit.Case

  test "" do
    charmander = nil
    bulbasaur = nil
    squirtle = nil
    assert is_struct(charmander), "Ensure `charmander` is a struct."
    assert is_struct(squirtle), "Ensure `squirtle` is a struct."
    assert is_struct(bulbasaur), "Ensure `bulbasaur` is a struct."

    assert %Pokemon{name: "Charmander", type: :fire, attack: 5, health: 20, speed: 20} =
             charmander

    assert %Pokemon{name: "Bulbasaur", type: :grass, attack: 5, health: 20, speed: 15} = bulbasaur
    assert %Pokemon{name: "Squirtle", type: :water, attack: 5, health: 20, speed: 10} = squirtle
  end
end

ExUnit.run()

# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
charmander = nil
bulbasaur = nil
squirtle = nil

Pokemon Battle

Create a PokemonBattle module with the following functions:

attack/2

Create an attack/2 function which:

  • accepts two pokemon structs as parameters.
  • return an updated version of the second pokemon struct.
  • subtract the first pokemon’s attack from the second pokemon’s health.
  • double the damage dealt if the pokemon has a type advantage.
  • half the damage dealt if the pokemon has a type weakness.

For example,

squirtle = %Pokemon{name: "Squirtle", type: :water, attack: 10}
charmander = %Pokemon{name: "Charmander", type: :fire, health: 20}

PokemonBattle.attack(squirtle, charmander)
%Pokemon{name: "Charmander", type: :fire, health: 0, speed: nil, attack: 5}

Use the following type advantages to determine half or double damage.

  • :fire beats :grass
  • :grass beats :water
  • :water beats :fire

battle/2

Create a battle/2 function which:

  • accepts two pokemon structs as parameters.
  • attack/2 with the pokemon that has the higher speed. If their speed is the same attack the second pokemon first.
  • if the attacked pokemon is still above 0 health, attack with the second pokemon.
  • returns two updated pokemon structs in a tuple.

For example,

charmander = %Pokemon{name: "Charmander", type: :fire, speed: 20}
squirtle = %Pokemon{name: "Squirtle", type: :water, speed: 10}

PokemonBattle.battle(charmander, squirtle)
{
    %Pokemon{name: "Charmander", type: :fire, health: 10, speed: 20, attack: 5},
    %Pokemon{name: "Squirtle", type: :water, health: 17.5, speed: 10, attack: 5}
}

charizard = %Pokemon{name: "Charizard", type: :fire, speed: 20, attack: 20}
blastoise = %Pokemon{name: "Blastoise", type: :water, speed: 10, health: 10}

# the faster pokemon will not receive damage if the slower pokemon's health is 0.
PokemonBattle.battle(charizard, blastoise)
{
    %Pokemon{name: "Charizard", type: :fire, health: 20, speed: 20, attack: 20},
    %Pokemon{name: "Blastoise", type: :water, health: 0, speed: 10, attack: 5}
}
ExUnit.start(auto_run: false)

defmodule Assertion do
  use ExUnit.Case

  test "" do
    defmodule PokemonBattle do
    end

    assert Keyword.get(Pokemon.__info__(:functions), :__struct__),
           "Ensure you complete the `Pokemon` module above first."

    assert match?(
             %{name: nil, type: nil, speed: nil, health: 20, attack: 5},
             struct(Pokemon)
           ),
           "Ensure you complete the `Pokemon` module above first."

    pokemon_types =
      for speed <- 10..30//5,
          type <- [:water, :fire, :grass],
          attack <- 5..40//5,
          health <- 5..20//5 do
        struct(Pokemon, %{
          name: "#{Atom.to_string(type)} pokemon",
          speed: speed,
          attack: attack,
          health: health,
          type: type
        })
      end

    pokemon_types
    |> Enum.shuffle()
    |> Enum.take(2)
    |> Enum.chunk_every(2)
    |> Enum.each(fn [pokemon1, pokemon2] ->
      attacked_pokemon = PokemonBattle.attack(pokemon1, pokemon2)

      multiplier = Utils.Solutions.PokemonBattle.multiplier(pokemon1, pokemon2)

      assert attacked_pokemon == %Pokemon{
               pokemon2
               | health: pokemon2.health - pokemon1.attack * multiplier
             }

      PokemonBattle.battle(pokemon1, pokemon2)

      assert {battled_pokemon1, battled_pokemon2} = PokemonBattle.battle(pokemon1, pokemon2)

      {expected_battled_pokemon1, expected_battled_pokemon2} =
        Utils.Solutions.PokemonBattle.battle(pokemon1, pokemon2)

      assert battled_pokemon1 == expected_battled_pokemon1
      assert battled_pokemon2 == expected_battled_pokemon2
    end)
  end
end

ExUnit.run()

# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
defmodule PokemonBattle do
end

Commit Your Progress

Run the following in your command line from the project folder to track and save your progress in a Git commit.

$ git add .
$ git commit -m "finish pokemon battle exercise"