M.Hiroi's Home Page
http://www.geocities.jp/m_hiroi/

Puzzle DE Programming

スライドパズル NO-OFF

[ Home | Puzzle ]

●パズルの説明

「15 パズル」でお馴染みのスライドパズルです。それでは問題です。

[問題] スライドパズル NO-OFF
  ┌───┬─┬─┐  ┌─┬─┬───┐  ┌───┬─┬─┐  
  │ 電球 │O│N│  │N│O│ 電球 │  │ 電球 │N│O│  
  ├─┬─┼─┼─┤  ├─┼─┼─┬─┤  ├─┬─┼─┼─┤  
  │O│F│F│  │  │F│O│F│  │  │O│F│F│  │  
  └─┴─┴─┴─┘  └─┴─┴─┴─┘  └─┴─┴─┴─┘  
        問題A              問題B             GOAL

                         図:NO-OFF

問題 A, B から GOAL までの最短手順を求めてください。

スライドパズル NO-OFF は、問題 A の "ON-OFF" を GOAL のように "NO-OFF" にチェンジするパズルです。NO-OFF は芦ヶ原伸之氏が考案されたパズルで、C MAGAZINE 1991 年 1 月号の「Cマガ電脳クラブ」でも出題されています。問題 B は GOAL からの最長手数の局面のひとつです。このパズルは局面の総数が少ないにもかかわらず、手数がけっこうかかる面白いパズルです。


●解答

このパズルは局面の総数が 540 通りしかありません。

電球(3 通り) * 空き場所(6 通り) * N (5 通り) * O (4C2 = 6 通り) = 540 通り

プログラムを作るのであれば、幅優先探索で簡単に解を求めることができます。ちなみに、GOAL までの最長手数は 56 手で、局面は全部で 3 通りあります。問題 B はその中の 1 つです。

  ┌─┬─┬───┐    ┌─┬─┬───┐    ┌─┬─┬───┐  
  │F│N│ 電球 │    │N│O│ 電球 │    │O│F│ 電球 │  
  ├─┼─┼─┬─┤    ├─┼─┼─┬─┤    ├─┼─┼─┬─┤  
  │O│O│F│  │    │F│O│F│  │    │N│O│  │F│  
  └─┴─┴─┴─┘    └─┴─┴─┴─┘    └─┴─┴─┴─┘  

                        図:最長手数の局面

●プログラム1

/*
 * no_off.c : NO-OFF puzzle
 *
 *            Copyright (C) 2004-2006 Makoto Hiroi
 */
/*

  大駒 [L] = 3 通り
  3 * 6 * 5  * 4C2 = 3 * 6 * 5 * 6 = 540

  space = 0
  [L]     1, 2
   N      3
   F      4
   O      5
 */
#include <stdio.h>
#include <string.h>
#include <time.h>

#define TRUE  1
#define FALSE 0
#define SIZE  8
#define MAX_STATE 540
#define S  0
#define L1 1
#define L2 2
#define N  3
#define F  4
#define O  5

/* 隣接リスト */
/* 0 1 2 3
   4 5 6 7 */
const char adjacent[SIZE][4] = {
  1, 4, -1, -1, /* 0 */
  0, 2, 5, -1,  /* 1 */
  1, 3, 6, -1,  /* 2 */
  2, 7, -1, -1, /* 3 */
  0, 5, -1, -1, /* 4 */
  1, 4, 6, -1,  /* 5 */
  2, 5, 7, -1,  /* 6 */
  3, 6, -1, -1, /* 7 */
};

/* キュー */
char  state[MAX_STATE + 1][SIZE];      /* +1 はワーク領域 */
char  space_position[MAX_STATE];
short prev_state[MAX_STATE];

/* 問題 A : 44 move*/
char init_state0[SIZE] = {
  L1, L2, O, N,
   O,  F, F, S,
};

/* 36 move */
char init_state1[SIZE] = {
  O, N, L1, L2,
  O, F,  F,  S,
};

/* 問題 B: 56 move */
char init_state[SIZE] = {
  N, O, L1, L2,
  F, O,  F,  S,
};

/* 最終状態 */
char final_state[SIZE] = {
  L1, L2, N, O,
   O,  F, F, S,
};

/* 同じ状態があるか */
int check_same_state( int n )
{
  int i;
  for( i = 0; i < n; i++ ){
    if( !memcmp( state[i], state[n], SIZE ) ) return TRUE;
  }
  return FALSE;
}

/* 結果を出力 */
void print_answer( int n )
{
  int i;
  static char piece_table[6] = {
    '_', 'L', 'L', 'N', 'F', 'O',
  };
  if( n != 0 ) print_answer( prev_state[n] );
  for( i = 0; i < SIZE; i++ ){
    if( i == 4 ) printf("\n");
    printf("%c ", piece_table[ state[n][i] ] );
  }
  printf("\n\n");
}

