先日のコードが2時間でできた物だったので、制限時間の残り1時間を使ってリファクタリング。
yahooニュースとかについてたコメントで麻雀を知らない人が不利とかかかれてたりしたけど、そういう人は文章をきちんと読まず早とちりするとか、仕様から外れた実装するとか別の意味でプログラマとしての最低用件を満たせないような気がする。
ちゃんと↓が書かれてるんだけどねぇ。
麻雀を知らない人は、順子・刻子・アタマ・待ちといった用語の意味だけ調べてから解答に取りかかってください。これを調べる時間は計算外とします。
麻雀を知らない人は開始する時間が遅れるだけで、よけいな思考が増えるわけでは無いのだが・・・
あとハッシュタグ界隈ではチートイツ対応とか4枚のカラテン対応とか機能拡張してるみたいだけど、自分はスルーで。
これらの対応も隠れた要件と見ることもできるけど、麻雀を知らない人でもできるテストなんだし、この辺を3時間の制限時間内で実装する価値はなさそうという判断。
と、いったところで続きにコードをぺたり。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_OUT_STR_SIZE 30
// 入力されたハイは、種類ごとに配列0~8にカウントする。ハイを使用したら関係する配列の要素をデクリメントして管理。
#define HAI_ARRAY_SIZE 9
// アタマやメンツを10進数で記述している。各桁の値がそれぞれのハイを表す。別に昇順にソートされてる必要はない。
#define WAIT_HEAD_SIZE 9
static const int wait_head[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
#define WAIT_MEMBER_SIZE 24
static const int wait_mem[] = {11, 12, 13, 22, 23, 24, 33, 34, 35, 44, 45, 46, 55, 56, 57, 66, 67, 68, 77, 78, 79, 88, 89, 99};
#define PATTERN_HEAD_SIZE 9
static const int head_pt[] = {11, 22, 33, 44, 55, 66, 77, 88, 99};
#define PATTERN_MEMBER_SIZE 16
static const int mem_pt[] = {111, 123, 222, 234, 333, 345, 444, 456, 555, 567, 666, 678, 777, 789, 888, 999};
typedef struct {
const int *wait;
const int wait_size;
} PATTERN_MATCH_CONDITIONS;
// targetで指定されたハイが、valsに存在するか確認する。
static int match_vals(int target, const int *vals)
{
int test[HAI_ARRAY_SIZE];
memcpy(test, vals, sizeof(test));
while(target){
int tmp = (target % 10) -1;
target /= 10;
test[tmp]--;
if(test[tmp] < 0) return 0;
}
return 1;
}
// targetで使用したハイをvalsから取っ払う
static void sub_vals(int target, int *vals)
{
while(target){
int tmp = (target % 10) -1;
target /= 10;
vals[tmp]--;
}
return;
}
static int is_last(const int *vals)
{
int tmp = 0;
int i;
for(i = 0; i < HAI_ARRAY_SIZE; i++){
tmp += vals[i];
}
return (tmp <= 2);
}
static void pattern_match(const PATTERN_MATCH_CONDITIONS *pmc, const char *base, const int *vals, int lastmem)
{
int i;
if(is_last(vals)){
// 待ちの枚数になったら待ちをパターンマッチする。
for(i = 0; i < pmc->wait_size; i++){
if(match_vals(pmc->wait[i], vals)){
printf("%s[%d]\n", base, pmc->wait[i]);
// 他にマッチしようがないのですぐreturn
return;
}
}
} else {
// メンツが作れる枚数の内は、再帰を使ってなめていく。
// lastmemは同じ出力をしないため、配列の自分自身未満のパターンを除外する。
for(i = lastmem; i < PATTERN_MEMBER_SIZE; i++){
if(match_vals(mem_pt[i], vals)){
char tmp[6];
int rest[HAI_ARRAY_SIZE];
char out[MAX_OUT_STR_SIZE];
strcpy(out, base);
memcpy(rest, vals, sizeof(rest));
sprintf(tmp, "(%d)", mem_pt[i]);
strcat(out, tmp);
sub_vals(mem_pt[i], rest);
pattern_match(pmc, out, rest, i);
}
}
}
return;
}
static int getdata(int *master)
{
int i;
int tmp;
// 配られるハイの数は13固定
for(i = 0; i < 13; i++){
tmp = getchar();
if(tmp == EOF) return -1;
// 文字列'1'~'9'を数字の0~8に変換
tmp = tmp - 0x30 - 1;
if(tmp < 0 || tmp > 8) return -1;
master[tmp]++;
}
return 0;
}
int main(int argc, char *argv[])
{
char out[MAX_OUT_STR_SIZE] = {0};
int master[HAI_ARRAY_SIZE] = {0};
// おかしなデータが混ざってたら止める。
if(getdata(master) != 0) return -1;
// アタマ待ちのパターン
PATTERN_MATCH_CONDITIONS pmc = {wait_head, WAIT_HEAD_SIZE};
pattern_match(&pmc, out, master, 0);
// メンツ待ちのパターン
// 先にアタマを確定させる
int i;
PATTERN_MATCH_CONDITIONS pmc_mem = {wait_mem, WAIT_MEMBER_SIZE};
for(i = 0; i < PATTERN_HEAD_SIZE; i++)
{
if(match_vals(head_pt[i], master)){
int rest[HAI_ARRAY_SIZE];
sprintf(out, "(%d)", head_pt[i]);
memcpy(rest, master, sizeof(rest));
sub_vals(head_pt[i], rest);
pattern_match(&pmc_mem, out, rest, 0);
}
}
return 0;
}
Comments are closed here.