読者です 読者をやめる 読者になる 読者になる

年齢並べ

プログラマのための論理パズルに掲載されているパズル「年齢並べ」をC言語に解かせてみました。順列の生成のところで、つまってしまって少し凹みました。

コード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
struct Person {
  const char* name;
  int age;
  struct Person* next;
  struct Person* prev;
};
struct Person* person(const char* name,int age) {
  struct Person* p = malloc(sizeof(struct Person));
  p->name = name;
  p->age = age;
  p->next = NULL;
  p->prev = NULL;
  return p;
}
struct People {
  struct Person** p;
  int n;
};
static struct People people;
void init_people(const char* name) {
people.p = malloc(5*sizeof(people.p[0]));
people.p[people.n++] = person(name, 0);
}
void free_people(void) {
int i;
for(i = 0;i < people.n; i++) 
  free(people.p[i]);
free(people.p);
people.n = 0;
}
void add_person(const char* name) {
struct Person* p = person(name, 0);
  people.p[people.n -1]->next = p;
  p->prev = people.p[people.n -1];
  people.p[people.n++] = p;
}
struct Person* lookup(const char* name) {
  int i;
  for(i = 0; i < people.n; i++)
    if(!strcmp(name, people.p[i]->name))
      return people.p[i];
  return NULL;
}

void set_age(void) {
struct Person* p;
  p = lookup("Luke");
  // Lukeは16歳
  p->age = 16; 
  // Lukeは左隣より5歳下
  p->prev->age = 21;//16 + 5 
  p = lookup("Carol");
  // Carolは40歳
  p->age = 40;
  // Carolは左隣より12歳上
  p->prev->age = 28;//40 -12
  //合計は135歳(残り一人の年齢は30=(135-16-21-40-28)歳)
  int i;
  for(i = 0; i < people.n; i++)
    if(0 ==  people.p[i]->age)
      people.p[i]->age = 30;// 135 - 16 - 21 - 40 - 28
}
int check_ordering(void) {
struct Person *Tommy, *Jessica;
  Tommy = lookup("Tommy");
//Tommyは右隣より5歳上
if(Tommy->age != (Tommy->next->age +5))
  return 0;
  Jessica = lookup("Jessica");
//Jessicaは左隣より14歳上
if(Jessica->age != (Jessica->prev->age +14))
  return 0;  
if(!(lookup("Luke")->age < Tommy->age &&
     Tommy->age < lookup("Andrew")->age &&
     lookup("Andrew")->age < Jessica->age &&
     Jessica->age < lookup("Carol")->age))
       return 0;
int i;
int tot = 0;
  for(i = 0; i < people.n; i++)
    tot += people.p[i]->age;
  people.n = 0;
  return tot == 135;
}
void construct_people(int argc, char* argv[]) {
  init_people(argv[0]);
  int i;
  for(i = 1; i < argc; i++)
    add_person(argv[i]);
  people.p[0]->prev = people.p[4];
  assert(people.n == 5 && "check #people");
  people.p[4]->next = people.p[0];
  set_age();
}
static char* names[] = { 
           "Andrew",
	   "Carol",
	   "Jessica", 
	   "Luke", 
	   "Tommy"};
void swap(char** names,int i, int j) {
char* t;
  t = names[i]; names[i] = names[j]; names[j] = t;
}
void permutation(char**names,int depth, int max_depth) {
static count = 1;  
  if(depth == max_depth) {
    int i;
    names--;
    construct_people(max_depth+1, names);
    printf("%d)", count++);
    for(i = 0; i < max_depth+1; i++) {
      printf("%s ", names[i]);
    }
    if(check_ordering()) {
      printf(":success!\n");
      int i;
      for(i = 0; i< 5; i++) {
        printf("  %s, age=%d\n", people.p[i]->name, people.p[i]->age);
      }
    }
    else
      printf(":failed\n");
    free_people();
    return;
  }
  int i;
  for(i = depth; i < max_depth; i++) {
    swap(names, depth , i);
    permutation(names, depth + 1, max_depth);
    swap(names, i , depth);
  }
}

void test(void);
int main(int argc, char* argv[]) { 
  if(argc == 2 && !strcmp(argv[1], "test")) {
    test();
    exit(0);
  }
  permutation(names+1,0, 4);
  exit(0);
}
CheckPerson(struct Person* p, const char* name,
            int age, struct Person* next, struct Person* prev, FILE* fp) {
  if(fp) {
    fprintf(fp,"name:%s, age:%d, next:%s, prev:%s\n", 
    p->name, p->age,
    (p->next)? "NN": "NULL",
    (p->prev)? "NN": "NULL"  
    );
  }
 assert(!strcmp(name, p->name) && "check name");
 assert(age == p->age && "check age");
 assert(next == p->next && "check next");
 assert(prev == p->prev && "check prev");
}
void test_person(void) {
struct Person* p = person("Luke", 16);
CheckPerson(p, "Luke", 16, NULL, NULL, NULL);
free(p);
}