/* 探索 */
void search( void )
{
  int front = 0, rear = 1, i;
  /* 初期化 */
  memcpy( state[0], init_state, SIZE );
  space_position[0] = 7;
  prev_state[0] = -1;
  while( front < rear ){
    int s = space_position[front];
    int n, piece;
    for( i = 0; (n = adjacent[s][i]) != -1; i++ ){
      /* 状態をコピー */
      memcpy( state[rear], state[front], SIZE );
      /* 移動 */
      piece = state[rear][n];
      if( piece == L1 && s > n ) continue;  /* 電球は左へ動けない */
      if( piece == L2 && s > 3 ) continue;  /* 電球は右へ動けない */
      if( piece == L1 ){
        /* 電球を左へ動かす */
        state[rear][s] = state[rear][n];
        state[rear][n] = state[rear][n + 1];
        state[rear][n + 1] = S;
        space_position[rear] = n + 1;
      } else if( piece == L2 ){
        /* 電球を右へ動かす */
        state[rear][s] = state[rear][n];
        state[rear][n] = state[rear][n - 1];
        state[rear][n - 1] = S;
        space_position[rear] = n - 1;
      } else {
        state[rear][s] = state[rear][n];
        state[rear][n] = S;
        space_position[rear] = n;
      }
      prev_state[rear] = front;
      if( !memcmp( state[rear], final_state, SIZE ) ){
        print_answer( rear );
        printf("状態数 %d 個\n", rear );
        return;
      } else if( !check_same_state( rear ) ){
        /* 登録 */
        rear++;
      }
    }
    front++;
  }
}

int main()
{
  int start, end;
  start = clock();
  search();
  end = clock();
  printf("時間 %d \n", end - start );
  return 0;
}

●プログラム2

/*
 * no_off_max.c : NO-OFF puzzle 最長手数を求める
 *
 *               Copyright (C) 2004-2006 Makoto Hiroi
 */
#include <stdio.h>
#include <string.h>
#include <time.h>

#define TRUE  1
#define FALSE 0
#define SIZE  8
#define MAX_STATE 540
#define S  0
#define L1 1
#define L2 2
#define N  3
#define F  4
#define O  5

/* 隣接リスト */
/* 0 1 2 3
   4 5 6 7 */
const char adjacent[SIZE][4] = {
  1, 4, -1, -1, /* 0 */
  0, 2, 5, -1,  /* 1 */
  1, 3, 6, -1,  /* 2 */
  2, 7, -1, -1, /* 3 */
  0, 5, -1, -1, /* 4 */
  1, 4, 6, -1,  /* 5 */
  2, 5, 7, -1,  /* 6 */
  3, 6, -1, -1, /* 7 */
};

/* キュー */
char  state[MAX_STATE + 1][SIZE];      /* +1 はワーク領域 */
char  space_position[MAX_STATE];
short move[MAX_STATE];

/* 初期状態 */
char init_state[SIZE] = {
  L1, L2, N, O,
   O,  F, F, S,
};

/* 同じ状態があるか */
int check_same_state( int n )
{
  int i;
  for( i = 0; i < n; i++ ){
    if( !memcmp( state[i], state[n], SIZE ) ) return TRUE;
  }
  return FALSE;
}

/* 結果を出力 */
void print_answer( int n )
{
  int i, m = move[n];
  static char piece_table[6] = {
    '_', 'L', 'L', 'N', 'F', 'O',
  };
  printf( "最長手数 %d 手\n", m );
  while( move[n--] == m ){
    for( i = 0; i < SIZE; i++ ){
      if( i == 4 ) printf("\n");
      printf("%c ", piece_table[ state[n][i] ] );
    }
    printf("\n\n");
  }
}

/* 探索 */
void search( void )
{
  int front = 0, rear = 1, i;
  /* 初期化 */
  memcpy( state[0], init_state, SIZE );
  space_position[0] = 7;
  move[0] = 0;
  while( front < rear ){
    int s = space_position[front];
    int n, piece;
    for( i = 0; (n = adjacent[s][i]) != -1; i++ ){
      /* 状態をコピー */
      memcpy( state[rear], state[front], SIZE );
      /* 移動 */
      piece = state[rear][n];
      if( piece == L1 && s > n ) continue;  /* 電球は左へ動けない */
      if( piece == L2 && s > 3 ) continue;  /* 電球は右へ動けない */
      if( piece == L1 ){
        /* 電球を左へ動かす */
        state[rear][s] = state[rear][n];
        state[rear][n] = state[rear][n + 1];
        state[rear][n + 1] = S;
        space_position[rear] = n + 1;
      } else if( piece == L2 ){
        /* 電球を右へ動かす */
        state[rear][s] = state[rear][n];
        state[rear][n] = state[rear][n - 1];
        state[rear][n - 1] = S;
        space_position[rear] = n - 1;
      } else {
        state[rear][s] = state[rear][n];
        state[rear][n] = S;
        space_position[rear] = n;
      }
      if( !check_same_state( rear ) ){
        /* 登録 */
        move[rear] = move[front] + 1;
        rear++;
      }
    }
    front++;
  }
  printf("状態数 %d\n", rear );
  print_answer( rear - 1 );
}

