0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

書籍 ダイテル C言語プログラミング 練習問題 解答 8章

0
Last updated at Posted at 2025-12-30
書籍

image.png

8.5: キーボードから1文字入力し、文字操作ライブラリの各関数を使ってその文字を検査するプログラム
source
#include <stdio.h>
#include <conio.h>
#include <ctype.h>

int main()
{
    char c;


    c = getchar();

    if (isalnum(c))
    {
        printf("英数字です\n");
    }
    else
    {
        printf("英数字ではありません\n");
    }

    if (isalpha(c))
    {
        printf("英字です\n");
    }
    else
    {
        printf("英字ではありません\n");
    }

    if (iscntrl(c))
    {
        printf("制御文字です\n");
    }
    else
    {
        printf("制御文字ではありません\n"); 
    }

    if (isdigit(c))
    {
        printf("数字です\n"); 
    }
    else
    {
        printf("数字ではありません\n");
    }

    if (isgraph(c))
    {
        printf("印字可能文字です(スペースを含まない)\n");  
    }
    else
    {
        printf("印字文字ではありません\n");
    }

    if (islower(c))
    {
        printf("英小文字です\n");
    }
    else
    {
        printf("英小文字ではありません\n");
    }
  
    if (isprint(c)) 
    {
        printf("印字可能文字です(スペースを含む)\n");
    }
    else
    {
        printf("印字可能文字ではありません\n");
    }

    if (ispunct(c))
    {
        printf("区切り文字です\n");
    }
    else
    {
        printf("区切り文字ではありません\n");
    }

    if (isspace(c))
    {
        printf("標準の空白文字です\n");
    }
    else
    {
       printf("標準の空白文字ではありません\n");
    }
 
    if (isupper(c))
    {
       printf("英大文字です\n");
    }
    else 
    {
       printf("英大文字ではありません\n");
    }

    if (isxdigit(c))
    {
       printf("16進数字です\n");
    }
    else
    {
       printf("16新文字ではありません\n");
    }

    getch();
    return 0;
}
8.6: 関数getsを使って文字型配列s[100]にテキストを1行入力するプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <ctype.h>

void stringToUpper(char *s)
{
    while (*s) {
        *s = toupper((unsigned char)*s);
        s++;
    }
}

void stringToLower(char *s)
{
    while (*s) {
        *s = tolower((unsigned char)*s);
        s++;
    }
}

int main()
{
    char buf[100];


    gets(buf);
    printf("入力後\n");
    printf("%s\n", buf);

    stringToUpper(buf);
    printf("全て大文字\n");
    printf("%s\n", buf);

    stringToLower(buf);
    printf("全て小文字\n");
    printf("%s\n", buf);
    
    getch();
    return 0;
}
8.7: 整数値を表す4個の文字列を入力し、それらの文字列をint値に変換してから合計し、その合計値をプリントするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int main()
{
    int i;
    int sum = 0;
    char buf[100];

    for (i = 1; i <= 4; i++)
    {
       gets(buf);
       sum += atoi(buf);
    }
   
    printf("sum %d\n", sum);
    getch();
    return 0;
}
8.8: 浮動小数点値を表す4個の文字列を入力し、それらの文字列をdouble値に変換してから合計し、その合計値をプリントするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int main()
{
    int i;
    double sum = 0;
    char buf[100];

    for (i = 1; i <= 4; i++)
    {
       gets(buf);
       sum += atof(buf);
    }
   
    printf("sum %lf\n", sum);
    getch();
    return 0;
}
8.9: 関数strcmpを使って、ユーザが入力した2つの文字列を比較するプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char buf1[100];
    char buf2[100];
    int result;
 
    printf("1つめの文字列を入力してください\n");
    fgets(buf1, sizeof(buf1), stdin);
 
    printf("2つめの文字列を入力してください\n");
    fgets(buf2, sizeof(buf2), stdin);

    result = strcmp(buf1, buf2);

    if (result < 0)
    {
        printf("1つめの文字列は2つめの文字列より小さいです\n");
    }
    else if (result == 0)
    {
        printf("1つめの文字列は2つめの文字列と同じです\n");
    }
    else
    {
        printf("1つめの文字列は2つめの文字列より大きいです\n");
    }
   
    getch();
    return 0;
}
8.10: 関数strncmpを使って、ユーザが入力した2つの文字列を比較するプログラム(比較する文字列の長さも入力する必要あり)
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char buf1[100];
    char buf2[100];
    int result;
    int n;
 
    printf("比較する文字列の長さを入力してください\n");
    scanf("%d", &n);
    fflush(stdin);

    printf("1つめの文字列を入力してください\n");
    fgets(buf1, sizeof(buf1), stdin);
 
    printf("2つめの文字列を入力してください\n");
    fgets(buf2, sizeof(buf2), stdin);

    result = strncmp(buf1, buf2, n);

    if (result < 0)
    {
        printf("1つめの文字列は2つめの文字列より小さいです\n");
    }
    else if (result == 0)
    {
        printf("1つめの文字列は2つめの文字列と同じです\n");
    }
    else
    {
        printf("1つめの文字列は2つめの文字列より大きいです\n");
    }
   
    getch();
    return 0;
}
8.11: 乱数生成を使って文章を作るプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define PASSAGE_MAX 100
#define WORD_MAX 5
#define PASSAGE_NUM_MAX 20
#define STORY_MAX 1000

void printPassageList(char *[], int);
void freePassageList(char *[], int);
void makeStory(char *[], int, char []);

int main()
{
    int i;
    int nowPosition = 0;
    char *passage;
    char *passageList[PASSAGE_NUM_MAX];
    char story[STORY_MAX] = {"\0"};

    char *article[WORD_MAX] = {"the", "a", "one", "some", "any"};
    char *noun[WORD_MAX] = {"boy", "girl", "dog", "town", "car"};
    char *verb[WORD_MAX] = {"drove", "jumped", "ran", "walke", "skipped"};
    char *preposition[WORD_MAX] = {"to", "from", "over", "under", "on"};

  
    //srand(time(NULL));

    for (i = 1; i <= 20; i++)
    {
       if ((passage = (char *)calloc(sizeof(char), PASSAGE_MAX)) == NULL)
       {
            freePassageList(passageList, nowPosition);
            fprintf(stderr, "メモリ領域確保失敗\n");
            return -1;
       }
   
       strcat(passage, article[rand() % WORD_MAX]);
       strcat(passage, " ");

       strcat(passage, noun[rand() % WORD_MAX]);
       strcat(passage, " ");

       strcat(passage, verb[rand() % WORD_MAX]);
       strcat(passage, " ");
  
       strcat(passage, preposition[rand() % WORD_MAX]);
       strcat(passage, " ");

       strcat(passage, article[rand() % WORD_MAX]);
       strcat(passage, " ");

       strcat(passage, noun[rand() % WORD_MAX]);
       strcat(passage, ".");

       passage[0] = toupper(passage[0]);

       passageList[nowPosition] = passage;
       nowPosition++;
    }

    printPassageList(passageList, nowPosition);

    makeStory(passageList, nowPosition, story);
    printf("\n%s\n", story);

    getch();
    return 0;
}

void printPassageList(char *passageList[], int position)
{
    int i;


    for (i = 0; i < position; i++)
    {
         printf("%2d %s\n", i + 1,  passageList[i]);
    }
}

void freePassageList(char *passageList[], int position)
{
    int i;

    for (i = 0; i < position; i++)
    {
        free(passageList[i]);
    }
}

void makeStory(char *passageList[], int position, char story[])
{
    int i;

    for (i = 0; i < position; i++)
    {
       strcat(story, passageList[i]);
    }
}
8.12: ランダムにリメリック(第1行と第2行は第5行と韻を踏み、第3行は第4行と韻を踏む)を作るプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define LINE_MAX 100
#define LIMERICK_LINES 5
#define POEM_MAX 1000
#define WORD_MAX 5

void capitalize(char *line);
void makeLimerick(char *[], char []);
void printLimerick(char *[], int);

int main() {
    srand((unsigned int)time(NULL));

    char *lines[LIMERICK_LINES];
    char poem[POEM_MAX] = {"\0"};

    // A行用の韻語
    char *rhymeA[WORD_MAX] = {"rat", "hat", "cat", "bat", "mat"};
    // B行用の韻語
    char *rhymeB[WORD_MAX] = {"dog", "log", "frog", "hog", "bog"};
    // その他語彙
    char *adjectives[WORD_MAX] = {"tiny", "funny", "grumpy", "happy", "silly"};
    char *verbs[WORD_MAX] = {"wore", "found", "lost", "grabbed", "held"};
    char *actions[WORD_MAX] = {"barked at a", "looked at a", "jumped on a", "ran from a", "hugged a"};
    char *events[WORD_MAX] = {"tripped on a", "slipped on a", "fell over a", "rolled on a", "stepped on a"};
    char *finals[WORD_MAX] = {"sleeping", "snoring", "dancing", "singing", "laughing"};

    for (int i = 0; i < LIMERICK_LINES; i++) {
        lines[i] = (char *)calloc(LINE_MAX, sizeof(char));
        if (lines[i] == NULL) {
            fprintf(stderr, "Memory allocation failed\n");
            return -1;
        }
    }

    // A行(1,2,5)
    sprintf(lines[0], "There once was a %s %s,", 
            adjectives[rand() % WORD_MAX], rhymeA[rand() % WORD_MAX]);

    sprintf(lines[1], "Who %s a ridiculous %s,", 
            verbs[rand() % WORD_MAX], rhymeA[rand() % WORD_MAX]);

    sprintf(lines[4], "And fell on a %s %s.", 
            finals[rand() % WORD_MAX], rhymeA[rand() % WORD_MAX]);

    // B行(3,4)
    sprintf(lines[2], "He %s %s,", 
            actions[rand() % WORD_MAX], rhymeB[rand() % WORD_MAX]);

    sprintf(lines[3], "Then %s %s,", 
            events[rand() % WORD_MAX], rhymeB[rand() % WORD_MAX]);

    // 先頭を大文字に
    for (int i = 0; i < LIMERICK_LINES; i++) {
        capitalize(lines[i]);
    }

    printLimerick(lines, LIMERICK_LINES);
    for (int i = 0; i < LIMERICK_LINES; i++) {
        free(lines[i]);
    }

    return 0;
}

void capitalize(char *line) {
    if (line[0]) line[0] = toupper(line[0]);
}

void printLimerick(char *lines[], int count) {
    for (int i = 0; i < count; i++) {
        printf("%s\n", lines[i]);
    }
}
8.13: 英語の言い回しを通俗ラテン語風に変えるプログラム
source
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define WORD_MAX 100
#define PHRASE_MAX 1000

void printLatinWord(const char *word);

int main() {
    char phrase[PHRASE_MAX];

    printf("Enter an English phrase (space-separated, no punctuation):\n");
    fgets(phrase, PHRASE_MAX, stdin);

    // 改行を除去
    phrase[strcspn(phrase, "\n")] = '\0';

    // 単語に分割
    char *token = strtok(phrase, " ");
    while (token != NULL) {
        printLatinWord(token);
        printf(" ");  // 空白を維持
        token = strtok(NULL, " ");
    }

    printf("\n");
    return 0;
}

void printLatinWord(const char *word) {
    int len = strlen(word);
    if (len < 2) {
        printf("%s", word);  // 2文字未満はそのまま
        return;
    }

    char latin[WORD_MAX];
    // 先頭以外をコピー
    strncpy(latin, word + 1, len - 1);
    latin[len - 1] = word[0];  // 先頭文字を末尾に
    latin[len] = '\0';

    strcat(latin, "ay");  // "ay" を付加

    printf("%s", latin);
}
8.14: 「(555) 555-5555」という形式の文字列として電話番号の入力を受け取り、市外局番、電話番号を出力するプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char input[32];
    char *tokenList[3];
    char tel7[8] = {0};  // 7桁 + 終端
    int areaCode;
    long number7;

    // 入力取得
    if (!fgets(input, sizeof(input), stdin)) {
        return 1;
    }

    // 改行除去
    input[strcspn(input, "\n")] = '\0';

    // "(" と ")" を空白に置換
    for (int i = 0; input[i]; i++) {
        if (input[i] == '(' || input[i] == ')') {
            input[i] = ' ';
        }
    }

    // strtok で分割
    tokenList[0] = strtok(input, " -");   // 市外局番
    tokenList[1] = strtok(NULL, " -");    // 市内局番
    tokenList[2] = strtok(NULL, " -");    // 回線番号

    if (!tokenList[0] || !tokenList[1] || !tokenList[2]) {
        printf("入力形式が正しくありません\n");
        return 1;
    }

    // 7桁の電話番号を連結
    strcat(tel7, tokenList[1]);
    strcat(tel7, tokenList[2]);

    // 数値に変換
    areaCode = atoi(tokenList[0]);
    number7  = atol(tel7);

    // 出力(0埋めで3桁表示)
    printf("市外局番: %03d\n", areaCode);
    printf("電話番号7桁: %ld\n", number7);

    return 0;
}
8.15: テキストを1行入力して関数strtokでトークンに切り分け、それらのトークンを逆順に出力するプログラム
source
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>

#define LIST_MAX 100

#define DEBUG(x) 

