#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 回繰り返すことにしました。