void test_add_person(void) {
init_people("Andrew");
add_person("Carol");
add_person("Jessica");
add_person("Luke");
add_person("Tommy");
people.p[0]->prev = people.p[4];
CheckPerson(people.p[0], "Andrew", 0, people.p[1], people.p[4], NULL);
CheckPerson(people.p[1], "Carol", 0, people.p[2], people.p[0], NULL);
CheckPerson(people.p[2], "Jessica", 0, people.p[3], people.p[1], NULL);
CheckPerson(people.p[3], "Luke", 0, people.p[4], people.p[2], NULL);
CheckPerson(people.p[4], "Tommy", 0, NULL, people.p[3], NULL);
assert(people.n == 5 && "#people");
free_people();
}

void test_lookup(void) {
init_people("Andrew");
add_person("Carol");
add_person("Jessica");
add_person("Luke");
add_person("Tommy");
CheckPerson(lookup("Andrew"), "Andrew", 0, people.p[1], NULL, NULL);
CheckPerson(lookup("Carol"), "Carol", 0, people.p[2], people.p[0], NULL);
CheckPerson(lookup("Jessica"), "Jessica", 0, people.p[3], people.p[1], NULL);
CheckPerson(lookup("Luke"), "Luke", 0, people.p[4], people.p[2], NULL);
CheckPerson(lookup("Tommy"), "Tommy", 0, NULL, people.p[3], NULL);
assert(NULL == lookup("nabeyang"));
free_people();
}

void test_set_age(void) {
init_people("Luke");
add_person("Jessica");
add_person("Andrew");
add_person("Carol");
add_person("Tommy");
people.p[4]->next = people.p[0];
people.p[0]->prev = people.p[4];
set_age();
CheckPerson(people.p[0], "Luke", 16, people.p[1], people.p[4], NULL);
CheckPerson(people.p[1], "Jessica", 30, people.p[2], people.p[0], NULL);
CheckPerson(people.p[2], "Andrew", 28, people.p[3], people.p[1], NULL);
CheckPerson(people.p[3], "Carol", 40, people.p[4], people.p[2], NULL);
CheckPerson(people.p[4], "Tommy", 21, people.p[0], people.p[3], NULL);
assert(check_ordering() && "check ordering");
free_people();
}

void test_check_ordering(void) {
{
  char* input[] = {"Tommy", "Luke", "Jessica", "Andrew", "Carol"};
  construct_people(5, input);
  assert(check_ordering() && "expect  success");
  free_people();
}
{
  char* input[] = {"Luke", "Tommy", "Jessica", "Andrew", "Carol"};
  construct_people(5, input);
  assert(!check_ordering() && "should be failed");
  free_people();
}
}
void test_swap(void) {
  char* greetings[] = {"good morning", "hello", "hi", "bye"};
  swap(greetings, 1, 2);
  assert(!strcmp("good morning", greetings[0]));
  assert(!strcmp("hi", greetings[1]));
  assert(!strcmp("hello", greetings[2]));
  assert(!strcmp("bye", greetings[3]));
}

void test(void) {
  test_person();
  test_add_person();
  test_lookup();
  test_set_age();
  test_check_ordering();
  test_swap();
}

出力結果



1)Andrew Carol Jessica Luke Tommy :failed
2)Andrew Carol Jessica Tommy Luke :failed
3)Andrew Carol Luke Jessica Tommy :failed
4)Andrew Carol Luke Tommy Jessica :failed
5)Andrew Carol Tommy Luke Jessica :success!
Andrew, age=28
Carol, age=40
Tommy, age=21
Luke, age=16
Jessica, age=30
6)Andrew Carol Tommy Jessica Luke :failed
7)Andrew Jessica Carol Luke Tommy :failed
8)Andrew Jessica Carol Tommy Luke :failed
9)Andrew Jessica Luke Carol Tommy :failed
10)Andrew Jessica Luke Tommy Carol :failed
11)Andrew Jessica Tommy Luke Carol :failed
12)Andrew Jessica Tommy Carol Luke :failed
13)Andrew Luke Jessica Carol Tommy :failed
14)Andrew Luke Jessica Tommy Carol :failed
15)Andrew Luke Carol Jessica Tommy :failed
16)Andrew Luke Carol Tommy Jessica :failed
17)Andrew Luke Tommy Carol Jessica :failed
18)Andrew Luke Tommy Jessica Carol :failed
19)Andrew Tommy Jessica Luke Carol :failed
20)Andrew Tommy Jessica Carol Luke :failed
21)Andrew Tommy Luke Jessica Carol :failed
22)Andrew Tommy Luke Carol Jessica :failed
23)Andrew Tommy Carol Luke Jessica :failed
24)Andrew Tommy Carol Jessica Luke :failed