元ネタ
概要
剰余算(%)を活用して、if文なしでの 勝敗の判定
を行う。
剰余算(%)を活用すると、値を円環させた形で値を範囲内に丸めることが出来る。簡単で非常に便利で、応用方法もたくさんあるので、布教の意味も含めて兼ねて書いてみた。
剰余算による対戦結果表
ぶっちゃけExcelでテストした(笑)
player | pc (cpu) | calc | result | mod (result) | output (label) |
---|---|---|---|---|---|
0 (✊) | 0 (✊) | 0 - 0 | 0 | 0 | [0] あいこ |
0 (✊) | 1 (✌) | 0 - 1 | -1 | 2 | [2] 勝ち |
0 (✊) | 2 (🖐) | 0 - 2 | -2 | 1 | [1] 負け |
1 (✌) | 0 (✊) | 1 - 0 | 1 | 1 | [1] 負け |
1 (✌) | 1 (✌) | 1 - 1 | 0 | 0 | [0] あいこ |
1 (✌) | 2 (🖐) | 1 - 2 | -1 | 2 | [2] 勝ち |
2 (🖐) | 0 (✊) | 2 - 0 | 2 | 2 | [2] 勝ち |
2 (🖐) | 1 (✌) | 2 - 1 | 1 | 1 | [1] 負け |
2 (🖐) | 2 (🖐) | 2 - 2 | 0 | 0 | [0] あいこ |
- 2つの手の差のベクトルを自然数に正規化して、テーブルにマッピングさせるイメージ。
-
あいこ
、負け
、勝ち
、の3パターンのため、3の余りを求めて、0 ~ 2
に丸める。 - マイナスの場合は
除数
を加えてプラス化さてから余りを求めることで、円環位置をズらさず丸める。
※ ExcelのMOD関数は、はなっからプラス側で値を返すとか、面倒くさいなお前。
ソース
ソース (ピックアップ)
private static int GetResult(int palyer_hand, int pc_hand) {
// (1) 基となる差を求める。
int result = palyer_hand - pc_hand;
// (2) 除数であまりを求め、±除数の範囲に収める。
result %= 3;
// (3) 除数を1回加えることでプラス化。
result += 3;
// (4) 再度除数であまりを求め、+除数の範囲に収める。
result %= 3;
return result;
}
ソース (全体)
@ri_ さんに倣って、入力想定文字列は グー
、 チョキ
、 パー
。
@tadsan さんに習って、出力は絵文字に置き換え。
(+ 検証用に総当りで結果を出力)
-
if文なしでじゃんけん(C#) - paiza.IO
https://paiza.io/projects/SDonsIDtCzfYQh6O_lXVbg -
Ideone.com
https://ideone.com/OuRqbr
using System;
using System.Collections.Generic;
public class Hello{
private static readonly Dictionary<string, int> HAND_CODE_DICT = new Dictionary<string, int>() {
{"グー", 0},
{"チョキ", 1},
{"パー", 2},
};
private static readonly string[] HAND_LABELS = new string[] {"✊", "✌", "🖐"};
private static readonly string[] RESULT_LABELS = new string[] {
"あいこ",
"あなたの負け",
"あなたの勝ち"
};
private static int GetResult(int palyer_hand, int pc_hand) {
int result = palyer_hand - pc_hand;
// -3 < n < 3
result %= 3;
// 0 < n < 6
result += 3;
// 0 <= n < 3
result %= 3;
return result;
}
public static void Main(){
// Your code here!
System.Console.WriteLine("Hellow C#");
foreach(string player_hand in new string[]{"グー", "チョキ", "パー"}) {
foreach(string pc_hand in new string[]{"グー", "チョキ", "パー"}) {
int player_hand_code = HAND_CODE_DICT[player_hand];
int pc_hand_code = HAND_CODE_DICT[pc_hand];
int result_code = GetResult(player_hand_code, pc_hand_code);
System.Console.WriteLine(string.Format("[{0} v.s. {1}] {2}", new object[] {
HAND_LABELS[player_hand_code],
HAND_LABELS[pc_hand_code],
RESULT_LABELS[result_code],
}));
}
}
}
}
Hellow C#
[✊ v.s. ✊] あいこ
[✊ v.s. ✌] あなたの勝ち
[✊ v.s. 🖐] あなたの負け
[✌ v.s. ✊] あなたの負け
[✌ v.s. ✌] あいこ
[✌ v.s. 🖐] あなたの勝ち
[🖐 v.s. ✊] あなたの勝ち
[🖐 v.s. ✌] あなたの負け
[🖐 v.s. 🖐] あいこ
あとがき
剰余算の活用は、ゲーム系では昔から結構使われるんじゃないかな。シンプルな算術演算のためCPUコストも少なく。コードも複雑にはならないので、結構美味しいテクニック。
私が今まで剰余算をよく使ったのは。
- キャラクターのアニメーションのループ
- メニュー画面などでの、画面選択肢のループ
- キャラクターの向き(角度)のループ
- テーブル表示で行を交互に色付け
業務系ではあんま活用したいシーンがない様で。(4)で剰余算使っていたときに、年配のSEに驚かれたことがある。
なお、『連想配列はズルくない?
』とも思ったけど、まぁご愛嬌で。(入力文字列をコード化するしてるだけだから許して)
そう言えば、『計算機は計算が得意
』 で 『条件分岐は苦手
』と言う定説は昔からあるけど。コスト的には何倍あるのだろう?