int main()
{
  int start, end;
  start = clock();
  search();
  end = clock();
  printf("時間 %d \n", end - start );
  return 0;
}

●スライドパズル NO-OFF 問題 A の解答

L L が電球を表し、_ が空き場所を表します。

  (0)        (1)        (2)        (3)        (4)        (5)        (6)        (7)
  L L O N    L L O _    L L _ O    _ L L O    O L L O    O L L O    O L L O    O L L O 
  O F F _    O F F N    O F F N    O F F N    _ F F N    F _ F N    F F _ N    F F N _ 

  (8)        (9)        (10)       (11)       (12)       (13)       (14)       (15)
  O L L _    O _ L L    _ O L L    F O L L    F O L L    F _ L L    F L L _    F L L O 
  F F N O    F F N O    F F N O    _ F N O    F _ N O    F O N O    F O N O    F O N _ 

  (16)       (17)       (18)       (19)       (20)       (21)       (22)       (23)
  F L L O    F L L O    F L L O    _ L L O    L L _ O    L L O _    L L O N    L L O N 
  F O _ N    F _ O N    _ F O N    F F O N    F F O N    F F O N    F F O _    F F _ O 

  (24)       (25)       (26)       (27)       (28)       (29)       (30)       (31)
  L L _ N    _ L L N    F L L N    F L L N    F L L N    F L L N    F L L _    F _ L L 
  F F O O    F F O O    _ F O O    F _ O O    F O _ O    F O O _    F O O N    F O O N 

  (32)       (33)       (34)       (35)       (36)       (37)       (38)       (39)
  F O L L    F O L L    _ O L L    O _ L L    O L L _    O L L N    O L L N    O L L N 
  F _ O N    _ F O N    F F O N    F F O N    F F O N    F F O _    F F _ O    F _ F O 

  (40)       (41)       (42)       (43)       (44)
  O L L N    _ L L N    L L _ N    L L N _    L L N O    
  _ F F O    O F F O    O F F O    O F F O    O F F _    

●スライドパズル NO-OFF 問題 B の解答

L L が電球を表し、_ が空き場所を表します。

  (0)        (1)        (2)        (3)        (4)        (5)        (6)        (7)
  N O L L    N O L L    N O L L    N _ L L    N L L _    N L L F    N L L F    N L L F 
  F O F _    F O _ F    F _ O F    F O O F    F O O F    F O O _    F O _ O    F _ O O 

  (8)        (9)        (10)       (11)       (12)       (13)       (14)       (15)
  N L L F    _ L L F    L L _ F    L L F _    L L F O    L L F O    L L _ O    _ L L O 
  _ F O O    N F O O    N F O O    N F O O    N F O _    N F _ O    N F F O    N F F O 

  (16)       (17)       (18)       (19)       (20)       (21)       (22)       (23)
  N L L O    N L L O    N L L O    N L L O    N L L _    N _ L L    _ N L L    F N L L 
  _ F F O    F _ F O    F F _ O    F F O _    F F O O    F F O O    F F O O    _ F O O 

  (24)       (25)       (26)       (27)       (28)       (29)       (30)       (31)
  F N L L    F _ L L    F L L _    F L L O    F L L O    F L L O    F L L O    _ L L O 
  F _ O O    F N O O    F N O O    F N O _    F N _ O    F _ N O    _ F N O    F F N O 

  (32)       (33)       (34)       (35)       (36)       (37)       (38)       (39)
  L L _ O    L L N O    L L N O    L L N _    L L _ N    _ L L N    F L L N    F L L N 
  F F N O    F F _ O    F F O _    F F O O    F F O O    F F O O    _ F O O    F _ O O 

  (40)       (41)       (42)       (43)       (44)       (45)       (46)       (47)
  F L L N    F L L N    F L L _    F _ L L    F O L L    F O L L    _ O L L    O _ L L 
  F O _ O    F O O _    F O O N    F O O N    F _ O N    _ F O N    F F O N    F F O N 

  (48)       (49)       (50)       (51)       (52)       (53)       (54)       (55)
  O L L _    O L L N    O L L N    O L L N    O L L N    _ L L N    L L _ N    L L N _ 
  F F O N    F F O _    F F _ O    F _ F O    _ F F O    O F F O    O F F O    O F F O 

  (56)
  L L N O 
  O F F _ 

Copyright (C) 2004-2006 Makoto Hiroi
All rights reserved.

[ Home | Puzzle ]