int main()
{
    char *tokenList[LIST_MAX];
    char buf[100];
    char *tokenPtr;
    int nowPosition = 0;
    int i;

    gets(buf);

    if ((tokenPtr = strtok(buf, " ")) == NULL)
    {
        printf("何もありません\n");
        return 1;
    }

    DEBUG(printf("デバッグ\n"));
    DEBUG(printf("%s\n", tokenPtr));

    tokenList[nowPosition] = tokenPtr;
    nowPosition++;
    

    while (((tokenPtr = strtok(NULL, " ")) != NULL) && nowPosition < LIST_MAX)
    {
        DEBUG(printf("デバッグ\n"));
        DEBUG(printf("%s\n", tokenPtr));

        tokenList[nowPosition] = tokenPtr;
        nowPosition++;
    }
    
    for (i = nowPosition - 1; i >= 0; i--)
    {
        printf("%s\n", tokenList[i]);
    }

    getch();
    return 0;
}
8.16: テキストを1行入力し、キーボードから読み込んだ文字列をそのテキスト行から探し出すプログラム
source
#include <stdio.h>
#include <string.h>

int main(void) {
    char text[256];
    char key[256];
    char *searchPtr;
    int count = 0;

    printf("テキストを入力してください: ");
    fgets(text, sizeof(text), stdin);

    printf("検索文字列を入力してください: ");
    fgets(key, sizeof(key), stdin);

    // fgets の改行を削除
    text[strcspn(text, "\n")] = '\0';
    key[strcspn(key, "\n")] = '\0';

    // 最初の検索
    searchPtr = strstr(text, key);

    while (searchPtr != NULL) {
        count++;
        printf("%d回目の発見位置以降: %s\n", count, searchPtr);

        // 次の検索は searchPtr + 1 から
        searchPtr = strstr(searchPtr + 1, key);
    }

    if (count == 0) {
        printf("検索文字列は見つかりませんでした\n");
    }

    return 0;
}
8.17: 8.16をもとに、複数行のテキストを入力し、キーボードから読み込んだ文字列をそれらのテキスト行から探し出すプログラム
source
#include <stdio.h>
#include <string.h>

#define MAX_LINES 100
#define MAX_LEN   256

int main(void) {
    char lines[MAX_LINES][MAX_LEN];
    char key[MAX_LEN];
    int lineCount = 0;

    printf("複数行のテキストを入力してください(空行で終了):\n");

    // 複数行入力
    while (lineCount < MAX_LINES) {
        fgets(lines[lineCount], MAX_LEN, stdin);

        // 改行削除
        lines[lineCount][strcspn(lines[lineCount], "\n")] = '\0';

        // 空行なら終了
        if (strlen(lines[lineCount]) == 0) {
            break;
        }

        lineCount++;
    }

    printf("検索文字列を入力してください: ");
    fgets(key, MAX_LEN, stdin);
    key[strcspn(key, "\n")] = '\0';

    int totalCount = 0;

    // 各行を検索
    for (int i = 0; i < lineCount; i++) {
        char *searchPtr = strstr(lines[i], key);
        int countInLine = 0;

        while (searchPtr != NULL) {
            countInLine++;
            totalCount++;

            printf("行 %d の %d 回目の発見位置以降: %s\n",
                   i + 1, countInLine, searchPtr);

            searchPtr = strstr(searchPtr + 1, key);
        }
    }

    if (totalCount == 0) {
        printf("検索文字列は見つかりませんでした\n");
    } else {
        printf("合計 %d 回見つかりました\n", totalCount);
    }

    return 0;
}
8.18: 複数行のテキストを入力し、キーボードから読み込んだ文字をそれらのテキスト行から探し出すプログラム
source
#include <stdio.h>
#include <string.h>

#define MAX_LINES 100
#define MAX_LEN   256

int main(void) {
    char lines[MAX_LINES][MAX_LEN];
    char key[256];
    int lineCount = 0;

    printf("複数行のテキストを入力してください(空行で終了):\n");

    // 複数行入力
    while (lineCount < MAX_LINES) {
        fgets(lines[lineCount], MAX_LEN, stdin);

        // 改行削除
        lines[lineCount][strcspn(lines[lineCount], "\n")] = '\0';

        // 空行なら終了
        if (strlen(lines[lineCount]) == 0) {
            break;
        }

        lineCount++;
    }

    printf("検索したい1文字を入力してください: ");
    fgets(key, sizeof(key), stdin);
    key[strcspn(key, "\n")] = '\0';

    if (strlen(key) != 1) {
        printf("検索文字は1文字にしてください\n");
        return 1;
    }

    char target = key[0];
    int totalCount = 0;

    // 各行を検索
    for (int i = 0; i < lineCount; i++) {
        char *ptr = strchr(lines[i], target);
        int countInLine = 0;

        while (ptr != NULL) {
            countInLine++;
            totalCount++;

            printf("行 %d の %d 回目の発見位置: %ld 文字目\n",
                   i + 1, countInLine, ptr - lines[i] + 1);

            // 次の検索位置へ
            ptr = strchr(ptr + 1, target);
        }
    }

    if (totalCount == 0) {
        printf("検索文字 '%c' は見つかりませんでした\n", target);
    } else {
        printf("合計 %d 回見つかりました\n", totalCount);
    }

    return 0;
}
8.19: 8.18のプログラムをもとに、複数行のテキストを入力し、関数strchrを使ってそれらのテキスト行に現れるアルファベットの各文字の出現回数を求めるプログラム
source
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINES 100
#define MAX_LEN   256

int main(void) {
    char lines[MAX_LINES][MAX_LEN];
    int lineCount = 0;
    
    printf("複数行のテキストを入力してください(空行で終了):\n");

    // 複数行入力
    while (lineCount < MAX_LINES) {
        fgets(lines[lineCount], MAX_LEN, stdin);

        // 改行削除
        lines[lineCount][strcspn(lines[lineCount], "\n")] = '\0';

        // 空行なら終了
        if (strlen(lines[lineCount]) == 0) {
            break;
        }

        lineCount++;
    }

    // A〜Z の出現回数を格納(大文字小文字を区別しない)
    int count[26] = {0};

    // 各行を検索
    for (int i = 0; i < lineCount; i++) {
        for (char c = 'A'; c <= 'Z'; c++) {
            char *ptr = strchr(lines[i], c);
            while (ptr != NULL) {
                count[c - 'A']++;
                ptr = strchr(ptr + 1, c);
            }
        }
        for (char c = 'a'; c <= 'z'; c++) {
            char *ptr = strchr(lines[i], c);
            while (ptr != NULL) {
                count[c - 'a']++;
                ptr = strchr(ptr + 1, c);
            }
        }
    }

    // 結果表示
    printf("\n--- アルファベット出現回数 ---\n");
    for (int i = 0; i < 26; i++) {
        if (count[i] > 0) {
            printf("%c / %c : %d 回\n", 'A' + i, 'a' + i, count[i]);
        }
    }

    return 0;
}
8.21: 8.6節で解説した文字列比較関数と第6章で開発した配列ソート法を使って、文字列のリストをアルファベット順に並び替えるプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STRING_MAX 100  // 文字列を格納できる最大数
#define BUF_SIZE   100  // 1行の最大文字数(CHAR_MAX と衝突しない)

// 関数プロトタイプ
int  inputStringToList(char *[], int);
void freeStringList(char *[], int);
void printStringList(char *[], int);
void bubleSort(char *[], int);
void discardLine(void);

// メイン関数
int main(void)
{
    char *stringList[STRING_MAX] = {0}; // NULL 初期化
    int count;

    count = inputStringToList(stringList, STRING_MAX);
    if (count < 0) {
        return 1; // メモリ確保失敗など
    }

    bubleSort(stringList, count);
    printStringList(stringList, count);
    freeStringList(stringList, count);

    return 0;
}

// 改行を読み捨てる
void discardLine(void)
{
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
        ; // 何もしない
    }
}

// 文字列入力
int inputStringToList(char *list[], int max)
{
    int nextPosition = 0;
    int yes;
    char tmp[BUF_SIZE];
    char *tmpPtr;

    while (1) {
        printf("入力しますか 1 Yes 0 No\n");

        if (scanf("%d", &yes) != 1) {
            fprintf(stderr, "入力エラーです\n");
            discardLine();
            continue;
        }
        discardLine(); // scanf の改行を捨てる

        if (yes == 0) break;
        if (yes != 1) {
            printf("1 か 0 を入力してください\n");
            continue;
        }

        if (nextPosition >= max) {
            printf("これ以上入力できません(最大 %d 個)\n", max);
            break;
        }

        printf("文字列を入力してください(最大 %d 文字)\n", BUF_SIZE - 1);

        if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
            printf("入力終了とみなします\n");
            break;
        }

        // 改行削除
        tmp[strcspn(tmp, "\n")] = '\0';

        tmpPtr = malloc(strlen(tmp) + 1);
        if (tmpPtr == NULL) {
            fprintf(stderr, "メモリ領域が確保できませんでした\n");
            freeStringList(list, nextPosition);
            return -1;
        }

        strcpy(tmpPtr, tmp);
        list[nextPosition] = tmpPtr;
        nextPosition++;
    }

    return nextPosition;
}

// メモリ解放
void freeStringList(char *list[], int count)
{
    for (int i = 0; i < count; i++) {
        free(list[i]);
        list[i] = NULL;
    }
}

// 表示
void printStringList(char *list[], int count)
{
    for (int i = 0; i < count; i++) {
        printf("%3d %s\n", i, list[i]);
    }
}

// バブルソート(昇順)
void bubleSort(char *list[], int count)
{
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - 1 - i; j++) {
            if (strcmp(list[j], list[j + 1]) > 0) {
                char *tmp = list[j];
                list[j] = list[j + 1];
                list[j + 1] = tmp;
            }
        }
    }
}
8.23: 一連の文字列を読み込み、文字'b'で始まる文字列だけをプリントするプログラム
source
#include <stdio.h>

int main(void) {
    char str[256];

    // EOF まで1行ずつ読み込む
    while (fgets(str, sizeof(str), stdin) != NULL) {
        // 先頭文字が 'b' ならプリント
        if (str[0] == 'b') {
            printf("%s", str);
        }
    }

    return 0;
}
8.24: 一連の文字列を読み込み、文字'ED'で終わる文字列だけをプリントするプログラム
source
#include <stdio.h>
#include <string.h>

int main(void) {
    char str[256];

    // 標準入力から1行ずつ読み込む
    while (fgets(str, sizeof(str), stdin) != NULL) {

        // 改行を取り除く
        // 改行文字もlenに含む
        size_t len = strlen(str);
        if (len > 0 && str[len - 1] == '\n') {
            str[len - 1] = '\0';
            len--;
        }

        // 長さが2以上で、末尾が 'E' 'D' ならプリント
        if (len >= 2 && str[len - 2] == 'E' && str[len - 1] == 'D') {
            printf("%s\n", str);
        }
    }

    return 0;
}
8.25: ASCIIコードを入力して、それに対応する文字をプリントするプログラム
source
#include <stdio.h>

int main(void) {
    int code;
    char ch;

    printf("ASCIIコードを入力してください (0~255):");
    if (scanf("%d", &code) != 1) {
        printf("入力エラーです。\n");
        return 1;
    }

    if (code < 0 || code > 255) {
        printf("範囲外の値です。\n");
        return 1;
    }

    ch = (char)code;
    printf("コード %d に対応する文字は '%c' です。\n", code, ch);

    return 0;
}
8.25_2: さらに、000~255の範囲にあるすべての3桁コードを生成し、各コードに対応する文字をプリントするプログラム
source
#include <stdio.h>

int main(void) {
    int code;
    char ch;

    for (code = 0; code <= 255; code++) {
        ch = (char)code;
        /* %03d で 3桁ゼロ埋め (000, 001, ... 255) */
        printf("%03d : ", code);

        /* 制御文字などはそのまま表示すると見えないので、簡単に判別 */
        if (code < 32 || code == 127) {
            printf("(制御文字)\n");
        } else {
            printf("%c\n", ch);
        }
    }

    return 0;
}
8.26: ASCIIコード表を参考に作成した表8.1(P300)に示した各文字操作関数の独自バージョン
source
#include <stdio.h>

int isdigit(int c);
int isalpha(int c);
int isalnum(int c);
int isxdigit(int c);
int islower(int c);
int isupper(int c);
int mytolower(int c);
int mytoupper(int c);
int isspace(int c);
int iscntrl(int c);
int ispunct(int c);
int isprint(int c);
int isgraph(int c);

int main()
{
    char c;
    printf("1文字入力してください\n");

    c = getchar();
    printf("isdigit %d\n", isdigit(c));
    printf("isalpha %d\n", isalpha(c));
    printf("isalnum %d\n", isalnum(c));
    printf("isxdigit %d\n", isxdigit(c));
    printf("islower %d\n", islower(c));
    printf("isupper %d\n", isupper(c));
    printf("mytolower %c\n", mytolower(c));
    printf("mytoupper %c\n", mytoupper(c));
    printf("isspace %d\n", isspace(c));
    printf("iscntrl %d\n", iscntrl(c));
    printf("ispunct %d\n", ispunct(c));
    printf("isprint %d\n", isprint(c));
    printf("isgraph %d\n", isgraph(c));

    return 0;
}

int isdigit(int c)
{
    return ('0' <= c && c <= '9');
}

