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

Easy AtCoder

easy_atcoder.livemd

Easy AtCoder

Mix.install(
  [
    {:httpoison, "~> 2.2.1"},
    {:floki, "~> 0.35.2"},
    {:kino, "~> 0.12.0"},
    {:mock, "~> 0.3.8"}
  ],
  config: [my_app: [use_myio: true]]
)

modules

defmodule AtCoder do
  import Mock
  defstruct [:contest_name, :problem_name, :samples_in, :samples_out]

  def new(contest_name, problem_name) do
    url =
      "https://atcoder.jp/contests/#{contest_name}/tasks/#{contest_name}_#{problem_name}"

    doc = HTTPoison.get!(url)
    {:ok, html} = Floki.parse_document(doc.body)
    section_list = Floki.find(html, "section")

    samples_in =
      section_list
      |> Enum.filter(fn {_, _, [hd | _]} -> String.starts_with?(Floki.text(hd), "入力例") end)
      |> Enum.map(fn {_, _, [_hd | tail]} -> Floki.text(hd(tail)) end)
      |> Enum.map(fn x -> String.replace(x, "\r\n", "\n") end)

    samples_out =
      section_list
      |> Enum.filter(fn {_, _, [hd | _]} -> String.starts_with?(Floki.text(hd), "出力例") end)
      |> Enum.map(fn {_, _, [_hd | tail]} -> Floki.text(hd(tail)) end)
      |> Enum.map(fn x -> String.replace(x, "\r\n", "\n") end)

    struct!(%AtCoder{},
      contest_name: contest_name,
      problem_name: problem_name,
      samples_in: samples_in,
      samples_out: samples_out
    )
  end

  def run_main(string_in) do
    {:ok, file} = StringIO.open(string_in)

    with_mock IO, [:passthrough],
      getn: fn :stdio, a, b -> passthrough([file, a, b]) end,
      getn: fn a, b -> passthrough([file, a, b]) end,
      read: fn :stdio, option -> passthrough([file, option]) end,
      read: fn option -> passthrough([file, option]) end,
      puts: fn :stdio, item -> passthrough([file, item]) end,
      puts: fn item -> passthrough([file, item]) end do
      Main.main()
    end

    {:ok, {_, result}} = StringIO.close(file)
    result
  end

  def judge(string_in, string_out) do
    result = run_main(string_in)

    if string_out == result do
      IO.puts("OK")
      true
    else
      IO.inspect(string_in, label: "Input")
      IO.inspect(string_out, label: "Expected")
      IO.inspect(result, label: "Received")
      false
    end
  end

  def judge_all(%AtCoder{samples_in: samples_in, samples_out: samples_out}) do
    n =
      for {{sample_in, sample_out}, n} <- Enum.zip([samples_in, samples_out]) |> Enum.with_index() do
        IO.puts("-- TEST #{n} --")
        AtCoder.judge(sample_in, sample_out)
      end
      |> Enum.count()

    IO.puts("Passed #{n} of #{length(samples_in)}")
  end
end
contest_name = Kino.Input.text("コンテスト名")
problem_name = Kino.Input.text("問題名")
judge_all = Kino.Control.button("入力例で実行")

Kino.listen(judge_all, fn _event ->
  atcoder = AtCoder.new(Kino.Input.read(contest_name), Kino.Input.read(problem_name))
  AtCoder.judge_all(atcoder)
end)

input_string = Kino.Input.textarea("入力")
run = Kino.Control.button("実行")

Kino.listen(run, fn _event ->
  input_string
  |> Kino.Input.read()
  |> AtCoder.run_main()
  |> IO.puts()
end)

frame = Kino.Frame.new()

Kino.Frame.render(
  frame,
  Kino.Layout.grid(
    [
      Kino.Layout.grid([contest_name, problem_name, judge_all], columns: 1),
      Kino.Layout.grid([input_string, run], columns: 1)
    ],
    columns: 2
  )
)

回答

defmodule Main do
  def next_token(acc \\ "") do
    case IO.getn(:stdio, "", 1) do
      " " -> acc
      "\n" -> acc
      x -> next_token(acc <> x)
    end
  end

  def input(), do: IO.read(:line) |> String.trim()
  def ii(), do: next_token() |> String.to_integer()
  def li(), do: input() |> String.split(" ") |> Enum.map(&amp;String.to_integer/1)

  def main() do
    x = input()
    IO.puts(x)
  end
end

実行

frame