三目並べ #3

冗長だったのでもっといい加減に。
3x3以外の枡、それはもはや三目並べではない。
不正入力に対してもう少しマシな動作に。

#include <stdio.h>

typedef enum { EMPTY, WHITE, BLACK, DRAW } side;

static void init(void);
static void show(void);
static char expr(int i);
static int input(void);
static int validate(int c);
static side judge(int p);

static side grid[9];
static side turn;
static const char symbol[] = {' ', 'O', 'X', '?'};
static const char esymbol[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'};

int main(void)
{
    int c, p;
    side winner = EMPTY;

    init();
    for (;;) {
        printf("%c's turn\n", symbol[turn]);
        show();
        fputs("where do you put? ", stdout);
        if ((p = validate(c = input())) >= 0) {
            winner = judge(p);
            if (winner != EMPTY) break;
            if (turn == WHITE) turn = BLACK; else turn = WHITE;
        } else {
            printf("invalid input: %c\n", c);
        }
    }
    show();
    if (winner == DRAW) {
        puts("draw game");
    } else {
        printf("winner is %c\n", symbol[winner]);
    }
    return 0;
}

static void init(void)
{
    int i;

    for (i = 0; i < 9; i++) grid[i] = EMPTY;
    turn = WHITE;
}

static void show(void)
{
    printf("%c|%c|%c\n", expr(0), expr(1), expr(2));
    puts("-+-+-");
    printf("%c|%c|%c\n", expr(3), expr(4), expr(5));
    puts("-+-+-");
    printf("%c|%c|%c\n", expr(6), expr(7), expr(8));
}

static char expr(int i)
{
    return grid[i] == EMPTY ? esymbol[i] : symbol[grid[i]];
}

static int input(void)
{
    int c = getchar(), d = c;

    while (d != '\n') d = getchar();
    return c;
}

static int validate(int c)
{
    int i;

    for (i = 0; i < 9; i++) {
        if (esymbol[i] == c && grid[i] == EMPTY) {
            grid[i] = turn;
            return i;
        }
    }
    return -1;
}

static side judge(int p)
{
    static const int line[][3] = {
        {0, 1, 2}, {3, 4, 5}, {6, 7, 8},
        {0, 3, 6}, {1, 4, 7}, {2, 5, 8},
        {0, 4, 8}, {2, 4, 6}
    };
    static const int pos[][5] = {
        {0, 3, 6, -1, 0}, {0, 4, -1, 0, 0}, {0, 5, 7, -1, 0},
        {1, 3, -1, 0, 0}, {1, 4, 6, 7, -1}, {1, 5, -1, 0, 0},
        {2, 3, 7, -1, 0}, {2, 4, -1, 0, 0}, {2, 5, 6, -1, 0}
    };
    int i;
    side g;

    for (i = 0; pos[p][i] >= 0; i++) {
        const int *l = line[pos[p][i]];
        if ((g = grid[l[0]]) == turn && g == grid[l[1]] && g == grid[l[2]]) return turn;
    }
    for (i = 0; i < 9; i++) if (grid[i] == EMPTY) return EMPTY;
    return DRAW;
}