int isalpha(int c)
{
    return (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
}

int isalnum(int c)
{
    return (isdigit(c) || isalpha(c));
}

int isxdigit(int c)
{
    return (isdigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'));
}

int islower(int c)
{
    return ('a' <= c && c <= 'z');
}

int isupper(int c)
{
    return ('A' <= c && c <= 'Z');
}

int mytolower(int c)
{
    if (isupper(c))
        return c + ('a' - 'A');
    return c;
}

int mytoupper(int c)
{
    if (islower(c))
        return c - ('a' - 'A');
    return c;
}

int isspace(int c)
{
    return (c == ' ' || c == '\f' || c == '\n' ||
            c == '\r' || c == '\t' || c == '\v');
}

int iscntrl(int c)
{
    return ((0 <= c && c <= 31) || c == 127);
}

int ispunct(int c)
{
    return (!isalnum(c) && !isspace(c) && isgraph(c));
}

int isprint(int c)
{
    return (c == ' ' || isgraph(c));
}

int isgraph(int c)
{
    return (33 <= c && c <= 126);
}
8.27: 表8.2(P305)の各文字列変換関数の独自バージョンをテストするプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

double myatof(const char *s)
{
    double value = 0.0;
    double sign = 1.0;

    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') s++;

    while (isdigit(*s)) {
        value = value * 10 + (*s - '0');
        s++;
    }

    if (*s == '.') {
        s++;
        double frac = 1.0;
        while (isdigit(*s)) {
            frac /= 10.0;
            value += (*s - '0') * frac;
            s++;
        }
    }

    return sign * value;
}

int myatoi(const char *s)
{
    int value = 0;
    int sign = 1;

    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') s++;

    while (isdigit(*s)) {
        value = value * 10 + (*s - '0');
        s++;
    }

    return sign * value;
}

long myatol(const char *s)
{
    long value = 0;
    int sign = 1;

    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') s++;

    while (isdigit(*s)) {
        value = value * 10 + (*s - '0');
        s++;
    }

    return sign * value;
}

double my_strtod(const char *s, char **endPtr)
{
    double value = 0.0;
    double sign = 1.0;

    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') s++;

    while (isdigit(*s)) {
        value = value * 10 + (*s - '0');
        s++;
    }

    if (*s == '.') {
        s++;
        double frac = 1.0;
        while (isdigit(*s)) {
            frac /= 10.0;
            value += (*s - '0') * frac;
            s++;
        }
    }

    if (endPtr) *endPtr = (char *)s;
    return sign * value;
}

long my_strtol(const char *s, char **endPtr, int base)
{
    long value = 0;
    int sign = 1;

    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') s++;

    if (base == 0) base = 10;

    while (1) {
        int digit;

        if (isdigit(*s)) digit = *s - '0';
        else if (isalpha(*s)) digit = tolower(*s) - 'a' + 10;
        else break;

        if (digit >= base) break;

        value = value * base + digit;
        s++;
    }

    if (endPtr) *endPtr = (char *)s;
    return sign * value;
}

unsigned long my_strtoul(const char *s, char **endPtr, int base)
{
    unsigned long value = 0;

    if (*s == '+') s++;

    if (base == 0) base = 10;

    while (1) {
        int digit;

        if (isdigit(*s)) digit = *s - '0';
        else if (isalpha(*s)) digit = tolower(*s) - 'a' + 10;
        else break;

        if (digit >= base) break;

        value = value * base + digit;
        s++;
    }

    if (endPtr) *endPtr = (char *)s;
    return value;
}

#define TEST_HEADER(title) printf("\n==== %s ====\n", title)

void test_atof()
{
    TEST_HEADER("atof / myatof");

    const char *tests[] = {
        "123.45", "0.001", "-12.34", "+56.78",
        "1b.234", "abc", "999999999.999", "3.", ".5",
        NULL
    };

    for (int i = 0; tests[i]; i++) {
        double std = atof(tests[i]);
        double my  = myatof(tests[i]);
        printf("input=\"%s\"  std=%lf  my=%lf\n", tests[i], std, my);
    }
}

void test_atoi()
{
    TEST_HEADER("atoi / myatoi");

    const char *tests[] = {
        "123", "-456", "+789", "1a23", "abc", "00012",
        NULL
    };

    for (int i = 0; tests[i]; i++) {
        int std = atoi(tests[i]);
        int my  = myatoi(tests[i]);
        printf("input=\"%s\"  std=%d  my=%d\n", tests[i], std, my);
    }
}

void test_atol()
{
    TEST_HEADER("atol / myatol");

    const char *tests[] = {
        "12345", "-9876", "1235a", "abc", "0000123",
        NULL
    };

    for (int i = 0; tests[i]; i++) {
        long std = atol(tests[i]);
        long my  = myatol(tests[i]);
        printf("input=\"%s\"  std=%ld  my=%ld\n", tests[i], std, my);
    }
}

void test_strtod()
{
    TEST_HEADER("strtod / my_strtod");

    const char *tests[] = {
        "51.2% are admitted", "3.14xyz", "-12.34abc",
        "abc", "0.001",
        NULL
    };

    for (int i = 0; tests[i]; i++) {
        char *end1, *end2;
        double std = strtod(tests[i], &end1);
        double my  = my_strtod(tests[i], &end2);

        printf("input=\"%s\"\n", tests[i]);
        printf("  std=%lf  rest=\"%s\"\n", std, end1);
        printf("  my =%lf  rest=\"%s\"\n", my, end2);
    }
}

void test_strtol()
{
    TEST_HEADER("strtol / my_strtol");

    const char *tests[] = {
        "77abc", "123xyz", "-456def", "abc", "777",
        NULL
    };

    int bases[] = {8, 10, 16};

    for (int b = 0; b < 3; b++) {
        printf("\n-- base=%d --\n", bases[b]);
        for (int i = 0; tests[i]; i++) {
            char *end1, *end2;
            long std = strtol(tests[i], &end1, bases[b]);
            long my  = my_strtol(tests[i], &end2, bases[b]);

            printf("input=\"%s\"\n", tests[i]);
            printf("  std=%ld  rest=\"%s\"\n", std, end1);
            printf("  my =%ld  rest=\"%s\"\n", my, end2);
        }
    }
}

void test_strtoul()
{
    TEST_HEADER("strtoul / my_strtoul");

    const char *tests[] = {
        "77abc", "123xyz", "456", "abc", "777",
        NULL
    };

    int bases[] = {8, 10, 16};

    for (int b = 0; b < 3; b++) {
        printf("\n-- base=%d --\n", bases[b]);
        for (int i = 0; tests[i]; i++) {
            char *end1, *end2;
            unsigned long std = strtoul(tests[i], &end1, bases[b]);
            unsigned long my  = my_strtoul(tests[i], &end2, bases[b]);

            printf("input=\"%s\"\n", tests[i]);
            printf("  std=%lu  rest=\"%s\"\n", std, end1);
            printf("  my =%lu  rest=\"%s\"\n", my, end2);
        }
    }
}

int main()
{
    test_atof();
    test_atoi();
    test_atol();
    test_strtod();
    test_strtol();
    test_strtoul();
    return 0;
}
8.28: 表8.4(P313)の各文字列コピー関数および各文字列連結関数の独自バージョンをテストするプログラム(第1バージョンは配列添字、第2バージョンはポインタ)
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

char *mystrcpy1(char s1[], const char s2[])
{
    int i = 0;

    while (s2[i] != '\0')
    {
        s1[i] = s2[i];
        i++;
    }
    s1[i] = '\0';
    return s1;
}

char *mystrcpy2(char *s1, const char *s2)
{
    char *headPtr;
   
    headPtr = s1;

    while (*s2 != '\0')
    {
        *s1 = *s2;
        s1++; 
        s2++;
    }
    *s1 = '\0';
    return headPtr;
}

char *mystrncpy1(char s1[], const char s2[], size_t n)
{
    size_t count = 1;
 
    while (count <= n)
    {
         s1[count - 1] = s2[count - 1];
         count++;
    }   
    s1[count - 1] = '\0';
   
    return s1;
}

char *mystrncpy2(char *s1, const char *s2, size_t n)
{
    char *headPtr;
    size_t count = 1;

    headPtr = s1;
 
    while (count <= n)
    {
         *s1 = *s2;
         s1++;
         s2++;
         count++;
    }   
    *s1 = '\0';
   
    return headPtr;
}

char *mystrcat1(char s1[], const char s2[])
{
    int i = 0;
    int j = 0;

    while (s1[i] != '\0') i++;

    while (s2[j] != '\0')
    {
         s1[i] = s2[j];
         i++; 
         j++;
    }
    s1[i] = s2[j];
    return s1;
}

char *mystrcat2(char *s1, const char *s2)
{
    char *headPtr;

    headPtr = s1;

    while (*s1 != '\0') s1++;

    while (*s2 != '\0')
    {
         *s1 = *s2;
         s1++;
         s2++;
    }
    *s1 = *s2;
    return headPtr;
}

char *mystrncat1(char s1[], const char s2[], size_t n)
{
    int i = 0;
    int j = 0;
    size_t count = 1;

    while (s1[i] != '\0') i++;

    while (count <= n)
    {
         s1[i] = s2[j];
         i++; 
         j++;
         count++;
    }
    s1[i] = '\0';
    return s1;
}

char *mystrncat2(char *s1, const char *s2, size_t n)
{
    char *headPtr;
    size_t count = 1;

   
    headPtr = s1;

    while (*s1 != '\0') s1++;

    while (count <= n)
    {
         *s1 = *s2;
         s1++;
         s2++;
         count++;
    }
    *s1 = '\0';
    return headPtr;
}

int main()
{
    char s1[100];
    char s2[100];

    printf("=== mystrcpy1 / mystrcpy2 ===\n");
    mystrcpy1(s1, "Hello");
    printf("mystrcpy1 : %s\n", s1);

    mystrcpy2(s1, "World");
    printf("mystrcpy2 : %s\n", s1);


    printf("\n=== mystrncpy1 / mystrncpy2 ===\n");
    mystrncpy1(s1, "ABCDEFG", 3);
    printf("mystrncpy1 (3 chars): %s\n", s1);

    mystrncpy2(s1, "ABCDEFG", 5);
    printf("mystrncpy2 (5 chars): %s\n", s1);


    printf("\n=== mystrcat1 / mystrcat2 ===\n");
    mystrcpy1(s1, "ABC");
    mystrcat1(s1, "DEF");
    printf("mystrcat1 : %s\n", s1);

    mystrcpy1(s1, "ABC");
    mystrcat2(s1, "XYZ");
    printf("mystrcat2 : %s\n", s1);


    printf("\n=== mystrncat1 / mystrncat2 ===\n");
    mystrcpy1(s1, "123");
    mystrncat1(s1, "ABCDE", 3);
    printf("mystrncat1 (3 chars): %s\n", s1);

    mystrcpy1(s1, "123");
    mystrncat2(s1, "ABCDE", 2);
    printf("mystrncat2 (2 chars): %s\n", s1);

    getch();
    return 0;
}
8.29: 表8.3(P309)の関数gtchar、gets、putchar、putsの独自バージョンをテストするプログラム
source
#include <stdio.h>
#include <string.h>

#define SIZE 100

int myGetchar(void);
char *myGets(char *s);
int myPutchar(int c);
int myPuts(const char *s);

int main(void)
{
    char s[100];
    int c;

    printf("=== myGetchar() テスト ===\n");
    printf("1行入力してください: ");

    c = myGetchar();
    printf("myGetchar → %c\n", c);

    c = myGetchar();
    printf("myGetchar → %c\n", c);

    printf("\n=== myGets() テスト ===\n");
    printf("文字列を入力してください: ");
    myGets(s);
    printf("myGets → [%s]\n", s);

    printf("\n=== myPutchar() テスト ===\n");
    printf("myPutchar → ");
    myPutchar('X');
    myPutchar('\n');

    printf("\n=== myPuts() テスト ===\n");
    myPuts("Hello from myPuts!");

    return 0;
}

int myGetchar(void)
{
    static char buf[SIZE];
    static int next = 0;
    char c;

    if (next == 0)
    {
        if (fgets(buf, sizeof(buf), stdin) == NULL)
            return EOF;
    }

    c = buf[next];

    if (c == '\n')
    {
        next = 0;
        return '\n';
    }

    next++;
    return c;
}

char *myGets(char *s)
{
    char *p = s;
    int c;

    while ((c = myGetchar()) != EOF && c != '\n')
    {
        *p++ = (char)c;
    }

    *p = '\0';
    return s;
}

int myPutchar(int c)
{
    putchar(c);
    return c;
}

int myPuts(const char *s)
{
    printf("%s\n", s);
    return 1;
}
8.30: 表8.5(P315)の各文字列比較関数を2通りの方法で書き(第1バージョンは配列添字、第2バージョンはポインタ)テストするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int mystrcmp1(const char [], const char []);
int mystrcmp2(const char *, const char *);

int mystrncmp1(const char [], const char [], int);
int mystrncmp2(const char *, const char *, int);

int main()
{
    const char s1[] = "ABCFE";
    const char s2[] = "ABCDE";

    printf("strcmp\n");
    printf("%d\n", strcmp(s1, s2));
    printf("%d\n", mystrcmp1(s1, s2));
    printf("%d\n", mystrcmp2(s1, s2));

    printf("strncmp\n");
    printf("%d\n", strncmp(s1, s2, 5));
    printf("%d\n", mystrncmp1(s1, s2, 5));
    printf("%d\n", mystrncmp2(s1, s2, 5));
    getch();
    return 0;
}

int mystrcmp1(const char a[], const char b[])
{
    int i;

    for (i = 0; a[i] != '\0' && b[i] != '\0'; i++)
    {
        if (a[i] != b[i])
        {
            return a[i] - b[i];
        }
    }
    return a[i] - b[i];
}

int mystrcmp2(const char *a, const char *b)
{
    for (; *a != '\0' && *b != '\0'; a++, b++)
    {
        if (*a != *b)
        {
            return *a - *b;
        }
    }
    return *a - *b;
}

int mystrncmp1(const char a[], const char b[], int n)
{
    int i;

    for (i = 0; a[i] != '\0' && b[i] != '\0' && i < n; i++)
    {
        if (a[i] != b[i])
        {
            return (unsigned char)a[i] - (unsigned char)b[i];
        }
    }
 
    if (i == n)
    {
        return 0;
    }
    return a[i] - b[i];
}

int mystrncmp2(const char *a, const char *b, int n)
{
    int i = 0;

    for (; *a != '\0' && *b != '\0' && i < n; a++, b++)
    {
        if (*a != *b)
        {
            return *a - *b;
        }
    }
 
    if (i == n)
    {
        return 0;
    }
    return *a - *b;
}
8.31: 表8.6(P317)の各文字列検索関数の独自バージョンを書きテストするプログラム
source
#include <stdio.h>
#include <string.h>

char *mystrchr(const char *s, int c)
{
    while (*s != '\0')
    {
        if (*s == c)
        {
            return (char *)s;
        }
        s++;
    }
    return NULL;
}

size_t mystrcspn(const char *s1, const char *s2)
{
    const char *headPtr = s1;
    const char *headPtr2;

    while (*s1 != '\0')
    {
        headPtr2 = s2;
        for (; *s2 != '\0'; s2++)
        {
            if (*s1 == *s2)
            {
                return s1 - headPtr;
            }
        }
        s2 = headPtr2;
        s1++;
    }
    return s1 - headPtr;
}

size_t mystrspn(const char *s1, const char *s2)
{
    const char *headPtr = s1;
    const char *headPtr2;

    while (*s1 != '\0')
    {
        headPtr2 = s2;
        for (; *s2 != '\0'; s2++)
        {
            if (*s1 == *s2)
            {
                break;
            }
        }

        if (*s2 == '\0')
        {
            return s1 - headPtr;
        }

        s2 = headPtr2;
        s1++;
    }
    return s1 - headPtr;
}

int main(void)
{
    char c;
    char s[100];

    printf("=== Test mystrchr ===\n");
    printf("Input a character: ");
    c = getchar();
    while (getchar() != '\n');  // 改行を捨てる

    char *pos = mystrchr("ABCABC", c);
    printf("Pointer returned: %p\n", (void *)pos);

    printf("\n=== Test mystrcspn ===\n");
    printf("Input a string: ");
    fgets(s, sizeof(s), stdin);
    s[strcspn(s, "\n")] = '\0';

    printf("mystrcspn(\"ABCABC\", \"%s\") = %zu\n", s, mystrcspn("ABCABC", s));

    printf("\n=== Test mystrspn ===\n");
    printf("Input a string: ");
    fgets(s, sizeof(s), stdin);
    s[strcspn(s, "\n")] = '\0';

    printf("mystrspn(\"ABCABC\", \"%s\") = %zu\n", s, mystrspn("ABCABC", s));

    return 0;
}
8.32: 表8.7(P324)の各メモリ操作関数の独自バージョンを書きテストするプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ============================
   my_memcpy
   ============================ */
void *my_memcpy(void *dest, const void *src, size_t n)
{
    unsigned char *d = dest;
    const unsigned char *s = src;

    for (size_t i = 0; i < n; i++)
        d[i] = s[i];

    return dest;
}

/* ============================
   my_memmove(必ず一時領域を使う仕様)
   ============================ */
void *my_memmove(void *s1, const void *s2, size_t n)
{
    unsigned char *dest = s1;
    const unsigned char *src = s2;

    unsigned char *tmp = malloc(n);
    if (tmp == NULL)
        return NULL;

    for (size_t i = 0; i < n; i++)
        tmp[i] = src[i];

    for (size_t i = 0; i < n; i++)
        dest[i] = tmp[i];

    free(tmp);
    return s1;
}

/* ============================
   my_memcmp
   ============================ */
int my_memcmp(const void *s1, const void *s2, size_t n)
{
    const unsigned char *a = s1;
    const unsigned char *b = s2;

    for (size_t i = 0; i < n; i++)
    {
        if (a[i] != b[i])
            return a[i] - b[i];
    }
    return 0;
}

/* ============================
   my_memchr
   ============================ */
void *my_memchr(const void *s, int c, size_t n)
{
    const unsigned char *p = s;
    unsigned char target = (unsigned char)c;

    for (size_t i = 0; i < n; i++)
    {
        if (p[i] == target)
            return (void *)(p + i);
    }
    return NULL;
}

/* ============================
   my_memset
   ============================ */
void *my_memset(void *s, int c, size_t n)
{
    unsigned char *p = s;
    unsigned char val = (unsigned char)c;

    for (size_t i = 0; i < n; i++)
        p[i] = val;

    return s;
}

/* ============================
   総合テスト
   ============================ */
int main(void)
{
    printf("===== TEST: my_memcpy =====\n");
    {
        char a[20], b[20];
        strcpy(b, "Hello");
        my_memcpy(a, b, strlen(b) + 1);
        printf("my_memcpy result: %s\n", a);
    }

    printf("\n===== TEST: my_memmove (non-overlap) =====\n");
    {
        char a[20], b[20];
        strcpy(b, "ABCDE");
        my_memmove(a, b, strlen(b) + 1);
        printf("my_memmove result: %s\n", a);
    }

    printf("\n===== TEST: my_memmove (overlap forward) =====\n");
    {
        char buf[20] = "ABCDEFGHIJ";
        my_memmove(buf + 2, buf, 5);  // オーバーラップ
        printf("overlap forward: %s\n", buf);
    }

    printf("\n===== TEST: my_memmove (overlap backward) =====\n");
    {
        char buf[20] = "ABCDEFGHIJ";
        my_memmove(buf, buf + 2, 5);  // オーバーラップ
        printf("overlap backward: %s\n", buf);
    }

    printf("\n===== TEST: my_memcmp =====\n");
    {
        printf("memcmp(\"ABC\",\"ABD\",3) = %d\n", my_memcmp("ABC", "ABD", 3));
        printf("memcmp(\"ABC\",\"ABC\",3) = %d\n", my_memcmp("ABC", "ABC", 3));
    }

    printf("\n===== TEST: my_memchr =====\n");
    {
        const char *s = "Hello World";
        char *p = my_memchr(s, 'W', strlen(s));
        if (p)
            printf("found 'W' at: %s\n", p);
        else
            printf("not found\n");
    }

    printf("\n===== TEST: my_memset =====\n");
    {
        char buf[10];
        my_memset(buf, 'X', 5);
        buf[5] = '\0';
        printf("my_memset result: %s\n", buf);
    }

    printf("\n===== ALL TESTS DONE =====\n");
    return 0;
}
8.33: 表8.8(P327)の関数strlenを2通りの方法で書きテストするプログラム
source
#include <stdio.h>
#include <conio.h>

int strlen1(const char []);
int strlen2(const char *);

int main()
{
    char buf[100];
 
    gets(buf);

    printf("%d strlen1\n", strlen1(buf));
    printf("%d strlen2\n", strlen2(buf));
    getch();
    return 0;
}

int strlen1(const char s[])
{
    int i = 0;

    while (s[i] != '\0')
    {
        i++;
    }
    return i;
}

int strlen2(const char *s)
{
    const char *head;

    head = s;

    while (*s != '\0')
    {
       s++;
    }
    return s - head;
}
8.34: テキスト分析
  • a: キーボードから数行のテキストを読み込み、アルファベットの各文字の出現回数をプリントするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX 100

int inputString(char *[], int *, int);
void freeList(char *[], int *);
void getAlphaNum(char *[], int *, int []);
void printAlpha(int []);

int main()
{
    char *stringList[MAX];
    static int alpha[52];
    int nextPosition = 0;

   
    if (inputString(stringList, &nextPosition, MAX) == -1)
    {
        fprintf(stderr, "error inputString\n");
        freeList(stringList, &nextPosition);
        return -1;
    }

 
    getAlphaNum(stringList, &nextPosition, alpha);
    
    
    printAlpha(alpha);
    
    getch();
    return 0;
}

int inputString(char *list[], int *next, int max)
{
    int yes;
    char input[100];
    char *tmpPtr;

    printf("テキストを入力しますか? 1 Yes 0 No: ");
    scanf("%d", &yes);
    fflush(stdin);

    while (yes == 1 && *next < max)
    {
         fgets(input, sizeof(input), stdin);

         if ((tmpPtr = (char *)malloc(strlen(input) + 1)) == NULL)
         {
            
            return -1;
         }
 
         strcpy(tmpPtr, input);
   
        list[*next] = tmpPtr;
        (*next)++;

        printf("テキストを入力しますか? 1 Yes 0 No: ");
        scanf("%d", &yes);
        fflush(stdin);
    }
    return 0;    
}

void freeList(char *list[], int *next)
{
    int i;

    for (i = 0; i < *next; i++)
    {
        free(list[i]);
    }
}

void getAlphaNum(char *list[], int *next, int alpha[])
{
    int i, j;

    for (i = 0; i < *next; i++)
    {
       for (j = 0; list[i][j] != '\n' && list[i][j] != '\0'; j++)
       {
          // 小文字ならば
          if (islower(list[i][j]))
          {
              alpha[list[i][j] - 'a' + 26]++;
          } 
          // 大文字ならば
          else if (isupper(list[i][j]))
          {
              alpha[list[i][j] - 'A']++;
          }
       }
    }
}

void printAlpha(int alpha[])
{
    int i;

    for (i = 0; i < 26; i++)
    {
        printf("%c %d", 'A' + i, alpha[i]);

        if ((i + 1) % 5 == 0)
        {
             printf("\n");
        }
        else 
        {
             printf(" ");
        }
    }

    for (i = 26; i < 52; i++)
    {
        printf("%c %d", 'a' + i - 26, alpha[i]);

        if ((i + 1) % 5 == 0)
        {
             printf("\n");
        }
        else 
        {
             printf(" ");
        }
    }
}
  • b: キーボードから数行のテキストを読み込み、その中に含まれている1文字単語、2文字単語、3文字単語、・・の出現回数をプリントするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX 100

int inputString(char *[], int *, int);
void freeList(char *[], int *);
void getLength(char *[], int *, int[]);
void printLength(int[], int);

int main()
{
    char *stringList[MAX];
    static int length[200];
   
    int nextPosition = 0;

   
    if (inputString(stringList, &nextPosition, MAX) == -1)
    {
        fprintf(stderr, "error inputString\n");
        freeList(stringList, &nextPosition);
        return -1;
    }

    getLength(stringList, &nextPosition, length);
    printLength(length, 200);
    getch();
    return 0;
}

int inputString(char *list[], int *next, int max)
{
    int yes;
    char input[100];
    char *tmpPtr;

    printf("テキストを入力しますか? 1 Yes 0 No: ");
    scanf("%d", &yes);
    fflush(stdin);

    while (yes == 1 && *next < max)
    {
         fgets(input, sizeof(input), stdin);

         if (strlen(input) + 1 != max)
         {
             input[strlen(input) - 1] = '\0';
         }

         if ((tmpPtr = (char *)malloc(strlen(input) + 1)) == NULL)
         {
            
            return -1;
         }
 
         strcpy(tmpPtr, input);
   
        list[*next] = tmpPtr;
        (*next)++;

        printf("テキストを入力しますか? 1 Yes 0 No: ");
        scanf("%d", &yes);
        fflush(stdin);
    }
    return 0;    
}

void freeList(char *list[], int *next)
{
    int i;

    for (i = 0; i < *next; i++)
    {
        free(list[i]);
    }
}

void getLength(char *list[], int *next, int len[])
{
    int i, j;
    char *wordPtr;

    for (i = 0; i < *next; i++)
    {
       wordPtr = strtok(list[i], " ");

       while (wordPtr != NULL)
       {
           len[strlen(wordPtr)]++;
           wordPtr = strtok(NULL, " ");
       }
    }
}

void printLength(int len[], int max)
{
    int i;

    for (i = 1; i < max; i++)
    {
        printf("%3d文字 %d", i, len[i]);
 
        if (i % 5 == 0)
        {
            printf("\n");
        }
        else
        {
            printf(" ");
        }
    }
}
  • c_v1: キーボードから数行のテキストを読み込み、その中に含まれている各単語の出現回数を表形式でプリントするプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_WORDS 500
#define MAX_LEN   100

// 単語とその出現回数を保持する構造体
typedef struct {
    char word[MAX_LEN];
    int count;
} WordEntry;

int main() {
    char input[1000];
    WordEntry table[MAX_WORDS];
    int wordCount = 0;

    printf("テキストを入力してください:\n> ");
    fgets(input, sizeof(input), stdin);

    // --- 句読点をスペースに変換 ---
    for (int i = 0; input[i]; i++) {
        if (ispunct(input[i])) {
            input[i] = ' ';
        }
    }

    // --- 単語分割 ---
    char *token = strtok(input, " ");
    while (token != NULL) {

        // 改行削除
        token[strcspn(token, "\n")] = '\0';

        // 空文字は無視
        if (strlen(token) > 0) {

            // すでに登録済みかチェック
            int found = -1;
            for (int i = 0; i < wordCount; i++) {
                if (strcmp(table[i].word, token) == 0) {
                    found = i;
                    break;
                }
            }

            if (found >= 0) {
                table[found].count++;
            } else {
                strcpy(table[wordCount].word, token);
                table[wordCount].count = 1;
                wordCount++;
            }
        }

        token = strtok(NULL, " ");
    }

    // --- 結果表示 ---
    printf("\n--- 単語出現表(出現順) ---\n");
    for (int i = 0; i < wordCount; i++) {
        printf("%-15s : %d\n", table[i].word, table[i].count);
    }

    return 0;
}
  • c_v2: v1 + 各単語をアルファベット順にソートしてからプリントするプログラム
source
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX 100
#define MAX_WORD 1000

int inputString(char *[], int *, int);
void freeList(char *[], int *);
void countWords(char *[], int *);

int main()
{
    char *stringList[MAX];
    int nextPosition = 0;

    if (inputString(stringList, &nextPosition, MAX) == -1)
    {
        fprintf(stderr, "error inputString\n");
        freeList(stringList, &nextPosition);
        return -1;
    }

    countWords(stringList, &nextPosition);

    freeList(stringList, &nextPosition);

    getch();
    return 0;
}

int inputString(char *list[], int *next, int max)
{
    int yes;
    char input[100];
    char *tmpPtr;

    printf("テキストを入力しますか? 1 Yes 0 No: ");
    scanf("%d", &yes);
    fflush(stdin);

    while (yes == 1 && *next < max)
    {
        fgets(input, sizeof(input), stdin);

        if (strlen(input) > 0 && input[strlen(input) - 1] == '\n')
        {
            input[strlen(input) - 1] = '\0';
        }

        tmpPtr = (char *)malloc(strlen(input) + 1);
        if (tmpPtr == NULL)
        {
            return -1;
        }

        strcpy(tmpPtr, input);

        list[*next] = tmpPtr;
        (*next)++;

        printf("テキストを入力しますか? 1 Yes 0 No: ");
        scanf("%d", &yes);
        fflush(stdin);
    }

    return 0;
}

void freeList(char *list[], int *next)
{
    int i;

    for (i = 0; i < *next; i++)
    {
        free(list[i]);
    }
}

void countWords(char *list[], int *next)
{
    char *words[MAX_WORD];
    int wordCount = 0;

    int i, j;
    char *token;

    /* 単語取得 */
    for (i = 0; i < *next; i++)
    {
        token = strtok(list[i], " ");

        while (token != NULL)
        {
            words[wordCount] = token;
            wordCount++;
            token = strtok(NULL, " ");
        }
    }

    /* アルファベット順ソート */
    for (i = 0; i < wordCount - 1; i++)
    {
        for (j = i + 1; j < wordCount; j++)
        {
            if (strcmp(words[i], words[j]) > 0)
            {
                char *tmp = words[i];
                words[i] = words[j];
                words[j] = tmp;
            }
        }
    }

    /* 出現回数表示 */
    printf("\n--- 単語出現回数(アルファベット順)---\n");

    for (i = 0; i < wordCount; i++)
    {
        if (i == 0 || strcmp(words[i], words[i - 1]) != 0)
        {
            int count = 1;

            for (j = i + 1; j < wordCount; j++)
            {
                if (strcmp(words[i], words[j]) == 0)
                {
                    count++;
                }
                else
                {
                    break;
                }
            }

            printf("%-15s : %d\n", words[i], count);
        }
    }
}
8.35: キーボードから数行のテキストを読み込み、自動文字配置を行ってからプリントするプログラム
source
/* A4の横幅:210mm
左右マージン:22.5mm × 2 = 45mm
印字可能幅:210mm - 45mm = 165mm
文字密度:25.4mm に 10文字 → 1文字 ≒ 2.54mm
165mm ÷ 2.54mm ≒ 65文字/行 */

#include <stdio.h>
#include <string.h>

#define MAX_LINE_LENGTH 65
#define MAX_TEXT_LENGTH 10000  // 最大入力文字数

int main() {
    char input[MAX_TEXT_LENGTH];
    char ch;
    int i = 0;

    printf("テキストを入力してください(Ctrl+Zで終了):\n");

    // キーボードから複数行のテキストを読み込む
    while ((ch = getchar()) != EOF && i < MAX_TEXT_LENGTH - 1) {
        input[i++] = ch;
    }
    input[i] = '\0';  // 文字列終端

    printf("\n--- 整形された出力 ---\n");

    // 自動文字配置(改行処理)
    int count = 0;
    for (int j = 0; j < i; j++) {
        if (input[j] == '\n') {
            count = 0;
            putchar('\n');
        } else {
            putchar(input[j]);
            count++;
            if (count == MAX_LINE_LENGTH) {
                putchar('\n');
                count = 0;
            }
        }
    }
    return 0;
}
8.36: 「07/21/97」の形式の日付を読み込んで「July 21, 1997」の形式でその日付をプリントするプログラム
source
#include <stdio.h>
#include <conio.h>

int main()
{
    int year, month, day;
    char *monthName[13] = {"", "January", "February", "March", "April", 
                          "May", "June", "July", "August", "September",
                          "October", "November", "December"};
                         
  
    printf("mm/day/yyで入力してください\n");

    scanf("%02d/%2d/%2d", &month, &day, &year);

    printf("%s %d, %d\n", monthName[month], day, 1900 + year);
    
    getch();
    return 0;
}
8.37: 支払い金額を入力して、それを小切手保護形式で印字するプログラム(金額欄は9桁まで印字可能)
source
#include <stdio.h>
#include <string.h>

#define WIDTH 9   // 右詰めする幅

int main() {
    char buf[100];  // 入力バッファ大きめ
    size_t len;

    printf("支払い金額を入力してください\n");

    // 安全に fgets
    // 123\n\0
    // 123456789\0
    if (fgets(buf, sizeof(buf), stdin) == NULL) {
        printf("入力がありません\n");
        return 1;
    }

    // 改行削除
    len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') {
        buf[len - 1] = '\0';
        len--;
    }

    // 右詰め用に '*' を計算
    if (len < WIDTH) {
        for (size_t i = 0; i < WIDTH - len; i++) {
            putchar('*');
        }
    }

    // 入力を表示
    printf("%s\n", buf);

    return 0;
}
8.38: 小切手の金額を数値で入力し、それの相当額を漢数字でプリントするプログラム
source
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

const char *kanji_digits[] = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
const char *kanji_units[] = {"", "十", "百", "千"};
const char *kanji_large_units[] = {"", "万", "億", "兆"};

void convert_group_to_kanji(int num, char *out) {
    char temp[64] = "";
    int digits[4] = {0};
    for (int i = 0; i < 4; i++) {
        digits[i] = num % 10;
        num /= 10;
    }

    // 三千五百四 3504 digits = [4, 0, 5, 3]
    for (int i = 3; i >= 0; i--) {
        if (digits[i] != 0) { 
            // 1かつ一番小さな位でないならばunitだけdigitsで一はつけない
            if (!(digits[i] == 1 && i != 0)) {
                strcat(temp, kanji_digits[digits[i]]);
            }
            strcat(temp, kanji_units[i]);
        }
    }

    if (strlen(temp) == 0) {
        strcat(temp, kanji_digits[0]);
    }

    strcpy(out, temp);
}

void convert_to_kanji(long long num, char *output) {
    if (num == 0) {
        strcpy(output, kanji_digits[0]);
        return;
    }

    char buffer[1024] = "";
    char part[128];
    int unit_index = 0;

    while (num > 0) {
        int group = num % 10000;
        if (group != 0) {
            char group_kanji[128] = "";
            convert_group_to_kanji(group, group_kanji);
            sprintf(part, "%s%s", group_kanji, kanji_large_units[unit_index]);
            char temp[1024];
            strcpy(temp, part);
            strcat(temp, buffer);
            strcpy(buffer, temp);
        }
        num /= 10000;
        unit_index++;
    }

    strcpy(output, buffer);
}

int main() {
    double amount;
    printf("金額を入力してください(例:1234567.89):");
    scanf("%lf", &amount);

    if (amount < 0) {
        printf("無効な金額です。\n");
        return 1;
    }

    long long dollars = (long long)amount;
    int cents = (int)round((amount - dollars) * 100);

    char kanji_dollars[1024] = "";
    char kanji_cents[128] = "";

    convert_to_kanji(dollars, kanji_dollars);

    if (cents > 0) {
        convert_to_kanji(cents, kanji_cents);
        printf("金、%sドル%sセント\n", kanji_dollars, kanji_cents);
    } else {
        printf("金、%sドル\n", kanji_dollars);
    }

    return 0;
}
8.39: 英語のフレーズを読み込んでモールス符号に変換、またその逆変換もするプログラム
source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main()
{
    // モールス信号テーブル: A-Z(0-25), 0-9(26-35)
    char *table[] = {
        ".-", "-...", "-.-.", "-..", ".",        // A-E
        "..-.", "--.", "....", "..", ".---",     // F-J
        "-.-", ".-..", "--", "-.", "---",        // K-O
        ".--.", "--.-", ".-.", "...", "-",       // P-T
        "..-", "...-", ".--", "-..-", "-.--",    // U-Y
        "--..",                                   // Z
        "-----", ".----", "..---", "...--", "....-", // 0-4
        ".....", "-....", "--...", "---..", "----."  // 5-9
    };

    char line[100];
    char input[1000];
    char *signalTable[200];
    char signal[100];
    int select;
    int endFlag = 0;
    int i, j;
    int nextT = 0;
    int nextP = 0;

    printf("1 単語→モールス信号\n");
    printf("2 モールス信号→単語\n");
    fgets(line, sizeof(line), stdin);
    sscanf(line, "%d", &select);

    if (select == 1)
    {
        printf("英単語を入力してください:\n");
        fgets(input, sizeof(input), stdin);

        for (i = 0; input[i] != '\0' && input[i] != '\n'; i++)
        {
            if (isalpha(input[i]))
            {
                char c = toupper(input[i]);
                printf("%s ", table[c - 'A']);
            }
            else if (isdigit(input[i]))
            {
                printf("%s ", table[input[i] - '0' + 26]);
            }
            else if (input[i] == ' ')
            {
                printf("/ "); // 単語区切り
            }
        }
        printf("\n");
    }
    else if (select == 2)
    {
        printf("モールス信号を入力してください(単語区切りは(空白)/(空白) ):\n");
        fgets(input, sizeof(input), stdin);

        for (i = 0; !endFlag; i++)
        {
            if (input[i] == ' ' || input[i] == '\0' || input[i] == '\n')
            {
                signal[nextP] = '\0';
                if (strlen(signal) > 0)
                {
                    char *tmpPtr = malloc(strlen(signal) + 1);
                    strcpy(tmpPtr, signal);
                    signalTable[nextT++] = tmpPtr;
                }
                nextP = 0;

                if (input[i] == '\0' || input[i] == '\n')
                    endFlag = 1;
            }
            else
            {
                signal[nextP++] = input[i];
            }
        }

        // モールス信号→文字
        for (i = 0; i < nextT; i++)
        {
            if (strcmp(signalTable[i], "/") == 0)
            {
                printf("   "); // 単語区切り
                free(signalTable[i]);
                continue;
            }

            int found = 0;
            for (j = 0; j < 36 && !found; j++)
            {
                if (strcmp(signalTable[i], table[j]) == 0)
                {
                    if (j < 26)
                        printf("%c", 'A' + j);
                    else
                        printf("%d", j - 26); // 0〜9
                    found = 1;
                }
            }
            free(signalTable[i]);
        }
        printf("\n");
    }
    else
    {
        printf("1か2を入力してください\n");
    }

    return 0;
}
8.40: メートル法換算プログラム
source
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

typedef struct {
    const char *metric;
    const char *imperial;
    double factor;
} Conversion;

Conversion conversions[] = {
    {"meter", "inch", 39.3701},
    {"meter", "foot", 3.28084},
    {"centimeter", "inch", 0.393701},
    {"centimeter", "foot", 0.0328084},
    {"liter", "quart", 1.05669},
    {"gram", "ounce", 0.035274},
    {"kilogram", "pound", 2.20462}
};

int is_valid_conversion(const char *metric, const char *imperial, double *factor) {
    for (int i = 0; i < sizeof(conversions)/sizeof(conversions[0]); i++) {
        printf("[DEBUG] Checking: %s → %s\n", conversions[i].metric, conversions[i].imperial);
        if (strcmp(conversions[i].metric, metric) == 0 &&
            strcmp(conversions[i].imperial, imperial) == 0) {
            *factor = conversions[i].factor;
            printf("[DEBUG] Match found. Factor = %.5f\n", *factor);
            return 1;
        }
    }
    printf("[DEBUG] No valid conversion found for %s → %s\n", metric, imperial);
    return 0;
}

int parse_input(const char *line, double *value, char *metric, char *imperial) {
    const char *to = strstr(line, "to");
    // `to <= line` checks if "to" is at the beginning of the string.
    if (!to || to <= line) {
        printf("[DEBUG] Failed to locate 'to' in correct position.\n");
        return 0;
    }

    printf("[DEBUG] Input line: %s\n", line);
    printf("[DEBUG] Position of 'to': %p\n", to);

    char left[128];
    strncpy(left, line, to - line);
    left[to - line] = '\0';
    printf("[DEBUG] Left part before 'to': %s\n", left);

    if (sscanf(left, "%lf%[^ ]", value, metric) != 2) {
        printf("[DEBUG] Failed to parse value and metric from: %s\n", left);
        return 0;
    }

    if (sscanf(to, "to %[^ ?]", imperial) != 1) {
        printf("[DEBUG] Failed to parse imperial unit from: %s\n", to);
        return 0;
    }

    printf("[DEBUG] Parsed value: %.2f\n", *value);
    printf("[DEBUG] Parsed metric: %s\n", metric);
    printf("[DEBUG] Parsed imperial: %s\n", imperial);
    return 1;
}

int main() {
    char line[256];
    double value;
    char metric[64], imperial[64];
    double factor;

    printf("Unit Conversion Program (type 'exit' to quit)\n");

    while (1) {
        printf("\nEnter your question (e.g., 2meter to inch?)\n> ");
        fgets(line, sizeof(line), stdin);
        line[strcspn(line, "\n")] = '\0';

        if (strcmp(line, "exit") == 0) break;

        if (parse_input(line, &value, metric, imperial)) {
            if (is_valid_conversion(metric, imperial, &factor)) {
                printf("%.2f %s is %.2f %s.\n", value, metric, value * factor, imperial);
            } else {
                printf("❌ Cannot convert %s to %s. Please enter a valid unit pair.\n", metric, imperial);
            }
        } else {
            printf("❌ Invalid input format. Example: 2meter to inch?\n");
        }
    }

    printf("Program terminated.\n");
    return 0;
}
8.41: 5段階に文言が厳しくなる督促状をプリントするプログラム
source
#include <stdio.h>
#include <string.h>

int main(void)
{
    char name[100];
    char address[100];

    const char *demand =
        "ご連絡\n"
        "%s様\n\n"
        "現在、%d円につきまして%dヶ月のお支払い遅延が確認されております。\n"
        "%s\n"
        "お手数ですが、ご住所(%s)宛のご案内もご確認ください。\n"
        "アカウント番号: %d\n";

    const char *level[] = {
        "お支払いのご確認をお願い申し上げます。",
        "恐れ入りますが、お支払い状況についてご確認ください。",
        "未払いの件につき、早めのご対応をお願い申し上げます。",
        "お手数ですが、お支払い予定日をご連絡いただけますと幸いです。",
        "本件につきまして、ご連絡をいただけない場合は、所定の手続きに基づき対応させていただくことがございます。"
    };

    int account;
    int debt;
    int month;

    // --- 入力 ---
    printf("名前: ");
    fgets(name, sizeof(name), stdin);

    printf("住所: ");
    fgets(address, sizeof(address), stdin);

    printf("口座番号: ");
    scanf("%d", &account);

    printf("借入額: ");
    scanf("%d", &debt);

    printf("滞納期間(月): ");
    scanf("%d", &month);

    // --- 改行削除 ---
    name[strcspn(name, "\n")] = '\0';
    address[strcspn(address, "\n")] = '\0';

    // --- レベル決定 ---
    int idx;
    if (month < 2) idx = 0;
    else if (month < 4) idx = 1;
    else if (month < 6) idx = 2;
    else if (month < 8) idx = 3;
    else idx = 4;

    // --- 出力 ---
    printf("\n");
    printf(demand, name, debt, month, level[idx], address, account);

    return 0;
}
8.42: クロスワードパズルを作成するプログラム
source
// crossword5.c
// 5x5 Crossword Generator with Backtracking (C99)
//
// 機能:
// - 黒マス(180°対称)ランダム生成
// - 白マス連結 / 1文字スロット禁止の検証
// - Across/Down スロット抽出
// - MRV(候補最少)+バックトラッキングで単語充填
// - 単語重複禁止 / 外部辞書対応

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdbool.h>

typedef struct {
    char **data;
    int count;
    int cap;
} WordList;

typedef struct {
    int len;
    char dir;      // 'A' or 'D'
    int *rows;     // len 要素
    int *cols;     // len 要素
    char id[8];    // "A1" / "D1" など
    int filled;    // 0/1
    char *word;    // 埋めた単語(または NULL)
} Slot;

typedef struct {
    Slot **data;
    int count;
    int cap;
} SlotList;

typedef struct {
    int r, c;
} Cell;

/* -----------------------------
 * フォールバック辞書
 * ----------------------------- */
static const char *FALLBACK =
"ACE ACT ADD AGE AID AIR ALE ALL AMP AND ANT ANY APE APT ARC ARM ART ASH ASK ATE "
    "BAL BAN BAR BAT BAY BED BEE BEG BEN BET BID BIG BIN BIT BOG BOW BOX BOY BUG BUN BUS BUT BUY BYE "
    "CAB CAD CAN CAP CAR CAT COD COG COP COT COW COY CUP CUT "
    "DAD DAM DAN DAY DEN DEW DID DIE DIG DIN DIP DOE DOG DOT DRY DUE DUG DYE "
    "EEL EGG EGO ELM END ENG ERR EVE EWE EYE "
    "FAD FAN FAR FAT FAX FAY FED FEE FEW FIN FIT FIX FOB FOG FOR FOX FUN FUR "
    "GAG GAL GAP GAS GEL GEM GET GIG GIN GIP GIT GOO GOT GUM GUN GUT GUY GYM "
    "HAD HAG HAM HAN HAS HAT HAW HAY HEM HEN HER HES HEW HEX HID HIM HIP HIS HIT HOG HOP HOT HOW HUE HUG HUH HUM HUT "
    "ICE ICH ICK ILL IMP INK INN ION IRE IRK IRS ISM ITS IVE "
    "JAB JAG JAM JAR JAW JAY JET JIB JIG JOG JOT JOY JUG JUT "
    "KID KIN KIT LAB LAC LAD LAG LAP LAW LAX LAY LED LEG LET LID LIE LIP LIT LOG LOT LOW LUG LYE "
    "MAD MAN MAP MAR MAT MAW MAY MEG MEN MET MID MOP MOT MOW MUG NAG NAP NAY NET NEW NIB NIP NIT NIX NOB NOD NOG NOR NOT NOW NUN NUT "
    "OAF OAK OAR OAT ODD ODE OFF OFT OIL OLD ONE ONO ONYX ORE OUR OUT OVA OWE OWL OWN OXO OXY "
    "PAD PAL PAN PAP PAR PAS PAT PAW PAY PEA PEG PEN PEP PER PET PEW PIG PIN PIT PLY POD POP POT POW PRO PUB PUG PUN PUP PUT "
    "QUO RAG RAM RAP RAT RAW RAY RED REP REV RIB RID RIG RIM RIP ROB ROD ROE ROT ROW RUB RUG RUM RUN RUT RYE "
    "SAD SAG SAP SAT SAW SAY SEA SEE SET SEW SHE SHY SIN SIP SIR SIT SIX SKI SKY SLY SOB SOD SON SOP SOW SOY SPA SPY SUB SUD SUE SUM SUN SUP "
    "TAB TAD TAG TAN TAP TAR TAX TEA TED TEE TEN THE THY TIC TIE TIM TIN TIP TOE TOG TOM TON TOO TOP TOR TOT TOW TOY TRY TUB TUG TUN TWO "
    "URN USE VAN VAT VET VEX VIA VIE VIM VOW WAD WAG WAR WAS WAX WAY WEB WED WEE WET WHO WHY WIN WIT WOE WON WOO WOW WRY "
    "YAK YAM YAP YAW YEA YEP YES YET YOU YOW YUM ZAP ZED ZEN ZIP ZOO "
    "ABLE ACID AGES AIDE AIRY ALGA ALLY ALTO AMEN AMID AMMO AMOK AMPS AMUSE ANEW ANTI APES APEX AREA ARID ARMY ARTS ASIA ATOM AUNT AVID AXIS "
    "BABY BACK BAGS BAKE BALD BALK BALL BAND BANE BANK BANS BARD BARE BARK BARN BARS BASE BASH BASK BASS BATH BATS BEAD BEAK BEAM BEAN BEAR BEAT "
    "BEEF BEEN BEER BEET BEGS BELT BEND BENT BIAS BIDE BIDS BIKE BILL BIND BING BIRD BITE BITS BLAB BLUR BOAR BOAT BODY BOIL BOLD BOLT BOMB BONE "
    "BONG BONUS BOOK BOOM BOON BOOT BORE BORN BOSS BOTH BOWL BOXY BOYS BRAG BRAT BRED BRIG BUCK BUDS BUFF BUGS BULB BULK BULL BURN BUSK BUSH BUSY "
    "BYTE CABS CAFE CAGE CAKE CALF CALL CALM CAME CAMP CANS CAPE CARD CARE CART CASE CASH CASK CAST CATS CAUL CEIL CELL CENT CERE CHAD CHAT CHEF "
    "CHEW CHIC CHIN CHIP CHOP CHOW CLUB COAL COAT COAX COBB COCO COIL COIN COKE COLD COOK COOL COOP COOT CORD CORE CORK CORN COST COUP COVE "
    "COWL CRAB CRAG CRAM CRAY CRED CROW CRUD CUBE CUBS CUED CUES CULT CURL CURR CURT CUSH CUSP CUTE CUTS CYAN "
    "DAIS DANK DARE DARK DART DARN DASH DATA DATE DAWN DAYS DAZE DEAF DEAL DEAN DEAR DEBT DECK DEED DEEM DEER DEFT DEFY DELL DENT DESK "
    "DIAL DICE DIED DIET DIME DINE DING DINT DIRE DIRT DISC DISH DIVE DOCK DODO DOES DOER DOGS DOJO DOLL DOLT DOOM DOOR DORM "
    "DOTE DOUR DOVE DOWN DRAG DRAW DREG DRIP DROP DRUG DRUM DUCK DUEL DUET DULL DUMB DUNE DUNK DUSK DUST DUTY DYED DYES "
    "EACH EARN EASE EAST EASY ECHO ECLAT EDGE EDIT EELS EGGS ELSE EMIT EMUS ENDS ENVY EPIC EROS EVEN EVER EVIL EXAM EXIT "
    "FACE FACT FADE FAIL FAIR FAKE FALL FARM FAST FATE FEAR FEAT FEED FEEL FEET FELL FELT FEND FENS FERN FEST FEUD FILE FILL FILM FIND FINE FIRE FIRM "
    "FISH FIST FIVE FLAG FLAP FLAT FLAW FLEA FLED FLEE FLEW FLIP FLOG FLOP FLOW FLUX FOAM FOGS FOIL FOLD FOLK FOOD FOOL FOOT FORD FORE FORK FORM "
    "FORT FOUL FOUR FOWL FOXY FREE FRET FROG FROM FUEL FULL FUME FUND FURY FUSE "
    "GABS GAGE GAIN GAIT GALA GALE GALL GAME GANG GARB GASH GASP GATE GAZE GEAR GEEK GEMS GENE GENT GERM GETS GIFT GILD GILL "
    "GILT GINS GIRD GIRL GIST GIVE GLAD GLAM GLIB GLOW GLUT GOAD GOAT GOLD GOLF GONE GONG GOOD GOOF GORE GOWN GRAB GRAD GRAM GREW GREY GRID GRIM "
    "GRIN GRIP GRIT GROW GRUB GULF GULL GULP GURU GUSH GUST GUTS GYMS "
    "HACK HAIL HAIR HALF HALL HALT HAND HANG HARD HARE HARK HARM HARP HART HATE HATH HAUL HAVE HAWK HAZE HEAD HEAL HEAP HEAR HEAT HEDGE HEEL HEFT HEIR "
    "HELD HELL HELP HEMP HENS HERB HERD HERE HERO HERS HEWN HIDE HIGH HIKE HILL HILT HIND HINT HIRE HIVE HOAX HOBS HOCK HOGS HOLD HOLE HOLY HOME HONE "
    "HONK HOOD HOOF HOOK HOOP HOOT HOPE HOPS HORN HOSE HOST HOUR HOVE HOWL HUES HUGS HULA HULL HUMP HUNG HUNT HURT HUSH HUSK HUTS HYMN "
    "ICED ICER ICES ICON IDEA IDLE IDOL IONS IOTA IRON ISLE ITCH ITEM IVES IVY "
    "JACK JADE JAIL JAMB JANE JAVA JAWL JEER JEEP JERK JEST JETS JILT JIVE JOAN JOBS JOCK JOEY JOGS JOIN JOKE JOLT JOSE JOTS JOWL "
    "JUDO JUKE JUMP JUNE JUNK JUST JUTE "
    "KICK KIDS KILO KILT KIND KING KISS KITE KNEE KNOB KNOT KNOW "
    "LACE LACK LACY LADY LAID LAKE LAMB LAME LANE LANG LARD LARK LASS LAST LATE LAWN LAWS LAYS LAZE LEAD LEAF LEAK LEAN LEAP LEER LEFT "
    "LEND LENS LENT LESS LEST LEWD LIAR LICE LICK LIED LIEN LIER LIES LIFE LIFT LILT LILY LIME LIMO LINT LION LIPS LISP LIST LITE LIVE LOAD LOAF LOAM "
    "LOAN LOCH LOCK LOFT LOGS LONE LONG LOOK LOOM LOON LOOP LOOT LORD LORE LOSE LOST LOTS LOUD LOUT LOVE LUBE LUCK LUGE LULL LUMP LUNA LURE "
    "LURE LUSH LUST LUTE LUXE LYNX "
    "MACE MACK MADE MAID MAIL MAIN MAKE MALE MALL MALT MANY MAPS MARE MARK MARS MART MASH MASK MASS MAST MATE MATH MAUL MAZE MEAD MEAL "
    "MEAN MEAT MEEK MEET MELD MELT MEMO MEND MENU MERE MESH META MICE MILD MILE MILK MILL MIND MINE MINI MINK MINT MIRE MISS MIST MITE MITT "
    "MOAN MOAT MOCK MODE MOLD MOLE MOLT MONK MOOD MOON MOOR MOOT MORE MORN MOSS MOST MOTH MOVE MOWN MUCH MUCK MULE MULL MUSE MUSK MUST MUTT "
    "NAIL NAME NARY NAVE NAVY NEAR NEAT NECK NEED NEON NERD NEST NEWS NEWT NEXT NIBS NICK NIGH NINE NOOK NOON NOPE NORM NOSE NOTE NOVA "
    "NUDE NULL NURSE NUTS "
    "OATH OATS OBEY OBOE ODDS ODOR OFFS OILS OKAY OLDS OLIVE OMAN OMEN ONCE ONES ONLY ONTO OOZE OOPS ORES OVAL OVEN OVER OWED OWLS OWNS OXEN OXID "
    "PACE PACK PACT PADS PAGE PAID PAIN PAIR PAIL PALE PALM PANE PANG PANS PANT PAPA PARE PARK PART PASS PAST PATE PATH PATS PEAK PEAL PEAR "
    "PEAS PEAT PECK PEEL PEER PEEP PEGS PENS PENT PEON PEPS PERT PEST PETS PHEW PIAN PICK PIER PIES PIGS PILE PILL PINE PING PINK PINS PINT "
    "PIPES PITH PITS PLAN PLAY PLED PLOD PLOT PLOW PLUG PLUM POEM POET POKE POLE POLK POND PONE PONG PONY POOL POOR POPE POPS PORK PORT "
    "POSE POTS POUR PRAM PRAT PRAY PREP PREY PRIM PROD PROM PROP PROS PUFF PUKE PULL PULP PUMA PUMP PUNK PUNS PUNT PUPS PURE PURR PUSH PUSS PUTS "
    "QUAD QUAY QUIT QUIZ "
    "RACE RACK RACY RAFT RAGE RAGS RAID RAIL RAIN RAKE RANK RANT RARE RASH RATE RATS RAVE READ REAL REAP REAR REEF REEL REIN RENT REST "
    "RICE RICH RIDE RIFT RIGS RIME RING RINK RISE RISK RITE ROAD ROAM ROAR ROOF ROOK ROOM ROOT ROPE ROTE ROTS ROVE RUBE RUDE RUGS RUIN RULE RUNG RUNS "
    "RUNT RUSH RUST RUTH "
    "SACK SAFE SAGE SAID SAIL SAKE SALE SALT SAME SAND SANE SANG SANK SASH SAVE SAWN SCAB SCAR SCAT SCOW SCUD SCUM SEAL SEAM "
    "SEAR SEAT SEED SEEK SEEM SEEN SEEP SEER SEES SELF SELL SEND SENT SETS SEWN SEWS SHAD SHAG SHAM SHED SHOE SHOT SHOW SHUN SHUT SICK SIDE "
    "SIFT SIGH SIGN SILK SILL SINE SING SINK SIRE SITE SITS SIZE SKID SKIM SKIN SLAB SLAM SLAP SLAT SLAW SLAY SLED SLEW SLID SLIM SLIP SLOB SLOG SLOT "
    "SLOW SLUG SLUM SLUR SMOG SMUG SMUT SNAP SNIP SNOW SNUB SNUG SOAK SOAP SOAR SOBER SOCK SODA SOFA SOFT SOIL SOLD SOLE SOLID SOME SONG SOON SOOT "
    "SORE SORT SOUL SOUR SOWN SOYA SPAM SPAN SPAR SPAS SPAT SPEC SPIN SPIT SPOT SPUN SPUR SPY "
    "STAB STAG STAN STAR STAY STEM STEP STEW STIR STOP STOW STUB STUN SUCH SUDS SUED SUES SUET SURE SWAB SWAG SWAM SWAN SWAP SWAY SWIM SWUM "
    "TACK TACO TACT TAIL TAKE TALE TALK TALL TAME TANG TANK TAPE TAPS TARE TARN TARP TART TASK TAUT TEAL TEAM TEAR TEAS TECH TEEM TEEN TEES TELL "
    "TEND TENT TERM TERN TEST TEXT THAN THIS THAT THEM THEN THEY THIN TIDE TIED TIER TIES TILL TILT TIME TINY TIPS TIRE TOAD TOAST TOCK TOLL "
    "TOMB TONE TONG TONK TONS TONY TOOL TOOT TORE TORN TORS TORT TOSS TOTE TOUR TOUT TOWN TOYS TRAM TRAP TRAY TREE TREK TRIM TRIO TRIP TRUE "
    "TUBA TUBE TUFT TUNA TUNE TURN TUSH TUSK TWIG TWIN TWIT TYPE TYRE "
    "UGLY UMBER UNIT URGE USED USER USES UTTER "
    "VACS VAIL VAIN VAMP VANE VARY VASE VAST VATS VEAL VEER VEIL VEIN VEND VENT VERB VERY VEST VETO VETS VIAL VICE VIED VIES VIEW VINE VINO VIOL VISE "
    "VOID VOTE VOWS "
    "WACK WADE WAGE WAGS WAIL WAIT WAKE WALK WALL WARD WARE WARM WARN WARP WARS WART WARY WASH WASP WAVE WAXY WEAK WEAL WEAN WEAR WEED WEEK WEEP WEIR WELD WELL WELT "
    "WENT WEPT WERE WEST WHAM WHAT WHEE WHEN WHET WHEY WHIM WHIP WHIR WHIT WHIZ WHOM WHYS WICK WIDE WIFE WILD WILL WILY WIND WINE WING WINK WIPE WIRE "
    "WISE WISH WISP WITH WITS WIVE WOKE WOLF WOMB WONK WONT WOOD WOOF WOOL WORE WORK WORM WORN WOVE WRAP WREN WRIT "
    "XENO XYLO "
    "YACK YAMS YARD YARN YAWN YAWP YEAH YEAR YEAS YELL YELP YOGA YOKE YOLK YORE YOUR YULE "
    "ZANY ZEAL ZEBU ZERO ZEST ZINC ZING ZONE ZOO "
;

/* -----------------------------
 * ユーティリティ
 * ----------------------------- */
static void wordlist_init(WordList *wl) {
    wl->data = NULL;
    wl->count = 0;
    wl->cap = 0;
}
static void wordlist_push(WordList *wl, char *s) {
    if (wl->count == wl->cap) {
        wl->cap = wl->cap ? wl->cap * 2 : 64;
        wl->data = (char**)realloc(wl->data, wl->cap * sizeof(char*));
    }
    wl->data[wl->count++] = s;
}
static int is_alpha_str(const char *s) {
    if (!s || !*s) return 0;
    for (; *s; ++s) if (!isalpha((unsigned char)*s)) return 0;
    return 1;
}
static void to_upper_str(char *s) {
    for (; *s; ++s) *s = (char)toupper((unsigned char)*s);
}
static int str_equals(const char *a, const char *b) {
    return strcmp(a, b) == 0;
}
static int used_contains(char **used, int used_count, const char *w) {
    for (int i = 0; i < used_count; ++i)
        if (str_equals(used[i], w)) return 1;
    return 0;
}

/* -----------------------------
 * 辞書ロード(2〜size文字)
 * ----------------------------- */
static void load_words(WordList *by_len, int size, const char *path) {
    for (int L = 0; L <= size; ++L) wordlist_init(&by_len[L]);

    // 外部ファイル読み込み
    int loaded = 0;
    if (path) {
        FILE *fp = fopen(path, "r");
        if (fp) {
            char buf[256];
            while (fgets(buf, sizeof(buf), fp)) {
                // 改行除去 & 非英字削除
                char tmp[256]; int k = 0;
                for (int i = 0; buf[i] && k < 255; ++i) {
                    if (isalpha((unsigned char)buf[i])) tmp[k++] = buf[i];
                }
                tmp[k] = '\0';
                to_upper_str(tmp);
                int L = (int)strlen(tmp);
                if (L >= 2 && L <= size && is_alpha_str(tmp)) {
                    char *dup = strdup(tmp);
                    wordlist_push(&by_len[L], dup);
                    loaded++;
                }
            }
            fclose(fp);
        }
    }

    // フォールバック
    if (!loaded) {
        char *copy = strdup(FALLBACK);
        char *tok = strtok(copy, " \n\t\r");
        while (tok) {
            to_upper_str(tok);
            int L = (int)strlen(tok);
            if (L >= 2 && L <= size && is_alpha_str(tok)) {
                wordlist_push(&by_len[L], strdup(tok));
            }
            tok = strtok(NULL, " \n\t\r");
        }
        free(copy);
    }
    // ※簡易: 重複除去は省略(必要なら sort+unique を実装)
}

/* -----------------------------
 * スロット管理
 * ----------------------------- */
static void slots_init(SlotList *sl) {
    sl->data = NULL; sl->count = 0; sl->cap = 0;
}

// スロット追加(ポインタ)
static int slots_push(SlotList *sl, Slot *s) {
    if (sl->count == sl->cap) {
        sl->cap = sl->cap ? sl->cap * 2 : 32;
        sl->data = (Slot**)realloc(sl->data, sl->cap * sizeof(Slot*));
    }
    sl->data[sl->count++] = s;
    return sl->count;
}

static void free_slots(SlotList *sl) {
    for (int i = 0; i < sl->count; ++i) {
        free(sl->data[i]->rows);
        free(sl->data[i]->cols);
        free(sl->data[i]);        // Slot 構造体自体を解放
    }
    free(sl->data);               // ポインタ配列自体を解放
}

/* -----------------------------
 * 盤面アクセス
 * ----------------------------- */
static inline int idx(int size, int r, int c) { return r*size + c; }
static char getcell(char *grid, int size, int r, int c) { return grid[idx(size,r,c)]; }
static void setcell(char *grid, int size, int r, int c, char ch) { grid[idx(size,r,c)] = ch; }

/* -----------------------------
 * 黒マス生成と検証
 * ----------------------------- */
static void extract_slots(char *grid, int size, SlotList *across, SlotList *down) {
    slots_init(across);
    slots_init(down);

    // Across
    int sid = 1;
    for (int r = 0; r < size; ++r) {
        int c = 0;
        while (c < size) {
            if (getcell(grid,size,r,c) != '#' && (c == 0 || getcell(grid,size,r,c-1) == '#')) {
                int start = c;
                while (c < size && getcell(grid,size,r,c) != '#') c++;
                int len = c - start;               
                Slot *s = (Slot*)malloc(sizeof(Slot));
                s->len = len;
                s->dir = 'A';
                s->rows = (int*)malloc(len * sizeof(int));
                s->cols = (int*)malloc(len * sizeof(int));
                s->filled = 0;
                s->word = NULL;
                snprintf(s->id, sizeof(s->id), "A%d", sid++);
                for (int cc = 0; cc < len; ++cc) {
                    s->rows[cc] = r;
                    s->cols[cc] = start + cc;
                }
                slots_push(across, s);
            } else c++;
        }
    }
    // Down
    sid = 1;
    for (int c = 0; c < size; ++c) {
        int r = 0;
        while (r < size) {
            if (getcell(grid,size,r,c) != '#' && (r == 0 || getcell(grid,size,r-1,c) == '#')) {
                int start = r;
                while (r < size && getcell(grid,size,r,c) != '#') r++;
                int len = r - start;
                Slot *s = (Slot *)malloc(sizeof(Slot));
                s->len = len;
                s->dir = 'D';
                s->rows = (int *)malloc(len * sizeof(int));
                s->cols = (int *)malloc(len * sizeof(int));
                s->filled = 0;
                s->word = NULL;
                snprintf(s->id, sizeof(s->id), "D%d", sid++);
                for (int rr = 0; rr < len; ++rr) {
                    s->rows[rr] = start + rr;
                    s->cols[rr] = c;
                }
                slots_push(down, s);
            } else r++;
        }
    }
}

static int validate_mask(char *grid, int size) {
    // 白マス連結
    int total_whites = 0;
    for (int r = 0; r < size; ++r)
        for (int c = 0; c < size; ++c)
            if (getcell(grid,size,r,c) != '#') total_whites++;

    if (total_whites == 0) return 0;

    int *visited = (int*)calloc(size*size, sizeof(int));
    int *queue = (int*)malloc(size*size * sizeof(int));
    int qh = 0, qt = 0;

    // 最初の白マスを探す
    int sr = -1, sc = -1;
    for (int r = 0; r < size && sr < 0; ++r)
        for (int c = 0; c < size; ++c)
            if (getcell(grid,size,r,c) != '#') { sr = r; sc = c; break; }

    visited[idx(size,sr,sc)] = 1;
    queue[qt++] = idx(size,sr,sc);

    int seen = 1;
    int dr[4] = {1,-1,0,0};
    int dc[4] = {0,0,1,-1};

    while (qh < qt) {
        int p = queue[qh++];
        int r = p / size, c = p % size;
        for (int k = 0; k < 4; ++k) {
            int nr = r + dr[k], nc = c + dc[k];
            if (nr < 0 || nr >= size || nc < 0 || nc >= size) continue;
            if (getcell(grid,size,nr,nc) == '#') continue;
            int ip = idx(size,nr,nc);
            if (!visited[ip]) { visited[ip] = 1; queue[qt++] = ip; seen++; }
        }
    }
    free(visited); free(queue);
    if (seen != total_whites) return 0;

    // 1文字スロット禁止 & 最低スロット数
    SlotList across, down;
    extract_slots(grid, size, &across, &down);
    int ok = 1;
    for (int i = 0; i < across.count; ++i) if (across.data[i] -> len < 2) ok = 0;
    for (int i = 0; i < down.count; ++i) if (down.data[i] -> len < 2) ok = 0;
    if (across.count + down.count < 6) ok = 0;
    free_slots(&across); 
    free_slots(&down);
    return ok;
}

static int generate_random_mask(char *grid, int size, double density, int symmetry, int max_tries) {
    for (int attempt = 0; attempt < max_tries; ++attempt) {
        // 全白で初期化
        for (int i = 0; i < size*size; ++i) grid[i] = '.';
        // 半分だけ振る → 180°対称
        for (int r = 0; r < size; ++r) {
            for (int c = 0; c < size; ++c) {
                int rr = size - 1 - r;
                int cc = size - 1 - c;
                if ((r > rr) || (r == rr && c > cc)) continue;
                double u = (double)rand() / (double)RAND_MAX;
                if (u < density) {
                    setcell(grid,size,r,c,'#');
                    setcell(grid,size,rr,cc,'#');
                }
            }
        }
        if (validate_mask(grid, size)) return 1;
    }
    return 0;
}

/* -----------------------------
 * バックトラッキング
 * ----------------------------- */
typedef struct {
    char **data;
    int count;
    int cap;
} PtrList;

static void ptrlist_init(PtrList *pl) { pl->data = NULL; pl->count = 0; pl->cap = 0; }
static void ptrlist_push(PtrList *pl, char *p) {
    if (pl->count == pl->cap) {
        pl->cap = pl->cap ? pl->cap*2 : 32;
        pl->data = (char**)realloc(pl->data, pl->cap * sizeof(char*));
    }
    pl->data[pl->count++] = p;
}
static void ptrlist_free(PtrList *pl) { free(pl->data); }

static void init_blanks(char *grid, int size) {
    for (int r = 0; r < size; ++r)
        for (int c = 0; c < size; ++c)
            if (getcell(grid,size,r,c) != '#')
                setcell(grid,size,r,c,'.');
}

static void candidates_for_slot(const Slot *s, char *grid, WordList *by_len, char **used, int used_count, PtrList *out) {
    ptrlist_init(out);
    int L = s->len;
    WordList *wl = &by_len[L];
    for (int i = 0; i < wl->count; ++i) {
        char *w = wl->data[i];
        if (used_contains(used, used_count, w)) continue;
        int ok = 1;
        for (int j = 0; j < L; ++j) {
            char gch = getcell(grid, L>0?L:5 /*dummy*/, s->rows[j], s->cols[j]); // size は別なので注意
            // ↑ size は実盤の size。ここは grid をそのまま使うので別引数にすべきだが、
            // 呼び出し側で正しい size を渡している前提。安全のため getcell を再定義しても良い。
        }
        // しっかりチェック
        for (int j = 0; j < L; ++j) {
            char gch = getcell(grid, /*size 不明→後で渡す*/ 0, 0, 0);
            (void)gch; // ダミー: 実際の size を持つ版を用意する
        }
    }
}

/* --- 上の candidates_for_slot は size 引数が必要。修正します --- */
static void candidates_for_slot_sz(const Slot *s, char *grid, int size, WordList *by_len, char **used, int used_count, PtrList *out) {
    ptrlist_init(out);
    int L = s->len;
    WordList *wl = &by_len[L];
    for (int i = 0; i < wl->count; ++i) {
        char *w = wl->data[i];
        if (used_contains(used, used_count, w)) continue;
        int ok = 1;
        for (int j = 0; j < L; ++j) {
            char gch = getcell(grid, size, s->rows[j], s->cols[j]);
            if (gch != '.' && gch != w[j]) { ok = 0; break; }
        }
        if (ok) ptrlist_push(out, w);
    }
}

static int choose_next_slot(SlotList *slots, char *grid, int size, WordList *by_len, char **used, int used_count, int *out_idx, PtrList *out_cands) {
    int best = -1;
    int best_count = 1000000;
    PtrList tmp;
    for (int i = 0; i < slots->count; ++i) {
        Slot *s = slots->data[i];
        if (s->filled) continue;
        candidates_for_slot_sz(s, grid, size, by_len, used, used_count, &tmp);
        if (tmp.count < best_count) {
            best = i;
            best_count = tmp.count;
            // out_cands を更新
            ptrlist_free(out_cands);
            ptrlist_init(out_cands);
            for (int k = 0; k < tmp.count; ++k)
                ptrlist_push(out_cands, tmp.data[k]);
            if (best_count <= 1) { ptrlist_free(&tmp); break; }
        }
        ptrlist_free(&tmp);
    }
    *out_idx = best;
    return (best >= 0) ? best_count : -1;
}

static int place_word(Slot *s, char *grid, int size, const char *w, Cell *changed, int *changed_count) {
    *changed_count = 0;
    for (int j = 0; j < s->len; ++j) {
        int r = s->rows[j], c = s->cols[j];
        char gch = getcell(grid, size, r, c);
        if (gch == '.') {
            setcell(grid,size,r,c,w[j]);
            changed[(*changed_count)++] = (Cell){r,c};
        } else if (gch != w[j]) {
            return 0; // conflict
        }
    }
    s->filled = 1;
    s->word = (char*)w;
    return 1;
}
static void unplace_word(Slot *s, char *grid, int size, Cell *changed, int changed_count) {
    for (int i = 0; i < changed_count; ++i)
        setcell(grid,size,changed[i].r, changed[i].c, '.');
    s->filled = 0;
    s->word = NULL;
}

static int shuffle_ptrs(char **arr, int n) {
    for (int i = n-1; i > 0; --i) {
        int j = rand() % (i+1);
        char *tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
    }
    return 0;
}

/* -----------------------------
 * 出力
 * ----------------------------- */
static void print_grid(char *grid, int size) {
    for (int r = 0; r < size; ++r) {
        for (int c = 0; c < size; ++c) putchar(getcell(grid,size,r,c));
        putchar('\n');
    }
}

static int solve_backtracking(char *grid, int size, SlotList *across, SlotList *down, WordList *by_len, int max_steps) {
    // 事前初期化
    init_blanks(grid, size);

    // スロット結合
    SlotList slots; slots_init(&slots);
    for (int i = 0; i < across->count; ++i) slots_push(&slots, across->data[i]);
    for (int i = 0; i < down->count; ++i) slots_push(&slots, down->data[i]);

    char **used = NULL; int used_count = 0, used_cap = 0;

    int steps = 0;
    PtrList cands; ptrlist_init(&cands);

    // 再帰をループでラップするのは複雑なので、C でも再帰で実装
    // ただし slots はポインタ複製が発生しないよう、そのまま参照
    int bt() {
        steps++;
        if (steps > max_steps) return 0;
        // 完成判定
        int all_done = 1;
        for (int i = 0; i < slots.count; ++i)
        {
           if (!slots.data[i] -> filled) 
           { 
               all_done = 0; 
               break; 
           }
        }
        if (all_done)
        {
            printf("bt() returning 1 (all done) steps %d\n", steps);
            return 1;
        }

        int idx_slot;
        int count = choose_next_slot(&slots, grid, size, by_len, used, used_count, &idx_slot, &cands);
        if (idx_slot < 0) 
        {
            return 1; // 何もないが完成に近い
        }
        if (count <= 0) 
        {
            return 0;
        }

        // 候補ランダム化
        shuffle_ptrs(cands.data, cands.count);

        Slot *s = slots.data[idx_slot];
        for (int i = 0; i < cands.count; ++i) {
            print_grid(grid, size);
            const char *w = cands.data[i];
            Cell changed[250]; int changed_count = 0;
            if (!place_word(s, grid, size, w, changed, &changed_count)) continue;

            // used に追加
            if (used_count == used_cap) {
                used_cap = used_cap ? used_cap * 2 : 64;
                used = (char**)realloc(used, used_cap * sizeof(char*));
            }
            used[used_count++] = (char*)w;

            if (bt()) 
            { 
                //ptrlist_free(&cands); 
                //free(used); 
                return 1; 
            }

            // 戻す
            used_count--;
            unplace_word(s, grid, size, changed, changed_count);
        }
        return 0;
    }

    int ok = bt();
    ptrlist_free(&cands);
    free(used);
    // 注意: slots 内の rows/cols は across/down とメモリ共有になっているためここで free しない
    return ok;
}

static void print_slots(const SlotList *across, const SlotList *down) {
    printf("Across:\n");
    for (int i = 0; i < across->count; ++i) {
        const Slot *s = across->data[i];
        printf("  %3s (%d): %s\n", s->id, s->len, s->word ? s->word : "");
    }
    printf("Down:\n");
    for (int i = 0; i < down->count; ++i) {
        const Slot *s = down->data[i];
        printf("  %3s (%d): %s\n", s->id, s->len, s->word ? s->word : "");
    }
}

/* -----------------------------
 * 盤面生成+解探索
 * ----------------------------- */
static int build_and_solve(int size, const char *words_path, double density, unsigned seed, int tries, int symmetry) {
    srand(seed ? seed : (unsigned)time(NULL));

    // グリッド
    char *grid = (char*)malloc(size*size);
    if (!grid) return 0;

    // 辞書
    WordList *by_len = (WordList*)calloc(size+1, sizeof(WordList));
    load_words(by_len, size, words_path);

    printf("load_words end\n");
    for (int attempt = 1; attempt <= tries; ++attempt) {
        if (!generate_random_mask(grid, size, density, symmetry, 2000)) continue;
        SlotList across, down;
        extract_slots(grid, size, &across, &down);
        int ok = solve_backtracking(grid, size, &across, &down, by_len, 100000);
        if (ok) {
            printf("生成成功: %d 回目で解を発見\n\n", attempt);
            print_grid(grid, size);
            print_slots(&across, &down);
            free_slots(&across); free_slots(&down);
            // by_len の解放
            for (int L = 0; L <= size; ++L) {
                for (int i = 0; i < by_len[L].count; ++i) free(by_len[L].data[i]);
                free(by_len[L].data);
            }
            free(by_len);
            free(grid);
            return 1;
        }
        free_slots(&across); free_slots(&down);
    }

    printf("解が見つかりませんでした。単語を増やす・密度を下げる・試行回数を増やすなど試してください。\n");
    // by_len の解放
    for (int L = 0; L <= size; ++L) {
        for (int i = 0; i < by_len[L].count; ++i) free(by_len[L].data[i]);
        free(by_len[L].data);
    }
    free(by_len);
    free(grid);
    return 0;
}

/* -----------------------------
 * CLI
 * ----------------------------- */
static int parse_int(const char *s, int def) { return s ? atoi(s) : def; }
static double parse_double(const char *s, double def) { return s ? atof(s) : def; }

int main(int argc, char **argv) {
    int size = 5;
    double density = 0.5;
    unsigned seed = 42; // 0→time(NULL)
    int tries = 50000;
    const char *words_path = ""; //"./8.42_dictionary.txt";
    int symmetry = 1;

    for (int i = 1; i < argc; ++i) {
        if (!strcmp(argv[i], "--size") && i+1 < argc) size = parse_int(argv[++i], 5);
        else if (!strcmp(argv[i], "--density") && i+1 < argc) density = parse_double(argv[++i], 0.28);
        else if (!strcmp(argv[i], "--seed") && i+1 < argc) seed = (unsigned)parse_int(argv[++i], 0);
        else if (!strcmp(argv[i], "--tries") && i+1 < argc) tries = parse_int(argv[++i], 500);
        else if (!strcmp(argv[i], "--words") && i+1 < argc) words_path = argv[++i];
        else if (!strcmp(argv[i], "--no-symmetry")) symmetry = 0;
        else if (!strcmp(argv[i], "--help")) {
            printf("Usage: %s [--size N] [--density D] [--seed S] [--tries T] [--words path] [--no-symmetry]\n", argv[0]);
            return 0;
        }
    }

    if (size < 3 || size > 9) {
        fprintf(stderr, "size は 3〜9 を推奨(デフォルト 5)。\n");
        size = 5;
    }
    if (density < 0.0) density = 0.0;
    if (density > 0.6) density = 0.6;

    return build_and_solve(size, words_path, density, seed, tries, symmetry) ? 0 : 1;
}
ポータルサイト
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?