diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2791d3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +elixir_fizz/_build/ +elixir_fizz/cover/ +elixir_fizz/deps/ +elixir_fizz/doc/ +elixir_fizz/.fetch +elixir_fizz/erl_crash.dump +elixir_fizz/*.ez +elixir_fizz/elixir_fizz-*.tar diff --git a/elixir_fizz/.formatter.exs b/elixir_fizz/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/elixir_fizz/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/elixir_fizz/README.md b/elixir_fizz/README.md new file mode 100644 index 0000000..4767dad --- /dev/null +++ b/elixir_fizz/README.md @@ -0,0 +1,102 @@ +# FizzBot Elixir Solution + +- Requirements for test this solution +- Download dependencies +- Run this solution +- How it works +- ElixirFizz module design + +## Requirements for test this solution + + 1. Install in your computer Elixir (I used 1.8.1 version) + +## Download dependencies + +For this project I created a mix project because I needed two dependencies for make http requests *httpoison 1.0* and parser the responses *poison 3.1*, that's the reason that I'm using a mix project. + +For download this dependencies you need an elixir installation in your computer, and in this directory *fizzbot/elixir_fizz* run: + +> mix deps.get + +## Run this solution + +Elixir have a useful REPL, we should use it for test this project. For run a REPL with this mix project you have to run: + +> iex -S mix + +Then for test the solution, into the REPL run: + +> ElixirFizz.start + +``` +$ iex -S mix + +Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace] +Compiling 1 file (.ex) +Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) + +iex(1)> ElixirFizz.start +``` + +## How it works + +The next step is answer the questions in the REPL. If your answer is *correct* then you will have to answer the next question, but if your answer is *incorrect* you will have to answer again until your answer will be correct. + +#### Correct Answer + +``` +iex(1)> ElixirFizz.start +What is your favorite programming language? +Mine is COBOL, of course. +POST your answer back to this URL in JSON format. If you are having difficulties, see the exampleResponse provided. + +Write your answer::_ Elixir + +Your answer is: Elixir + +Answer Correct!! +Next question... +``` + +#### Incorrect Answer + +``` +Your numbers are: +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + +Write your answer::_ 1 2 Fizz 4 Buzz 6 + +Your answer is: 1 2 Fizz 4 Buzz 6 +Answer is incorrect starting at position 16 ("6") +Try again!! + +Write your answer::_ +``` + +## ElixirFizz module design + +The *ElixirFizz* elixir module contains many funcions. + +The main funcion is *start()* and it's the only public function, this initialize the Fizzbot and start to make the request for answer to this bot. + +This are the private functions: + +Make the first request to Fizzbot +- `start_fizzbot_for_first_time(starter_uri)` + +Ask and answer +- `ask_to_fizzbot(fizzbot)` +- `answer_to_fizzbot({fizzbot, question_uri})` + +Input and output functions +- `getting_answer()` +- `show_rules(fizzbot)` + +Retry answer when it's wrong +- `validate_answer({fizzbot_answer, fizzbot, question_uri})` + +Make and parse requests +- `get(uri)` +- `post(uri)` + +If you want to see it you could follow this simple guide for understand how it works, and if you have some questions you can find me at Twitter as @carlogilmar. diff --git a/elixir_fizz/config/config.exs b/elixir_fizz/config/config.exs new file mode 100644 index 0000000..d2d855e --- /dev/null +++ b/elixir_fizz/config/config.exs @@ -0,0 +1 @@ +use Mix.Config diff --git a/elixir_fizz/lib/bot.ex b/elixir_fizz/lib/bot.ex new file mode 100644 index 0000000..50748a1 --- /dev/null +++ b/elixir_fizz/lib/bot.ex @@ -0,0 +1,46 @@ +defmodule ElixirFizz.Bot do + + def answer({_fizzbot, false}), do: "Elixir" + def answer({fizzbot, true}), do: generate_answer(fizzbot, length(fizzbot["rules"])) + + defp generate_answer(fizzbot, 2) do + [numberA, numberB] = fizzbot["rules"] + tokens = + for number <- fizzbot["numbers"] do + fizzbuzz({number, rem(number, numberA["number"])== 0, numberA["response"], rem(number, numberB["number"]) == 0, numberB["response"]}) + end + Enum.join(tokens, " ") + end + + defp generate_answer(fizzbot, 3) do + [numberA, numberB, numberC] = fizzbot["rules"] + tokens = + for number <- fizzbot["numbers"] do + fizzbot({ number, + rem(number, numberA["number"])== 0, + numberA["response"], + rem(number, numberB["number"]) == 0, + numberB["response"], + rem(number, numberC["number"]) == 0, + numberC["response"] + }) + end + Enum.join(tokens, " ") + end + + defp fizzbuzz({number, false, _numberA, false, _numberB}), do: "#{number}" + defp fizzbuzz({_number, true, numberA, false, _numberB}), do: numberA + defp fizzbuzz({_number, false, _numberA, true, numberB}), do: numberB + defp fizzbuzz({_number, true, numberA, true, numberB}), do: "#{numberA}#{numberB}" + + + defp fizzbot({_number, true, respA, true, respB, true, respC}), do: "#{respA}#{respB}#{respC}" + defp fizzbot({_number, true, respA, false, _respB, false, _respC}), do: respA + defp fizzbot({_number, false, _respA, true, respB, false, _respC}), do: respB + defp fizzbot({_number, false, _respA, false, _respB, true, respC}), do: respC + defp fizzbot({number, false, _respA, false, _respB, false, _respC}), do: number + defp fizzbot({_number, true, respA, true, respB, false, _respC}), do: "#{respA}#{respB}" + defp fizzbot({_number, true, respA, false, _respB, true, respC}), do: "#{respA}#{respC}" + defp fizzbot({_number, false, _respA, true, respB, true, respC}), do: "#{respB}#{respC}" + +end diff --git a/elixir_fizz/lib/elixir_fizz.ex b/elixir_fizz/lib/elixir_fizz.ex new file mode 100644 index 0000000..1a718b7 --- /dev/null +++ b/elixir_fizz/lib/elixir_fizz.ex @@ -0,0 +1,79 @@ +defmodule ElixirFizz do + alias ElixirFizz.Bot + + @base_url "https://api.noopschallenge.com" + + # Only you have to open the iEX in this project and: + # + # > iex -S mix + # + # Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) + # iex(1)> ElixirFizz.start + # + def start() do + start_time = :os.system_time(:millisecond) + "/fizzbot" + |> start_fizzbot_for_first_time() + |> ask_to_fizzbot() + end_time = :os.system_time(:millisecond) + duration = (end_time - start_time)/1000 + IO.puts "Interview finalized in #{duration} seconds" + end + + defp start_fizzbot_for_first_time(starter_uri), do: get(starter_uri) + + defp ask_to_fizzbot(fizzbot) do + next_fizzbot = get(fizzbot["nextQuestion"]) + show_rules(next_fizzbot) + answer = Bot.answer({next_fizzbot, Map.has_key?(next_fizzbot, "numbers")}) + answer_to_fizzbot({next_fizzbot, fizzbot["nextQuestion"], answer}) + end + + defp answer_to_fizzbot({fizzbot, question_uri, answer}) do + IO.puts "\nSe generĂ³ la siguiente respuesta: #{answer}" + fizzbot_answer = post(question_uri, %{"answer" => answer}) + validate_answer({fizzbot_answer, fizzbot, question_uri}) + end + + defp show_rules(fizzbot) do + validate_rules = fn + true -> + IO.puts ":: GitHub :: Meet the Noobs :: Fizzbot Message ::" + IO.puts fizzbot["message"] + IO.puts "The rules are:" + IO.inspect fizzbot["rules"] + IO.puts "\nYour numbers are:" + IO.inspect fizzbot["numbers"] + false -> + IO.puts fizzbot["message"] + end + validate_rules.(Map.has_key?(fizzbot, "rules")) + end + + defp validate_answer({fizzbot_answer, fizzbot, question_uri}) do + validate_fizzbot = fn + "correct" -> + IO.puts "\nAnswer Correct!!" + IO.puts "Next question...\n" + ask_to_fizzbot(fizzbot_answer) + "incorrect" -> + IO.puts fizzbot_answer["message"] + IO.puts "Try again!!" + answer_to_fizzbot({fizzbot, question_uri}) + "interview complete" -> + IO.puts " DONE " + end + validate_fizzbot.(fizzbot_answer["result"]) + end + + defp get(uri) do + {:ok, response} = HTTPoison.get("#{@base_url}#{uri}") + Poison.decode!(response.body) + end + + defp post(uri, body) do + {:ok, response} = HTTPoison.post("#{@base_url}#{uri}", Poison.encode!(body), [{"Content-type", "application/json"}]) + Poison.decode!(response.body) + end + +end diff --git a/elixir_fizz/mix.exs b/elixir_fizz/mix.exs new file mode 100644 index 0000000..3c83c0f --- /dev/null +++ b/elixir_fizz/mix.exs @@ -0,0 +1,26 @@ +defmodule ElixirFizz.MixProject do + use Mix.Project + + def project do + [ + app: :elixir_fizz, + version: "0.1.0", + elixir: "~> 1.8", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger] + ] + end + + defp deps do + [ + {:httpoison, "~> 1.0", override: true}, + {:poison, "~> 3.1"} + ] + end +end diff --git a/elixir_fizz/mix.lock b/elixir_fizz/mix.lock new file mode 100644 index 0000000..d33695c --- /dev/null +++ b/elixir_fizz/mix.lock @@ -0,0 +1,12 @@ +%{ + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "1.5.1", "0f55b5b673b03c5c327dac7015a67cb571b99b631acc0bc1b0b98dcd6b9f2104", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, +}