Created
October 26, 2023 12:52
-
-
Save mbuffa/d5df6e2fc5c70f7c3a292af68f38be24 to your computer and use it in GitHub Desktop.
World of Warcraft Trade
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a possible implementation for a trade in copper, silver and gold | |
# coins, as it happens in a few MMORPGs, such as World of Warcraft. | |
# A simpler and smarter approach would be to handle it as a single integer, | |
# and let the UI display the amount in the different coins. This is just | |
# an exercise for fun. | |
# Run with `elixir pay.ex` | |
defmodule Trade do | |
defmodule Amount do | |
defstruct gold: 0, silver: 0, copper: 0 | |
def new(copper, silver, gold) | |
def new(_, 100, _), do: {:error, :silver_too_big} | |
def new(100, _, _), do: {:error, :copper_too_big} | |
def new(copper, silver, gold) do | |
{:ok, %Amount{copper: copper, silver: silver, gold: gold}} | |
end | |
end | |
def pay(cost, purse) | |
def pay( | |
%Trade.Amount{copper: cost_in_copper, silver: cost_in_silver, gold: cost_in_gold}, | |
%Trade.Amount{copper: owned_copper, silver: owned_silver, gold: owned_gold} | |
) do | |
owned = owned_copper + owned_silver * 100 + owned_gold * 10000 | |
cost = cost_in_copper + cost_in_silver * 100 + cost_in_gold * 10000 | |
if owned < cost do | |
{:error, "Not enough money"} | |
else | |
remaining = cost - owned | |
remaining_gold = div(remaining, 10000) | |
remaining_silver = | |
remaining | |
|> div(100) | |
|> Kernel.-(remaining_gold * 100) | |
remaining_copper = | |
remaining | |
|> Kernel.-(remaining_gold * 10000) | |
|> Kernel.-(remaining_silver * 100) | |
{:ok, %Trade.Amount{ | |
gold: abs(remaining_gold), | |
silver: abs(remaining_silver), | |
copper: abs(remaining_copper) | |
}} | |
end | |
end | |
end | |
ExUnit.start() | |
defmodule Test do | |
use ExUnit.Case | |
test "not enough money" do | |
{:ok, cost} = Trade.Amount.new(1, 0, 0) | |
{:ok, owned} = Trade.Amount.new(0, 0, 0) | |
assert Trade.pay(cost, owned) == {:error, "Not enough money"} | |
{:ok, cost} = Trade.Amount.new(0, 1, 0) | |
{:ok, owned} = Trade.Amount.new(0, 0, 0) | |
assert Trade.pay(cost, owned) == {:error, "Not enough money"} | |
{:ok, cost} = Trade.Amount.new(0, 0, 1) | |
{:ok, owned} = Trade.Amount.new(0, 0, 0) | |
assert Trade.pay(cost, owned) == {:error, "Not enough money"} | |
{:ok, cost} = Trade.Amount.new(4, 2, 1) | |
{:ok, owned} = Trade.Amount.new(3, 2, 1) | |
assert Trade.pay(cost, owned) == {:error, "Not enough money"} | |
end | |
test "enough money, exact amount" do | |
{:ok, cost} = Trade.Amount.new(0, 0, 1) | |
{:ok, owned} = Trade.Amount.new(0, 0, 1) | |
assert Trade.pay(cost, owned) == {:ok, %Trade.Amount{gold: 0, silver: 0, copper: 0}} | |
end | |
test "enough money, with remainders" do | |
{:ok, cost} = Trade.Amount.new(0, 50, 0) | |
{:ok, owned} = Trade.Amount.new(0, 0, 1) | |
assert Trade.pay(cost, owned) == {:ok, %Trade.Amount{gold: 0, silver: 50, copper: 0}} | |
{:ok, cost} = Trade.Amount.new(23, 0, 0) | |
{:ok, owned} = Trade.Amount.new(0, 0, 1) | |
assert Trade.pay(cost, owned) == {:ok, %Trade.Amount{gold: 0, silver: 99, copper: 77}} | |
{:ok, cost} = Trade.Amount.new(23, 1, 0) | |
{:ok, owned} = Trade.Amount.new(0, 0, 1) | |
assert Trade.pay(cost, owned) == {:ok, %Trade.Amount{gold: 0, silver: 98, copper: 77}} | |
end | |
test "new/3" do | |
assert Trade.Amount.new(100, 99, 23) == {:error, :copper_too_big} | |
assert Trade.Amount.new(100, 100, 23) == {:error, :silver_too_big} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment