12
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 4

Elixir Livebook でじゃんけんを実装する

Last updated at Posted at 2022-12-19

はじめに

Qiita の記事には「じゃんけん」の実装が結構多いようです

色々な言語で実装されています

というわけで、 Elixir で実装してみます

せっかくなので Livebook を使います

実装したノートブックはこちら

じゃんけんとは

じゃんけんは手の形で勝敗を決する遊びです

手の形には以下の3種類があります

  • ✊グー
  • ✌️チョキ
  • ✋パー

参加者は「じゃんけんぽん」の掛け声に合わせていずれかの手の形を提示します

グーはチョキに勝ち、チョキはパーに勝ち、パーはグーに勝ちます

同じ手の形の場合は「あいこ」になります

セットアップ

入出力に便利な Kino をインストールしておきます

Mix.install([
  {:kino, "~> 0.8"}
])

手と結果の定義

じゃんけんをコード化するため、以下のように定義します

手の形

  • 0 = グー
  • 1 = チョキ
  • 2 = パー
hands = [
  {0, "グー"},
  {1, "チョキ"},
  {2, "パー"}
]

スクリーンショット 2022-12-19 15.14.00.png

Kino.Input.select に渡すために Tuple の List にしています

勝敗結果

  • 0 = あいこ
  • 1 = 勝ち
  • 2 = 負け
results = ["あいこ", "勝ち", "負け"]

スクリーンショット 2022-12-19 15.14.50.png

この並びには意味があります

手の選択

自分の手を選択できるようにします

my_hand_input = Kino.Input.select("あなたの手", hands) 

select.gif

my_hand = Kino.Input.read(my_hand_input)

スクリーンショット 2022-12-19 15.18.53.png

相手の手はランダムにします

# ランダムに決定
opponents_hand = 0..2 |> Enum.random

スクリーンショット 2022-12-19 15.21.04.png

手の名称を表示する関数を用意します

get_hand_name = fn hand ->
  hands |> Enum.at(hand) |> elem(1)
end
IO.puts("あなたの手: #{get_hand_name.(my_hand)}")
IO.puts("あいての手: #{get_hand_name.(opponents_hand)}")

スクリーンショット 2022-12-19 15.31.36.png

勝敗判定

では肝となる勝敗判定を実装します

まず「あいこ」は単純で、自分と相手の数字が同じ場合です

つまり組み合わせでは

  • 0:0 (グー:グー)
  • 1:1 (チョキ:チョキ)
  • 2:2 (パー:パー)

続いて勝ちのパターンを見てみましょう

  • 0:1 (グー:チョキ)
  • 1:2 (チョキ:パー)
  • 2:0 (パー:グー)

最後に負けのパターンです

  • 0:2 (グー:パー)
  • 1:0 (チョキ:グー)
  • 2:1 (パー:チョキ)

勝敗判定を関数とみなすと、以下のように考えられます

  • f(0,0) = 0
  • f(0,1) = 1
  • f(0,2) = 2
  • f(1,0) = 2
  • f(1,1) = 0
  • f(1,2) = 1
  • f(2,0) = 1
  • f(2,1) = 2
  • f(2,2) = 0

これを満たす関数を定義すれば勝敗判定できそうです

勘のいい人はもう理解したと思います
(同じ解法はおそらくすでに世にあるものと思います)

相手の手 - 自分の手は以下のようになります

  • d(0,0) = 0
  • d(0,1) = 1
  • d(0,2) = 2
  • d(1,0) = -1
  • d(1,1) = 0
  • d(1,2) = 1
  • d(2,0) = -2
  • d(2,1) = -1
  • d(2,2) = 0

これに 3 を足してみます

  • d'(0,0) = 3
  • d'(0,1) = 4
  • d'(0,2) = 5
  • d'(1,0) = 2
  • d'(1,1) = 3
  • d'(1,2) = 4
  • d'(2,0) = 1
  • d'(2,1) = 2
  • d'(2,2) = 0

3 で割った余りを計算します

  • d''(0,0) = 0
  • d''(0,1) = 1
  • d''(0,2) = 2
  • d''(1,0) = 2
  • d''(1,1) = 0
  • d''(1,2) = 1
  • d''(2,0) = 1
  • d''(2,1) = 2
  • d''(2,2) = 0

この d''() 関数は f() 関数と同じですね

というわけで、じゃんけんの勝敗判定は以下の式で表せます

fn x_hand, y_hand ->
  rem(y_hand - x_hand, 3)
end

手の名称表示、結果の名称表示も入れて、以下のような関数を定義しましょう

judge = fn x_hand, y_hand ->
  IO.puts("あなたの手: #{get_hand_name.(x_hand)}")
  IO.puts("あいての手: #{get_hand_name.(y_hand)}")
  result = rem(y_hand - x_hand, 3)
  result_name = Enum.at(results, result)
  IO.puts("結果: #{result_name}")
  result_name
end

呼び出してみます

judge.(my_hand, opponents_hand)

スクリーンショット 2022-12-19 15.48.46.png

全ての組み合わせでテスト

では全ての組み合わせを作ってテストします

%{
  "あいての手\\あなたの手" => Enum.map(hands, &elem(&1, 1))
}
|> Map.merge(
  for {x_hand, x_hand_name} <- hands, into: %{} do
    {
      x_hand_name,
      Enum.map(hands, fn {y_hand, _} ->
        judge.(x_hand, y_hand)
      end)
    }
  end
)
|> Kino.DataTable.new()

スクリーンショット 2022-12-19 15.50.02.png

どの組み合わせでも正しい結果になっていますね

まとめ

こういうのは気分転換できていいですね

12
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?