元ネタ
概要
剰余算(%)を活用して、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関数は、はなっからプラス側で値を返すとか、面倒くさいなお前。
ソース
ソース (ピックアップ)
ピックアップ
privatestaticintGetResult(intpalyer_hand,intpc_hand){// (1) 基となる差を求める。intresult=palyer_hand-pc_hand;// (2) 除数であまりを求め、±除数の範囲に収める。result%=3;// (3) 除数を1回加えることでプラス化。result+=3;// (4) 再度除数であまりを求め、+除数の範囲に収める。result%=3;returnresult;}ソース (全体)
@ri_さんに倣って、入力想定文字列は グー、 チョキ、 パー。
@tadsanさんに習って、出力は絵文字に置き換え。
(+ 検証用に総当りで結果を出力)
if文なしでじゃんけん(C#) - paiza.IO
https://paiza.io/projects/SDonsIDtCzfYQh6O_lXVbgIdeone.com
https://ideone.com/OuRqbr
C#
usingSystem;usingSystem.Collections.Generic;publicclassHello{privatestaticreadonlyDictionary<string,int>HAND_CODE_DICT=newDictionary<string,int>(){{"グー",0},{"チョキ",1},{"パー",2},};privatestaticreadonlystring[]HAND_LABELS=newstring[]{"✊","✌","🖐"};privatestaticreadonlystring[]RESULT_LABELS=newstring[]{"あいこ","あなたの負け","あなたの勝ち"};privatestaticintGetResult(intpalyer_hand,intpc_hand){intresult=palyer_hand-pc_hand;// -3 < n < 3result%=3;// 0 < n < 6result+=3;// 0 <= n < 3result%=3;returnresult;}publicstaticvoidMain(){// Your code here!System.Console.WriteLine("Hellow C#");foreach(stringplayer_handinnewstring[]{"グー","チョキ","パー"}){foreach(stringpc_handinnewstring[]{"グー","チョキ","パー"}){intplayer_hand_code=HAND_CODE_DICT[player_hand];intpc_hand_code=HAND_CODE_DICT[pc_hand];intresult_code=GetResult(player_hand_code,pc_hand_code);System.Console.WriteLine(string.Format("[{0} v.s. {1}] {2}",newobject[]{HAND_LABELS[player_hand_code],HAND_LABELS[pc_hand_code],RESULT_LABELS[result_code],}));}}}}output
Hellow C#
[✊ v.s. ✊] あいこ
[✊ v.s. ✌] あなたの勝ち
[✊ v.s. 🖐] あなたの負け
[✌ v.s. ✊] あなたの負け
[✌ v.s. ✌] あいこ
[✌ v.s. 🖐] あなたの勝ち
[🖐 v.s. ✊] あなたの勝ち
[🖐 v.s. ✌] あなたの負け
[🖐 v.s. 🖐] あいこ
あとがき
剰余算の活用は、ゲーム系では昔から結構使われるんじゃないかな。シンプルな算術演算のためCPUコストも少なく。コードも複雑にはならないので、結構美味しいテクニック。
私が今まで剰余算をよく使ったのは。
- キャラクターのアニメーションのループ
- メニュー画面などでの、画面選択肢のループ
- キャラクターの向き(角度)のループ
- テーブル表示で行を交互に色付け
業務系ではあんま活用したいシーンがない様で。(4)で剰余算使っていたときに、年配のSEに驚かれたことがある。
なお、『連想配列はズルくない?』とも思ったけど、まぁご愛嬌で。(入力文字列をコード化するしてるだけだから許して)
そう言えば、『計算機は計算が得意』 で 『条件分岐は苦手』と言う定説は昔からあるけど。コスト的には何倍あるのだろう?