#include <stdio.h> #include <stdlib.h> #include <conio.h> #define GU 0 #define CHOKI 1 #define PA 2 #define DRAWN 0 #define WON 1 #define LOST 2 const char* hands[3] = {"Gu", "Choki", "Pa"}; int wonCount = 0; int lostCount = 0; void IfDrawn(void); void IfWon(void); void IfLost(void); void (*callbacks[3])(void) = {IfDrawn, IfWon, IfLost}; unsigned char GetResult(unsigned char a, unsigned char d) { puts(hands[a]); puts(" vs "); puts(hands[d]); puts(""); return (unsigned char) ((0x91224 >> ((a << 2 | d) << 1)) & 0x3); } unsigned char GetHand(void) { unsigned char v = 0; puts( "Input! (0 is GU, 1 is CHOKI, another key is PA)" ); v = (unsigned char) (_getch() - '0'); return ((((v >> 1) + 0x7f) >> 7) << 1) | (v & ((((v >> 1) + 0x7f) >> 7) ^ 0x1)); } void CallProc(void) { callbacks[GetResult(GetHand(), (unsigned char) (rand() % 3))](); } void IfDrawn(void) { puts("DRAWN\n"); CallProc(); } void IfWon(void) { puts("WON\n"); wonCount++; } void IfLost(void) { puts("LOST\n"); lostCount++; } int main(void) { CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); CallProc(); printf_s("You won %d time(s) and lost %d time(s).", wonCount, lostCount); _getch(); return 0; }
・変数や関数の名前、および英語はテキトーです。本来、こんな名前の付け方はすべきでありません。
・Windows 以外の環境では検証していません。恐らく _getch と printf_s は置換が必要です。
・これはあくまで実験です。常識的な状況では、どう考えても、普通に条件分岐するほうが賢明です。
呼び出し順に行きます。まず CallProc 関数について。最初に、rand で乱数を取得し、それを 3 で割った余りを計算します。すると 0, 1, 2 の何れかになりますので、それを CPU の手とします。ただし、GetResult が先に呼ばれる場合もあります。C/C++ では、引数の評価順序は定義されていないので、順序に依存したコードは厳禁です。でも今回は、どちらが先でも構わないから、問題ありません。
次に、プレーヤーの手を GetHand 関数で取得します。まず、_getch でキーボードからの入力を待ちます。入力された文字が '0' なら 0 を、'1' なら 1 を、それ以外なら 2 を、GetHand の結果として返します。最後に奇妙な式がありますが、これは、入力された文字を数値 0, 1, 2 に変換するためのものです。
ここまでで、プレーヤーと CPU の手を取得できたので、次に GetResult 関数で勝敗の判定をします。ここにも複雑な式がありますが、これは結果をテーブル(0x91224)から取得するためのものです。他にも方法はあると思うので、まだまだ研究の余地があるはずです。
しかしこう云うビット単位の処理は、アセンブラで書いたほうが早いかも知れませんね。
で、GetResult の戻り値をインデックスとして、callbacks に代入されている該当関数を呼び出します。勝ち(IfWon)と負け(IfLost)の場合、メッセージを表示してカウンタを増やし、処理を戻します。あいこ(IfDrawn)の場合ですが、もう一度 CallProc を呼び出します。勝敗が付くまで繰り返されます。もし延々と勝負が続いた場合、スタックオーバーフローが発生する可能性がありますが、その可能性は無視できるほど小さいので、問題ありません。
以上が 1 勝負ですので、これを 10 回繰り返し、最後に勝敗数を表示して終わりです。普通 for とか使うんでしょうけど、for もまた条件分岐なので、ここでは関数呼び出しそのものを 10 回繰り返すことにしました。